5 Geometry base class implements pre and post states of geometry as well as two types of transforms.
7 - `ApplyToData`: One transform directly applies to both pre and post geometry immediately.
8 - `ConcatenateToTransform`: The other transform applies to pre geometry in order to produce post geometry.
10 First see how to get both post and pre vertices of a PointSet. A similar convention is used among the other geometries (even post and pre values of primitive shapes)
13 std::shared_ptr<PointSet> myPoints = MeshIO::read<PointSet>("file path here");
14 auto preVerticesPtr = myPoints->getVertexPositions(Geometry::DataType::PostTransform);
15 auto postVerticesPtr = myPoints->getVertexPositions(Geometry::DataType::PreTransform);
18 With any geometry, apply transforms to either pre and/or post.
21 std::shared_ptr<SurfaceMesh> mySurfaceMesh = MeshIO::read<SurfaceMesh>("file path here");
23 // Apply immediately to data, transforming both post and pre vertices
24 mySurfaceMesh->translate(Vec3d(1.0, 0.0, 0.0), Geometry::TransformType::ApplyToData);
25 mySurfaceMesh->rotate(Vec3d(0.0, 1.0, 0.0), 1.57, Geometry::TransformType::ApplyToData);
26 // Also available are Quatd (quaternion) and Mat3d(3x3 matrix) rotations
27 mySurfaceMesh->scale(Vec3d(2.0, 2.0, 2.0), Geometry::TransformType::ApplyToData);
28 mySurfaceMesh->transform(myTransform, Geometry::TransformType::ApplyToData);
30 // Concat the transform, when get is later called it will update (lazy update)
31 mySurfaceMesh->translate(Vec3d(1.0, 0.0, 0.0), Geometry::TransformType::ConcatenateToTransform);
32 mySurfaceMesh->rotate(Vec3d(0.0, 1.0, 0.0), 1.57, Geometry::TransformType::ConcatenateToTransform);
33 mySurfaceMesh->scale(Vec3d(2.0, 2.0, 2.0), Geometry::TransformType::ConcatenateToTransform);
34 mySurfaceMesh->transform(myTransform, Geometry::TransformType::ConcatenateToTransform);
37 The advantage of the `ConcatenateToTransform` is transforms are not applied four times and the initial vertices are preserved.
39 ## Analytical & Implicit
41 Analytical geometries are basic primitives such as sphere, box, capsule, plane, etc. They are given by parameters. All analytical geometries are also implicit. Implicit geometries provide a function value for all points in space (usually signed or unsigned distance).
43 Similarly they have post and pre geometry as well as a transform, but some transforms may not apply fully. For instance, a sphere cannot anisotropically scale or shear.
46 auto sphere = std::make_shared<Sphere>(Vec3d(0.0, 3.0, 0.0), 2.0); // center, radius
47 sphere->translate(Vec3d(0.0, 1.0, 0.0)); // Default is ConcatenateToTransform
48 Vec3d newPos = sphere->getPosition(Geometry::DataType::PostTransform);
49 // newPos is (0.0, 4.0, 0.0)
51 double signedDistance = sphere->getFunctionValue(Vec3d(0.0, 0.0, 0.0));
52 // signedDistance is 2.0
53 signedDistance = sphere->getFunctionValue(Vec3d(0.0, 2.0, 0.0));
54 // signedDistance is 0.0
55 signedDistance = sphere->getFunctionValue(Vec3d(0.0, 4.0, 0.0));
56 // signedDistance is -2.0
61 PointSets represent a collection of points. A set of vertices. They can be created like so:
64 auto pointSet = std::make_shared<PointSet>();
66 auto verticesPtr = std::make_shared<VecDataArray<double, 3>>(2);
67 VecDataArray<double, 3>& vertices = *verticesPtr;
68 vertices[0] = Vec3d(0.0, 1.0, 0.0);
69 vertices[1] = Vec3d(0.0, 2.0, 2.0);
71 pointSet->initialize(verticesPtr);
74 PointSet can also come with named per vertex "attributes". These are abstract arrays. The abstract arrays could be single per vertex scalars, 3d vectors (normals, displacements, velocities), matrices, 2d texture coordinates, etc. There are then a set of designated/labeled/active attributes for normals, texture coordinates, tangents, and scalar. This gives context so other codes can just ask the PointSet what the active normals, texture coordinates, etc are.
76 Add and access an attribute to the same geometry:
79 auto scalarsPtr = std::make_shared<DataArray<double>>(2);
80 DataArray<double>& scalars = *scalarsPtr;
84 pointSet->setVertexAttribute("myScalars", scalarsPtr);
86 // Can later access by name, or designate the active scalars to provide context
87 pointSet->setVertexScalars("myScalars");
90 ## LineMesh, SurfaceMesh, TetrahedralMesh, & HexahedralMesh
92 Subclassing PointSet there are `LineMesh`, `SurfaceMesh`, `TetrahedralMesh`, & `HexahedralMesh`. These are all mostly the same stemming off a AbstractCellMesh base class giving indices/order of the vertices per cell. For instance, a triangle has 3 integer indices that specify the location of the 3 vertices involved in the triangle (from the vertex array). Similarly a LineMesh has 2, TetrahedralMesh has 4, HexahedralMesh has 8.
95 auto verticesPtr = std::make_shared<VecDataArray<double, 3>>(3);
96 VecDataArray<double, 3>& vertices = *verticesPtr;
97 vertices[0] = Vec3d(0.0, 1.0, 0.0);
98 vertices[1] = Vec3d(-1.0, 0.0, 0.0);
99 vertices[2] = Vec3d(0.0, 0.0, 1.0);
101 auto cellsPtr = std::make_shared<VecDataArray<int, 3>>(1);
102 VecDataArray<int, 3>& cells = *cellsPtr;
103 cells[0] = Vec3i(0, 1, 2);
105 // Create SurfaceMesh with given buffers
106 SurfaceMesh surfMesh;
107 surfMesh.initialize(verticesPtr, cellsPtr);
110 Similar to vertex data you may have per cell attributes. That is a normal, scalar, matrix, etc specified per cell given a name and labeled/designated as active.
112 Other notable functions:
114 - SurfaceMesh::computeVertexNormals
115 - SurfaceMesh::computeTriangleNormals
116 - SurfaceMesh::computeVertexTangents
117 - SurfaceMesh::computeTriangleTangents
118 - TetrahedralMesh::extractSurfaceMesh
121 <img src="media/Heart.png" alt="Heart Tetrahedral Mesh Cut in Half" width="600"/>
126 It is often useful to share the vertex buffer or other large buffers between meshes.
129 auto verticesPtr = std::make_shared<VecDataArray<double, 3>>(3);
130 VecDataArray<double, 3>& vertices = *verticesPtr;
131 vertices[0] = Vec3d(0.0, 1.0, 0.0);
132 vertices[1] = Vec3d(-1.0, 0.0, 0.0);
133 vertices[2] = Vec3d(0.0, 0.0, 1.0);
134 vertices[3] = Vec3d(0.0, 0.0, -1.0);
136 auto cellsPtr1 = std::make_shared<VecDataArray<int, 3>>(1);
137 VecDataArray<int, 3>& cells1 = *cellsPtr1;
138 cells1[0] = Vec3i(0, 1, 2);
140 auto cellsPtr2 = std::make_shared<VecDataArray<int, 4>>(1);
141 VecDataArray<int, 4>& cells2 = *cellsPtr2;
142 cells2[0] = Vec4i(3, 1, 2, 0);
144 // Create two meshes with differing index buffers
145 SurfaceMesh surfMesh;
146 surfMesh.initialize(verticesPtr, cellsPtr1);
148 TetrahedralMesh tetMesh;
149 tetMesh.initialize(verticesPtr, cellsPtr2);
154 An ImageData has points that are not explicitly computed (unless asked for), given implicity via origin, spacing, and dimensions of an image. It supports 3d and 2d images.
157 auto imageData = std::make_shared<ImageData>();
159 IMSTK_DOUBLE, // Scalar Type
160 1, // Number of Components
161 Vec3i(10, 10, 10), // XYZ Dimensions
162 Vec3d(1.0, 1.0, 1.0), // XYZ Spacing
163 Vec3d(0.0, 0.0, 0.0)); // XYZ origin
165 // Another type of allocation, 3 tuple, 2d
167 IMSTK_DOUBLE, // Scalar Type
168 3, // Number of Components
169 Vec3i(10, 10, 1), // XYZ Dimensions
170 Vec3d(1.0, 1.0, 1.0), // XYZ Spacing
171 Vec3d(0.0, 0.0, 0.0)); // XYZ origin
177 std::shared_ptr<AbstractDataArray> myAbstractScalars = myImage->getScalars();
178 auto myScalarArray = std::dynamic_pointer_cast<DataArray<double>>(imageData->getScalars());
180 std::shared_ptr<AbstractDataArray> myAbstractScalars = myImage->getScalars();
181 auto myScalarArray = std::dynamic_pointer_cast<VecDataArray<double, 3>>(imageData->getScalars());
185 <img src="media/vhp.png" alt="Femur Signed Distance Function" width="600"/>
188 ImageData does not support transformations due to sampling performance purposes. You may change the origin for translational offsets and change the spacing for scaling.
190 Other notable functions:
192 - ImageData::computePoints
193 - Computes the points of the underlying PointSet for the regular grid
195 - Returns same image of differing/specified type
196 - ImageData::getScalarIndex
197 - Index into the 1d array for 3d coordinates.
199 ## SignedDistanceField
201 SignedDistanceField's are types of ImplicitGeometries. They provide signed distances via trilinear interpolation of a field of values. That is, given a uniform (or non-uniform) field of values it finds the the voxel (8 nearest neighbors) and linearly interpolates the value to give you the value at the point.
204 <img src="media/FemurSDF.png" alt="Femur Signed Distance Function" width="600"/>
208 <img src="media/sdf.png" alt="Signed Distance Function" width="600"/>
212 // This shows how to use a filter to compute the SDF of a SurfaceMesh (slow)
213 auto myWatertightMesh = MeshIO::read<SurfaceMesh>("file path here");
215 SurfaceMeshDistanceTransform computeSdf;
216 computeSdf.setInputMesh(myWatertightMesh);
217 computeSdf.setDimensions(100, 100, 100);
220 // Construct SDFs with images
221 auto sdf = std::make_shared<SignedDistanceField>(computeSdf.getOutputImage());
224 double signedDist = sdf->getFunctionValue(position);
227 You may set a scale to the SDF as well which is multiplied with the return value. One should be careful not to anistropically scale an SDF.
229 The pro to SDFs is that they give some of the fastest collision available just by sampling the 3d image. (normalized gradient = normal, value = penetration depth). Additionally one can inverse transform into them for dynamic objects. The downside is memory consumption and topology changes.
231 ## CompositeImplicitGeometry
233 CompositeImplicitGeometries allow you to do boolean operations between implicit geometries dynamically. That is, it does not precompute anything but when asked what the signed distance is given a point, it does min & max operations to compute unions, intersections, or differences on the fly.
235 For example, you can punch and move a hole or grow a capsule in a signed distance field in real time, without any added cost. You are not updating the field. Simply doing some mins/maxes when getting the SDF value. You can also rasterize this back into a field.
238 // Subtract the sphere from the plane to make a crater
239 auto planeGeom = std::make_shared<Plane>();
240 planeGeom->setWidth(40.0);
241 auto sphere = std::make_shared<Sphere>();
242 sphereGeom->setRadius(25.0);
243 sphereGeom->setPosition(0.0, 10.0, 0.0);
245 auto compGeom = std::make_shared<CompositeImplicitGeometry>();
246 compGeom->addImplicitGeometry(planeGeom, CompositeImplicitGeometry::GeometryBoolType::Union);
247 compGeom->addImplicitGeometry(sphereGeom, CompositeImplicitGeometry::GeometryBoolType::Difference);
251 <img src="media/compositeGeo.png" alt="Composite Implicit Geometry" width="600"/>