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>
33 #include <QJsonDocument>
34 #include <QJsonObject>
38 : QObject(parent), m_viewer(nullptr) {
57 "[cvSelectionAnnotationManager] Cannot annotate empty "
64 QString(
"annotation_%1").arg(QDateTime::currentMSecsSinceEpoch());
69 annotation.
text = text;
85 if (autoPosition && m_viewer) {
88 double* center = computeSelectionCenter(selection,
nullptr);
96 QString(
"[cvSelectionAnnotationManager] Annotation "
97 "position: [%1, %2, %3]")
108 "[cvSelectionAnnotationManager] Using origin for "
109 "annotation (no polyData available)");
113 m_annotations[id] = annotation;
114 createTextActor(annotation);
119 CVLog::Print(QString(
"[cvSelectionAnnotationManager] Added annotation: %1")
129 QString annotationId =
130 id.isEmpty() ? QString(
"annotation_%1")
131 .arg(QDateTime::currentMSecsSinceEpoch())
149 m_annotations[annotationId] = annotation;
150 createTextActor(annotation);
155 CVLog::Print(QString(
"[cvSelectionAnnotationManager] Added annotation at "
167 if (!m_annotations.contains(
id)) {
168 CVLog::Warning(QString(
"[cvSelectionAnnotationManager] Annotation not "
175 m_annotations.remove(
id);
181 QString(
"[cvSelectionAnnotationManager] Removed annotation: %1")
189 const QString& text) {
190 if (!m_annotations.contains(
id)) {
194 m_annotations[id].text = text;
205 const QString&
id,
const double position[3]) {
206 if (!m_annotations.contains(
id)) {
210 m_annotations[id].position[0] =
position[0];
211 m_annotations[id].position[1] =
position[1];
212 m_annotations[id].position[2] =
position[2];
223 if (!m_annotations.contains(
id)) {
227 m_annotations[id].visible = visible;
237 const QColor&
color) {
238 if (!m_annotations.contains(
id)) {
242 m_annotations[id].color =
color;
253 if (!m_annotations.contains(
id)) {
257 m_annotations[id].fontSize = fontSize;
270 for (
auto& annotation : m_annotations) {
279 annotation.horizontalJustification =
281 annotation.verticalJustification =
291 annotation.horizontalJustification =
293 annotation.verticalJustification =
298 updateTextActor(annotation.id);
309 m_defaultCellLabelProps = props;
311 m_defaultPointLabelProps = props;
315 QString(
"[cvSelectionAnnotationManager] Set default %1 "
316 "label properties: family=%2, size=%3, color=(%4,%5,%6)")
317 .arg(isCellLabel ?
"cell" :
"point")
327 const QString&
id)
const {
333 return m_annotations.values();
338 return m_annotations.keys();
344 for (
const QString&
id : m_annotations.keys()) {
348 m_annotations.clear();
349 m_textActors.clear();
353 CVLog::Print(
"[cvSelectionAnnotationManager] All annotations cleared");
358 for (
const QString&
id : m_annotations.keys()) {
359 m_annotations[id].visible =
true;
368 for (
const QString&
id : m_annotations.keys()) {
369 m_annotations[id].visible =
false;
379 CVLog::Error(
"[cvSelectionAnnotationManager] Invalid filename");
383 QJsonArray annotationsArray;
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;
409 annotationsArray.append(annotationObj);
414 root[
"count"] = m_annotations.size();
415 root[
"annotations"] = annotationsArray;
417 QJsonDocument doc(root);
420 if (!file.open(QIODevice::WriteOnly)) {
421 CVLog::Error(QString(
"[cvSelectionAnnotationManager] Failed to open "
427 file.write(doc.toJson(QJsonDocument::Indented));
430 CVLog::Print(QString(
"[cvSelectionAnnotationManager] Exported %1 "
431 "annotations to: %2")
432 .arg(m_annotations.size())
442 CVLog::Error(
"[cvSelectionAnnotationManager] Invalid filename");
447 if (!file.open(QIODevice::ReadOnly)) {
448 CVLog::Error(QString(
"[cvSelectionAnnotationManager] Failed to open "
454 QByteArray
data = file.readAll();
457 QJsonDocument doc = QJsonDocument::fromJson(
data);
458 if (doc.isNull() || !doc.isObject()) {
459 CVLog::Error(
"[cvSelectionAnnotationManager] Invalid JSON format");
463 QJsonObject root = doc.object();
469 QJsonArray annotationsArray = root[
"annotations"].toArray();
472 for (
const QJsonValue& value : annotationsArray) {
473 QJsonObject annotationObj = value.toObject();
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()};
482 if (merge && m_annotations.contains(
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);
497 annotationObj[
"horizontalJustification"].toString(
"Left");
499 annotationObj[
"verticalJustification"].toString(
"Bottom");
500 annotation.
visible = annotationObj[
"visible"].toBool(
true);
502 annotationObj[
"followSelection"].toBool(
false);
504 annotationObj[
"timestamp"].toVariant().toLongLong();
506 m_annotations[id] = annotation;
507 createTextActor(annotation);
514 CVLog::Print(QString(
"[cvSelectionAnnotationManager] Imported %1 "
515 "annotations from: %2")
526 void cvSelectionAnnotationManager::createTextActor(
538 vtkTextActor* textActor = vtkTextActor::New();
539 textActor->SetInput(annotation.
text.toUtf8().constData());
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);
554 textProp->SetJustificationToLeft();
556 textProp->SetJustificationToCentered();
558 textProp->SetJustificationToRight();
563 textProp->SetVerticalJustificationToTop();
565 textProp->SetVerticalJustificationToCentered();
567 textProp->SetVerticalJustificationToBottom();
571 textProp->Modified();
575 double displayPos[2] = {100, 100};
576 if (worldToDisplay(annotation.
position, displayPos)) {
577 textActor->SetPosition(displayPos[0], displayPos[1]);
579 "positioned at display [%1, %2]")
581 .arg(displayPos[1]));
584 textActor->SetPosition(100, 100);
586 "[cvSelectionAnnotationManager] Using default "
587 "position for annotation");
590 textActor->SetVisibility(annotation.
visible);
593 renderer->AddActor2D(textActor);
594 m_textActors[annotation.
id] = textActor;
597 vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
599 renderWindow->Render();
604 void cvSelectionAnnotationManager::updateTextActor(
const QString&
id) {
605 if (!m_textActors.contains(
id) || !m_annotations.contains(
id)) {
609 vtkTextActor* textActor = m_textActors[id];
613 textActor->SetInput(annotation.
text.toUtf8().constData());
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);
628 textProp->SetJustificationToLeft();
630 textProp->SetJustificationToCentered();
632 textProp->SetJustificationToRight();
637 textProp->SetVerticalJustificationToTop();
639 textProp->SetVerticalJustificationToCentered();
641 textProp->SetVerticalJustificationToBottom();
645 textProp->Modified();
646 textActor->Modified();
649 textActor->SetVisibility(annotation.
visible);
655 vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
657 renderWindow->Render();
664 void cvSelectionAnnotationManager::removeTextActor(
const QString&
id) {
665 if (!m_textActors.contains(
id)) {
669 vtkTextActor* textActor = m_textActors[id];
674 renderer->RemoveActor2D(textActor);
676 vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
678 renderWindow->Render();
684 m_textActors.remove(
id);
688 double* cvSelectionAnnotationManager::computeSelectionCenter(
690 static double center[3] = {0.0, 0.0, 0.0};
692 if (!polyData || selection.
isEmpty()) {
696 QVector<qint64> ids = selection.
ids();
697 double sum[3] = {0.0, 0.0, 0.0};
701 for (qint64
id : ids) {
702 if (
id >= 0 && id < polyData->GetNumberOfPoints()) {
704 polyData->GetPoint(
id, pt);
712 for (qint64
id : ids) {
713 if (
id >= 0 && id < polyData->GetNumberOfCells()) {
714 vtkCell* cell = polyData->GetCell(
id);
716 vtkIdType npts = cell->GetNumberOfPoints();
717 for (vtkIdType i = 0; i < npts; ++i) {
719 polyData->GetPoint(cell->GetPointId(i), pt);
731 center[0] = sum[0] /
count;
732 center[1] = sum[1] /
count;
733 center[2] = sum[2] /
count;
740 bool cvSelectionAnnotationManager::worldToDisplay(
const double worldPos[3],
741 double displayPos[2]) {
755 coordinate->SetCoordinateSystemToWorld();
756 coordinate->SetValue(worldPos[0], worldPos[1], worldPos[2]);
759 int* displayCoords = coordinate->GetComputedDisplayValue(renderer);
761 displayPos[0] =
static_cast<double>(displayCoords[0]);
762 displayPos[1] =
static_cast<double>(displayCoords[1]);
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool Print(const char *format,...)
Prints out a formatted message in console.
static bool PrintVerbose(const char *format,...)
Prints out a verbose formatted message in console.
static bool Error(const char *format,...)
Display an error dialog with formatted message.
vtkRenderer * getCurrentRenderer(int viewport=0)
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() override
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)
Annotation for a selection.
QString fontFamily
Font family (e.g., "Arial")
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 followSelection
Auto-update position with selection.
QString verticalJustification
"Top", "Center", "Bottom"
Label properties structure.
QString cellLabelHorizontalJustification
QString pointLabelFontFamily
QString pointLabelVerticalJustification
QString pointLabelHorizontalJustification
QString cellLabelFontFamily
QString cellLabelVerticalJustification