\documentclass{carve} \begin{document} \title{Carve Documentation} \author{Tobias Sargeant} \frontmatter \maketitle \cleardoublepage\tableofcontents \cleardoublepage\listoffigures \cleardoublepage\listoftables \cleardoublepage\lstlistoflistings \mainmatter \chapter{Representation of polyhedra} Carve polyhedra are defined by collections of vertices (instances of \code{carve::poly::Vertex}) that define points in 3-dimensional space, and collections of faces (instances of \code{carve::poly::Face}) that define the connectivity of vertices. Because faces refer to vertices by pointer, vertex identity is determined by address rather than by location in 3-dimensional space. Faces are oriented anticlockwise in a right handed coordinate system. Although a face may consist of more than three vertices, all vertices of any given face must lie on a single plane. A polyhedron defined by a set of faces and vertices consists of one or more connected surfaces. The decomposition of a set of faces into surfaces is computed automatically, and shared vertices (\fref{fig:polyhedron-shared-vertex}) and edges (\fref{fig:polyhedron-shared-edge}) are handled correctly. A polyhedron may not, however, be self intersecting. Each surface is either ``closed'' or ``open''. A closed surface obeys the property that for every edge (determined by a pair of consecutive vertices forming part of a face) there exists an edge of the opposite orientation that is part of some other face. A closed surface bounds a non-zero (possibly infinite) volume of space. The space defined by a surface depends upon the orientation of its defining faces. By inverting the vertex order of all faces of a closed surface, the complementary volume is created. For example, a cube with faces ordered clockwise in a right-handed coordinate system describes the infinite volume consisting of all space except that delimited by the cube. Used carefully, more than one closed surface may be combined to create a shell. A surface representing an infinite volume enclosed within a surface representing a finite volume represents a hollow solid, and such solids are handled correctly during CSG operations. \begin{figure} \begin{center}\includegraphics[width=3.453in]{polyhedra/shared-vertex.png}\end{center} \caption{A polyhedron consisting of two surfaces sharing a vertex} \label{fig:polyhedron-shared-vertex} \end{figure} \begin{figure} \begin{center}\includegraphics[width=3.453in]{polyhedra/shared-edge.png}\end{center} \caption{A polyhedron consisting of two surfaces sharing an edge} \label{fig:polyhedron-shared-edge} \end{figure} \begin{section}{Construction of Polyhedra} A polyhedron may be constructed in a number of ways. In the first case (Listing \ref{code:poly-ctor-1}) a vector of faces and a vector of vertices is provided. The vertices pointed to by the faces in \code{\_faces} must be members of the vector of vertices, \code{\_vertices}. Ownership is taken of the face and vertex vectors, so as to avoid unnecessary copies. On return, both vectors will be empty. \begin{lstlisting}[language=C++,float,caption=\code{carve::poly::Polyhedron} constructor 1,label=code:poly-ctor-1] carve::poly::Polyhedron( std::vector &_faces, std::vector &_vertices, bool _recalc = false); \end{lstlisting} \begin{lstlisting}[language=C++,float,caption=\code{carve::poly::Polyhedron} constructor 2,label=code:poly-ctor-2] carve::poly::Polyhedron( std::vector &_faces, bool _recalc = false); \end{lstlisting} \begin{lstlisting}[language=C++,float,caption=\code{carve::poly::Polyhedron} constructor 3,label=code:poly-ctor-3] carve::poly::Polyhedron( std::list &_faces, bool _recalc = false); \end{lstlisting} \begin{lstlisting}[language=C++,float,caption=\code{carve::poly::Polyhedron} constructor 4,label=code:poly-ctor-4] carve::poly::Polyhedron( const std::vector &vertices, int n_faces, const std::vector &face_indices); \end{lstlisting} \begin{lstlisting}[float,language=C++,caption=Constructing a cube directly,label=code:cube-direct] #include carve::poly::Polyhedron *makeCube( const carve::math::Matrix &t = carve::math::Matrix()) { std::vector verts; std::vector faces; verts.reserve(8); faces.reserve(6); verts.push_back( carve::poly::Vertex(t * carve::geom::VECTOR(+1.0, +1.0, +1.0))); verts.push_back( carve::poly::Vertex(t * carve::geom::VECTOR(-1.0, +1.0, +1.0))); verts.push_back( carve::poly::Vertex(t * carve::geom::VECTOR(-1.0, -1.0, +1.0))); verts.push_back( carve::poly::Vertex(t * carve::geom::VECTOR(+1.0, -1.0, +1.0))); verts.push_back( carve::poly::Vertex(t * carve::geom::VECTOR(+1.0, +1.0, -1.0))); verts.push_back( carve::poly::Vertex(t * carve::geom::VECTOR(-1.0, +1.0, -1.0))); verts.push_back( carve::poly::Vertex(t * carve::geom::VECTOR(-1.0, -1.0, -1.0))); verts.push_back( carve::poly::Vertex(t * carve::geom::VECTOR(+1.0, -1.0, -1.0))); faces.push_back( new Face(&verts[0], &verts[1], &verts[2], &verts[3])); faces.push_back( new Face(&verts[7], &verts[6], &verts[5], &verts[4])); faces.push_back( new Face(&verts[0], &verts[4], &verts[5], &verts[1])); faces.push_back( new Face(&verts[1], &verts[5], &verts[6], &verts[2])); faces.push_back( new Face(&verts[2], &verts[6], &verts[7], &verts[3])); faces.push_back( new Face(&verts[3], &verts[7], &verts[4], &verts[0])); // note that carve::poly::Polyhedron takes ownership of face // pointers and the contents of the vertex array. return new carve::poly::Polyhedron(faces, vertices); } \end{lstlisting} \begin{lstlisting}[float,language=C++,caption=Constructing a cube using {\codefont carve::input::PolyhedronData}] #include #include carve::poly::Polyhedron *makeCube( const carve::math::Matrix &t = carve::math::Matrix()) { carve::input::PolyhedronData data; data.addVertex(t * carve::geom::VECTOR(+1.0, +1.0, +1.0)); data.addVertex(t * carve::geom::VECTOR(-1.0, +1.0, +1.0)); data.addVertex(t * carve::geom::VECTOR(-1.0, -1.0, +1.0)); data.addVertex(t * carve::geom::VECTOR(+1.0, -1.0, +1.0)); data.addVertex(t * carve::geom::VECTOR(+1.0, +1.0, -1.0)); data.addVertex(t * carve::geom::VECTOR(-1.0, +1.0, -1.0)); data.addVertex(t * carve::geom::VECTOR(-1.0, -1.0, -1.0)); data.addVertex(t * carve::geom::VECTOR(+1.0, -1.0, -1.0)); data.addFace(0, 1, 2, 3); data.addFace(7, 6, 5, 4); data.addFace(0, 4, 5, 1); data.addFace(1, 5, 6, 2); data.addFace(2, 6, 7, 3); data.addFace(3, 7, 4, 0); return data.create(); } \end{lstlisting} \end{section} \chapter{CSG Operations} The \code{carve::csg::CSG} class is responsible for managing CSG calculations. It provides methods for CSG binary computations between both closed and open polyhedra, as well as divison of polyhedra by their common line of intersection. The operation computed by methods of the \code{carve::csg::CSG} class may be chosen from the standard primitive binary operations or may be defined by the caller. The CSG computation may be influenced by the registration of hook objects that can be used to perform such tasks as triangulation of result faces and transfer and interpolation of attributes from source polyhedra to the result. \begin{section}{CSG Operations on Closed Manifolds} The \code{compute} method of \code{carve::csg::CSG} has two prototypes: \begin{lstlisting}[language=C++] carve::poly::Polyhedron *compute( const carve::poly::Polyhedron *a, const carve::poly::Polyhedron *b, carve::csg::CSG::OP op, carve::csg::V2Set *shared_edges = NULL, carve::csg::CSG::CLASSIFY_TYPE classify_type = CLASSIFY_NORMAL); \end{lstlisting} \begin{lstlisting}[language=C++] carve::poly::Polyhedron *compute( const carve::poly::Polyhedron *a, const carve::poly::Polyhedron *b, carve::csg::CSG::Collector &collector, carve::csg::V2Set *shared_edges = NULL, carve::csg::CSG::CLASSIFY_TYPE classify_type = CLASSIFY_NORMAL); \end{lstlisting} These methods compute a boolean operation between polyhedra \code{a} and \code{b}. In the first case, the operation is determined by the enumeration \code{carve::csg::CSG::OP}, which can take the values: \begin{itemize} \item \code{UNION} \item \code{INTERSECTION} \item \code{A\_MINUS\_B} \item \code{B\_MINUS\_A} \item \code{SYMMETRIC\_DIFFERENCE} \end{itemize} \noindent Results for these boolean operations are shown in \tref{tab:input-polyhedra} and \tref{tab:csg-results}. \begin{table} \begin{center} \begin{tabular}{m{2.2in}m{3in}} \includegraphics[width=2.167in]{ops/a.png} & \vfill Manifold A \vfill \\ \includegraphics[width=2.167in]{ops/b.png} & Manifold B \\ \end{tabular} \end{center} \caption{Input polyhedra.} \label{tab:input-polyhedra} \end{table} \begin{table} \begin{center} \begin{tabular}{m{2.2in}m{3in}} \includegraphics[width=2.167in]{ops/a_union_b.png} & Operation: Union (A | B) \newline Enumeration: \code{carve::CSG::UNION} \\ \includegraphics[width=2.167in]{ops/a_intersection_b.png} & Operation: Intersection (A \& B) \newline Enumeration: \code{carve::CSG::INTERSECTION} \\ \includegraphics[width=2.167in]{ops/a_minus_b.png} & Operation: Difference (A - B) \newline Enumeration: \code{carve::CSG::A\_MINUS\_B} \\ \includegraphics[width=2.167in]{ops/b_minus_a.png} & Operation: Difference (B - A) \newline Enumeration: \code{carve::CSG::B\_MINUS\_A} \\ \includegraphics[width=2.167in]{ops/a_xor_b.png} & Operation: Symmetric Difference (A B) \newline Enumeration: \code{carve::CSG::SYMMETRIC\_DIFFERENCE} \\ \end{tabular} \end{center} \caption{The result of predefined CSG operations.} \label{tab:csg-results} \end{table} In the second case, the result is determined by a custom collector. Custom collectors allow the caller to programatically define which regions of the intersected input polyhedra appear in the output. The \code{shared\_edges} parameter provides a way to access the computed set of edges that defines the point of intersection of the two polyhedra. The algorithm used to classify connected components of the intersected polyhedra is determined by the parameter \code{classify\_type}. The type of \code{classify\_type} is an enumeration taking values from the set \{\code{CLASSIFY\_NORMAL}, \code{CLASSIFY\_EDGE}\}. The classifier is responsible for classifying portions of each polyhedron bounded by the line of intersection as either: \begin{itemize} \item \code{carve::csg::FACE\_OUT} \newline The group is outside the space defined by the opposing polyhedron. \item \code{carve::csg::FACE\_IN} \newline The group is inside the space defined by the opposing polyhedron. \item \code{carve::csg::FACE\_ON\_ORIENT\_IN} \newline The group is lying on the surface of the opposing polyhedron, oriented towards its interior. \item \code{carve::csg::FACE\_ON\_ORIENT\_OUT} \newline The group is lying on the surface of the opposing polyhedron, oriented towards its exterior. \end{itemize} \noindent with respect to (each surface of) the opposing polyhedron. \end{section} \begin{section}{CSG Operations on Open Manifolds} \end{section} \chapter{Attribute Interpolation} \begin{lstlisting}[float,language=C++,caption=Associating texture coordinates with a cube] #include struct tex_t { float u, v; tex_t() : u(0.0f), v(0.0f) { } tex_t(float _u, float _v) : u(_u), v(_v) { } }; // interpolated attributes must support scalar multiplication. tex_t operator*(double s, const tex_t &t) { return tex_t(t.u * s, t.v * s); } // interpolated attributes must support operator+=. tex_t &operator+=(tex_t &t1, const tex_t &t2) { t1.u += t2.u; t1.v += t2.v; return t1; } void associateTextureVertices( carve::poly::Polyhedron *cube, carve::interpolate::FaceVertexAttr &fv_tex) { fv_tex.setAttribute(cube->faces[0], 0, tex_t(1.0f, 1.0f)); fv_tex.setAttribute(cube->faces[0], 1, tex_t(0.0f, 1.0f)); fv_tex.setAttribute(cube->faces[0], 2, tex_t(0.0f, 0.0f)); fv_tex.setAttribute(cube->faces[0], 3, tex_t(1.0f, 0.0f)); // ... continue to record other texture coordinates by // face pointer and vertex number. } \end{lstlisting} \begin{lstlisting}[float,language=C++,caption=Interpolating texture coordinates during a CSG operation] #include carve::poly::Polyhedron *doCSG() { carve::poly::Polyhedron *result; carve::poly::Polyhedron *cube_1 = makeCube(); carve::poly::Polyhedron *cube_2 = makeCube( carve::math::Matrix::ROT(.4, .2, .3, .4)); carve::interpolate::FaceVertexAttr fv_tex; associateTextureVertices(cube_1, fv_tex); carve::csg::CSG csg; fv_tex.installHooks(csg); result = csg.compute(cube_1, cube_2, carve::csg::CSG::A_MINUS_B); } \end{lstlisting} \appendix \backmatter \end{document}