INTERFACE Thicken; (* Adds layers of cells on the surface of a tetrahedral mesh. *) (* Created 1995/1996 by J. Stolfi. *) IMPORT Vertex, Face, Model, LR3, ParseParams; (* Each "Add...Layer" procedure below adds a new layer of cells on top of a given set of exposed faces. Layer structure: Let "F" be the set of ``floor'' faces which are to be covered by the layer. Let "P" be the set of edges on the perimeter of "F", i.e. the set of exposed edges that belong to exactly one face of "F". The cells added on top of "F" will be colelctively bounded by a ``wall'' of new faces, roughly perpendicular to the surface and erected on top of "P", and a ``roof'' of new faces, roughly parallel to "F". The roof may be isomorphic to "F", or may have more faces. In any case, for every exposed vertex "u" in the mesh "F", there will be a new vertex "u'" on the roof, located roughly above "u". The name of the new vertex will be the name of "u", with "\".v\"" appended. The wall is made up of isomorphic ``panels,'' each panel meeting the perimeter "P" in eactly one edge. Consecutive wall panels meet along a path of one or more ``corner'' edges, roughly perpendicular to the surface. Transition ramps: The procedures will also add a ``ramp'' on top of a given set of ``border'' faces, adjacent to "F". The ramp will smoothly connect the roof of the new layer with any original faces (not part of "F") adjacent to the border. The border fces are given in two sets, "R" and "S". Each face in "R" should share a single vertex with the faces in "F". Each face in "S" should share a single edge (and its two vertices) with the mesh "F". (Typically, the union of "R" and "S" is a strip of faces surrounding "F", and corresponds to one or more closed simple cycles in the graph-dual of the surface mesh.) Each "R"-face "(u,v,w)", where "u" is the vertex shared with "F", will be covered by a ramp consisting of all cells (r, s, v, w)", where "(r,s)" is an edge of the wall corner rising up from "u". For each "S"-face "(u,v,w)", where "(u,w)" is the edge shared with "F", the ramp will consist of all cells "(r,s,t,w)", where "(r,s,t)" is a triangle of the wall panel rising up from edge "(u,v)". *) PROCEDURE AddSimpleLayer( READONLY core: Model.T; READONLY F: Face.List; (* The set "F" of ``floor'' faces. *) READONLY R: Face.List; (* The set "R" of ``ramp'' faces. *) READONLY S: Face.List; (* The set "S" of ``ramp'' faces. *) liftVertex: PROCEDURE (u: Vertex.Num): LR3.T; mat: TEXT; (* Properties of material *) vis: TEXT; (* Visual appearance of material *) ): Model.T; (* Adds a ``simple'' layer of cells: for each original face in the given "F" list, there will be exactly one new face on the roof. The same vislies to any vertices and edges incident to the faces of "F". Let "roof(x)" stand for the roof element (vertex, edge, or face) corresponding to the floor element "x". If "v" is such a vertex, the coordinates of "roof(v)" are assuemd to be defined by "liftVertex(v)". The space between floor and roof will be filled with new cells. Specifically, for each face "f" in "F", there will be: (a) one new cell sitting on top of "f" with tip at a new vertex "fMid(f)", buried midway between "f" and "roof(f)"; (b) one new cell hanging down from "roof(f)", also with tip at "fMid(f)"; Also, for each edge "e" with endpoint "o" and "d" and incident to original faces "f" and "g", there will be (c) four new cells, sharing the vertices "fMid(f)" and "fMid(g)", and each incident to two consecutive vertices in the circular list "o", "roof(o)", "roof(d)", and "d". In item (c), if "f" is not a face to be thickened, "fMid(f)" will be replaced by a new vertex "eMid(e)", placed at the barycenter of those four vertices; and ditto for "fMid(g)". Thus, the four new cells will make up either a square pyramid or an octahedron, depending on whether the edge is on the perimeter of "F" or in its interior. The layer, excluding the ramps, will contain "fv + ff + fp" new vertices and "2*ff + 4*fe + fp" new cells, where "ff" is the number of faces in "F", "fv", "fe" are the number of vertices and edges incident to those faces, and "fp" is the number of edges on the perimeter of "F". (If there are no perimeter edges, the number of new cells is exactly "8*ff".) The ramps will comprise zero new vertices and "rf + 4*sf" cells, where "rf" and "sf" are the number of faces in "R" and "S", respectively. *) PROCEDURE AddTripleLayer( READONLY core: Model.T; READONLY F: Face.List; (* The set "F" of ``floor'' faces. *) READONLY R: Face.List; (* The The set "R" of ``ramp'' faces. *) READONLY S: Face.List; (* The set "S" of ``ramp'' faces. *) liftVertex: PROCEDURE (u: Vertex.Num): LR3.T; liftEdge: PROCEDURE (u, v: Vertex.Num): LR3.T; liftFace: PROCEDURE (u, v, w: Vertex.Num): LR3.T; mat: TEXT; (* Properties of material *) vis: TEXT; (* Visual appearance of material *) ): Model.T; (* Adds a "triple" layer of cells: for each original face in the "F" list, there will be about 3 new faces on the roof. NOTE: in spite of the name, this ``triple'' layer has fewer cells than the ``simple'' layer above (5.5 per floor face, against 8). Not to mention that it gives a more accurate surface. More precisely, for each face "f" on the floor, there will be six faces on the roof, each lying half above "f", and half above some face adjacent to "f". There will be a new roof vertex "roof(v)" for original vertex "v" incident to one of the given faces; and also one new roof vertex "fmid(f)" lying just above the center of each floor face "f". The coordinates of "roof(v)" are defined by "liftVertex(v)", and the coordinates of "fmid(f)" are defined by "liftFace(u,v,w)" where "u", "v", and "w" are the vertices of "f". The space between floor and roof will be filled with new cells. Specifically, for each floor face "f" with vertices "u", "v", "w" there will be (a) one new cell sitting on top of "f" with tip placed at "fmid(f)"; Also, for each edge "e" in the mesh "F", with endpoints "o" and "d", there will be (b) one new cell with corners "o", "d", "fmid(f)", and "fmid(g)", where "f"and "g" are the floor faces adjacent to "e"; (c) two new cells sharing the corners "fmid(f)", and "fmid(g)", with other two corners at "o" and "roof(o)", and "d" and "roof(d), respectively. In items (b) and (c), if "fmid(f)" doesn not exist (because "e" is a perimeter edge, and "f" is not in "F"), then a new vertex will be used in its place, with coordinates "liftEdge(o,d)"; and ditto for "fmid(g)". The layer will contain "fv + ff + fp" new vertices and "ff + 3*fe" new cells, where "ff" is the number of faces in "F", "fv", "fe" are the number of vertices and edges incident to those faces, and "fp" is the number of edges on the perimeter of "F". (If there are no perimeter edges, the number of new cells is approximately "5.5*ff" (plus a constant that depends on the genus of "F").) The ramps will comprise zero new vertices and "rf + 3*sf" cells, where "rf" and "sf" are the number of faces in "R" and "S", respectively. *) PROCEDURE AddQuadLayer( READONLY core: Model.T; READONLY F: Face.List; (* The set "F" of ``floor'' faces. *) READONLY R: Face.List; (* The set "R" of ``ramp'' faces. *) READONLY S: Face.List; (* The set "S" of ``ramp'' faces. *) liftVertex: PROCEDURE (u: Vertex.Num): LR3.T; liftEdge: PROCEDURE (u, v: Vertex.Num): LR3.T; mat: TEXT; (* Properties of material *) vis: TEXT; (* Visual appearance of material *) ): Model.T; (* Adds a "quad" layer: for each face on the floor, there will be four new faces on the roof. The four roof faces above floor face "f" will be denoted here by "roof(f).u", "roof(f).v", "roof(f).w", and "roof(f).mid". For any edge "e" in the "F" mesh, there will be a new vertex "roof(e).mid" and two new edges "roof(e).o", "roof(e).d". Finally, for each vertex "v" of the "F" mesh, there will be a new vertex "roof(v)". There will also be new vertices buried inside the layer: "fmid(f)" for each floor face "f", "vmid(v)" for each floor vertex "v", and "emid(e)" for each edge "e" on the perimeter of "F". The coordinates of "roof(v)" are defined by "liftVertex(v)", and the coordinates of "roof(e).mid" are defined by "liftEdge(o,d)" where "o" and "d" are the endpoints of "e". The positions of the buried vertices are computed from these. The space between floor and roof will be filled with new cells. Specifically, for each face "f" with vertices "u", "v", "w" there will be (a) one new cell sitting on top of each original face "f" with tip placed at "fmid(f)"; (b) one new cell hanging down from "roof(f).mid", also with tip at "fmid(f)"; (c) one new cell hanging down from each of the faces "roof(f).u", "roof(f).v", "roof(f).w", with tips respectivly at "vmid(f.u)", "vmid(f.v)", vmid(f,w)"; (d) three new buried cells with corners "vmid(u)", "fmid(f)", "roof((u,v))", "roof((u,w))" Also, for each edge "e" in the "F" mesh with endpoint "o" and "d", there will be (e) five cells sharing the two vertices "fmid(f)" and "fmid(g)", where "f" and "g" are the faces adjacent to "e". The other corners of these cells will be pairs of consecutive vertices in the circular list "o", "vmid(o)", "roof(e).mid", "vmid(d)", and "d". In item (e), if "fmid(f)" does not exist (because "e" is a perimeter edge, and "f" is not in "F"), then a new vertex will be used in its place, located at the barycenter of those five vertices; and ditto for "fmid(g)". Thus, the five new cells will make up either a pentagonal pyramid or a pentagonal bipyramid, depending on whether "e" is a perimeter edge or not. The layer will contain "2*fv + fe + ff + fp" new vertices and "8*ff + 5*fe" new cells, where "ff" is the number of faces in "F", "fv", "fe" are the number of vertices and edges incident to those faces, and "fp" is the number of edges on the perimeter of "F". (I there are no perimeter edges, the number of new cells is approximately "15.5*ff" (plus a constant that depends on the genus of "F").) The ramps will include no new vertices, and "2*rf + 7*sf" cells, where "rf" and "sf" are the number of faces in "R" and "S", respectively. *) (* OPTIONS PARSING *) TYPE LayerSpecs = RECORD thickness: LONGREAL; (* Thickness, usually relative *) kind: LayerKind; (* Layer structure *) mat: TEXT := "default"; (* Layer material name *) vis: TEXT := "default"; (* Texture name *) END; LayerKind = {Kernel, Simple, Triple, Quad}; CONST LayerOptionsHelp = " { \\\n" & " { -kernel | -layer {simple|triple|quad} } \\\n" & " thickness NUM [ material NAME ] [ visual NAME ] \\\n" & " }..."; PROCEDURE ParseLayerOptions(pp: ParseParams.T): REF ARRAY OF LayerSpecs RAISES{ParseParams.Error}; (* Parses options of the form described in the "OptionsHelp" text above. *) END Thicken.