20
20
#include " itkInputMesh.h"
21
21
#include " itkOutputMesh.h"
22
22
#include " itkSupportInputMeshTypes.h"
23
+ #include " itkInputTextStream.h"
24
+ #include " itkPolyLineCell.h"
23
25
24
- #include " itkmeshToGeogramMesh.h"
25
- #include " itkgeogramMeshToMesh.h"
26
+ #include " MeshPlaneIntersect.hpp"
26
27
27
- #include < geogram/mesh/mesh_remesh.h>
28
- #include < geogram/mesh/mesh_geometry.h>
29
- #include < geogram/basic/command_line.h>
30
- #include < geogram/basic/command_line_args.h>
31
-
32
- #include < geogram/basic/common.h>
33
- #include < geogram/basic/process.h>
34
- #include < geogram/basic/logger.h>
35
- #include < geogram/basic/command_line.h>
28
+ #include " glaze/glaze.hpp"
36
29
37
30
template <typename TMesh>
38
- int repairMesh (itk::wasm::Pipeline &pipeline, const TMesh *inputMesh)
31
+ int sliceMesh (itk::wasm::Pipeline &pipeline, const TMesh *inputMesh)
39
32
{
40
33
using MeshType = TMesh;
41
34
constexpr unsigned int Dimension = MeshType::PointDimension;
42
35
using PixelType = typename MeshType::PixelType;
36
+ using PointIdentifierType = typename MeshType::PointIdentifier;
37
+ using CellIdentifierType = typename MeshType::CellIdentifier;
38
+
39
+ using FloatType = typename MeshType::CoordRepType;
40
+ using IndexType = typename MeshType::CellIdentifier;
41
+ using MeshPlaneIntersectType = MeshPlaneIntersect<FloatType, IndexType>;
42
+ using Vec3D = typename MeshPlaneIntersectType::Vec3D;
43
+ using Face = typename MeshPlaneIntersectType::Face;
44
+ using Plane = typename MeshPlaneIntersectType::Plane;
45
+ using Planes = std::vector<Plane>;
46
+
47
+ using OutputMeshType = itk::Mesh<uint16_t , Dimension>;
48
+ using CellAutoPointer = typename OutputMeshType::CellAutoPointer;
49
+ using PolyLineCellType = itk::PolyLineCell<typename OutputMeshType::CellType>;
50
+ using PointDataContainer = typename OutputMeshType::PointDataContainer;
51
+ using CellDataContainer = typename OutputMeshType::CellDataContainer;
43
52
44
53
pipeline.get_option (" input-mesh" )->required ()->type_name (" INPUT_MESH" );
45
54
46
- double numberPointsPercent = 75.0 ;
47
- pipeline.add_option (" --number-points" , numberPointsPercent, " Number of points as a percent of the bounding box diagonal. Output may have slightly more points." );
48
-
49
- double triangleShapeAdaptation = 1.0 ;
50
- pipeline.add_option (" --triangle-shape-adaptation" , triangleShapeAdaptation, " Triangle shape adaptation factor. Use 0.0 to disable." );
51
-
52
- double triangleSizeAdaptation = 0.0 ;
53
- pipeline.add_option (" --triangle-size-adaptation" , triangleSizeAdaptation, " Triangle size adaptation factor. Use 0.0 to disable." );
54
-
55
- uint32_t normalIterations = 3 ;
56
- pipeline.add_option (" --normal-iterations" , normalIterations, " Number of normal smoothing iterations." );
57
-
58
- uint32_t lloydIterations = 5 ;
59
- pipeline.add_option (" --lloyd-iterations" , lloydIterations, " Number of Lloyd relaxation iterations." );
60
-
61
- uint32_t newtonIterations = 30 ;
62
- pipeline.add_option (" --newton-iterations" , newtonIterations, " Number of Newton iterations." );
63
-
64
- uint32_t newtonM = 7 ;
65
- pipeline.add_option (" --newton-m" , newtonM, " Number of Newton evaluations per step for Hessian approximation." );
66
-
67
- uint64_t lfsSamples = 10000 ;
68
- pipeline.add_option (" --lfs-samples" , lfsSamples, " Number of samples for size adaptation if triangle size adaptation is not 0.0." );
55
+ itk::wasm::InputTextStream planesJson;
56
+ pipeline.add_option (" planes" , planesJson, " An array of plane locations to slice the mesh. Each plane is defined by an array of 'origin' and 'spacing' values." )->required ()->type_name (" INPUT_JSON" );
69
57
70
- itk::wasm::OutputMesh<MeshType > outputMesh;
71
- pipeline.add_option (" output-mesh " , outputMesh, " The output repaired mesh." )->type_name (" OUTPUT_MESH" );
58
+ itk::wasm::OutputMesh<OutputMeshType > outputMesh;
59
+ pipeline.add_option (" polylines " , outputMesh, " The output mesh comprised of polylines. Cell data indicates whether part of a closed line. Point data indicates the slice index ." )->type_name (" OUTPUT_MESH" );
72
60
73
61
ITK_WASM_PARSE (pipeline);
74
62
75
- // GEO::initialize(GEO::GEOGRAM_INSTALL_ALL);
76
-
77
- GEO::Logger::initialize (); GEO::Logger::instance ()->set_quiet (true );
78
- GEO::CmdLine::initialize ();
79
- const auto flags = GEO::GEOGRAM_INSTALL_ALL;
80
- GEO::Process::initialize (flags);
81
- GEO::CmdLine::import_arg_group (" algo" );
63
+ std::vector<Vec3D> vertices;
64
+ vertices.resize (inputMesh->GetNumberOfPoints ());
65
+ for (itk::SizeValueType i = 0 ; i < inputMesh->GetNumberOfPoints (); ++i)
66
+ {
67
+ typename MeshType::PointType point = inputMesh->GetPoint (i);
68
+ Vec3D vertex;
69
+ for (unsigned int d = 0 ; d < Dimension; d++)
70
+ {
71
+ vertex[d] = point[d];
72
+ }
73
+ vertices[i] = vertex;
74
+ }
82
75
83
- GEO::Mesh geoMesh (Dimension, false );
84
- itk::meshToGeogramMesh<MeshType>(inputMesh, geoMesh);
76
+ std::vector<Face> faces;
77
+ faces.resize (inputMesh->GetNumberOfCells ());
78
+ using CellIterator = typename MeshType::CellsContainer::ConstIterator;
79
+ CellIterator cellIterator = inputMesh->GetCells ()->Begin ();
80
+ CellIterator cellEnd = inputMesh->GetCells ()->End ();
85
81
86
- if (geoMesh. facets . nb () == 0 )
82
+ while (cellIterator != cellEnd )
87
83
{
88
- std::cerr << " The input mesh has no facets." << std::endl;
89
- return EXIT_FAILURE;
84
+ const auto cell = cellIterator.Value ();
85
+ if (cell->GetNumberOfPoints () != 3 )
86
+ {
87
+ std::cerr << " Only triangle cells are supported." << std::endl;
88
+ return EXIT_FAILURE;
89
+ }
90
+ typename MeshType::CellType::PointIdIterator pointIdIterator = cell->PointIdsBegin ();
91
+ Face face;
92
+ for (unsigned int j = 0 ; j < cell->GetNumberOfPoints (); ++j)
93
+ {
94
+ face[j] = *pointIdIterator;
95
+ ++pointIdIterator;
96
+ }
97
+ faces[cellIterator.Index ()] = face;
98
+ ++cellIterator;
90
99
}
91
100
92
- if (!geoMesh.facets .are_simplices ())
101
+ const std::string planesJsonString (std::istreambuf_iterator<char >(planesJson.Get ()), {});
102
+ auto deserializedAttempt = glz::read_json<Planes>(planesJsonString);
103
+ if (!deserializedAttempt)
93
104
{
94
- std::cerr << " The mesh needs to be simplicial. Use repair." << std::endl;
105
+ const std::string descriptiveError = glz::format_error (deserializedAttempt, planesJsonString);
106
+ std::cerr << " Failed to deserialize planes: " << descriptiveError << std::endl;
95
107
return EXIT_FAILURE;
96
108
}
109
+ const auto planes = deserializedAttempt.value ();
97
110
98
- uint64_t numberPoints = static_cast <uint64_t >(std::ceil (numberPointsPercent * 0.01 * geoMesh.vertices .nb ()));
99
-
100
- std::cout << " Remeshing with " << numberPoints << " points" << std::endl;
111
+ typename OutputMeshType::Pointer outputMeshPtr = OutputMeshType::New ();
101
112
102
- if (triangleShapeAdaptation != 0.0 )
103
- {
104
- triangleShapeAdaptation *= 0.02 ;
105
- GEO::compute_normals (geoMesh);
106
- if (normalIterations != 0 )
107
- {
108
- GEO::simple_Laplacian_smooth (geoMesh, normalIterations, true );
109
- }
110
- GEO::set_anisotropy (geoMesh, triangleShapeAdaptation);
111
- }
112
- else
113
- {
114
- geoMesh.vertices .set_dimension (3 );
115
- }
113
+ PointIdentifierType pointId = 0 ;
114
+ CellIdentifierType cellId = 0 ;
115
+ size_t sliceIndex = 0 ;
116
+ PointDataContainer *pointData = outputMeshPtr->GetPointData ();
117
+ CellDataContainer *cellData = outputMeshPtr->GetCellData ();
116
118
117
- if (triangleSizeAdaptation != 0.0 )
118
- {
119
- GEO::compute_sizing_field (geoMesh, triangleSizeAdaptation, lfsSamples);
120
- }
121
- else
119
+ typename MeshPlaneIntersectType::Mesh meshPlaneIntersect (vertices, faces);
120
+ for (const auto &plane : planes)
122
121
{
123
- GEO::AttributesManager& attributes = geoMesh. vertices . attributes ( );
124
- if (attributes. is_defined ( " weight " ) )
122
+ const auto paths = meshPlaneIntersect. Intersect (plane );
123
+ for ( const auto &path : paths )
125
124
{
126
- attributes.delete_attribute_store (" weight" );
125
+ CellAutoPointer cell;
126
+ cell.TakeOwnership (new PolyLineCellType);
127
+ PointIdentifierType polyPointId = 0 ;
128
+ const auto initialPointId = pointId;
129
+ for (const auto &point : path.points )
130
+ {
131
+ typename OutputMeshType::PointType outputPoint;
132
+ for (unsigned int d = 0 ; d < Dimension; d++)
133
+ {
134
+ outputPoint[d] = point[d];
135
+ }
136
+ outputMeshPtr->SetPoint (pointId, outputPoint);
137
+ cell->SetPointId (polyPointId++, pointId);
138
+ pointId++;
139
+ pointData->push_back (static_cast <uint16_t >(sliceIndex));
140
+ }
141
+ if (path.isClosed )
142
+ {
143
+ cell->SetPointId (polyPointId++, initialPointId);
144
+ }
145
+ outputMeshPtr->SetCell (cellId++, cell);
146
+ const uint16_t isClosed = path.isClosed ? 1 : 0 ;
147
+ cellData->push_back (isClosed);
127
148
}
149
+ ++sliceIndex;
128
150
}
129
151
130
- GEO::Mesh remesh;
131
-
132
- GEO::remesh_smooth (geoMesh, remesh, numberPoints, 0 , lloydIterations, newtonIterations, newtonM);
133
-
134
- GEO::MeshElementsFlags what = GEO::MeshElementsFlags (
135
- GEO::MESH_VERTICES | GEO::MESH_EDGES | GEO::MESH_FACETS | GEO::MESH_CELLS
136
- );
137
- geoMesh.clear ();
138
- geoMesh.copy (remesh, true , what);
139
-
140
- typename MeshType::Pointer itkMesh = MeshType::New ();
141
- itk::geogramMeshToMesh<MeshType>(geoMesh, itkMesh);
142
-
143
- outputMesh.Set (itkMesh);
152
+ outputMesh.Set (outputMeshPtr);
144
153
145
154
return EXIT_SUCCESS;
146
155
}
@@ -154,18 +163,18 @@ class PipelineFunctor
154
163
using MeshType = TMesh;
155
164
156
165
itk::wasm::InputMesh<MeshType> testMesh;
157
- pipeline.add_option (" input-mesh" , testMesh, " The input mesh" )->type_name (" INPUT_MESH" );
166
+ pipeline.add_option (" input-mesh" , testMesh, " The input triangle mesh. " )->type_name (" INPUT_MESH" );
158
167
159
168
ITK_WASM_PRE_PARSE (pipeline);
160
169
161
170
typename MeshType::ConstPointer inputMeshRef = testMesh.Get ();
162
- return repairMesh <MeshType>(pipeline, inputMeshRef);
171
+ return sliceMesh <MeshType>(pipeline, inputMeshRef);
163
172
}
164
173
};
165
174
166
175
int main (int argc, char *argv[])
167
176
{
168
- itk::wasm::Pipeline pipeline (" remesh " , " Smooth and remesh a mesh to improve quality ." , argc, argv);
177
+ itk::wasm::Pipeline pipeline (" slice-mesh " , " Slice a mesh along planes into polylines ." , argc, argv);
169
178
170
179
return itk::wasm::SupportInputMeshTypes<PipelineFunctor,
171
180
// uint8_t,
0 commit comments