1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkBorderRepresentation.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 "vtkBorderRepresentation.h"
16 #include "vtkActor2D.h"
17 #include "vtkCellArray.h"
18 #include "vtkFeatureEdges.h"
19 #include "vtkMath.h"
20 #include "vtkObjectFactory.h"
21 #include "vtkPointData.h"
22 #include "vtkPoints.h"
23 #include "vtkPolyData.h"
24 #include "vtkPolyDataMapper2D.h"
25 #include "vtkProperty2D.h"
26 #include "vtkRenderer.h"
27 #include "vtkTransform.h"
28 #include "vtkTransformPolyDataFilter.h"
29 #include "vtkWindow.h"
30
31 #include <algorithm>
32 #include <cassert>
33
34 vtkStandardNewMacro(vtkBorderRepresentation);
35
36 //------------------------------------------------------------------------------
vtkBorderRepresentation()37 vtkBorderRepresentation::vtkBorderRepresentation()
38 {
39 this->InteractionState = vtkBorderRepresentation::Outside;
40
41 // Initial positioning information
42 this->Negotiated = 0;
43 this->PositionCoordinate->SetCoordinateSystemToNormalizedViewport();
44 this->PositionCoordinate->SetValue(0.05, 0.05);
45 this->Position2Coordinate->SetCoordinateSystemToNormalizedViewport();
46 this->Position2Coordinate->SetValue(0.1, 0.1); // may be updated by the subclass
47 this->Position2Coordinate->SetReferenceCoordinate(this->PositionCoordinate);
48
49 // Create the geometry in canonical coordinates
50 this->BWPoints->SetDataTypeToDouble();
51 this->BWPoints->SetNumberOfPoints(4);
52 this->BWPoints->SetPoint(0, 0.0, 0.0, 0.0); // may be updated by the subclass
53 this->BWPoints->SetPoint(1, 1.0, 0.0, 0.0);
54 this->BWPoints->SetPoint(2, 1.0, 1.0, 0.0);
55 this->BWPoints->SetPoint(3, 0.0, 1.0, 0.0);
56
57 vtkNew<vtkCellArray> outline;
58 outline->InsertNextCell(5);
59 outline->InsertCellPoint(0);
60 outline->InsertCellPoint(1);
61 outline->InsertCellPoint(2);
62 outline->InsertCellPoint(3);
63 outline->InsertCellPoint(0);
64
65 this->BWPolyData->SetPoints(this->BWPoints);
66 this->BWPolyData->SetLines(outline);
67
68 this->BWTransformFilter->SetTransform(this->BWTransform);
69 this->BWTransformFilter->SetInputData(this->BWPolyData);
70
71 // In order to link a different property for the border
72 // and the inner polygon, we create 2 new polydata that will
73 // share the points of the input poly data
74 // Beware that this will break the pipeline, so we need to
75 // Call update manually on BWTransformFilter
76
77 // Edges
78 this->BWMapperEdges->SetInputData(this->PolyDataEdges);
79 this->BWActorEdges->SetMapper(this->BWMapperEdges);
80 this->BorderProperty->SetColor(this->BorderColor);
81 this->BorderProperty->SetLineWidth(this->BorderThickness);
82 this->BorderProperty->SetPointSize(1.5f);
83 this->BWActorEdges->SetProperty(this->BorderProperty);
84
85 // Inner polygon
86 this->BWMapperPolygon->SetInputData(this->PolyDataPolygon);
87 this->BWActorPolygon->SetMapper(this->BWMapperPolygon);
88 this->PolygonProperty->SetColor(this->PolygonColor);
89 this->PolygonProperty->SetOpacity(this->PolygonOpacity);
90 this->PolygonProperty->SetPointSize(0.f);
91 this->BWActorPolygon->SetProperty(this->PolygonProperty);
92 }
93
94 //------------------------------------------------------------------------------
95 vtkBorderRepresentation::~vtkBorderRepresentation() = default;
96
97 //------------------------------------------------------------------------------
ComputeRoundCorners()98 void vtkBorderRepresentation::ComputeRoundCorners()
99 {
100 vtkCellArray* lines = this->BWPolyData->GetLines();
101
102 // Link the pipeline manually as we need two properties for the border
103 // and for the inner polygon
104 this->BWTransformFilter->Update();
105
106 // Create round corners after the transform as we do not want to scale the corners
107 vtkPolyData* pd = this->BWTransformFilter->GetOutput();
108 vtkNew<vtkPoints> pdPoints;
109 pdPoints->DeepCopy(pd->GetPoints());
110
111 if (lines->GetNumberOfCells() != 1 || this->CornerResolution == 0)
112 {
113 // All borders are not shown, we cannot compute
114 // round corners
115 this->PolyDataEdges->SetPoints(pdPoints);
116 this->PolyDataEdges->SetLines(lines);
117
118 this->PolyDataPolygon->SetPoints(pdPoints);
119 this->PolyDataPolygon->SetPolys(lines);
120
121 return;
122 }
123
124 // Get the bottom left corner point
125 double p0[3];
126 pdPoints->GetPoint(0, p0);
127
128 // And the top right corner point
129 double p1[3];
130 pdPoints->GetPoint(2, p1);
131
132 // Scale the maximum radius by radius strength
133 double radius = this->CornerRadiusStrength * std::min(p1[0] - p0[0], p1[1] - p0[1]) / 2.0;
134
135 // Add 2 points of each side of each corners to start and end the
136 // curve of the round corner. With the previous 4 points, the number of
137 // points is now 12
138 pdPoints->SetNumberOfPoints(12);
139 // Bottom-left corner
140 pdPoints->SetPoint(4, p0[0], p0[1] + radius, 0.0);
141 pdPoints->SetPoint(5, p0[0] + radius, p0[1], 0.0);
142 // Bottom-right corner
143 pdPoints->SetPoint(6, p1[0] - radius, p0[1], 0.0);
144 pdPoints->SetPoint(7, p1[0], p0[1] + radius, 0.0);
145 // Top-right corner
146 pdPoints->SetPoint(8, p1[0], p1[1] - radius, 0.0);
147 pdPoints->SetPoint(9, p1[0] - radius, p1[1], 0.0);
148 // Top-left corner
149 pdPoints->SetPoint(10, p0[0] + radius, p1[1], 0.0);
150 pdPoints->SetPoint(11, p0[0], p1[1] - radius, 0.0);
151
152 // Create polygon with only one cell
153 vtkNew<vtkCellArray> polys;
154 polys->InsertNextCell(4 * this->CornerResolution + 1);
155
156 // Compute bottom-left corner
157 this->ComputeOneRoundCorner(polys, pdPoints, radius, 5, 4, vtkMath::Pi());
158 // Compute bottom-right corner
159 this->ComputeOneRoundCorner(polys, pdPoints, radius, 6, 7, 3.0 * vtkMath::Pi() / 2.0);
160 // Compute top-right corner
161 this->ComputeOneRoundCorner(polys, pdPoints, radius, 9, 8, 0.0);
162 // Compute top-left corner
163 this->ComputeOneRoundCorner(polys, pdPoints, radius, 10, 11, vtkMath::Pi() / 2.0);
164
165 // Don't forget to link the last point
166 polys->InsertCellPoint(12);
167
168 this->PolyDataEdges->SetPoints(pdPoints);
169 this->PolyDataEdges->SetVerts(polys);
170 this->PolyDataEdges->SetLines(polys);
171
172 this->PolyDataPolygon->SetPoints(pdPoints);
173 this->PolyDataPolygon->SetPolys(polys);
174 }
175
176 //------------------------------------------------------------------------------
ComputeOneRoundCorner(vtkCellArray * polys,vtkPoints * points,const double radius,vtkIdType idCenterX,vtkIdType idCenterY,const double startAngle)177 void vtkBorderRepresentation::ComputeOneRoundCorner(vtkCellArray* polys, vtkPoints* points,
178 const double radius, vtkIdType idCenterX, vtkIdType idCenterY, const double startAngle)
179 {
180 double xPoint[3], yPoint[3];
181 points->GetPoint(idCenterX, xPoint);
182 points->GetPoint(idCenterY, yPoint);
183 double center[2] = { xPoint[0], yPoint[1] };
184
185 // Angle step in radians
186 double angleStep = vtkMath::Pi() / (2.0 * this->CornerResolution);
187 double angle = startAngle;
188
189 for (int i = 0; i < this->CornerResolution; ++i)
190 {
191 double x = center[0] + radius * cos(angle);
192 double y = center[1] + radius * sin(angle);
193 vtkIdType id = points->InsertNextPoint(x, y, 0.0);
194 polys->InsertCellPoint(id);
195 angle += angleStep;
196 }
197 }
198
199 //------------------------------------------------------------------------------
GetMTime()200 vtkMTimeType vtkBorderRepresentation::GetMTime()
201 {
202 vtkMTimeType mTime = this->Superclass::GetMTime();
203 mTime = std::max(mTime, this->PositionCoordinate->GetMTime());
204 mTime = std::max(mTime, this->Position2Coordinate->GetMTime());
205 mTime = std::max(mTime, this->BorderProperty->GetMTime());
206 mTime = std::max(mTime, this->PolygonProperty->GetMTime());
207 return mTime;
208 }
209
210 //------------------------------------------------------------------------------
SetShowBorder(int border)211 void vtkBorderRepresentation::SetShowBorder(int border)
212 {
213 this->SetShowVerticalBorder(border);
214 this->SetShowHorizontalBorder(border);
215 this->UpdateShowBorder();
216 }
217
218 //------------------------------------------------------------------------------
GetShowBorderMinValue()219 int vtkBorderRepresentation::GetShowBorderMinValue()
220 {
221 return BORDER_OFF;
222 }
223
224 //------------------------------------------------------------------------------
GetShowBorderMaxValue()225 int vtkBorderRepresentation::GetShowBorderMaxValue()
226 {
227 return BORDER_ACTIVE;
228 }
229
230 //------------------------------------------------------------------------------
GetShowBorder()231 int vtkBorderRepresentation::GetShowBorder()
232 {
233 return this->GetShowVerticalBorder() != BORDER_OFF ? this->GetShowVerticalBorder()
234 : this->GetShowHorizontalBorder();
235 }
236
237 //------------------------------------------------------------------------------
StartWidgetInteraction(double eventPos[2])238 void vtkBorderRepresentation::StartWidgetInteraction(double eventPos[2])
239 {
240 this->StartEventPosition[0] = eventPos[0];
241 this->StartEventPosition[1] = eventPos[1];
242 }
243
244 //------------------------------------------------------------------------------
WidgetInteraction(double eventPos[2])245 void vtkBorderRepresentation::WidgetInteraction(double eventPos[2])
246 {
247 double XF = eventPos[0];
248 double YF = eventPos[1];
249
250 // convert to normalized viewport coordinates
251 this->Renderer->DisplayToNormalizedDisplay(XF, YF);
252 this->Renderer->NormalizedDisplayToViewport(XF, YF);
253 this->Renderer->ViewportToNormalizedViewport(XF, YF);
254
255 // there are four parameters that can be adjusted
256 double* fpos1 = this->PositionCoordinate->GetValue();
257 double* fpos2 = this->Position2Coordinate->GetValue();
258 double par1[2];
259 double par2[2];
260 par1[0] = fpos1[0];
261 par1[1] = fpos1[1];
262 par2[0] = fpos1[0] + fpos2[0];
263 par2[1] = fpos1[1] + fpos2[1];
264
265 double delX = XF - this->StartEventPosition[0];
266 double delY = YF - this->StartEventPosition[1];
267 double delX2 = 0.0, delY2 = 0.0;
268
269 // Based on the state, adjust the representation. Note that we force a
270 // uniform scaling of the widget when tugging on the corner points (and
271 // when proportional resize is on). This is done by finding the maximum
272 // movement in the x-y directions and using this to scale the widget.
273 if (this->ProportionalResize && !this->Moving)
274 {
275 double sx = fpos2[0] / fpos2[1];
276 double sy = fpos2[1] / fpos2[0];
277 if (fabs(delX) > fabs(delY))
278 {
279 delY = sy * delX;
280 delX2 = delX;
281 delY2 = -delY;
282 }
283 else
284 {
285 delX = sx * delY;
286 delY2 = delY;
287 delX2 = -delX;
288 }
289 }
290 else
291 {
292 delX2 = delX;
293 delY2 = delY;
294 }
295
296 // The previous "if" statement has taken care of the proportional resize
297 // for the most part. However, tugging on edges has special behavior, which
298 // is to scale the box about its center.
299 switch (this->InteractionState)
300 {
301 case vtkBorderRepresentation::AdjustingP0:
302 par1[0] = par1[0] + delX;
303 par1[1] = par1[1] + delY;
304 break;
305 case vtkBorderRepresentation::AdjustingP1:
306 par2[0] = par2[0] + delX2;
307 par1[1] = par1[1] + delY2;
308 break;
309 case vtkBorderRepresentation::AdjustingP2:
310 par2[0] = par2[0] + delX;
311 par2[1] = par2[1] + delY;
312 break;
313 case vtkBorderRepresentation::AdjustingP3:
314 par1[0] = par1[0] + delX2;
315 par2[1] = par2[1] + delY2;
316 break;
317 case vtkBorderRepresentation::AdjustingE0:
318 par1[1] = par1[1] + delY;
319 if (this->ProportionalResize)
320 {
321 par2[1] = par2[1] - delY;
322 par1[0] = par1[0] + delX;
323 par2[0] = par2[0] - delX;
324 }
325 break;
326 case vtkBorderRepresentation::AdjustingE1:
327 par2[0] = par2[0] + delX;
328 if (this->ProportionalResize)
329 {
330 par1[0] = par1[0] - delX;
331 par1[1] = par1[1] - delY;
332 par2[1] = par2[1] + delY;
333 }
334 break;
335 case vtkBorderRepresentation::AdjustingE2:
336 par2[1] = par2[1] + delY;
337 if (this->ProportionalResize)
338 {
339 par1[1] = par1[1] - delY;
340 par1[0] = par1[0] - delX;
341 par2[0] = par2[0] + delX;
342 }
343 break;
344 case vtkBorderRepresentation::AdjustingE3:
345 par1[0] = par1[0] + delX;
346 if (this->ProportionalResize)
347 {
348 par2[0] = par2[0] - delX;
349 par1[1] = par1[1] + delY;
350 par2[1] = par2[1] - delY;
351 }
352 break;
353 case vtkBorderRepresentation::Inside:
354 if (this->Moving)
355 {
356 par1[0] = par1[0] + delX;
357 par1[1] = par1[1] + delY;
358 par2[0] = par2[0] + delX;
359 par2[1] = par2[1] + delY;
360 }
361 break;
362 }
363
364 // Enforce bounds to keep the widget on screen and bigger than minimum size
365 if (!this->ProportionalResize && this->EnforceNormalizedViewportBounds)
366 {
367 switch (this->InteractionState)
368 {
369 case vtkBorderRepresentation::AdjustingP0:
370 par1[0] = std::min(
371 std::max(par1[0] /*+ delX*/, 0.0), par2[0] - this->MinimumNormalizedViewportSize[0]);
372 par1[1] = std::min(
373 std::max(par1[1] /*+ delY*/, 0.0), par2[1] - this->MinimumNormalizedViewportSize[1]);
374 break;
375 case vtkBorderRepresentation::AdjustingP1:
376 par2[0] = std::min(
377 std::max(par2[0] /*+ delX2*/, par1[0] + this->MinimumNormalizedViewportSize[0]), 1.0);
378 par1[1] = std::min(
379 std::max(par1[1] /*+ delY2*/, 0.0), par2[1] - this->MinimumNormalizedViewportSize[1]);
380 break;
381 case vtkBorderRepresentation::AdjustingP2:
382 par2[0] = std::min(
383 std::max(par2[0] /*+ delX*/, par1[0] + this->MinimumNormalizedViewportSize[0]), 1.0);
384 par2[1] = std::min(
385 std::max(par2[1] /*+ delY*/, par1[1] + this->MinimumNormalizedViewportSize[1]), 1.0);
386 break;
387 case vtkBorderRepresentation::AdjustingP3:
388 par1[0] = std::min(
389 std::max(par1[0] /*+ delX2*/, 0.0), par2[0] - this->MinimumNormalizedViewportSize[0]);
390 par2[1] = std::min(
391 std::max(par2[1] /*+ delY2*/, par1[1] + this->MinimumNormalizedViewportSize[1]), 1.0);
392 break;
393 case vtkBorderRepresentation::AdjustingE0:
394 par1[1] = std::min(
395 std::max(par1[1] /*+ delY*/, 0.0), par2[1] - this->MinimumNormalizedViewportSize[1]);
396 break;
397 case vtkBorderRepresentation::AdjustingE1:
398 par2[0] = std::min(
399 std::max(par2[0] /*+ delX*/, par1[0] + this->MinimumNormalizedViewportSize[0]), 1.0);
400 break;
401 case vtkBorderRepresentation::AdjustingE2:
402 par2[1] = std::min(
403 std::max(par2[1] /*+ delY*/, par1[1] + this->MinimumNormalizedViewportSize[1]), 1.0);
404 break;
405 case vtkBorderRepresentation::AdjustingE3:
406 par1[0] = std::min(
407 std::max(par1[0] /*+ delX*/, 0.0), par2[0] - this->MinimumNormalizedViewportSize[0]);
408 break;
409 case vtkBorderRepresentation::Inside:
410 if (this->Moving)
411 {
412 // Keep border from moving off normalized screen
413 if (par1[0] < 0.0)
414 {
415 double delta = -par1[0];
416 par1[0] += delta;
417 par2[0] += delta;
418 }
419 if (par1[1] < 0.0)
420 {
421 double delta = -par1[1];
422 par1[1] += delta;
423 par2[1] += delta;
424 }
425 if (par2[0] > 1.0)
426 {
427 double delta = par2[0] - 1.0;
428 par1[0] -= delta;
429 par2[0] -= delta;
430 }
431 if (par2[1] > 1.0)
432 {
433 double delta = par2[1] - 1.0;
434 par1[1] -= delta;
435 par2[1] -= delta;
436 }
437 }
438 break;
439 default:
440 break;
441 }
442 }
443
444 // Modify the representation
445 if (par2[0] > par1[0] && par2[1] > par1[1])
446 {
447 this->PositionCoordinate->SetValue(par1[0], par1[1]);
448 this->Position2Coordinate->SetValue(par2[0] - par1[0], par2[1] - par1[1]);
449 this->StartEventPosition[0] = XF;
450 this->StartEventPosition[1] = YF;
451 }
452
453 this->Modified();
454 this->BuildRepresentation();
455 }
456
457 //------------------------------------------------------------------------------
NegotiateLayout()458 void vtkBorderRepresentation::NegotiateLayout()
459 {
460 double size[2];
461 this->GetSize(size);
462
463 // Update the initial border geometry
464 this->BWPoints->SetPoint(0, 0.0, 0.0, 0.0); // may be updated by the subclass
465 this->BWPoints->SetPoint(1, size[0], 0.0, 0.0);
466 this->BWPoints->SetPoint(2, size[0], size[1], 0.0);
467 this->BWPoints->SetPoint(3, 0.0, size[1], 0.0);
468 }
469
470 //------------------------------------------------------------------------------
ComputeInteractionState(int X,int Y,int vtkNotUsed (modify))471 int vtkBorderRepresentation::ComputeInteractionState(int X, int Y, int vtkNotUsed(modify))
472 {
473 int* pos1 = this->PositionCoordinate->GetComputedDisplayValue(this->Renderer);
474 int* pos2 = this->Position2Coordinate->GetComputedDisplayValue(this->Renderer);
475
476 // Figure out where we are in the widget. Exclude outside case first.
477 if (X < (pos1[0] - this->Tolerance) || (pos2[0] + this->Tolerance) < X ||
478 Y < (pos1[1] - this->Tolerance) || (pos2[1] + this->Tolerance) < Y)
479 {
480 this->InteractionState = vtkBorderRepresentation::Outside;
481 }
482
483 else // we are on the boundary or inside the border
484 {
485 // Now check for proximinity to edges and points
486 int e0 = (Y >= (pos1[1] - this->Tolerance) && Y <= (pos1[1] + this->Tolerance));
487 int e1 = (X >= (pos2[0] - this->Tolerance) && X <= (pos2[0] + this->Tolerance));
488 int e2 = (Y >= (pos2[1] - this->Tolerance) && Y <= (pos2[1] + this->Tolerance));
489 int e3 = (X >= (pos1[0] - this->Tolerance) && X <= (pos1[0] + this->Tolerance));
490
491 int adjustHorizontalEdges = (this->ShowHorizontalBorder != BORDER_OFF);
492 int adjustVerticalEdges = (this->ShowVerticalBorder != BORDER_OFF);
493 int adjustPoints = (adjustHorizontalEdges && adjustVerticalEdges);
494
495 if (e0 && e1 && adjustPoints)
496 {
497 this->InteractionState = vtkBorderRepresentation::AdjustingP1;
498 }
499 else if (e1 && e2 && adjustPoints)
500 {
501 this->InteractionState = vtkBorderRepresentation::AdjustingP2;
502 }
503 else if (e2 && e3 && adjustPoints)
504 {
505 this->InteractionState = vtkBorderRepresentation::AdjustingP3;
506 }
507 else if (e3 && e0 && adjustPoints)
508 {
509 this->InteractionState = vtkBorderRepresentation::AdjustingP0;
510 }
511
512 // Edges
513 else if (e0 || e1 || e2 || e3)
514 {
515 if (e0 && adjustHorizontalEdges)
516 {
517 this->InteractionState = vtkBorderRepresentation::AdjustingE0;
518 }
519 else if (e1 && adjustVerticalEdges)
520 {
521 this->InteractionState = vtkBorderRepresentation::AdjustingE1;
522 }
523 else if (e2 && adjustHorizontalEdges)
524 {
525 this->InteractionState = vtkBorderRepresentation::AdjustingE2;
526 }
527 else if (e3 && adjustVerticalEdges)
528 {
529 this->InteractionState = vtkBorderRepresentation::AdjustingE3;
530 }
531 }
532
533 else // must be interior
534 {
535 if (this->Moving)
536 {
537 // FIXME: This must be wrong. Moving is not an entry in the
538 // _InteractionState enum. It is an ivar flag and it has no business
539 // being set to InteractionState. This just happens to work because
540 // Inside happens to be 1, and this gets set when Moving is 1.
541 this->InteractionState = vtkBorderRepresentation::Moving;
542 }
543 else
544 {
545 this->InteractionState = vtkBorderRepresentation::Inside;
546 }
547 }
548 } // else inside or on border
549 this->UpdateShowBorder();
550
551 return this->InteractionState;
552 }
553
554 //------------------------------------------------------------------------------
UpdateShowBorder()555 void vtkBorderRepresentation::UpdateShowBorder()
556 {
557 enum
558 {
559 NoBorder = 0x00,
560 VerticalBorder = 0x01,
561 HorizontalBorder = 0x02,
562 AllBorders = VerticalBorder | HorizontalBorder
563 };
564 int currentBorder = NoBorder;
565 switch (this->BWPolyData->GetLines()->GetNumberOfCells())
566 {
567 case 1:
568 currentBorder = AllBorders;
569 break;
570 case 2:
571 {
572 vtkIdType npts = 0;
573 const vtkIdType* pts = nullptr;
574 this->BWPolyData->GetLines()->GetCellAtId(0, npts, pts);
575 assert(npts == 2);
576 currentBorder = (pts[0] == 0 ? HorizontalBorder : VerticalBorder);
577 break;
578 }
579 case 0:
580 default: // not supported
581 currentBorder = NoBorder;
582 break;
583 }
584 int newBorder = NoBorder;
585 if (this->ShowVerticalBorder == this->ShowHorizontalBorder)
586 {
587 newBorder = (this->ShowVerticalBorder == BORDER_ON ||
588 (this->ShowVerticalBorder == BORDER_ACTIVE &&
589 this->InteractionState != vtkBorderRepresentation::Outside))
590 ? AllBorders
591 : NoBorder;
592 }
593 else
594 {
595 newBorder = newBorder |
596 ((this->ShowVerticalBorder == BORDER_ON ||
597 (this->ShowVerticalBorder == BORDER_ACTIVE &&
598 this->InteractionState != vtkBorderRepresentation::Outside))
599 ? VerticalBorder
600 : NoBorder);
601 newBorder = newBorder |
602 ((this->ShowHorizontalBorder == BORDER_ON ||
603 (this->ShowHorizontalBorder == BORDER_ACTIVE &&
604 this->InteractionState != vtkBorderRepresentation::Outside))
605 ? HorizontalBorder
606 : NoBorder);
607 }
608 bool visible = (newBorder != NoBorder);
609 if (currentBorder != newBorder && visible)
610 {
611 vtkNew<vtkCellArray> outline;
612 switch (newBorder)
613 {
614 case AllBorders:
615 outline->InsertNextCell(5);
616 outline->InsertCellPoint(0);
617 outline->InsertCellPoint(1);
618 outline->InsertCellPoint(2);
619 outline->InsertCellPoint(3);
620 outline->InsertCellPoint(0);
621 break;
622 case VerticalBorder:
623 outline->InsertNextCell(2);
624 outline->InsertCellPoint(1);
625 outline->InsertCellPoint(2);
626 outline->InsertNextCell(2);
627 outline->InsertCellPoint(3);
628 outline->InsertCellPoint(0);
629 break;
630 case HorizontalBorder:
631 outline->InsertNextCell(2);
632 outline->InsertCellPoint(0);
633 outline->InsertCellPoint(1);
634 outline->InsertNextCell(2);
635 outline->InsertCellPoint(2);
636 outline->InsertCellPoint(3);
637 break;
638 default:
639 break;
640 }
641 this->BWPolyData->SetLines(outline);
642 this->BWPolyData->Modified();
643 this->Modified();
644 this->ComputeRoundCorners();
645 }
646 this->BWActorEdges->SetVisibility(visible);
647 }
648
649 //------------------------------------------------------------------------------
SetBWActorDisplayOverlay(bool enable)650 void vtkBorderRepresentation::SetBWActorDisplayOverlay(bool enable)
651 {
652 if (this->BWActorEdges)
653 {
654 this->BWActorEdges->SetVisibility(enable);
655 }
656 if (this->BWActorPolygon)
657 {
658 this->BWActorPolygon->SetVisibility(enable);
659 }
660 }
661
662 //------------------------------------------------------------------------------
BuildRepresentation()663 void vtkBorderRepresentation::BuildRepresentation()
664 {
665 if (this->Renderer &&
666 (this->GetMTime() > this->BuildTime ||
667 (this->Renderer->GetVTKWindow() &&
668 this->Renderer->GetVTKWindow()->GetMTime() > this->BuildTime)))
669 {
670 // Negotiate with subclasses
671 if (!this->Negotiated)
672 {
673 this->NegotiateLayout();
674 this->Negotiated = 1;
675 }
676
677 // Set things up
678 int* pos1 = this->PositionCoordinate->GetComputedViewportValue(this->Renderer);
679 int* pos2 = this->Position2Coordinate->GetComputedViewportValue(this->Renderer);
680
681 // If the widget's aspect ratio is to be preserved (ProportionalResizeOn),
682 // then (pos1,pos2) are a bounding rectangle.
683 if (this->ProportionalResize)
684 {
685 }
686
687 // Now transform the canonical widget into display coordinates
688 double size[2];
689 this->GetSize(size);
690 double tx = pos1[0];
691 double ty = pos1[1];
692 double sx = (pos2[0] - pos1[0]) / size[0];
693 double sy = (pos2[1] - pos1[1]) / size[1];
694
695 sx = (sx < this->MinimumSize[0] ? this->MinimumSize[0]
696 : (sx > this->MaximumSize[0] ? this->MaximumSize[0] : sx));
697 sy = (sy < this->MinimumSize[1] ? this->MinimumSize[1]
698 : (sy > this->MaximumSize[1] ? this->MaximumSize[1] : sy));
699
700 this->BWTransform->Identity();
701 this->BWTransform->Translate(tx, ty, 0.0);
702 this->BWTransform->Scale(sx, sy, 1);
703
704 // Compute round corners after the transform has been set
705 // Only if the polydata contains a unique cell (ie. all borders
706 // are visible)
707 this->ComputeRoundCorners();
708
709 // Modify border properties
710 this->BorderProperty->SetColor(this->BorderColor);
711 this->BorderProperty->SetLineWidth(this->BorderThickness);
712
713 // In order to fill the holes in the corners
714 // We use a little trick : we render the points with
715 // a point size that fill the holes
716 double pointSize = this->BorderThickness;
717 this->BorderProperty->SetPointSize(std::max(0.0, pointSize - 1.0));
718
719 // And polygon properties
720 this->PolygonProperty->SetColor(this->PolygonColor);
721 this->PolygonProperty->SetOpacity(this->PolygonOpacity);
722
723 this->BuildTime.Modified();
724 }
725 }
726
727 //------------------------------------------------------------------------------
GetActors2D(vtkPropCollection * pc)728 void vtkBorderRepresentation::GetActors2D(vtkPropCollection* pc)
729 {
730 pc->AddItem(this->BWActorEdges);
731 pc->AddItem(this->BWActorPolygon);
732 }
733
734 //------------------------------------------------------------------------------
ReleaseGraphicsResources(vtkWindow * w)735 void vtkBorderRepresentation::ReleaseGraphicsResources(vtkWindow* w)
736 {
737 this->BWActorEdges->ReleaseGraphicsResources(w);
738 this->BWActorPolygon->ReleaseGraphicsResources(w);
739 }
740
741 //------------------------------------------------------------------------------
RenderOverlay(vtkViewport * w)742 int vtkBorderRepresentation::RenderOverlay(vtkViewport* w)
743 {
744 this->BuildRepresentation();
745 if (!this->BWActorEdges->GetVisibility())
746 {
747 return 0;
748 }
749 return this->BWActorEdges->RenderOverlay(w) && this->BWActorPolygon->RenderOverlay(w);
750 }
751
752 //------------------------------------------------------------------------------
RenderOpaqueGeometry(vtkViewport * w)753 int vtkBorderRepresentation::RenderOpaqueGeometry(vtkViewport* w)
754 {
755 this->BuildRepresentation();
756 if (!this->BWActorEdges->GetVisibility())
757 {
758 return 0;
759 }
760 return this->BWActorEdges->RenderOpaqueGeometry(w) &&
761 this->BWActorPolygon->RenderOpaqueGeometry(w);
762 }
763
764 //------------------------------------------------------------------------------
RenderTranslucentPolygonalGeometry(vtkViewport * w)765 int vtkBorderRepresentation::RenderTranslucentPolygonalGeometry(vtkViewport* w)
766 {
767 this->BuildRepresentation();
768 if (!this->BWActorEdges->GetVisibility())
769 {
770 return 0;
771 }
772 return this->BWActorEdges->RenderTranslucentPolygonalGeometry(w) &&
773 this->BWActorPolygon->RenderTranslucentPolygonalGeometry(w);
774 }
775
776 //------------------------------------------------------------------------------
777 // Description:
778 // Does this prop have some translucent polygonal geometry?
HasTranslucentPolygonalGeometry()779 vtkTypeBool vtkBorderRepresentation::HasTranslucentPolygonalGeometry()
780 {
781 this->BuildRepresentation();
782 if (!this->BWActorEdges->GetVisibility())
783 {
784 return 0;
785 }
786 return this->BWActorEdges->HasTranslucentPolygonalGeometry() &&
787 this->BWActorPolygon->HasTranslucentPolygonalGeometry();
788 }
789
790 //------------------------------------------------------------------------------
SetPolygonRGBA(double rgba[4])791 void vtkBorderRepresentation::SetPolygonRGBA(double rgba[4])
792 {
793 this->SetPolygonRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
794 }
795
796 //------------------------------------------------------------------------------
SetPolygonRGBA(double r,double g,double b,double a)797 void vtkBorderRepresentation::SetPolygonRGBA(double r, double g, double b, double a)
798 {
799 this->SetPolygonColor(r, g, b);
800 this->SetPolygonOpacity(a);
801 }
802
803 //------------------------------------------------------------------------------
GetPolygonRGBA(double rgba[4])804 void vtkBorderRepresentation::GetPolygonRGBA(double rgba[4])
805 {
806 this->GetPolygonRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
807 }
808
809 //------------------------------------------------------------------------------
GetPolygonRGBA(double & r,double & g,double & b,double & a)810 void vtkBorderRepresentation::GetPolygonRGBA(double& r, double& g, double& b, double& a)
811 {
812 this->GetPolygonColor(r, g, b);
813 a = this->GetPolygonOpacity();
814 }
815
816 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)817 void vtkBorderRepresentation::PrintSelf(ostream& os, vtkIndent indent)
818 {
819 this->Superclass::PrintSelf(os, indent);
820
821 os << indent << "Show Vertical Border: ";
822 if (this->ShowVerticalBorder == BORDER_OFF)
823 {
824 os << "Off" << endl;
825 }
826 else if (this->ShowVerticalBorder == BORDER_ON)
827 {
828 os << "On" << endl;
829 }
830 else // if ( this->ShowVerticalBorder == BORDER_ACTIVE)
831 {
832 os << "Active" << endl;
833 }
834
835 os << indent << "Show Horizontal Border: ";
836 if (this->ShowHorizontalBorder == BORDER_OFF)
837 {
838 os << "Off" << endl;
839 }
840 else if (this->ShowHorizontalBorder == BORDER_ON)
841 {
842 os << "On" << endl;
843 }
844 else // if ( this->ShowHorizontalBorder == BORDER_ACTIVE)
845 {
846 os << "Active" << endl;
847 }
848
849 if (this->BorderProperty)
850 {
851 os << indent << "Border Property:" << endl;
852 this->BorderProperty->PrintSelf(os, indent.GetNextIndent());
853 }
854 else
855 {
856 os << indent << "Border Property: (none)" << endl;
857 }
858
859 if (this->PolygonProperty)
860 {
861 os << indent << "Polygon Property:" << endl;
862 this->PolygonProperty->PrintSelf(os, indent.GetNextIndent());
863 }
864 else
865 {
866 os << indent << "Polygon Property: (none)" << endl;
867 }
868
869 os << indent << "Enforce Normalized Viewport Bounds: "
870 << (this->EnforceNormalizedViewportBounds ? "On\n" : "Off\n");
871 os << indent << "Proportional Resize: " << (this->ProportionalResize ? "On" : "Off") << endl;
872 os << indent << "Minimum Normalized Viewport Size: " << this->MinimumNormalizedViewportSize[0]
873 << " " << this->MinimumNormalizedViewportSize[1] << endl;
874 os << indent << "Minimum Size: " << this->MinimumSize[0] << " " << this->MinimumSize[1] << endl;
875 os << indent << "Maximum Size: " << this->MaximumSize[0] << " " << this->MaximumSize[1] << endl;
876
877 os << indent << "Moving: " << (this->Moving ? "On" : "Off") << endl;
878 os << indent << "Tolerance: " << this->Tolerance << endl;
879
880 os << indent << "Selection Point: (" << this->SelectionPoint[0] << "," << this->SelectionPoint[1]
881 << "}" << endl;
882
883 os << indent << "BorderColor: (" << this->BorderColor[0] << ", " << this->BorderColor[1] << ", "
884 << this->BorderColor[2] << ")" << endl;
885 os << indent << "BorderThickness: " << this->BorderThickness << endl;
886 os << indent << "CornerRadiusStrength: " << this->CornerRadiusStrength << endl;
887 os << indent << "CornerResolution: " << this->CornerResolution << endl;
888
889 os << indent << "PolygonColor: (" << this->PolygonColor[0] << ", " << this->PolygonColor[1]
890 << ", " << this->PolygonColor[2] << ")" << endl;
891 os << indent << "PolygonOpacity: " << this->PolygonOpacity << endl;
892 }
893