/*========================================================================= Program: Visualization Toolkit Module: vtkCubeAxesActor2D.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkCubeAxesActor2D.h" #include "vtkAxisActor2D.h" #include "vtkCamera.h" #include "vtkDataSet.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkTextProperty.h" #include "vtkTrivialProducer.h" #include "vtkViewport.h" vtkStandardNewMacro(vtkCubeAxesActor2D); vtkCxxSetObjectMacro(vtkCubeAxesActor2D, Camera, vtkCamera); vtkCxxSetObjectMacro(vtkCubeAxesActor2D, ViewProp, vtkProp); vtkCxxSetObjectMacro(vtkCubeAxesActor2D, AxisLabelTextProperty, vtkTextProperty); vtkCxxSetObjectMacro(vtkCubeAxesActor2D, AxisTitleTextProperty, vtkTextProperty); class vtkCubeAxesActor2DConnection : public vtkAlgorithm { public: static vtkCubeAxesActor2DConnection* New(); vtkTypeMacro(vtkCubeAxesActor2DConnection, vtkAlgorithm); vtkCubeAxesActor2DConnection() { this->SetNumberOfInputPorts(1); } }; vtkStandardNewMacro(vtkCubeAxesActor2DConnection); //------------------------------------------------------------------------------ // Instantiate this object. vtkCubeAxesActor2D::vtkCubeAxesActor2D() { this->ConnectionHolder = vtkCubeAxesActor2DConnection::New(); this->ViewProp = nullptr; this->Bounds[0] = -1.0; this->Bounds[1] = 1.0; this->Bounds[2] = -1.0; this->Bounds[3] = 1.0; this->Bounds[4] = -1.0; this->Bounds[5] = 1.0; this->UseRanges = 0; this->Ranges[0] = 0; this->Ranges[1] = 0; this->Ranges[2] = 0; this->Ranges[3] = 0; this->Ranges[4] = 0; this->Ranges[5] = 0; this->Camera = nullptr; this->FlyMode = VTK_FLY_CLOSEST_TRIAD; this->Scaling = 1; this->XAxis = vtkAxisActor2D::New(); this->XAxis->GetPositionCoordinate()->SetCoordinateSystemToDisplay(); this->XAxis->GetPosition2Coordinate()->SetCoordinateSystemToDisplay(); this->XAxis->AdjustLabelsOff(); this->YAxis = vtkAxisActor2D::New(); this->YAxis->GetPositionCoordinate()->SetCoordinateSystemToDisplay(); this->YAxis->GetPosition2Coordinate()->SetCoordinateSystemToDisplay(); this->YAxis->AdjustLabelsOff(); this->ZAxis = vtkAxisActor2D::New(); this->ZAxis->GetPositionCoordinate()->SetCoordinateSystemToDisplay(); this->ZAxis->GetPosition2Coordinate()->SetCoordinateSystemToDisplay(); this->ZAxis->AdjustLabelsOff(); this->NumberOfLabels = 3; this->AxisLabelTextProperty = vtkTextProperty::New(); this->AxisLabelTextProperty->SetBold(1); this->AxisLabelTextProperty->SetItalic(1); this->AxisLabelTextProperty->SetShadow(1); this->AxisLabelTextProperty->SetFontFamilyToArial(); this->AxisTitleTextProperty = vtkTextProperty::New(); this->AxisTitleTextProperty->ShallowCopy(this->AxisLabelTextProperty); this->LabelFormat = new char[8]; snprintf(this->LabelFormat, 8, "%s", "%-#6.3g"); this->FontFactor = 1.0; this->CornerOffset = 0.05; this->Inertia = 1; this->ShowActualBounds = 1; this->RenderCount = 0; this->XAxisVisibility = 1; this->YAxisVisibility = 1; this->ZAxisVisibility = 1; this->XLabel = new char[2]; snprintf(this->XLabel, 2, "%s", "X"); this->YLabel = new char[2]; snprintf(this->YLabel, 2, "%s", "Y"); this->ZLabel = new char[2]; snprintf(this->ZLabel, 2, "%s", "Z"); // Allow the user to specify an origin for the axes. The axes will then run // from this origin to the bounds and will cross over at this origin. this->XOrigin = VTK_DOUBLE_MAX; this->YOrigin = VTK_DOUBLE_MAX; this->ZOrigin = VTK_DOUBLE_MAX; } //------------------------------------------------------------------------------ // Shallow copy of an actor. void vtkCubeAxesActor2D::ShallowCopy(vtkCubeAxesActor2D* actor) { this->vtkActor2D::ShallowCopy(actor); this->SetAxisLabelTextProperty(actor->GetAxisLabelTextProperty()); this->SetAxisTitleTextProperty(actor->GetAxisTitleTextProperty()); this->SetLabelFormat(actor->GetLabelFormat()); this->SetFontFactor(actor->GetFontFactor()); this->SetCornerOffset(actor->GetCornerOffset()); this->SetInertia(static_cast(actor->GetInertia())); this->SetXLabel(actor->GetXLabel()); this->SetYLabel(actor->GetYLabel()); this->SetZLabel(actor->GetZLabel()); this->SetFlyMode(actor->GetFlyMode()); this->SetInputConnection(actor->ConnectionHolder->GetInputConnection(0, 0)); this->SetViewProp(actor->GetViewProp()); this->SetCamera(actor->GetCamera()); } //------------------------------------------------------------------------------ vtkCubeAxesActor2D::~vtkCubeAxesActor2D() { this->ConnectionHolder->Delete(); if (this->ViewProp) { this->ViewProp->Delete(); } if (this->Camera) { this->Camera->UnRegister(this); } this->XAxis->Delete(); this->YAxis->Delete(); this->ZAxis->Delete(); delete[] this->LabelFormat; this->LabelFormat = nullptr; delete[] this->XLabel; delete[] this->YLabel; delete[] this->ZLabel; this->SetAxisLabelTextProperty(nullptr); this->SetAxisTitleTextProperty(nullptr); } //------------------------------------------------------------------------------ void vtkCubeAxesActor2D::SetInputConnection(vtkAlgorithmOutput* ao) { this->ConnectionHolder->SetInputConnection(ao); } //------------------------------------------------------------------------------ void vtkCubeAxesActor2D::SetInputData(vtkDataSet* ds) { vtkTrivialProducer* tp = vtkTrivialProducer::New(); tp->SetOutput(ds); this->SetInputConnection(tp->GetOutputPort()); tp->Delete(); } //------------------------------------------------------------------------------ vtkDataSet* vtkCubeAxesActor2D::GetInput() { return vtkDataSet::SafeDownCast(this->ConnectionHolder->GetInputDataObject(0, 0)); } //------------------------------------------------------------------------------ // Static variable describes connections in cube. static int Conn[8][3] = { { 1, 2, 4 }, { 0, 3, 5 }, { 3, 0, 6 }, { 2, 1, 7 }, { 5, 6, 0 }, { 4, 7, 1 }, { 7, 4, 2 }, { 6, 5, 3 } }; //------------------------------------------------------------------------------ // Project the bounding box and compute edges on the border of the bounding // cube. Determine which parts of the edges are visible via intersection // with the boundary of the viewport (minus borders). int vtkCubeAxesActor2D::RenderOverlay(vtkViewport* viewport) { int renderedSomething = 0; // Initialization if (!this->RenderSomething) { return 0; } // Render the axes if (this->XAxisVisibility) { renderedSomething += this->XAxis->RenderOverlay(viewport); } if (this->YAxisVisibility) { renderedSomething += this->YAxis->RenderOverlay(viewport); } if (this->ZAxisVisibility) { renderedSomething += this->ZAxis->RenderOverlay(viewport); } return renderedSomething; } //------------------------------------------------------------------------------ // Project the bounding box and compute edges on the border of the bounding // cube. Determine which parts of the edges are visible via intersection // with the boundary of the viewport (minus borders). int vtkCubeAxesActor2D::RenderOpaqueGeometry(vtkViewport* viewport) { double bounds[6]; double slope = 0.0, minSlope, num, den; double pts[8][3], d2, d2Min, min; int i, idx = 0; int xIdx = 0, yIdx = 0, zIdx = 0, zIdx2 = 0, renderedSomething = 0; int xAxes = 0, yAxes = 0, zAxes = 0; // Initialization if (!this->Camera) { vtkErrorMacro(<< "No camera!"); this->RenderSomething = 0; return 0; } this->RenderSomething = 1; // determine the bounds to use this->GetBounds(bounds); // Check for user specified origins. By default, these are placed at a corner of the // bounding box of the dataset (corner is based on the fly mode) if (this->XOrigin != VTK_DOUBLE_MAX) { bounds[0] = this->XOrigin; } if (this->YOrigin != VTK_DOUBLE_MAX) { bounds[2] = this->YOrigin; } if (this->ZOrigin != VTK_DOUBLE_MAX) { bounds[4] = this->ZOrigin; } // Build the axes (almost always needed so we don't check mtime) // Transform all points into display coordinates this->TransformBounds(viewport, bounds, pts); // Find the portion of the bounding box that fits within the viewport, if (!this->ShowActualBounds && (this->ClipBounds(viewport, pts, bounds) == 0)) { this->RenderSomething = 0; return 0; } // Take into account the inertia. Process only so often. if (this->RenderCount++ == 0 || !(this->RenderCount % this->Inertia)) { // Okay, we have a bounding box, maybe clipped and scaled, that is visible. // We setup the axes depending on the fly mode. if (this->FlyMode == VTK_FLY_NONE) { idx = 2; // Just use the default axis orientation. xAxes = 0; xIdx = Conn[idx][0]; yAxes = 1; yIdx = Conn[idx][1]; zAxes = 2; zIdx = idx; zIdx2 = Conn[idx][2]; } else if (this->FlyMode == VTK_FLY_CLOSEST_TRIAD) { // Loop over points and find the closest point to the camera min = VTK_DOUBLE_MAX; for (i = 0; i < 8; i++) { if (pts[i][2] < min) { idx = i; min = pts[i][2]; } } // Setup the three axes to be drawn xAxes = 0; xIdx = Conn[idx][0]; yAxes = 1; yIdx = Conn[idx][1]; zAxes = 2; zIdx = idx; zIdx2 = Conn[idx][2]; } else if (this->FlyMode == VTK_FLY_OUTER_EDGES) { double e1[2], e2[2], e3[2]; // Find distance to origin d2Min = VTK_DOUBLE_MAX; for (i = 0; i < 8; i++) { d2 = pts[i][0] * pts[i][0] + pts[i][1] * pts[i][1]; if (d2 < d2Min) { d2Min = d2; idx = i; } } // find minimum slope point connected to closest point and on // right side (in projected coordinates). This is the first edge. minSlope = VTK_DOUBLE_MAX; for (xIdx = 0, i = 0; i < 3; i++) { num = (pts[Conn[idx][i]][1] - pts[idx][1]); den = (pts[Conn[idx][i]][0] - pts[idx][0]); if (den != 0.0) { slope = num / den; } if (slope < minSlope && den > 0) { xIdx = Conn[idx][i]; yIdx = Conn[idx][(i + 1) % 3]; zIdx = Conn[idx][(i + 2) % 3]; xAxes = i; minSlope = slope; } } // find edge (connected to closest point) on opposite side for (i = 0; i < 2; i++) { e1[i] = (pts[xIdx][i] - pts[idx][i]); e2[i] = (pts[yIdx][i] - pts[idx][i]); e3[i] = (pts[zIdx][i] - pts[idx][i]); } vtkMath::Normalize2D(e1); vtkMath::Normalize2D(e2); vtkMath::Normalize2D(e3); if (vtkMath::Dot2D(e1, e2) < vtkMath::Dot2D(e1, e3)) { yAxes = (xAxes + 1) % 3; } else { yIdx = zIdx; yAxes = (xAxes + 2) % 3; } // Find the final point by determining which global x-y-z axes have not // been represented, and then determine the point closest to the viewer. zAxes = (xAxes != 0 && yAxes != 0 ? 0 : (xAxes != 1 && yAxes != 1 ? 1 : 2)); if (pts[Conn[xIdx][zAxes]][2] < pts[Conn[yIdx][zAxes]][2]) { zIdx = xIdx; zIdx2 = Conn[xIdx][zAxes]; } else { zIdx = yIdx; zIdx2 = Conn[yIdx][zAxes]; } } // else boundary edges fly mode this->InertiaAxes[0] = idx; this->InertiaAxes[1] = xIdx; this->InertiaAxes[2] = yIdx; this->InertiaAxes[3] = zIdx; this->InertiaAxes[4] = zIdx2; this->InertiaAxes[5] = xAxes; this->InertiaAxes[6] = yAxes; this->InertiaAxes[7] = zAxes; } // inertia else { idx = this->InertiaAxes[0]; xIdx = this->InertiaAxes[1]; yIdx = this->InertiaAxes[2]; zIdx = this->InertiaAxes[3]; zIdx2 = this->InertiaAxes[4]; xAxes = this->InertiaAxes[5]; yAxes = this->InertiaAxes[6]; zAxes = this->InertiaAxes[7]; } // Setup the axes for plotting double xCoords[4], yCoords[4], zCoords[4], xRange[2], yRange[2], zRange[2]; this->AdjustAxes(pts, bounds, idx, xIdx, yIdx, zIdx, zIdx2, xAxes, yAxes, zAxes, xCoords, yCoords, zCoords, xRange, yRange, zRange); // Sorry for ugly hack. I find the fonts slightly too large on the axis. BNW double AxisFontFactor = this->FontFactor * .75; // Update axes this->Labels[0] = this->XLabel; this->Labels[1] = this->YLabel; this->Labels[2] = this->ZLabel; this->XAxis->GetPositionCoordinate()->SetValue(xCoords[0], xCoords[1]); this->XAxis->GetPosition2Coordinate()->SetValue(xCoords[2], xCoords[3]); this->XAxis->SetRange(xRange[0], xRange[1]); this->XAxis->SetTitle(this->Labels[xAxes]); this->XAxis->SetNumberOfLabels(this->NumberOfLabels); this->XAxis->SetLabelFormat(this->LabelFormat); this->XAxis->SetFontFactor(AxisFontFactor); this->XAxis->SetProperty(this->GetProperty()); this->YAxis->GetPositionCoordinate()->SetValue(yCoords[2], yCoords[3]); this->YAxis->GetPosition2Coordinate()->SetValue(yCoords[0], yCoords[1]); this->YAxis->SetRange(yRange[1], yRange[0]); this->YAxis->SetTitle(this->Labels[yAxes]); this->YAxis->SetNumberOfLabels(this->NumberOfLabels); this->YAxis->SetLabelFormat(this->LabelFormat); this->YAxis->SetFontFactor(AxisFontFactor); this->YAxis->SetProperty(this->GetProperty()); this->ZAxis->GetPositionCoordinate()->SetValue(zCoords[0], zCoords[1]); this->ZAxis->GetPosition2Coordinate()->SetValue(zCoords[2], zCoords[3]); this->ZAxis->SetRange(zRange[0], zRange[1]); this->ZAxis->SetTitle(this->Labels[zAxes]); this->ZAxis->SetNumberOfLabels(this->NumberOfLabels); this->ZAxis->SetLabelFormat(this->LabelFormat); this->ZAxis->SetFontFactor(AxisFontFactor); this->ZAxis->SetProperty(this->GetProperty()); // Rebuid text props // Perform shallow copy here since each individual axis can be // accessed through the class API (i.e. each individual axis text prop // can be changed). Therefore, we can not just assign pointers otherwise // each individual axis text prop would point to the same text prop. if (this->AxisLabelTextProperty && this->AxisLabelTextProperty->GetMTime() > this->BuildTime) { if (this->XAxis->GetLabelTextProperty()) { this->XAxis->GetLabelTextProperty()->ShallowCopy(this->AxisLabelTextProperty); } if (this->YAxis->GetLabelTextProperty()) { this->YAxis->GetLabelTextProperty()->ShallowCopy(this->AxisLabelTextProperty); } if (this->ZAxis->GetLabelTextProperty()) { this->ZAxis->GetLabelTextProperty()->ShallowCopy(this->AxisLabelTextProperty); } } if (this->AxisTitleTextProperty && this->AxisTitleTextProperty->GetMTime() > this->BuildTime) { if (this->XAxis->GetLabelTextProperty()) { this->XAxis->GetTitleTextProperty()->ShallowCopy(this->AxisTitleTextProperty); } if (this->YAxis->GetLabelTextProperty()) { this->YAxis->GetTitleTextProperty()->ShallowCopy(this->AxisTitleTextProperty); } if (this->ZAxis->GetLabelTextProperty()) { this->ZAxis->GetTitleTextProperty()->ShallowCopy(this->AxisTitleTextProperty); } } this->BuildTime.Modified(); // Render the axes if (this->XAxisVisibility) { renderedSomething += this->XAxis->RenderOpaqueGeometry(viewport); } if (this->YAxisVisibility) { renderedSomething += this->YAxis->RenderOpaqueGeometry(viewport); } if (this->ZAxisVisibility) { renderedSomething += this->ZAxis->RenderOpaqueGeometry(viewport); } return renderedSomething; } //------------------------------------------------------------------------------ // Description: // Does this prop have some translucent polygonal geometry? vtkTypeBool vtkCubeAxesActor2D::HasTranslucentPolygonalGeometry() { return 0; } //------------------------------------------------------------------------------ // Do final adjustment of axes to control offset, etc. void vtkCubeAxesActor2D::AdjustAxes(double pts[8][3], double bounds[6], int idx, int xIdx, int yIdx, int zIdx, int zIdx2, int xAxes, int yAxes, int zAxes, double xCoords[4], double yCoords[4], double zCoords[4], double xRange[2], double yRange[2], double zRange[2]) { double* internal_bounds; if (this->UseRanges) { internal_bounds = this->Ranges; } else { internal_bounds = bounds; } // The x-axis xCoords[0] = pts[idx][0]; xCoords[1] = pts[idx][1]; xCoords[2] = pts[xIdx][0]; xCoords[3] = pts[xIdx][1]; if (idx < xIdx) { xRange[0] = internal_bounds[2 * xAxes]; xRange[1] = internal_bounds[2 * xAxes + 1]; } else { xRange[0] = internal_bounds[2 * xAxes + 1]; xRange[1] = internal_bounds[2 * xAxes]; } // The y-axis yCoords[0] = pts[idx][0]; yCoords[1] = pts[idx][1]; yCoords[2] = pts[yIdx][0]; yCoords[3] = pts[yIdx][1]; if (idx < yIdx) { yRange[0] = internal_bounds[2 * yAxes]; yRange[1] = internal_bounds[2 * yAxes + 1]; } else { yRange[0] = internal_bounds[2 * yAxes + 1]; yRange[1] = internal_bounds[2 * yAxes]; } // The z-axis if (zIdx != xIdx && zIdx != idx) // rearrange for labels { zIdx = zIdx2; zIdx2 = yIdx; } zCoords[0] = pts[zIdx][0]; zCoords[1] = pts[zIdx][1]; zCoords[2] = pts[zIdx2][0]; zCoords[3] = pts[zIdx2][1]; if (zIdx < zIdx2) { zRange[0] = internal_bounds[2 * zAxes]; zRange[1] = internal_bounds[2 * zAxes + 1]; } else { zRange[0] = internal_bounds[2 * zAxes + 1]; zRange[1] = internal_bounds[2 * zAxes]; } // Pull back the corners if specified if (this->CornerOffset > 0.0) { double ave; // x-axis ave = (xCoords[0] + xCoords[2]) / 2.0; xCoords[0] = xCoords[0] - this->CornerOffset * (xCoords[0] - ave); xCoords[2] = xCoords[2] - this->CornerOffset * (xCoords[2] - ave); ave = (xCoords[1] + xCoords[3]) / 2.0; xCoords[1] = xCoords[1] - this->CornerOffset * (xCoords[1] - ave); xCoords[3] = xCoords[3] - this->CornerOffset * (xCoords[3] - ave); ave = (xRange[1] + xRange[0]) / 2.0; if (!this->ShowActualBounds) { xRange[0] = xRange[0] - this->CornerOffset * (xRange[0] - ave); xRange[1] = xRange[1] - this->CornerOffset * (xRange[1] - ave); } // y-axis ave = (yCoords[0] + yCoords[2]) / 2.0; yCoords[0] = yCoords[0] - this->CornerOffset * (yCoords[0] - ave); yCoords[2] = yCoords[2] - this->CornerOffset * (yCoords[2] - ave); ave = (yCoords[1] + yCoords[3]) / 2.0; yCoords[1] = yCoords[1] - this->CornerOffset * (yCoords[1] - ave); yCoords[3] = yCoords[3] - this->CornerOffset * (yCoords[3] - ave); ave = (yRange[1] + yRange[0]) / 2.0; if (!this->ShowActualBounds) { yRange[0] = yRange[0] - this->CornerOffset * (yRange[0] - ave); yRange[1] = yRange[1] - this->CornerOffset * (yRange[1] - ave); } // z-axis ave = (zCoords[0] + zCoords[2]) / 2.0; zCoords[0] = zCoords[0] - this->CornerOffset * (zCoords[0] - ave); zCoords[2] = zCoords[2] - this->CornerOffset * (zCoords[2] - ave); ave = (zCoords[1] + zCoords[3]) / 2.0; zCoords[1] = zCoords[1] - this->CornerOffset * (zCoords[1] - ave); zCoords[3] = zCoords[3] - this->CornerOffset * (zCoords[3] - ave); ave = (zRange[1] + zRange[0]) / 2.0; if (!this->ShowActualBounds) { zRange[0] = zRange[0] - this->CornerOffset * (zRange[0] - ave); zRange[1] = zRange[1] - this->CornerOffset * (zRange[1] - ave); } } } //------------------------------------------------------------------------------ // Release any graphics resources that are being consumed by this actor. // The parameter window could be used to determine which graphic // resources to release. void vtkCubeAxesActor2D::ReleaseGraphicsResources(vtkWindow* win) { this->XAxis->ReleaseGraphicsResources(win); this->YAxis->ReleaseGraphicsResources(win); this->ZAxis->ReleaseGraphicsResources(win); } //------------------------------------------------------------------------------ // Return the ranges void vtkCubeAxesActor2D::GetRanges(double ranges[6]) { int i; for (i = 0; i < 6; i++) { ranges[i] = this->Ranges[i]; } } //------------------------------------------------------------------------------ // Compute the ranges void vtkCubeAxesActor2D::GetRanges( double& xmin, double& xmax, double& ymin, double& ymax, double& zmin, double& zmax) { double ranges[6]; this->GetRanges(ranges); xmin = ranges[0]; xmax = ranges[1]; ymin = ranges[2]; ymax = ranges[3]; zmin = ranges[4]; zmax = ranges[5]; } //------------------------------------------------------------------------------ // Compute the bounds double* vtkCubeAxesActor2D::GetRanges() { double ranges[6]; this->GetRanges(ranges); return this->Ranges; } //------------------------------------------------------------------------------ // Compute the bounds void vtkCubeAxesActor2D::GetBounds(double bounds[6]) { const double* propBounds; int i; if (this->GetInput()) { this->ConnectionHolder->GetInputAlgorithm()->Update(); this->GetInput()->GetBounds(bounds); for (i = 0; i < 6; i++) { this->Bounds[i] = bounds[i]; } } else if (this->ViewProp && ((propBounds = this->ViewProp->GetBounds()) && propBounds != nullptr)) { for (i = 0; i < 6; i++) { bounds[i] = this->Bounds[i] = propBounds[i]; } } else { for (i = 0; i < 6; i++) { bounds[i] = this->Bounds[i]; } } } //------------------------------------------------------------------------------ // Compute the bounds void vtkCubeAxesActor2D::GetBounds( double& xmin, double& xmax, double& ymin, double& ymax, double& zmin, double& zmax) { double bounds[6]; this->GetBounds(bounds); xmin = bounds[0]; xmax = bounds[1]; ymin = bounds[2]; ymax = bounds[3]; zmin = bounds[4]; zmax = bounds[5]; } //------------------------------------------------------------------------------ // Compute the bounds double* vtkCubeAxesActor2D::GetBounds() { this->GetBounds(this->Bounds); return this->Bounds; } //------------------------------------------------------------------------------ void vtkCubeAxesActor2D::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); if (this->GetInput()) { os << indent << "Input: (" << (void*)this->GetInput() << ")\n"; } else { os << indent << "Input: (none)\n"; } if (this->ViewProp) { os << indent << "ViewProp: (" << (void*)this->ViewProp << ")\n"; } else { os << indent << "ViewProp: (none)\n"; } os << indent << "Bounds: \n"; os << indent << " Xmin,Xmax: (" << this->Bounds[0] << ", " << this->Bounds[1] << ")\n"; os << indent << " Ymin,Ymax: (" << this->Bounds[2] << ", " << this->Bounds[3] << ")\n"; os << indent << " Zmin,Zmax: (" << this->Bounds[4] << ", " << this->Bounds[5] << ")\n"; if (this->Camera) { os << indent << "Camera:\n"; this->Camera->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "Camera: (none)\n"; } if (this->AxisTitleTextProperty) { os << indent << "Axis Title Text Property:\n"; this->AxisTitleTextProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "Axis Title Text Property: (none)\n"; } if (this->AxisLabelTextProperty) { os << indent << "Axis Label Text Property:\n"; this->AxisLabelTextProperty->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "Axis Label Text Property: (none)\n"; } if (this->FlyMode == VTK_FLY_CLOSEST_TRIAD) { os << indent << "Fly Mode: CLOSEST_TRIAD\n"; } else if (this->FlyMode == VTK_FLY_OUTER_EDGES) { os << indent << "Fly Mode: OUTER_EDGES\n"; } else if (this->FlyMode == VTK_FLY_NONE) { os << indent << "Fly Mode: Disabled\n"; } os << indent << "Scaling: " << (this->Scaling ? "On\n" : "Off\n"); os << indent << "UseRanges: " << (this->UseRanges ? "On\n" : "Off\n"); os << indent << "Ranges: \n"; os << indent << " Xmin,Xmax: (" << this->Ranges[0] << ", " << this->Ranges[1] << ")\n"; os << indent << " Ymin,Ymax: (" << this->Ranges[2] << ", " << this->Ranges[3] << ")\n"; os << indent << " Zmin,Zmax: (" << this->Ranges[4] << ", " << this->Ranges[5] << ")\n"; os << indent << "Number Of Labels: " << this->NumberOfLabels << "\n"; os << indent << "X Label: " << this->XLabel << "\n"; os << indent << "Y Label: " << this->YLabel << "\n"; os << indent << "Z Label: " << this->ZLabel << "\n"; os << indent << "X Axis Visibility: " << (this->XAxisVisibility ? "On\n" : "Off\n"); os << indent << "Y Axis Visibility: " << (this->YAxisVisibility ? "On\n" : "Off\n"); os << indent << "Z Axis Visibility: " << (this->ZAxisVisibility ? "On\n" : "Off\n"); os << indent << "Label Format: " << this->LabelFormat << "\n"; os << indent << "Font Factor: " << this->FontFactor << "\n"; os << indent << "Inertia: " << this->Inertia << "\n"; os << indent << "Corner Offset: " << this->CornerOffset << "\n"; os << indent << "UseRanges: " << (this->UseRanges ? "On" : "Off") << "\n"; os << indent << "Ranges: " << this->Ranges[0] << ", " << this->Ranges[1] << ", " << this->Ranges[2] << ", " << this->Ranges[3] << ", " << this->Ranges[4] << ", " << this->Ranges[5] << "\n"; os << indent << "Show Actual Bounds: " << (this->ShowActualBounds ? "On\n" : "Off\n"); if (this->XOrigin != VTK_DOUBLE_MAX) { os << indent << "User specified X Origin: " << this->XOrigin << endl; } if (this->YOrigin != VTK_DOUBLE_MAX) { os << indent << "User specified Y Origin: " << this->YOrigin << endl; } if (this->ZOrigin != VTK_DOUBLE_MAX) { os << indent << "User specified Z Origin: " << this->ZOrigin << endl; } } //------------------------------------------------------------------------------ static int IsInBounds(double x[3], double bounds[6]); // Clip the axes to fit into the viewport. Do this clipping each of the three // axes to determine which part of the cube is in view. Returns 0 if // nothing should be drawn. #define VTK_DIVS 10 int vtkCubeAxesActor2D::ClipBounds(vtkViewport* viewport, double pts[8][3], double bounds[6]) { int i, j, k, numIters; double planes[24]; double x[3]; double val, maxVal = 0, anchor[3], scale; double delX, delY, delZ; double bounds2[6]; double scale2, newScale, origin[3]; double aspect[2]; // Only do this mojo if scaling is required if (!this->Scaling) { return 1; } // Get the 6 planes defining the view frustrum viewport->GetAspect(aspect); this->Camera->GetFrustumPlanes((aspect[0] / aspect[1]), planes); // Hunt for the point in the bounds furthest inside the frustum // Iteratively loop over points in bounding box and evaluate the // maximum minimum distance. Find the point furthest inside of the // bounding box. Use this as an anchor point to scale to. Repeat // the process to hone in on the best point. delX = (bounds[1] - bounds[0]) / (VTK_DIVS - 1); delY = (bounds[3] - bounds[2]) / (VTK_DIVS - 1); delZ = (bounds[5] - bounds[4]) / (VTK_DIVS - 1); anchor[0] = (bounds[1] + bounds[0]) / 2.0; anchor[1] = (bounds[3] + bounds[2]) / 2.0; anchor[2] = (bounds[5] + bounds[4]) / 2.0; for (numIters = 0; numIters < 8; numIters++) { origin[0] = anchor[0] - delX * (VTK_DIVS - 1) / 2.0; origin[1] = anchor[1] - delY * (VTK_DIVS - 1) / 2.0; origin[2] = anchor[2] - delZ * (VTK_DIVS - 1) / 2.0; for (maxVal = 0.0, k = 0; k < VTK_DIVS; k++) { x[2] = origin[2] + k * delZ; for (j = 0; j < VTK_DIVS; j++) { x[1] = origin[1] + j * delY; for (i = 0; i < VTK_DIVS; i++) { x[0] = origin[0] + i * delX; if (IsInBounds(x, bounds)) { val = this->EvaluatePoint(planes, x); if (val > maxVal) { anchor[0] = x[0]; anchor[1] = x[1]; anchor[2] = x[2]; maxVal = val; } } // if in bounding box } // i } // j } // k delX /= (VTK_DIVS - 1) * 1.414; delY /= (VTK_DIVS - 1) * 1.414; delZ /= (VTK_DIVS - 1) * 1.414; } // Iteratively find anchor point if (maxVal <= 0.0) { return 0; // couldn't find a point inside } // Now iteratively scale the bounding box until all points are inside // the frustrum. Use bisection method. scale = 1.0; scale2 = 0.00001; val = this->EvaluateBounds(planes, bounds); // Get other end point for bisection technique for (i = 0; i < 3; i++) { bounds2[2 * i] = (bounds[2 * i] - anchor[i]) * scale2 + anchor[i]; bounds2[2 * i + 1] = (bounds[2 * i + 1] - anchor[i]) * scale2 + anchor[i]; } val = this->EvaluateBounds(planes, bounds2); if (val <= 0.0) { return 0; // not worth drawing - too small } for (numIters = 0; numIters < 10; numIters++) { newScale = (scale + scale2) / 2.0; for (i = 0; i < 3; i++) { bounds2[2 * i] = (bounds[2 * i] - anchor[i]) * newScale + anchor[i]; bounds2[2 * i + 1] = (bounds[2 * i + 1] - anchor[i]) * newScale + anchor[i]; } val = this->EvaluateBounds(planes, bounds2); if (val > 0.0) { scale2 = newScale; } else { scale = newScale; } } // for converged for (i = 0; i < 6; i++) // copy the result { bounds[i] = bounds2[i]; } this->TransformBounds(viewport, bounds, pts); return 1; } #undef VTK_DIVS //------------------------------------------------------------------------------ void vtkCubeAxesActor2D::TransformBounds(vtkViewport* viewport, double bounds[6], double pts[8][3]) { int i, j, k, idx; double x[3]; // loop over verts of bounding box for (k = 0; k < 2; k++) { x[2] = bounds[4 + k]; for (j = 0; j < 2; j++) { x[1] = bounds[2 + j]; for (i = 0; i < 2; i++) { idx = i + 2 * j + 4 * k; x[0] = bounds[i]; viewport->SetWorldPoint(x[0], x[1], x[2], 1.0); viewport->WorldToDisplay(); viewport->GetDisplayPoint(pts[idx]); } } } } //------------------------------------------------------------------------------ // Return smallest value of point evaluated against frustum planes. Also // sets the closest point coordinates in xyz. double vtkCubeAxesActor2D::EvaluatePoint(double planes[24], double x[3]) { int kk; double* plane; double val, minPlanesValue; for (minPlanesValue = VTK_DOUBLE_MAX, kk = 0; kk < 6; kk++) { plane = planes + kk * 4; val = plane[0] * x[0] + plane[1] * x[1] + plane[2] * x[2] + plane[3]; if (val < minPlanesValue) { minPlanesValue = val; } } // for all planes return minPlanesValue; } //------------------------------------------------------------------------------ // Return the smallest point of the bounding box evaluated against the // frustum planes. double vtkCubeAxesActor2D::EvaluateBounds(double planes[24], double bounds[6]) { double val, minVal, x[3]; int i, j, k; for (minVal = VTK_DOUBLE_MAX, k = 0; k < 2; k++) { x[2] = bounds[4 + k]; for (j = 0; j < 2; j++) { x[1] = bounds[2 + j]; for (i = 0; i < 2; i++) { x[0] = bounds[i]; val = this->EvaluatePoint(planes, x); if (val < minVal) { minVal = val; } } } } // loop over verts of bounding box return minVal; } //------------------------------------------------------------------------------ static int IsInBounds(double x[3], double bounds[6]) { if (x[0] < bounds[0] || x[0] > bounds[1] || x[1] < bounds[2] || x[1] > bounds[3] || x[2] < bounds[4] || x[2] > bounds[5]) { return 0; } else { return 1; } }