/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkPointWidget.cxx,v $ 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 "vtkPointWidget.h" #include "vtkActor.h" #include "vtkAssemblyNode.h" #include "vtkCallbackCommand.h" #include "vtkCamera.h" #include "vtkCellPicker.h" #include "vtkMath.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkProperty.h" #include "vtkRenderer.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderWindow.h" #include "vtkObjectFactory.h" vtkCxxRevisionMacro(vtkPointWidget, "$Revision: 1.1 $"); vtkStandardNewMacro(vtkPointWidget); vtkPointWidget::vtkPointWidget() { this->State = vtkPointWidget::Start; this->EventCallbackCommand->SetCallback(vtkPointWidget::ProcessEvents); // Represent the line this->Cursor3D = vtkCursor3D::New(); this->Mapper = vtkPolyDataMapper::New(); this->Mapper->SetInput(this->Cursor3D->GetOutput()); this->Actor = vtkActor::New(); this->Actor->SetMapper(this->Mapper); // Define the point coordinates double bounds[6]; bounds[0] = -0.5; bounds[1] = 0.5; bounds[2] = -0.5; bounds[3] = 0.5; bounds[4] = -0.5; bounds[5] = 0.5; // Initial creation of the widget, serves to initialize it this->PlaceWidget(bounds); //Manage the picking stuff this->CursorPicker = vtkCellPicker::New(); this->CursorPicker->PickFromListOn(); this->CursorPicker->AddPickList(this->Actor); this->CursorPicker->SetTolerance(0.005); //need some fluff // Set up the initial properties this->CreateDefaultProperties(); // Constraints not set this->ConstraintAxis = -1; // Override superclass' this->PlaceFactor = 1.0; // The size of the hot spot this->HotSpotSize = 0.05; this->WaitingForMotion = 0; } vtkPointWidget::~vtkPointWidget() { this->Actor->Delete(); this->Mapper->Delete(); this->Cursor3D->Delete(); this->CursorPicker->Delete(); this->Property->Delete(); this->SelectedProperty->Delete(); } void vtkPointWidget::SetEnabled(int enabling) { if ( ! this->Interactor ) { vtkErrorMacro(<<"The interactor must be set prior to enabling/disabling widget"); return; } if ( enabling ) //----------------------------------------------------------- { vtkDebugMacro(<<"Enabling point 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 == NULL) { 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->Actor); this->Actor->SetProperty(this->Property); this->Cursor3D->Update(); this->InvokeEvent(vtkCommand::EnableEvent,NULL); } else //disabling---------------------------------------------------------- { vtkDebugMacro(<<"Disabling point 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->Actor); this->InvokeEvent(vtkCommand::DisableEvent,NULL); this->SetCurrentRenderer(NULL); } this->Interactor->Render(); } void vtkPointWidget::ProcessEvents(vtkObject* vtkNotUsed(object), unsigned long event, void* clientdata, void* vtkNotUsed(calldata)) { vtkPointWidget* self = reinterpret_cast( clientdata ); //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 vtkPointWidget::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); if ( this->Property ) { os << indent << "Property: " << this->Property << "\n"; } else { os << indent << "Property: (none)\n"; } if ( this->SelectedProperty ) { os << indent << "Selected Property: " << this->SelectedProperty << "\n"; } else { os << indent << "Selected Property: (none)\n"; } double *pos = this->Cursor3D->GetFocalPoint(); os << indent << "Position: (" << pos[0] << ", " << pos[1] << ", " << pos[2] << ")\n"; os << indent << "Outline: " << (this->GetOutline() ? "On\n" : "Off\n"); os << indent << "XShadows: " << (this->GetXShadows() ? "On\n" : "Off\n"); os << indent << "YShadows: " << (this->GetYShadows() ? "On\n" : "Off\n"); os << indent << "ZShadows: " << (this->GetZShadows() ? "On\n" : "Off\n"); os << indent << "Translation Mode: " << (this->Cursor3D->GetTranslationMode() ? "On\n" : "Off\n"); os << indent << "Hot Spot Size: " << this->HotSpotSize << "\n"; } void vtkPointWidget::Highlight(int highlight) { if ( highlight ) { this->Actor->SetProperty(this->SelectedProperty); this->CursorPicker->GetPickPosition(this->LastPickPosition); this->ValidPick = 1; } else { this->Actor->SetProperty(this->Property); } } int vtkPointWidget::DetermineConstraintAxis(int constraint, double *x) { // Look for trivial cases if ( ! this->Interactor->GetShiftKey() ) { return -1; } else if ( constraint >= 0 && constraint < 3 ) { return constraint; } // Okay, figure out constraint. First see if the choice is // outside the hot spot if ( ! this->WaitingForMotion ) { double p[3], d2, tol; this->CursorPicker->GetPickPosition(p); d2 = vtkMath::Distance2BetweenPoints(p,this->LastPickPosition); tol = this->HotSpotSize*this->InitialLength; if ( d2 > (tol*tol) ) { this->WaitingForMotion = 0; return this->CursorPicker->GetCellId(); } else { this->WaitingForMotion = 1; this->WaitCount = 0; return -1; } } else if ( this->WaitingForMotion && x ) { double v[3]; this->WaitingForMotion = 0; v[0] = fabs(x[0] - this->LastPickPosition[0]); v[1] = fabs(x[1] - this->LastPickPosition[1]); v[2] = fabs(x[2] - this->LastPickPosition[2]); return ( v[0]>v[1] ? (v[0]>v[2]?0:2) : (v[1]>v[2]?1:2)); } else { return -1; } } void vtkPointWidget::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 = vtkPointWidget::Outside; return; } vtkAssemblyPath *path; this->CursorPicker->Pick(X,Y,0.0,this->CurrentRenderer); path = this->CursorPicker->GetPath(); if ( path != NULL ) { this->State = vtkPointWidget::Moving; this->Highlight(1); this->ConstraintAxis = this->DetermineConstraintAxis(-1,NULL); } else { this->State = vtkPointWidget::Outside; this->Highlight(0); this->ConstraintAxis = -1; return; } this->EventCallbackCommand->SetAbortFlag(1); this->StartInteraction(); this->InvokeEvent(vtkCommand::StartInteractionEvent,NULL); this->Interactor->Render(); } void vtkPointWidget::OnLeftButtonUp() { if ( this->State == vtkPointWidget::Outside || this->State == vtkPointWidget::Start ) { return; } this->State = vtkPointWidget::Start; this->Highlight(0); this->EventCallbackCommand->SetAbortFlag(1); this->EndInteraction(); this->InvokeEvent(vtkCommand::EndInteractionEvent,NULL); this->Interactor->Render(); } void vtkPointWidget::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 = vtkPointWidget::Outside; return; } // Okay, we can process this. vtkAssemblyPath *path; this->CursorPicker->Pick(X,Y,0.0,this->CurrentRenderer); path = this->CursorPicker->GetPath(); if ( path != NULL ) { this->State = vtkPointWidget::Translating; this->Highlight(1); this->ConstraintAxis = this->DetermineConstraintAxis(-1,NULL); } else { this->State = vtkPointWidget::Outside; this->ConstraintAxis = -1; return; } this->EventCallbackCommand->SetAbortFlag(1); this->StartInteraction(); this->InvokeEvent(vtkCommand::StartInteractionEvent,NULL); this->Interactor->Render(); } void vtkPointWidget::OnMiddleButtonUp() { if ( this->State == vtkPointWidget::Outside || this->State == vtkPointWidget::Start ) { return; } this->State = vtkPointWidget::Start; this->Highlight(0); this->EventCallbackCommand->SetAbortFlag(1); this->EndInteraction(); this->InvokeEvent(vtkCommand::EndInteractionEvent,NULL); this->Interactor->Render(); } void vtkPointWidget::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 = vtkPointWidget::Outside; return; } // Okay, we can process this. Pick the cursor. vtkAssemblyPath *path; this->CursorPicker->Pick(X,Y,0.0,this->CurrentRenderer); path = this->CursorPicker->GetPath(); if ( path != NULL ) { this->State = vtkPointWidget::Scaling; int idx = this->CursorPicker->GetCellId(); if ( idx >= 0 && idx < 3 ) { this->ConstraintAxis = idx; } this->Highlight(1); } else { this->State = vtkPointWidget::Outside; this->ConstraintAxis = -1; return; } this->EventCallbackCommand->SetAbortFlag(1); this->StartInteraction(); this->InvokeEvent(vtkCommand::StartInteractionEvent,NULL); this->Interactor->Render(); } void vtkPointWidget::OnRightButtonUp() { if ( this->State == vtkPointWidget::Outside || this->State == vtkPointWidget::Start ) { return; } this->State = vtkPointWidget::Start; this->Highlight(0); this->EventCallbackCommand->SetAbortFlag(1); this->EndInteraction(); this->InvokeEvent(vtkCommand::EndInteractionEvent,NULL); this->Interactor->Render(); } void vtkPointWidget::OnMouseMove() { // See whether we're active if ( this->State == vtkPointWidget::Outside || this->State == vtkPointWidget::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; 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 == vtkPointWidget::Moving ) { if ( !this->WaitingForMotion || this->WaitCount++ > 3 ) { this->ConstraintAxis = this->DetermineConstraintAxis(this->ConstraintAxis,pickPoint); this->MoveFocus(prevPickPoint, pickPoint); } else { return; //avoid the extra render } } else if ( this->State == vtkPointWidget::Scaling ) { this->Scale(prevPickPoint, pickPoint, X, Y); } else if ( this->State == vtkPointWidget::Translating ) { if ( !this->WaitingForMotion || this->WaitCount++ > 3 ) { this->ConstraintAxis = this->DetermineConstraintAxis(this->ConstraintAxis,pickPoint); this->Translate(prevPickPoint, pickPoint); } else { return; //avoid the extra render } } // Interact, if desired this->EventCallbackCommand->SetAbortFlag(1); this->InvokeEvent(vtkCommand::InteractionEvent,NULL); this->Interactor->Render(); } void vtkPointWidget::MoveFocus(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 focus[3]; this->Cursor3D->GetFocalPoint(focus); if ( this->ConstraintAxis >= 0 ) { focus[this->ConstraintAxis] += v[this->ConstraintAxis]; } else { focus[0] += v[0]; focus[1] += v[1]; focus[2] += v[2]; } this->Cursor3D->SetFocalPoint(focus); } // Translate everything void vtkPointWidget::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 *bounds = this->Cursor3D->GetModelBounds(); double *pos = this->Cursor3D->GetFocalPoint(); double newBounds[6], newFocus[3]; int i; if ( this->ConstraintAxis >= 0 ) {//move along axis for (i=0; i<3; i++) { if ( i != this->ConstraintAxis ) { v[i] = 0.0; } } } for (i=0; i<3; i++) { newBounds[2*i] = bounds[2*i] + v[i]; newBounds[2*i+1] = bounds[2*i+1] + v[i]; newFocus[i] = pos[i] + v[i]; } this->Cursor3D->SetModelBounds(newBounds); this->Cursor3D->SetFocalPoint(newFocus); } void vtkPointWidget::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]; //int res = this->Cursor3D->GetResolution(); double *bounds = this->Cursor3D->GetModelBounds(); double *focus = this->Cursor3D->GetFocalPoint(); // Compute the scale factor double sf = vtkMath::Norm(v) / 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])); if ( Y > this->Interactor->GetLastEventPosition()[1] ) { sf = 1.0 + sf; } else { sf = 1.0 - sf; } // Move the end points double newBounds[6]; for (int i=0; i<3; i++) { newBounds[2*i] = sf * (bounds[2*i] - focus[i]) + focus[i]; newBounds[2*i+1] = sf * (bounds[2*i+1] - focus[i]) + focus[i]; } this->Cursor3D->SetModelBounds(newBounds); this->Cursor3D->Update(); } void vtkPointWidget::CreateDefaultProperties() { this->Property = vtkProperty::New(); this->Property->SetAmbient(1.0); this->Property->SetAmbientColor(1.0,1.0,1.0); this->Property->SetLineWidth(0.5); this->SelectedProperty = vtkProperty::New(); this->SelectedProperty->SetAmbient(1.0); this->SelectedProperty->SetAmbientColor(0.0,1.0,0.0); this->SelectedProperty->SetLineWidth(2.0); } void vtkPointWidget::PlaceWidget(double bds[6]) { int i; double bounds[6], center[3]; this->AdjustBounds(bds, bounds, center); this->Cursor3D->SetModelBounds(bounds); this->Cursor3D->SetFocalPoint(center); this->Cursor3D->Update(); for (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])); } void vtkPointWidget::GetPolyData(vtkPolyData *pd) { this->Cursor3D->Update(); pd->DeepCopy(this->Cursor3D->GetFocus()); }