1 /*==============================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkLabeledContourMapper.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 "vtkLabeledContourMapper.h"
17
18 #include "vtkCamera.h"
19 #include "vtkCellArray.h"
20 #include "vtkCoordinate.h"
21 #include "vtkDoubleArray.h"
22 #include "vtkExecutive.h"
23 #include "vtkInformation.h"
24 #include "vtkIntArray.h"
25 #include "vtkMatrix4x4.h"
26 #include "vtkMath.h"
27 #include "vtkObjectFactory.h"
28 #include "vtkPointData.h"
29 #include "vtkPolyData.h"
30 #include "vtkPolyDataMapper.h"
31 #include "vtkRenderer.h"
32 #include "vtkRenderWindow.h"
33 #include "vtkTextActor3D.h"
34 #include "vtkTextProperty.h"
35 #include "vtkTextPropertyCollection.h"
36 #include "vtkTextRenderer.h"
37 #include "vtkTimerLog.h"
38 #include "vtkTransform.h"
39 #include "vtkVector.h"
40 #include "vtkVectorOperators.h"
41 #include "vtkWindow.h"
42
43 #include <algorithm>
44 #include <cassert>
45 #include <cmath>
46 #include <map>
47 #include <sstream>
48 #include <string>
49 #include <vector>
50
51 //------------------------------------------------------------------------------
52 struct LabelMetric
53 {
54 bool Valid;
55 double Value;
56 vtkTextProperty *TProp;
57 std::string Text;
58 // These measure the pixel size of the text texture:
59 vtkTuple<int, 4> BoundingBox;
60 vtkTuple<int, 2> Dimensions;
61 };
62
63 //------------------------------------------------------------------------------
64 struct LabelInfo
65 {
66 // Position in actor space:
67 vtkVector3d Position;
68
69 // Orientation (normalized, world space)
70 vtkVector3d RightW; // Left --> Right
71 vtkVector3d UpW; // Bottom --> Top
72
73 // Orientation (normalized in world space, represented in actor space)
74 vtkVector3d RightA; // Left --> Right
75 vtkVector3d UpA; // Bottom --> Top
76
77 // Corner locations (actor space):
78 vtkVector3d TLa;
79 vtkVector3d TRa;
80 vtkVector3d BRa;
81 vtkVector3d BLa;
82
83 // Corner location (display space):
84 vtkVector2i TLd;
85 vtkVector2i TRd;
86 vtkVector2i BRd;
87 vtkVector2i BLd;
88
89 // Factor to scale the text actor by:
90 double ScaleDisplayToActor;
91 };
92
93 namespace {
94
95 // Circular iterator through a vtkTextPropertyCollection -----------------------
96 class TextPropLoop
97 {
98 vtkTextPropertyCollection *TProps;
99 public:
TextPropLoop(vtkTextPropertyCollection * col)100 TextPropLoop(vtkTextPropertyCollection *col)
101 : TProps(col)
102 {
103 TProps->InitTraversal();
104 }
105
Next()106 vtkTextProperty* Next()
107 {
108 // The input checks should fail if this is the case:
109 assert("No text properties set! Prerender check failed!" &&
110 TProps->GetNumberOfItems() != 0);
111
112 vtkTextProperty *result = TProps->GetNextItem();
113 if (!result)
114 {
115 TProps->InitTraversal();
116 result = TProps->GetNextItem();
117 assert("Text property traversal error." && result != nullptr);
118 }
119 return result;
120 }
121 };
122
123 //------------------------------------------------------------------------------
calculateSmoothness(double pathLength,double distance)124 double calculateSmoothness(double pathLength, double distance)
125 {
126 return (pathLength - distance) / distance;
127 }
128
129 } // end anon namespace
130
131 //------------------------------------------------------------------------------
132 struct vtkLabeledContourMapper::Private
133 {
134 // One entry per isoline.
135 std::vector<LabelMetric> LabelMetrics;
136
137 // One LabelInfo per label groups by isoline.
138 std::vector<std::vector<LabelInfo> > LabelInfos;
139
140 // Info for calculating display coordinates:
141 vtkTuple<double, 16> AMVP; // actor-model-view-projection matrix
142 vtkTuple<double, 16> ActorMatrix; // Actor model matrix
143 vtkTuple<double, 16> InverseActorMatrix; // Inverse Actor model matrix
144 vtkTuple<double, 4> ViewPort; // viewport
145 vtkTuple<double, 4> NormalizedViewPort; // see vtkViewport::ViewToNormalizedVP
146 vtkTuple<int, 2> WindowSize;
147 vtkTuple<int, 2> ViewPortSize;
148 vtkTuple<double, 2> DisplayOffset;
149 vtkTuple<double, 4> ViewportBounds;
150
151 // Needed to orient the labels
152 vtkVector3d CameraRight;
153 vtkVector3d CameraUp;
154 vtkVector3d CameraForward;
155
156 // Render times:
157 double PrepareTime;
158 double RenderTime;
159
160 // Only want to print the stencil warning once:
161 bool AlreadyWarnedAboutStencils;
162
163 // Project coordinates. Note that the vector objects must be unique.
164 void ActorToWorld(const vtkVector3d &actor, vtkVector3d &world) const;
165 void WorldToActor(const vtkVector3d &world, vtkVector3d &actor) const;
166 void ActorToDisplay(const vtkVector3d &actor, vtkVector2i &display) const;
167 void ActorToDisplay(const vtkVector3d &actor, vtkVector2d &display) const;
168
169 // Camera axes:
170 bool SetViewInfo(vtkRenderer *ren, vtkActor *act);
171
172 // Visibility test (display space):
173 template <typename ScalarType>
174 bool PixelIsVisible(const vtkVector2<ScalarType> &dispCoord) const;
175
176 bool LineCanBeLabeled(vtkPoints *points, vtkIdType numIds,
177 const vtkIdType *ids, const LabelMetric &metrics);
178
179 // Determine the first smooth position on the line defined by ids that is
180 // 1.2x the length of the label (in display coordinates).
181 // The position will be no less than skipDistance along the line from the
182 // starting location. This can be used to ensure that labels are placed a
183 // minimum distance apart.
184 bool NextLabel(vtkPoints *points, vtkIdType &numIds, vtkIdType *&ids,
185 const LabelMetric &metrics, LabelInfo &info,
186 double targetSmoothness, double skipDistance);
187
188 // Configure the text actor:
189 bool BuildLabel(vtkTextActor3D *actor, const LabelMetric &metric,
190 const LabelInfo &info);
191
192 // Compute the scaling factor and corner info for the label
193 void ComputeLabelInfo(LabelInfo &info, const LabelMetric &metrics);
194
195 // Test if the display quads overlap:
196 bool TestOverlap(const LabelInfo &a, const LabelInfo &b);
197 };
198
199 //------------------------------------------------------------------------------
vtkObjectFactoryNewMacro(vtkLabeledContourMapper)200 vtkObjectFactoryNewMacro(vtkLabeledContourMapper)
201
202 //------------------------------------------------------------------------------
203 vtkLabeledContourMapper::vtkLabeledContourMapper()
204 {
205 this->SkipDistance = 0.;
206 this->LabelVisibility = true;
207 this->TextActors = nullptr;
208 this->NumberOfTextActors = 0;
209 this->NumberOfUsedTextActors = 0;
210
211 this->StencilQuads = nullptr;
212 this->StencilQuadsSize = 0;
213 this->StencilQuadIndices = nullptr;
214 this->StencilQuadIndicesSize = 0;
215
216 this->TextProperties = vtkSmartPointer<vtkTextPropertyCollection>::New();
217 vtkNew<vtkTextProperty> defaultTProp;
218 this->TextProperties->AddItem(defaultTProp);
219
220 this->Internal = new vtkLabeledContourMapper::Private();
221 this->Internal->PrepareTime = 0.0;
222 this->Internal->RenderTime = 0.0;
223 this->Internal->AlreadyWarnedAboutStencils = false;
224
225 this->Reset();
226 }
227
228 //------------------------------------------------------------------------------
~vtkLabeledContourMapper()229 vtkLabeledContourMapper::~vtkLabeledContourMapper()
230 {
231 this->FreeStencilQuads();
232 this->FreeTextActors();
233
234 delete this->Internal;
235 }
236
237 //------------------------------------------------------------------------------
Render(vtkRenderer * ren,vtkActor * act)238 void vtkLabeledContourMapper::Render(vtkRenderer *ren, vtkActor *act)
239 {
240 if (vtkRenderWindow *renderWindow = ren->GetRenderWindow())
241 {
242 // Is the viewport's RenderWindow capturing GL2PS-special props?
243 if (renderWindow->GetCapturingGL2PSSpecialProps())
244 {
245 ren->CaptureGL2PSSpecialProp(act);
246 }
247 }
248
249 // Make sure input data is synced
250 if (vtkAlgorithm *inputAlgorithm = this->GetInputAlgorithm())
251 {
252 inputAlgorithm->Update();
253 }
254
255 if (!this->CheckInputs(ren))
256 {
257 return;
258 }
259
260 if (!this->LabelVisibility)
261 {
262 this->RenderPolyData(ren, act);
263 return;
264 }
265
266 if (this->CheckRebuild(ren, act))
267 {
268 double startPrep = vtkTimerLog::GetUniversalTime();
269
270 this->Reset();
271
272 if (!this->PrepareRender(ren, act))
273 {
274 return;
275 }
276
277 if (!this->PlaceLabels())
278 {
279 return;
280 }
281
282 if (!this->ResolveLabels())
283 {
284 return;
285 }
286
287 if (!this->CreateLabels(act))
288 {
289 return;
290 }
291
292 if (!this->BuildStencilQuads())
293 {
294 return;
295 }
296
297 this->Internal->PrepareTime = vtkTimerLog::GetUniversalTime() - startPrep;
298 this->LabelBuildTime.Modified();
299 }
300
301 double startRender = vtkTimerLog::GetUniversalTime();
302
303 if (!this->ApplyStencil(ren, act))
304 {
305 return;
306 }
307
308 if (!this->RenderPolyData(ren, act))
309 {
310 this->RemoveStencil(ren);
311 return;
312 }
313
314 if (!this->RemoveStencil(ren))
315 {
316 return;
317 }
318
319 if (!this->RenderLabels(ren, act))
320 {
321 return;
322 }
323
324 this->Internal->RenderTime = vtkTimerLog::GetUniversalTime() - startRender;
325 }
326
327 //------------------------------------------------------------------------------
SetInputData(vtkPolyData * input)328 void vtkLabeledContourMapper::SetInputData(vtkPolyData *input)
329 {
330 this->SetInputDataInternal(0, input);
331 }
332
333 //------------------------------------------------------------------------------
GetInput()334 vtkPolyData *vtkLabeledContourMapper::GetInput()
335 {
336 return vtkPolyData::SafeDownCast(this->GetExecutive()->GetInputData(0, 0));
337 }
338
339 //------------------------------------------------------------------------------
GetBounds()340 double *vtkLabeledContourMapper::GetBounds()
341 {
342 if (this->GetNumberOfInputConnections(0) == 0)
343 {
344 vtkMath::UninitializeBounds(this->Bounds);
345 return this->Bounds;
346 }
347 else
348 {
349 this->ComputeBounds();
350 return this->Bounds;
351 }
352 }
353
354 //------------------------------------------------------------------------------
GetBounds(double bounds[])355 void vtkLabeledContourMapper::GetBounds(double bounds[])
356 {
357 this->Superclass::GetBounds(bounds);
358 }
359
360 //------------------------------------------------------------------------------
SetTextProperty(vtkTextProperty * tprop)361 void vtkLabeledContourMapper::SetTextProperty(vtkTextProperty *tprop)
362 {
363 if (this->TextProperties->GetNumberOfItems() != 1 ||
364 this->TextProperties->GetItemAsObject(0) != tprop)
365 {
366 this->TextProperties->RemoveAllItems();
367 this->TextProperties->AddItem(tprop);
368 this->Modified();
369 }
370 }
371
372 //------------------------------------------------------------------------------
SetTextProperties(vtkTextPropertyCollection * coll)373 void vtkLabeledContourMapper::SetTextProperties(vtkTextPropertyCollection *coll)
374 {
375 if (coll != this->TextProperties)
376 {
377 this->TextProperties = coll;
378 this->Modified();
379 }
380 }
381
382 //------------------------------------------------------------------------------
GetTextProperties()383 vtkTextPropertyCollection *vtkLabeledContourMapper::GetTextProperties()
384 {
385 return this->TextProperties;
386 }
387
388 //------------------------------------------------------------------------------
GetTextPropertyMapping()389 vtkDoubleArray *vtkLabeledContourMapper::GetTextPropertyMapping()
390 {
391 return this->TextPropertyMapping;
392 }
393
394 //------------------------------------------------------------------------------
SetTextPropertyMapping(vtkDoubleArray * mapping)395 void vtkLabeledContourMapper::SetTextPropertyMapping(vtkDoubleArray *mapping)
396 {
397 if (this->TextPropertyMapping != mapping)
398 {
399 this->TextPropertyMapping = mapping;
400 this->Modified();
401 }
402 }
403
404 //------------------------------------------------------------------------------
ReleaseGraphicsResources(vtkWindow * win)405 void vtkLabeledContourMapper::ReleaseGraphicsResources(vtkWindow *win)
406 {
407 this->PolyDataMapper->ReleaseGraphicsResources(win);
408 for (vtkIdType i = 0; i < this->NumberOfTextActors; ++i)
409 {
410 this->TextActors[i]->ReleaseGraphicsResources(win);
411 }
412 }
413
414 //------------------------------------------------------------------------------
ComputeBounds()415 void vtkLabeledContourMapper::ComputeBounds()
416 {
417 this->GetInput()->GetBounds(this->Bounds);
418 }
419
420 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)421 void vtkLabeledContourMapper::PrintSelf(ostream& os, vtkIndent indent)
422 {
423 this->Superclass::PrintSelf(os,indent);
424
425 os << indent << "SkipDistance: " << this->SkipDistance << "\n"
426 << indent << "LabelVisibility: " << (this->LabelVisibility ? "On\n"
427 : "Off\n")
428 << indent << "NumberOfTextActors: " << this->NumberOfTextActors << "\n"
429 << indent << "NumberOfUsedTextActors: "
430 << this->NumberOfUsedTextActors << "\n"
431 << indent << "StencilQuadsSize: " << this->StencilQuadsSize << "\n"
432 << indent << "StencilQuadIndicesSize: "
433 << this->StencilQuadIndicesSize << "\n"
434 << indent << "BuildTime: " << this->LabelBuildTime.GetMTime() << "\n"
435 << indent << "PolyDataMapper:\n";
436 this->PolyDataMapper->PrintSelf(os, indent.GetNextIndent());
437 os << indent << "TextProperties:\n";
438 this->TextProperties->PrintSelf(os, indent.GetNextIndent());
439 os << indent << "TextPropertyMapping:";
440 if (this->TextPropertyMapping)
441 {
442 os << "\n";
443 this->TextPropertyMapping->PrintSelf(os, indent.GetNextIndent());
444 }
445 else
446 {
447 os << " (nullptr)\n";
448 }
449
450 }
451
452 //------------------------------------------------------------------------------
FillInputPortInformation(int vtkNotUsed (port),vtkInformation * info)453 int vtkLabeledContourMapper::FillInputPortInformation(
454 int vtkNotUsed(port), vtkInformation* info)
455 {
456 info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");
457 return 1;
458 }
459
460 //------------------------------------------------------------------------------
Reset()461 void vtkLabeledContourMapper::Reset()
462 {
463 this->Internal->LabelMetrics.clear();
464 this->Internal->LabelInfos.clear();
465
466 this->TextProperties->InitTraversal();
467 while (vtkTextProperty *tprop = this->TextProperties->GetNextItem())
468 {
469 tprop->SetJustificationToCentered();
470 tprop->SetVerticalJustificationToCentered();
471 }
472 }
473
474 //------------------------------------------------------------------------------
CheckInputs(vtkRenderer * ren)475 bool vtkLabeledContourMapper::CheckInputs(vtkRenderer *ren)
476 {
477 vtkPolyData *input = this->GetInput();
478 if (!input)
479 {
480 vtkErrorMacro(<<"No input data!")
481 return false;
482 }
483
484 if (!input->GetPoints())
485 {
486 vtkErrorMacro(<<"No points in dataset!");
487 return false;
488 }
489
490 if (!input->GetPointData())
491 {
492 vtkErrorMacro(<<"No point data in dataset!");
493 return false;
494 }
495
496 vtkCellArray *lines = input->GetLines();
497 if (!lines)
498 {
499 vtkErrorMacro(<<"No lines in dataset!");
500 return false;
501 }
502
503 vtkDataArray *scalars = input->GetPointData()->GetScalars();
504 if (!scalars)
505 {
506 vtkErrorMacro(<<"No scalars in dataset!");
507 return false;
508 }
509
510 vtkTextRenderer *tren = vtkTextRenderer::GetInstance();
511 if (!tren)
512 {
513 vtkErrorMacro(<<"Text renderer unavailable.");
514 return false;
515 }
516
517 if (this->TextProperties->GetNumberOfItems() == 0)
518 {
519 vtkErrorMacro(<<"No text properties set!");
520 return false;
521 }
522
523 // Print a warning if stenciling is not enabled:
524 vtkRenderWindow *win = ren->GetRenderWindow();
525 if (!this->Internal->AlreadyWarnedAboutStencils && win)
526 {
527 if (win->GetStencilCapable() == 0)
528 {
529 vtkWarningMacro(<< "Stenciling is not enabled in the render window. "
530 "Isoline labels will have artifacts. To fix this, "
531 "call vtkRenderWindow::StencilCapableOn().")
532 this->Internal->AlreadyWarnedAboutStencils = true;
533 }
534 }
535
536 return true;
537 }
538
539 //------------------------------------------------------------------------------
CheckRebuild(vtkRenderer *,vtkActor * act)540 bool vtkLabeledContourMapper::CheckRebuild(vtkRenderer *, vtkActor *act)
541 {
542 // Get the highest mtime for the text properties:
543 vtkMTimeType tPropMTime = this->TextProperties->GetMTime();
544 this->TextProperties->InitTraversal();
545 while (vtkTextProperty *tprop = this->TextProperties->GetNextItem())
546 {
547 tPropMTime = std::max(tPropMTime, tprop->GetMTime());
548 }
549
550 // Are we out of date?
551 if (this->LabelBuildTime.GetMTime() < this->GetInput()->GetMTime() ||
552 this->LabelBuildTime.GetMTime() < tPropMTime)
553 {
554 return true;
555 }
556
557 // Is there enough time allocated? (i.e. is this not an interactive render?)
558 if (act->GetAllocatedRenderTime() >=
559 (this->Internal->RenderTime + this->Internal->PrepareTime))
560 {
561 return true;
562 }
563
564 return false;
565 }
566
567 //------------------------------------------------------------------------------
PrepareRender(vtkRenderer * ren,vtkActor * act)568 bool vtkLabeledContourMapper::PrepareRender(vtkRenderer *ren, vtkActor *act)
569 {
570 if (!this->Internal->SetViewInfo(ren, act))
571 {
572 return false;
573 }
574
575 // Already checked that these exist in CheckInputs()
576 vtkPolyData *input = this->GetInput();
577 vtkCellArray *lines = input->GetLines();
578 vtkDataArray *scalars = input->GetPointData()->GetScalars();
579 vtkTextRenderer *tren = vtkTextRenderer::GetInstance();
580 if (!tren)
581 {
582 vtkErrorMacro(<< "Text renderer unavailable.");
583 return false;
584 }
585
586 // Maps scalar values to text properties:
587 typedef std::map<double, vtkTextProperty*> LabelPropertyMapType;
588 LabelPropertyMapType labelMap;
589
590 // Initialize with the user-requested mapping, if it exists.
591 if (this->TextPropertyMapping != nullptr)
592 {
593 vtkDoubleArray::Iterator valIt = this->TextPropertyMapping->Begin();
594 vtkDoubleArray::Iterator valItEnd = this->TextPropertyMapping->End();
595 TextPropLoop tprops(this->TextProperties);
596 for (; valIt != valItEnd; ++valIt)
597 {
598 labelMap.insert(std::make_pair(*valIt, tprops.Next()));
599 }
600 }
601
602 // Create the list of metrics, but no text property information yet.
603 vtkIdType numPts;
604 vtkIdType *ids;
605 for (lines->InitTraversal(); lines->GetNextCell(numPts, ids);)
606 {
607 this->Internal->LabelMetrics.push_back(LabelMetric());
608 LabelMetric &metric = this->Internal->LabelMetrics.back();
609 if (!(metric.Valid = (numPts > 0)))
610 {
611 // Mark as invalid and skip if there are no points.
612 continue;
613 }
614 metric.Value = scalars->GetComponent(ids[0], 0);
615 metric.Value = std::fabs(metric.Value) > 1e-6 ? metric.Value : 0.0;
616 std::ostringstream str;
617 str << metric.Value;
618 metric.Text = str.str();
619
620 // Beware future maintainers: The following line of code has been carefully
621 // crafted to reach a zen-like harmony of compatibility between various
622 // compilers that have differing syntactic requirements for creating a
623 // pair containing a nullptr:
624 // - Pedantically strict C++11 compilers (e.g. MSVC 2012) will not compile:
625 // std::make_pair<double, X*>(someDouble, nullptr);
626 // or any make_pair call with explicit template args and value arguments,
627 // as the signature expects an rvalue.
628
629 // The value will be replaced in the next loop:
630 labelMap.insert(std::pair<double, vtkTextProperty*>(
631 metric.Value, static_cast<vtkTextProperty*>(nullptr)));
632 }
633
634 // Now that all present scalar values are known, assign text properties:
635 TextPropLoop tprops(this->TextProperties);
636 typedef LabelPropertyMapType::iterator LabelPropertyMapIter;
637 for (LabelPropertyMapIter it = labelMap.begin(), itEnd = labelMap.end();
638 it != itEnd; ++it)
639 {
640 if (!it->second) // Skip if initialized from TextPropertyMapping
641 {
642 it->second = tprops.Next();
643 }
644 }
645
646 // Update metrics with appropriate text info:
647 typedef std::vector<LabelMetric>::iterator MetricsIter;
648 for (MetricsIter it = this->Internal->LabelMetrics.begin(),
649 itEnd = this->Internal->LabelMetrics.end(); it != itEnd; ++it)
650 {
651 if (!it->Valid)
652 {
653 continue;
654 }
655
656 // Look up text property for the scalar value:
657 LabelPropertyMapIter tpropIt = labelMap.find(it->Value);
658 assert("No text property assigned for scalar value." &&
659 tpropIt != labelMap.end());
660 it->TProp = tpropIt->second;
661
662 // Assign bounding box/dims.
663 if (!tren->GetBoundingBox(it->TProp, it->Text, it->BoundingBox.GetData(),
664 vtkTextActor3D::GetRenderedDPI()))
665 {
666 vtkErrorMacro(<<"Error calculating bounding box for string '"
667 << it->Text << "'.");
668 return false;
669 }
670 it->Dimensions[0] = it->BoundingBox[1] - it->BoundingBox[0] + 1;
671 it->Dimensions[1] = it->BoundingBox[3] - it->BoundingBox[2] + 1;
672 }
673
674 return true;
675 }
676
677 //------------------------------------------------------------------------------
PlaceLabels()678 bool vtkLabeledContourMapper::PlaceLabels()
679 {
680 vtkPolyData *input = this->GetInput();
681 vtkPoints *points = input->GetPoints();
682 vtkCellArray *lines = input->GetLines();
683
684 // Progression of smoothness tolerances to use.
685 std::vector<double> tols;
686 tols.push_back(0.010);
687 tols.push_back(0.025);
688 tols.push_back(0.050);
689 tols.push_back(0.100);
690 tols.push_back(0.200);
691 tols.push_back(0.300);
692
693 typedef std::vector<LabelMetric>::const_iterator MetricsIterator;
694 MetricsIterator metric = this->Internal->LabelMetrics.begin();
695
696 // Identify smooth parts of the isoline for labeling
697 vtkIdType numIds;
698 vtkIdType *origIds;
699 this->Internal->LabelInfos.reserve(this->Internal->LabelMetrics.size());
700 for (lines->InitTraversal(); lines->GetNextCell(numIds, origIds); ++metric)
701 {
702 assert(metric != this->Internal->LabelMetrics.end());
703
704 this->Internal->LabelInfos.push_back(std::vector<LabelInfo>());
705
706 // Test if it is possible to place a label (e.g. the line is big enough
707 // to not be completely obscured)
708 if (this->Internal->LineCanBeLabeled(points, numIds, origIds, *metric))
709 {
710 std::vector<LabelInfo> &infos = this->Internal->LabelInfos.back();
711 LabelInfo info;
712 // If no labels are found, increase the tolerance:
713 for (std::vector<double>::const_iterator it = tols.begin(),
714 itEnd = tols.end(); it != itEnd && infos.empty(); ++it)
715 {
716 vtkIdType nIds = numIds;
717 vtkIdType *ids = origIds;
718 while (this->Internal->NextLabel(points, nIds, ids, *metric, info, *it,
719 this->SkipDistance))
720 {
721 infos.push_back(info);
722 }
723 }
724 }
725 }
726
727 return true;
728 }
729
730 //------------------------------------------------------------------------------
ResolveLabels()731 bool vtkLabeledContourMapper::ResolveLabels()
732 {
733 typedef std::vector<LabelInfo>::iterator InnerIterator;
734 typedef std::vector<std::vector<LabelInfo> >::iterator OuterIterator;
735
736 bool removedA = false;
737 bool removedB = false;
738
739 OuterIterator outerA = this->Internal->LabelInfos.begin();
740 OuterIterator outerEnd = this->Internal->LabelInfos.end();
741 while (outerA != outerEnd)
742 {
743 InnerIterator innerA = outerA->begin();
744 InnerIterator innerAEnd = outerA->end();
745 while (innerA != innerAEnd)
746 {
747 removedA = false;
748 OuterIterator outerB = outerA;
749 while (!removedA && outerB != outerEnd)
750 {
751 InnerIterator innerB = outerA == outerB ? innerA + 1 : outerB->begin();
752 InnerIterator innerBEnd = outerB->end();
753 while (!removedA && innerB != innerBEnd)
754 {
755 removedB = false;
756 // Does innerA overlap with innerB?
757 if (this->Internal->TestOverlap(*innerA, *innerB))
758 {
759 // Remove the label that has the most labels for its isoline:
760 if (outerA->size() > outerB->size())
761 {
762 // Remove innerA
763 innerA = outerA->erase(innerA);
764 innerAEnd = outerA->end();
765 removedA = true;
766 }
767 else
768 {
769 // Remove innerB
770 // Need to update A's iterators if outerA == outerB
771 if (outerA == outerB)
772 {
773 // We know that aIdx < bIdx, so removing B won't change
774 // the position of A:
775 size_t aIdx = innerA - outerA->begin();
776 innerB = outerB->erase(innerB);
777 innerBEnd = outerB->end();
778 innerA = outerA->begin() + aIdx;
779 innerAEnd = outerA->end();
780 }
781 else
782 {
783 innerB = outerB->erase(innerB);
784 innerBEnd = outerB->end();
785 }
786 removedB = true;
787 }
788 }
789 // Erase will increment B if we removed it.
790 if (!removedB)
791 {
792 ++innerB;
793 }
794 }
795 ++outerB;
796 }
797 // Erase will increment A if we removed it.
798 if (!removedA)
799 {
800 ++innerA;
801 }
802 }
803 ++outerA;
804 }
805
806 return true;
807 }
808
809 //------------------------------------------------------------------------------
CreateLabels(vtkActor *)810 bool vtkLabeledContourMapper::CreateLabels(vtkActor *)
811 {
812 typedef std::vector<LabelMetric> MetricVector;
813 typedef std::vector<LabelInfo> InfoVector;
814
815 std::vector<InfoVector>::const_iterator outerLabels =
816 this->Internal->LabelInfos.begin();
817 std::vector<InfoVector>::const_iterator outerLabelsEnd =
818 this->Internal->LabelInfos.end();
819
820 // count the number of labels:
821 vtkIdType numLabels = 0;
822 while (outerLabels != outerLabelsEnd)
823 {
824 numLabels += static_cast<vtkIdType>((outerLabels++)->size());
825 }
826
827 if (!this->AllocateTextActors(numLabels))
828 {
829 vtkErrorMacro(<< "Error while allocating text actors.");
830 return false;
831 }
832
833 outerLabels = this->Internal->LabelInfos.begin();
834 MetricVector::const_iterator metrics = this->Internal->LabelMetrics.begin();
835 MetricVector::const_iterator metricsEnd = this->Internal->LabelMetrics.end();
836 vtkTextActor3D **actor = this->TextActors;
837 vtkTextActor3D **actorEnd = this->TextActors + this->NumberOfUsedTextActors;
838
839 while (metrics != metricsEnd &&
840 outerLabels != outerLabelsEnd &&
841 actor != actorEnd)
842 {
843 for (InfoVector::const_iterator label = outerLabels->begin(),
844 labelEnd = outerLabels->end(); label != labelEnd; ++label)
845 {
846 this->Internal->BuildLabel(*actor, *metrics, *label);
847 ++actor;
848 }
849 ++metrics;
850 ++outerLabels;
851 }
852
853 return true;
854 }
855
856 //------------------------------------------------------------------------------
ApplyStencil(vtkRenderer *,vtkActor *)857 bool vtkLabeledContourMapper::ApplyStencil(vtkRenderer *, vtkActor *)
858 {
859 // Handled in backend override.
860 return true;
861 }
862
863 //------------------------------------------------------------------------------
RenderPolyData(vtkRenderer * ren,vtkActor * act)864 bool vtkLabeledContourMapper::RenderPolyData(vtkRenderer *ren, vtkActor *act)
865 {
866 this->PolyDataMapper->SetInputConnection(this->GetInputConnection(0, 0));
867 this->PolyDataMapper->Render(ren, act);
868 return true;
869 }
870
871 //------------------------------------------------------------------------------
RemoveStencil(vtkRenderer *)872 bool vtkLabeledContourMapper::RemoveStencil(vtkRenderer *)
873 {
874 // Handled in backend override.
875 return true;
876 }
877
878 //------------------------------------------------------------------------------
RenderLabels(vtkRenderer * ren,vtkActor *)879 bool vtkLabeledContourMapper::RenderLabels(vtkRenderer *ren, vtkActor *)
880 {
881 for (vtkIdType i = 0; i < this->NumberOfUsedTextActors; ++i)
882 {
883 // Needed for GL2PS capture:
884 this->TextActors[i]->RenderOpaqueGeometry(ren);
885 // Actually draw:
886 this->TextActors[i]->RenderTranslucentPolygonalGeometry(ren);
887 }
888 return true;
889 }
890
891 //------------------------------------------------------------------------------
AllocateTextActors(vtkIdType num)892 bool vtkLabeledContourMapper::AllocateTextActors(vtkIdType num)
893 {
894 if (num != this->NumberOfUsedTextActors)
895 {
896 if (this->NumberOfTextActors < num ||
897 this->NumberOfTextActors > 2 * num)
898 {
899 this->FreeTextActors();
900
901 // Leave some room to grow:
902 this->NumberOfTextActors = num * 1.2;
903
904 this->TextActors = new vtkTextActor3D*[this->NumberOfTextActors];
905 for (vtkIdType i = 0; i < this->NumberOfTextActors; ++i)
906 {
907 this->TextActors[i] = vtkTextActor3D::New();
908 }
909 }
910
911 this->NumberOfUsedTextActors = num;
912 }
913
914 return true;
915 }
916
917 //------------------------------------------------------------------------------
FreeTextActors()918 bool vtkLabeledContourMapper::FreeTextActors()
919 {
920 for (vtkIdType i = 0; i < this->NumberOfTextActors; ++i)
921 {
922 this->TextActors[i]->Delete();
923 }
924
925 delete [] this->TextActors;
926 this->TextActors = nullptr;
927 this->NumberOfTextActors = 0;
928 this->NumberOfUsedTextActors = 0;
929 return true;
930 }
931
932 //------------------------------------------------------------------------------
FreeStencilQuads()933 void vtkLabeledContourMapper::FreeStencilQuads()
934 {
935 if (this->StencilQuads)
936 {
937 delete [] this->StencilQuads;
938 this->StencilQuads = nullptr;
939 this->StencilQuadsSize = 0;
940
941 delete [] this->StencilQuadIndices;
942 this->StencilQuadIndices = nullptr;
943 this->StencilQuadIndicesSize = 0;
944 }
945 }
946
947 //------------------------------------------------------------------------------
BuildStencilQuads()948 bool vtkLabeledContourMapper::BuildStencilQuads()
949 {
950 vtkIdType quadCount = this->NumberOfUsedTextActors * 12;
951 vtkIdType idxCount = this->NumberOfUsedTextActors * 6;
952 if (quadCount != this->StencilQuadsSize)
953 {
954 this->FreeStencilQuads();
955 this->StencilQuads = new float[quadCount];
956 this->StencilQuadsSize = quadCount;
957 this->StencilQuadIndices = new unsigned int[idxCount];
958 this->StencilQuadIndicesSize = idxCount;
959 }
960
961 unsigned int qIndex = 0; // quad array index
962 unsigned int iIndex = 0; // index array index
963 unsigned int eIndex = 0; // index array element
964
965 typedef std::vector<LabelInfo>::const_iterator InnerIterator;
966 typedef std::vector<std::vector<LabelInfo> >::const_iterator OuterIterator;
967
968 for (OuterIterator out = this->Internal->LabelInfos.begin(),
969 outEnd = this->Internal->LabelInfos.end(); out != outEnd; ++out)
970 {
971 for (InnerIterator in = out->begin(), inEnd = out->end(); in != inEnd; ++in)
972 {
973 this->StencilQuads[qIndex + 0] = static_cast<float>(in->TLa[0]);
974 this->StencilQuads[qIndex + 1] = static_cast<float>(in->TLa[1]);
975 this->StencilQuads[qIndex + 2] = static_cast<float>(in->TLa[2]);
976 this->StencilQuads[qIndex + 3] = static_cast<float>(in->TRa[0]);
977 this->StencilQuads[qIndex + 4] = static_cast<float>(in->TRa[1]);
978 this->StencilQuads[qIndex + 5] = static_cast<float>(in->TRa[2]);
979 this->StencilQuads[qIndex + 6] = static_cast<float>(in->BRa[0]);
980 this->StencilQuads[qIndex + 7] = static_cast<float>(in->BRa[1]);
981 this->StencilQuads[qIndex + 8] = static_cast<float>(in->BRa[2]);
982 this->StencilQuads[qIndex + 9] = static_cast<float>(in->BLa[0]);
983 this->StencilQuads[qIndex + 10] = static_cast<float>(in->BLa[1]);
984 this->StencilQuads[qIndex + 11] = static_cast<float>(in->BLa[2]);
985
986 this->StencilQuadIndices[iIndex + 0] = eIndex + 0;
987 this->StencilQuadIndices[iIndex + 1] = eIndex + 1;
988 this->StencilQuadIndices[iIndex + 2] = eIndex + 2;
989 this->StencilQuadIndices[iIndex + 3] = eIndex + 0;
990 this->StencilQuadIndices[iIndex + 4] = eIndex + 2;
991 this->StencilQuadIndices[iIndex + 5] = eIndex + 3;
992
993 qIndex += 12;
994 iIndex += 6;
995 eIndex += 4;
996 }
997 }
998
999 return true;
1000 }
1001
1002 //------------------------------------------------------------------------------
ActorToWorld(const vtkVector3d & in,vtkVector3d & out) const1003 void vtkLabeledContourMapper::Private::ActorToWorld(const vtkVector3d &in,
1004 vtkVector3d &out) const
1005 {
1006 const vtkTuple<double, 16> &x = this->ActorMatrix;
1007 double w;
1008 out[0] = in[0] * x[0] + in[1] * x[1] + in[2] * x[2] + x[3];
1009 out[1] = in[0] * x[4] + in[1] * x[5] + in[2] * x[6] + x[7];
1010 out[2] = in[0] * x[8] + in[1] * x[9] + in[2] * x[10] + x[11];
1011 w = in[0] * x[12] + in[1] * x[13] + in[2] * x[14] + x[15];
1012 out = out * (1. / w);
1013 }
1014
1015 //------------------------------------------------------------------------------
WorldToActor(const vtkVector3d & in,vtkVector3d & out) const1016 void vtkLabeledContourMapper::Private::WorldToActor(const vtkVector3d &in,
1017 vtkVector3d &out) const
1018 {
1019 const vtkTuple<double, 16> &x = this->InverseActorMatrix;
1020 double w;
1021 out[0] = in[0] * x[0] + in[1] * x[1] + in[2] * x[2] + x[3];
1022 out[1] = in[0] * x[4] + in[1] * x[5] + in[2] * x[6] + x[7];
1023 out[2] = in[0] * x[8] + in[1] * x[9] + in[2] * x[10] + x[11];
1024 w = in[0] * x[12] + in[1] * x[13] + in[2] * x[14] + x[15];
1025 out = out * (1. / w);
1026 }
1027
1028 //------------------------------------------------------------------------------
ActorToDisplay(const vtkVector3d & actor,vtkVector2i & out) const1029 void vtkLabeledContourMapper::Private::ActorToDisplay(const vtkVector3d &actor,
1030 vtkVector2i &out) const
1031 {
1032 vtkVector2d v;
1033 this->ActorToDisplay(actor, v);
1034 out = vtkVector2i(v.Cast<int>().GetData());
1035 }
1036
1037 //------------------------------------------------------------------------------
ActorToDisplay(const vtkVector3d & actor,vtkVector2d & v) const1038 void vtkLabeledContourMapper::Private::ActorToDisplay(const vtkVector3d &actor,
1039 vtkVector2d &v) const
1040 {
1041 // This is adapted from vtkCoordinate's world to display conversion. We
1042 // reimplement it here for efficiency.
1043
1044 // vtkRenderer::WorldToView (AMVP includes the actor matrix, too)
1045 const vtkTuple<double, 16> &x = this->AMVP;
1046 double w;
1047 v[0] = actor[0] * x[0] + actor[1] * x[1] + actor[2] * x[2] + x[3];
1048 v[1] = actor[0] * x[4] + actor[1] * x[5] + actor[2] * x[6] + x[7];
1049 w = actor[0] * x[12] + actor[1] * x[13] + actor[2] * x[14] + x[15];
1050 v = v * (1. / w);
1051
1052 // vtkViewport::ViewToNormalizedViewport
1053 v[0] = this->NormalizedViewPort[0] + ((v[0] + 1.) / 2.) *
1054 (this->NormalizedViewPort[2] - this->NormalizedViewPort[0]);
1055 v[1] = this->NormalizedViewPort[1] + ((v[1] + 1.) / 2.) *
1056 (this->NormalizedViewPort[3] - this->NormalizedViewPort[1]);
1057 v[0] = (v[0] - this->ViewPort[0]) /
1058 (this->ViewPort[2] - this->ViewPort[0]);
1059 v[1] = (v[1] - this->ViewPort[1]) /
1060 (this->ViewPort[3] - this->ViewPort[1]);
1061
1062 // vtkViewport::NormalizedViewportToViewport
1063 v[0] *= this->ViewPortSize[0] - 1.;
1064 v[1] *= this->ViewPortSize[1] - 1.;
1065
1066 // vtkViewport::ViewportToNormalizedDisplay
1067 // vtkViewport::NormalizedDisplayToDisplay
1068 v[0] += this->DisplayOffset[0];
1069 v[1] += this->DisplayOffset[1];
1070 }
1071
1072 //------------------------------------------------------------------------------
SetViewInfo(vtkRenderer * ren,vtkActor * act)1073 bool vtkLabeledContourMapper::Private::SetViewInfo(vtkRenderer *ren,
1074 vtkActor *act)
1075 {
1076 vtkCamera *cam = ren->GetActiveCamera();
1077 if (!cam)
1078 {
1079 vtkGenericWarningMacro(<<"No active camera on renderer.");
1080 return false;
1081 }
1082
1083 vtkMatrix4x4 *mat = cam->GetModelViewTransformMatrix();
1084 this->CameraRight.Set(mat->GetElement(0, 0),
1085 mat->GetElement(0, 1),
1086 mat->GetElement(0, 2));
1087 this->CameraUp.Set(mat->GetElement(1, 0),
1088 mat->GetElement(1, 1),
1089 mat->GetElement(1, 2));
1090 this->CameraForward.Set(mat->GetElement(2, 0),
1091 mat->GetElement(2, 1),
1092 mat->GetElement(2, 2));
1093
1094 // figure out the same aspect ratio used by the render engine
1095 // (see vtkOpenGLCamera::Render())
1096 int lowerLeft[2];
1097 int usize, vsize;
1098 double aspect1[2];
1099 double aspect2[2];
1100 ren->GetTiledSizeAndOrigin(&usize, &vsize, lowerLeft, lowerLeft+1);
1101 ren->ComputeAspect();
1102 ren->GetAspect(aspect1);
1103 ren->vtkViewport::ComputeAspect();
1104 ren->vtkViewport::GetAspect(aspect2);
1105 double aspectModification = (aspect1[0] * aspect2[1]) /
1106 (aspect1[1] * aspect2[0]);
1107 double aspect = aspectModification * usize / vsize;
1108
1109 // Get the mvp (mcdc) matrix
1110 double mvp[16];
1111 mat = cam->GetCompositeProjectionTransformMatrix(aspect, -1, 1);
1112 vtkMatrix4x4::DeepCopy(mvp, mat);
1113
1114 // Apply the actor's matrix:
1115 vtkMatrix4x4::DeepCopy(this->ActorMatrix.GetData(), act->GetMatrix());
1116 vtkMatrix4x4::Multiply4x4(mvp, this->ActorMatrix.GetData(),
1117 this->AMVP.GetData());
1118
1119 vtkMatrix4x4::Invert(this->ActorMatrix.GetData(),
1120 this->InverseActorMatrix.GetData());
1121
1122 if (vtkWindow *win = ren->GetVTKWindow())
1123 {
1124 int *size = win->GetSize();
1125 this->WindowSize[0] = size[0];
1126 this->WindowSize[1] = size[1];
1127
1128 size = ren->GetSize();
1129 this->ViewPortSize[0] = size[0];
1130 this->ViewPortSize[1] = size[1];
1131
1132 ren->GetViewport(this->ViewPort.GetData());
1133
1134 double *tvport = win->GetTileViewport();
1135 this->NormalizedViewPort[0] = std::max(this->ViewPort[0], tvport[0]);
1136 this->NormalizedViewPort[1] = std::max(this->ViewPort[1], tvport[1]);
1137 this->NormalizedViewPort[2] = std::min(this->ViewPort[2], tvport[2]);
1138 this->NormalizedViewPort[3] = std::min(this->ViewPort[3], tvport[3]);
1139
1140 this->ViewportBounds[0] = this->ViewPort[0] * this->WindowSize[0];
1141 this->ViewportBounds[1] = this->ViewPort[2] * this->WindowSize[0];
1142 this->ViewportBounds[2] = this->ViewPort[1] * this->WindowSize[1];
1143 this->ViewportBounds[3] = this->ViewPort[3] * this->WindowSize[1];
1144
1145 this->DisplayOffset[0] = static_cast<double>(this->ViewportBounds[0]) + 0.5;
1146 this->DisplayOffset[1] = static_cast<double>(this->ViewportBounds[2]) + 0.5;
1147 }
1148 else
1149 {
1150 vtkGenericWarningMacro(<<"No render window present.");
1151 return false;
1152 }
1153
1154 return true;
1155 }
1156
1157 //------------------------------------------------------------------------------
LineCanBeLabeled(vtkPoints * points,vtkIdType numIds,const vtkIdType * ids,const LabelMetric & metrics)1158 bool vtkLabeledContourMapper::Private::LineCanBeLabeled(
1159 vtkPoints *points, vtkIdType numIds, const vtkIdType *ids,
1160 const LabelMetric &metrics)
1161 {
1162 vtkTuple<int, 4> bbox(0);
1163 vtkVector3d actorCoord;
1164 vtkVector2i displayCoord;
1165 if (numIds > 0)
1166 {
1167 do
1168 {
1169 points->GetPoint(*(ids++), actorCoord.GetData());
1170 this->ActorToDisplay(actorCoord, displayCoord);
1171 --numIds;
1172 }
1173 while (numIds > 0 && !this->PixelIsVisible(displayCoord));
1174
1175 if (!this->PixelIsVisible(displayCoord))
1176 {
1177 // No visible points
1178 return false;
1179 }
1180
1181 bbox[0] = displayCoord.GetX();
1182 bbox[1] = displayCoord.GetX();
1183 bbox[2] = displayCoord.GetY();
1184 bbox[3] = displayCoord.GetY();
1185 }
1186 while (numIds-- > 0)
1187 {
1188 points->GetPoint(*(ids++), actorCoord.GetData());
1189 this->ActorToDisplay(actorCoord, displayCoord);
1190 if (this->PixelIsVisible(displayCoord))
1191 {
1192 bbox[0] = std::min(bbox[0], displayCoord.GetX());
1193 bbox[1] = std::max(bbox[1], displayCoord.GetX());
1194 bbox[2] = std::min(bbox[2], displayCoord.GetY());
1195 bbox[3] = std::max(bbox[3], displayCoord.GetY());
1196 }
1197 }
1198
1199 // Must be at least twice the label length in at least one direction:
1200 return (metrics.Dimensions[0] * 2 < bbox[1] - bbox[0] ||
1201 metrics.Dimensions[0] * 2 < bbox[3] - bbox[2]);
1202 }
1203
1204 //------------------------------------------------------------------------------
1205 template <typename ScalarType>
PixelIsVisible(const vtkVector2<ScalarType> & dispCoord) const1206 bool vtkLabeledContourMapper::Private::PixelIsVisible(
1207 const vtkVector2<ScalarType> &dispCoord) const
1208 {
1209 return (dispCoord.GetX() >= this->ViewportBounds[0] &&
1210 dispCoord.GetX() <= this->ViewportBounds[1] &&
1211 dispCoord.GetY() >= this->ViewportBounds[2] &&
1212 dispCoord.GetY() <= this->ViewportBounds[3]);
1213 }
1214
1215 //------------------------------------------------------------------------------
NextLabel(vtkPoints * points,vtkIdType & numIds,vtkIdType * & ids,const LabelMetric & metrics,LabelInfo & info,double targetSmoothness,double skipDistance)1216 bool vtkLabeledContourMapper::Private::NextLabel(
1217 vtkPoints *points, vtkIdType &numIds, vtkIdType *&ids,
1218 const LabelMetric &metrics, LabelInfo &info, double targetSmoothness,
1219 double skipDistance)
1220 {
1221 if (numIds < 3)
1222 {
1223 return false;
1224 }
1225
1226 // First point in this call to NextLabel (index into ids).
1227 vtkIdType firstIdx = 0;
1228 vtkVector3d firstPoint;
1229 vtkVector2d firstPointDisplay;
1230 points->GetPoint(ids[firstIdx], firstPoint.GetData());
1231 this->ActorToDisplay(firstPoint, firstPointDisplay);
1232
1233 // Start of current smooth run (index into ids).
1234 vtkIdType startIdx = 0;
1235 vtkVector3d startPoint;
1236 vtkVector2d startPointDisplay;
1237 points->GetPoint(ids[startIdx], startPoint.GetData());
1238 this->ActorToDisplay(startPoint, startPointDisplay);
1239
1240 // Accumulated length of segments since startId
1241 std::vector<double> segmentLengths;
1242 double rAccum = 0.;
1243
1244 // Straight-line distance from start --> previous
1245 double rPrevStraight = 0.;
1246
1247 // Straight-line distance from start --> current
1248 double rStraight = 0.;
1249
1250 // Straight-line distance from prev --> current
1251 double rSegment = 0.;
1252
1253 // Minimum length of a smooth segment in display space
1254 const double minLength = 1.2 * metrics.Dimensions[0];
1255
1256 // Vector of segment prev --> current
1257 vtkVector2d segment(0, 0);
1258
1259 // Vector of segment start --> current
1260 vtkVector2d prevStraight(0, 0);
1261 vtkVector2d straight(0, 0);
1262
1263 // Smoothness of start --> current
1264 double smoothness = 0;
1265
1266 // Account for skip distance:
1267 while (segment.Norm() < skipDistance)
1268 {
1269 ++startIdx;
1270 points->GetPoint(ids[startIdx], startPoint.GetData());
1271 this->ActorToDisplay(startPoint, startPointDisplay);
1272
1273 segment = startPointDisplay - firstPointDisplay;
1274 }
1275
1276 // Find the first visible point
1277 while (startIdx + 1 < numIds && !this->PixelIsVisible(startPointDisplay))
1278 {
1279 ++startIdx;
1280 points->GetPoint(ids[startIdx], startPoint.GetData());
1281 this->ActorToDisplay(startPoint, startPointDisplay);
1282 }
1283
1284 // Start point in current segment.
1285 vtkVector3d prevPoint = startPoint;
1286 vtkVector2d prevPointDisplay = startPointDisplay;
1287
1288 // End point of current segment (index into ids).
1289 vtkIdType curIdx = startIdx + 1;
1290 vtkVector3d curPoint = prevPoint;
1291 vtkVector2d curPointDisplay = prevPointDisplay;
1292
1293 while (curIdx < numIds)
1294 {
1295 // Copy cur --> prev
1296 prevPoint = curPoint;
1297 prevPointDisplay = curPointDisplay;
1298 prevStraight = straight;
1299 rPrevStraight = rStraight;
1300
1301 // Update current:
1302 points->GetPoint(ids[curIdx], curPoint.GetData());
1303 this->ActorToDisplay(curPoint, curPointDisplay);
1304
1305 // Calculate lengths and smoothness.
1306 segment = curPointDisplay - prevPointDisplay;
1307 straight = curPointDisplay - startPointDisplay;
1308 rSegment = segment.Norm();
1309 rStraight = straight.Norm();
1310 segmentLengths.push_back(rSegment);
1311 rAccum += rSegment;
1312 smoothness = calculateSmoothness(rAccum, rStraight);
1313
1314 // Are we still dealing with a reasonably smooth line?
1315 // The first check tests if we've traveled far enough to get a fair estimate
1316 // of smoothness.
1317 if (rAccum < 10. || smoothness <= targetSmoothness)
1318 {
1319 // Advance to the next point:
1320 ++curIdx;
1321 continue;
1322 }
1323 else
1324 {
1325 // The line is no longer smooth "enough". Was start --> previous long
1326 // enough (twice label width)?
1327 if (rPrevStraight >= minLength)
1328 {
1329 // We have a winner!
1330 break;
1331 }
1332 else
1333 {
1334 // This startIdx won't work. On to the next visible startIdx.
1335 do
1336 {
1337 ++startIdx;
1338 points->GetPoint(ids[startIdx], startPoint.GetData());
1339 this->ActorToDisplay(startPoint, startPointDisplay);
1340 }
1341 while (startIdx < numIds && !this->PixelIsVisible(startPointDisplay));
1342
1343 prevPoint = startPoint;
1344 prevPointDisplay = startPointDisplay;
1345 curPoint = startPoint;
1346 curPointDisplay = startPointDisplay;
1347 curIdx = startIdx + 1;
1348 rAccum = 0.;
1349 rPrevStraight = 0.;
1350 segmentLengths.clear();
1351 continue;
1352 }
1353 }
1354 }
1355
1356 // Was the last segment ok?
1357 if (rPrevStraight >= minLength)
1358 {
1359 // The final index of the segment:
1360 vtkIdType endIdx = curIdx - 1;
1361
1362 // The direction of the text.
1363 vtkVector3d prevPointWorld;
1364 vtkVector3d startPointWorld;
1365 this->ActorToWorld(prevPoint, prevPointWorld);
1366 this->ActorToWorld(startPoint, startPointWorld);
1367 info.RightW = (prevPointWorld - startPointWorld).Normalized();
1368 // Ensure the text reads left->right:
1369 if (info.RightW.Dot(this->CameraRight) < 0.)
1370 {
1371 info.RightW = -info.RightW;
1372 }
1373
1374 // The up vector. Cross the forward direction with the orientation and
1375 // ensure that the result vector is in the same hemisphere as CameraUp
1376 info.UpW = info.RightW.Compare(this->CameraForward, 10e-10)
1377 ? this->CameraUp
1378 : info.RightW.Cross(this->CameraForward).Normalized();
1379 if (info.UpW.Dot(this->CameraUp) < 0.)
1380 {
1381 info.UpW = -info.UpW;
1382 }
1383
1384 // Walk through the segment lengths to find where the center is for label
1385 // placement:
1386 double targetLength = rPrevStraight * 0.5;
1387 rAccum = 0.;
1388 size_t endIdxOffset = 1;
1389 for (; endIdxOffset <= segmentLengths.size(); ++endIdxOffset)
1390 {
1391 rSegment = segmentLengths[endIdxOffset - 1];
1392 double tmp = rAccum + rSegment;
1393 if (tmp > targetLength)
1394 {
1395 break;
1396 }
1397 rAccum = tmp;
1398 }
1399 targetLength -= rAccum;
1400 points->GetPoint(ids[startIdx + endIdxOffset - 1], prevPoint.GetData());
1401 points->GetPoint(ids[startIdx + endIdxOffset], curPoint.GetData());
1402 vtkVector3d offset = curPoint - prevPoint;
1403 double rSegmentActor = offset.Normalize();
1404 offset = offset * (targetLength * rSegmentActor / rSegment);
1405 info.Position = prevPoint + offset;
1406
1407 this->ComputeLabelInfo(info, metrics);
1408
1409 // Update the cell array:
1410 ids += endIdx;
1411 numIds -= endIdx;
1412
1413 return true;
1414 }
1415
1416 return false;
1417 }
1418
1419 //------------------------------------------------------------------------------
BuildLabel(vtkTextActor3D * actor,const LabelMetric & metric,const LabelInfo & info)1420 bool vtkLabeledContourMapper::Private::BuildLabel(vtkTextActor3D *actor,
1421 const LabelMetric &metric,
1422 const LabelInfo &info)
1423
1424 {
1425 assert(metric.Valid);
1426 actor->SetInput(metric.Text.c_str());
1427 actor->SetTextProperty(metric.TProp);
1428 actor->SetPosition(const_cast<double*>(info.Position.GetData()));
1429
1430 vtkNew<vtkTransform> xform;
1431 xform->PostMultiply();
1432
1433 xform->Translate((-info.Position).GetData());
1434
1435 xform->Scale(info.ScaleDisplayToActor,
1436 info.ScaleDisplayToActor,
1437 info.ScaleDisplayToActor);
1438
1439 vtkVector3d forward = info.UpA.Cross(info.RightA);
1440 double rot[16];
1441 rot[4 * 0 + 0] = info.RightA[0];
1442 rot[4 * 1 + 0] = info.RightA[1];
1443 rot[4 * 2 + 0] = info.RightA[2];
1444 rot[4 * 3 + 0] = 0;
1445 rot[4 * 0 + 1] = info.UpA[0];
1446 rot[4 * 1 + 1] = info.UpA[1];
1447 rot[4 * 2 + 1] = info.UpA[2];
1448 rot[4 * 3 + 1] = 0;
1449 rot[4 * 0 + 2] = forward[0];
1450 rot[4 * 1 + 2] = forward[1];
1451 rot[4 * 2 + 2] = forward[2];
1452 rot[4 * 3 + 2] = 0;
1453 rot[4 * 0 + 3] = 0;
1454 rot[4 * 1 + 3] = 0;
1455 rot[4 * 2 + 3] = 0;
1456 rot[4 * 3 + 3] = 1;
1457 xform->Concatenate(rot);
1458
1459 xform->Translate(info.Position.GetData());
1460 actor->SetUserTransform(xform);
1461
1462 return true;
1463 }
1464
1465 //------------------------------------------------------------------------------
ComputeLabelInfo(LabelInfo & info,const LabelMetric & metrics)1466 void vtkLabeledContourMapper::Private::ComputeLabelInfo(
1467 LabelInfo &info, const LabelMetric &metrics)
1468 {
1469 // Convert the right and up vectors into actor space:
1470 vtkVector3d worldPosition;
1471 this->ActorToWorld(info.Position, worldPosition);
1472
1473 vtkVector3d endW = worldPosition + info.RightW;
1474 vtkVector3d endA;
1475 this->WorldToActor(endW, endA);
1476 info.RightA = endA - info.Position;
1477
1478 endW = worldPosition + info.UpW;
1479 this->WorldToActor(endW, endA);
1480 info.UpA = endA - info.Position;
1481
1482 // Compute scaling factor. Use the Up vector for deltas as we know it is
1483 // perpendicular to the view axis:
1484 vtkVector3d delta = info.UpA * (0.5 * metrics.Dimensions[0]);
1485 vtkVector3d leftActor = info.Position - delta;
1486 vtkVector3d rightActor = info.Position + delta;
1487 vtkVector2d leftDisplay;
1488 vtkVector2d rightDisplay;
1489 this->ActorToDisplay(leftActor, leftDisplay);
1490 this->ActorToDisplay(rightActor, rightDisplay);
1491 info.ScaleDisplayToActor = static_cast<double>(metrics.Dimensions[0]) /
1492 (rightDisplay - leftDisplay).Norm();
1493
1494 // Compute the corners of the quad. Actor coordinates are used to create the
1495 // stencil, display coordinates are used to detect collisions.
1496 // Note that we make this a little bigger (4px) than a tight bbox to give a
1497 // little breathing room around the text.
1498 vtkVector3d halfWidth =
1499 ((0.5 * metrics.Dimensions[0] + 2) * info.ScaleDisplayToActor)
1500 * info.RightA;
1501 vtkVector3d halfHeight =
1502 ((0.5 * metrics.Dimensions[1] + 2) * info.ScaleDisplayToActor)
1503 * info.UpA;
1504 info.TLa = info.Position + halfHeight - halfWidth;
1505 info.TRa = info.Position + halfHeight + halfWidth;
1506 info.BRa = info.Position - halfHeight + halfWidth;
1507 info.BLa = info.Position - halfHeight - halfWidth;
1508 this->ActorToDisplay(info.TLa, info.TLd);
1509 this->ActorToDisplay(info.TRa, info.TRd);
1510 this->ActorToDisplay(info.BRa, info.BRd);
1511 this->ActorToDisplay(info.BLa, info.BLd);
1512 }
1513
1514 // Anonymous namespace for some TestOverlap helpers:
1515 namespace {
1516
1517 // Rotates the vector by -90 degrees.
perp(vtkVector2i & vec)1518 void perp(vtkVector2i &vec)
1519 {
1520 std::swap(vec[0], vec[1]);
1521 vec[1] = -vec[1];
1522 }
1523
1524 // Project all points in other onto the line (point + t * direction).
1525 // Return true if t is positive for all points in other (e.g. all points in
1526 // 'other' are outside the polygon containing 'point').
allOutside(const vtkVector2i & point,const vtkVector2i & direction,const LabelInfo & other)1527 bool allOutside(const vtkVector2i &point, const vtkVector2i &direction,
1528 const LabelInfo &other)
1529 {
1530 vtkVector2i testVector;
1531
1532 testVector = other.TLd - point;
1533 if (direction.Dot(testVector) <= 0)
1534 {
1535 return false;
1536 }
1537
1538 testVector = other.TRd - point;
1539 if (direction.Dot(testVector) <= 0)
1540 {
1541 return false;
1542 }
1543
1544 testVector = other.BRd - point;
1545 if (direction.Dot(testVector) <= 0)
1546 {
1547 return false;
1548 }
1549
1550 testVector = other.BLd - point;
1551 if (direction.Dot(testVector) <= 0)
1552 {
1553 return false;
1554 }
1555
1556 return true;
1557 }
1558
1559 // Generate a vector pointing out from each edge of the rectangle. Do this
1560 // by traversing the corners counter-clockwise and using the perp() function.
1561 // Use allOutside() to determine whether the other polygon is outside the edge.
1562 // Return true if the axis separates the polygons.
testAxis(const LabelInfo & poly,const vtkVector2i & edgeStart,const vtkVector2i & edgeEnd)1563 bool testAxis(const LabelInfo &poly, const vtkVector2i &edgeStart,
1564 const vtkVector2i &edgeEnd)
1565 {
1566 // Vector pointing out of polygon:
1567 vtkVector2i direction = edgeEnd - edgeStart;
1568 perp(direction);
1569
1570 return allOutside(edgeStart, direction, poly);
1571 }
1572
1573 } // end anon namespace
1574
1575 //------------------------------------------------------------------------------
1576 // Implements axis separation method for detecting polygon intersection.
1577 // Ref: http://www.geometrictools.com/Documentation/MethodOfSeparatingAxes.pdf
1578 // In essence, look for an axis that separates the two rectangles.
1579 // Return true if overlap occurs.
TestOverlap(const LabelInfo & a,const LabelInfo & b)1580 bool vtkLabeledContourMapper::Private::TestOverlap(const LabelInfo &a,
1581 const LabelInfo &b)
1582 {
1583 // Note that the order of the points matters, must be CCW to get the correct
1584 // perpendicular vector:
1585 return !(testAxis(a, b.TLd, b.BLd) ||
1586 testAxis(a, b.BLd, b.BRd) ||
1587 testAxis(a, b.BRd, b.TRd) ||
1588 testAxis(a, b.TRd, b.TLd) ||
1589 testAxis(b, a.TLd, a.BLd) ||
1590 testAxis(b, a.BLd, a.BRd) ||
1591 testAxis(b, a.BRd, a.TRd) ||
1592 testAxis(b, a.TRd, a.TLd));
1593 }
1594