ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
cvSelectionAnnotation.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 // LOCAL
11 #include "PclUtils/PCLVis.h"
13 
14 // CV_CORE_LIB
15 #include <CVLog.h>
16 
17 // VTK
18 #include <vtkActor.h>
19 #include <vtkCell.h>
20 #include <vtkCoordinate.h>
21 #include <vtkPolyData.h>
22 #include <vtkProperty.h>
23 #include <vtkRenderWindow.h>
24 #include <vtkRenderer.h>
25 #include <vtkSmartPointer.h>
26 #include <vtkTextActor.h>
27 #include <vtkTextProperty.h>
28 
29 // Qt
30 #include <QDateTime>
31 #include <QFile>
32 #include <QJsonArray>
33 #include <QJsonDocument>
34 #include <QJsonObject>
35 
36 //-----------------------------------------------------------------------------
38  : QObject(parent), m_viewer(nullptr) {
39  CVLog::PrintVerbose("[cvSelectionAnnotationManager] Initialized");
40 }
41 
42 //-----------------------------------------------------------------------------
44 
45 //-----------------------------------------------------------------------------
47  m_viewer = viewer;
48 }
49 
50 //-----------------------------------------------------------------------------
52  const cvSelectionData& selection,
53  const QString& text,
54  bool autoPosition) {
55  if (selection.isEmpty()) {
57  "[cvSelectionAnnotationManager] Cannot annotate empty "
58  "selection");
59  return QString();
60  }
61 
62  // Generate unique ID
63  QString id =
64  QString("annotation_%1").arg(QDateTime::currentMSecsSinceEpoch());
65 
66  // Create annotation with default font properties (cell label by default)
67  cvAnnotation annotation;
68  annotation.id = id;
69  annotation.text = text;
70  annotation.followSelection = autoPosition;
71 
72  // Apply default cell label font properties
73  annotation.fontFamily = m_defaultCellLabelProps.cellLabelFontFamily;
74  annotation.fontSize = m_defaultCellLabelProps.cellLabelFontSize;
75  annotation.color = m_defaultCellLabelProps.cellLabelColor;
76  annotation.opacity = m_defaultCellLabelProps.cellLabelOpacity;
77  annotation.bold = m_defaultCellLabelProps.cellLabelBold;
78  annotation.italic = m_defaultCellLabelProps.cellLabelItalic;
79  annotation.shadow = m_defaultCellLabelProps.cellLabelShadow;
80  annotation.horizontalJustification =
81  m_defaultCellLabelProps.cellLabelHorizontalJustification;
82  annotation.verticalJustification =
83  m_defaultCellLabelProps.cellLabelVerticalJustification;
84 
85  if (autoPosition && m_viewer) {
86  // Compute selection center from polyData (ParaView-style)
87  // Reference: ParaView's vtkSMSelectionHelper::ComputeSelectionBounds
88  double* center = computeSelectionCenter(selection, nullptr);
89 
90  if (center) {
91  annotation.position[0] = center[0];
92  annotation.position[1] = center[1];
93  annotation.position[2] = center[2];
94 
96  QString("[cvSelectionAnnotationManager] Annotation "
97  "position: [%1, %2, %3]")
98  .arg(center[0])
99  .arg(center[1])
100  .arg(center[2]));
101  delete[] center;
102  } else {
103  // Fallback to origin
104  annotation.position[0] = 0.0;
105  annotation.position[1] = 0.0;
106  annotation.position[2] = 0.0;
108  "[cvSelectionAnnotationManager] Using origin for "
109  "annotation (no polyData available)");
110  }
111  }
112 
113  m_annotations[id] = annotation;
114  createTextActor(annotation);
115 
116  emit annotationAdded(id);
117  emit annotationsChanged();
118 
119  CVLog::Print(QString("[cvSelectionAnnotationManager] Added annotation: %1")
120  .arg(id));
121 
122  return id;
123 }
124 
125 //-----------------------------------------------------------------------------
127  const double position[3],
128  const QString& id) {
129  QString annotationId =
130  id.isEmpty() ? QString("annotation_%1")
131  .arg(QDateTime::currentMSecsSinceEpoch())
132  : id;
133 
134  cvAnnotation annotation(text, position, annotationId);
135 
136  // Apply default cell label font properties
137  annotation.fontFamily = m_defaultCellLabelProps.cellLabelFontFamily;
138  annotation.fontSize = m_defaultCellLabelProps.cellLabelFontSize;
139  annotation.color = m_defaultCellLabelProps.cellLabelColor;
140  annotation.opacity = m_defaultCellLabelProps.cellLabelOpacity;
141  annotation.bold = m_defaultCellLabelProps.cellLabelBold;
142  annotation.italic = m_defaultCellLabelProps.cellLabelItalic;
143  annotation.shadow = m_defaultCellLabelProps.cellLabelShadow;
144  annotation.horizontalJustification =
145  m_defaultCellLabelProps.cellLabelHorizontalJustification;
146  annotation.verticalJustification =
147  m_defaultCellLabelProps.cellLabelVerticalJustification;
148 
149  m_annotations[annotationId] = annotation;
150  createTextActor(annotation);
151 
152  emit annotationAdded(annotationId);
153  emit annotationsChanged();
154 
155  CVLog::Print(QString("[cvSelectionAnnotationManager] Added annotation at "
156  "(%1, %2, %3): %4")
157  .arg(position[0])
158  .arg(position[1])
159  .arg(position[2])
160  .arg(annotationId));
161 
162  return annotationId;
163 }
164 
165 //-----------------------------------------------------------------------------
167  if (!m_annotations.contains(id)) {
168  CVLog::Warning(QString("[cvSelectionAnnotationManager] Annotation not "
169  "found: %1")
170  .arg(id));
171  return false;
172  }
173 
174  removeTextActor(id);
175  m_annotations.remove(id);
176 
177  emit annotationRemoved(id);
178  emit annotationsChanged();
179 
180  CVLog::Print(
181  QString("[cvSelectionAnnotationManager] Removed annotation: %1")
182  .arg(id));
183 
184  return true;
185 }
186 
187 //-----------------------------------------------------------------------------
189  const QString& text) {
190  if (!m_annotations.contains(id)) {
191  return false;
192  }
193 
194  m_annotations[id].text = text;
195  updateTextActor(id);
196 
197  emit annotationUpdated(id);
198  emit annotationsChanged();
199 
200  return true;
201 }
202 
203 //-----------------------------------------------------------------------------
205  const QString& id, const double position[3]) {
206  if (!m_annotations.contains(id)) {
207  return false;
208  }
209 
210  m_annotations[id].position[0] = position[0];
211  m_annotations[id].position[1] = position[1];
212  m_annotations[id].position[2] = position[2];
213  updateTextActor(id);
214 
215  emit annotationUpdated(id);
216 
217  return true;
218 }
219 
220 //-----------------------------------------------------------------------------
222  bool visible) {
223  if (!m_annotations.contains(id)) {
224  return false;
225  }
226 
227  m_annotations[id].visible = visible;
228  updateTextActor(id);
229 
230  emit annotationUpdated(id);
231 
232  return true;
233 }
234 
235 //-----------------------------------------------------------------------------
237  const QColor& color) {
238  if (!m_annotations.contains(id)) {
239  return false;
240  }
241 
242  m_annotations[id].color = color;
243  updateTextActor(id);
244 
245  emit annotationUpdated(id);
246 
247  return true;
248 }
249 
250 //-----------------------------------------------------------------------------
252  int fontSize) {
253  if (!m_annotations.contains(id)) {
254  return false;
255  }
256 
257  m_annotations[id].fontSize = fontSize;
258  updateTextActor(id);
259 
260  emit annotationUpdated(id);
261 
262  return true;
263 }
264 
265 //-----------------------------------------------------------------------------
268  bool isCellLabel) {
269  // Apply font properties to all annotations
270  for (auto& annotation : m_annotations) {
271  if (isCellLabel) {
272  annotation.fontFamily = props.cellLabelFontFamily;
273  annotation.fontSize = props.cellLabelFontSize;
274  annotation.color = props.cellLabelColor;
275  annotation.opacity = props.cellLabelOpacity;
276  annotation.bold = props.cellLabelBold;
277  annotation.italic = props.cellLabelItalic;
278  annotation.shadow = props.cellLabelShadow;
279  annotation.horizontalJustification =
281  annotation.verticalJustification =
283  } else {
284  annotation.fontFamily = props.pointLabelFontFamily;
285  annotation.fontSize = props.pointLabelFontSize;
286  annotation.color = props.pointLabelColor;
287  annotation.opacity = props.pointLabelOpacity;
288  annotation.bold = props.pointLabelBold;
289  annotation.italic = props.pointLabelItalic;
290  annotation.shadow = props.pointLabelShadow;
291  annotation.horizontalJustification =
293  annotation.verticalJustification =
295  }
296 
297  // Update the text actor immediately to apply changes
298  updateTextActor(annotation.id);
299  }
300 
301  emit annotationsChanged();
302 }
303 
304 //-----------------------------------------------------------------------------
307  bool isCellLabel) {
308  if (isCellLabel) {
309  m_defaultCellLabelProps = props;
310  } else {
311  m_defaultPointLabelProps = props;
312  }
313 
315  QString("[cvSelectionAnnotationManager] Set default %1 "
316  "label properties: family=%2, size=%3, color=(%4,%5,%6)")
317  .arg(isCellLabel ? "cell" : "point")
318  .arg(props.cellLabelFontFamily)
319  .arg(props.cellLabelFontSize)
320  .arg(props.cellLabelColor.red())
321  .arg(props.cellLabelColor.green())
322  .arg(props.cellLabelColor.blue()));
323 }
324 
325 //-----------------------------------------------------------------------------
327  const QString& id) const {
328  return m_annotations.value(id, cvAnnotation());
329 }
330 
331 //-----------------------------------------------------------------------------
332 QList<cvAnnotation> cvSelectionAnnotationManager::allAnnotations() const {
333  return m_annotations.values();
334 }
335 
336 //-----------------------------------------------------------------------------
338  return m_annotations.keys();
339 }
340 
341 //-----------------------------------------------------------------------------
343  // Remove all actors
344  for (const QString& id : m_annotations.keys()) {
345  removeTextActor(id);
346  }
347 
348  m_annotations.clear();
349  m_textActors.clear();
350 
351  emit annotationsChanged();
352 
353  CVLog::Print("[cvSelectionAnnotationManager] All annotations cleared");
354 }
355 
356 //-----------------------------------------------------------------------------
358  for (const QString& id : m_annotations.keys()) {
359  m_annotations[id].visible = true;
360  updateTextActor(id);
361  }
362 
363  emit annotationsChanged();
364 }
365 
366 //-----------------------------------------------------------------------------
368  for (const QString& id : m_annotations.keys()) {
369  m_annotations[id].visible = false;
370  updateTextActor(id);
371  }
372 
373  emit annotationsChanged();
374 }
375 
376 //-----------------------------------------------------------------------------
378  if (filename.isEmpty()) {
379  CVLog::Error("[cvSelectionAnnotationManager] Invalid filename");
380  return false;
381  }
382 
383  QJsonArray annotationsArray;
384 
385  for (const cvAnnotation& annotation : m_annotations.values()) {
386  QJsonObject annotationObj;
387  annotationObj["id"] = annotation.id;
388  annotationObj["text"] = annotation.text;
389  annotationObj["x"] = annotation.position[0];
390  annotationObj["y"] = annotation.position[1];
391  annotationObj["z"] = annotation.position[2];
392  annotationObj["colorR"] = annotation.color.red();
393  annotationObj["colorG"] = annotation.color.green();
394  annotationObj["colorB"] = annotation.color.blue();
395  annotationObj["fontSize"] = annotation.fontSize;
396  annotationObj["fontFamily"] = annotation.fontFamily;
397  annotationObj["bold"] = annotation.bold;
398  annotationObj["italic"] = annotation.italic;
399  annotationObj["shadow"] = annotation.shadow;
400  annotationObj["opacity"] = annotation.opacity;
401  annotationObj["horizontalJustification"] =
402  annotation.horizontalJustification;
403  annotationObj["verticalJustification"] =
404  annotation.verticalJustification;
405  annotationObj["visible"] = annotation.visible;
406  annotationObj["followSelection"] = annotation.followSelection;
407  annotationObj["timestamp"] = annotation.timestamp;
408 
409  annotationsArray.append(annotationObj);
410  }
411 
412  QJsonObject root;
413  root["version"] = 1;
414  root["count"] = m_annotations.size();
415  root["annotations"] = annotationsArray;
416 
417  QJsonDocument doc(root);
418 
419  QFile file(filename);
420  if (!file.open(QIODevice::WriteOnly)) {
421  CVLog::Error(QString("[cvSelectionAnnotationManager] Failed to open "
422  "file: %1")
423  .arg(filename));
424  return false;
425  }
426 
427  file.write(doc.toJson(QJsonDocument::Indented));
428  file.close();
429 
430  CVLog::Print(QString("[cvSelectionAnnotationManager] Exported %1 "
431  "annotations to: %2")
432  .arg(m_annotations.size())
433  .arg(filename));
434 
435  return true;
436 }
437 
438 //-----------------------------------------------------------------------------
440  bool merge) {
441  if (filename.isEmpty()) {
442  CVLog::Error("[cvSelectionAnnotationManager] Invalid filename");
443  return false;
444  }
445 
446  QFile file(filename);
447  if (!file.open(QIODevice::ReadOnly)) {
448  CVLog::Error(QString("[cvSelectionAnnotationManager] Failed to open "
449  "file: %1")
450  .arg(filename));
451  return false;
452  }
453 
454  QByteArray data = file.readAll();
455  file.close();
456 
457  QJsonDocument doc = QJsonDocument::fromJson(data);
458  if (doc.isNull() || !doc.isObject()) {
459  CVLog::Error("[cvSelectionAnnotationManager] Invalid JSON format");
460  return false;
461  }
462 
463  QJsonObject root = doc.object();
464 
465  if (!merge) {
466  clearAll();
467  }
468 
469  QJsonArray annotationsArray = root["annotations"].toArray();
470  int imported = 0;
471 
472  for (const QJsonValue& value : annotationsArray) {
473  QJsonObject annotationObj = value.toObject();
474 
475  QString id = annotationObj["id"].toString();
476  QString text = annotationObj["text"].toString();
477  double position[3] = {annotationObj["x"].toDouble(),
478  annotationObj["y"].toDouble(),
479  annotationObj["z"].toDouble()};
480 
481  // Skip if already exists and merging
482  if (merge && m_annotations.contains(id)) {
483  continue;
484  }
485 
486  cvAnnotation annotation(text, position, id);
487  annotation.color = QColor(annotationObj["colorR"].toInt(),
488  annotationObj["colorG"].toInt(),
489  annotationObj["colorB"].toInt());
490  annotation.fontSize = annotationObj["fontSize"].toInt(12);
491  annotation.fontFamily = annotationObj["fontFamily"].toString("Arial");
492  annotation.bold = annotationObj["bold"].toBool(false);
493  annotation.italic = annotationObj["italic"].toBool(false);
494  annotation.shadow = annotationObj["shadow"].toBool(true);
495  annotation.opacity = annotationObj["opacity"].toDouble(1.0);
496  annotation.horizontalJustification =
497  annotationObj["horizontalJustification"].toString("Left");
498  annotation.verticalJustification =
499  annotationObj["verticalJustification"].toString("Bottom");
500  annotation.visible = annotationObj["visible"].toBool(true);
501  annotation.followSelection =
502  annotationObj["followSelection"].toBool(false);
503  annotation.timestamp =
504  annotationObj["timestamp"].toVariant().toLongLong();
505 
506  m_annotations[id] = annotation;
507  createTextActor(annotation);
508 
509  ++imported;
510  }
511 
512  emit annotationsChanged();
513 
514  CVLog::Print(QString("[cvSelectionAnnotationManager] Imported %1 "
515  "annotations from: %2")
516  .arg(imported)
517  .arg(filename));
518 
519  return true;
520 }
521 
522 //-----------------------------------------------------------------------------
523 // Private methods
524 //-----------------------------------------------------------------------------
525 
526 void cvSelectionAnnotationManager::createTextActor(
527  const cvAnnotation& annotation) {
528  if (!m_viewer) {
529  return;
530  }
531 
532  vtkRenderer* renderer = m_viewer->getCurrentRenderer();
533  if (!renderer) {
534  return;
535  }
536 
537  // Create text actor
538  vtkTextActor* textActor = vtkTextActor::New();
539  textActor->SetInput(annotation.text.toUtf8().constData());
540 
541  // Set text properties - apply ALL font properties from annotation
542  vtkTextProperty* textProp = textActor->GetTextProperty();
543  textProp->SetFontFamilyAsString(annotation.fontFamily.toUtf8().constData());
544  textProp->SetFontSize(annotation.fontSize);
545  textProp->SetColor(annotation.color.redF(), annotation.color.greenF(),
546  annotation.color.blueF());
547  textProp->SetBold(annotation.bold ? 1 : 0);
548  textProp->SetItalic(annotation.italic ? 1 : 0);
549  textProp->SetShadow(annotation.shadow ? 1 : 0);
550  textProp->SetOpacity(annotation.opacity);
551 
552  // Apply horizontal justification
553  if (annotation.horizontalJustification == "Left") {
554  textProp->SetJustificationToLeft();
555  } else if (annotation.horizontalJustification == "Center") {
556  textProp->SetJustificationToCentered();
557  } else if (annotation.horizontalJustification == "Right") {
558  textProp->SetJustificationToRight();
559  }
560 
561  // Apply vertical justification
562  if (annotation.verticalJustification == "Top") {
563  textProp->SetVerticalJustificationToTop();
564  } else if (annotation.verticalJustification == "Center") {
565  textProp->SetVerticalJustificationToCentered();
566  } else if (annotation.verticalJustification == "Bottom") {
567  textProp->SetVerticalJustificationToBottom();
568  }
569 
570  // Mark as modified to ensure VTK updates
571  textProp->Modified();
572 
573  // Convert 3D world position to 2D display coordinates (ParaView-style)
574  // Reference: ParaView's vtkSMTextWidgetRepresentationProxy
575  double displayPos[2] = {100, 100}; // Default fallback
576  if (worldToDisplay(annotation.position, displayPos)) {
577  textActor->SetPosition(displayPos[0], displayPos[1]);
578  CVLog::PrintVerbose(QString("[cvSelectionAnnotationManager] Annotation "
579  "positioned at display [%1, %2]")
580  .arg(displayPos[0])
581  .arg(displayPos[1]));
582  } else {
583  // Fallback: use default position
584  textActor->SetPosition(100, 100);
586  "[cvSelectionAnnotationManager] Using default "
587  "position for annotation");
588  }
589 
590  textActor->SetVisibility(annotation.visible);
591 
592  // Add to renderer
593  renderer->AddActor2D(textActor);
594  m_textActors[annotation.id] = textActor;
595 
596  // Render
597  vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
598  if (renderWindow) {
599  renderWindow->Render();
600  }
601 }
602 
603 //-----------------------------------------------------------------------------
604 void cvSelectionAnnotationManager::updateTextActor(const QString& id) {
605  if (!m_textActors.contains(id) || !m_annotations.contains(id)) {
606  return;
607  }
608 
609  vtkTextActor* textActor = m_textActors[id];
610  const cvAnnotation& annotation = m_annotations[id];
611 
612  // Update text
613  textActor->SetInput(annotation.text.toUtf8().constData());
614 
615  // Update properties - apply ALL font properties from annotation
616  vtkTextProperty* textProp = textActor->GetTextProperty();
617  textProp->SetFontFamilyAsString(annotation.fontFamily.toUtf8().constData());
618  textProp->SetFontSize(annotation.fontSize);
619  textProp->SetColor(annotation.color.redF(), annotation.color.greenF(),
620  annotation.color.blueF());
621  textProp->SetBold(annotation.bold ? 1 : 0);
622  textProp->SetItalic(annotation.italic ? 1 : 0);
623  textProp->SetShadow(annotation.shadow ? 1 : 0);
624  textProp->SetOpacity(annotation.opacity);
625 
626  // Apply horizontal justification
627  if (annotation.horizontalJustification == "Left") {
628  textProp->SetJustificationToLeft();
629  } else if (annotation.horizontalJustification == "Center") {
630  textProp->SetJustificationToCentered();
631  } else if (annotation.horizontalJustification == "Right") {
632  textProp->SetJustificationToRight();
633  }
634 
635  // Apply vertical justification
636  if (annotation.verticalJustification == "Top") {
637  textProp->SetVerticalJustificationToTop();
638  } else if (annotation.verticalJustification == "Center") {
639  textProp->SetVerticalJustificationToCentered();
640  } else if (annotation.verticalJustification == "Bottom") {
641  textProp->SetVerticalJustificationToBottom();
642  }
643 
644  // Mark as modified to ensure VTK updates
645  textProp->Modified();
646  textActor->Modified();
647 
648  // Update visibility
649  textActor->SetVisibility(annotation.visible);
650 
651  // Force render window update to ensure changes are visible immediately
652  if (m_viewer) {
653  vtkRenderer* renderer = m_viewer->getCurrentRenderer();
654  if (renderer) {
655  vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
656  if (renderWindow) {
657  renderWindow->Render();
658  }
659  }
660  }
661 }
662 
663 //-----------------------------------------------------------------------------
664 void cvSelectionAnnotationManager::removeTextActor(const QString& id) {
665  if (!m_textActors.contains(id)) {
666  return;
667  }
668 
669  vtkTextActor* textActor = m_textActors[id];
670 
671  if (m_viewer) {
672  vtkRenderer* renderer = m_viewer->getCurrentRenderer();
673  if (renderer) {
674  renderer->RemoveActor2D(textActor);
675 
676  vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
677  if (renderWindow) {
678  renderWindow->Render();
679  }
680  }
681  }
682 
683  // Smart pointer handles cleanup automatically
684  m_textActors.remove(id);
685 }
686 
687 //-----------------------------------------------------------------------------
688 double* cvSelectionAnnotationManager::computeSelectionCenter(
689  const cvSelectionData& selection, vtkPolyData* polyData) {
690  static double center[3] = {0.0, 0.0, 0.0};
691 
692  if (!polyData || selection.isEmpty()) {
693  return center;
694  }
695 
696  QVector<qint64> ids = selection.ids();
697  double sum[3] = {0.0, 0.0, 0.0};
698  int count = 0;
699 
700  if (selection.fieldAssociation() == cvSelectionData::POINTS) {
701  for (qint64 id : ids) {
702  if (id >= 0 && id < polyData->GetNumberOfPoints()) {
703  double pt[3];
704  polyData->GetPoint(id, pt);
705  sum[0] += pt[0];
706  sum[1] += pt[1];
707  sum[2] += pt[2];
708  ++count;
709  }
710  }
711  } else {
712  for (qint64 id : ids) {
713  if (id >= 0 && id < polyData->GetNumberOfCells()) {
714  vtkCell* cell = polyData->GetCell(id);
715  if (cell) {
716  vtkIdType npts = cell->GetNumberOfPoints();
717  for (vtkIdType i = 0; i < npts; ++i) {
718  double pt[3];
719  polyData->GetPoint(cell->GetPointId(i), pt);
720  sum[0] += pt[0];
721  sum[1] += pt[1];
722  sum[2] += pt[2];
723  ++count;
724  }
725  }
726  }
727  }
728  }
729 
730  if (count > 0) {
731  center[0] = sum[0] / count;
732  center[1] = sum[1] / count;
733  center[2] = sum[2] / count;
734  }
735 
736  return center;
737 }
738 
739 //-----------------------------------------------------------------------------
740 bool cvSelectionAnnotationManager::worldToDisplay(const double worldPos[3],
741  double displayPos[2]) {
742  if (!m_viewer) {
743  return false;
744  }
745 
746  vtkRenderer* renderer = m_viewer->getCurrentRenderer();
747  if (!renderer) {
748  return false;
749  }
750 
751  // Use vtkCoordinate to convert from world to display coordinates
752  // Reference: ParaView's vtkSMTextWidgetRepresentationProxy
753  vtkSmartPointer<vtkCoordinate> coordinate =
755  coordinate->SetCoordinateSystemToWorld();
756  coordinate->SetValue(worldPos[0], worldPos[1], worldPos[2]);
757 
758  // Get display position
759  int* displayCoords = coordinate->GetComputedDisplayValue(renderer);
760  if (displayCoords) {
761  displayPos[0] = static_cast<double>(displayCoords[0]);
762  displayPos[1] = static_cast<double>(displayCoords[1]);
763  return true;
764  }
765 
766  return false;
767 }
std::string filename
math::float4 color
math::float3 position
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool PrintVerbose(const char *format,...)
Prints out a verbose formatted message in console.
Definition: CVLog.cpp:103
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
vtkRenderer * getCurrentRenderer(int viewport=0)
Definition: PCLVis.cpp:2924
void setVisualizer(PclUtils::PCLVis *viewer)
Set the visualizer.
bool updateAnnotationPosition(const QString &id, const double position[3])
Update annotation position.
QStringList annotationIds() const
Get annotation IDs.
QList< cvAnnotation > allAnnotations() const
Get all annotations.
QString addAnnotationAt(const QString &text, const double position[3], const QString &id=QString())
Add annotation at specific position.
bool removeAnnotation(const QString &id)
Remove annotation.
void annotationsChanged()
Emitted when annotations change.
void hideAll()
Hide all annotations.
bool setAnnotationColor(const QString &id, const QColor &color)
Set annotation color.
void applyLabelProperties(const cvSelectionLabelPropertiesDialog::LabelProperties &props, bool isCellLabel)
Apply label properties to all annotations (for cell or point labels)
void clearAll()
Clear all annotations.
bool setAnnotationFontSize(const QString &id, int fontSize)
Set annotation font size.
bool importFromFile(const QString &filename, bool merge=true)
Import annotations from JSON file.
cvSelectionAnnotationManager(QObject *parent=nullptr)
cvAnnotation getAnnotation(const QString &id) const
Get annotation.
void annotationRemoved(const QString &id)
Emitted when annotation is removed.
int count() const
Get annotation count.
void annotationAdded(const QString &id)
Emitted when annotation is added.
void setDefaultLabelProperties(const cvSelectionLabelPropertiesDialog::LabelProperties &props, bool isCellLabel)
Set default label properties for new annotations.
bool setAnnotationVisible(const QString &id, bool visible)
Set annotation visibility.
bool updateAnnotationText(const QString &id, const QString &text)
Update annotation text.
void showAll()
Show all annotations.
bool exportToFile(const QString &filename) const
Export annotations to JSON file.
void annotationUpdated(const QString &id)
Emitted when annotation is updated.
QString addAnnotation(const cvSelectionData &selection, const QString &text, bool autoPosition=true)
Add annotation for a selection.
Encapsulates selection data without exposing VTK types.
FieldAssociation fieldAssociation() const
Get field association.
@ POINTS
Selection applies to points.
bool isEmpty() const
Check if selection is empty.
QVector< qint64 > ids() const
Get selected IDs as a vector (copy)
GraphType data
Definition: graph_cut.cc:138
Annotation for a selection.
QString fontFamily
Font family (e.g., "Arial")
QColor color
Text color.
QString id
Unique ID.
qint64 timestamp
Creation timestamp.
QString text
Annotation text.
double position[3]
3D position
int fontSize
Font size in points.
double opacity
Text opacity (0.0 to 1.0)
QString horizontalJustification
"Left", "Center", "Right"
bool visible
Visibility flag.
bool shadow
Shadow flag.
bool followSelection
Auto-update position with selection.
QString verticalJustification
"Top", "Center", "Bottom"
bool italic
Italic flag.
bool bold
Bold flag.