/*========================================================================= Program: Visualization Toolkit Module: vtkConstrainedPointHandleRepresentation.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 "vtkConstrainedPointHandleRepresentation.h" #include "vtkActor.h" #include "vtkAssemblyPath.h" #include "vtkCamera.h" #include "vtkCellPicker.h" #include "vtkCleanPolyData.h" #include "vtkCoordinate.h" #include "vtkCursor2D.h" #include "vtkCylinderSource.h" #include "vtkDoubleArray.h" #include "vtkGlyph3D.h" #include "vtkInteractorObserver.h" #include "vtkLine.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPlane.h" #include "vtkPlaneCollection.h" #include "vtkPlanes.h" #include "vtkPointData.h" #include "vtkPoints.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkProperty.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderer.h" #include "vtkSmartPointer.h" #include "vtkTransform.h" #include "vtkTransformPolyDataFilter.h" vtkStandardNewMacro(vtkConstrainedPointHandleRepresentation); vtkCxxSetObjectMacro(vtkConstrainedPointHandleRepresentation, ObliquePlane, vtkPlane); vtkCxxSetObjectMacro(vtkConstrainedPointHandleRepresentation, BoundingPlanes, vtkPlaneCollection); //------------------------------------------------------------------------------ vtkConstrainedPointHandleRepresentation::vtkConstrainedPointHandleRepresentation() { // Initialize state this->InteractionState = vtkHandleRepresentation::Outside; this->ProjectionPosition = 0; this->ObliquePlane = nullptr; this->ProjectionNormal = vtkConstrainedPointHandleRepresentation::ZAxis; this->CursorShape = nullptr; this->ActiveCursorShape = nullptr; // Represent the position of the cursor this->FocalPoint = vtkPoints::New(); this->FocalPoint->SetNumberOfPoints(1); this->FocalPoint->SetPoint(0, 0.0, 0.0, 0.0); vtkSmartPointer normals = vtkSmartPointer::New(); normals->SetNumberOfComponents(3); normals->SetNumberOfTuples(1); double normal[3]; this->GetProjectionNormal(normal); normals->SetTuple(0, normal); this->FocalData = vtkPolyData::New(); this->FocalData->SetPoints(this->FocalPoint); this->FocalData->GetPointData()->SetNormals(normals); this->Glypher = vtkGlyph3D::New(); this->Glypher->SetInputData(this->FocalData); this->Glypher->SetVectorModeToUseNormal(); this->Glypher->OrientOn(); this->Glypher->ScalingOn(); this->Glypher->SetScaleModeToDataScalingOff(); this->Glypher->SetScaleFactor(1.0); // The transformation of the cursor will be done via vtkGlyph3D // By default a vtkCursor2D will be used to define the cursor shape vtkSmartPointer cursor2D = vtkSmartPointer::New(); cursor2D->AllOff(); cursor2D->PointOn(); cursor2D->Update(); this->SetCursorShape(cursor2D->GetOutput()); vtkSmartPointer cylinder = vtkSmartPointer::New(); cylinder->SetResolution(64); cylinder->SetRadius(1.0); cylinder->SetHeight(0.0); cylinder->CappingOff(); cylinder->SetCenter(0, 0, 0); vtkSmartPointer clean = vtkSmartPointer::New(); clean->PointMergingOn(); clean->CreateDefaultLocator(); clean->SetInputConnection(0, cylinder->GetOutputPort(0)); vtkSmartPointer t = vtkSmartPointer::New(); t->RotateZ(90.0); vtkSmartPointer tpd = vtkSmartPointer::New(); tpd->SetInputConnection(0, clean->GetOutputPort(0)); tpd->SetTransform(t); tpd->Update(); this->SetActiveCursorShape(tpd->GetOutput()); this->Mapper = vtkPolyDataMapper::New(); this->Mapper->SetInputConnection(this->Glypher->GetOutputPort()); vtkPolyDataMapper::SetResolveCoincidentTopologyToPolygonOffset(); this->Mapper->ScalarVisibilityOff(); // Set up the initial properties this->CreateDefaultProperties(); this->Actor = vtkActor::New(); this->Actor->SetMapper(this->Mapper); this->Actor->SetProperty(this->Property); this->InteractionOffset[0] = 0.0; this->InteractionOffset[1] = 0.0; this->BoundingPlanes = nullptr; } //------------------------------------------------------------------------------ vtkConstrainedPointHandleRepresentation::~vtkConstrainedPointHandleRepresentation() { this->FocalPoint->Delete(); this->FocalData->Delete(); this->SetCursorShape(nullptr); this->SetActiveCursorShape(nullptr); this->RemoveAllBoundingPlanes(); this->Glypher->Delete(); this->Mapper->Delete(); this->Actor->Delete(); this->Property->Delete(); this->SelectedProperty->Delete(); this->ActiveProperty->Delete(); if (this->ObliquePlane) { this->ObliquePlane->UnRegister(this); this->ObliquePlane = nullptr; } if (this->BoundingPlanes) { this->BoundingPlanes->UnRegister(this); } } //------------------------------------------------------------------------------ int vtkConstrainedPointHandleRepresentation::CheckConstraint( vtkRenderer* renderer, double eventPos[2]) { double worldPos[3]; double tolerance = 0.0; return this->GetIntersectionPosition(eventPos, worldPos, tolerance, renderer); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::SetProjectionPosition(double position) { if (this->ProjectionPosition != position) { this->ProjectionPosition = position; this->Modified(); } } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::SetCursorShape(vtkPolyData* shape) { if (shape != this->CursorShape) { if (this->CursorShape) { this->CursorShape->Delete(); } this->CursorShape = shape; if (this->CursorShape) { this->CursorShape->Register(this); this->Glypher->SetSourceData(this->CursorShape); } this->Modified(); } } //------------------------------------------------------------------------------ vtkPolyData* vtkConstrainedPointHandleRepresentation::GetCursorShape() { return this->CursorShape; } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::SetActiveCursorShape(vtkPolyData* shape) { if (shape != this->ActiveCursorShape) { if (this->ActiveCursorShape) { this->ActiveCursorShape->Delete(); } this->ActiveCursorShape = shape; if (this->CursorShape) { this->ActiveCursorShape->Register(this); } this->Modified(); } } //------------------------------------------------------------------------------ vtkPolyData* vtkConstrainedPointHandleRepresentation::GetActiveCursorShape() { return this->ActiveCursorShape; } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::AddBoundingPlane(vtkPlane* plane) { if (this->BoundingPlanes == nullptr) { this->BoundingPlanes = vtkPlaneCollection::New(); this->BoundingPlanes->Register(this); this->BoundingPlanes->Delete(); } this->BoundingPlanes->AddItem(plane); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::RemoveBoundingPlane(vtkPlane* plane) { if (this->BoundingPlanes) { this->BoundingPlanes->RemoveItem(plane); } } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::RemoveAllBoundingPlanes() { if (this->BoundingPlanes) { this->BoundingPlanes->RemoveAllItems(); this->BoundingPlanes->Delete(); this->BoundingPlanes = nullptr; } } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::SetBoundingPlanes(vtkPlanes* planes) { if (!planes) { return; } vtkPlane* plane; int numPlanes = planes->GetNumberOfPlanes(); this->RemoveAllBoundingPlanes(); for (int i = 0; i < numPlanes; i++) { plane = vtkPlane::New(); planes->GetPlane(i, plane); this->AddBoundingPlane(plane); plane->Delete(); } } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::SetRenderer(vtkRenderer* ren) { this->WorldPosition->SetViewport(ren); this->Superclass::SetRenderer(ren); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::SetPosition(double x, double y, double z) { this->WorldPosition->SetValue(x, y, z); this->FocalPoint->SetPoint(0, x, y, z); this->FocalPoint->Modified(); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::SetDisplayPosition(double eventPos[3]) { double worldPos[3]; this->DisplayPosition->SetValue(eventPos); if (this->Renderer) { if (this->GetIntersectionPosition(eventPos, worldPos)) { this->SetPosition(worldPos); } } this->DisplayPositionTime.Modified(); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::SetPosition(double xyz[3]) { this->SetPosition(xyz[0], xyz[1], xyz[2]); } //------------------------------------------------------------------------------ double* vtkConstrainedPointHandleRepresentation::GetPosition() { return this->FocalPoint->GetPoint(0); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::GetPosition(double xyz[3]) { this->FocalPoint->GetPoint(0, xyz); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::ShallowCopy(vtkProp* prop) { vtkConstrainedPointHandleRepresentation* rep = vtkConstrainedPointHandleRepresentation::SafeDownCast(prop); if (rep) { this->Property->DeepCopy(rep->GetProperty()); this->SelectedProperty->DeepCopy(rep->GetSelectedProperty()); this->ActiveProperty->DeepCopy(rep->GetActiveProperty()); this->ProjectionNormal = rep->GetProjectionNormal(); this->ProjectionPosition = rep->GetProjectionPosition(); this->SetObliquePlane(rep->GetObliquePlane()); this->SetBoundingPlanes(rep->GetBoundingPlanes()); } this->Superclass::ShallowCopy(prop); } //------------------------------------------------------------------------------ int vtkConstrainedPointHandleRepresentation::ComputeInteractionState( int X, int Y, int vtkNotUsed(modify)) { double pos[4], xyz[3]; this->FocalPoint->GetPoint(0, pos); pos[3] = 1.0; this->Renderer->SetWorldPoint(pos); this->Renderer->WorldToDisplay(); this->Renderer->GetDisplayPoint(pos); xyz[0] = static_cast(X); xyz[1] = static_cast(Y); xyz[2] = pos[2]; this->VisibilityOn(); double tol2 = this->Tolerance * this->Tolerance; if (vtkMath::Distance2BetweenPoints(xyz, pos) <= tol2) { this->InteractionState = vtkHandleRepresentation::Nearby; this->Glypher->SetSourceData(this->ActiveCursorShape); this->Actor->SetProperty(this->ActiveProperty); if (!this->ActiveCursorShape) { this->VisibilityOff(); } } else { this->InteractionState = vtkHandleRepresentation::Outside; this->Glypher->SetSourceData(this->CursorShape); this->Actor->SetProperty(this->Property); if (!this->CursorShape) { this->VisibilityOff(); } } return this->InteractionState; } //------------------------------------------------------------------------------ // Record the current event position, and the rectilinear wipe position. void vtkConstrainedPointHandleRepresentation::StartWidgetInteraction(double startEventPos[2]) { this->StartEventPosition[0] = startEventPos[0]; this->StartEventPosition[1] = startEventPos[1]; this->StartEventPosition[2] = 0.0; this->LastEventPosition[0] = startEventPos[0]; this->LastEventPosition[1] = startEventPos[1]; // How far is this in pixels from the position of this widget? // Maintain this during interaction such as translating (don't // force center of widget to snap to mouse position) // convert position to display coordinates double pos[3]; this->GetDisplayPosition(pos); this->InteractionOffset[0] = pos[0] - startEventPos[0]; this->InteractionOffset[1] = pos[1] - startEventPos[1]; } //------------------------------------------------------------------------------ // Based on the displacement vector (computed in display coordinates) and // the cursor state (which corresponds to which part of the widget has been // selected), the widget points are modified. // First construct a local coordinate system based on the display coordinates // of the widget. void vtkConstrainedPointHandleRepresentation::WidgetInteraction(double eventPos[2]) { // Process the motion if (this->InteractionState == vtkHandleRepresentation::Selecting || this->InteractionState == vtkHandleRepresentation::Translating) { this->Translate(eventPos); } else if (this->InteractionState == vtkHandleRepresentation::Scaling) { this->Scale(eventPos); } // Book keeping this->LastEventPosition[0] = eventPos[0]; this->LastEventPosition[1] = eventPos[1]; } //------------------------------------------------------------------------------ // Translate everything void vtkConstrainedPointHandleRepresentation::Translate(const double* eventPos) { double worldPos[3], prevWorldPos[3]; if (this->GetIntersectionPosition(eventPos, worldPos)) { this->GetWorldPosition(prevWorldPos); Superclass::Translate(prevWorldPos, worldPos); } else { // I really want to track the closest point here, // but I am postponing this at the moment.... } } //------------------------------------------------------------------------------ int vtkConstrainedPointHandleRepresentation::GetIntersectionPosition( const double eventPos[2], double worldPos[3], double tolerance, vtkRenderer* renderer) { double nearWorldPoint[4]; double farWorldPoint[4]; double tmp[3]; tmp[0] = eventPos[0] + this->InteractionOffset[0]; tmp[1] = eventPos[1] + this->InteractionOffset[1]; tmp[2] = 0.0; // near plane if (renderer == nullptr) { renderer = this->Renderer; } renderer->SetDisplayPoint(tmp); renderer->DisplayToWorld(); renderer->GetWorldPoint(nearWorldPoint); tmp[2] = 1.0; // far plane renderer->SetDisplayPoint(tmp); renderer->DisplayToWorld(); renderer->GetWorldPoint(farWorldPoint); double normal[3]; double origin[3]; this->GetProjectionNormal(normal); this->GetProjectionOrigin(origin); vtkSmartPointer picker = vtkSmartPointer::New(); picker->Pick(eventPos[0], eventPos[1], 0, renderer); vtkAssemblyPath* path = picker->GetPath(); if (path == nullptr) { return 0; } double pickPos[3]; picker->GetPickPosition(pickPos); if (this->BoundingPlanes) { vtkPlane* p; this->BoundingPlanes->InitTraversal(); while ((p = this->BoundingPlanes->GetNextItem())) { double v = p->EvaluateFunction(pickPos); if (v < tolerance) { return 0; } } } worldPos[0] = pickPos[0]; worldPos[1] = pickPos[1]; worldPos[2] = pickPos[2]; return 1; } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::GetProjectionNormal(double normal[3]) { switch (this->ProjectionNormal) { case vtkConstrainedPointHandleRepresentation::XAxis: normal[0] = 1.0; normal[1] = 0.0; normal[2] = 0.0; break; case vtkConstrainedPointHandleRepresentation::YAxis: normal[0] = 0.0; normal[1] = 1.0; normal[2] = 0.0; break; case vtkConstrainedPointHandleRepresentation::ZAxis: normal[0] = 0.0; normal[1] = 0.0; normal[2] = 1.0; break; case vtkConstrainedPointHandleRepresentation::Oblique: this->ObliquePlane->GetNormal(normal); break; } } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::GetProjectionOrigin(double origin[3]) { switch (this->ProjectionNormal) { case vtkConstrainedPointHandleRepresentation::XAxis: origin[0] = this->ProjectionPosition; origin[1] = 0.0; origin[2] = 0.0; break; case vtkConstrainedPointHandleRepresentation::YAxis: origin[0] = 0.0; origin[1] = this->ProjectionPosition; origin[2] = 0.0; break; case vtkConstrainedPointHandleRepresentation::ZAxis: origin[0] = 0.0; origin[1] = 0.0; origin[2] = this->ProjectionPosition; break; case vtkConstrainedPointHandleRepresentation::Oblique: this->ObliquePlane->GetOrigin(origin); break; } } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::Scale(const double* eventPos) { // Get the current scale factor double sf = this->Glypher->GetScaleFactor(); // Compute the scale factor const int* size = this->Renderer->GetSize(); double dPos = static_cast(eventPos[1] - this->LastEventPosition[1]); sf *= (1.0 + 2.0 * (dPos / size[1])); // scale factor of 2.0 is arbitrary // Scale the handle this->Glypher->SetScaleFactor(sf); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::Highlight(int highlight) { if (highlight) { this->Actor->SetProperty(this->SelectedProperty); } else { this->Actor->SetProperty(this->ActiveProperty); } } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::CreateDefaultProperties() { this->Property = vtkProperty::New(); this->Property->SetColor(1.0, 1.0, 1.0); this->Property->SetLineWidth(0.5); this->Property->SetPointSize(3); this->SelectedProperty = vtkProperty::New(); this->SelectedProperty->SetColor(0.0, 1.0, 1.0); this->SelectedProperty->SetRepresentationToWireframe(); this->SelectedProperty->SetAmbient(1.0); this->SelectedProperty->SetDiffuse(0.0); this->SelectedProperty->SetSpecular(0.0); this->SelectedProperty->SetLineWidth(2.0); this->ActiveProperty = vtkProperty::New(); this->ActiveProperty->SetColor(0.0, 1.0, 0.0); this->ActiveProperty->SetRepresentationToWireframe(); this->ActiveProperty->SetAmbient(1.0); this->ActiveProperty->SetDiffuse(0.0); this->ActiveProperty->SetSpecular(0.0); this->ActiveProperty->SetLineWidth(1.0); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::BuildRepresentation() { double normal[3]; this->GetProjectionNormal(normal); this->FocalData->GetPointData()->GetNormals()->SetTuple(0, normal); double* pos = this->WorldPosition->GetValue(); this->FocalPoint->SetPoint(0, pos[0], pos[1], pos[2]); this->FocalPoint->Modified(); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::GetActors(vtkPropCollection* pc) { this->Actor->GetActors(pc); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::ReleaseGraphicsResources(vtkWindow* win) { this->Actor->ReleaseGraphicsResources(win); } //------------------------------------------------------------------------------ int vtkConstrainedPointHandleRepresentation::RenderOverlay(vtkViewport* viewport) { return this->Actor->RenderOverlay(viewport); } //------------------------------------------------------------------------------ int vtkConstrainedPointHandleRepresentation::RenderOpaqueGeometry(vtkViewport* viewport) { return this->Actor->RenderOpaqueGeometry(viewport); } //------------------------------------------------------------------------------ int vtkConstrainedPointHandleRepresentation::RenderTranslucentPolygonalGeometry( vtkViewport* viewport) { return this->Actor->RenderTranslucentPolygonalGeometry(viewport); } //------------------------------------------------------------------------------ vtkTypeBool vtkConstrainedPointHandleRepresentation::HasTranslucentPolygonalGeometry() { return this->Actor->HasTranslucentPolygonalGeometry(); } //------------------------------------------------------------------------------ void vtkConstrainedPointHandleRepresentation::PrintSelf(ostream& os, vtkIndent indent) { // Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h this->Superclass::PrintSelf(os, indent); os << indent << "Projection Normal: "; if (this->ProjectionNormal == vtkConstrainedPointHandleRepresentation::XAxis) { os << "XAxis\n"; } else if (this->ProjectionNormal == vtkConstrainedPointHandleRepresentation::YAxis) { os << "YAxis\n"; } else if (this->ProjectionNormal == vtkConstrainedPointHandleRepresentation::ZAxis) { os << "ZAxis\n"; } else // if ( this->ProjectionNormal == vtkConstrainedPointHandleRepresentation::Oblique ) { os << "Oblique\n"; } os << indent << "Active Property: "; this->ActiveProperty->PrintSelf(os, indent.GetNextIndent()); os << indent << "Projection Position: " << this->ProjectionPosition << "\n"; os << indent << "Property: "; this->Property->PrintSelf(os, indent.GetNextIndent()); os << indent << "Selected Property: "; this->SelectedProperty->PrintSelf(os, indent.GetNextIndent()); os << indent << "Oblique Plane: "; if (this->ObliquePlane) { this->ObliquePlane->PrintSelf(os, indent.GetNextIndent()); } else { os << "(none)\n"; } os << indent << "Bounding Planes: "; if (this->BoundingPlanes) { this->BoundingPlanes->PrintSelf(os, indent.GetNextIndent()); } else { os << "(none)\n"; } }