1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkCompositeControlPointsItem.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
16 #include "vtkCompositeControlPointsItem.h"
17 #include "vtkBrush.h"
18 #include "vtkCallbackCommand.h"
19 #include "vtkColorTransferFunction.h"
20 #include "vtkContext2D.h"
21 #include "vtkContextScene.h"
22 #include "vtkIdTypeArray.h"
23 #include "vtkObjectFactory.h"
24 #include "vtkPen.h"
25 #include "vtkPiecewiseFunction.h"
26 #include "vtkPiecewisePointHandleItem.h"
27 #include "vtkPoints2D.h"
28
29 // to handle mouse.GetButton
30 #include "vtkContextMouseEvent.h"
31
32 #include <algorithm>
33 #include <cassert>
34 #include <limits>
35
36 //------------------------------------------------------------------------------
37 vtkStandardNewMacro(vtkCompositeControlPointsItem);
38
39 //------------------------------------------------------------------------------
vtkCompositeControlPointsItem()40 vtkCompositeControlPointsItem::vtkCompositeControlPointsItem()
41 {
42 this->SetColorFill(true);
43 }
44
45 //------------------------------------------------------------------------------
~vtkCompositeControlPointsItem()46 vtkCompositeControlPointsItem::~vtkCompositeControlPointsItem()
47 {
48 if (this->OpacityFunction)
49 {
50 this->OpacityFunction->RemoveObserver(this->Callback);
51 this->OpacityFunction->Delete();
52 this->OpacityFunction = nullptr;
53 }
54 if (this->OpacityPointHandle)
55 {
56 this->OpacityPointHandle->Delete();
57 this->OpacityPointHandle = nullptr;
58 }
59 }
60
61 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)62 void vtkCompositeControlPointsItem::PrintSelf(ostream& os, vtkIndent indent)
63 {
64 this->Superclass::PrintSelf(os, indent);
65 os << indent << "OpacityFunction: ";
66 if (this->OpacityFunction)
67 {
68 os << endl;
69 this->OpacityFunction->PrintSelf(os, indent.GetNextIndent());
70 }
71 else
72 {
73 os << "(none)" << endl;
74 }
75 os << indent << "OpacityFunction: ";
76 if (this->OpacityPointHandle)
77 {
78 os << endl;
79 this->OpacityPointHandle->PrintSelf(os, indent.GetNextIndent());
80 }
81 else
82 {
83 os << "(none)" << endl;
84 }
85 os << indent << "UseOpacityPointHandles: " << this->UseOpacityPointHandles << endl;
86 }
87
88 //------------------------------------------------------------------------------
emitEvent(unsigned long event,void * params)89 void vtkCompositeControlPointsItem::emitEvent(unsigned long event, void* params)
90 {
91 if (this->OpacityFunction)
92 {
93 this->OpacityFunction->InvokeEvent(event, params);
94 }
95 this->Superclass::emitEvent(event, params);
96 }
97
98 //------------------------------------------------------------------------------
GetControlPointsMTime()99 vtkMTimeType vtkCompositeControlPointsItem::GetControlPointsMTime()
100 {
101 vtkMTimeType mTime = this->Superclass::GetControlPointsMTime();
102 if (this->OpacityFunction)
103 {
104 mTime = std::max(mTime, this->OpacityFunction->GetMTime());
105 }
106 return mTime;
107 }
108
109 //------------------------------------------------------------------------------
SetOpacityFunction(vtkPiecewiseFunction * function)110 void vtkCompositeControlPointsItem::SetOpacityFunction(vtkPiecewiseFunction* function)
111 {
112 if (function == this->OpacityFunction)
113 {
114 return;
115 }
116 if (this->OpacityFunction)
117 {
118 this->OpacityFunction->RemoveObserver(this->Callback);
119 }
120 vtkSetObjectBodyMacro(OpacityFunction, vtkPiecewiseFunction, function);
121 if (this->PointsFunction == ColorAndOpacityPointsFunction)
122 {
123 this->SilentMergeTransferFunctions();
124 }
125 if (this->OpacityFunction)
126 {
127 this->OpacityFunction->AddObserver(vtkCommand::StartEvent, this->Callback);
128 this->OpacityFunction->AddObserver(vtkCommand::ModifiedEvent, this->Callback);
129 this->OpacityFunction->AddObserver(vtkCommand::EndEvent, this->Callback);
130 }
131 this->ResetBounds();
132 this->ComputePoints();
133 }
134
135 //------------------------------------------------------------------------------
SetColorTransferFunction(vtkColorTransferFunction * c)136 void vtkCompositeControlPointsItem::SetColorTransferFunction(vtkColorTransferFunction* c)
137 {
138 if (c == this->ColorTransferFunction)
139 {
140 return;
141 }
142 // We need to set the color transfer function here (before
143 // Superclass::SetPiecewiseFunction) to be able to have a valid
144 // color transfer function for MergeColorTransferFunction().
145 this->Superclass::SetColorTransferFunction(c);
146 if (this->PointsFunction == ColorAndOpacityPointsFunction)
147 {
148 this->SilentMergeTransferFunctions();
149 }
150 }
151
152 //------------------------------------------------------------------------------
DrawPoint(vtkContext2D * painter,vtkIdType index)153 void vtkCompositeControlPointsItem::DrawPoint(vtkContext2D* painter, vtkIdType index)
154 {
155 if (this->PointsFunction == ColorPointsFunction ||
156 this->PointsFunction == ColorAndOpacityPointsFunction)
157 {
158 this->Superclass::DrawPoint(painter, index);
159 return;
160 }
161 if (this->PointsFunction == OpacityPointsFunction && this->ColorFill &&
162 this->ColorTransferFunction)
163 {
164 double xvms[4];
165 this->OpacityFunction->GetNodeValue(index, xvms);
166 const unsigned char* rgb = this->ColorTransferFunction->MapValue(xvms[0]);
167 painter->GetBrush()->SetColorF(rgb[0] / 255., rgb[1] / 255., rgb[2] / 255., 0.55);
168 }
169 // The superclass logic was already considered at the beginning of this method.
170 // As such, the grandparent class must be called here in order to avoid applying
171 // the color transfer function a second time.
172 // NOLINTNEXTLINE(bugprone-parent-virtual-call)
173 this->Superclass::Superclass::DrawPoint(painter, index);
174 }
175
176 //------------------------------------------------------------------------------
GetNumberOfPoints() const177 vtkIdType vtkCompositeControlPointsItem::GetNumberOfPoints() const
178 {
179 if (this->ColorTransferFunction &&
180 (this->PointsFunction == ColorPointsFunction ||
181 this->PointsFunction == ColorAndOpacityPointsFunction))
182 {
183 return this->Superclass::GetNumberOfPoints();
184 }
185 if (this->OpacityFunction &&
186 (this->PointsFunction == OpacityPointsFunction ||
187 this->PointsFunction == ColorAndOpacityPointsFunction))
188 {
189 return static_cast<vtkIdType>(this->OpacityFunction->GetSize());
190 }
191 return 0;
192 }
193
194 //------------------------------------------------------------------------------
SetControlPoint(vtkIdType index,double * newPos)195 void vtkCompositeControlPointsItem::SetControlPoint(vtkIdType index, double* newPos)
196 {
197 if (this->PointsFunction == ColorPointsFunction ||
198 this->PointsFunction == ColorAndOpacityPointsFunction)
199 {
200 this->Superclass::SetControlPoint(index, newPos);
201 }
202 if (this->OpacityFunction &&
203 (this->PointsFunction == OpacityPointsFunction ||
204 this->PointsFunction == ColorAndOpacityPointsFunction))
205 {
206 this->StartChanges();
207 this->OpacityFunction->SetNodeValue(index, newPos);
208 this->EndChanges();
209 }
210 }
211
212 //------------------------------------------------------------------------------
GetControlPoint(vtkIdType index,double * pos) const213 void vtkCompositeControlPointsItem::GetControlPoint(vtkIdType index, double* pos) const
214 {
215 if (!this->OpacityFunction || this->PointsFunction == ColorPointsFunction)
216 {
217 this->Superclass::GetControlPoint(index, pos);
218 if (this->OpacityFunction)
219 {
220 pos[1] = const_cast<vtkPiecewiseFunction*>(this->OpacityFunction)->GetValue(pos[0]);
221 }
222 return;
223 }
224 const_cast<vtkPiecewiseFunction*>(this->OpacityFunction)->GetNodeValue(index, pos);
225 }
226
227 //------------------------------------------------------------------------------
EditPoint(float tX,float tY)228 void vtkCompositeControlPointsItem::EditPoint(float tX, float tY)
229 {
230 if (this->PointsFunction == ColorPointsFunction ||
231 this->PointsFunction == ColorAndOpacityPointsFunction)
232 {
233 this->Superclass::EditPoint(tX, tY);
234 }
235 if (this->OpacityFunction &&
236 (this->PointsFunction == ColorPointsFunction ||
237 this->PointsFunction == ColorAndOpacityPointsFunction))
238 {
239 this->StartChanges();
240 double xvms[4];
241 this->OpacityFunction->GetNodeValue(this->CurrentPoint, xvms);
242 xvms[2] += tX;
243 xvms[3] += tY;
244 this->OpacityFunction->SetNodeValue(this->CurrentPoint, xvms);
245 // Not sure why we move the point before too
246 if (this->CurrentPoint > 0)
247 {
248 this->OpacityFunction->GetNodeValue(this->CurrentPoint - 1, xvms);
249 xvms[2] += tX;
250 xvms[3] += tY;
251 this->OpacityFunction->SetNodeValue(this->CurrentPoint - 1, xvms);
252 }
253 this->EndChanges();
254 }
255 }
256
257 //------------------------------------------------------------------------------
AddPoint(double * newPos)258 vtkIdType vtkCompositeControlPointsItem::AddPoint(double* newPos)
259 {
260 vtkIdType addedPoint = -1;
261 this->StartChanges();
262 if (this->OpacityFunction &&
263 (this->PointsFunction == OpacityPointsFunction ||
264 this->PointsFunction == ColorAndOpacityPointsFunction))
265 {
266 addedPoint = this->OpacityFunction->AddPoint(newPos[0], newPos[1]);
267 if (this->PointsFunction == OpacityPointsFunction)
268 {
269 this->vtkControlPointsItem::AddPointId(addedPoint);
270 }
271 }
272 if (this->PointsFunction == ColorPointsFunction ||
273 this->PointsFunction == ColorAndOpacityPointsFunction)
274 {
275 addedPoint = this->Superclass::AddPoint(newPos);
276 }
277 this->EndChanges();
278 return addedPoint;
279 }
280
281 //------------------------------------------------------------------------------
RemovePoint(double * currentPoint)282 vtkIdType vtkCompositeControlPointsItem::RemovePoint(double* currentPoint)
283 {
284 vtkIdType removedPoint = -1;
285 if (!this->IsPointRemovable(this->GetControlPointId(currentPoint)))
286 {
287 return removedPoint;
288 }
289
290 this->StartChanges();
291 if (this->PointsFunction == ColorPointsFunction ||
292 this->PointsFunction == ColorAndOpacityPointsFunction)
293 {
294 removedPoint = this->Superclass::RemovePoint(currentPoint);
295 }
296 if (this->OpacityFunction &&
297 (this->PointsFunction == OpacityPointsFunction ||
298 this->PointsFunction == ColorAndOpacityPointsFunction))
299 {
300 removedPoint = this->OpacityFunction->RemovePoint(currentPoint[0], currentPoint[1]);
301 }
302
303 if (this->CurrentPoint > removedPoint || this->CurrentPoint == this->GetNumberOfPoints() - 1)
304 {
305 this->SetCurrentPoint(this->CurrentPoint - 1);
306 }
307
308 this->EndChanges();
309 return removedPoint;
310 }
311
312 //------------------------------------------------------------------------------
MergeTransferFunctions()313 void vtkCompositeControlPointsItem::MergeTransferFunctions()
314 {
315 if (!this->ColorTransferFunction || !this->OpacityFunction)
316 {
317 return;
318 }
319 // Naive implementation that does the work but can be a bit slow
320 // Copy OpacityFunction points into the ColorTransferFunction
321 const int piecewiseFunctionCount = this->OpacityFunction->GetSize();
322 for (int i = 0; i < piecewiseFunctionCount; ++i)
323 {
324 double piecewisePoint[4];
325 this->OpacityFunction->GetNodeValue(i, piecewisePoint);
326 double rgb[3];
327 this->ColorTransferFunction->GetColor(piecewisePoint[0], rgb);
328 // note that we might lose the midpoint/sharpness of the point if any
329 this->ColorTransferFunction->RemovePoint(piecewisePoint[0]);
330 this->ColorTransferFunction->AddRGBPoint(
331 piecewisePoint[0], rgb[0], rgb[1], rgb[2], piecewisePoint[2], piecewisePoint[3]);
332 }
333 // Copy ColorTransferFunction points into the OpacityFunction
334 const int colorFunctionCount = this->ColorTransferFunction->GetSize();
335 for (int i = 0; i < colorFunctionCount; ++i)
336 {
337 double xrgbms[6];
338 this->ColorTransferFunction->GetNodeValue(i, xrgbms);
339 double value = this->OpacityFunction->GetValue(xrgbms[0]);
340 // note that we might lose the midpoint/sharpness of the point if any
341 this->OpacityFunction->RemovePoint(xrgbms[0]);
342 this->OpacityFunction->AddPoint(xrgbms[0], value, xrgbms[4], xrgbms[5]);
343 }
344 }
345
346 //------------------------------------------------------------------------------
SilentMergeTransferFunctions()347 void vtkCompositeControlPointsItem::SilentMergeTransferFunctions()
348 {
349 this->StartChanges();
350 this->MergeTransferFunctions();
351 this->EndChanges();
352 }
353
354 //------------------------------------------------------------------------------
MouseButtonPressEvent(const vtkContextMouseEvent & mouse)355 bool vtkCompositeControlPointsItem::MouseButtonPressEvent(const vtkContextMouseEvent& mouse)
356 {
357 bool result = false;
358 if (this->OpacityPointHandle && this->OpacityPointHandle->GetVisible())
359 {
360 result = this->OpacityPointHandle->MouseButtonPressEvent(mouse);
361 }
362 if (!result)
363 {
364 result = this->Superclass::MouseButtonPressEvent(mouse);
365 if (result && this->OpacityPointHandle && this->OpacityPointHandle->GetVisible() &&
366 this->OpacityPointHandle->GetCurrentPointIndex() != this->GetCurrentPoint())
367 {
368 this->OpacityPointHandle->SetVisible(false);
369 }
370 }
371 return result;
372 }
373
374 //------------------------------------------------------------------------------
MouseDoubleClickEvent(const vtkContextMouseEvent & mouse)375 bool vtkCompositeControlPointsItem::MouseDoubleClickEvent(const vtkContextMouseEvent& mouse)
376 {
377 bool superRes = this->Superclass::MouseDoubleClickEvent(mouse);
378 if (superRes)
379 {
380 vtkIdType curIdx = this->GetCurrentPoint();
381 this->EditPointCurve(curIdx);
382 }
383 return superRes;
384 }
385
386 //------------------------------------------------------------------------------
MouseMoveEvent(const vtkContextMouseEvent & mouse)387 bool vtkCompositeControlPointsItem::MouseMoveEvent(const vtkContextMouseEvent& mouse)
388 {
389 bool result = false;
390 if (this->OpacityPointHandle && this->OpacityPointHandle->GetVisible())
391 {
392 result = this->OpacityPointHandle->MouseMoveEvent(mouse);
393 }
394 if (!result)
395 {
396 result = this->Superclass::MouseMoveEvent(mouse);
397 }
398 return result;
399 }
400
401 //------------------------------------------------------------------------------
EditPointCurve(vtkIdType index)402 void vtkCompositeControlPointsItem::EditPointCurve(vtkIdType index)
403 {
404 if (index < 0 || index >= this->GetNumberOfPoints())
405 {
406 return;
407 }
408 if (this->GetUseOpacityPointHandles())
409 {
410 if (!this->OpacityPointHandle)
411 {
412 this->OpacityPointHandle = vtkPiecewisePointHandleItem::New();
413 this->AddItem(this->OpacityPointHandle);
414 this->OpacityPointHandle->SetPiecewiseFunction(this->GetOpacityFunction());
415 }
416 else
417 {
418 this->OpacityPointHandle->SetVisible(!this->OpacityPointHandle->GetVisible());
419 this->GetScene()->SetDirty(true);
420 }
421 }
422 }
423