/*========================================================================= 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() ); this->LineMapper->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->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->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() { this->Interactor->GetPickingManager()->AddPicker(this->HandlePicker, this); this->Interactor->GetPickingManager()->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( void ) { 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(); } }