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