/*========================================================================= Program: Visualization Toolkit Module: vtkParallelopipedWidget.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 "vtkParallelopipedWidget.h" #include "vtkActor.h" #include "vtkCallbackCommand.h" #include "vtkCommand.h" #include "vtkEvent.h" #include "vtkGarbageCollector.h" #include "vtkHandleWidget.h" #include "vtkInteractorObserver.h" #include "vtkObjectFactory.h" #include "vtkParallelopipedRepresentation.h" #include "vtkProperty.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderer.h" #include "vtkRendererCollection.h" #include "vtkWidgetCallbackMapper.h" #include "vtkWidgetEvent.h" #include "vtkWidgetEventTranslator.h" #include "vtkWidgetSet.h" vtkStandardNewMacro(vtkParallelopipedWidget); //------------------------------------------------------------------------------ vtkParallelopipedWidget::vtkParallelopipedWidget() { // Allow chairs to be created. this->EnableChairCreation = 1; // 8 handles for the 8 corners of the piped. this->HandleWidgets = new vtkHandleWidget*[8]; for (int i = 0; i < 8; i++) { this->HandleWidgets[i] = vtkHandleWidget::New(); // The widget gets a higher priority than the handles. this->HandleWidgets[i]->SetPriority(this->Priority - 0.01); this->HandleWidgets[i]->SetParent(this); // The piped widget will decide what cursor to show. this->HandleWidgets[i]->ManagesCursorOff(); } // Define widget events this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent, vtkEvent::NoModifier, 0, 1, nullptr, vtkParallelopipedWidget::RequestResizeEvent, this, vtkParallelopipedWidget::RequestResizeAlongAnAxisCallback); // Commented out by Will because it is unstable code // this, vtkParallelopipedWidget::RequestResizeCallback); this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent, vtkEvent::ShiftModifier, 0, 1, nullptr, vtkParallelopipedWidget::RequestResizeAlongAnAxisEvent, this, vtkParallelopipedWidget::RequestResizeAlongAnAxisCallback); this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent, vtkEvent::ControlModifier, 0, 1, nullptr, vtkParallelopipedWidget::RequestChairModeEvent, this, vtkParallelopipedWidget::RequestChairModeCallback); this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonReleaseEvent, vtkWidgetEvent::EndSelect, this, vtkParallelopipedWidget::OnLeftButtonUpCallback); this->CallbackMapper->SetCallbackMethod(vtkCommand::MouseMoveEvent, vtkWidgetEvent::Move, this, vtkParallelopipedWidget::OnMouseMoveCallback); this->WidgetSet = nullptr; } //------------------------------------------------------------------------------ vtkParallelopipedWidget::~vtkParallelopipedWidget() { for (int i = 0; i < 8; i++) { this->HandleWidgets[i]->Delete(); } delete[] this->HandleWidgets; } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::CreateDefaultRepresentation() { if (!this->WidgetRep) { this->WidgetRep = vtkParallelopipedRepresentation::New(); this->WidgetRep->SetRenderer(this->CurrentRenderer); } } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::SetEnabled(int enabling) { if (enabling) //---------------- { vtkDebugMacro(<< "Enabling widget"); if (this->Enabled) // already enabled, just return { return; } if (!this->Interactor) { vtkErrorMacro(<< "The interactor must be set prior to enabling the widget"); return; } int X = this->Interactor->GetEventPosition()[0]; int Y = this->Interactor->GetEventPosition()[1]; if (!this->CurrentRenderer) { this->SetCurrentRenderer(this->Interactor->FindPokedRenderer(X, Y)); if (this->CurrentRenderer == nullptr) { return; } } // We're ready to enable this->Enabled = 1; this->CreateDefaultRepresentation(); this->WidgetRep->SetRenderer(this->CurrentRenderer); // listen for the events found in the EventTranslator if (!this->Parent) { this->EventTranslator->AddEventsToInteractor( this->Interactor, this->EventCallbackCommand, this->Priority); } else { this->EventTranslator->AddEventsToParent( this->Parent, this->EventCallbackCommand, this->Priority); } // Enable each of the handle widgets. for (int i = 0; i < 8; i++) { if (this->HandleWidgets[i]) { this->HandleWidgets[i]->SetRepresentation( vtkParallelopipedRepresentation::SafeDownCast(this->WidgetRep) ->GetHandleRepresentation(i)); this->HandleWidgets[i]->SetInteractor(this->Interactor); this->HandleWidgets[i]->GetRepresentation()->SetRenderer(this->CurrentRenderer); this->HandleWidgets[i]->SetEnabled(enabling); } } if (this->ManagesCursor) { this->WidgetRep->ComputeInteractionState(X, Y); this->SetCursor(this->WidgetRep->GetInteractionState()); } this->WidgetRep->BuildRepresentation(); this->CurrentRenderer->AddViewProp(this->WidgetRep); this->InvokeEvent(vtkCommand::EnableEvent, nullptr); } else // disabling------------------ { vtkDebugMacro(<< "Disabling widget"); if (!this->Enabled) // already disabled, just return { return; } this->Enabled = 0; // don't listen for events any more if (!this->Parent) { this->Interactor->RemoveObserver(this->EventCallbackCommand); } else { this->Parent->RemoveObserver(this->EventCallbackCommand); } // Disable each of the handle widgets. for (int i = 0; i < 8; i++) { if (this->HandleWidgets[i]) { this->HandleWidgets[i]->SetEnabled(enabling); } } this->CurrentRenderer->RemoveViewProp(this->WidgetRep); this->InvokeEvent(vtkCommand::DisableEvent, nullptr); this->SetCurrentRenderer(nullptr); } // Should only render if there is no parent if (this->Interactor && !this->Parent) { this->Interactor->Render(); } } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::RequestResizeCallback(vtkAbstractWidget* w) { vtkParallelopipedWidget* self = reinterpret_cast(w); vtkParallelopipedRepresentation* rep = reinterpret_cast(self->WidgetRep); const int modifier = self->Interactor->GetShiftKey() | self->Interactor->GetControlKey() | self->Interactor->GetAltKey(); // This interaction could potentially select a handle, if we are close to // one. Lets make a request on the representation and see what it says. rep->SetInteractionState(vtkParallelopipedRepresentation::RequestResizeParallelopiped); // Let the representation decide what the appropriate state is. int interactionState = rep->ComputeInteractionState( self->Interactor->GetEventPosition()[0], self->Interactor->GetEventPosition()[1], modifier); self->SetCursor(interactionState); if (interactionState != vtkParallelopipedRepresentation::Outside) { self->EventCallbackCommand->SetAbortFlag(1); self->StartInteraction(); self->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); self->Interactor->Render(); } } //------------------------------------------------------------------------------ void vtkParallelopipedWidget ::RequestResizeAlongAnAxisCallback(vtkAbstractWidget* w) { vtkParallelopipedWidget* self = reinterpret_cast(w); vtkParallelopipedRepresentation* rep = reinterpret_cast(self->WidgetRep); const int modifier = self->Interactor->GetShiftKey() | self->Interactor->GetControlKey() | self->Interactor->GetAltKey(); // This interaction could potentially select a handle, if we are close to // one. Lets make a request on the representation and see what it says. rep->SetInteractionState(vtkParallelopipedRepresentation::RequestResizeParallelopipedAlongAnAxis); // Let the representation decide what the appropriate state is. int interactionState = rep->ComputeInteractionState( self->Interactor->GetEventPosition()[0], self->Interactor->GetEventPosition()[1], modifier); self->SetCursor(interactionState); if (interactionState == vtkParallelopipedRepresentation::Inside) { // We did not select any of the handles, nevertheless we are at least // inside the parallelopiped. We could do things like Translate etc. So // we will delegate responsibility to those callbacks vtkParallelopipedWidget::TranslateCallback(self); } else if (interactionState != vtkParallelopipedRepresentation::Outside) { self->EventCallbackCommand->SetAbortFlag(1); self->StartInteraction(); self->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); self->Interactor->Render(); } } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::RequestChairModeCallback(vtkAbstractWidget* w) { vtkParallelopipedWidget* self = reinterpret_cast(w); if (!self->EnableChairCreation) { return; } vtkParallelopipedRepresentation* rep = reinterpret_cast(self->WidgetRep); const int modifier = self->Interactor->GetShiftKey() | self->Interactor->GetControlKey() | self->Interactor->GetAltKey(); // This interaction could potentially select a handle, if we are close to // one. Lets make a request on the representation and see what it says. rep->SetInteractionState(vtkParallelopipedRepresentation::RequestChairMode); // Let the representation decide what the appropriate state is. int interactionState = rep->ComputeInteractionState( self->Interactor->GetEventPosition()[0], self->Interactor->GetEventPosition()[1], modifier); self->SetCursor(interactionState); if (interactionState != vtkParallelopipedRepresentation::Outside) { // Ok, so we did select a handle.... Render.. self->EventCallbackCommand->SetAbortFlag(1); self->StartInteraction(); self->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); self->Interactor->Render(); } } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::TranslateCallback(vtkAbstractWidget* w) { vtkParallelopipedWidget* self = reinterpret_cast(w); vtkParallelopipedRepresentation* rep = reinterpret_cast(self->WidgetRep); // We know we are inside the parallelopiped. // Change the cursor to the Translate thingie. self->SetCursor(vtkParallelopipedRepresentation::TranslatingParallelopiped); rep->SetInteractionState(vtkParallelopipedRepresentation::TranslatingParallelopiped); // Dispatch to all widgets in the set. if (self->WidgetSet) { self->WidgetSet->DispatchAction(self, &vtkParallelopipedWidget::BeginTranslateAction); } else { self->BeginTranslateAction(self); } } //------------------------------------------------------------------------------ void vtkParallelopipedWidget ::BeginTranslateAction(vtkParallelopipedWidget* vtkNotUsed(dispatcher)) { vtkParallelopipedRepresentation* rep = reinterpret_cast(this->WidgetRep); // We know we are inside the parallelopiped. // Change the cursor to the Translate thingie. rep->SetInteractionState(vtkParallelopipedRepresentation::TranslatingParallelopiped); this->SetCursor(rep->GetInteractionState()); this->EventCallbackCommand->SetAbortFlag(1); this->StartInteraction(); this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); this->Interactor->Render(); } //------------------------------------------------------------------------------ void vtkParallelopipedWidget ::TranslateAction(vtkParallelopipedWidget* vtkNotUsed(dispatcher)) { vtkParallelopipedRepresentation* rep = reinterpret_cast(this->WidgetRep); rep->Translate(this->Interactor->GetEventPosition()[0], this->Interactor->GetEventPosition()[1]); } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::OnLeftButtonUpCallback(vtkAbstractWidget* w) { vtkParallelopipedWidget* self = reinterpret_cast(w); vtkParallelopipedRepresentation* rep = reinterpret_cast(self->WidgetRep); int interactionState = rep->GetInteractionState(); // Reset the state rep->SetInteractionState(vtkParallelopipedRepresentation::Outside); // Let the representation re-compute what the appropriate state is. const int modifier = self->Interactor->GetShiftKey() | self->Interactor->GetControlKey() | self->Interactor->GetAltKey(); int newInteractionState = rep->ComputeInteractionState( self->Interactor->GetEventPosition()[0], self->Interactor->GetEventPosition()[1], modifier); // If we computed a different interaction state than the one we were in, // render in response to any changes. if (newInteractionState != interactionState) { self->Interactor->Render(); self->SetCursor(newInteractionState); self->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr); } } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::OnMouseMoveCallback(vtkAbstractWidget* w) { vtkParallelopipedWidget* self = reinterpret_cast(w); vtkParallelopipedRepresentation* rep = reinterpret_cast(self->WidgetRep); int interactionState = rep->GetInteractionState(); int newInteractionState = interactionState; if (interactionState == vtkParallelopipedRepresentation ::TranslatingParallelopiped) { // Dispatch to all widgets in the set. if (self->WidgetSet) { self->WidgetSet->DispatchAction(self, &vtkParallelopipedWidget::TranslateAction); } else { self->TranslateAction(self); } } else { // Let the representation re-compute what the appropriate state is. const int modifier = self->Interactor->GetShiftKey() | self->Interactor->GetControlKey() | self->Interactor->GetAltKey(); newInteractionState = rep->ComputeInteractionState( self->Interactor->GetEventPosition()[0], self->Interactor->GetEventPosition()[1], modifier); } // If we computed a different interaction state than the one we were in, // render in response to any changes. Also take care of trivial cases that // require no rendering. if (newInteractionState != interactionState || (newInteractionState != vtkParallelopipedRepresentation::Inside && newInteractionState != vtkParallelopipedRepresentation::Outside)) { self->Interactor->Render(); self->SetCursor(newInteractionState); self->InvokeEvent(vtkCommand::InteractionEvent, nullptr); } } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::SetCursor(int state) { switch (state) { case vtkParallelopipedRepresentation::ResizingParallelopiped: case vtkParallelopipedRepresentation::ResizingParallelopipedAlongAnAxis: this->RequestCursorShape(VTK_CURSOR_HAND); break; default: this->RequestCursorShape(VTK_CURSOR_DEFAULT); } } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::SetProcessEvents(vtkTypeBool pe) { this->Superclass::SetProcessEvents(pe); for (int i = 0; i < 8; i++) { this->HandleWidgets[i]->SetProcessEvents(pe); } } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::ReportReferences(vtkGarbageCollector* collector) { this->Superclass::ReportReferences(collector); vtkGarbageCollectorReport(collector, this->WidgetSet, "WidgetSet"); } //------------------------------------------------------------------------------ void vtkParallelopipedWidget::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Chair Creation: " << (this->EnableChairCreation ? "On\n" : "Off\n"); }