/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkXYPlotWidget.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 "vtkXYPlotWidget.h" #include "vtkXYPlotActor.h" #include "vtkCallbackCommand.h" #include "vtkObjectFactory.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkCoordinate.h" vtkCxxRevisionMacro(vtkXYPlotWidget, "$Revision: 1.1 $"); vtkStandardNewMacro(vtkXYPlotWidget); vtkCxxSetObjectMacro(vtkXYPlotWidget, XYPlotActor, vtkXYPlotActor); vtkXYPlotWidget::vtkXYPlotWidget() { this->XYPlotActor = vtkXYPlotActor::New(); this->EventCallbackCommand->SetCallback(vtkXYPlotWidget::ProcessEvents); this->State = vtkXYPlotWidget::Outside; this->Priority = 0.55; } vtkXYPlotWidget::~vtkXYPlotWidget() { if (this->XYPlotActor) { this->XYPlotActor->Delete(); } } void vtkXYPlotWidget::SetEnabled(int enabling) { if ( ! this->Interactor ) { vtkErrorMacro(<<"The interactor must be set prior to enabling/disabling widget"); return; } if ( enabling ) { vtkDebugMacro(<<"Enabling 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 == 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); // Add the xy plot this->CurrentRenderer->AddViewProp(this->XYPlotActor); this->InvokeEvent(vtkCommand::EnableEvent,NULL); } else //disabling------------------------------------------ { vtkDebugMacro(<<"Disabling 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->XYPlotActor); this->InvokeEvent(vtkCommand::DisableEvent,NULL); this->SetCurrentRenderer(NULL); } this->Interactor->Render(); } void vtkXYPlotWidget::ProcessEvents(vtkObject* vtkNotUsed(object), unsigned long event, void* clientdata, void* vtkNotUsed(calldata)) { vtkXYPlotWidget* 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::MouseMoveEvent: self->OnMouseMove(); break; } } int vtkXYPlotWidget::ComputeStateBasedOnPosition(int X, int Y, int *pos1, int *pos2) { int Result; // what are we modifying? The position, or size? // if size what piece? // if we are within 7 pixels of an edge... int e1 = 0; int e2 = 0; int e3 = 0; int e4 = 0; if (X - pos1[0] < 7) { e1 = 1; } if (pos2[0] - X < 7) { e3 = 1; } if (Y - pos1[1] < 7) { e2 = 1; } if (pos2[1] - Y < 7) { e4 = 1; } // assume we are moving Result = vtkXYPlotWidget::Moving; // unless we are on a corner or edges if (e2) { Result = vtkXYPlotWidget::AdjustingE2; } if (e4) { Result = vtkXYPlotWidget::AdjustingE4; } if (e1) { Result = vtkXYPlotWidget::AdjustingE1; if (e2) { Result = vtkXYPlotWidget::AdjustingP1; } if (e4) { Result = vtkXYPlotWidget::AdjustingP4; } } if (e3) { Result = vtkXYPlotWidget::AdjustingE3; if (e2) { Result = vtkXYPlotWidget::AdjustingP2; } if (e4) { Result = vtkXYPlotWidget::AdjustingP3; } } return Result; } void vtkXYPlotWidget::SetCursor(int cState) { switch (cState) { case vtkXYPlotWidget::AdjustingP1: this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZESW); break; case vtkXYPlotWidget::AdjustingP3: this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZENE); break; case vtkXYPlotWidget::AdjustingP2: this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZESE); break; case vtkXYPlotWidget::AdjustingP4: this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZENW); break; case vtkXYPlotWidget::AdjustingE1: case vtkXYPlotWidget::AdjustingE3: this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZEWE); break; case vtkXYPlotWidget::AdjustingE2: case vtkXYPlotWidget::AdjustingE4: this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZENS); break; case vtkXYPlotWidget::Moving: this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZEALL); break; } } void vtkXYPlotWidget::OnLeftButtonDown() { // We're only here is we are enabled int X = this->Interactor->GetEventPosition()[0]; int Y = this->Interactor->GetEventPosition()[1]; // are we over the widget? //this->Interactor->FindPokedRenderer(X,Y); int *pos1 = this->XYPlotActor->GetPositionCoordinate() ->GetComputedDisplayValue(this->CurrentRenderer); int *pos2 = this->XYPlotActor->GetPosition2Coordinate() ->GetComputedDisplayValue(this->CurrentRenderer); // are we not over the xy plot, ignore if (X < pos1[0] || X > pos2[0] || Y < pos1[1] || Y > pos2[1]) { return; } // start a drag, store the normalized view coords double X2 = X; double Y2 = Y; // convert to normalized viewport coordinates this->CurrentRenderer->DisplayToNormalizedDisplay(X2,Y2); this->CurrentRenderer->NormalizedDisplayToViewport(X2,Y2); this->CurrentRenderer->ViewportToNormalizedViewport(X2,Y2); this->StartPosition[0] = X2; this->StartPosition[1] = Y2; this->State = this->ComputeStateBasedOnPosition(X, Y, pos1, pos2); this->SetCursor(this->State); this->EventCallbackCommand->SetAbortFlag(1); this->StartInteraction(); this->InvokeEvent(vtkCommand::StartInteractionEvent,NULL); } void vtkXYPlotWidget::OnMouseMove() { // compute some info we need for all cases int X = this->Interactor->GetEventPosition()[0]; int Y = this->Interactor->GetEventPosition()[1]; // compute the display bounds of the xy plot if we are inside or outside int *pos1, *pos2; if (this->State == vtkXYPlotWidget::Outside || this->State == vtkXYPlotWidget::Inside) { pos1 = this->XYPlotActor->GetPositionCoordinate() ->GetComputedDisplayValue(this->CurrentRenderer); pos2 = this->XYPlotActor->GetPosition2Coordinate() ->GetComputedDisplayValue(this->CurrentRenderer); if (this->State == vtkXYPlotWidget::Outside) { // if we are not over the xy plot, ignore if (X < pos1[0] || X > pos2[0] || Y < pos1[1] || Y > pos2[1]) { return; } // otherwise change our state to inside this->State = vtkXYPlotWidget::Inside; } // if inside, set the cursor to the correct shape if (this->State == vtkXYPlotWidget::Inside) { // if we have left then change cursor back to default if (X < pos1[0] || X > pos2[0] || Y < pos1[1] || Y > pos2[1]) { this->State = vtkXYPlotWidget::Outside; this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_DEFAULT); return; } // adjust the cursor based on our position this->SetCursor(this->ComputeStateBasedOnPosition(X,Y,pos1,pos2)); return; } } double XF = X; double YF = Y; // convert to normalized viewport coordinates this->CurrentRenderer->DisplayToNormalizedDisplay(XF,YF); this->CurrentRenderer->NormalizedDisplayToViewport(XF,YF); this->CurrentRenderer->ViewportToNormalizedViewport(XF,YF); // there are four parameters that can be adjusted double *fpos1 = this->XYPlotActor->GetPositionCoordinate()->GetValue(); double *fpos2 = this->XYPlotActor->GetPosition2Coordinate()->GetValue(); float par1[2]; float par2[2]; par1[0] = fpos1[0]; par1[1] = fpos1[1]; par2[0] = fpos1[0] + fpos2[0]; par2[1] = fpos1[1] + fpos2[1]; // based on the state, adjust the xy plot parameters switch (this->State) { case vtkXYPlotWidget::AdjustingP1: par1[0] = par1[0] + XF - this->StartPosition[0]; par1[1] = par1[1] + YF - this->StartPosition[1]; break; case vtkXYPlotWidget::AdjustingP2: par2[0] = par2[0] + XF - this->StartPosition[0]; par1[1] = par1[1] + YF - this->StartPosition[1]; break; case vtkXYPlotWidget::AdjustingP3: par2[0] = par2[0] + XF - this->StartPosition[0]; par2[1] = par2[1] + YF - this->StartPosition[1]; break; case vtkXYPlotWidget::AdjustingP4: par1[0] = par1[0] + XF - this->StartPosition[0]; par2[1] = par2[1] + YF - this->StartPosition[1]; break; case vtkXYPlotWidget::AdjustingE1: par1[0] = par1[0] + XF - this->StartPosition[0]; break; case vtkXYPlotWidget::AdjustingE2: par1[1] = par1[1] + YF - this->StartPosition[1]; break; case vtkXYPlotWidget::AdjustingE3: par2[0] = par2[0] + XF - this->StartPosition[0]; break; case vtkXYPlotWidget::AdjustingE4: par2[1] = par2[1] + YF - this->StartPosition[1]; break; case vtkXYPlotWidget::Moving: // first apply the move par1[0] = par1[0] + XF - this->StartPosition[0]; par1[1] = par1[1] + YF - this->StartPosition[1]; par2[0] = par2[0] + XF - this->StartPosition[0]; par2[1] = par2[1] + YF - this->StartPosition[1]; // then check for an orientation change if the xy plot moves so that // its center is closer to a different edge that its current edge by // 0.2 then swap orientation float centerX = (par1[0] + par2[0])/2.0; float centerY = (par1[1] + par2[1])/2.0; // what edge is it closest to if (fabs(centerX - 0.5) > fabs(centerY - 0.5)) { // is it far enough in to consider a change in orientation? if (fabs(centerX - 0.5) > 0.2+fabs(centerY - 0.5)) { // do we need to change orientation if (!this->XYPlotActor->GetExchangeAxes()) { this->XYPlotActor->SetExchangeAxes(1); // also change the corners par2[0] = centerX + centerY - par1[1]; par2[1] = centerY + centerX - par1[0]; par1[0] = 2*centerX - par2[0]; par1[1] = 2*centerY - par2[1]; } } } else { // is it far enough in to consider a change in orientation? if (fabs(centerY - 0.5) > 0.2+fabs(centerX - 0.5)) { // do we need to change orientation if (this->XYPlotActor->GetExchangeAxes()) { this->XYPlotActor->SetExchangeAxes(0); // also change the corners par2[0] = centerX + centerY - par1[1]; par2[1] = centerY + centerX - par1[0]; par1[0] = 2*centerX - par2[0]; par1[1] = 2*centerY - par2[1]; } } } break; } // push the change out to the xy plot // make sure the xy plot doesn't shrink to nothing if (par2[0] > par1[0] && par2[1] > par1[1]) { this->XYPlotActor->GetPositionCoordinate()->SetValue(par1[0],par1[1]); this->XYPlotActor->GetPosition2Coordinate()-> SetValue(par2[0] - par1[0], par2[1] - par1[1]); this->StartPosition[0] = XF; this->StartPosition[1] = YF; } // start a drag this->EventCallbackCommand->SetAbortFlag(1); this->InvokeEvent(vtkCommand::InteractionEvent, NULL); this->Interactor->Render(); } void vtkXYPlotWidget::OnLeftButtonUp() { if (this->State == vtkXYPlotWidget::Outside) { return; } // stop adjusting this->State = vtkXYPlotWidget::Outside; this->EventCallbackCommand->SetAbortFlag(1); this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_DEFAULT); this->EndInteraction(); this->InvokeEvent(vtkCommand::EndInteractionEvent,NULL); this->Interactor->Render(); } void vtkXYPlotWidget::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "XYPlotActor: " << this->XYPlotActor << "\n"; }