/* Last edited on 2007-01-15 17:24:02 by stolfi */ /* Various enumeration procedures for graph-like structures. */ /* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */ /* oct-marcone/Oct.m3 */ PROCEDURE Enum(a: Arc; visit: VisitProc; edges: BOOLEAN := FALSE)= CONST stacksize = 16*1024; (* Guess: max edges in a connected component *) VAR stack: ARRAY [0..stacksize-1] OF Edge; bitstack: ARRAY [0..stacksize-1] OF (* BITS 3 FOR *) [0..7]; top: [0..stacksize]; PROCEDURE VisitandMark (c: Arc)= (* If edge(c) is unmarked: visit, mark, and stack it. *) BEGIN IF NOT c.edge.marks[FlipBit(c)] THEN (* IF NOT c.edge.marks[0] THEN *) visit(c); IF NOT edges THEN visit(Sym(c)) END; <* ASSERT top < stacksize *> c.edge.marks[FlipBit(c)] := TRUE; stack[top] := c.edge; bitstack[top] := c.bits; top := top + 1; END; END VisitandMark; VAR seen: [0..stacksize]; (* # of quads whose childeren were looked at *) atop: Arc; BEGIN <* ASSERT NOT a.edge.marks[FlipBit(a)] *> top := 0; seen := 0; TRY VisitandMark (a); WHILE seen < top DO atop.edge := stack[seen]; atop.bits := bitstack[seen]; VisitandMark(Onext(atop)); VisitandMark(Onext(Sym(atop))); seen := seen + 1 END; FINALLY (* Erase all marks *) WHILE top > 0 DO top := top - 1; atop.edge := stack[top]; atop.bits := bitstack[top]; atop.edge.marks[FlipBit(atop)] := FALSE; END; (* WHILE top > 0 DO top := top - 1; stack[top].marks[FlipBit(stack[top]) := FALSE END; *) END; END Enum; PROCEDURE EnumVertices (a: Arc; visit: VisitProc)= CONST stacksize = 16*1024; (* Guess: max quads in a connected component *) VAR stack: ARRAY [0..stacksize-1] OF Edge; bitstack: ARRAY [0..stacksize-1] OF (* BITS 3 FOR *) [0..7]; top: [0..stacksize]; (* # of visited(=marked=stacked) vertices *) PROCEDURE VisitandMark (c: Arc)= (* If org(c) is unmarked: visit, mark, and stack it *) VAR cn: Arc; BEGIN IF NOT c.edge.marks[SenseBit(c)] THEN visit(c); <* ASSERT top < stacksize *> cn := c; REPEAT cn.edge.marks[SenseBit(cn)] := TRUE; cn := Onext(cn); UNTIL (cn = c); stack[top] := c.edge; bitstack[top] := c.bits; top := top + 1; END; END VisitandMark; VAR seen: [0..stacksize]; (* # of vertices whose neighbs were looked at *) atop, atn: Arc; BEGIN <* ASSERT NOT a.edge.marks[0] *> top := 0; seen := 0; TRY VisitandMark (a); WHILE seen < top DO atop.edge := stack[seen]; atop.bits := bitstack[seen]; atn := atop; REPEAT VisitandMark(Sym(atn)); atn := Onext(atn); UNTIL (atn = atop); seen := seen + 1; END; FINALLY (* Erase all marks *) WHILE top > 0 DO top := top - 1; atop.edge := stack[top]; atop.bits := bitstack[top]; atn := atop; REPEAT atn.edge.marks[SenseBit(atn)] := FALSE; atn:= Onext(atn); UNTIL (atop = atn); END; END; END EnumVertices; PROCEDURE NumberArcs(READONLY a: ARRAY OF Arc; primal: BOOLEAN): REF ARRAY OF Arc = VAR sz: CARDINAL := 1024; (* Number of edges allocated on stack *) ne: CARDINAL := 0; (* Number of edges seen *) estack := NEW(REF ARRAY OF Edge, sz); (* Edge stack *) na: CARDINAL := 0; (* Number of arcs seen *) astack := NEW(REF ARRAY OF Arc, 8 * sz); (* Arc stack *) (* An arc "a" is "marked" if astack[8 * a.edge.no + a.bits] = a. *) PROCEDURE VisitAndMark (t: Arc) = (* If t is unmarked: visit, mark, and stack it. *) BEGIN WITH teno = t.edge.no, tno = 8*teno + t.bits DO IF teno < ne AND astack[tno] = t THEN (* Arc is already marked *) ELSE IF teno < ne AND estack[teno] = t.edge THEN (* New arc of old edge *) ELSE (* New edge *) IF nstack = sz THEN WITH newstack = NEW(REF ARRAY OF Arc, 2 * sz) DO SUBARRAY(newstack^, 0, sz) := stack^; stack := newstack END; END; t.edge.no := nstack; stack[nstack] := t; INC(nstack); t.edge.no := nstack; stack[nstack] := t; INC(nstack); END END END VisitAndMark; VAR seen: CARDINAL := 0; (* # of edges whose childeren were looked at *) s: Arc; BEGIN nstack := 0; seen := 0; FOR i := 0 TO LAST(a) DO VisitAndMark (a[i]) END; WHILE seen < nstack DO s := stack[seen]; VisitAndMark(Onext(s)); VisitAndMark(Onext(Sym(s))); seen := seen + 1 END; WITH r = NEW(REF ARRAY OF Arc, nstack) DO r^ := SUBARRAY(stack^, 0, nstack); RETURN r END; END NumberEdges; PROCEDURE NumberVertices(READONLY e: ARRAY OF Arc): REF ARRAY OF Arc = VAR sz: CARDINAL := 1024; stack := NEW(REF ARRAY OF Arc, sz); nstack: CARDINAL := 0; (* An arc "a" is "marked" if stack[Get].edge = e. *) PROCEDURE VisitAndMark (t: Arc) = (* If t is unmarked: visit, mark, and stack it. *) BEGIN IF t.edge.no < nstack AND stack[t.edge.no].edge = t.edge THEN (* Edge is already marked *) ELSE IF nstack = sz THEN WITH newstack = NEW(REF ARRAY OF Arc, 2 * sz) DO SUBARRAY(newstack^, 0, sz) := stack^; stack := newstack END; END; t.edge.no := nstack; stack[nstack] := t; INC(nstack); END END VisitAndMark; VAR seen: CARDINAL := 0; (* # of edges whose childeren were looked at *) s: Arc; BEGIN nstack := 0; seen := 0; FOR i := 0 TO LAST(a) DO VisitAndMark (a[i]) END; WHILE seen < nstack DO s := stack[seen]; VisitAndMark(Onext(s)); VisitAndMark(Onext(Sym(s))); seen := seen + 1 END; WITH r = NEW(REF ARRAY OF Arc, nstack) DO r^ := SUBARRAY(stack^, 0, nstack); RETURN r END; END NumberVertices; PROCEDURE NumberVerticesAndFaces( READONLY e: ARRAY OF Arc ): RECORD v, f: REF ARRAY OF Arc END = (* Assumes "e[i]" is one of the arcs of edge number "i". Numbers the origin and destination vertices of all those arcs, and also the left and right faces. Returns a vector "v" with one arc out of each vertex, and a vector "f" with one arc on the ccw boundary of each face. *) VAR nv: CARDINAL := 0; nf: CARDINAL := 0; v: REF ARRAY OF Arc; f: REF ARRAY OF Arc; BEGIN WITH NE = NUMBER(e), rep = NEW(REF ARRAY OF ArcNo, 8*NE)^ (* Define #a = GetArcNo(a), a[i] = arc such that #a = i. Invariant: rep[#a] = #a if arc "a" is the least arc out of its origin; otherwise rep[#a] = #b where b is some lesser arc with Org(a)=Org(b). *) DO PROCEDURE Identify(a: Arc) = (* Records the fact that "a" and "Onext(a)" have the same origin *) VAR ia := GetArcNo(a); ib := GetArcNo(Onext(a)); BEGIN WHILE rep[ia] # ia DO ia := rep[ia] END; WHILE rep[ib] # ib DO ib := rep[ib] END; IF ia < ib THEN rep[ib] := ia ELSIF ib < ia THEN rep[ia] := ib ELSE (* Ok *) END END Identify; BEGIN (* Initialize "rep" *) FOR ia := 0 TO LAST(rep) DO rep[ia] := ia END; (* Identify arcs with same origin: *) FOR ie := 0 TO LAST(e) DO VAR ee := e[ie]; BEGIN FOR r := 0 TO 3 DO Identify(ee); Identify(Flip(ee)); ee := Rot(ee) END; END END; (* Count and collect distinct vertices and faces: *) PROCEDURE Count(a: Arc; VAR n: CARDINAL) = BEGIN WITH ia = GetArcNo(a) DO IF rep[ia] = ia THEN INC(n) END; END END Count; PROCEDURE Collect(a: Arc; VAR n: CARDINAL; VAR r: ARRAY OF Arc) = BEGIN WITH ia = GetArcNo(a) DO IF rep[ia] = ia THEN r[n] := a; INC(n) END; END END Collect; BEGIN nv := 0; nf := 0; FOR ie := 0 TO LAST(e) DO WITH ei = e[i] DO Count(ei, nv); Count(Rot(ei), nf); Count(Sym(ei), nv); Count(Tor(ei), nf) END; END; v := NEW(REF ARRAY OF Arc, nv); f := NEW(REF ARRAY OF Arc, nf); nv := 0; nf := 0; FOR ie := 0 TO LAST(e) DO WITH ei = e[i] DO Collect(ei, nv, v^); Collect(Rot(ei), nf, f^); Collect(Sym(ei), nv, v^); Collect(Tor(ei), nf, f^) END; END; END END END; RETURN END NumberVerices; /* ---------------------------------------------------------------------- */ /* oct-marcone/NewOct.m3 */ PROCEDURE NumberEdges(READONLY e: ARRAY OF Edge): REF ARRAY OF Edge = (* Enumerates all edges reachable from "a", and assigns them consecutive serial numbers starting from 0. Returns a vector of those edges, indexeed by serial number. *) CONST InitNE = 64; VAR et := NEW(REF ARRAY OF Edge, InitNE); NE: CARDINAL := 0; (* Num of Edges seen so far *) KE: CARDINAL := 0; (* Num of Edges whose "enext"s have been seen. *) (* An edge "e" has been seen iff "et[e.no] = e". *) PROCEDURE VisitAndMark (t: Edge) = (* If t is unmarked: visit, mark, and es it. *) BEGIN WITH tno = t.edge DO IF tno >= NE OR et[tno] # t THEN tno := NE; StoreEdge(t, NE, et) END END END VisitAndMark; BEGIN FOR i := 0 TO LAST(e) DO VisitAndMark (e[i]) END; WHILE KE < NE DO VisitAndMark(et[KE].enext[0]); VisitAndMark(et[KE].enext[1]); VisitAndMark(et[KE].enext[2]); VisitAndMark(et[KE].enext[3]); INC(KE) END; WITH r = NEW(REF ARRAY OF Edge, NE) DO r^ := SUBARRAY(et^, 0, NE); RETURN r END; END NumberEdges; /* ---------------------------------------------------------------------- */ /* oct-marcone/DOct.m3 */ PROCEDURE NumberVertices(a: Arc): CARDINAL = VAR nv: CARDINAL := 0; PROCEDURE Visit(c: Arc) = VAR cn: Arc; BEGIN cn := c; REPEAT NARROW(cn.edge, DataOctet).org[SenseBit(cn)] := nv; cn := Onext(cn); UNTIL (cn = c); INC(nv); END Visit; BEGIN EnumVertices(a, Visit); RETURN nv END NumberVertices; /* ---------------------------------------------------------------------- */ /* libm3octet-2/Triang.m3 */ PROCEDURE NumberVertices(a: Arc): CARDINAL = VAR n: CARDINAL := 0; PROCEDURE Visit(c: Arc) = VAR cn: Arc; BEGIN WITH v = Org(c) DO cn := c; REPEAT WITH vn = Org(cn) DO <* ASSERT vn = v *> vn.num := n END; cn := Onext(cn); UNTIL (cn = c); END; INC(n); END Visit; BEGIN Oct.EnumVertices(a, Visit); RETURN n END NumberVertices; /* ---------------------------------------------------------------------- */ /* libm3octet-2/Triang.i3 */ PROCEDURE EnumGridVertices( a: Arc; nx, ny: CARDINAL; visit: GridVisitProc; ); (* Assumes "a" is the east-pointing arc out of the southwest corner of a triangulated grid. Calls "visit(e, x, y)" for each vertex of the grid, where "e" is one arc out of the vertex, and "x,y" are its indices. The vertices are numbered from [0,0] to [2*nx, 2*ny]; both indices are even for cell corners, and both are odd for cell centers. *) /* ---------------------------------------------------------------------- */ /* libm3map3d/Triangulation.m3 */ (* === GLOBAL PROCEDURES === *) VAR seenNode: REF ARRAY OF Node := NIL; nodeOnum: REF ARRAY OF CARDINAL := NIL; seenNodeCount: CARDINAL := 0; PROCEDURE MarkNode(n: Node) = BEGIN IF seenNode = NIL OR NUMBER(seenNode^) <= seenNodeCount THEN DoubleseenNode() END; WITH k = seenNodeCount+0 DO seenNode[k] := n; nodeOnum[k] := n.num; n.num := k; INC(seenNodeCount) END END MarkNode; PROCEDURE DoubleseenNode() = VAR sz: CARDINAL; BEGIN IF seenNode = NIL THEN sz := 0 ELSE sz := NUMBER(seenNode^) END; WITH szNew = MAX(2*sz, 1000), seenNodeNew = NEW(REF ARRAY OF Node, szNew), nodeOnumNew = NEW(REF ARRAY OF CARDINAL, szNew) DO IF seenNode # NIL THEN SUBARRAY(seenNodeNew^, 0, sz) := seenNode^; SUBARRAY(nodeOnumNew^, 0, sz) := nodeOnum^; END; seenNode := seenNodeNew; nodeOnum := nodeOnumNew; END END DoubleseenNode; PROCEDURE NodeIsMarked(n: Node) : BOOLEAN = BEGIN RETURN (n.num < seenNodeCount) AND (seenNode[n.num] = n) END NodeIsMarked; PROCEDURE UnmarkMarkedNodes() = BEGIN WHILE seenNodeCount > 0 DO WITH k = seenNodeCount-1, n = seenNode[k] DO n.num := nodeOnum[k]; DEC(seenNodeCount) END END END UnmarkMarkedNodes; PROCEDURE EnumVertices(a: Handle; visit: VisitProc) = CONST IniStackSize = 1000; VAR festack := NEW(REF ARRAY OF FaceEdge, IniStackSize); bstack := NEW(REF ARRAY OF VFEBits, IniStackSize); top : CARDINAL; (* top for "festack" stack *) PROCEDURE DoubleStack() = BEGIN WITH sz = NUMBER(festack^), szNew = 2*sz, festackNew = NEW(REF ARRAY OF FaceEdge, szNew), bstackNew = NEW(REF ARRAY OF VFEBits, szNew) DO SUBARRAY(festackNew^, 0, sz):= festack^; festack := festackNew; SUBARRAY(bstackNew^, 0, sz) := bstack^; bstack := bstackNew; END END DoubleStack; PROCEDURE Stack(c: Handle) = VAR cn : Handle := c; dn : Handle; BEGIN REPEAT IF top > LAST(festack^) THEN DoubleStack() END; festack[top] := cn.fe; bstack[top] := cn.bits; top := top + 1; dn := Flip(prevE(cn)); REPEAT IF top > LAST(festack^) THEN DoubleStack() END; festack[top] := dn.fe; bstack[top] := dn.bits; top := top + 1; dn := nextF(dn); UNTIL( dn = Flip(prevE(cn)) ); cn := nextF(cn); UNTIL (cn = c); END Stack; PROCEDURE VisitAndMark(c: Handle)= (* If org(c) is diferent that the origins handles stacked, stack the handle "c". *) VAR cn : Handle; BEGIN WITH n = Org(c) DO IF n # NIL AND NOT NodeIsMarked(n) THEN visit(c); MarkNode(n); Stack(c); cn := c; REPEAT Stack(cn); cn := Onext(cn); UNTIL (cn = c) END END; END VisitAndMark; VAR seen: CARDINAL; BEGIN top := 0; seen := 0; <* ASSERT seenNodeCount = 0 *> VisitAndMark(a); WHILE seen < top DO WITH b = Handle{festack[seen], bstack[seen]} DO VisitAndMark(nextF(b)); VisitAndMark(nextF(nextE(b))); VisitAndMark(nextE(b)); VisitAndMark(prevF(prevE(b))); END; seen := seen + 1; END; UnmarkMarkedNodes(); END EnumVertices; PROCEDURE NumberVertices(a: Handle): CARDINAL = VAR n: CARDINAL := 0; PROCEDURE Visit(c: Handle) = PROCEDURE Visit_(c: Handle) = VAR cn,dn: Handle; BEGIN WITH v = Org(c) DO cn := c; REPEAT WITH vn = Org(cn) DO <* ASSERT vn = v *> vn.num := n; dn := Flip(prevE(cn)); REPEAT WITH wn = Org(dn) DO <* ASSERT wn = vn *> wn.num := n; dn := nextF(dn); END; UNTIL ( dn = Flip(prevE(cn)) ); cn := nextF(cn); END; UNTIL ( cn = c ); END; END Visit_; VAR cn: Handle := c; BEGIN REPEAT Visit_(cn); cn := Onext(cn); UNTIL cn = c; INC(n); END Visit; PROCEDURE VisitDual(c: Handle) = PROCEDURE VisitDual_(c: Handle) = VAR cn: Handle; p,pn: Node; BEGIN p := Org(c); IF p # NIL THEN cn := c; REPEAT pn := Org(cn); IF pn # NIL THEN <* ASSERT pn = p *> pn.num := n; END; cn := nextF(cn); UNTIL( cn = c ) END; END VisitDual_; VAR cn: Handle := c; BEGIN VisitDual_(cn); VisitDual_(Flip(prevE(cn))); INC(n); END VisitDual; BEGIN IF Map3D.DualBit(a) = 0 THEN EnumVertices(a, Visit); ELSE EnumVertices(a, VisitDual); END; RETURN n END NumberVertices; /* ---------------------------------------------------------------------- */ /* libm3map3d/Map3D.m3 */ (* Tridimensional maps. The facet-edge data structure for tridimensional maps, decorated with material properties of vertices, edges, faces, and cells. Written by L. Lozada (see copyright at end of file). *) (* ================ TRAVESAL ===================== *) CONST InitHandleStackSize = 1024; InitEdgeStackSize = 1024; InitVertexStackSize = 1024; InitOutStackSize = 20; PROCEDURE EnsureStackSpace(VAR stack: REF ARRAY OF Handle; VAR top: CARDINAL) = (* If the stack is full, allocates a new one, copying the old contents to it. *) BEGIN IF top >= NUMBER(stack^) THEN WITH sz = NUMBER(stack^), szNew = 2*sz, stackNew = NEW(REF ARRAY OF Handle, szNew) DO SUBARRAY(stackNew^, 0, sz) := stack^; stack := stackNew; END END END EnsureStackSpace; PROCEDURE EnumHandles(READONLY a: ARRAY OF Handle; visit: VisitProc; fes: BOOLEAN := FALSE) = VAR stack := NEW(REF ARRAY OF Handle, InitHandleStackSize); np: CARDINAL; (* The fe record of handle "t" is marked by setting "t.fe.emark[DualBit(t)]". *) (* Note that only the primal or dual half of the record is marked, but each mark *) (* applies to both t and Flip(t). *) PROCEDURE VisitAndMark(t: Handle)= (* If fe(t) is unmarked: visit, mark, and stack it. *) BEGIN IF NOT t.fe.emark[DualBit(t)] THEN visit(t); IF NOT fes THEN visit(Flip(t)) END; t.fe.emark[DualBit(t)] := TRUE; EnsureStackSpace(stack, np); stack[np] := t; INC(np); END; END VisitAndMark; VAR seen: CARDINAL; (* # of quads whose childeren were looked at *) BEGIN IF NUMBER(a) > 0 THEN <* ASSERT NOT a[0].fe.emark[DualBit(a[0])] *> END; np := 0; seen := 0; TRY FOR i := 0 TO LAST(a) DO VisitAndMark(a[i]) END; WHILE seen < np DO WITH b = stack[seen] DO VisitAndMark(nextE(b)); VisitAndMark(nextF(b)); END; seen := seen + 1 END; FINALLY (* Erase all marks *) FOR i := 0 TO np-1 DO WITH b = stack[i] DO b.fe.emark[DualBit(b)] := FALSE; END END END END EnumHandles; (* ====== Numbering ====== *) PROCEDURE NumberFaceEdges(READONLY a: ARRAY OF Handle): REF ARRAY OF Handle = VAR stack := NEW(REF ARRAY OF Handle, InitHandleStackSize); nfe: CARDINAL := 0; (* The fe record of handle "t" is marked by setting "t.fe.num" to the index "k" *) (* such that stack[k] = t. *) (* Note that the mark applies to all eight handles of that facet-edge. *) PROCEDURE VisitAndMark(t: Handle) = (* If t is unmarked: visit, mark, and stack it. *) BEGIN WITH num = t.fe.num DO IF NOT (num < nfe AND stack[num].fe = t.fe) THEN EnsureStackSpace(stack, nfe); num := nfe; stack[nfe] := t; INC(nfe); END END END VisitAndMark; VAR seen: CARDINAL := 0; (* # of fes whose childeren were looked at *) BEGIN nfe := 0; seen := 0; FOR i := 0 TO LAST(a) DO VisitAndMark(a[i]) END; WHILE seen < nfe DO WITH s = stack[seen] DO VisitAndMark(nextF(s)); VisitAndMark(nextE(s)); END; seen := seen + 1 END; WITH r = NEW(REF ARRAY OF Handle, nfe) DO r^ := SUBARRAY(stack^, 0, nfe); RETURN r END; END NumberFaceEdges; PROCEDURE NumberEdgesOrFacets(READONLY a: ARRAY OF Handle; facets: BOOLEAN := FALSE): REF ARRAY OF Handle = VAR stack := NEW(REF ARRAY OF Handle, InitHandleStackSize); ne: CARDINAL; (* The edge "e" is marked by setting t.fe.mark[DualBit(t)] for every handle t *) (* with that same edge. *) PROCEDURE VisitAndMark(t: Handle) = (* If t is unmarked: visit, mark, and stack it. *) VAR tn: Handle; BEGIN IF NOT t.fe.emark[DualBit(t)] THEN tn := t; REPEAT tn.fe.emark[DualBit(tn)] := TRUE; WITH e = Edg(tn) DO IF e # NIL THEN e.num := ne END END; tn := nextF(tn); UNTIL (tn = t); EnsureStackSpace(stack, ne); stack[ne] := t; INC(ne); END; END VisitAndMark; VAR seen: CARDINAL := 0; sn: Handle; BEGIN seen := 0; ne := 0; FOR i := 0 TO LAST(a) DO IF facets THEN VisitAndMark(Sdual(a[i])) ELSE VisitAndMark(a[i]) END END; WHILE seen < ne DO WITH s = stack[seen] DO sn := s; REPEAT VisitAndMark(nextE(sn)); sn := nextF(sn); UNTIL (sn = s); END; seen := seen + 1 END; (* Erase all marks *) FOR i := 0 TO ne-1 DO WITH b = stack[i] DO VAR bn: Handle := b; BEGIN REPEAT bn.fe.emark[DualBit(bn)] := FALSE; bn:= nextF(bn); UNTIL (bn = b); END; END END; WITH r = NEW(REF ARRAY OF Handle, ne) DO r^ := SUBARRAY(stack^, 0, ne); IF facets THEN FOR i := 0 TO ne-1 DO r[i] := Sdual(r[i]) END END; RETURN r END; END NumberEdgesOrFacets; PROCEDURE NumberFaceEdges(READONLY a: ARRAY OF Handle): REF ARRAY OF Handle; (* Assigns distinct serial numbers to all fes reachable from "a" by nextE/nextF chains. Stores those numbers in the "num" field of each face-edge record. Returns a vector with one reachable handle from each face-edge (not necessarily the reference one). *) PROCEDURE NumberEdges(READONLY a: ARRAY OF Handle): REF ARRAY OF Handle; (* Enumerates all edges that are reachable from "a" by nextE/nextF chains. Assigns distinct serial numbers to those edges and stores them in the ".num" field of the corresponding EFNode records, if they exist. In any case, returns a representative handle "b" for each edge "e" such that Edge(b) = e (resp. for each facet f such that Face(b) = f). *) PROCEDURE NumberVerticesOrCells(READONLY a: ARRAY OF Handle; cells: BOOLEAN := FALSE): REF ARRAY OF Handle; (* Enumerates all vertices (resp. cells, if "cells = TRUE") that are reachable from "a" by nextE/nextF chains. Assigns distinct serial numbers to those vertices (resp. cells) and stores them in the ".num" field of the corresponding VPNode records, if they exist. In any case, returns a representative handle "b" for of each vertex v such that Org(b) = v (resp. for each cell p such that NegC(b) = p). *) /* ---------------------------------------------------------------------- */ /* libm3delaunay/DelauTest.m3 */ PROCEDURE PlotDelaunay(e: Arc; name: TEXT) = (* Uses "Ldata" and "Rdata" to mark visited edges. *) <* FATAL Wr.Failure, Thread.Alerted *> VAR (*CONST*) PltMark: REF INTEGER := NEW(REF INTEGER); f: Wr.T := FileStream.OpenWrite(name) ; PROCEDURE DoPlotDelaunay(e:Arc) = BEGIN WHILE (Ldata(e) # PltMark) AND (Rdata(e) # PltMark) DO WITH op = Org(e)^, dp = Dest(e)^ DO PSPlot.DrawSegment(f, L(op.x), L(op.y), L(dp.x), L(dp.y)); END; SetLdata(e, PltMark); SetRdata(e, PltMark); DoPlotDelaunay(Onext(Sym(e))); e := Onext(e) END END DoPlotDelaunay; BEGIN PSPlot.BeginFile(f); PSPlot.BeginPage(f, 1, -0.6D0, 0.6D0, -0.6D0, 0.6D0, 1, 1); PSPlot.AddCaption(f, "Delaunay triangulation"); DoPlotDelaunay(e); PSPlot.DrawFrame(f); PSPlot.EndPage(f); PSPlot.EndFile(f); Wr.Close(f); END PlotDelaunay; /* ---------------------------------------------------------------------- */ /* fgc-quad/QuadEdge.m3 */ (*** TRAVERSAL ***) TYPE VisitProc = PROCEDURE (a: Arc) RAISES ANY; PROCEDURE EnumArcs (a: Arc; visit: VisitProc) RAISES ANY; PROCEDURE EnumEdges (a: Arc; visit: VisitProc) RAISES ANY; PROCEDURE EnumVertices (a: Arc; visit: VisitProc) RAISES ANY; (* Calls "visit(e)" for arcs "e" that can be reached from "a" by repeated application of "Sym"s and "Onext"s. "EnumArcs" visits all such arcs. "EnumEdges" visits only one arc on each edge: that is, if arc "e" is reachable from "root", exactly one of "e" and "Sym(e)" will be visited. "EnumVertices" visits exactly one arc out of each reachable vertex. Note that if arc "e" is reachable then neither "Rot(e)" nor "Tor(e)" are reachable (unless "Splice" has been improperly used.) *) (*** ELEMENT NUMBERING ***) PROCEDURE ArcId(a: Arc): CARDINAL; PROCEDURE EdgeId(a: Arc): CARDINAL; PROCEDURE VertexId(a: Arc): CARDINAL; (* Each arc, edge, and vertex is identified by a distinct numerical id. The four arcs corresponding to the same edge have consecutive ids; the lowest of them is divisible by four, and is the edge id. The id of a vertex is that of the lowest arc with the same origin. Thus, the following hold EdgeId(a) = EdgeId(Rot(a)) <= ArcId(a) VertexId(a) = VertexId(ONext(a)) <= ArcId(a) The cost of "VertexId" s proprotional to the vertex's degree. *) PROCEDURE EnumOrbits ( e: Edge; s1, s2: EdgeStep; vEdge, vOrbit: EdgeProc; ) RAISES ANY = (* Enumerates all edges that can be reached from "e" through any combination of "s1" and "s2". If "vEdge" is not NIL, applies it to every reachable edge. If "vOrbit" is not NIL, applies it to exactly one reachable edge out of each "s1"-orbit. *) CONST InitialPileSize = 1024; VAR orb: REF ARRAY OF Edge := NEW(REF ARRAY OF Edge, InitialPileSize); nOrb: CARDINAL := 0; (* Invariant: The array "orb[0..nOrb-1]" contains exactly one edge (the entry one) out of each "s1"-orbit seen so far. If the "mark" bit of an edge is TRUE, its "s1"-orbit has been stored in "orb"; all the edges in it have been marked and visited. *) PROCEDURE Stack(f: Edge) = BEGIN IF nOrb >= NUMBER(orb^) THEN WITH vNew := NEW(REF ARRAY OF Edge, 2*nOrb) DO SUBARRAY(vNew^, 0, nOrb) := orb^; orb := vNew END END; orb[nOrb] := f; INC(nOrb) END Stack; PROCEDURE VisitAndMarkOrbit (f: Edge) RAISES ANY = (* Assumes edge "f" is unmarked; puts "f" in the pile "orb", and visits and marks all the edges in its "s1"-orbit. *) VAR g: Edge := f; BEGIN IF vOrbit # NIL THEN vOrbit(f) END; Stack(f); REPEAT IF vEdge # NIL THEN vEdge(g) END; WITH ge = g.p.edge[g.r] DO <* ASSERT NOT ge.mark *> ge.mark := TRUE; END; g := s1(g) UNTIL g = f END VisitAndMarkOrbit; PROCEDURE CheckNeighbors (f: Edge) = (* Applies VisitAndMarkOrbit to all "s1"-orbits reachable from the "s1"-orbit of "f" through one "s2" step. *) VAR g: Edge := f; BEGIN REPEAT WITH h = s2(g) DO IF NOT h.p.edge[h.r].mark THEN VisitAndMarkOrbit(h) END; END; g := s1(g) UNTIL g = f; END CheckNeighbors; PROCEDURE UnmarkOrbit (c: Edge) = (* Removes the mark from all edges in the "s1"-orbit of "f". *) VAR g: Edge := f; BEGIN REPEAT g.p.edge[g.r].mark := FALSE; g := s1(g) UNTIL g = f; END UnmarkOrbit; VAR ns: CARDINAL := 0; BEGIN TRY VisitandMarkVertex(a); WHILE ns < nOrb DO CheckNeighbors(orb[ns]); INC(ns); END; FINALLY (* Erase all marks *) WHILE nOrb > 0 DO DEC(nOrb); UnmarkOrbit(orb[nOrb]) END END END EnumOrbits; PROCEDURE EdgeId(e: Edge): CARDINAL = BEGIN RETURN e.p.id + e.r END EdgeId; PROCEDURE PatchId(e: Edge): CARDINAL = BEGIN RETURN e.p.id END PatchId; /* ---------------------------------------------------------------------- */ /* fgc-quad/EnumOrbits.mg */ GENERIC MODULE EnumOrbits(Elem); (* WHERE TYPE Elem.T *) PROCEDURE Enum3 ( e: Elem.T; mark: MarkProc; vElem: VisitProc; s1: StepProc; v1Orbit: VisitProc; s2: StepProc; v12Orbit: VisitProc; s3: StepProc; ) RAISES ANY = (* Enumerates every Elem.T that can be reached from "e" through any combination of "s1", "s2", and "s3". Applies "vElem" to every reachable Elem.T. Applies "v1Orbit" to exactly one reachable Elem.T out of each "s1"-orbit. Applies "v12Orbit" to exactly one reachable Elem.T out of each "(s1,s2)"-orbit. For exach Elem.T "e", the first call to "mark(e)" from within one call to "Enum3" should return FALSE, and succeeding ones should return TRUE. A null "VisitProc" is treated as a no-op. A null StepProc is treated as the identity function. *) CONST InitialPileSize = 1024; VAR orb: REF ARRAY OF Elem.T := NEW(REF ARRAY OF Elem.T, InitialPileSize); n: CARDINAL := 0; PROCEDURE Stack(f: Elem.T) = BEGIN IF nOrb >= NUMBER(orb^) THEN WITH vNew := NEW(REF ARRAY OF Elem.T, 2*nOrb) DO SUBARRAY(vNew^, 0, nOrb) := orb^; orb := vNew END END; orb[nOrb] := f; INC(nOrb) END Stack; PROCEDURE VisitAndMark1Orbit (f: Elem.T) RAISES ANY = (* Applies "v1Orbit" to "f", and "vElem" to each Elem.T in its "s1" orbit. Assumes Elem.T "f" was unmarked. In/Out Invariant: The array "orb[0..n-1]" contains exactly one Elem.T out of each "s1"-orbit seen so far, and these Elem.Ts are all marked. An Elem.T "f" has "mark = TRUE" if and only if its "s1"-orbit is all marked. (Thus, the set of all marked Elem.Ts is closed under "s1".) If an Elem.T is marked, it doesn't need to be visited. *) VAR g: Elem.T := f; BEGIN IF vElem # NIL THEN vElem(g) END; IF v1Orbit # NIL THEN v1Orbit(f) END; Stack(f); IF s1 # NIL THEN g := s1(g) END; WHILE g # f DO IF vElem # NIL THEN vElem(g) END; <* ASSERT NOT mark(g) *> IF s1 # NIL THEN g := s1(g) END; UNTIL g = f END VisitAndMark1Orbit; VisitAndMark12Orbit (f: Elem.T) RAISES ANY = (* Applies "v12Orbit" to "f"; then applies "VisitAndMark1Orbit" to one Elem.T from each "s1"-orbit that is reachable from "f" through chains of "s1" and "s2". Assumes "f" is unmarked. In/Out Invariant: Same as for VisitAndMark1Orbit, plus: the set of all Elem.Ts marked so far is closed under "s2" (besides being closed under "s1"). *) VAR g: Elem.T := f; nc: CARDINAL := n; BEGIN IF v12Orbit # NIL THEN v12Orbit(f) END; VisitAndMark1Orbit(f); IF s2 # NIL THEN WHILE nc < n DO (* Loop Invariant: if Elem.T "e" is in the "s1"-closure of "orb[0..nc-1]", then "s2(e)" is in "orb[0..n-1]". *) WITH g = orb[nc] DO (* Checks "s2(h)" for every "h" in the "s1"-orbit of "g": *) VAR h: Elem.T := g; BEGIN REPEAT WITH a = s2(h) DO IF NOT a.p.Elem.T[a.r].mark THEN VisitAndMark1Orbit(a) END END; IF s1 # NIL THEN h := s1(h) END; UNTIL h = g; END END; INC(nc) END END END VisitAndMark12Orbit VisitAndMark123Orbit (f: Elem.T) RAISES ANY = (* Applies "VisitAndMark12Orbit" to one Elem.T in each "(s1,s2)"-orbit that is reachable from "f" through chains of "s1", "s2", and "s3" steps. Assumes "f" is unmarked. In/Out Invariant: Same as for VisitAndMark12Orbit, plus: the set of all Elem.Ts marked so far is closed under "s3" (besides being closed under "s1" and "s2"). *) VAR g: Elem.T := f; nc: CARDINAL := n; BEGIN VisitAndMark12Orbit(f); IF s3 # NIL THEN WHILE nc < n DO (* Loop Invariant: if Elem.T "e" is in the "s1"-closure of "orb[0..nc-1]", then "s3(e)" is in "orb[0..n-1]". *) WITH g = orb[nc] DO (* Checks "s3(h)" for every "h" in the "s1"-orbit of "g": *) VAR h: Elem.T := g; BEGIN REPEAT WITH a = s3(h) DO IF NOT a.p.Elem.T[a.r].mark THEN VisitAndMark12Orbit(a) END END; IF s1 # NIL THEN h := s1(h) END; UNTIL h = g; END; INC(nc) END END END END VisitAndMark123Orbit PROCEDURE Unmark1Orbit (f: Elem.T) = (* Removes the mark from all Elem.Ts in the "s1"-orbit of "f". *) VAR g: Elem.T := f; BEGIN REPEAT g.p.Elem.T[g.r].mark := FALSE; IF s1 # NIL THEN g := s1(g) END; UNTIL g = f; END Unmark1Orbit; BEGIN TRY VisitandMark123Orbit(a); FINALLY (* Erase all marks *) WHILE n > 0 DO DEC(n); UnmarkOrbit(orb[n]) END END END Enum3; BEGIN END EnumOrbits. /* ---------------------------------------------------------------------- */ /* sphere-splines/SPTriang.c */ Arc_vec_t RenumberVertices(Arc_vec_t arc) { auto void VisitOrg(Arc a); /* Visits vertex {Org(a)}. */ auto bool_t VisitedOrg(Arc a); /* TRUE if {Org(a)} has been visited already. */ Arc_vec_t r = SPQuad_Arc_vec_new(1); int nVertices = 0; int i; bool_t VisitedOrg(Arc a) { Site *v = Org(a); return (v != NULL) && (v->num < nVertices) && (Org(r.el[v->num]) == v); } void VisitOrg(Arc a) { Site *v = Org(a); Arc b = a; v->num = nVertices; do { affirm(Org(b) == v, "inconsistent Org"); b = Onext(b); } while (b != a); SPQuad_Arc_vec_expand(&(r), nVertices); r.el[nVertices] = a; nVertices++; } for (i = 0; i < arc.nel; i++) { if (! VisitedOrg(arc.el[i])) { VisitOrg(arc.el[i]); } } SPQuad_Arc_vec_trim(&(r), nVertices); return r; } #define INIT_VISITED_SIZE 1024 Arc_vec_t SPTriang_CreateFaceRecords(Arc_vec_t arc) { auto bool_t VisitedLeft(SPQuad_Arc a); /* TRUE if face {Left(a)} has been visited before. */ auto void VisitLeft(Arc a); /* If the left face of {a} has not been visited yet, create its face record, set all pointers to it, and mark that face as visited. */ Arc_vec_t side = SPQuad_Arc_vec_new(INIT_VISITED_SIZE); int nFaces = 0; /* A face has been visited if all arcs around it have the {Left} field pointing to the same triangle record {t}, and {Left(side[f.num]) == t}. Outside of {VisitLeft}, either all arcs around the face satisfy this condition, or none of them does. */ void VisitLeft(Arc a) { if (! VisitedLeft(a)) { Face *t = (Face *)malloc(sizeof(Face)); Arc b = a; int order = 0; t->num = nFaces; r3x3_zero(&(t->b2c)); r3x3_zero(&(t->c2b)); t->sp = (S2Point_vec_t){ 0, NULL }; t->wp = (double_vec_t){ 0, NULL }; do { Ldata(b) = t; b = Lnext(b); order++; } while (b != a); affirm(order == 3, "non-triangular face"); SPQuad_Arc_vec_expand(&side, nFaces); side.el[nFaces] = a; nFaces++; } } bool_t VisitedLeft(Arc a) { Face *t = Left(a); return (t != NULL) && (t->num < nFaces) && (Left(side.el[t->num]) == t); } int i; int nClosed = 0; /* Faces {Left(side.el[0..nClosed-1])} are the visited faces whose children were already visited. The children of {Left(e)}, by definition, are all faces that share a vertex or edge with {Left(e)}. */ /* Put the given faces on the visited (minus repetitions): */ for (i = 0; i < arc.nel; i++) { VisitLeft(arc.el[i]); } /* Visit descendants of visited faces in BFS order: */ while (nClosed < nFaces ) { Arc s = side.el[nClosed]; /* Enumerate edges around {Left(s)} including {s}: */ Arc e = s; do { /* Enumerate edges around {Dest(e)} except {e}: */ Arc a = Dnext(e); while (a != e) { VisitLeft(a); a = Dnext(a); } e = Lnext(e); } while (e != s); nClosed = nClosed + 1; } /* Return the triangles which were found: */ SPQuad_Arc_vec_trim(&side, nFaces); return side; } /* ---------------------------------------------------------------------- */ /* sphere-splines/SPQuad.c */ #define Mark(e) (((SPQuad_EdgeRec *)((e)&0xfffffffcu))->mark) SPQuad_Arc SPQuad_MakeEdge(void) { SPQuad_Arc e; e = (SPQuad_Arc)notnull(malloc(sizeof(SPQuad_EdgeRec)), "no mem for edge"); Onext(e) = e; SymDnext(e) = Sym(e); RotRnext(e) = Tor(e); TorLnext(e) = Rot(e); Edge(e)->num = 0; Odata(e) = NULL; Ldata(e) = NULL; Ddata(e) = NULL; Rdata(e) = NULL; return e; } void SPQuad_DestroyEdge(SPQuad_Arc e) { SPQuad_Arc f = Sym(e); if (Onext(e) != e) SPQuad_Splice(e, Oprev(e)); if (Onext(f) != f) SPQuad_Splice(f, Oprev(f)); free((char *)((e) & 0xfffffffcu)); } void SPQuad_Splice(SPQuad_Arc a, SPQuad_Arc b) { SPQuad_Arc ta, tb; SPQuad_Arc alpha = Rot(Onext(a)); SPQuad_Arc beta = Rot(Onext(b)); ta = Onext(a); tb = Onext(b); Onext(a) = tb; Onext(b) = ta; ta = Onext(alpha); tb = Onext(beta); Onext(alpha) = tb; Onext(beta) = ta; } SPQuad_Arc_vec_t SPQuad_RenumberEdges(SPQuad_Arc_vec_t a, nat_t start) { auto void VisitEdge(SPQuad_Arc a); /* If {Edge(a)} has not been visited before, renumber it. */ auto bool_t VisitedEdge(SPQuad_Arc a); /* TRUE if {Edge(a)} has been visited before. */ #define GUESS_NVISITED 1024 SPQuad_Arc_vec_t visited = SPQuad_Arc_vec_new(GUESS_NVISITED); int nVisited = 0; /* {visited.el[0..nVisited-1]} contain one arc from each visited edge. An edge record {e} has been visited if {Edge(visited.el[e->num - start]) == e}. */ bool_t VisitedEdge(SPQuad_Arc a) { nat_t en = Edge(a)->num; return (en >= start) && (en < start + nVisited) && (Edge(visited.el[en-start]) == Edge(a)); } void VisitEdge(SPQuad_Arc a) { if (! VisitedEdge(a)) { /* Edge {Edge(a)} hasn't been visited yet. */ SPQuad_Arc_vec_expand(&visited, nVisited); Edge(a)->num = start + nVisited; visited.el[nVisited] = a; nVisited++; } } int i; int nClosed = 0; /* Edges {Edge(visited.el[0..nClosed-1])} are the edges whose children were already visited. The children of {e}, by definition, are {Onext(e)} and {Onext(Sym(e))}. */ /* Put the given arcs on the visited (minus repetitions): */ for (i = 0; i < a.nel; i++) { VisitEdge(a.el[i]); } /* Visit descendants of visited edges in BFS order: */ while (nClosed < nVisited ) { SPQuad_Arc s = visited.el[nClosed]; VisitEdge(Onext(s)); VisitEdge(Onext(Sym(s))); nClosed++; } /* Return the edges which were found: */ SPQuad_Arc_vec_trim(&visited, nVisited); return visited; } /* ---------------------------------------------------------------------- */ /* quadwfc/wavefront.c */ qarc_vec_t renumber_edges(qarc_vec_t a, unsigned int start) { auto void visit_edge(qarc_t a); /* If {Edge(a)} has not been visited before, renumber it. */ auto bool_t edge_is_visited(qarc_t a); /* TRUE if {Edge(a)} has been visited before. */ #define GUESS_NVISITED 1024 qarc_vec_t visited = qarc_vec_new(GUESS_NVISITED); int nVisited = 0; /* {visited.el[0..nVisited-1]} contain one arc from each visited edge. An edge record {e} has been visited if {Edge(visited.el[e->num - start]) == e}. */ bool_t edge_is_visited(qarc_t a) { unsigned int en = EDGE(a)->mark; return (en >= start) && (en < start + nVisited) && (EDGE(visited.el[en-start]) == EDGE(a)); } void visit_edge(qarc_t a) { if (! edge_is_visited(a)) { /* Edge {Edge(a)} hasn't been visited yet. */ qarc_vec_expand(&visited, nVisited); EDGE(a)->mark = start + nVisited; visited.el[nVisited] = a; nVisited++; } } int i; int nClosed = 0; /* Edges {EDGE(visited.el[0..nClosed-1])} are the edges whose children were already visited. The children of {e}, by definition, are {Onext(e)} and {Onext(Sym(e))}. */ /* Put the given arcs on the visited (minus repetitions): */ for (i = 0; i < a.nel; i++) { visit_edge(a.el[i]); } /* Visit descendants of visited edges in BFS order: */ while (nClosed < nVisited ) { qarc_t s = visited.el[nClosed]; visit_edge(ONEXT(s)); visit_edge(ONEXT(SYM(s))); nClosed++; } /* Return the edges which were found: */ qarc_vec_trim(&visited, nVisited); /* fprintf(stderr, "visited %d edges\n", nVisited); */ return visited; } qarc_vec_t arcs_from_edges(qarc_vec_t edge) { qarc_vec_t arc = qarc_vec_new(2*edge.nel); int i; for (i = 0; i < edge.nel; i++) { arc.el[2*i] = edge.el[i]; arc.el[2*i+1] = SYM(edge.el[i]); } return arc; } qarc_vec_t renumber_vertices(qarc_vec_t arc) { auto void visit_org(qarc_t a); /* Visits vertex {ORG(a)}. */ auto bool_t org_is_visited(qarc_t a); /* TRUE if {ORG(a)} has been visited already. */ qarc_vec_t r = qarc_vec_new(1); int nVertices = 0; int i; for (i = 0; i < arc.nel; i++) { if (! org_is_visited(arc.el[i])) { visit_org(arc.el[i]); } } qarc_vec_trim(&(r), nVertices); /* fprintf(stderr, "visited %d vertices\n", nVertices); */ return r; bool_t org_is_visited(qarc_t a) { segment_t *v = ORG(a); affirm(v != NULL, "null vertex"); return (v->num < nVertices) && (ORG(r.el[v->num]) == v); } void visit_org(qarc_t a) { segment_t *v = ORG(a); affirm(v != NULL, "null vertex"); qarc_t b = a; v->num = nVertices; do { affirm(ORG(b) == v, "inconsistent ORG"); b = ONEXT(b); } while (b != a); qarc_vec_expand(&(r), nVertices); r.el[nVertices] = a; nVertices++; } } qarc_vec_t renumber_faces(qarc_vec_t arc) { auto void visit_left(qarc_t a); /* Visits face {Left(a)}. */ auto bool_t left_is_visited(qarc_t a); /* TRUE if {Left(a)} has been visited already. */ qarc_vec_t r = qarc_vec_new(1); int nFaces = 0; int i; bool_t left_is_visited(qarc_t a) { face_t *f = LEFT(a); affirm(f != NULL, "null face"); return (f->num < nFaces) && (LEFT(r.el[f->num]) == f); } void visit_left(qarc_t a) { face_t *f = LEFT(a); affirm(f != NULL, "null face"); qarc_t b = a; f->num = nFaces; do { affirm(LEFT(b) == f, "inconsistent Left"); b = LNEXT(b); } while (b != a); qarc_vec_expand(&(r), nFaces); r.el[nFaces] = a; nFaces++; } for (i = 0; i < arc.nel; i++) { if (! left_is_visited(arc.el[i])) { visit_left(arc.el[i]); } } qarc_vec_trim(&(r), nFaces); /* fprintf(stderr, "visited %d faces\n", nFaces); */ return r; } #define INIT_VISITED_SIZE 1024 void create_face_records(qarc_t e) { auto bool_t left_is_visited(qarc_t a); /* TRUE if face {LEFT(a)} has been visited before. */ auto void visit_left(qarc_t a); /* If the left face of {a} has not been visited yet, create its face record, set all pointers to it, and mark that face as visited. */ qarc_vec_t side = qarc_vec_new(INIT_VISITED_SIZE); int nFaces = 0; /* A face has been visited if all arcs around it have the {Left} field pointing to the same triangle record {f}, and {LEFT(side[f.num]) == f}. Outside of {visit_left}, either all arcs around the face satisfy this condition, or none of them does. */ void visit_left(qarc_t a) { if (! left_is_visited(a)) { face_t *f = (face_t *)malloc(sizeof(face_t)); qarc_t b = a; int order = 0; f->num = nFaces; f->omit = FALSE; do { SET_LEFT(b, f); b = LNEXT(b); order++; } while (b != a); qarc_vec_expand(&side, nFaces); side.el[nFaces] = a; nFaces++; } } bool_t left_is_visited(qarc_t a) { face_t *f = LEFT(a); return (f != NULL) && (f->num < nFaces) && (LEFT(side.el[f->num]) == f); } int nClosed = 0; /* Faces {LEFT(side.el[0..nClosed-1])} are the visited faces whose children were already visited. The children of {LEFT(e)}, by definition, are all faces that share a face or edge with {LEFT(e)}. */ /* Put the given face on the visited (minus repetitions): */ visit_left(e); /* Visit descendants of visited faces in BFS order: */ while (nClosed < nFaces ) { qarc_t s = side.el[nClosed]; /* Enumerate edges around {LEFT(s)} including {s}: */ qarc_t e = s; do { /* Enumerate edges around {Dest(e)} except {e}: */ qarc_t a = DNEXT(e); while (a != e) { visit_left(a); a = DNEXT(a); } e = LNEXT(e); } while (e != s); nClosed = nClosed + 1; } /* Be nice, recycle: */ free(side.el); } /* ---------------------------------------------------------------------- */ /* libquad/quad.c */ /* Enumerate edge quads */ void quad_do_enum ( quad_arc_t a, void visit_proc(quad_arc_t e, void *closure), void *closure, unsigned mark ); unsigned next_mark = 1; void quad_enum( quad_arc_t a, void visit_proc(quad_arc_t e, void *closure), void *closure ) { unsigned mark = next_mark; next_mark++; if (next_mark == 0) next_mark = 1; quad_do_enum(a, visit_proc, closure, mark); } void quad_do_enum ( quad_arc_t e, void visit_proc(quad_arc_t e, void *closure), void *closure, unsigned mark ) { while (MARK(e) != mark) { visit_proc(e, closure); MARK(e) = mark; quad_do_enum (ONEXT(SYM(e)), visit_proc, closure, mark); e = ONEXT(e); } }