1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkXYPlotWidget.cxx
5
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE. See the above copyright notice for more information.
13
14 =========================================================================*/
15 #include "vtkXYPlotWidget.h"
16 #include "vtkCallbackCommand.h"
17 #include "vtkCoordinate.h"
18 #include "vtkObjectFactory.h"
19 #include "vtkRenderWindow.h"
20 #include "vtkRenderWindowInteractor.h"
21 #include "vtkRenderer.h"
22 #include "vtkXYPlotActor.h"
23
24 #include <cmath>
25
26 vtkStandardNewMacro(vtkXYPlotWidget);
27 vtkCxxSetObjectMacro(vtkXYPlotWidget, XYPlotActor, vtkXYPlotActor);
28
29 //------------------------------------------------------------------------------
vtkXYPlotWidget()30 vtkXYPlotWidget::vtkXYPlotWidget()
31 {
32 this->XYPlotActor = vtkXYPlotActor::New();
33 this->EventCallbackCommand->SetCallback(vtkXYPlotWidget::ProcessEvents);
34 this->State = vtkXYPlotWidget::Outside;
35 this->Priority = 0.55;
36 }
37
38 //------------------------------------------------------------------------------
~vtkXYPlotWidget()39 vtkXYPlotWidget::~vtkXYPlotWidget()
40 {
41 if (this->XYPlotActor)
42 {
43 this->XYPlotActor->Delete();
44 }
45 }
46
47 //------------------------------------------------------------------------------
SetEnabled(int enabling)48 void vtkXYPlotWidget::SetEnabled(int enabling)
49 {
50 if (!this->Interactor)
51 {
52 vtkErrorMacro(<< "The interactor must be set prior to enabling/disabling widget");
53 return;
54 }
55
56 if (enabling)
57 {
58 vtkDebugMacro(<< "Enabling line widget");
59 if (this->Enabled) // already enabled, just return
60 {
61 return;
62 }
63
64 if (!this->CurrentRenderer)
65 {
66 this->SetCurrentRenderer(this->Interactor->FindPokedRenderer(
67 this->Interactor->GetLastEventPosition()[0], this->Interactor->GetLastEventPosition()[1]));
68 if (this->CurrentRenderer == nullptr)
69 {
70 return;
71 }
72 }
73
74 this->Enabled = 1;
75
76 // listen for the following events
77 vtkRenderWindowInteractor* i = this->Interactor;
78 i->AddObserver(vtkCommand::MouseMoveEvent, this->EventCallbackCommand, this->Priority);
79 i->AddObserver(vtkCommand::LeftButtonPressEvent, this->EventCallbackCommand, this->Priority);
80 i->AddObserver(vtkCommand::LeftButtonReleaseEvent, this->EventCallbackCommand, this->Priority);
81
82 // Add the xy plot
83 this->CurrentRenderer->AddViewProp(this->XYPlotActor);
84 this->InvokeEvent(vtkCommand::EnableEvent, nullptr);
85 }
86 else // disabling------------------------------------------
87 {
88 vtkDebugMacro(<< "Disabling line widget");
89 if (!this->Enabled) // already disabled, just return
90 {
91 return;
92 }
93 this->Enabled = 0;
94
95 // don't listen for events any more
96 this->Interactor->RemoveObserver(this->EventCallbackCommand);
97
98 // turn off the line
99 this->CurrentRenderer->RemoveActor(this->XYPlotActor);
100 this->InvokeEvent(vtkCommand::DisableEvent, nullptr);
101 this->SetCurrentRenderer(nullptr);
102 }
103
104 this->Interactor->Render();
105 }
106
107 //------------------------------------------------------------------------------
ProcessEvents(vtkObject * vtkNotUsed (object),unsigned long event,void * clientdata,void * vtkNotUsed (calldata))108 void vtkXYPlotWidget::ProcessEvents(
109 vtkObject* vtkNotUsed(object), unsigned long event, void* clientdata, void* vtkNotUsed(calldata))
110 {
111 vtkXYPlotWidget* self = reinterpret_cast<vtkXYPlotWidget*>(clientdata);
112
113 // okay, let's do the right thing
114 switch (event)
115 {
116 case vtkCommand::LeftButtonPressEvent:
117 self->OnLeftButtonDown();
118 break;
119 case vtkCommand::LeftButtonReleaseEvent:
120 self->OnLeftButtonUp();
121 break;
122 case vtkCommand::MouseMoveEvent:
123 self->OnMouseMove();
124 break;
125 }
126 }
127
128 //------------------------------------------------------------------------------
ComputeStateBasedOnPosition(int X,int Y,int * pos1,int * pos2)129 int vtkXYPlotWidget::ComputeStateBasedOnPosition(int X, int Y, int* pos1, int* pos2)
130 {
131 int Result;
132
133 // what are we modifying? The position, or size?
134 // if size what piece?
135 // if we are within 7 pixels of an edge...
136 int e1 = 0;
137 int e2 = 0;
138 int e3 = 0;
139 int e4 = 0;
140 if (X - pos1[0] < 7)
141 {
142 e1 = 1;
143 }
144 if (pos2[0] - X < 7)
145 {
146 e3 = 1;
147 }
148 if (Y - pos1[1] < 7)
149 {
150 e2 = 1;
151 }
152 if (pos2[1] - Y < 7)
153 {
154 e4 = 1;
155 }
156
157 // assume we are moving
158 Result = vtkXYPlotWidget::Moving;
159 // unless we are on a corner or edges
160 if (e2)
161 {
162 Result = vtkXYPlotWidget::AdjustingE2;
163 }
164 if (e4)
165 {
166 Result = vtkXYPlotWidget::AdjustingE4;
167 }
168 if (e1)
169 {
170 Result = vtkXYPlotWidget::AdjustingE1;
171 if (e2)
172 {
173 Result = vtkXYPlotWidget::AdjustingP1;
174 }
175 if (e4)
176 {
177 Result = vtkXYPlotWidget::AdjustingP4;
178 }
179 }
180 if (e3)
181 {
182 Result = vtkXYPlotWidget::AdjustingE3;
183 if (e2)
184 {
185 Result = vtkXYPlotWidget::AdjustingP2;
186 }
187 if (e4)
188 {
189 Result = vtkXYPlotWidget::AdjustingP3;
190 }
191 }
192
193 return Result;
194 }
195
196 //------------------------------------------------------------------------------
SetCursor(int cState)197 void vtkXYPlotWidget::SetCursor(int cState)
198 {
199 switch (cState)
200 {
201 case vtkXYPlotWidget::AdjustingP1:
202 this->RequestCursorShape(VTK_CURSOR_SIZESW);
203 break;
204 case vtkXYPlotWidget::AdjustingP3:
205 this->RequestCursorShape(VTK_CURSOR_SIZENE);
206 break;
207 case vtkXYPlotWidget::AdjustingP2:
208 this->RequestCursorShape(VTK_CURSOR_SIZESE);
209 break;
210 case vtkXYPlotWidget::AdjustingP4:
211 this->RequestCursorShape(VTK_CURSOR_SIZENW);
212 break;
213 case vtkXYPlotWidget::AdjustingE1:
214 case vtkXYPlotWidget::AdjustingE3:
215 this->RequestCursorShape(VTK_CURSOR_SIZEWE);
216 break;
217 case vtkXYPlotWidget::AdjustingE2:
218 case vtkXYPlotWidget::AdjustingE4:
219 this->RequestCursorShape(VTK_CURSOR_SIZENS);
220 break;
221 case vtkXYPlotWidget::Moving:
222 this->RequestCursorShape(VTK_CURSOR_SIZEALL);
223 break;
224 }
225 }
226
227 //------------------------------------------------------------------------------
OnLeftButtonDown()228 void vtkXYPlotWidget::OnLeftButtonDown()
229 {
230 // We're only here is we are enabled
231 int X = this->Interactor->GetEventPosition()[0];
232 int Y = this->Interactor->GetEventPosition()[1];
233
234 // are we over the widget?
235 // this->Interactor->FindPokedRenderer(X,Y);
236 int* pos1 =
237 this->XYPlotActor->GetPositionCoordinate()->GetComputedDisplayValue(this->CurrentRenderer);
238 int* pos2 =
239 this->XYPlotActor->GetPosition2Coordinate()->GetComputedDisplayValue(this->CurrentRenderer);
240
241 // are we not over the xy plot, ignore
242 if (X < pos1[0] || X > pos2[0] || Y < pos1[1] || Y > pos2[1])
243 {
244 return;
245 }
246
247 // start a drag, store the normalized view coords
248 double X2 = X;
249 double Y2 = Y;
250 // convert to normalized viewport coordinates
251 this->CurrentRenderer->DisplayToNormalizedDisplay(X2, Y2);
252 this->CurrentRenderer->NormalizedDisplayToViewport(X2, Y2);
253 this->CurrentRenderer->ViewportToNormalizedViewport(X2, Y2);
254 this->StartPosition[0] = X2;
255 this->StartPosition[1] = Y2;
256
257 this->State = this->ComputeStateBasedOnPosition(X, Y, pos1, pos2);
258 this->SetCursor(this->State);
259
260 this->EventCallbackCommand->SetAbortFlag(1);
261 this->StartInteraction();
262 this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
263 }
264
265 //------------------------------------------------------------------------------
OnMouseMove()266 void vtkXYPlotWidget::OnMouseMove()
267 {
268 // compute some info we need for all cases
269 int X = this->Interactor->GetEventPosition()[0];
270 int Y = this->Interactor->GetEventPosition()[1];
271
272 // compute the display bounds of the xy plot if we are inside or outside
273 int *pos1, *pos2;
274 if (this->State == vtkXYPlotWidget::Outside || this->State == vtkXYPlotWidget::Inside)
275 {
276 pos1 =
277 this->XYPlotActor->GetPositionCoordinate()->GetComputedDisplayValue(this->CurrentRenderer);
278 pos2 =
279 this->XYPlotActor->GetPosition2Coordinate()->GetComputedDisplayValue(this->CurrentRenderer);
280
281 if (this->State == vtkXYPlotWidget::Outside)
282 {
283 // if we are not over the xy plot, ignore
284 if (X < pos1[0] || X > pos2[0] || Y < pos1[1] || Y > pos2[1])
285 {
286 return;
287 }
288 // otherwise change our state to inside
289 this->State = vtkXYPlotWidget::Inside;
290 }
291
292 // if inside, set the cursor to the correct shape
293 if (this->State == vtkXYPlotWidget::Inside)
294 {
295 // if we have left then change cursor back to default
296 if (X < pos1[0] || X > pos2[0] || Y < pos1[1] || Y > pos2[1])
297 {
298 this->State = vtkXYPlotWidget::Outside;
299 this->RequestCursorShape(VTK_CURSOR_DEFAULT);
300 return;
301 }
302 // adjust the cursor based on our position
303 this->SetCursor(this->ComputeStateBasedOnPosition(X, Y, pos1, pos2));
304 return;
305 }
306 }
307
308 double XF = X;
309 double YF = Y;
310 // convert to normalized viewport coordinates
311 this->CurrentRenderer->DisplayToNormalizedDisplay(XF, YF);
312 this->CurrentRenderer->NormalizedDisplayToViewport(XF, YF);
313 this->CurrentRenderer->ViewportToNormalizedViewport(XF, YF);
314
315 // there are four parameters that can be adjusted
316 double* fpos1 = this->XYPlotActor->GetPositionCoordinate()->GetValue();
317 double* fpos2 = this->XYPlotActor->GetPosition2Coordinate()->GetValue();
318 float par1[2];
319 float par2[2];
320 par1[0] = fpos1[0];
321 par1[1] = fpos1[1];
322 par2[0] = fpos1[0] + fpos2[0];
323 par2[1] = fpos1[1] + fpos2[1];
324
325 // based on the state, adjust the xy plot parameters
326 switch (this->State)
327 {
328 case vtkXYPlotWidget::AdjustingP1:
329 par1[0] = par1[0] + XF - this->StartPosition[0];
330 par1[1] = par1[1] + YF - this->StartPosition[1];
331 break;
332 case vtkXYPlotWidget::AdjustingP2:
333 par2[0] = par2[0] + XF - this->StartPosition[0];
334 par1[1] = par1[1] + YF - this->StartPosition[1];
335 break;
336 case vtkXYPlotWidget::AdjustingP3:
337 par2[0] = par2[0] + XF - this->StartPosition[0];
338 par2[1] = par2[1] + YF - this->StartPosition[1];
339 break;
340 case vtkXYPlotWidget::AdjustingP4:
341 par1[0] = par1[0] + XF - this->StartPosition[0];
342 par2[1] = par2[1] + YF - this->StartPosition[1];
343 break;
344 case vtkXYPlotWidget::AdjustingE1:
345 par1[0] = par1[0] + XF - this->StartPosition[0];
346 break;
347 case vtkXYPlotWidget::AdjustingE2:
348 par1[1] = par1[1] + YF - this->StartPosition[1];
349 break;
350 case vtkXYPlotWidget::AdjustingE3:
351 par2[0] = par2[0] + XF - this->StartPosition[0];
352 break;
353 case vtkXYPlotWidget::AdjustingE4:
354 par2[1] = par2[1] + YF - this->StartPosition[1];
355 break;
356 case vtkXYPlotWidget::Moving:
357 // first apply the move
358 par1[0] = par1[0] + XF - this->StartPosition[0];
359 par1[1] = par1[1] + YF - this->StartPosition[1];
360 par2[0] = par2[0] + XF - this->StartPosition[0];
361 par2[1] = par2[1] + YF - this->StartPosition[1];
362 // then check for an orientation change if the xy plot moves so that
363 // its center is closer to a different edge that its current edge by
364 // 0.2 then swap orientation
365 float centerX = (par1[0] + par2[0]) / 2.0;
366 float centerY = (par1[1] + par2[1]) / 2.0;
367 // what edge is it closest to
368 if (fabs(centerX - 0.5) > fabs(centerY - 0.5))
369 {
370 // is it far enough in to consider a change in orientation?
371 if (fabs(centerX - 0.5) > 0.2 + fabs(centerY - 0.5))
372 {
373 // do we need to change orientation
374 if (!this->XYPlotActor->GetExchangeAxes())
375 {
376 this->XYPlotActor->SetExchangeAxes(1);
377 // also change the corners
378 par2[0] = centerX + centerY - par1[1];
379 par2[1] = centerY + centerX - par1[0];
380 par1[0] = 2 * centerX - par2[0];
381 par1[1] = 2 * centerY - par2[1];
382 }
383 }
384 }
385 else
386 {
387 // is it far enough in to consider a change in orientation?
388 if (fabs(centerY - 0.5) > 0.2 + fabs(centerX - 0.5))
389 {
390 // do we need to change orientation
391 if (this->XYPlotActor->GetExchangeAxes())
392 {
393 this->XYPlotActor->SetExchangeAxes(0);
394 // also change the corners
395 par2[0] = centerX + centerY - par1[1];
396 par2[1] = centerY + centerX - par1[0];
397 par1[0] = 2 * centerX - par2[0];
398 par1[1] = 2 * centerY - par2[1];
399 }
400 }
401 }
402 break;
403 }
404
405 // push the change out to the xy plot
406 // make sure the xy plot doesn't shrink to nothing
407 if (par2[0] > par1[0] && par2[1] > par1[1])
408 {
409 this->XYPlotActor->GetPositionCoordinate()->SetValue(par1[0], par1[1]);
410 this->XYPlotActor->GetPosition2Coordinate()->SetValue(par2[0] - par1[0], par2[1] - par1[1]);
411 this->StartPosition[0] = XF;
412 this->StartPosition[1] = YF;
413 }
414
415 // start a drag
416 this->EventCallbackCommand->SetAbortFlag(1);
417 this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
418 this->Interactor->Render();
419 }
420
421 //------------------------------------------------------------------------------
OnLeftButtonUp()422 void vtkXYPlotWidget::OnLeftButtonUp()
423 {
424 if (this->State == vtkXYPlotWidget::Outside)
425 {
426 return;
427 }
428
429 // stop adjusting
430 this->State = vtkXYPlotWidget::Outside;
431 this->EventCallbackCommand->SetAbortFlag(1);
432 this->RequestCursorShape(VTK_CURSOR_DEFAULT);
433 this->EndInteraction();
434 this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
435 this->Interactor->Render();
436 }
437
438 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)439 void vtkXYPlotWidget::PrintSelf(ostream& os, vtkIndent indent)
440 {
441 this->Superclass::PrintSelf(os, indent);
442
443 os << indent << "XYPlotActor: " << this->XYPlotActor << "\n";
444 }
445