diff --git a/Isosurfacing_3/doc/Isosurfacing_3/Isosurfacing_3.txt b/Isosurfacing_3/doc/Isosurfacing_3/Isosurfacing_3.txt index 9778d0ccf99..c3aff4fbefd 100644 --- a/Isosurfacing_3/doc/Isosurfacing_3/Isosurfacing_3.txt +++ b/Isosurfacing_3/doc/Isosurfacing_3/Isosurfacing_3.txt @@ -12,7 +12,7 @@ namespace CGAL { \cgalFigureCaptionBegin{IsosurfacingTeaser} -Generating a surface from a 3D gray level image using Marching Cubes (3D input image: qim.dk) +Generating a surface from a 3D gray level image using Marching Cubes (3D input image from qim.dk) \cgalFigureCaptionEnd \section SecIsoSurfacingIntroduction Introduction @@ -24,19 +24,19 @@ the level set forms a surface. In the following, we shall refer to the the field of scalar values as the value field. "Isosurfacing", also known as "isosurface extraction" or "contouring", is the process of constructing the isosurface corresponding to a given value field and isovalue. -Isosurfacing is often needed for volume visualization and for the simulation of physical phenomena. +%Isosurfacing is often needed for volume visualization and for the simulation of physical phenomena. This \cgal package provides methods to extract isosurfaces from 3D value fields. These contouring techniques rely on the partition of the 3D space and of the field to construct an approximate representation of the isosurface. The 3D value field can be described through various representations: an implicit function, -an interpolated set of discrete sampling values, etc. (see \ref SecIsosurfacingExamples). +an interpolated set of discrete sampling values, a 3D image, etc. (see \ref SecIsosurfacingExamples). The isovalue is user-defined. The output is a polygon soup, made either of triangles or quads depending on the method, and may consist of a single connected component, or multiple, disjoint components. Note that due to the inherent approximate nature of these discrete methods that only sample 3D value fields, parts of the "true" isosurface may be missing from the output, and the output may contain artifacts -that re not present in the true isosurface. +that are not present in the true isosurface. \section SecIsosurfacingMethods Isosurfacing Methods @@ -104,6 +104,11 @@ does not intersect the domain boundaries. \cgalFigureCaptionBegin{IsosurfacingMCTMC} Marching Cubes vs Topologically Correct Marching Cubes. +Some vertex values can represent complex surfaces within a single cell: on the left, +the values at the vertices of the cell have been interpolated within the cell and correspond +to a tunnel-like surface. This surface is not correctly recovered when applying the cases of +the Marching Cubes algorithm to the cell: 2 independent disk-like sheets are created (middle). +However, by inserting new vertices, TMC correctly captures the topology within a single cell (right). \cgalFigureCaptionEnd \subsection SubSecDualContouring Dual Contouring (DC) @@ -148,11 +153,13 @@ of the output surface mesh. | ---- | ---- | ---- | ---- | ---- | ---- | MC | Triangles | no | no | no | no | TMC | Triangles | yes | yes | yes | no | - DC | Polygons | no | no | no | yes (not guaranteed) | - + DC | Quads** | no | no | no | yes (not guaranteed) | (* assuming the isosurface does not exit the specified bounding box of the input 3D domain) +(** which can be triangulated on-the-fly) + + Note that the output mesh has boundaries when the isosurface intersects the domain boundaries, regardless of the method (see \cgalFigureRef{IsosurfacingOpen}). @@ -170,12 +177,12 @@ Output meshes can have boundaries when the isosurface intersects the domain boun The following functions are the main entry points to the isosurfacing algorithms: -These three free functions share the same signature: +All these free functions share the same signature: \code{.cpp} template FT return std::exp(-1.5 * ((p.x() - 0.2) * (p.x() - 0.2) + (p.y() - 0.2) * (p.y() - 0.2) + (p.z() - 0.2) * (p.z() - 0.2))) + std::exp(-1.5 * ((p.x() + 0.2) * (p.x() + 0.2) + (p.y() + 0.2) * (p.y() + 0.2) + (p.z() + 0.2) * (p.z() + 0.2))) + std::exp(-1.5 * ((p.x() - 0.4) * (p.x() - 0.4) + (p.y() + 0.4) * (p.y() + 0.4) + (p.z() - 0.4) * (p.z() - 0.4))) + - std::exp(-6 * ((p.x() - 0.1) * (p.x() - 0.1) + (p.y() - 0.1) * (p.y() - 0.1))) + // Tentacle along z-axis - std::exp(-6 * ((p.y() + 0.1) * (p.y() + 0.1) + (p.z() + 0.1) * (p.z() + 0.1))) + // Tentacle along x-axis - std::exp(-6 * ((p.x() + 0.1) * (p.x() + 0.1) + (p.z() - 0.1) * (p.z() - 0.1))) - // Tentacle along y-axis + std::exp(-6 * ((p.x() - 0.1) * (p.x() - 0.1) + (p.y() - 0.1) * (p.y() - 0.1))) + + std::exp(-6 * ((p.y() + 0.1) * (p.y() + 0.1) + (p.z() + 0.1) * (p.z() + 0.1))) + + std::exp(-6 * ((p.x() + 0.1) * (p.x() + 0.1) + (p.z() - 0.1) * (p.z() - 0.1))) - 0.3; }; @@ -59,9 +59,9 @@ auto blobby_gradient = [](const Point& p) -> Vector const FT g1 = -3 * std::exp(-1.5 * ((p.x() - 0.2) * (p.x() - 0.2) + (p.y() - 0.2) * (p.y() - 0.2) + (p.z() - 0.2) * (p.z() - 0.2))); const FT g2 = -3 * std::exp(-1.5 * ((p.x() + 0.2) * (p.x() + 0.2) + (p.y() + 0.2) * (p.y() + 0.2) + (p.z() + 0.2) * (p.z() + 0.2))); const FT g3 = -3 * std::exp(-1.5 * ((p.x() - 0.4) * (p.x() - 0.4) + (p.y() + 0.4) * (p.y() + 0.4) + (p.z() - 0.4) * (p.z() - 0.4))); - const FT g4 = -12 * std::exp(-6 * ((p.x() - 0.1) * (p.x() - 0.1) + (p.y() - 0.1) * (p.y() - 0.1))); // Gradient for z-axis tentacle - const FT g5 = -12 * std::exp(-6 * ((p.y() + 0.1) * (p.y() + 0.1) + (p.z() + 0.1) * (p.z() + 0.1))); // Gradient for x-axis tentacle - const FT g6 = -12 * std::exp(-6 * ((p.x() + 0.1) * (p.x() + 0.1) + (p.z() - 0.1) * (p.z() - 0.1))); // Gradient for y-axis tentacle + const FT g4 = -12 * std::exp(-6 * ((p.x() - 0.1) * (p.x() - 0.1) + (p.y() - 0.1) * (p.y() - 0.1))); + const FT g5 = -12 * std::exp(-6 * ((p.y() + 0.1) * (p.y() + 0.1) + (p.z() + 0.1) * (p.z() + 0.1))); + const FT g6 = -12 * std::exp(-6 * ((p.x() + 0.1) * (p.x() + 0.1) + (p.z() - 0.1) * (p.z() - 0.1))); return Vector(g1 * (p.x() - 0.2) + g2 * (p.x() + 0.2) + g3 * (p.x() - 0.4) + g4 * (p.x() - 0.1) + g6 * (p.x() + 0.1), g1 * (p.y() - 0.2) + g2 * (p.y() + 0.2) + g3 * (p.y() + 0.4) + g4 * (p.y() - 0.1) + g5 * (p.y() + 0.1),