1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkControlPointsItem.h
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 
16 /**
17  * @class   vtkControlPointsItem
18  * @brief   Abstract class for control points items.
19  *
20  * vtkControlPointsItem provides control point painting and management for
21  * subclasses that provide points (typically control points of a transfer
22  * function)
23  * @sa
24  * vtkScalarsToColorsItem
25  * vtkPiecewiseControlPointsItem
26  */
27 
28 #ifndef vtkControlPointsItem_h
29 #define vtkControlPointsItem_h
30 
31 #include "vtkPlot.h"
32 
33 #include "vtkChartsCoreModule.h" // For export macro
34 #include "vtkCommand.h"          // For vtkCommand enum
35 #include "vtkSmartPointer.h"     // For SmartPointer
36 #include "vtkVector.h"           // For vtkVector2f
37 #include <string>                // Used by GetControlPointLabel
38 
39 class vtkCallbackCommand;
40 class vtkContext2D;
41 class vtkControlPointsAddPointItem;
42 class vtkPiecewisePointHandleItem;
43 class vtkPoints2D;
44 class vtkTransform2D;
45 
46 class VTKCHARTSCORE_EXPORT vtkControlPointsItem : public vtkPlot
47 {
48 public:
49   vtkTypeMacro(vtkControlPointsItem, vtkPlot);
50   void PrintSelf(ostream& os, vtkIndent indent) override;
51 
52   // Events fires by this class (and subclasses).
53   // \li CurrentPointChangedEvent is fired when the current point index is changed.
54   // \li CurrentPointEditEvent is fired to request the application to show UI to
55   // edit the current point.
56   // \li vtkCommand::StartEvent and vtkCommand::EndEvent is fired
57   // to mark groups of changes to control points.
58   enum
59   {
60     CurrentPointChangedEvent = vtkCommand::UserEvent,
61     CurrentPointEditEvent
62   };
63 
64   /**
65    * Bounds of the item, typically the bound of all the control points
66    * except if custom bounds have been set \sa SetUserBounds.
67    */
68   void GetBounds(double bounds[4]) override;
69 
70   ///@{
71   /**
72    * Set custom bounds, except if bounds are invalid, bounds will be
73    * automatically computed based on the range of the control points
74    * Invalid bounds by default.
75    */
76   vtkSetVector4Macro(UserBounds, double);
77   vtkGetVector4Macro(UserBounds, double);
78   ///@}
79 
80   ///@{
81   /**
82    * Controls the valid range for the values.
83    * An invalid value (0, -1, 0., -1, 0, -1.) indicates that the valid
84    * range is the current bounds. It is the default behavior.
85    */
86   vtkSetVector4Macro(ValidBounds, double);
87   vtkGetVector4Macro(ValidBounds, double);
88   ///@}
89 
90   ///@{
91   /**
92    * Get/set the radius for screen points.
93    * Default is 6.f
94    */
95   vtkGetMacro(ScreenPointRadius, float);
96   vtkSetMacro(ScreenPointRadius, float);
97   ///@}
98 
99   ///@{
100   /**
101    * Get/Set the flag to draw points.
102    * Default is true.
103    */
104   vtkGetMacro(DrawPoints, bool);
105   vtkSetMacro(DrawPoints, bool);
106   vtkBooleanMacro(DrawPoints, bool);
107   ///@}
108 
109   /**
110    * Paint the points with a fixed size (cosmetic) which doesn't depend
111    * on the scene zoom factor. Selected and unselected points are drawn
112    * with a different color.
113    */
114   bool Paint(vtkContext2D* painter) override;
115 
116   /**
117    * Select a point by its ID
118    */
119   void SelectPoint(vtkIdType pointId);
120 
121   /**
122    * Utility function that selects a point providing its coordinates.
123    * To be found, the position of the point must be no further away than its
124    * painted point size
125    */
126   void SelectPoint(double* currentPoint);
127 
128   /**
129    * Select all the points
130    */
131   void SelectAllPoints();
132 
133   /**
134    * Unselect a point by its ID
135    */
136   void DeselectPoint(vtkIdType pointId);
137 
138   /**
139    * Utility function that unselects a point providing its coordinates.
140    * To be found, the position of the point must be no further away than its
141    * painted point size
142    */
143   void DeselectPoint(double* currentPoint);
144 
145   /**
146    * Unselect all the previously selected points
147    */
148   void DeselectAllPoints();
149 
150   /**
151    * Toggle the selection of a point by its ID. If the point was selected then
152    * unselect it, otherwise select it.
153    */
154   void ToggleSelectPoint(vtkIdType pointId);
155 
156   /**
157    * Utility function that toggles the selection a point providing its
158    * coordinates. To be found, the position of the point must be no further
159    * away than its painted point size
160    */
161   void ToggleSelectPoint(double* currentPoint);
162 
163   /**
164    * Select all points in the specified rectangle.
165    */
166   bool SelectPoints(const vtkVector2f& min, const vtkVector2f& max) override;
167 
168   /**
169    * Return the number of selected points.
170    */
171   vtkIdType GetNumberOfSelectedPoints() const;
172 
173   /**
174    * Returns the vtkIdType of the point given its coordinates and a tolerance
175    * based on the screen point size.
176    */
177   vtkIdType FindPoint(double* pos);
178 
179   /**
180    * Returns true if pos is above the pointId point, false otherwise.
181    * It uses the size of the drawn point. To search what point is under the pos,
182    * use the more efficient \sa FindPoint() instead.
183    */
184   bool IsOverPoint(double* pos, vtkIdType pointId);
185 
186   /**
187    * Returns the id of the control point exactly matching pos, -1 if not found.
188    */
189   vtkIdType GetControlPointId(double* pos);
190 
191   /**
192    * Utility function that returns an array of all the control points IDs
193    * Typically: [0, 1, 2, ... n -1] where n is the point count
194    * Can exclude the first and last point ids from the array.
195    */
196   void GetControlPointsIds(vtkIdTypeArray* ids, bool excludeFirstAndLast = false) const;
197 
198   ///@{
199   /**
200    * Controls whether or not control points are drawn (true) or clicked and
201    * moved (false).
202    * False by default.
203    */
204   vtkSetMacro(StrokeMode, bool);
205   vtkGetMacro(StrokeMode, bool);
206   ///@}
207 
208   ///@{
209   /**
210    * If DrawPoints is false, SwitchPoints controls the behavior when a control
211    * point is dragged past another point. The crossed point becomes current
212    * (true) or the current point is blocked/stopped (false).
213    * False by default.
214    */
215   vtkSetMacro(SwitchPointsMode, bool);
216   vtkGetMacro(SwitchPointsMode, bool);
217   ///@}
218 
219   ///@{
220   /**
221    * If EndPointsMovable is false, the two end points will not
222    * be moved. True by default.
223    */
224   vtkSetMacro(EndPointsXMovable, bool);
225   vtkGetMacro(EndPointsXMovable, bool);
226   vtkSetMacro(EndPointsYMovable, bool);
227   vtkGetMacro(EndPointsYMovable, bool);
228   virtual bool GetEndPointsMovable();
229   ///@}
230 
231   ///@{
232   /**
233    * If EndPointsRemovable is false, the two end points will not
234    * be removed. True by default.
235    */
236   vtkSetMacro(EndPointsRemovable, bool);
237   vtkGetMacro(EndPointsRemovable, bool);
238   ///@}
239 
240   ///@{
241   /**
242    * When set to true, labels are shown on the current control point and the end
243    * points. Default is false.
244    */
245   vtkSetMacro(ShowLabels, bool);
246   vtkGetMacro(ShowLabels, bool);
247   ///@}
248 
249   ///@{
250   /**
251    * Get/Set the label format. Default is "%.4f, %.4f".
252    */
253   vtkSetStringMacro(LabelFormat);
254   vtkGetStringMacro(LabelFormat);
255   ///@}
256 
257   /**
258    * Add a point to the function. Returns the index of the point (0 based),
259    * or -1 on error.
260    * Subclasses should reimplement this function to do the actual work.
261    */
262   virtual vtkIdType AddPoint(double* newPos) = 0;
263 
264   /**
265    * Remove a point of the function. Returns the index of the point (0 based),
266    * or -1 on error.
267    * Subclasses should reimplement this function to do the actual work.
268    */
269   virtual vtkIdType RemovePoint(double* pos) = 0;
270 
271   /**
272    * Remove a point give its id. It is a utility function that internally call
273    * the virtual method RemovePoint(double*) and return its result.
274    */
275   vtkIdType RemovePoint(vtkIdType pointId);
276 
277   /**
278    * Remove the current point.
279    */
280   void RemoveCurrentPoint();
281 
282   /**
283    * Returns the total number of points
284    */
285   virtual vtkIdType GetNumberOfPoints() const = 0;
286 
287   /**
288    * Returns the x and y coordinates as well as the midpoint and sharpness
289    * of the control point corresponding to the index.
290    * point must be a double array of size 4.
291    */
292   virtual void GetControlPoint(vtkIdType index, double* point) const = 0;
293 
294   /**
295    * Sets the x and y coordinates as well as the midpoint and sharpness
296    * of the control point corresponding to the index.
297    */
298   virtual void SetControlPoint(vtkIdType index, double* point) = 0;
299 
300   /**
301    * Move the points referred by pointIds by a given translation.
302    * The new positions won't be outside the bounds.
303    * MovePoints is typically called with GetControlPointsIds() or GetSelection().
304    * Warning: if you pass this->GetSelection(), the array is deleted after
305    * each individual point move. Increase the reference count of the array.
306    * See also MoveAllPoints()
307    */
308   void MovePoints(const vtkVector2f& translation, vtkIdTypeArray* pointIds);
309 
310   /**
311    * Utility function to move all the control points of the given translation
312    * If dontMoveFirstAndLast is true, then the first and last points won't be
313    * moved.
314    */
315   void MovePoints(const vtkVector2f& translation, bool dontMoveFirstAndLast = false);
316 
317   /**
318    * Spread the points referred by pointIds
319    * If factor > 0, points are moved away from each other.
320    * If factor < 0, points are moved closer to each other
321    * SpreadPoints is typically called with GetControlPointsIds() or GetSelection().
322    * Warning: if you pass this->GetSelection(), the array is deleted after
323    * each individual point move. Increase the reference count of the array.
324    */
325   void SpreadPoints(float factor, vtkIdTypeArray* pointIds);
326 
327   /**
328    * Utility function to spread all the control points of a given factor
329    * If dontSpreadFirstAndLast is true, then the first and last points won't be
330    * spread.
331    */
332   void SpreadPoints(float factor, bool dontSpreadFirstAndLast = false);
333 
334   /**
335    * Returns the current point ID selected or -1 if there is no point current.
336    * No current point by default.
337    */
338   vtkIdType GetCurrentPoint() const;
339 
340   /**
341    * Sets the current point selected.
342    */
343   void SetCurrentPoint(vtkIdType index);
344 
345   ///@{
346   /**
347    * Gets the selected point pen and brush.
348    */
349   vtkGetObjectMacro(SelectedPointPen, vtkPen);
350   ///@}
351 
352   ///@{
353   /**
354    * Depending on the control points item, the brush might not be taken into
355    * account.
356    */
357   vtkGetObjectMacro(SelectedPointBrush, vtkBrush);
358   ///@}
359 
360   ///@{
361   /**
362    * When enabled, a dedicated item is used to determine if a point should
363    * be added when clicking anywhere.
364    * This item can be recovered with GetAddPointItem and can this be placed
365    * below all other items. False by default.
366    */
367   vtkGetMacro(UseAddPointItem, bool);
368   vtkSetMacro(UseAddPointItem, bool);
369   vtkBooleanMacro(UseAddPointItem, bool);
370   ///@}
371 
372   /**
373    * Item dedicated to add point, to be added below all other items.
374    * Used only if UseAddPointItem is set to true.
375    */
376   vtkPlot* GetAddPointItem();
377 
378   /**
379    * Recompute the bounds next time they are requested.
380    * You shouldn't have to call it but it is provided for rare cases.
381    */
382   void ResetBounds();
383 
384   ///@{
385   /**
386    * Mouse and key events.
387    */
388   bool MouseButtonPressEvent(const vtkContextMouseEvent& mouse) override;
389   bool MouseDoubleClickEvent(const vtkContextMouseEvent& mouse) override;
390   bool MouseButtonReleaseEvent(const vtkContextMouseEvent& mouse) override;
391   bool MouseMoveEvent(const vtkContextMouseEvent& mouse) override;
392   bool KeyPressEvent(const vtkContextKeyEvent& key) override;
393   bool KeyReleaseEvent(const vtkContextKeyEvent& key) override;
394   ///@}
395 
396 protected:
397   vtkControlPointsItem();
398   ~vtkControlPointsItem() override;
399 
400   friend class vtkPiecewisePointHandleItem;
401 
402   void StartChanges();
403   void EndChanges();
404   void StartInteraction();
405   void StartInteractionIfNotStarted();
406   void Interaction();
407   void EndInteraction();
408   int GetInteractionsCount() const;
409   virtual void emitEvent(unsigned long event, void* params = nullptr) = 0;
410 
411   static void CallComputePoints(
412     vtkObject* sender, unsigned long event, void* receiver, void* params);
413 
414   ///@{
415   /**
416    * Must be reimplemented by subclasses to calculate the points to draw.
417    * It's subclass responsibility to call ComputePoints() via the callback
418    */
419   virtual void ComputePoints();
420   virtual vtkMTimeType GetControlPointsMTime() = 0;
421   ///@}
422 
423   /**
424    * Returns true if the supplied x, y are within the bounds or on a control point.
425    * If UseAddPointItem is true,
426    * returns true only if the supplied x, y are on a control point.
427    */
428   bool Hit(const vtkContextMouseEvent& mouse) override;
429 
430   ///@{
431   /**
432    * Clamp the given 2D pos into the bounds of the function.
433    * Return true if the pos has been clamped, false otherwise.
434    */
435   bool ClampValidDataPos(double pos[2]);
436   bool ClampValidScreenPos(double pos[2]);
437   ///@}
438 
439   ///@{
440   /**
441    * Internal function that paints a collection of points and optionally
442    * excludes some.
443    */
444   void DrawUnselectedPoints(vtkContext2D* painter);
445   void DrawSelectedPoints(vtkContext2D* painter);
446   virtual void DrawPoint(vtkContext2D* painter, vtkIdType index);
447   ///@}
448 
449   void SetCurrentPointPos(const vtkVector2f& newPos);
450   vtkIdType SetPointPos(vtkIdType point, const vtkVector2f& newPos);
451   void MoveCurrentPoint(const vtkVector2f& translation);
452   vtkIdType MovePoint(vtkIdType point, const vtkVector2f& translation);
453 
454   vtkVector2f GetSelectionCenterOfMass() const;
455   vtkVector2f GetCenterOfMass(vtkIdTypeArray* pointIDs) const;
456 
457   void Stroke(const vtkVector2f& newPos);
458   virtual void EditPoint(float vtkNotUsed(tX), float vtkNotUsed(tY));
459 
460   /**
461    * Generate label for a control point.
462    */
463   virtual std::string GetControlPointLabel(vtkIdType index);
464 
465   void AddPointId(vtkIdType addedPointId);
466 
467   /**
468    * Return true if any of the end points is current point
469    * or part of the selection
470    */
471   bool IsEndPointPicked();
472 
473   /**
474    * Return true if the point is removable
475    */
476   bool IsPointRemovable(vtkIdType pointId);
477 
478   /**
479    * Compute the bounds for this item. Typically, the bounds should be aligned
480    * to the range of the vtkScalarsToColors or vtkPiecewiseFunction that is
481    * being controlled by the subclasses.
482    * Default implementation uses the range of the control points themselves.
483    */
484   virtual void ComputeBounds(double* bounds);
485 
486   vtkNew<vtkCallbackCommand> Callback;
487   vtkNew<vtkPen> SelectedPointPen;
488   vtkNew<vtkBrush> SelectedPointBrush;
489   int BlockUpdates = 0;
490   int StartedInteractions = 0;
491   int StartedChanges = 0;
492   vtkIdType CurrentPoint = -1;
493 
494   double Bounds[4] = { 0., -1., 0., -1. };
495   double UserBounds[4] = { 0., -1., 0., -1. };
496   double ValidBounds[4] = { 0., -1., 0., -1. };
497 
498   vtkNew<vtkTransform2D> ControlPointsTransform;
499   float ScreenPointRadius = 6.f;
500 
501   bool DrawPoints = true;
502   bool StrokeMode = false;
503   bool SwitchPointsMode = false;
504   bool MouseMoved = false;
505   bool EnforceValidFunction = true;
506   vtkIdType PointToDelete = -1;
507   bool PointAboutToBeDeleted = false;
508   vtkIdType PointToToggle = -1;
509   bool PointAboutToBeToggled = false;
510   bool InvertShadow = false;
511   bool EndPointsXMovable = true;
512   bool EndPointsYMovable = true;
513   bool EndPointsRemovable = true;
514   bool ShowLabels = false;
515   char* LabelFormat = nullptr;
516 
517 private:
518   vtkControlPointsItem(const vtkControlPointsItem&) = delete;
519   void operator=(const vtkControlPointsItem&) = delete;
520 
521   void ComputeBounds();
522 
523   vtkIdType RemovePointId(vtkIdType removedPointId);
524 
525   bool UseAddPointItem = false;
526   vtkNew<vtkControlPointsAddPointItem> AddPointItem;
527 };
528 #endif
529