PROCEDURE WriteSurface( WITH v = SortedFaceCorners(f.v, f.c, cell) PROCEDURE SortedFaceCorners( READONLY fv: Face.Corners; READONLY fc: Face.SideCells; READONLY cell: Cell.List; ): Face.Corners = VAR which: [0..4] := 4; BEGIN IF fc[0] = Cell.None OR fc[1] # Cell.None THEN RETURN fv END; (* Find which face of the cell "f" is: *) WITH c = cell[fc[0]], cv = c.v DO FOR i := 0 TO 3 DO IF cv[i] # fv[0] AND cv[i] # fv[1] AND cv[i] # fv[2] THEN <* ASSERT which = 4 *> which := i END END; CASE which OF | 0 => RETURN Face.Corners{cv[1], cv[2], cv[3]}; | 1 => RETURN Face.Corners{cv[0], cv[3], cv[2]}; | 2 => RETURN Face.Corners{cv[3], cv[0], cv[1]}; | 3 => RETURN Face.Corners{cv[2], cv[1], cv[0]}; | 4 => <* ASSERT FALSE *> END END END SortedFaceCorners; PROCEDURE VertexCurl( v: Vertex.Num; READONLY cell: List; READONLY vertex: Vertex.List ): LR3.T; (* Computes the vertex curl (``un-normalized vertex normal'') for an exposed vertex "v". The result is equivalent to "Vertex.RingCurl" on the ring of exposed vertices adjacent to "v", taken in counterclockwise sense when seen from the outside. The result will be garbage (theoretically zero, except for rounding errors) if "v" is not an exposed vertex. *) PROCEDURE VertexNormal( v: Vertex.Num; READONLY cell: List; READONLY vertex: Vertex.List ): LR3.T = VAR d: LR3.T; BEGIN (* Computes the sum of the inward curls of all faces opposite to "v" in all cells incident to "v". If "v" is exposed, things will neatly cancel out to give the curl of the exposed edges surrounding "v". *) FOR i := 0 TO LAST(cell) DO WITH c = cell[i] DO IF c.v[0] = v THEN AddCurl(c.v[0], c.v[1], c.v[2], c.v[3]) ELSIF c.v[1] = v THEN AddCurl(c.v[1], c.v[0], c.v[2], c.v[3]) ELSIF c.v[2] = v THEN AddCurl(c.v[2], c.v[0], c.v[1], c.v[3]) ELSIF c.v[3] = v THEN AddCurl(c.v[3], c.v[0], c.v[1], c.v[2]) END END; END; RETURN d END VertexCurl; PROCEDURE GetStar(READONLY edge: List; v: Vertex.Num): REF ARRAY OF Vertex.Num = VAR nn: CARDINAL := 0; (* Neighbor count *) nk: CARDINAL := 0; (* Number of neighbor-neighbor edges *) nv: CARDINAL := 0; (* Number of vertices *) BEGIN <* ASSERT v # Vertex.None *> (* Find effective vertex count "nv": *) FOR i := 0 TO LAST(edge) DO FOR k := 0 TO 1 DO WITH u = edge[i].v[k] DO <* ASSERT u # Vertex.None *> nv := MAX(nv, u+1) END END END; WITH ne = NUMBER(edge), mark = NEW(REF ARRAY OF BOOLEAN, nv)^ DO FOR u := 0 TO nv-1 DO mark[u] := FALSE END; (* Set "mark[x]=TRUE" for every neighbor of "v": *) nn := 0; FOR k := 0 TO LAST(edge) DO WITH e = edge[k], x = e.v[0], y = e.v[1] DO IF x = v THEN <* ASSERT y # v AND NOT mark[y] *> mark[y] := TRUE; INC(nn) ELSIF y = v THEN <* ASSERT x # v AND NOT mark[x] *> mark[x] := TRUE; INC(nn) END END END; WITH link = NEW(REF List, nn)^ DO (* Collect edges that link neighbors: *) nk := 0; FOR k := 0 TO LAST(edge) DO WITH e = edge[k], x = e.v[0], y = e.v[1] DO IF mark[x] AND mark[y] THEN link[nk] := e; INC(nk) END END END; <* ASSERT nk = nn *> (* Sort linking edges in connected order: *) FOR i := 1 TO nk-2 DO WITH u = link[i-1].v[1] DO FOR j := i TO nk-1 DO WITH e = link[j], r = e.v[0], s = e.v[1] DO IF s = u THEN VAR z := r; BEGIN r := s; s := z END END; IF r = u AND i # j THEN VAR z := link[i]; BEGIN link[i] := link[j]; link[j] := z END; END END; END; <* ASSERT link[i].v[0] = u *> END END; <* ASSERT link[0].v[0] = link[nk-1].v[1] *> (* Return vertex numbers: *) WITH r = NEW(REF ARRAY OF Vertex.Num, nn+1), star = r^ DO star[0] := v; FOR k := 0 TO nn-1 DO star[k+1] := link[k].v[0] END; RETURN r END END END END GetStar; PROCEDURE GuessTexture(name: TEXT): CARDINAL = VAR hash: Word.T := 0; k: CARDINAL := 0; BEGIN WITH n = Text.Length(name) DO WHILE k < n AND Text.GetChar(name, k) # '.' DO hash := Word.Plus(Word.Times(hash, 27), ORD(Text.GetChar(name, k))); INC(k) END; RETURN hash MOD 6 END END GuessTexture; \ PROCEDURE SetPair(r, s: Vertex.Num) = BEGIN IF pa[s] = Vertex.None THEN pa[s] := r ELSE <* ASSERT pb[s] = Vertex.None *> pb[s] := r END; IF pa[r] = Vertex.None THEN pa[r] := s ELSE <* ASSERT pb[r] = Vertex.None *> pb[r] := s END; INC(nn) END SetPair;