20 #include <vtkAbstractArray.h>
22 #include <vtkActor2D.h>
23 #include <vtkCellCenters.h>
24 #include <vtkDataSet.h>
25 #include <vtkDataSetAttributes.h>
26 #include <vtkDataSetMapper.h>
27 #include <vtkExtractSelection.h>
28 #include <vtkIdTypeArray.h>
29 #include <vtkLabeledDataMapper.h>
30 #include <vtkMapper.h>
31 #include <vtkMaskPoints.h>
32 #include <vtkPolyData.h>
33 #include <vtkPolyDataMapper.h>
35 #include <vtkPropCollection.h>
36 #include <vtkProperty.h>
37 #include <vtkRenderWindow.h>
38 #include <vtkRenderer.h>
39 #include <vtkSelection.h>
40 #include <vtkSelectionNode.h>
41 #include <vtkTextProperty.h>
42 #include <vtkUnstructuredGrid.h>
49 m_preselectedOpacity(1.0),
50 m_selectedOpacity(1.0),
51 m_boundaryOpacity(1.0),
53 m_preselectedPointSize(5),
54 m_selectedPointSize(5),
55 m_boundaryPointSize(5),
57 m_preselectedLineWidth(2),
58 m_selectedLineWidth(2),
59 m_boundaryLineWidth(2),
61 m_pointLabelVisible(false),
62 m_cellLabelVisible(false) {
68 m_hoverColor[0] = 0.5;
69 m_hoverColor[1] = 0.0;
70 m_hoverColor[2] = 1.0;
74 m_preselectedColor[0] = 0.5;
75 m_preselectedColor[1] = 0.0;
76 m_preselectedColor[2] = 1.0;
80 m_selectedColor[0] = 1.0;
81 m_selectedColor[1] = 0.0;
82 m_selectedColor[2] = 1.0;
85 m_boundaryColor[0] = 1.0;
86 m_boundaryColor[1] = 0.0;
87 m_boundaryColor[2] = 1.0;
89 m_hoverActorId =
"__highlight_hover__";
90 m_preselectedActorId =
"__highlight_preselected__";
91 m_selectedActorId =
"__highlight_selected__";
92 m_boundaryActorId =
"__highlight_boundary__";
95 "[cvSelectionHighlighter] Initialized with ParaView default "
96 "colors: Hover=Purple(0.5,0,1), Selected=Magenta(1,0,1)");
109 double*
color =
nullptr;
114 color = m_hoverColor;
115 actor = &m_hoverActor;
118 color = m_preselectedColor;
119 actor = &m_preselectedActor;
122 color = m_selectedColor;
123 actor = &m_selectedActor;
126 color = m_boundaryColor;
127 actor = &m_boundaryActor;
140 if (actor && *actor) {
141 (*actor)->GetProperty()->SetColor(r, g, b);
142 (*actor)->Modified();
147 vtkRenderWindow* renWin = pclVis->getRenderWindow();
161 QString(
"[cvSelectionHighlighter] Color set for mode %1: "
173 double oldOpacity = 0.0;
178 oldOpacity = m_hoverOpacity;
179 m_hoverOpacity = opacity;
180 actor = &m_hoverActor;
183 oldOpacity = m_preselectedOpacity;
184 m_preselectedOpacity = opacity;
185 actor = &m_preselectedActor;
188 oldOpacity = m_selectedOpacity;
189 m_selectedOpacity = opacity;
190 actor = &m_selectedActor;
193 oldOpacity = m_boundaryOpacity;
194 m_boundaryOpacity = opacity;
195 actor = &m_boundaryActor;
200 if (actor && *actor) {
201 (*actor)->GetProperty()->SetOpacity(opacity);
202 (*actor)->Modified();
207 vtkRenderWindow* renWin = pclVis->getRenderWindow();
215 if (oldOpacity != opacity) {
221 QString(
"[cvSelectionHighlighter] Opacity set for mode %1: %2")
233 return m_preselectedColor;
235 return m_selectedColor;
237 return m_boundaryColor;
247 return m_hoverOpacity;
249 return m_preselectedOpacity;
251 return m_selectedOpacity;
253 return m_boundaryOpacity;
270 int fieldAssociation,
273 CVLog::Warning(
"[cvSelectionHighlighter] Highlighter is disabled");
283 CVLog::Warning(
"[cvSelectionHighlighter] Selection array is null");
294 "[cvSelectionHighlighter::highlightSelection] No polyData "
295 "available for highlighting - this is a critical error!");
301 .arg(polyData->GetNumberOfCells())
302 .arg(polyData->GetNumberOfPoints()));
310 vtkPolyData* polyData,
312 int fieldAssociation,
314 if (!m_enabled || !
m_viewer || !polyData || !selection) {
318 if (selection->GetNumberOfTuples() == 0) {
325 createHighlightActor(polyData, selection, fieldAssociation, mode);
337 removeActorFromVisualizer(m_hoverActorId);
338 m_hoverActor = actor;
339 actorId = m_hoverActorId;
343 removeActorFromVisualizer(m_preselectedActorId);
344 m_preselectedActor = actor;
345 actorId = m_preselectedActorId;
349 removeActorFromVisualizer(m_selectedActorId);
350 m_selectedActor = actor;
351 actorId = m_selectedActorId;
355 removeActorFromVisualizer(m_boundaryActorId);
356 m_boundaryActor = actor;
357 actorId = m_boundaryActorId;
361 addActorToVisualizer(actor, actorId);
372 CVLog::Warning(
"[cvSelectionHighlighter] Selection data is empty");
377 QString(
"[cvSelectionHighlighter] Highlighting %1 %2 in mode %3")
378 .arg(selectionData.
count())
387 CVLog::Error(
"[cvSelectionHighlighter] Failed to highlight selection");
396 int fieldAssociation) {
399 "[cvSelectionHighlighter::highlightElement] Highlighter is "
405 "[cvSelectionHighlighter::highlightElement] No viewer set - "
406 "call setVisualizer() first!");
411 "[cvSelectionHighlighter::highlightElement] No polyData "
419 selection->InsertNextValue(elementId);
431 removeActorFromVisualizer(m_hoverActorId);
432 removeActorFromVisualizer(m_preselectedActorId);
433 removeActorFromVisualizer(m_selectedActorId);
434 removeActorFromVisualizer(m_boundaryActorId);
436 m_hoverActor =
nullptr;
437 m_preselectedActor =
nullptr;
438 m_selectedActor =
nullptr;
439 m_boundaryActor =
nullptr;
444 vtkRenderWindow* renWin = pclVis->getRenderWindow();
455 removeActorFromVisualizer(m_hoverActorId);
456 m_hoverActor =
nullptr;
466 m_hoverActor->SetVisibility(visible ? 1 : 0);
468 if (m_preselectedActor) {
469 m_preselectedActor->SetVisibility(visible ? 1 : 0);
471 if (m_selectedActor) {
472 m_selectedActor->SetVisibility(visible ? 1 : 0);
474 if (m_boundaryActor) {
475 m_boundaryActor->SetVisibility(visible ? 1 : 0);
481 vtkRenderWindow* renWin = pclVis->getRenderWindow();
490 vtkPolyData* polyData,
491 vtkIdTypeArray* selection,
492 int fieldAssociation,
493 HighlightMode mode) {
494 if (!polyData || !selection) {
501 bool isPointSelection =
502 (fieldAssociation != 0);
503 vtkIdType maxValidId = isPointSelection ? polyData->GetNumberOfPoints()
504 : polyData->GetNumberOfCells();
509 for (vtkIdType i = 0; i < selection->GetNumberOfTuples(); ++i) {
510 vtkIdType
id = selection->GetValue(i);
511 if (
id >= 0 &&
id < maxValidId) {
512 validSelection->InsertNextValue(
id);
518 if (validSelection->GetNumberOfTuples() == 0) {
520 QString(
"[cvSelectionHighlighter::createHighlightActor] No "
522 "selection IDs (all %1 IDs are out of range [0, %2))")
523 .arg(selection->GetNumberOfTuples())
530 createSelectionNode(validSelection, fieldAssociation);
531 if (!selectionNode) {
533 "[cvSelectionHighlighter::createHighlightActor] Failed to "
534 "create selection node");
540 vtkSel->AddNode(selectionNode);
545 extractor->SetInputData(0, polyData);
546 extractor->SetInputData(1, vtkSel);
550 vtkUnstructuredGrid* extracted =
551 vtkUnstructuredGrid::SafeDownCast(extractor->GetOutput());
555 "[cvSelectionHighlighter::createHighlightActor] Extraction "
556 "failed: extracted is nullptr");
560 vtkIdType numCells = extracted->GetNumberOfCells();
561 vtkIdType numPoints = extracted->GetNumberOfPoints();
565 if (numCells == 0 && numPoints == 0) {
567 "[cvSelectionHighlighter::createHighlightActor] Extraction "
568 "resulted in 0 cells and 0 points (normal case, e.g., "
569 "degenerate geometry)");
576 mapper->SetInputData(extracted);
577 mapper->ScalarVisibilityOff();
581 actor->SetMapper(mapper);
584 vtkProperty* prop = actor->GetProperty();
585 double*
color =
nullptr;
586 double opacity = 1.0;
591 color = m_hoverColor;
592 opacity = m_hoverOpacity;
593 prop->SetLineWidth(
static_cast<float>(m_hoverLineWidth));
594 prop->SetPointSize(
static_cast<float>(m_hoverPointSize));
595 prop->SetRenderLinesAsTubes(
true);
600 color = m_preselectedColor;
601 opacity = m_preselectedOpacity;
602 prop->SetLineWidth(
static_cast<float>(m_preselectedLineWidth));
603 prop->SetPointSize(
static_cast<float>(m_preselectedPointSize));
604 prop->SetRenderLinesAsTubes(
true);
609 color = m_selectedColor;
610 opacity = m_selectedOpacity;
611 prop->SetLineWidth(
static_cast<float>(m_selectedLineWidth));
612 prop->SetPointSize(
static_cast<float>(m_selectedPointSize));
613 prop->SetRenderLinesAsTubes(
true);
618 color = m_boundaryColor;
619 opacity = m_boundaryOpacity;
620 prop->SetLineWidth(
static_cast<float>(m_boundaryLineWidth));
621 prop->SetPointSize(
static_cast<float>(m_boundaryPointSize));
622 prop->SetRenderLinesAsTubes(
true);
629 prop->SetOpacity(opacity);
632 prop->SetAmbient(0.6);
633 prop->SetDiffuse(0.8);
634 prop->SetSpecular(0.5);
635 prop->SetSpecularPower(
637 prop->SetRenderLinesAsTubes(
true);
638 prop->SetRenderPointsAsSpheres(
true);
641 prop->EdgeVisibilityOn();
642 prop->SetEdgeColor(1.0, 1.0, 1.0);
647 if (fieldAssociation == 0) {
649 prop->SetRepresentationToWireframe();
656 prop->SetRepresentationToPoints();
666 actor->SetPickable(
false);
673 vtkIdTypeArray* selection,
int fieldAssociation) {
680 node->SetContentType(vtkSelectionNode::INDICES);
682 if (fieldAssociation == 0) {
683 node->SetFieldType(vtkSelectionNode::CELL);
685 node->SetFieldType(vtkSelectionNode::POINT);
688 node->SetSelectionList(selection);
694 void cvSelectionHighlighter::addActorToVisualizer(vtkActor* actor,
698 "[cvSelectionHighlighter::addActorToVisualizer] No viewer!");
704 "[cvSelectionHighlighter::addActorToVisualizer] No actor!");
712 "[cvSelectionHighlighter::addActorToVisualizer] Visualizer is "
721 "[cvSelectionHighlighter::addActorToVisualizer] No renderer!");
727 vtkMapper* mapper = actor->GetMapper();
730 mapper->SetResolveCoincidentTopologyToPolygonOffset();
731 mapper->SetRelativeCoincidentTopologyPolygonOffsetParameters(-1.0,
733 mapper->SetResolveCoincidentTopologyPolygonOffsetFaces(1);
737 renderer->AddActor(actor);
738 actor->SetVisibility(1);
741 vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
743 renderWindow->Render();
746 CVLog::Warning(
"[cvSelectionHighlighter] No render window to update!");
751 void cvSelectionHighlighter::removeActorFromVisualizer(
const QString&
id) {
768 vtkActor* actor =
nullptr;
769 if (
id == m_hoverActorId && m_hoverActor) {
770 actor = m_hoverActor;
771 }
else if (
id == m_preselectedActorId && m_preselectedActor) {
772 actor = m_preselectedActor;
773 }
else if (
id == m_selectedActorId && m_selectedActor) {
774 actor = m_selectedActor;
775 }
else if (
id == m_boundaryActorId && m_boundaryActor) {
776 actor = m_boundaryActor;
780 renderer->RemoveActor(actor);
783 vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
785 renderWindow->Render();
789 QString(
"[cvSelectionHighlighter] Removed highlight actor: %1")
801 oldSize = m_hoverPointSize;
802 m_hoverPointSize =
size;
803 actor = &m_hoverActor;
806 oldSize = m_preselectedPointSize;
807 m_preselectedPointSize =
size;
808 actor = &m_preselectedActor;
811 oldSize = m_selectedPointSize;
812 m_selectedPointSize =
size;
813 actor = &m_selectedActor;
816 oldSize = m_boundaryPointSize;
817 m_boundaryPointSize =
size;
818 actor = &m_boundaryActor;
823 if (actor && *actor) {
824 (*actor)->GetProperty()->SetPointSize(
static_cast<float>(
size));
828 if (oldSize !=
size) {
834 QString(
"[cvSelectionHighlighter] Point size set for mode %1: %2")
843 return m_hoverPointSize;
845 return m_preselectedPointSize;
847 return m_selectedPointSize;
849 return m_boundaryPointSize;
862 oldWidth = m_hoverLineWidth;
863 m_hoverLineWidth =
width;
864 actor = &m_hoverActor;
867 oldWidth = m_preselectedLineWidth;
868 m_preselectedLineWidth =
width;
869 actor = &m_preselectedActor;
872 oldWidth = m_selectedLineWidth;
873 m_selectedLineWidth =
width;
874 actor = &m_selectedActor;
877 oldWidth = m_boundaryLineWidth;
878 m_boundaryLineWidth =
width;
879 actor = &m_boundaryActor;
884 if (actor && *actor) {
885 (*actor)->GetProperty()->SetLineWidth(
static_cast<float>(
width));
889 if (oldWidth !=
width) {
895 QString(
"[cvSelectionHighlighter] Line width set for mode %1: %2")
904 return m_hoverLineWidth;
906 return m_preselectedLineWidth;
908 return m_selectedLineWidth;
910 return m_boundaryLineWidth;
918 bool interactive)
const {
919 return interactive ? m_interactiveLabelProperties : m_labelProperties;
926 interactive ? m_interactiveLabelProperties : m_labelProperties;
964 if (m_pointLabelVisible) {
965 updateLabelActor(
true);
967 if (m_cellLabelVisible) {
968 updateLabelActor(
false);
976 QString(
"[cvSelectionPropertiesWidget] Label properties applied: "
977 "opacity=%1, pointSize=%2, lineWidth=%3")
986 m_pointLabelArrayName = arrayName;
987 m_pointLabelVisible = visible && !arrayName.isEmpty();
990 QString(
"[cvSelectionHighlighter] Point label array set: "
993 .arg(m_pointLabelVisible));
996 updateLabelActor(
true);
1005 m_cellLabelArrayName = arrayName;
1006 m_cellLabelVisible = visible && !arrayName.isEmpty();
1009 QString(
"[cvSelectionHighlighter] Cell label array set: "
1012 .arg(m_cellLabelVisible));
1015 updateLabelActor(
false);
1022 void cvSelectionHighlighter::updateLabelActor(
bool isPointLabels) {
1025 CVLog::Warning(
"[cvSelectionHighlighter] No visualizer for labels");
1029 vtkRenderer* renderer = pclVis->getRendererCollection()->GetFirstRenderer();
1031 CVLog::Warning(
"[cvSelectionHighlighter] No renderer for labels");
1037 isPointLabels ? m_pointLabelArrayName : m_cellLabelArrayName;
1038 bool visible = isPointLabels ? m_pointLabelVisible : m_cellLabelVisible;
1040 isPointLabels ? m_pointLabelActor : m_cellLabelActor;
1044 renderer->RemoveActor2D(labelActor);
1045 labelActor =
nullptr;
1048 if (!visible || arrayName.isEmpty()) {
1051 QString(
"[cvSelectionHighlighter] Clearing %1 labels")
1052 .arg(isPointLabels ?
"point" :
"cell"));
1054 if (pclVis->getRenderWindow()) {
1055 pclVis->getRenderWindow()->Modified();
1056 pclVis->getRenderWindow()->Render();
1063 if (!highlightActor) {
1065 "[cvSelectionHighlighter] No highlight actor for labels");
1069 vtkMapper* mapper = highlightActor->GetMapper();
1074 vtkDataSet*
data = mapper->GetInput();
1075 if (!
data ||
data->GetNumberOfPoints() == 0) {
1082 const int maxLabels =
1093 if (!isPointLabels) {
1097 cellCenters->SetInputData(
data);
1098 cellCenters->Update();
1101 QString(
"[cvSelectionHighlighter] Cell centers: %1 cells -> %2 "
1103 .arg(
data->GetNumberOfCells())
1104 .arg(cellCenters->GetOutput()->GetNumberOfPoints()));
1107 maskFilter->SetInputConnection(cellCenters->GetOutputPort());
1110 maskFilter->SetInputData(
data);
1113 maskFilter->SetMaximumNumberOfPoints(maxLabels);
1114 maskFilter->SetOnRatio(1);
1115 maskFilter->RandomModeOn();
1116 maskFilter->Update();
1119 QString(
"[cvSelectionHighlighter] Label mask: %1 points "
1120 "-> %2 labels (max %3)")
1121 .arg(
data->GetNumberOfPoints())
1122 .arg(maskFilter->GetOutput()->GetNumberOfPoints())
1128 labelMapper->SetInputConnection(maskFilter->GetOutputPort());
1134 if (arrayName ==
"ID" || arrayName ==
"PointID" || arrayName ==
"CellID") {
1138 labelMapper->SetLabelModeToLabelFieldData();
1139 const char* idArrayName =
1140 isPointLabels ?
"vtkOriginalPointIds" :
"vtkOriginalCellIds";
1141 labelMapper->SetFieldDataName(idArrayName);
1145 vtkDataSetAttributes* attrData =
nullptr;
1146 if (isPointLabels) {
1147 attrData = maskFilter->GetOutput()->GetPointData();
1149 attrData = maskFilter->GetOutput()
1154 const char* arrayNameCStr = arrayName.toUtf8().constData();
1155 vtkAbstractArray* array =
1156 attrData ? attrData->GetArray(arrayNameCStr) :
nullptr;
1160 labelMapper->SetLabelModeToLabelFieldData();
1161 labelMapper->SetFieldDataName(arrayNameCStr);
1165 CVLog::Warning(QString(
"[cvSelectionHighlighter] Label field '%1' "
1166 "not found in %2 data, labels disabled")
1168 .arg(isPointLabels ?
"point" :
"cell"));
1175 vtkTextProperty* textProp = labelMapper->GetLabelTextProperty();
1178 if (isPointLabels) {
1201 labelActor->SetMapper(labelMapper);
1204 renderer->AddActor2D(labelActor);
1207 QString(
"[cvSelectionHighlighter] Added %1 labels with array '%2'")
1208 .arg(isPointLabels ?
"point" :
"cell")
1212 if (pclVis->getRenderWindow()) {
1213 pclVis->getRenderWindow()->Render();
1223 return QColor(255, 0, 255);
1237 #include <vtkCell.h>
1238 #include <vtkCellData.h>
1239 #include <vtkDataArray.h>
1240 #include <vtkFieldData.h>
1241 #include <vtkPointData.h>
1242 #include <vtkStringArray.h>
1244 #include <QStringList>
1254 m_maxAttributes = maxAttribs;
1259 vtkIdType elementId,
1261 const QString& datasetName) {
1267 if (association ==
POINTS) {
1268 return formatPointTooltip(polyData, elementId, datasetName);
1270 return formatCellTooltip(polyData, elementId, datasetName);
1276 vtkIdType elementId,
1278 const QString& datasetName) {
1280 QString htmlTooltip =
1284 QString plainText = htmlTooltip;
1286 plainText.replace(
" ",
" ");
1292 QString cvTooltipFormatter::formatPointTooltip(vtkPolyData* polyData,
1294 const QString& datasetName) {
1295 if (pointId < 0 || pointId >= polyData->GetNumberOfPoints()) {
1296 CVLog::Error(
"[cvTooltipFormatter] Invalid point ID: %lld", pointId);
1303 if (!datasetName.isEmpty()) {
1304 tooltip += QString(
"<b>%1</b>").arg(datasetName);
1308 tooltip += QString(
"\n Id: %1").arg(pointId);
1312 polyData->GetPoint(pointId, point);
1313 tooltip += QString(
"\n Coords: (%1, %2, %3)")
1314 .arg(point[0], 0,
'g', 6)
1315 .arg(point[1], 0,
'g', 6)
1316 .arg(point[2], 0,
'g', 6);
1319 vtkPointData* pointData = polyData->GetPointData();
1321 int numArrays = pointData->GetNumberOfArrays();
1322 int displayedArrays = 0;
1325 vtkUnsignedCharArray* colorArray =
nullptr;
1326 const char* colorNames[] = {
"RGB",
"Colors",
"rgba",
"rgb"};
1327 for (
const char*
name : colorNames) {
1328 vtkDataArray* arr = pointData->GetArray(
name);
1329 if (arr && (arr->GetNumberOfComponents() == 3 ||
1330 arr->GetNumberOfComponents() == 4)) {
1331 colorArray = vtkUnsignedCharArray::SafeDownCast(arr);
1332 if (colorArray)
break;
1336 unsigned char color[4] = {0, 0, 0, 255};
1337 colorArray->GetTypedTuple(pointId,
color);
1338 tooltip += QString(
"\n RGB: (%1, %2, %3)")
1348 vtkDataArray* normalsArray = pointData->GetNormals();
1349 if (!normalsArray) {
1351 normalsArray = pointData->GetArray(
"Normals");
1353 if (normalsArray && normalsArray->GetNumberOfComponents() != 3) {
1354 normalsArray =
nullptr;
1357 if (normalsArray && normalsArray->GetNumberOfComponents() == 3) {
1358 double*
normal = normalsArray->GetTuple3(pointId);
1360 tooltip += QString(
"\n Normals: (%1, %2, %3)")
1361 .arg(
normal[0], 0,
'g', 6)
1362 .arg(
normal[1], 0,
'g', 6)
1363 .arg(
normal[2], 0,
'g', 6);
1370 vtkDataArray* tcoordsArray =
nullptr;
1371 QString tcoordsArrayName;
1373 for (
int i = 0; i < numArrays; ++i) {
1374 vtkDataArray* array = pointData->GetArray(i);
1375 if (!array)
continue;
1377 QString arrayName = QString::fromUtf8(array->GetName());
1378 int numComp = array->GetNumberOfComponents();
1381 if ((numComp == 2 || numComp == 3) &&
1390 tcoordsArray = array;
1391 tcoordsArrayName = arrayName;
1401 if (tcoordsArray && displayedArrays < m_maxAttributes) {
1402 int numComp = tcoordsArray->GetNumberOfComponents();
1404 double* tc = tcoordsArray->GetTuple2(pointId);
1407 tooltip += QString(
"\n TCoords: (%1, %2)")
1408 .arg(tc[0], 0,
'g', 6)
1409 .arg(tc[1], 0,
'g', 6);
1410 }
else if (numComp == 3) {
1411 double* tc = tcoordsArray->GetTuple3(pointId);
1412 tooltip += QString(
"\n TCoords: (%1, %2, %3)")
1413 .arg(tc[0], 0,
'g', 6)
1414 .arg(tc[1], 0,
'g', 6)
1415 .arg(tc[2], 0,
'g', 6);
1421 if (pointData->GetScalars() && displayedArrays < m_maxAttributes) {
1422 QString scalarName =
1423 QString::fromUtf8(pointData->GetScalars()->GetName());
1426 double scalar = pointData->GetScalars()->GetTuple1(pointId);
1427 tooltip += QString(
"\n %1: %2")
1428 .arg(scalarName.isEmpty() ?
"Scalars"
1430 .arg(formatNumber(
scalar));
1436 for (
int i = 0; i < pointData->GetNumberOfArrays(); ++i) {
1437 if (displayedArrays >= m_maxAttributes) {
1438 tooltip += QString(
"\n <i>... (%1 more attributes "
1440 .arg(pointData->GetNumberOfArrays() - i);
1444 vtkDataArray* array = pointData->GetArray(i);
1445 if (!array)
continue;
1447 QString arrayName = QString::fromUtf8(array->GetName());
1453 bool isNormalsArray =
1454 (array == pointData->GetNormals()) ||
1461 bool isTcoordsArray =
1467 if (arrayName.isEmpty() || isNormalsArray || isColorArray ||
1468 array == pointData->GetScalars() || isTcoordsArray) {
1472 QString valueStr = formatArrayValue(array, pointId);
1473 if (!valueStr.isEmpty()) {
1474 tooltip += QString(
"\n %1: %2").arg(arrayName).arg(valueStr);
1481 vtkFieldData* fieldDataPoint = polyData->GetFieldData();
1482 if (fieldDataPoint && fieldDataPoint->GetNumberOfArrays() > 0) {
1483 bool hasFieldData =
false;
1484 bool isFirstField =
true;
1485 for (
int i = 0; i < fieldDataPoint->GetNumberOfArrays(); ++i) {
1486 vtkAbstractArray* abstractArray =
1487 fieldDataPoint->GetAbstractArray(i);
1488 if (!abstractArray)
continue;
1490 QString arrayName = QString::fromUtf8(abstractArray->GetName());
1494 arrayName.startsWith(
1497 arrayName ==
"DatasetName" ||
1498 arrayName ==
"MaterialNames" ||
1505 if (!hasFieldData) {
1506 tooltip +=
"\n <hr>";
1507 hasFieldData =
true;
1510 QString linePrefix = isFirstField ?
" " :
"\n ";
1512 vtkStringArray* stringArray =
1513 vtkStringArray::SafeDownCast(abstractArray);
1514 if (stringArray && stringArray->GetNumberOfTuples() > 0) {
1515 if (stringArray->GetNumberOfTuples() == 1) {
1517 QString::fromStdString(stringArray->GetValue(0));
1518 tooltip += QString(
"%1%2: %3")
1524 for (vtkIdType j = 0; j < stringArray->GetNumberOfTuples();
1526 values << QString::fromStdString(
1527 stringArray->GetValue(j));
1529 tooltip += QString(
"%1%2: %3")
1532 .arg(values.join(
", "));
1534 isFirstField =
false;
1536 vtkDataArray* array = vtkDataArray::SafeDownCast(abstractArray);
1537 if (array && array->GetNumberOfTuples() > 0) {
1538 if (array->GetNumberOfTuples() == 1) {
1539 QString valueStr = formatArrayValue(array, 0);
1540 tooltip += QString(
"%1%2: %3")
1545 QString valueStr = formatArrayValue(array, 0);
1546 tooltip += QString(
"%1%2: %3 (array of %4)")
1550 .arg(array->GetNumberOfTuples());
1552 isFirstField =
false;
1560 return QString(
"<p style='white-space:pre'>%1</p>").arg(tooltip);
1564 QString cvTooltipFormatter::formatCellTooltip(vtkPolyData* polyData,
1566 const QString& datasetName) {
1567 if (cellId < 0 || cellId >= polyData->GetNumberOfCells()) {
1568 CVLog::Error(
"[cvTooltipFormatter] Invalid cell ID: %lld", cellId);
1575 if (!datasetName.isEmpty()) {
1576 tooltip += QString(
"<b>%1</b>").arg(datasetName);
1580 tooltip += QString(
"\n Id: %1").arg(cellId);
1583 vtkCell* cell = polyData->GetCell(cellId);
1586 switch (cell->GetCellType()) {
1587 case VTK_EMPTY_CELL:
1588 cellType =
"Empty Cell";
1591 cellType =
"Vertex";
1593 case VTK_POLY_VERTEX:
1594 cellType =
"Poly Vertex";
1600 cellType =
"Poly Line";
1603 cellType =
"Triangle";
1605 case VTK_TRIANGLE_STRIP:
1606 cellType =
"Triangle Strip";
1609 cellType =
"Polygon";
1623 case VTK_HEXAHEDRON:
1624 cellType =
"Hexahedron";
1630 cellType =
"Pyramid";
1632 case VTK_PENTAGONAL_PRISM:
1633 cellType =
"Pentagonal Prism";
1635 case VTK_HEXAGONAL_PRISM:
1636 cellType =
"Hexagonal Prism";
1639 cellType = QString(
"Unknown (%1)").arg(cell->GetCellType());
1641 tooltip += QString(
"\n Type: %1").arg(cellType);
1643 vtkIdType npts = cell->GetNumberOfPoints();
1644 tooltip += QString(
"\n Number of Points: %1").arg(npts);
1646 if (npts > 0 && npts <= 10) {
1648 for (vtkIdType i = 0; i < npts; ++i) {
1649 if (i > 0) pointIds +=
", ";
1650 pointIds += QString::number(cell->GetPointId(i));
1652 tooltip += QString(
"\n Point IDs: [%1]").arg(pointIds);
1656 double center[3] = {0, 0, 0};
1657 for (vtkIdType i = 0; i < npts; ++i) {
1659 polyData->GetPoint(cell->GetPointId(i), pt);
1668 tooltip += QString(
"\n Center: (%1, %2, %3)")
1669 .arg(center[0], 0,
'g', 6)
1670 .arg(center[1], 0,
'g', 6)
1671 .arg(center[2], 0,
'g', 6);
1676 vtkCellData* cellData = polyData->GetCellData();
1678 int displayedArrays = 0;
1682 vtkDataArray* cellNormalsArray = cellData->GetNormals();
1683 if (!cellNormalsArray) {
1684 cellNormalsArray = cellData->GetArray(
"Normals");
1685 if (cellNormalsArray &&
1686 cellNormalsArray->GetNumberOfComponents() != 3) {
1687 cellNormalsArray =
nullptr;
1690 if (cellNormalsArray &&
1691 cellNormalsArray->GetNumberOfComponents() == 3) {
1692 double*
normal = cellNormalsArray->GetTuple3(cellId);
1693 tooltip += QString(
"\n Normals: (%1, %2, %3)")
1694 .arg(
normal[0], 0,
'f', 4)
1695 .arg(
normal[1], 0,
'f', 4)
1696 .arg(
normal[2], 0,
'f', 4);
1700 if (cellData->GetScalars() && displayedArrays < m_maxAttributes) {
1701 QString scalarName =
1702 QString::fromUtf8(cellData->GetScalars()->GetName());
1703 double scalar = cellData->GetScalars()->GetTuple1(cellId);
1705 QString(
"\n %1: %2")
1706 .arg(scalarName.isEmpty() ?
"Scalars" : scalarName)
1707 .arg(formatNumber(
scalar));
1711 for (
int i = 0; i < cellData->GetNumberOfArrays(); ++i) {
1712 if (displayedArrays >= m_maxAttributes) {
1713 tooltip += QString(
"\n<i>... (%1 more attributes "
1715 .arg(cellData->GetNumberOfArrays() - i);
1719 vtkDataArray* array = cellData->GetArray(i);
1720 if (!array)
continue;
1722 QString arrayName = QString::fromUtf8(array->GetName());
1727 bool isCellNormals =
1728 (array == cellData->GetNormals()) ||
1734 if (arrayName.isEmpty() || isCellNormals || isColorArray ||
1735 array == cellData->GetScalars()) {
1739 QString valueStr = formatArrayValue(array, cellId);
1740 if (!valueStr.isEmpty()) {
1741 tooltip += QString(
"\n%1: %2").arg(arrayName).arg(valueStr);
1748 vtkFieldData* fieldDataCell = polyData->GetFieldData();
1749 if (fieldDataCell && fieldDataCell->GetNumberOfArrays() > 0) {
1750 bool hasFieldData =
false;
1751 bool isFirstField =
true;
1752 for (
int i = 0; i < fieldDataCell->GetNumberOfArrays(); ++i) {
1753 vtkAbstractArray* abstractArray =
1754 fieldDataCell->GetAbstractArray(i);
1755 if (!abstractArray)
continue;
1757 QString arrayName = QString::fromUtf8(abstractArray->GetName());
1761 arrayName.startsWith(
1764 arrayName ==
"DatasetName" ||
1765 arrayName ==
"MaterialNames" ||
1772 if (!hasFieldData) {
1773 tooltip +=
"\n<hr>";
1774 hasFieldData =
true;
1777 QString linePrefix = isFirstField ?
"" :
"\n";
1779 vtkStringArray* stringArray =
1780 vtkStringArray::SafeDownCast(abstractArray);
1781 if (stringArray && stringArray->GetNumberOfTuples() > 0) {
1782 if (stringArray->GetNumberOfTuples() == 1) {
1784 QString::fromStdString(stringArray->GetValue(0));
1785 tooltip += QString(
"%1%2: %3")
1791 for (vtkIdType j = 0; j < stringArray->GetNumberOfTuples();
1793 values << QString::fromStdString(
1794 stringArray->GetValue(j));
1796 tooltip += QString(
"%1%2: %3")
1799 .arg(values.join(
", "));
1801 isFirstField =
false;
1803 vtkDataArray* array = vtkDataArray::SafeDownCast(abstractArray);
1804 if (array && array->GetNumberOfTuples() > 0) {
1805 if (array->GetNumberOfTuples() == 1) {
1806 QString valueStr = formatArrayValue(array, 0);
1807 tooltip += QString(
"%1%2: %3")
1812 QString valueStr = formatArrayValue(array, 0);
1813 tooltip += QString(
"%1%2: %3 (array of %4)")
1817 .arg(array->GetNumberOfTuples());
1819 isFirstField =
false;
1827 return QString(
"<p style='white-space:pre'>%1</p>").arg(tooltip);
1831 void cvTooltipFormatter::addArrayValues(QString& tooltip,
1832 vtkFieldData* fieldData,
1833 vtkIdType tupleIndex) {
1838 int numArrays = fieldData->GetNumberOfArrays();
1840 for (
int i = 0; i < numArrays; ++i) {
1841 vtkDataArray* array = fieldData->GetArray(i);
1846 QString arrayName = QString::fromUtf8(array->GetName());
1847 if (arrayName.isEmpty() ||
1849 arrayName ==
"vtkOriginalPointIds" ||
1850 arrayName ==
"vtkOriginalCellIds" ||
1851 arrayName ==
"vtkCompositeIndexArray" ||
1852 arrayName ==
"vtkGhostType" || arrayName ==
"vtkValidPointMask") {
1856 QString valueStr = formatArrayValue(array, tupleIndex);
1857 if (!valueStr.isEmpty()) {
1858 tooltip += QString(
"\n%1: %2").arg(arrayName).arg(valueStr);
1864 QString cvTooltipFormatter::formatArrayValue(vtkDataArray* array,
1865 vtkIdType tupleIndex) {
1866 if (!array || tupleIndex < 0 || tupleIndex >= array->GetNumberOfTuples()) {
1870 int numComponents = array->GetNumberOfComponents();
1871 const int maxDisplayedComp = 9;
1873 if (numComponents == 1) {
1874 double value = array->GetTuple1(tupleIndex);
1875 return formatNumber(value);
1878 if (numComponents > 1) {
1882 for (
int i = 0; i < std::min(numComponents, maxDisplayedComp); ++i) {
1883 double value = array->GetComponent(tupleIndex, i);
1884 result += formatNumber(value);
1885 if (i + 1 < numComponents && i < maxDisplayedComp) {
1890 if (numComponents > maxDisplayedComp) {
1894 if (numComponents > 1) {
1902 QString cvTooltipFormatter::formatNumber(
double value) {
1903 double absValue = qAbs(value);
1905 if (absValue > 0 && (absValue < 1e-4 || absValue >= 1e6)) {
1906 return QString::number(value,
'e', 4);
1908 return QString::number(value,
'g', 6);
QRegularExpression QtCompatRegExp
static bool Warning(const char *format,...)
Prints out a formatted warning 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)
PclUtils::PCLVis * getPCLVis() const
Get PCLVis instance (for VTK-specific operations)
ecvGenericVisualizer3D * m_viewer
Visualizer instance (abstract interface)
Encapsulates selection data without exposing VTK types.
FieldAssociation fieldAssociation() const
Get field association.
vtkSmartPointer< vtkIdTypeArray > vtkArray() const
Get the underlying VTK array (for internal use only)
QString fieldTypeString() const
Get human-readable field type string.
bool isEmpty() const
Check if selection is empty.
int count() const
Get number of selected items.
void setLineWidth(int width, HighlightMode mode=SELECTED)
Set line width for highlight rendering.
void setHighlightQColor(const QColor &color, HighlightMode mode=SELECTED)
Set highlight color from QColor.
cvSelectionHighlighter()
Constructor.
bool highlightSelection(const vtkSmartPointer< vtkIdTypeArray > &selection, int fieldAssociation, HighlightMode mode=SELECTED)
Highlight selected elements (automatically gets polyData from visualizer)
void propertiesChanged()
Emitted when any property changes (general notification)
~cvSelectionHighlighter()
Destructor.
const SelectionLabelProperties & getLabelProperties(bool interactive=false) const
Get label properties for selection mode.
void colorChanged(int mode)
Emitted when any highlight color changes.
void setEnabled(bool enabled)
Enable/disable highlight.
void opacityChanged(int mode)
Emitted when any opacity changes.
void labelPropertiesChanged(bool interactive)
Emitted when label properties change.
HighlightMode
Highlight mode (Enhanced multi-level highlighting)
void setHighlightColor(double r, double g, double b, HighlightMode mode=SELECTED)
Set highlight color.
const double * getHighlightColor(HighlightMode mode) const
Get highlight color for a specific mode.
void setHighlightsVisible(bool visible)
Set visibility of all highlight actors.
void setPointSize(int size, HighlightMode mode=SELECTED)
Set point size for highlight rendering.
void setCellLabelArray(const QString &arrayName, bool visible=true)
Set the cell label array name.
void setLabelProperties(const SelectionLabelProperties &props, bool interactive=false)
Set label properties for selection mode.
int getLineWidth(HighlightMode mode) const
Get line width for a specific mode.
void pointSizeChanged(int mode)
Emitted when point size changes.
double getHighlightOpacity(HighlightMode mode) const
Get highlight opacity for a specific mode.
bool highlightElement(vtkPolyData *polyData, vtkIdType elementId, int fieldAssociation)
Highlight a single element (for hover preview)
QColor getHighlightQColor(HighlightMode mode) const
Get highlight color as QColor.
int getPointSize(HighlightMode mode) const
Get point size for a specific mode.
void clearHoverHighlight()
Clear only hover highlight (keep selected/preselected)
void setPointLabelArray(const QString &arrayName, bool visible=true)
Set the point label array name.
void lineWidthChanged(int mode)
Emitted when line width changes.
void clearHighlights()
Clear all highlights.
void setHighlightOpacity(double opacity, HighlightMode mode=SELECTED)
Set highlight opacity.
constexpr QRegularExpression::PatternOption CaseInsensitive
Label properties for selection annotations.