/*========================================================================= Program: Visualization Toolkit Module: vtkContourRepresentation.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 "vtkContourRepresentation.h" #include "vtkBox.h" #include "vtkCamera.h" #include "vtkCellArray.h" #include "vtkContourLineInterpolator.h" #include "vtkCoordinate.h" #include "vtkHandleRepresentation.h" #include "vtkIncrementalOctreePointLocator.h" #include "vtkIntArray.h" #include "vtkInteractorObserver.h" #include "vtkLine.h" #include "vtkMath.h" #include "vtkMatrix4x4.h" #include "vtkObjectFactory.h" #include "vtkPointPlacer.h" #include "vtkPolyData.h" #include "vtkRenderer.h" #include "vtkWindow.h" #include #include #include vtkCxxSetObjectMacro(vtkContourRepresentation, PointPlacer, vtkPointPlacer); vtkCxxSetObjectMacro(vtkContourRepresentation, LineInterpolator, vtkContourLineInterpolator); //------------------------------------------------------------------------------ vtkContourRepresentation::vtkContourRepresentation() { this->Internal = new vtkContourRepresentationInternals; this->PixelTolerance = 7; this->WorldTolerance = 0.001; this->PointPlacer = nullptr; this->LineInterpolator = nullptr; this->Locator = nullptr; this->RebuildLocator = false; this->ActiveNode = -1; this->NeedToRender = 0; this->ClosedLoop = 0; this->ShowSelectedNodes = 0; this->CurrentOperation = vtkContourRepresentation::Inactive; this->ResetLocator(); } //------------------------------------------------------------------------------ vtkContourRepresentation::~vtkContourRepresentation() { this->SetPointPlacer(nullptr); this->SetLineInterpolator(nullptr); this->Internal->ClearNodes(); delete this->Internal; if (this->Locator) { this->Locator->Delete(); } } //------------------------------------------------------------------------------ void vtkContourRepresentation::ResetLocator() { if (this->Locator) { this->Locator->Delete(); } this->Locator = vtkIncrementalOctreePointLocator::New(); this->Locator->SetBuildCubicOctree(1); this->RebuildLocator = true; } //------------------------------------------------------------------------------ void vtkContourRepresentation::ClearAllNodes() { this->ResetLocator(); this->Internal->ClearNodes(); this->BuildLines(); this->BuildLocator(); this->NeedToRender = 1; this->Modified(); } //------------------------------------------------------------------------------ void vtkContourRepresentation::AddNodeAtPositionInternal( double worldPos[3], double worldOrient[9], double displayPos[2]) { // Add a new point at this position vtkContourRepresentationNode* node = new vtkContourRepresentationNode; node->WorldPosition[0] = worldPos[0]; node->WorldPosition[1] = worldPos[1]; node->WorldPosition[2] = worldPos[2]; node->Selected = 0; node->NormalizedDisplayPosition[0] = displayPos[0]; node->NormalizedDisplayPosition[1] = displayPos[1]; this->Renderer->DisplayToNormalizedDisplay( node->NormalizedDisplayPosition[0], node->NormalizedDisplayPosition[1]); memcpy(node->WorldOrientation, worldOrient, 9 * sizeof(double)); this->Internal->Nodes.push_back(node); if (this->LineInterpolator && this->GetNumberOfNodes() > 1) { // Give the line interpolator a chance to update the node. int didNodeChange = this->LineInterpolator->UpdateNode( this->Renderer, this, node->WorldPosition, this->GetNumberOfNodes() - 1); // Give the point placer a chance to validate the updated node. If its not // valid, discard the LineInterpolator's change. if (didNodeChange && !this->PointPlacer->ValidateWorldPosition(node->WorldPosition, worldOrient)) { node->WorldPosition[0] = worldPos[0]; node->WorldPosition[1] = worldPos[1]; node->WorldPosition[2] = worldPos[2]; } } this->UpdateLines(static_cast(this->Internal->Nodes.size()) - 1); this->NeedToRender = 1; } //------------------------------------------------------------------------------ void vtkContourRepresentation::GetNodePolyData(vtkPolyData* poly) { poly->Initialize(); int count = this->GetNumberOfNodes(); if (count == 0) { return; } vtkPoints* points = vtkPoints::New(); vtkCellArray* lines = vtkCellArray::New(); points->SetNumberOfPoints(count); vtkIdType numLines = count; if (this->ClosedLoop) { numLines++; } vtkIdType* lineIndices = new vtkIdType[numLines]; int i; vtkIdType index = 0; double pos[3]; for (i = 0; i < this->GetNumberOfNodes(); ++i) { // Add the node this->GetNthNodeWorldPosition(i, pos); points->InsertPoint(index, pos); lineIndices[index] = index; index++; } if (this->ClosedLoop) { lineIndices[index] = 0; } lines->InsertNextCell(numLines, lineIndices); delete[] lineIndices; poly->SetPoints(points); poly->SetLines(lines); points->Delete(); lines->Delete(); } //------------------------------------------------------------------------------ void vtkContourRepresentation::AddNodeAtPositionInternal( double worldPos[3], double worldOrient[9], int displayPos[2]) { double dispPos[2]; dispPos[0] = static_cast(displayPos[0]); dispPos[1] = static_cast(displayPos[1]); this->AddNodeAtPositionInternal(worldPos, worldOrient, dispPos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::AddNodeAtWorldPosition(double worldPos[3], double worldOrient[9]) { // Check if this is a valid location if (!this->PointPlacer->ValidateWorldPosition(worldPos, worldOrient)) { return 0; } double displayPos[2]; this->GetRendererComputedDisplayPositionFromWorldPosition(worldPos, worldOrient, displayPos); this->AddNodeAtPositionInternal(worldPos, worldOrient, displayPos); return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::AddNodeAtWorldPosition(double x, double y, double z) { double worldPos[3] = { x, y, z }; return this->AddNodeAtWorldPosition(worldPos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::AddNodeAtWorldPosition(double worldPos[3]) { // Check if this is a valid location if (!this->PointPlacer->ValidateWorldPosition(worldPos)) { return 0; } double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; double displayPos[2]; this->GetRendererComputedDisplayPositionFromWorldPosition(worldPos, worldOrient, displayPos); this->AddNodeAtPositionInternal(worldPos, worldOrient, displayPos); return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::AddNodeAtDisplayPosition(double displayPos[2]) { double worldPos[3]; double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; // Compute the world position from the display position // based on the concrete representation's constraints // If this is not a valid display location return 0 if (!this->PointPlacer->ComputeWorldPosition(this->Renderer, displayPos, worldPos, worldOrient)) { return 0; } this->AddNodeAtPositionInternal(worldPos, worldOrient, displayPos); return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::AddNodeAtDisplayPosition(int displayPos[2]) { double doubleDisplayPos[2]; doubleDisplayPos[0] = displayPos[0]; doubleDisplayPos[1] = displayPos[1]; return this->AddNodeAtDisplayPosition(doubleDisplayPos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::AddNodeAtDisplayPosition(int X, int Y) { double displayPos[2]; displayPos[0] = X; displayPos[1] = Y; return this->AddNodeAtDisplayPosition(displayPos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::ActivateNode(double displayPos[2]) { this->BuildLocator(); // Find closest node to this display pos that // is within PixelTolerance double dPos[3] = { displayPos[0], displayPos[1], 0 }; double closestDistance2 = VTK_DOUBLE_MAX; int closestNode = this->Locator->FindClosestPointWithinRadius(this->PixelTolerance, dPos, closestDistance2); if (closestNode != this->ActiveNode) { this->ActiveNode = closestNode; this->NeedToRender = 1; } return (this->ActiveNode >= 0); } //------------------------------------------------------------------------------ int vtkContourRepresentation::ActivateNode(int displayPos[2]) { double doubleDisplayPos[2]; doubleDisplayPos[0] = displayPos[0]; doubleDisplayPos[1] = displayPos[1]; return this->ActivateNode(doubleDisplayPos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::ActivateNode(int X, int Y) { double doubleDisplayPos[2]; doubleDisplayPos[0] = X; doubleDisplayPos[1] = Y; return this->ActivateNode(doubleDisplayPos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetActiveNodeToWorldPosition( double worldPos[3], double worldOrient[9]) { if (this->ActiveNode < 0 || static_cast(this->ActiveNode) >= this->Internal->Nodes.size()) { return 0; } // Check if this is a valid location if (!this->PointPlacer->ValidateWorldPosition(worldPos, worldOrient)) { return 0; } this->SetNthNodeWorldPositionInternal(this->ActiveNode, worldPos, worldOrient); return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetActiveNodeToWorldPosition(double worldPos[3]) { if (this->ActiveNode < 0 || static_cast(this->ActiveNode) >= this->Internal->Nodes.size()) { return 0; } // Check if this is a valid location if (!this->PointPlacer->ValidateWorldPosition(worldPos)) { return 0; } double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; this->SetNthNodeWorldPositionInternal(this->ActiveNode, worldPos, worldOrient); return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetActiveNodeToDisplayPosition(double displayPos[2]) { if (this->ActiveNode < 0 || static_cast(this->ActiveNode) >= this->Internal->Nodes.size()) { return 0; } double worldPos[3]; double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; // Compute the world position from the display position // based on the concrete representation's constraints // If this is not a valid display location return 0 if (!this->PointPlacer->ComputeWorldPosition(this->Renderer, displayPos, worldPos, worldOrient)) { return 0; } this->SetNthNodeWorldPositionInternal(this->ActiveNode, worldPos, worldOrient); return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetActiveNodeToDisplayPosition(int displayPos[2]) { double doubleDisplayPos[2]; doubleDisplayPos[0] = displayPos[0]; doubleDisplayPos[1] = displayPos[1]; return this->SetActiveNodeToDisplayPosition(doubleDisplayPos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetActiveNodeToDisplayPosition(int X, int Y) { double doubleDisplayPos[2]; doubleDisplayPos[0] = X; doubleDisplayPos[1] = Y; return this->SetActiveNodeToDisplayPosition(doubleDisplayPos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::ToggleActiveNodeSelected() { if (this->ActiveNode < 0 || static_cast(this->ActiveNode) >= this->Internal->Nodes.size()) { // Failed to toggle the value return 0; } this->Internal->Nodes[this->ActiveNode]->Selected = this->Internal->Nodes[this->ActiveNode]->Selected ? 0 : 1; this->NeedToRender = 1; this->Modified(); return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetNthNodeSelected(int n) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { // This case is considered not Selected. return 0; } return this->Internal->Nodes[n]->Selected; } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetNthNodeSelected(int n) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { // Failed. return 0; } int val = n > 0 ? 1 : 0; if (this->Internal->Nodes[n]->Selected != val) { this->Internal->Nodes[n]->Selected = val; this->NeedToRender = 1; this->Modified(); } return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetActiveNodeSelected() { return this->GetNthNodeSelected(this->ActiveNode); } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetActiveNodeWorldPosition(double pos[3]) { return this->GetNthNodeWorldPosition(this->ActiveNode, pos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetActiveNodeWorldOrientation(double orient[9]) { return this->GetNthNodeWorldOrientation(this->ActiveNode, orient); } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetActiveNodeDisplayPosition(double pos[2]) { return this->GetNthNodeDisplayPosition(this->ActiveNode, pos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetNumberOfNodes() { return static_cast(this->Internal->Nodes.size()); } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetNumberOfIntermediatePoints(int n) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { return 0; } return static_cast(this->Internal->Nodes[n]->Points.size()); } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetIntermediatePointWorldPosition(int n, int idx, double point[3]) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { return 0; } if (idx < 0 || static_cast(idx) >= this->Internal->Nodes[n]->Points.size()) { return 0; } point[0] = this->Internal->Nodes[n]->Points[idx]->WorldPosition[0]; point[1] = this->Internal->Nodes[n]->Points[idx]->WorldPosition[1]; point[2] = this->Internal->Nodes[n]->Points[idx]->WorldPosition[2]; return 1; } //------------------------------------------------------------------------------ // The display position for a given world position must be re-computed // from the world positions... It should not be queried from the renderer // whose camera position may have changed int vtkContourRepresentation::GetNthNodeDisplayPosition(int n, double displayPos[2]) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { return 0; } double pos[4]; pos[0] = this->Internal->Nodes[n]->WorldPosition[0]; pos[1] = this->Internal->Nodes[n]->WorldPosition[1]; pos[2] = this->Internal->Nodes[n]->WorldPosition[2]; pos[3] = 1.0; this->Renderer->SetWorldPoint(pos); this->Renderer->WorldToDisplay(); this->Renderer->GetDisplayPoint(pos); displayPos[0] = pos[0]; displayPos[1] = pos[1]; return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetNthNodeWorldPosition(int n, double worldPos[3]) { if (vtkContourRepresentationNode* node = this->GetNthNode(n)) { worldPos[0] = node->WorldPosition[0]; worldPos[1] = node->WorldPosition[1]; worldPos[2] = node->WorldPosition[2]; return 1; } return 0; } //------------------------------------------------------------------------------ vtkContourRepresentationNode* vtkContourRepresentation::GetNthNode(int n) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { return nullptr; } return this->Internal->Nodes[n]; } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetNthNodeWorldOrientation(int n, double worldOrient[9]) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { return 0; } memcpy(worldOrient, this->Internal->Nodes[n]->WorldOrientation, 9 * sizeof(double)); return 1; } //------------------------------------------------------------------------------ void vtkContourRepresentation::SetNthNodeWorldPositionInternal( int n, double worldPos[3], double worldOrient[9]) { this->Internal->Nodes[n]->WorldPosition[0] = worldPos[0]; this->Internal->Nodes[n]->WorldPosition[1] = worldPos[1]; this->Internal->Nodes[n]->WorldPosition[2] = worldPos[2]; this->GetRendererComputedDisplayPositionFromWorldPosition( worldPos, worldOrient, this->Internal->Nodes[n]->NormalizedDisplayPosition); this->Renderer->DisplayToNormalizedDisplay(this->Internal->Nodes[n]->NormalizedDisplayPosition[0], this->Internal->Nodes[n]->NormalizedDisplayPosition[1]); memcpy(this->Internal->Nodes[n]->WorldOrientation, worldOrient, 9 * sizeof(double)); this->UpdateLines(n); this->NeedToRender = 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetNthNodeWorldPosition( int n, double worldPos[3], double worldOrient[9]) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { return 0; } // Check if this is a valid location if (!this->PointPlacer->ValidateWorldPosition(worldPos, worldOrient)) { return 0; } this->SetNthNodeWorldPositionInternal(n, worldPos, worldOrient); return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetNthNodeWorldPosition(int n, double worldPos[3]) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { return 0; } // Check if this is a valid location if (!this->PointPlacer->ValidateWorldPosition(worldPos)) { return 0; } double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; this->SetNthNodeWorldPositionInternal(n, worldPos, worldOrient); return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetNthNodeDisplayPosition(int n, double displayPos[2]) { double worldPos[3]; double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; // Compute the world position from the display position // based on the concrete representation's constraints // If this is not a valid display location return 0 if (!this->PointPlacer->ComputeWorldPosition(this->Renderer, displayPos, worldPos, worldOrient)) { return 0; } return this->SetNthNodeWorldPosition(n, worldPos, worldOrient); } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetNthNodeDisplayPosition(int n, int displayPos[2]) { double doubleDisplayPos[2]; doubleDisplayPos[0] = displayPos[0]; doubleDisplayPos[1] = displayPos[1]; return this->SetNthNodeDisplayPosition(n, doubleDisplayPos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::SetNthNodeDisplayPosition(int n, int X, int Y) { double doubleDisplayPos[2]; doubleDisplayPos[0] = X; doubleDisplayPos[1] = Y; return this->SetNthNodeDisplayPosition(n, doubleDisplayPos); } //------------------------------------------------------------------------------ int vtkContourRepresentation::FindClosestPointOnContour( int X, int Y, double closestWorldPos[3], int* idx) { // Make a line out of this viewing ray double p1[4], p2[4], *p3 = nullptr, *p4 = nullptr; double tmp1[4], tmp2[4]; tmp1[0] = X; tmp1[1] = Y; tmp1[2] = 0.0; this->Renderer->SetDisplayPoint(tmp1); this->Renderer->DisplayToWorld(); this->Renderer->GetWorldPoint(p1); tmp1[2] = 1.0; this->Renderer->SetDisplayPoint(tmp1); this->Renderer->DisplayToWorld(); this->Renderer->GetWorldPoint(p2); double closestDistance2 = VTK_DOUBLE_MAX; int closestNode = 0; // compute a world tolerance based on pixel // tolerance on the focal plane double fp[4]; this->Renderer->GetActiveCamera()->GetFocalPoint(fp); fp[3] = 1.0; this->Renderer->SetWorldPoint(fp); this->Renderer->WorldToDisplay(); this->Renderer->GetDisplayPoint(tmp1); tmp1[0] = 0; tmp1[1] = 0; this->Renderer->SetDisplayPoint(tmp1); this->Renderer->DisplayToWorld(); this->Renderer->GetWorldPoint(tmp2); tmp1[0] = this->PixelTolerance; this->Renderer->SetDisplayPoint(tmp1); this->Renderer->DisplayToWorld(); this->Renderer->GetWorldPoint(tmp1); double wt2 = vtkMath::Distance2BetweenPoints(tmp1, tmp2); // Now loop through all lines and look for closest one within tolerance for (unsigned int i = 0; i < this->Internal->Nodes.size(); i++) { for (unsigned int j = 0; j <= this->Internal->Nodes[i]->Points.size(); j++) { if (j == 0) { p3 = this->Internal->Nodes[i]->WorldPosition; if (!this->Internal->Nodes[i]->Points.empty()) { p4 = this->Internal->Nodes[i]->Points[j]->WorldPosition; } else { if (i < this->Internal->Nodes.size() - 1) { p4 = this->Internal->Nodes[i + 1]->WorldPosition; } else if (this->ClosedLoop) { p4 = this->Internal->Nodes[0]->WorldPosition; } } } else if (j == this->Internal->Nodes[i]->Points.size()) { p3 = this->Internal->Nodes[i]->Points[j - 1]->WorldPosition; if (i < this->Internal->Nodes.size() - 1) { p4 = this->Internal->Nodes[i + 1]->WorldPosition; } else if (this->ClosedLoop) { p4 = this->Internal->Nodes[0]->WorldPosition; } else { // Shouldn't be able to get here (only if we don't have // a closed loop but we do have intermediate points after // the last node - contradictary conditions) continue; } } else { p3 = this->Internal->Nodes[i]->Points[j - 1]->WorldPosition; p4 = this->Internal->Nodes[i]->Points[j]->WorldPosition; } // Now we have the four points - check closest intersection double u, v; if (vtkLine::Intersection(p1, p2, p3, p4, u, v)) { double p5[3], p6[3]; p5[0] = p1[0] + u * (p2[0] - p1[0]); p5[1] = p1[1] + u * (p2[1] - p1[1]); p5[2] = p1[2] + u * (p2[2] - p1[2]); p6[0] = p3[0] + v * (p4[0] - p3[0]); p6[1] = p3[1] + v * (p4[1] - p3[1]); p6[2] = p3[2] + v * (p4[2] - p3[2]); double d = vtkMath::Distance2BetweenPoints(p5, p6); if (d < wt2 && d < closestDistance2) { closestWorldPos[0] = p6[0]; closestWorldPos[1] = p6[1]; closestWorldPos[2] = p6[2]; closestDistance2 = d; closestNode = static_cast(i); } } else { double d = vtkLine::DistanceToLine(p3, p1, p2); if (d < wt2 && d < closestDistance2) { closestWorldPos[0] = p3[0]; closestWorldPos[1] = p3[1]; closestWorldPos[2] = p3[2]; closestDistance2 = d; closestNode = static_cast(i); } d = vtkLine::DistanceToLine(p4, p1, p2); if (d < wt2 && d < closestDistance2) { closestWorldPos[0] = p4[0]; closestWorldPos[1] = p4[1]; closestWorldPos[2] = p4[2]; closestDistance2 = d; closestNode = static_cast(i); } } } } if (closestDistance2 < VTK_DOUBLE_MAX) { if (closestNode < this->GetNumberOfNodes() - 1) { *idx = closestNode + 1; return 1; } else if (this->ClosedLoop) { *idx = 0; return 1; } } return 0; } //------------------------------------------------------------------------------ int vtkContourRepresentation::AddNodeOnContour(int X, int Y) { int idx; double worldPos[3]; double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; // Compute the world position from the display position // based on the concrete representation's constraints // If this is not a valid display location return 0 double displayPos[2]; displayPos[0] = X; displayPos[1] = Y; if (!this->PointPlacer->ComputeWorldPosition(this->Renderer, displayPos, worldPos, worldOrient)) { return 0; } double pos[3]; if (!this->FindClosestPointOnContour(X, Y, pos, &idx)) { return 0; } if (!this->PointPlacer->ComputeWorldPosition( this->Renderer, displayPos, pos, worldPos, worldOrient)) { return 0; } // Add a new point at this position vtkContourRepresentationNode* node = new vtkContourRepresentationNode; node->WorldPosition[0] = worldPos[0]; node->WorldPosition[1] = worldPos[1]; node->WorldPosition[2] = worldPos[2]; node->Selected = 0; this->GetRendererComputedDisplayPositionFromWorldPosition( worldPos, worldOrient, node->NormalizedDisplayPosition); this->Renderer->DisplayToNormalizedDisplay( node->NormalizedDisplayPosition[0], node->NormalizedDisplayPosition[1]); memcpy(node->WorldOrientation, worldOrient, 9 * sizeof(double)); this->Internal->Nodes.insert(this->Internal->Nodes.begin() + idx, node); this->UpdateLines(idx); this->NeedToRender = 1; return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::DeleteNthNode(int n) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { return 0; } for (unsigned int j = 0; j < this->Internal->Nodes[n]->Points.size(); j++) { delete this->Internal->Nodes[n]->Points[j]; } this->Internal->Nodes[n]->Points.clear(); delete this->Internal->Nodes[n]; this->Internal->Nodes.erase(this->Internal->Nodes.begin() + n); if (n) { this->UpdateLines(n - 1); } else { this->UpdateLines(this->GetNumberOfNodes() - 1); } this->NeedToRender = 1; return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::DeleteActiveNode() { return this->DeleteNthNode(this->ActiveNode); } //------------------------------------------------------------------------------ int vtkContourRepresentation::DeleteLastNode() { return this->DeleteNthNode(static_cast(this->Internal->Nodes.size()) - 1); } //------------------------------------------------------------------------------ void vtkContourRepresentation::SetClosedLoop(vtkTypeBool val) { if (this->ClosedLoop != val) { this->ClosedLoop = val; this->UpdateLines(this->GetNumberOfNodes() - 1); this->NeedToRender = 1; this->Modified(); } } //------------------------------------------------------------------------------ void vtkContourRepresentation::UpdateLines(int index) { int indices[2]; if (this->LineInterpolator) { vtkIntArray* arr = vtkIntArray::New(); this->LineInterpolator->GetSpan(index, arr, this); int nNodes = arr->GetNumberOfTuples(); for (int i = 0; i < nNodes; i++) { arr->GetTypedTuple(i, indices); this->UpdateLine(indices[0], indices[1]); } arr->Delete(); } // A check to make sure that we have no line segments in // the last node if the loop is not closed if (!this->ClosedLoop && this->GetNumberOfNodes() > 0) { int idx = static_cast(this->Internal->Nodes.size()) - 1; for (unsigned int j = 0; j < this->Internal->Nodes[idx]->Points.size(); j++) { delete this->Internal->Nodes[idx]->Points[j]; } this->Internal->Nodes[idx]->Points.clear(); } this->BuildLines(); this->RebuildLocator = true; } //------------------------------------------------------------------------------ int vtkContourRepresentation::AddIntermediatePointWorldPosition(int n, double pos[3]) { return this->AddIntermediatePointWorldPosition(n, pos, 0); } //------------------------------------------------------------------------------ int vtkContourRepresentation ::AddIntermediatePointWorldPosition( int n, double pos[3], vtkIdType ptId) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { return 0; } vtkContourRepresentationPoint* point = new vtkContourRepresentationPoint; point->WorldPosition[0] = pos[0]; point->WorldPosition[1] = pos[1]; point->WorldPosition[2] = pos[2]; point->PointId = ptId; double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; this->GetRendererComputedDisplayPositionFromWorldPosition( pos, worldOrient, point->NormalizedDisplayPosition); this->Renderer->DisplayToNormalizedDisplay( point->NormalizedDisplayPosition[0], point->NormalizedDisplayPosition[1]); this->Internal->Nodes[n]->Points.push_back(point); return 1; } //------------------------------------------------------------------------------ int vtkContourRepresentation::GetNthNodeSlope(int n, double slope[3]) { if (n < 0 || static_cast(n) >= this->Internal->Nodes.size()) { return 0; } int idx1, idx2; if (n == 0 && !this->ClosedLoop) { idx1 = 0; idx2 = 1; } else if (n == this->GetNumberOfNodes() - 1 && !this->ClosedLoop) { idx1 = this->GetNumberOfNodes() - 2; idx2 = idx1 + 1; } else { idx1 = n - 1; idx2 = n + 1; if (idx1 < 0) { idx1 += this->GetNumberOfNodes(); } if (idx2 >= this->GetNumberOfNodes()) { idx2 -= this->GetNumberOfNodes(); } } slope[0] = this->Internal->Nodes[idx2]->WorldPosition[0] - this->Internal->Nodes[idx1]->WorldPosition[0]; slope[1] = this->Internal->Nodes[idx2]->WorldPosition[1] - this->Internal->Nodes[idx1]->WorldPosition[1]; slope[2] = this->Internal->Nodes[idx2]->WorldPosition[2] - this->Internal->Nodes[idx1]->WorldPosition[2]; vtkMath::Normalize(slope); return 1; } //------------------------------------------------------------------------------ void vtkContourRepresentation::UpdateLine(int idx1, int idx2) { if (!this->LineInterpolator) { return; } // Clear all the points at idx1 for (unsigned int j = 0; j < this->Internal->Nodes[idx1]->Points.size(); j++) { delete this->Internal->Nodes[idx1]->Points[j]; } this->Internal->Nodes[idx1]->Points.clear(); this->LineInterpolator->InterpolateLine(this->Renderer, this, idx1, idx2); } //------------------------------------------------------------------------------ int vtkContourRepresentation::ComputeInteractionState( int vtkNotUsed(X), int vtkNotUsed(Y), int vtkNotUsed(modified)) { return this->InteractionState; } //------------------------------------------------------------------------------ int vtkContourRepresentation::UpdateContour() { this->PointPlacer->UpdateInternalState(); // even if just the camera has moved we need to mark the locator // as needing to be rebuilt if (this->Locator->GetMTime() < this->Renderer->GetActiveCamera()->GetMTime()) { this->RebuildLocator = true; } if (this->ContourBuildTime > this->PointPlacer->GetMTime()) { // Contour does not need to be rebuilt return 0; } unsigned int i; for (i = 0; i < this->Internal->Nodes.size(); i++) { this->PointPlacer->UpdateWorldPosition(this->Renderer, this->Internal->Nodes[i]->WorldPosition, this->Internal->Nodes[i]->WorldOrientation); } for (i = 0; (i + 1) < this->Internal->Nodes.size(); i++) { this->UpdateLine(i, i + 1); } if (this->ClosedLoop) { this->UpdateLine(static_cast(this->Internal->Nodes.size()) - 1, 0); } this->BuildLines(); this->RebuildLocator = true; this->ContourBuildTime.Modified(); return 1; } //------------------------------------------------------------------------------ void vtkContourRepresentation ::GetRendererComputedDisplayPositionFromWorldPosition( double worldPos[3], double worldOrient[9], int displayPos[2]) { double dispPos[2]; dispPos[0] = static_cast(displayPos[0]); dispPos[1] = static_cast(displayPos[1]); this->GetRendererComputedDisplayPositionFromWorldPosition(worldPos, worldOrient, dispPos); displayPos[0] = static_cast(dispPos[0]); displayPos[1] = static_cast(dispPos[1]); } //------------------------------------------------------------------------------ void vtkContourRepresentation ::GetRendererComputedDisplayPositionFromWorldPosition( double worldPos[3], double* vtkNotUsed(worldOrient[9]), double displayPos[2]) { double pos[4]; pos[0] = worldPos[0]; pos[1] = worldPos[1]; pos[2] = worldPos[2]; pos[3] = 1.0; this->Renderer->SetWorldPoint(pos); this->Renderer->WorldToDisplay(); this->Renderer->GetDisplayPoint(pos); displayPos[0] = pos[0]; displayPos[1] = pos[1]; } //------------------------------------------------------------------------------ void vtkContourRepresentation::Initialize(vtkPolyData* pd) { // For backward compatibility this->InitializeContour(pd, nullptr); } //------------------------------------------------------------------------------ void vtkContourRepresentation ::Initialize(vtkPolyData* pd, vtkIdList* nodeIds) { if (!nodeIds) { this->Initialize(pd); return; } this->InitializeContour(pd, nodeIds); } //------------------------------------------------------------------------------ void vtkContourRepresentation ::InitializeContour(vtkPolyData* pd, vtkIdList* nodeIds) { vtkPoints* points = pd->GetPoints(); vtkIdType nPoints = points->GetNumberOfPoints(); if (nPoints <= 0) { return; // Yeah right.. build from nothing ! } // Clear all existing nodes. for (unsigned int i = 0; i < this->Internal->Nodes.size(); i++) { for (unsigned int j = 0; j < this->Internal->Nodes[i]->Points.size(); j++) { delete this->Internal->Nodes[i]->Points[j]; } this->Internal->Nodes[i]->Points.clear(); delete this->Internal->Nodes[i]; } this->Internal->Nodes.clear(); vtkPolyData* tmpPoints = vtkPolyData::New(); tmpPoints->DeepCopy(pd); this->Locator->SetDataSet(tmpPoints); tmpPoints->Delete(); // reserver space in memory to speed up vector push_back this->Internal->Nodes.reserve(nPoints); vtkIdList* pointIds = pd->GetCell(0)->GetPointIds(); // Get the worldOrient from the point placer double ref[3], displayPos[2], worldPos[3]; double worldOrient[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; ref[0] = 0.0; ref[1] = 0.0; ref[2] = 0.0; displayPos[0] = 0.0; displayPos[1] = 0.0; this->PointPlacer->ComputeWorldPosition(this->Renderer, displayPos, ref, worldPos, worldOrient); // Add nodes without calling rebuild lines // to improve performance dramatically(~15x) on large datasets double* pos; for (vtkIdType i = 0; i < nPoints; i++) { pos = points->GetPoint(i); this->GetRendererComputedDisplayPositionFromWorldPosition(pos, worldOrient, displayPos); // Add a new point at this position vtkContourRepresentationNode* node = new vtkContourRepresentationNode; node->WorldPosition[0] = pos[0]; node->WorldPosition[1] = pos[1]; node->WorldPosition[2] = pos[2]; node->Selected = 0; // Give the point placer a chance to update the node if (nodeIds && nodeIds->GetNumberOfIds() == nPoints) { this->PointPlacer->UpdateNodeWorldPosition(pos, nodeIds->GetId(i)); } node->NormalizedDisplayPosition[0] = displayPos[0]; node->NormalizedDisplayPosition[1] = displayPos[1]; this->Renderer->DisplayToNormalizedDisplay( node->NormalizedDisplayPosition[0], node->NormalizedDisplayPosition[1]); memcpy(node->WorldOrientation, worldOrient, 9 * sizeof(double)); this->Internal->Nodes.push_back(node); if (this->LineInterpolator && this->GetNumberOfNodes() > 1) { // Give the line interpolator a chance to update the node. int didNodeChange = this->LineInterpolator->UpdateNode( this->Renderer, this, node->WorldPosition, this->GetNumberOfNodes() - 1); // Give the point placer a chance to validate the updated node. If its // not valid, discard the LineInterpolator's change. if (didNodeChange && !this->PointPlacer->ValidateWorldPosition(node->WorldPosition, worldOrient)) { node->WorldPosition[0] = worldPos[0]; node->WorldPosition[1] = worldPos[1]; node->WorldPosition[2] = worldPos[2]; } } } if (pointIds->GetNumberOfIds() > nPoints) { this->ClosedLoopOn(); } // Update the contour representation from the nodes using the line interpolator for (vtkIdType i = 1; i <= nPoints; ++i) { this->UpdateLines(i); } this->BuildRepresentation(); // Show the contour. this->VisibilityOn(); } //------------------------------------------------------------------------------ void vtkContourRepresentation::BuildLocator() { if (!this->RebuildLocator && !this->NeedToRender) { // rebuild if rebuildLocator or needtorender are true return; } vtkIdType size = (vtkIdType)this->Internal->Nodes.size(); vtkPoints* points = vtkPoints::New(); points->SetNumberOfPoints(size); // setup up the matrixes needed to transform // world to display. We are going to do this manually // as calling the renderer will create a new matrix for each call vtkMatrix4x4* matrix = vtkMatrix4x4::New(); matrix->DeepCopy(this->Renderer->GetActiveCamera()->GetCompositeProjectionTransformMatrix( this->Renderer->GetTiledAspectRatio(), 0, 1)); // viewport info double viewPortRatio[2]; int sizex, sizey; /* get physical window dimensions */ if (this->Renderer->GetVTKWindow()) { double* viewPort = this->Renderer->GetViewport(); sizex = this->Renderer->GetVTKWindow()->GetSize()[0]; sizey = this->Renderer->GetVTKWindow()->GetSize()[1]; viewPortRatio[0] = (sizex * (viewPort[2] - viewPort[0])) / 2.0 + sizex * viewPort[0]; viewPortRatio[1] = (sizey * (viewPort[3] - viewPort[1])) / 2.0 + sizey * viewPort[1]; } else { // can't compute the locator without a vtk window return; } double view[4]; double pos[3] = { 0, 0, 0 }; double* wp; for (vtkIdType i = 0; i < size; ++i) { wp = this->Internal->Nodes[i]->WorldPosition; pos[0] = this->Internal->Nodes[i]->WorldPosition[0]; pos[1] = this->Internal->Nodes[i]->WorldPosition[1]; pos[2] = this->Internal->Nodes[i]->WorldPosition[2]; // convert from world to view view[0] = wp[0] * matrix->Element[0][0] + wp[1] * matrix->Element[0][1] + wp[2] * matrix->Element[0][2] + matrix->Element[0][3]; view[1] = wp[0] * matrix->Element[1][0] + wp[1] * matrix->Element[1][1] + wp[2] * matrix->Element[1][2] + matrix->Element[1][3]; view[2] = wp[0] * matrix->Element[2][0] + wp[1] * matrix->Element[2][1] + wp[2] * matrix->Element[2][2] + matrix->Element[2][3]; view[3] = wp[0] * matrix->Element[3][0] + wp[1] * matrix->Element[3][1] + wp[2] * matrix->Element[3][2] + matrix->Element[3][3]; if (view[3] != 0.0) { pos[0] = view[0] / view[3]; pos[1] = view[1] / view[3]; } // now from view to display pos[0] = (pos[0] + 1.0) * viewPortRatio[0]; pos[1] = (pos[1] + 1.0) * viewPortRatio[1]; pos[2] = 0; points->InsertPoint(i, pos); } matrix->Delete(); vtkPolyData* tmp = vtkPolyData::New(); tmp->SetPoints(points); this->Locator->SetDataSet(tmp); tmp->FastDelete(); points->FastDelete(); // we fully updated the display locator this->RebuildLocator = false; } //------------------------------------------------------------------------------ void vtkContourRepresentation::SetShowSelectedNodes(vtkTypeBool flag) { if (this->ShowSelectedNodes != flag) { this->ShowSelectedNodes = flag; this->Modified(); } } //------------------------------------------------------------------------------ void vtkContourRepresentation::PrintSelf(ostream& os, vtkIndent indent) { // Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h this->Superclass::PrintSelf(os, indent); os << indent << "Pixel Tolerance: " << this->PixelTolerance << "\n"; os << indent << "World Tolerance: " << this->WorldTolerance << "\n"; os << indent << "Closed Loop: " << (this->ClosedLoop ? "On\n" : "Off\n"); os << indent << "ShowSelectedNodes: " << this->ShowSelectedNodes << endl; os << indent << "Rebuild Locator: " << (this->RebuildLocator ? "On" : "Off") << endl; os << indent << "Current Operation: "; if (this->CurrentOperation == vtkContourRepresentation::Inactive) { os << "Inactive\n"; } else { os << "Translate\n"; } os << indent << "Line Interpolator: " << this->LineInterpolator << "\n"; os << indent << "Point Placer: " << this->PointPlacer << "\n"; }