/*========================================================================= Program: Visualization Toolkit Module: vtkBrokenLineWidget.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 "vtkBrokenLineWidget.h" #include "vtkActor.h" #include "vtkAssemblyNode.h" #include "vtkAssemblyPath.h" #include "vtkCallbackCommand.h" #include "vtkCamera.h" #include "vtkCellArray.h" #include "vtkCellPicker.h" #include "vtkLineSource.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPickingManager.h" #include "vtkPlaneSource.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkProperty.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderer.h" #include "vtkSphereSource.h" #include "vtkTransform.h" vtkStandardNewMacro(vtkBrokenLineWidget); vtkCxxSetObjectMacro(vtkBrokenLineWidget, HandleProperty, vtkProperty); vtkCxxSetObjectMacro(vtkBrokenLineWidget, SelectedHandleProperty, vtkProperty); vtkCxxSetObjectMacro(vtkBrokenLineWidget, LineProperty, vtkProperty); vtkCxxSetObjectMacro(vtkBrokenLineWidget, SelectedLineProperty, vtkProperty); vtkBrokenLineWidget::vtkBrokenLineWidget() { this->State = vtkBrokenLineWidget::Start; this->EventCallbackCommand->SetCallback(vtkBrokenLineWidget::ProcessEventsHandler); this->ProjectToPlane = 0; // default off this->ProjectionNormal = 0; // default YZ not used this->ProjectionPosition = 0.; this->PlaneSource = nullptr; // Does this widget respond to interaction? this->ProcessEvents = 1; // Default handle size factor this->HandleSizeFactor = 1.; // Build the representation of the widget // Default bounds to get started double bounds[6] = { -.5, .5, -.5, .5, -.5, .5 }; // Create the handles along a straight line within the bounds of a unit cube this->NumberOfHandles = 5; this->Handle = new vtkActor*[this->NumberOfHandles]; this->HandleGeometry = new vtkSphereSource*[this->NumberOfHandles]; double u; double x0 = bounds[0]; double x1 = bounds[1]; double y0 = bounds[2]; double y1 = bounds[3]; double z0 = bounds[4]; double z1 = bounds[5]; double x; double y; double z; vtkPoints* points = vtkPoints::New(VTK_DOUBLE); points->SetNumberOfPoints(this->NumberOfHandles); for (int i = 0; i < this->NumberOfHandles; ++i) { this->HandleGeometry[i] = vtkSphereSource::New(); this->HandleGeometry[i]->SetThetaResolution(16); this->HandleGeometry[i]->SetPhiResolution(8); vtkPolyDataMapper* handleMapper = vtkPolyDataMapper::New(); handleMapper->SetInputConnection(this->HandleGeometry[i]->GetOutputPort()); this->Handle[i] = vtkActor::New(); this->Handle[i]->SetMapper(handleMapper); handleMapper->Delete(); u = i / (this->NumberOfHandles - 1.); x = (1. - u) * x0 + u * x1; y = (1. - u) * y0 + u * y1; z = (1. - u) * z0 + u * z1; points->SetPoint(i, x, y, z); this->HandleGeometry[i]->SetCenter(x, y, z); } // Create the broken line this->LineSource = vtkLineSource::New(); this->LineSource->SetPoints(points); points->Delete(); // Represent the broken line this->LineMapper = vtkPolyDataMapper::New(); this->LineMapper->SetInputConnection(this->LineSource->GetOutputPort()); vtkPolyDataMapper::SetResolveCoincidentTopologyToPolygonOffset(); this->LineActor = vtkActor::New(); this->LineActor->SetMapper(this->LineMapper); // Initial creation of the widget, serves to initialize it this->PlaceFactor = 1.; this->PlaceWidget(bounds); // Manage the picking stuff this->HandlePicker = vtkCellPicker::New(); this->HandlePicker->SetTolerance(.005); for (int i = 0; i < this->NumberOfHandles; ++i) { this->HandlePicker->AddPickList(this->Handle[i]); } this->HandlePicker->PickFromListOn(); this->LinePicker = vtkCellPicker::New(); this->LinePicker->SetTolerance(.01); this->LinePicker->AddPickList(this->LineActor); this->LinePicker->PickFromListOn(); this->CurrentHandle = nullptr; this->CurrentHandleIndex = -1; this->Transform = vtkTransform::New(); // Set up the initial properties this->HandleProperty = nullptr; this->SelectedHandleProperty = nullptr; this->LineProperty = nullptr; this->SelectedLineProperty = nullptr; this->CreateDefaultProperties(); } vtkBrokenLineWidget::~vtkBrokenLineWidget() { this->LineActor->Delete(); this->LineMapper->Delete(); this->LineSource->Delete(); for (int i = 0; i < this->NumberOfHandles; ++i) { this->HandleGeometry[i]->Delete(); this->Handle[i]->Delete(); } delete[] this->Handle; delete[] this->HandleGeometry; this->HandlePicker->Delete(); this->LinePicker->Delete(); if (this->HandleProperty) { this->HandleProperty->Delete(); } if (this->SelectedHandleProperty) { this->SelectedHandleProperty->Delete(); } if (this->LineProperty) { this->LineProperty->Delete(); } if (this->SelectedLineProperty) { this->SelectedLineProperty->Delete(); } this->Transform->Delete(); } void vtkBrokenLineWidget::SetHandlePosition(int handle, double x, double y, double z) { if (handle < 0 || handle >= this->NumberOfHandles) { vtkErrorMacro(<< "vtkBrokenLineWidget: handle index out of range."); return; } this->HandleGeometry[handle]->SetCenter(x, y, z); this->HandleGeometry[handle]->Update(); if (this->ProjectToPlane) { this->ProjectPointsToPlane(); } this->BuildRepresentation(); } void vtkBrokenLineWidget::SetHandlePosition(int handle, double xyz[3]) { this->SetHandlePosition(handle, xyz[0], xyz[1], xyz[2]); } void vtkBrokenLineWidget::GetHandlePosition(int handle, double xyz[3]) { if (handle < 0 || handle >= this->NumberOfHandles) { vtkErrorMacro(<< "vtkBrokenLineWidget: handle index out of range."); return; } this->HandleGeometry[handle]->GetCenter(xyz); } double* vtkBrokenLineWidget::GetHandlePosition(int handle) { if (handle < 0 || handle >= this->NumberOfHandles) { vtkErrorMacro(<< "vtkBrokenLineWidget: handle index out of range."); return nullptr; } return this->HandleGeometry[handle]->GetCenter(); } void vtkBrokenLineWidget::SetEnabled(int enabling) { if (!this->Interactor) { vtkErrorMacro(<< "The interactor must be set prior to enabling/disabling widget"); return; } if (enabling) //------------------------------------------------------------ { vtkDebugMacro(<< "Enabling broken line widget"); if (this->Enabled) // already enabled, just return { return; } if (!this->CurrentRenderer) { this->SetCurrentRenderer(this->Interactor->FindPokedRenderer( this->Interactor->GetLastEventPosition()[0], this->Interactor->GetLastEventPosition()[1])); if (this->CurrentRenderer == nullptr) { return; } } this->Enabled = 1; // Listen for the following events vtkRenderWindowInteractor* i = this->Interactor; i->AddObserver(vtkCommand::MouseMoveEvent, this->EventCallbackCommand, this->Priority); i->AddObserver(vtkCommand::LeftButtonPressEvent, this->EventCallbackCommand, this->Priority); i->AddObserver(vtkCommand::LeftButtonReleaseEvent, this->EventCallbackCommand, this->Priority); i->AddObserver(vtkCommand::MiddleButtonPressEvent, this->EventCallbackCommand, this->Priority); i->AddObserver( vtkCommand::MiddleButtonReleaseEvent, this->EventCallbackCommand, this->Priority); i->AddObserver(vtkCommand::RightButtonPressEvent, this->EventCallbackCommand, this->Priority); i->AddObserver(vtkCommand::RightButtonReleaseEvent, this->EventCallbackCommand, this->Priority); // Add the line this->CurrentRenderer->AddActor(this->LineActor); this->LineActor->SetProperty(this->LineProperty); // Turn on the handles for (int j = 0; j < this->NumberOfHandles; ++j) { this->CurrentRenderer->AddActor(this->Handle[j]); this->Handle[j]->SetProperty(this->HandleProperty); } this->BuildRepresentation(); this->SizeHandles(); this->RegisterPickers(); this->InvokeEvent(vtkCommand::EnableEvent, nullptr); } else // disabling---------------------------------------------------------- { vtkDebugMacro(<< "Disabling broken line widget"); if (!this->Enabled) // already disabled, just return { return; } this->Enabled = 0; // Don't listen for events any more this->Interactor->RemoveObserver(this->EventCallbackCommand); // Turn off the line this->CurrentRenderer->RemoveActor(this->LineActor); // Turn off the handles for (int i = 0; i < this->NumberOfHandles; ++i) { this->CurrentRenderer->RemoveActor(this->Handle[i]); } this->CurrentHandle = nullptr; this->InvokeEvent(vtkCommand::DisableEvent, nullptr); this->SetCurrentRenderer(nullptr); this->UnRegisterPickers(); } this->Interactor->Render(); } void vtkBrokenLineWidget::ProcessEventsHandler( vtkObject* vtkNotUsed(object), unsigned long event, void* clientdata, void* vtkNotUsed(calldata)) { vtkBrokenLineWidget* self = reinterpret_cast(clientdata); // if ProcessEvents is Off, we ignore all interaction events. if (!self->GetProcessEvents()) { return; } // Okay, let's do the right thing switch (event) { case vtkCommand::LeftButtonPressEvent: self->OnLeftButtonDown(); break; case vtkCommand::LeftButtonReleaseEvent: self->OnLeftButtonUp(); break; case vtkCommand::MiddleButtonPressEvent: self->OnMiddleButtonDown(); break; case vtkCommand::MiddleButtonReleaseEvent: self->OnMiddleButtonUp(); break; case vtkCommand::RightButtonPressEvent: self->OnRightButtonDown(); break; case vtkCommand::RightButtonReleaseEvent: self->OnRightButtonUp(); break; case vtkCommand::MouseMoveEvent: self->OnMouseMove(); break; } } void vtkBrokenLineWidget::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "ProcessEvents: " << (this->ProcessEvents ? "On" : "Off") << "\n"; if (this->HandleProperty) { os << indent << "Handle Property: " << this->HandleProperty << "\n"; } else { os << indent << "Handle Property: ( none )\n"; } if (this->SelectedHandleProperty) { os << indent << "Selected Handle Property: " << this->SelectedHandleProperty << "\n"; } else { os << indent << "Selected Handle Property: ( none )\n"; } if (this->LineProperty) { os << indent << "Line Property: " << this->LineProperty << "\n"; } else { os << indent << "Line Property: ( none )\n"; } if (this->SelectedLineProperty) { os << indent << "Selected Line Property: " << this->SelectedLineProperty << "\n"; } else { os << indent << "Selected Line Property: ( none )\n"; } os << indent << "Project To Plane: " << (this->ProjectToPlane ? "On" : "Off") << "\n"; os << indent << "Projection Normal: " << this->ProjectionNormal << "\n"; os << indent << "Projection Position: " << this->ProjectionPosition << "\n"; os << indent << "Number Of Handles: " << this->NumberOfHandles << "\n"; os << indent << "Handle Size Factor" << this->HandleSizeFactor << "\n"; } void vtkBrokenLineWidget::ProjectPointsToPlane() { if (this->ProjectionNormal == VTK_PROJECTION_OBLIQUE) { if (this->PlaneSource != nullptr) { this->ProjectPointsToObliquePlane(); } else { vtkGenericWarningMacro(<< "Set the plane source for oblique projections..."); } } else { this->ProjectPointsToOrthoPlane(); } } void vtkBrokenLineWidget::ProjectPointsToObliquePlane() { double o[3]; double u[3]; double v[3]; this->PlaneSource->GetPoint1(u); this->PlaneSource->GetPoint2(v); this->PlaneSource->GetOrigin(o); int i; for (i = 0; i < 3; ++i) { u[i] = u[i] - o[i]; v[i] = v[i] - o[i]; } vtkMath::Normalize(u); vtkMath::Normalize(v); double o_dot_u = vtkMath::Dot(o, u); double o_dot_v = vtkMath::Dot(o, v); double fac1; double fac2; double ctr[3]; for (i = 0; i < this->NumberOfHandles; ++i) { this->HandleGeometry[i]->GetCenter(ctr); fac1 = vtkMath::Dot(ctr, u) - o_dot_u; fac2 = vtkMath::Dot(ctr, v) - o_dot_v; ctr[0] = o[0] + fac1 * u[0] + fac2 * v[0]; ctr[1] = o[1] + fac1 * u[1] + fac2 * v[1]; ctr[2] = o[2] + fac1 * u[2] + fac2 * v[2]; this->HandleGeometry[i]->SetCenter(ctr); this->HandleGeometry[i]->Update(); } } void vtkBrokenLineWidget::ProjectPointsToOrthoPlane() { double ctr[3]; for (int i = 0; i < this->NumberOfHandles; ++i) { this->HandleGeometry[i]->GetCenter(ctr); ctr[this->ProjectionNormal] = this->ProjectionPosition; this->HandleGeometry[i]->SetCenter(ctr); this->HandleGeometry[i]->Update(); } } //------------------------------------------------------------------------------ void vtkBrokenLineWidget::RegisterPickers() { vtkPickingManager* pm = this->GetPickingManager(); if (!pm) { return; } pm->AddPicker(this->HandlePicker, this); pm->AddPicker(this->LinePicker, this); } void vtkBrokenLineWidget::BuildRepresentation() { // Get points array from line source vtkPoints* points = this->LineSource->GetPoints(); if (points->GetNumberOfPoints() != this->NumberOfHandles) { points->SetNumberOfPoints(this->NumberOfHandles); } double pt[3]; int i; for (i = 0; i < this->NumberOfHandles; ++i) { this->HandleGeometry[i]->GetCenter(pt); points->SetPoint(i, pt); } this->LineSource->Modified(); } int vtkBrokenLineWidget::HighlightHandle(vtkProp* prop) { // First unhighlight anything picked if (this->CurrentHandle) { this->CurrentHandle->SetProperty(this->HandleProperty); } this->CurrentHandle = static_cast(prop); if (this->CurrentHandle) { for (int i = 0; i < this->NumberOfHandles; ++i) // find handle { if (this->CurrentHandle == this->Handle[i]) { this->ValidPick = 1; this->HandlePicker->GetPickPosition(this->LastPickPosition); this->CurrentHandle->SetProperty(this->SelectedHandleProperty); return i; } } } return -1; } void vtkBrokenLineWidget::HighlightLine(int highlight) { if (highlight) { this->ValidPick = 1; this->LinePicker->GetPickPosition(this->LastPickPosition); this->LineActor->SetProperty(this->SelectedLineProperty); } else { this->LineActor->SetProperty(this->LineProperty); } } void vtkBrokenLineWidget::OnLeftButtonDown() { int X = this->Interactor->GetEventPosition()[0]; int Y = this->Interactor->GetEventPosition()[1]; // Okay, make sure that the pick is in the current renderer if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y)) { this->State = vtkBrokenLineWidget::Outside; return; } this->State = vtkBrokenLineWidget::Moving; // Okay, we can process this. Try to pick handles first; // if no handles picked, then try to pick the line. vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker); if (path != nullptr) { this->CurrentHandleIndex = this->HighlightHandle(path->GetFirstNode()->GetViewProp()); } else { path = this->GetAssemblyPath(X, Y, 0., this->LinePicker); if (path != nullptr) { this->HighlightLine(1); } else { this->CurrentHandleIndex = this->HighlightHandle(nullptr); this->State = vtkBrokenLineWidget::Outside; return; } } this->EventCallbackCommand->SetAbortFlag(1); this->StartInteraction(); this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); this->Interactor->Render(); } void vtkBrokenLineWidget::OnLeftButtonUp() { if (this->State == vtkBrokenLineWidget::Outside || this->State == vtkBrokenLineWidget::Start) { return; } this->State = vtkBrokenLineWidget::Start; this->HighlightHandle(nullptr); this->HighlightLine(0); this->SizeHandles(); this->EventCallbackCommand->SetAbortFlag(1); this->EndInteraction(); this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr); this->Interactor->Render(); } void vtkBrokenLineWidget::OnMiddleButtonDown() { int X = this->Interactor->GetEventPosition()[0]; int Y = this->Interactor->GetEventPosition()[1]; // Okay, make sure that the pick is in the current renderer if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y)) { this->State = vtkBrokenLineWidget::Outside; return; } if (this->Interactor->GetControlKey()) { this->State = vtkBrokenLineWidget::Spinning; this->CalculateCentroid(); } else { this->State = vtkBrokenLineWidget::Moving; } // Okay, we can process this. Try to pick handles first; // if no handles picked, then try to pick the line. vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker); if (path == nullptr) { path = this->GetAssemblyPath(X, Y, 0., this->LinePicker); if (path == nullptr) { this->State = vtkBrokenLineWidget::Outside; this->HighlightLine(0); return; } else { this->HighlightLine(1); } } else // we picked a handle but lets make it look like the line is picked { this->HighlightLine(1); } this->EventCallbackCommand->SetAbortFlag(1); this->StartInteraction(); this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); this->Interactor->Render(); } void vtkBrokenLineWidget::OnMiddleButtonUp() { if (this->State == vtkBrokenLineWidget::Outside || this->State == vtkBrokenLineWidget::Start) { return; } this->State = vtkBrokenLineWidget::Start; this->HighlightLine(0); this->SizeHandles(); this->EventCallbackCommand->SetAbortFlag(1); this->EndInteraction(); this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr); this->Interactor->Render(); } void vtkBrokenLineWidget::OnRightButtonDown() { int X = this->Interactor->GetEventPosition()[0]; int Y = this->Interactor->GetEventPosition()[1]; // Okay, make sure that the pick is in the current renderer if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y)) { this->State = vtkBrokenLineWidget::Outside; return; } if (this->Interactor->GetShiftKey()) { this->State = vtkBrokenLineWidget::Inserting; } else if (this->Interactor->GetControlKey()) { this->State = vtkBrokenLineWidget::Erasing; } else { this->State = vtkBrokenLineWidget::Scaling; } vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker); if (path != nullptr) { switch (this->State) { // deny insertion over existing handles case vtkBrokenLineWidget::Inserting: this->State = vtkBrokenLineWidget::Outside; return; case vtkBrokenLineWidget::Erasing: this->CurrentHandleIndex = this->HighlightHandle(path->GetFirstNode()->GetViewProp()); break; case vtkBrokenLineWidget::Scaling: this->HighlightLine(1); break; } } else { // trying to erase handle but nothing picked if (this->State == vtkBrokenLineWidget::Erasing) { this->State = vtkBrokenLineWidget::Outside; return; } // try to insert or scale so pick the line path = this->GetAssemblyPath(X, Y, 0., this->LinePicker); if (path != nullptr) { this->HighlightLine(1); } else { this->State = vtkBrokenLineWidget::Outside; return; } } this->EventCallbackCommand->SetAbortFlag(1); this->StartInteraction(); this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); this->Interactor->Render(); } void vtkBrokenLineWidget::OnRightButtonUp() { if (this->State == vtkBrokenLineWidget::Outside || this->State == vtkBrokenLineWidget::Start) { return; } if (this->State == vtkBrokenLineWidget::Inserting) { this->InsertHandleOnLine(this->LastPickPosition); } else if (this->State == vtkBrokenLineWidget::Erasing) { int index = this->CurrentHandleIndex; this->CurrentHandleIndex = this->HighlightHandle(nullptr); this->EraseHandle(index); } this->State = vtkBrokenLineWidget::Start; this->HighlightLine(0); this->SizeHandles(); this->EventCallbackCommand->SetAbortFlag(1); this->EndInteraction(); this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr); this->Interactor->Render(); } void vtkBrokenLineWidget::OnMouseMove() { // See whether we're active if (this->State == vtkBrokenLineWidget::Outside || this->State == vtkBrokenLineWidget::Start) { return; } int X = this->Interactor->GetEventPosition()[0]; int Y = this->Interactor->GetEventPosition()[1]; // Do different things depending on state // Calculations everybody does double focalPoint[4], pickPoint[4], prevPickPoint[4]; double z, vpn[3]; vtkCamera* camera = this->CurrentRenderer->GetActiveCamera(); if (!camera) { return; } // Compute the two points defining the motion vector this->ComputeWorldToDisplay( this->LastPickPosition[0], this->LastPickPosition[1], this->LastPickPosition[2], focalPoint); z = focalPoint[2]; this->ComputeDisplayToWorld(double(this->Interactor->GetLastEventPosition()[0]), double(this->Interactor->GetLastEventPosition()[1]), z, prevPickPoint); this->ComputeDisplayToWorld(double(X), double(Y), z, pickPoint); // Process the motion if (this->State == vtkBrokenLineWidget::Moving) { // Okay to process if (this->CurrentHandle) { this->MovePoint(prevPickPoint, pickPoint); } else // Must be moving the spline { this->Translate(prevPickPoint, pickPoint); } } else if (this->State == vtkBrokenLineWidget::Scaling) { this->Scale(prevPickPoint, pickPoint, X, Y); } else if (this->State == vtkBrokenLineWidget::Spinning) { camera->GetViewPlaneNormal(vpn); this->Spin(prevPickPoint, pickPoint, vpn); } if (this->ProjectToPlane) { this->ProjectPointsToPlane(); } this->BuildRepresentation(); // Interact, if desired this->EventCallbackCommand->SetAbortFlag(1); this->InvokeEvent(vtkCommand::InteractionEvent, nullptr); this->Interactor->Render(); } void vtkBrokenLineWidget::MovePoint(double* p1, double* p2) { if (this->CurrentHandleIndex < 0 || this->CurrentHandleIndex >= this->NumberOfHandles) { vtkGenericWarningMacro(<< "BrokenLine handle index out of range."); return; } // Get the motion vector double v[3]; v[0] = p2[0] - p1[0]; v[1] = p2[1] - p1[1]; v[2] = p2[2] - p1[2]; double* ctr = this->HandleGeometry[this->CurrentHandleIndex]->GetCenter(); double newCtr[3]; newCtr[0] = ctr[0] + v[0]; newCtr[1] = ctr[1] + v[1]; newCtr[2] = ctr[2] + v[2]; this->HandleGeometry[this->CurrentHandleIndex]->SetCenter(newCtr); this->HandleGeometry[this->CurrentHandleIndex]->Update(); } void vtkBrokenLineWidget::Translate(double* p1, double* p2) { // Get the motion vector double v[3]; v[0] = p2[0] - p1[0]; v[1] = p2[1] - p1[1]; v[2] = p2[2] - p1[2]; double newCtr[3]; for (int i = 0; i < this->NumberOfHandles; ++i) { double* ctr = this->HandleGeometry[i]->GetCenter(); for (int j = 0; j < 3; ++j) { newCtr[j] = ctr[j] + v[j]; } this->HandleGeometry[i]->SetCenter(newCtr); this->HandleGeometry[i]->Update(); } } void vtkBrokenLineWidget::Scale(double* p1, double* p2, int vtkNotUsed(X), int Y) { // Get the motion vector double v[3]; v[0] = p2[0] - p1[0]; v[1] = p2[1] - p1[1]; v[2] = p2[2] - p1[2]; double center[3] = { 0., 0., 0. }; double avgdist = 0.; double* prevctr = this->HandleGeometry[0]->GetCenter(); double* ctr; center[0] += prevctr[0]; center[1] += prevctr[1]; center[2] += prevctr[2]; int i; for (i = 1; i < this->NumberOfHandles; ++i) { ctr = this->HandleGeometry[i]->GetCenter(); center[0] += ctr[0]; center[1] += ctr[1]; center[2] += ctr[2]; avgdist += sqrt(vtkMath::Distance2BetweenPoints(ctr, prevctr)); prevctr = ctr; } avgdist /= this->NumberOfHandles; center[0] /= this->NumberOfHandles; center[1] /= this->NumberOfHandles; center[2] /= this->NumberOfHandles; // Compute the scale factor double sf = vtkMath::Norm(v) / avgdist; if (Y > this->Interactor->GetLastEventPosition()[1]) { sf = 1.0 + sf; } else { sf = 1.0 - sf; } // Move the handle points double newCtr[3]; for (i = 0; i < this->NumberOfHandles; ++i) { ctr = this->HandleGeometry[i]->GetCenter(); for (int j = 0; j < 3; ++j) { newCtr[j] = sf * (ctr[j] - center[j]) + center[j]; } this->HandleGeometry[i]->SetCenter(newCtr); this->HandleGeometry[i]->Update(); } } void vtkBrokenLineWidget::Spin(double* p1, double* p2, double* vpn) { // Mouse motion vector in world space double v[3]; v[0] = p2[0] - p1[0]; v[1] = p2[1] - p1[1]; v[2] = p2[2] - p1[2]; // Axis of rotation double axis[3] = { 0., 0., 0. }; if (this->ProjectToPlane) { if (this->ProjectionNormal == VTK_PROJECTION_OBLIQUE) { if (this->PlaneSource != nullptr) { double* normal = this->PlaneSource->GetNormal(); axis[0] = normal[0]; axis[1] = normal[1]; axis[2] = normal[2]; vtkMath::Normalize(axis); } else { axis[0] = 1.; } } else { axis[this->ProjectionNormal] = 1.; } } else { // Create axis of rotation and angle of rotation vtkMath::Cross(vpn, v, axis); if (vtkMath::Normalize(axis) == 0.) { return; } } // Radius vector ( from mean center to cursor position ) double rv[3] = { p2[0] - this->Centroid[0], p2[1] - this->Centroid[1], p2[2] - this->Centroid[2] }; // Distance between center and cursor location double rs = vtkMath::Normalize(rv); // Spin direction double ax_cross_rv[3]; vtkMath::Cross(axis, rv, ax_cross_rv); // Spin angle double theta = 360. * vtkMath::Dot(v, ax_cross_rv) / rs; // Manipulate the transform to reflect the rotation this->Transform->Identity(); this->Transform->Translate(this->Centroid[0], this->Centroid[1], this->Centroid[2]); this->Transform->RotateWXYZ(theta, axis); this->Transform->Translate(-this->Centroid[0], -this->Centroid[1], -this->Centroid[2]); // Set the handle points double newCtr[3]; double ctr[3]; for (int i = 0; i < this->NumberOfHandles; ++i) { this->HandleGeometry[i]->GetCenter(ctr); this->Transform->TransformPoint(ctr, newCtr); this->HandleGeometry[i]->SetCenter(newCtr); this->HandleGeometry[i]->Update(); } } void vtkBrokenLineWidget::CreateDefaultProperties() { if (!this->HandleProperty) { this->HandleProperty = vtkProperty::New(); this->HandleProperty->SetColor(1, 1, 1); } if (!this->SelectedHandleProperty) { this->SelectedHandleProperty = vtkProperty::New(); this->SelectedHandleProperty->SetColor(1, 0, 0); } if (!this->LineProperty) { this->LineProperty = vtkProperty::New(); this->LineProperty->SetRepresentationToWireframe(); this->LineProperty->SetAmbient(1.); this->LineProperty->SetColor(1., 1., 0.); this->LineProperty->SetLineWidth(2.); } if (!this->SelectedLineProperty) { this->SelectedLineProperty = vtkProperty::New(); this->SelectedLineProperty->SetRepresentationToWireframe(); this->SelectedLineProperty->SetAmbient(1.); this->SelectedLineProperty->SetAmbientColor(0., 1., 0.); this->SelectedLineProperty->SetLineWidth(2.); } } void vtkBrokenLineWidget::PlaceWidget(double bds[6]) { double bounds[6], center[3]; this->AdjustBounds(bds, bounds, center); if (this->ProjectToPlane) { this->ProjectPointsToPlane(); } else // place the center { // Create a default straight line within the data bounds double x0 = bounds[0]; double x1 = bounds[1]; double y0 = bounds[2]; double y1 = bounds[3]; double z0 = bounds[4]; double z1 = bounds[5]; double x; double y; double z; double u; for (int i = 0; i < this->NumberOfHandles; ++i) { u = i / (this->NumberOfHandles - 1.0); x = (1.0 - u) * x0 + u * x1; y = (1.0 - u) * y0 + u * y1; z = (1.0 - u) * z0 + u * z1; this->HandleGeometry[i]->SetCenter(x, y, z); } } for (int i = 0; i < 6; ++i) { this->InitialBounds[i] = bounds[i]; } this->InitialLength = sqrt((bounds[1] - bounds[0]) * (bounds[1] - bounds[0]) + (bounds[3] - bounds[2]) * (bounds[3] - bounds[2]) + (bounds[5] - bounds[4]) * (bounds[5] - bounds[4])); this->BuildRepresentation(); this->SizeHandles(); } void vtkBrokenLineWidget::SetProjectionPosition(double position) { this->ProjectionPosition = position; if (this->ProjectToPlane) { this->ProjectPointsToPlane(); } this->BuildRepresentation(); } void vtkBrokenLineWidget::SetPlaneSource(vtkPlaneSource* plane) { if (this->PlaneSource == plane) { return; } this->PlaneSource = plane; } void vtkBrokenLineWidget::SetNumberOfHandles(int npts) { if (this->NumberOfHandles == npts) { return; } if (npts < 2) { vtkGenericWarningMacro(<< "Minimum of 2 points required to define a broken line."); return; } double radius = this->HandleGeometry[0]->GetRadius(); this->Initialize(); this->NumberOfHandles = npts; // Create the handles this->Handle = new vtkActor*[this->NumberOfHandles]; this->HandleGeometry = new vtkSphereSource*[this->NumberOfHandles]; for (int i = 0; i < this->NumberOfHandles; ++i) { this->HandleGeometry[i] = vtkSphereSource::New(); this->HandleGeometry[i]->SetThetaResolution(16); this->HandleGeometry[i]->SetPhiResolution(8); vtkPolyDataMapper* handleMapper = vtkPolyDataMapper::New(); handleMapper->SetInputConnection(this->HandleGeometry[i]->GetOutputPort()); this->Handle[i] = vtkActor::New(); this->Handle[i]->SetMapper(handleMapper); handleMapper->Delete(); this->Handle[i]->SetProperty(this->HandleProperty); this->HandleGeometry[i]->SetRadius(radius); this->HandlePicker->AddPickList(this->Handle[i]); } if (this->Interactor) { if (!this->CurrentRenderer) { this->SetCurrentRenderer(this->Interactor->FindPokedRenderer( this->Interactor->GetLastEventPosition()[0], this->Interactor->GetLastEventPosition()[1])); } if (this->CurrentRenderer != nullptr) { for (int i = 0; i < this->NumberOfHandles; ++i) { this->CurrentRenderer->AddViewProp(this->Handle[i]); } this->SizeHandles(); } this->Interactor->Render(); } } void vtkBrokenLineWidget::Initialize() { int i; if (this->Interactor) { if (!this->CurrentRenderer) { this->SetCurrentRenderer(this->Interactor->FindPokedRenderer( this->Interactor->GetLastEventPosition()[0], this->Interactor->GetLastEventPosition()[1])); } if (this->CurrentRenderer != nullptr) { for (i = 0; i < this->NumberOfHandles; ++i) { this->CurrentRenderer->RemoveViewProp(this->Handle[i]); } } } for (i = 0; i < this->NumberOfHandles; ++i) { this->HandlePicker->DeletePickList(this->Handle[i]); this->HandleGeometry[i]->Delete(); this->Handle[i]->Delete(); } this->NumberOfHandles = 0; delete[] this->Handle; delete[] this->HandleGeometry; } void vtkBrokenLineWidget::GetPolyData(vtkPolyData* pd) { pd->ShallowCopy(this->LineSource->GetOutput()); } void vtkBrokenLineWidget::SizeHandles() { double radius = this->vtk3DWidget::SizeHandles(this->HandleSizeFactor); for (int i = 0; i < this->NumberOfHandles; ++i) { this->HandleGeometry[i]->SetRadius(radius); } } double vtkBrokenLineWidget::GetSummedLength() { vtkPoints* points = this->LineSource->GetOutput()->GetPoints(); if (!points) { return 0.; } int npts = points->GetNumberOfPoints(); if (npts < 2) { return 0.; } double a[3]; double b[3]; double sum = 0.; int i = 0; points->GetPoint(i, a); int imax = (npts % 2 == 0) ? npts - 2 : npts - 1; while (i < imax) { points->GetPoint(i + 1, b); sum += sqrt(vtkMath::Distance2BetweenPoints(a, b)); i = i + 2; points->GetPoint(i, a); sum = sum + sqrt(vtkMath::Distance2BetweenPoints(a, b)); } if (npts % 2 == 0) { points->GetPoint(i + 1, b); sum += sqrt(vtkMath::Distance2BetweenPoints(a, b)); } return sum; } void vtkBrokenLineWidget::CalculateCentroid() { this->Centroid[0] = 0.; this->Centroid[1] = 0.; this->Centroid[2] = 0.; double ctr[3]; for (int i = 0; i < this->NumberOfHandles; ++i) { this->HandleGeometry[i]->GetCenter(ctr); this->Centroid[0] += ctr[0]; this->Centroid[1] += ctr[1]; this->Centroid[2] += ctr[2]; } this->Centroid[0] /= this->NumberOfHandles; this->Centroid[1] /= this->NumberOfHandles; this->Centroid[2] /= this->NumberOfHandles; } void vtkBrokenLineWidget::InsertHandleOnLine(double* pos) { if (this->NumberOfHandles < 2) { return; } vtkIdType id = this->LinePicker->GetCellId(); if (id == -1) { return; } vtkIdType subid = this->LinePicker->GetSubId(); vtkPoints* newpoints = vtkPoints::New(VTK_DOUBLE); newpoints->SetNumberOfPoints(this->NumberOfHandles + 1); int istart = subid; int istop = istart + 1; int count = 0; int i; for (i = 0; i <= istart; ++i) { newpoints->SetPoint(count++, this->HandleGeometry[i]->GetCenter()); } newpoints->SetPoint(count++, pos); for (i = istop; i < this->NumberOfHandles; ++i) { newpoints->SetPoint(count++, this->HandleGeometry[i]->GetCenter()); } this->InitializeHandles(newpoints); newpoints->Delete(); } void vtkBrokenLineWidget::EraseHandle(const int& index) { if (this->NumberOfHandles < 3 || index < 0 || index >= this->NumberOfHandles) { return; } vtkPoints* newpoints = vtkPoints::New(VTK_DOUBLE); newpoints->SetNumberOfPoints(this->NumberOfHandles - 1); int count = 0; for (int i = 0; i < this->NumberOfHandles; ++i) { if (i != index) { newpoints->SetPoint(count++, this->HandleGeometry[i]->GetCenter()); } } this->InitializeHandles(newpoints); newpoints->Delete(); } void vtkBrokenLineWidget::InitializeHandles(vtkPoints* points) { if (!points) { return; } int npts = points->GetNumberOfPoints(); if (npts < 2) { return; } double p0[3]; double p1[3]; points->GetPoint(0, p0); points->GetPoint(npts - 1, p1); if (vtkMath::Distance2BetweenPoints(p0, p1) == 0.) { --npts; } this->SetNumberOfHandles(npts); int i; for (i = 0; i < npts; ++i) { this->SetHandlePosition(i, points->GetPoint(i)); } if (this->Interactor && this->Enabled) { this->Interactor->Render(); } }