15 #include <vtkCellData.h>
16 #include <vtkDataArray.h>
17 #include <vtkFieldData.h>
18 #include <vtkIdList.h>
19 #include <vtkKdTreePointLocator.h>
20 #include <vtkPointData.h>
21 #include <vtkPolyData.h>
22 #include <vtkSignedCharArray.h>
23 #include <vtkSmartPointer.h>
39 if (
a.isEmpty())
return b;
43 CVLog::Error(
"[cvSelectionAlgebra] Incompatible selections for union");
50 QSet<qint64>
result = setA.unite(setB);
54 CVLog::Print(QString(
"[cvSelectionAlgebra] Union: %1 U %2 = %3")
57 .arg(resultIds.size()));
71 "[cvSelectionAlgebra] Incompatible selections for "
79 QSet<qint64>
result = setA.intersect(setB);
83 CVLog::Print(QString(
"[cvSelectionAlgebra] Intersection: %1 & %2 = %3")
86 .arg(resultIds.size()));
104 "[cvSelectionAlgebra] Incompatible selections for difference");
111 QSet<qint64>
result = setA.subtract(setB);
115 CVLog::Print(QString(
"[cvSelectionAlgebra] Difference: %1 - %2 = %3")
118 .arg(resultIds.size()));
132 "[cvSelectionAlgebra] Incompatible selections for symmetric "
143 QSet<qint64> aMinusB = setA;
144 aMinusB.subtract(setB);
147 QSet<qint64> bMinusA = setB;
148 bMinusA.subtract(setA);
151 QSet<qint64>
result = aMinusB.unite(bMinusA);
156 QString(
"[cvSelectionAlgebra] Symmetric Difference: %1 ^ %2 = %3")
159 .arg(resultIds.size()));
168 CVLog::Error(
"[cvSelectionAlgebra] polyData is nullptr for complement");
174 vtkIdType totalCount =
176 ? polyData->GetNumberOfPoints()
177 : polyData->GetNumberOfCells();
179 QVector<qint64> allIds;
180 for (vtkIdType i = 0; i < totalCount; ++i) {
184 CVLog::Print(QString(
"[cvSelectionAlgebra] Complement: 0 -> %1 (all)")
191 QVector<qint64> complementIds;
194 ? polyData->GetNumberOfPoints()
195 : polyData->GetNumberOfCells();
197 for (vtkIdType i = 0; i < totalCount; ++i) {
198 if (!selectedSet.contains(i)) {
199 complementIds.append(i);
203 CVLog::Print(QString(
"[cvSelectionAlgebra] Complement: ~%1 = %2")
205 .arg(complementIds.size()));
214 vtkPolyData* polyData) {
227 "[cvSelectionAlgebra] polyData required for "
233 CVLog::Error(QString(
"[cvSelectionAlgebra] Unknown operation: %1")
234 .arg(
static_cast<int>(op)));
241 vtkPolyData* polyData,
245 bool removeIntermediateLayers) {
246 if (!polyData || input.
isEmpty()) {
252 "[cvSelectionAlgebra] Grow only works with cell selection");
258 QSet<vtkIdType> seedSet;
259 for (qint64
id : idsSet) {
260 seedSet.insert(
static_cast<vtkIdType
>(
id));
262 QSet<vtkIdType> currentSet = seedSet;
263 QSet<vtkIdType> previousLayerSet = seedSet;
266 for (
int iter = 0; iter < layers; ++iter) {
267 QSet<vtkIdType> newSet = currentSet;
270 for (vtkIdType cellId : currentSet) {
271 QSet<vtkIdType> neighbors = getCellNeighbors(polyData, cellId);
272 newSet.unite(neighbors);
276 previousLayerSet = currentSet;
281 QSet<vtkIdType> resultSet = currentSet;
283 if (removeIntermediateLayers) {
286 resultSet = currentSet;
287 resultSet.subtract(previousLayerSet);
289 QString(
"[cvSelectionAlgebra] Removed intermediate layers, "
290 "keeping outermost: %1 cells")
291 .arg(resultSet.size()));
296 resultSet.subtract(seedSet);
298 QString(
"[cvSelectionAlgebra] Removed seed, result: %1 cells")
299 .arg(resultSet.size()));
302 QVector<qint64> resultIds;
303 for (vtkIdType
id : resultSet) {
304 resultIds.append(
id);
308 QString(
"[cvSelectionAlgebra] Grow %1 layers (removeSeed=%2, "
309 "removeIntermediate=%3): %4 -> %5 cells")
312 .arg(removeIntermediateLayers)
314 .arg(resultIds.size()));
322 if (!polyData || input.
isEmpty()) {
328 "[cvSelectionAlgebra] Shrink only works with cell selection");
333 QSet<vtkIdType> currentSet;
334 for (qint64
id : idsSet) {
335 currentSet.insert(
static_cast<vtkIdType
>(
id));
338 for (
int iter = 0; iter < iterations; ++iter) {
339 QSet<vtkIdType> newSet;
342 for (vtkIdType cellId : currentSet) {
343 if (!isBoundaryCell(polyData, cellId, currentSet)) {
344 newSet.insert(cellId);
350 if (currentSet.isEmpty()) {
355 QVector<qint64> resultIds;
356 for (vtkIdType
id : currentSet) {
357 resultIds.append(
id);
361 QString(
"[cvSelectionAlgebra] Shrink %1 iterations: %2 -> %3 cells")
364 .arg(resultIds.size()));
372 if (!polyData || input.
isEmpty()) {
378 "[cvSelectionAlgebra] Boundary extraction only works with cell "
384 QSet<vtkIdType> selectedSet;
385 for (qint64
id : idsSet) {
386 selectedSet.insert(
static_cast<vtkIdType
>(
id));
388 QVector<qint64> boundaryIds;
390 for (vtkIdType cellId : selectedSet) {
391 if (isBoundaryCell(polyData, cellId, selectedSet)) {
392 boundaryIds.append(cellId);
397 QString(
"[cvSelectionAlgebra] Boundary extraction: %1 -> %2 cells")
399 .arg(boundaryIds.size()));
418 QSet<vtkIdType> cvSelectionAlgebra::getCellNeighbors(vtkPolyData* polyData,
420 QSet<vtkIdType> neighbors;
422 vtkCell* cell = polyData->GetCell(cellId);
423 if (!cell)
return neighbors;
425 vtkIdType npts = cell->GetNumberOfPoints();
428 for (vtkIdType i = 0; i < npts; ++i) {
429 vtkIdType p1 = cell->GetPointId(i);
430 vtkIdType p2 = cell->GetPointId((i + 1) % npts);
433 polyData->GetCellEdgeNeighbors(cellId, p1, p2, cellIds);
435 for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); ++j) {
436 neighbors.insert(cellIds->GetId(j));
444 bool cvSelectionAlgebra::isBoundaryCell(vtkPolyData* polyData,
446 const QSet<vtkIdType>& selectedSet) {
447 QSet<vtkIdType> neighbors = getCellNeighbors(polyData, cellId);
451 for (vtkIdType neighborId : neighbors) {
452 if (!selectedSet.contains(neighborId)) {
458 return neighbors.isEmpty();
463 vtkPolyData* polyData,
467 bool removeIntermediateLayers) {
472 if (!polyData || input.
isEmpty()) {
481 bool isPointSelection =
486 QSet<qint64> currentSet;
488 for (qint64
id : input.
ids()) {
489 currentSet.insert(
id);
492 QSet<qint64> seedSet =
496 for (
int iter = 0; iter < layers; ++iter) {
497 QSet<qint64> newElements;
500 for (qint64
id : currentSet) {
501 QSet<vtkIdType> neighbors;
502 if (isPointSelection) {
503 neighbors = getPointNeighbors(polyData,
504 static_cast<vtkIdType
>(
id));
506 neighbors = getCellNeighbors(polyData,
507 static_cast<vtkIdType
>(
id));
511 for (vtkIdType neighborId : neighbors) {
512 if (!currentSet.contains(
static_cast<qint64
>(neighborId))) {
513 newElements.insert(
static_cast<qint64
>(neighborId));
519 if (newElements.isEmpty()) {
521 "at iteration %1: no new neighbors")
526 currentSet.unite(newElements);
531 currentSet.subtract(seedSet);
536 if (removeIntermediateLayers && layers > 1) {
539 QSet<qint64> layerSet = seedSet;
541 for (
int iter = 0; iter < layers; ++iter) {
542 QSet<qint64> nextLayerSet;
545 for (qint64
id : layerSet) {
546 QSet<vtkIdType> neighbors;
547 if (isPointSelection) {
548 neighbors = getPointNeighbors(
549 polyData,
static_cast<vtkIdType
>(
id));
551 neighbors = getCellNeighbors(
552 polyData,
static_cast<vtkIdType
>(
id));
555 for (vtkIdType neighborId : neighbors) {
556 if (!currentSet.contains(
557 static_cast<qint64
>(neighborId)) ||
559 static_cast<qint64
>(neighborId))) {
562 nextLayerSet.insert(
static_cast<qint64
>(neighborId));
566 if (iter == layers - 1) {
568 currentSet = nextLayerSet;
572 layerSet = nextLayerSet;
573 if (layerSet.isEmpty()) {
580 QVector<qint64> expandedIds;
581 for (qint64
id : currentSet) {
582 expandedIds.append(
id);
586 QString(
"[cvSelectionAlgebra] Grow: %1 layers, %2 -> %3 "
587 "%4 (removeSeed=%5, removeIntermediate=%6)")
589 .arg(input.
ids().size())
590 .arg(expandedIds.size())
591 .arg(isPointSelection ?
"points" :
"cells")
593 .arg(removeIntermediateLayers));
604 return resultSelection;
609 int shrinkLayers = -layers;
614 vtkIdType numCells = polyData->GetNumberOfCells();
615 bool hasMeshTopology = (numCells > 0);
617 if (!hasMeshTopology) {
619 "[cvSelectionAlgebra] Shrink operation requires mesh "
621 "Pure point clouds without cells are not supported. "
622 "Returning input unchanged.");
627 QString(
"[cvSelectionAlgebra] Starting shrink: %1 %2, %3 "
629 .arg(input.
ids().size())
630 .arg(isPointSelection ?
"points" :
"cells")
634 QSet<qint64> currentSet;
635 for (qint64
id : input.
ids()) {
636 currentSet.insert(
id);
642 for (
int iter = 0; iter < shrinkLayers; ++iter) {
649 for (qint64
id : currentSet) {
650 QSet<vtkIdType> neighbors;
652 if (isPointSelection) {
653 neighbors = getPointNeighbors(polyData,
654 static_cast<vtkIdType
>(
id));
656 neighbors = getCellNeighbors(polyData,
657 static_cast<vtkIdType
>(
id));
661 int neighborsOutside = 0;
662 for (vtkIdType neighborId : neighbors) {
663 if (!currentSet.contains(
static_cast<qint64
>(neighborId))) {
671 bool isBoundary = (neighbors.isEmpty() || neighborsOutside > 0);
674 boundaryElements.insert(
id);
676 interiorElements.insert(
id);
681 QString(
"[cvSelectionAlgebra] Shrink iteration %1: "
682 "found %2 boundary, %3 interior elements")
684 .arg(boundaryElements.size())
685 .arg(interiorElements.size()));
688 if (boundaryElements.isEmpty()) {
690 QString(
"[cvSelectionAlgebra] Shrink stopped at "
692 "no boundary elements found (all %2 %3 have "
693 "all neighbors selected)")
695 .arg(currentSet.size())
696 .arg(isPointSelection ?
"points" :
"cells"));
701 currentSet = interiorElements;
704 if (currentSet.isEmpty()) {
706 QString(
"[cvSelectionAlgebra] Shrink iteration "
707 "%1: selection shrunk to empty")
713 QVector<qint64> shrunkIds;
714 for (qint64
id : currentSet) {
715 shrunkIds.append(
id);
719 QString(
"[cvSelectionAlgebra] Shrink: %1 layers, %2 -> %3 %4")
721 .arg(input.
ids().size())
722 .arg(shrunkIds.size())
723 .arg(isPointSelection ?
"points" :
"cells"));
734 return resultSelection;
740 vtkPolyData* polyData,
744 bool removeIntermediateLayers) {
745 if (!polyData || input.
isEmpty()) {
751 "[cvSelectionAlgebra] growPointSelection only works with point "
758 QSet<vtkIdType> seedSet;
759 for (qint64
id : idsSet) {
760 seedSet.insert(
static_cast<vtkIdType
>(
id));
762 QSet<vtkIdType> currentSet = seedSet;
763 QSet<vtkIdType> previousLayerSet = seedSet;
766 for (
int iter = 0; iter < layers; ++iter) {
767 QSet<vtkIdType> newSet = currentSet;
768 bool hasAnyNeighbors =
false;
771 for (vtkIdType pointId : currentSet) {
772 QSet<vtkIdType> neighbors = getPointNeighbors(polyData, pointId);
773 if (!neighbors.isEmpty()) {
774 hasAnyNeighbors =
true;
776 newSet.unite(neighbors);
781 if (!hasAnyNeighbors && iter == 0) {
783 "[cvSelectionAlgebra] No topological neighbors found for "
785 "This appears to be a pure point cloud. Returning input "
791 if (newSet.size() == currentSet.size()) {
793 QString(
"[cvSelectionAlgebra] Grow stopped at layer %1: "
794 "no new neighbors found")
799 previousLayerSet = currentSet;
804 QSet<vtkIdType> resultSet = currentSet;
806 if (removeIntermediateLayers) {
807 resultSet = currentSet;
808 resultSet.subtract(previousLayerSet);
812 resultSet.subtract(seedSet);
815 QVector<qint64> resultIds;
816 for (vtkIdType
id : resultSet) {
817 resultIds.append(
id);
821 QString(
"[cvSelectionAlgebra] Grow points %1 layers: %2 -> %3 "
825 .arg(resultIds.size()));
833 if (!polyData || input.
isEmpty()) {
839 "[cvSelectionAlgebra] shrinkPointSelection only works with "
845 QSet<vtkIdType> currentSet;
846 for (qint64
id : idsSet) {
847 currentSet.insert(
static_cast<vtkIdType
>(
id));
853 bool hasTopology =
false;
854 for (vtkIdType pointId : currentSet) {
856 polyData->GetPointCells(pointId, cellIds);
857 for (vtkIdType i = 0; i < cellIds->GetNumberOfIds(); ++i) {
858 vtkCell* cell = polyData->GetCell(cellIds->GetId(i));
859 if (cell && cell->GetNumberOfPoints() > 1) {
871 "[cvSelectionAlgebra] No topological neighbors found for "
873 "This appears to be a pure point cloud. Returning input "
878 for (
int iter = 0; iter < iterations; ++iter) {
879 QSet<vtkIdType> newSet;
882 for (vtkIdType pointId : currentSet) {
883 if (!isBoundaryPoint(polyData, pointId, currentSet)) {
884 newSet.insert(pointId);
889 if (newSet.size() == currentSet.size()) {
891 QString(
"[cvSelectionAlgebra] Shrink stopped at "
893 "no boundary points found")
900 if (currentSet.isEmpty()) {
905 QVector<qint64> resultIds;
906 for (vtkIdType
id : currentSet) {
907 resultIds.append(
id);
911 QString(
"[cvSelectionAlgebra] Shrink points %1 iterations: %2 "
915 .arg(resultIds.size()));
921 QSet<vtkIdType> cvSelectionAlgebra::getPointNeighbors(vtkPolyData* polyData,
923 QSet<vtkIdType> neighbors;
928 "[cvSelectionAlgebra::getPointNeighbors] polyData is null");
932 vtkIdType numPoints = polyData->GetNumberOfPoints();
933 if (numPoints == 0) {
935 "[cvSelectionAlgebra::getPointNeighbors] polyData has no "
940 if (pointId < 0 || pointId >= numPoints) {
942 "pointId %1 out of range [0, %2)")
950 polyData->GetPointCells(pointId, cellIds);
953 for (vtkIdType i = 0; i < cellIds->GetNumberOfIds(); ++i) {
954 vtkIdType cellId = cellIds->GetId(i);
955 vtkCell* cell = polyData->GetCell(cellId);
957 for (vtkIdType j = 0; j < cell->GetNumberOfPoints(); ++j) {
958 vtkIdType neighborPtId = cell->GetPointId(j);
959 if (neighborPtId != pointId) {
960 neighbors.insert(neighborPtId);
971 if (neighbors.isEmpty()) {
975 QString(
"[cvSelectionAlgebra] Point %1: no topological "
977 "(pure point cloud - grow/shrink not applicable)")
985 bool cvSelectionAlgebra::isBoundaryPoint(vtkPolyData* polyData,
987 const QSet<vtkIdType>& selectedSet) {
993 vtkIdType numPoints = polyData->GetNumberOfPoints();
994 if (pointId < 0 || pointId >= numPoints) {
1004 polyData->GetPointCells(pointId, cellIds);
1006 QSet<vtkIdType> topoNeighbors;
1007 for (vtkIdType i = 0; i < cellIds->GetNumberOfIds(); ++i) {
1008 vtkIdType cellId = cellIds->GetId(i);
1009 vtkCell* cell = polyData->GetCell(cellId);
1011 for (vtkIdType j = 0; j < cell->GetNumberOfPoints(); ++j) {
1012 vtkIdType neighborPtId = cell->GetPointId(j);
1013 if (neighborPtId != pointId) {
1014 topoNeighbors.insert(neighborPtId);
1020 if (!topoNeighbors.isEmpty()) {
1022 for (vtkIdType neighborId : topoNeighbors) {
1023 if (!selectedSet.contains(neighborId)) {
1040 #include <vtkTriangle.h>
1052 vtkPolyData* polyData,
1054 const QString& attributeName,
1057 if (!polyData || input.
isEmpty() || attributeName.isEmpty()) {
1061 vtkDataArray* array =
nullptr;
1063 array = polyData->GetPointData()->GetArray(
1064 attributeName.toUtf8().constData());
1066 array = polyData->GetCellData()->GetArray(
1067 attributeName.toUtf8().constData());
1071 CVLog::Warning(QString(
"[cvSelectionFilter] Attribute '%1' not found")
1072 .arg(attributeName));
1076 QVector<qint64> filteredIds;
1077 QVector<qint64> inputIds = input.
ids();
1079 for (qint64
id : inputIds) {
1080 if (id < 0 || id >= array->GetNumberOfTuples()) {
1084 double value = array->GetComponent(
id, 0);
1086 if (value >= minValue && value <= maxValue) {
1087 filteredIds.append(
id);
1092 QString(
"[cvSelectionFilter] Attribute range filter: %1 -> %2 "
1094 .arg(inputIds.size())
1095 .arg(filteredIds.size()));
1102 vtkPolyData* polyData,
1104 const QString& attributeName,
1107 if (!polyData || input.
isEmpty() || attributeName.isEmpty()) {
1111 vtkDataArray* array =
nullptr;
1113 array = polyData->GetPointData()->GetArray(
1114 attributeName.toUtf8().constData());
1116 array = polyData->GetCellData()->GetArray(
1117 attributeName.toUtf8().constData());
1121 CVLog::Warning(QString(
"[cvSelectionFilter] Attribute '%1' not found")
1122 .arg(attributeName));
1126 QVector<qint64> filteredIds;
1127 QVector<qint64> inputIds = input.
ids();
1129 for (qint64
id : inputIds) {
1130 if (id < 0 || id >= array->GetNumberOfTuples()) {
1134 double attrValue = array->GetComponent(
id, 0);
1139 pass = (qAbs(attrValue - value) < 1
e-6);
1142 pass = (qAbs(attrValue - value) >= 1
e-6);
1145 pass = (attrValue < value);
1148 pass = (attrValue <= value);
1151 pass = (attrValue > value);
1154 pass = (attrValue >= value);
1161 filteredIds.append(
id);
1166 QString(
"[cvSelectionFilter] Attribute comparison filter: %1 "
1168 .arg(inputIds.size())
1169 .arg(filteredIds.size()));
1179 if (!polyData || input.
isEmpty()) {
1185 "[cvSelectionFilter] Area filter only works with cell "
1190 QVector<qint64> filteredIds;
1191 QVector<qint64> inputIds = input.
ids();
1193 for (qint64
id : inputIds) {
1194 if (id < 0 || id >= polyData->GetNumberOfCells()) {
1198 double area = computeCellArea(polyData,
id);
1199 if (area >= minArea && area <= maxArea) {
1200 filteredIds.append(
id);
1205 QString(
"[cvSelectionFilter] Area filter: %1 -> %2 cells")
1206 .arg(inputIds.size())
1207 .arg(filteredIds.size()));
1214 vtkPolyData* polyData,
1220 double maxAngleDeg) {
1221 if (!polyData || input.
isEmpty()) {
1227 "[cvSelectionFilter] Normal angle filter only works with cell "
1232 vtkDataArray*
normals = polyData->GetCellData()->GetNormals();
1238 double refNormal[3] = {refX, refY, refZ};
1239 vtkMath::Normalize(refNormal);
1241 QVector<qint64> filteredIds;
1242 QVector<qint64> inputIds = input.
ids();
1244 for (qint64
id : inputIds) {
1245 if (id < 0 || id >= polyData->GetNumberOfCells()) {
1250 double angleDeg = computeAngleBetweenNormals(
normal, refNormal);
1252 if (angleDeg >= minAngleDeg && angleDeg <= maxAngleDeg) {
1253 filteredIds.append(
id);
1258 QString(
"[cvSelectionFilter] Normal angle filter: %1 -> %2 cells")
1259 .arg(inputIds.size())
1260 .arg(filteredIds.size()));
1267 vtkPolyData* polyData,
1269 const double bounds[6]) {
1270 if (!polyData || input.
isEmpty()) {
1274 QVector<qint64> filteredIds;
1275 QVector<qint64> inputIds = input.
ids();
1278 for (qint64
id : inputIds) {
1279 if (id < 0 || id >= polyData->GetNumberOfPoints()) {
1284 polyData->GetPoint(
id, point);
1286 if (isPointInBounds(point, bounds)) {
1287 filteredIds.append(
id);
1291 for (qint64
id : inputIds) {
1292 if (id < 0 || id >= polyData->GetNumberOfCells()) {
1296 vtkCell* cell = polyData->GetCell(
id);
1297 if (!cell)
continue;
1299 double center[3] = {0, 0, 0};
1300 vtkIdType npts = cell->GetNumberOfPoints();
1301 for (vtkIdType i = 0; i < npts; ++i) {
1303 polyData->GetPoint(cell->GetPointId(i), pt);
1314 if (isPointInBounds(center, bounds)) {
1315 filteredIds.append(
id);
1321 QString(
"[cvSelectionFilter] Bounding box filter: %1 -> %2 items")
1322 .arg(inputIds.size())
1323 .arg(filteredIds.size()));
1330 vtkPolyData* polyData,
1336 double maxDistance) {
1337 if (!polyData || input.
isEmpty()) {
1341 double refPoint[3] = {
x,
y,
z};
1342 QVector<qint64> filteredIds;
1343 QVector<qint64> inputIds = input.
ids();
1346 for (qint64
id : inputIds) {
1347 if (id < 0 || id >= polyData->GetNumberOfPoints()) {
1352 polyData->GetPoint(
id, point);
1354 double dist = computeDistance(point, refPoint);
1355 if (dist >= minDistance && dist <= maxDistance) {
1356 filteredIds.append(
id);
1360 for (qint64
id : inputIds) {
1361 if (id < 0 || id >= polyData->GetNumberOfCells()) {
1365 vtkCell* cell = polyData->GetCell(
id);
1366 if (!cell)
continue;
1368 double center[3] = {0, 0, 0};
1369 vtkIdType npts = cell->GetNumberOfPoints();
1370 for (vtkIdType i = 0; i < npts; ++i) {
1372 polyData->GetPoint(cell->GetPointId(i), pt);
1383 double dist = computeDistance(center, refPoint);
1384 if (dist >= minDistance && dist <= maxDistance) {
1385 filteredIds.append(
id);
1391 QString(
"[cvSelectionFilter] Distance filter: %1 -> %2 items")
1392 .arg(inputIds.size())
1393 .arg(filteredIds.size()));
1400 vtkPolyData* polyData,
1404 if (!polyData || input.
isEmpty()) {
1410 "[cvSelectionFilter] Neighbor count filter only works with "
1415 QVector<qint64> filteredIds;
1416 QVector<qint64> inputIds = input.
ids();
1418 for (qint64
id : inputIds) {
1419 if (id < 0 || id >= polyData->GetNumberOfCells()) {
1423 int neighborCount = countCellNeighbors(polyData,
id);
1424 if (neighborCount >= minNeighbors && neighborCount <= maxNeighbors) {
1425 filteredIds.append(
id);
1430 QString(
"[cvSelectionFilter] Neighbor count filter: %1 -> %2 cells")
1431 .arg(inputIds.size())
1432 .arg(filteredIds.size()));
1450 QSet<qint64>
result = setA.intersect(setB);
1457 .arg(resultIds.size()));
1465 if (
a.isEmpty())
return b;
1474 QSet<qint64>
result = setA.unite(setB);
1481 .arg(resultIds.size()));
1489 if (!polyData || input.
isEmpty()) {
1494 QVector<qint64> invertedIds;
1497 ? polyData->GetNumberOfPoints()
1498 : polyData->GetNumberOfCells();
1500 for (vtkIdType i = 0; i < totalCount; ++i) {
1501 if (!selectedSet.contains(i)) {
1502 invertedIds.append(i);
1508 .arg(invertedIds.size()));
1522 vtkFieldData*
data = pointData ? (vtkFieldData*)polyData->GetPointData()
1523 : (vtkFieldData*)polyData->GetCellData();
1529 int numArrays =
data->GetNumberOfArrays();
1530 for (
int i = 0; i < numArrays; ++i) {
1531 vtkDataArray* array =
data->GetArray(i);
1532 if (array && array->GetName()) {
1533 names.append(QString::fromUtf8(array->GetName()));
1544 double cvSelectionFilter::computeCellArea(vtkPolyData* polyData,
1546 vtkCell* cell = polyData->GetCell(cellId);
1547 if (!cell)
return 0.0;
1549 if (cell->GetCellType() == VTK_TRIANGLE) {
1550 double p0[3], p1[3], p2[3];
1551 polyData->GetPoint(cell->GetPointId(0), p0);
1552 polyData->GetPoint(cell->GetPointId(1), p1);
1553 polyData->GetPoint(cell->GetPointId(2), p2);
1555 return vtkTriangle::TriangleArea(p0, p1, p2);
1558 vtkIdType npts = cell->GetNumberOfPoints();
1559 if (npts < 3)
return 0.0;
1562 double p0[3], p1[3], p2[3];
1563 polyData->GetPoint(cell->GetPointId(0), p0);
1565 for (vtkIdType i = 1; i < npts - 1; ++i) {
1566 polyData->GetPoint(cell->GetPointId(i), p1);
1567 polyData->GetPoint(cell->GetPointId(i + 1), p2);
1568 area += vtkTriangle::TriangleArea(p0, p1, p2);
1575 double cvSelectionFilter::computeAngleBetweenNormals(
const double n1[3],
1576 const double n2[3]) {
1577 double dot = vtkMath::Dot(n1, n2);
1578 dot = qBound(-1.0, dot, 1.0);
1579 double angleRad = std::acos(dot);
1580 return vtkMath::DegreesFromRadians(angleRad);
1584 bool cvSelectionFilter::isPointInBounds(
const double point[3],
1585 const double bounds[6]) {
1586 return (point[0] >= bounds[0] && point[0] <= bounds[1] &&
1587 point[1] >= bounds[2] && point[1] <= bounds[3] &&
1588 point[2] >= bounds[4] && point[2] <= bounds[5]);
1592 double cvSelectionFilter::computeDistance(
const double p1[3],
1593 const double p2[3]) {
1594 double dx = p1[0] - p2[0];
1595 double dy = p1[1] - p2[1];
1596 double dz = p1[2] - p2[2];
1597 return std::sqrt(dx * dx + dy * dy + dz * dz);
1601 int cvSelectionFilter::countCellNeighbors(vtkPolyData* polyData,
1603 vtkCell* cell = polyData->GetCell(cellId);
1604 if (!cell)
return 0;
1606 QSet<vtkIdType> neighbors;
1608 vtkIdType npts = cell->GetNumberOfPoints();
1609 for (vtkIdType i = 0; i < npts; ++i) {
1610 vtkIdType p1 = cell->GetPointId(i);
1611 vtkIdType p2 = cell->GetPointId((i + 1) % npts);
1614 polyData->GetCellEdgeNeighbors(cellId, p1, p2, cellIds);
1616 for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); ++j) {
1617 neighbors.insert(cellIds->GetId(j));
1621 return neighbors.size();
QSet< T > qSetFromVector(const QVector< T > &vec)
QVector< T > qVectorFromSet(const QSet< T > &set)
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.
static cvSelectionData complementOf(vtkPolyData *polyData, const cvSelectionData &input)
Compute complement of a selection.
static cvSelectionData extractBoundary(vtkPolyData *polyData, const cvSelectionData &input)
Extract boundary elements of selection.
static cvSelectionData differenceOf(const cvSelectionData &a, const cvSelectionData &b)
Compute difference of two selections.
static cvSelectionData growPointSelection(vtkPolyData *polyData, const cvSelectionData &input, int layers=1, bool removeSeed=false, bool removeIntermediateLayers=false)
Grow point selection by adding neighbor points.
static cvSelectionData unionOf(const cvSelectionData &a, const cvSelectionData &b)
Compute union of two selections.
static cvSelectionData shrinkPointSelection(vtkPolyData *polyData, const cvSelectionData &input, int iterations=1)
Shrink point selection by removing boundary points.
Operation
Algebra operations Using enum class to avoid macro conflicts (e.g., DIFFERENCE may be defined as a ma...
cvSelectionAlgebra(QObject *parent=nullptr)
static cvSelectionData expandSelection(vtkPolyData *polyData, const cvSelectionData &input, int layers, bool removeSeed=false, bool removeIntermediateLayers=false)
Expand selection (ParaView-compatible)
static cvSelectionData symmetricDifferenceOf(const cvSelectionData &a, const cvSelectionData &b)
Compute symmetric difference of two selections.
static cvSelectionData performOperation(Operation op, const cvSelectionData &a, const cvSelectionData &b, vtkPolyData *polyData=nullptr)
Perform algebra operation on two selections.
static cvSelectionData intersectionOf(const cvSelectionData &a, const cvSelectionData &b)
Compute intersection of two selections.
static cvSelectionData shrinkSelection(vtkPolyData *polyData, const cvSelectionData &input, int iterations=1)
Shrink selection by removing boundary elements.
static bool areCompatible(const cvSelectionData &a, const cvSelectionData &b)
Validate that two selections are compatible for operations.
static cvSelectionData growSelection(vtkPolyData *polyData, const cvSelectionData &input, int layers=1, bool removeSeed=false, bool removeIntermediateLayers=false)
Grow selection by adding neighbors.
Encapsulates selection data without exposing VTK types.
FieldAssociation fieldAssociation() const
Get field association.
@ CELLS
Selection applies to cells.
@ POINTS
Selection applies to points.
bool isEmpty() const
Check if selection is empty.
QVector< qint64 > ids() const
Get selected IDs as a vector (copy)
bool hasActorInfo() const
Check if actor information is available.
int count() const
Get number of selected items.
vtkPolyData * primaryPolyData() const
Get the primary (front-most) polyData.
vtkActor * primaryActor() const
Get the primary (front-most) actor.
void setActorInfo(vtkActor *actor, vtkPolyData *polyData, double zValue=1.0)
Set actor information (single actor case)
static cvSelectionData combineAND(const cvSelectionData &a, const cvSelectionData &b)
Combine two selections with AND operation.
ComparisonOp
Comparison operators.
cvSelectionData filterByAttributeRange(vtkPolyData *polyData, const cvSelectionData &input, const QString &attributeName, double minValue, double maxValue)
Filter by attribute value range.
static QStringList getAttributeNames(vtkPolyData *polyData, bool pointData=true)
Get attribute names available in polyData.
cvSelectionData filterByDistanceFromPoint(vtkPolyData *polyData, const cvSelectionData &input, double x, double y, double z, double minDistance, double maxDistance)
Filter by distance from point.
cvSelectionData filterByBoundingBox(vtkPolyData *polyData, const cvSelectionData &input, const double bounds[6])
Filter by bounding box.
cvSelectionData filterByAttributeComparison(vtkPolyData *polyData, const cvSelectionData &input, const QString &attributeName, ComparisonOp op, double value)
Filter by attribute comparison.
static cvSelectionData combineOR(const cvSelectionData &a, const cvSelectionData &b)
Combine two selections with OR operation.
~cvSelectionFilter() override
cvSelectionData filterByArea(vtkPolyData *polyData, const cvSelectionData &input, double minArea, double maxArea)
Filter cells by area.
cvSelectionFilter(QObject *parent=nullptr)
static cvSelectionData invert(vtkPolyData *polyData, const cvSelectionData &input)
Invert selection (NOT operation)
cvSelectionData filterByNeighborCount(vtkPolyData *polyData, const cvSelectionData &input, int minNeighbors, int maxNeighbors)
Filter by neighbor count (topology)
cvSelectionData filterByNormalAngle(vtkPolyData *polyData, const cvSelectionData &input, double refX, double refY, double refZ, double minAngleDeg, double maxAngleDeg)
Filter by normal angle relative to reference direction.