<<>> <> <> <> <> <> <> DIRECTORY Draw2d, G2dBasic, G2dVector, G3dBasic, G3dDraw, G3dMatrix, G3dModel, G3dPlane, G3dPolygon, G3dShape, G3dSpline, G3dVector, Imager, Real, RefTab, Rope, RuntimeError; G3dModelImpl: CEDAR MONITOR IMPORTS G2dBasic, G2dVector, G3dBasic, G3dDraw, G3dMatrix, G3dPlane, G3dPolygon, G3dShape, G3dSpline, G3dVector, RefTab, RuntimeError EXPORTS G3dModel ~ BEGIN <> DrawType: TYPE ~ Draw2d.DrawType; Border: TYPE ~ G3dBasic.Border; Box: TYPE ~ G3dBasic.Box; Pair: TYPE ~ G3dBasic.Pair; Triple: TYPE ~ G3dBasic.Triple; Plane: TYPE ~ G3dPlane.Plane; Ray: TYPE ~ G3dBasic.Ray; Segment: TYPE ~ G3dBasic.Segment; NatSequence: TYPE ~ G3dBasic.NatSequence; NatSequenceRep: TYPE ~ G3dBasic.NatSequenceRep; IntegerPair: TYPE ~ G3dBasic.IntegerPair; IntSequence: TYPE ~ G3dBasic.IntSequence; IntSequenceRep: TYPE ~ G3dBasic.IntSequenceRep; SurfaceSequence: TYPE ~ G3dBasic.SurfaceSequence; SurfaceSequenceRep: TYPE ~ G3dBasic.SurfaceSequenceRep; PairSequence: TYPE ~ G3dBasic.PairSequence; PairSequenceRep: TYPE ~ G3dBasic.PairSequenceRep; TripleSequence: TYPE ~ G3dBasic.TripleSequence; TripleSequenceRep: TYPE ~ G3dBasic.TripleSequenceRep; Matrix: TYPE ~ G3dMatrix.Matrix; Viewport: TYPE ~ G3dMatrix.Viewport; Model: TYPE ~ G3dModel.Model; ModelRep: TYPE ~ G3dModel.ModelRep; NearModel: TYPE ~ G3dModel.NearModel; Polygon: TYPE ~ G3dPolygon.Polygon; PolygonSequenceRep: TYPE ~ G3dPolygon.PolygonSequenceRep; Edge: TYPE ~ G3dShape.Edge; EdgeRep: TYPE ~ G3dShape.EdgeRep; EdgeSequence: TYPE ~ G3dShape.EdgeSequence; Face: TYPE ~ G3dShape.Face; FaceRep: TYPE ~ G3dShape.FaceRep; FaceSequence: TYPE ~ G3dShape.FaceSequence; FaceSequenceRep: TYPE ~ G3dShape.FaceSequenceRep; ScreenSequence: TYPE ~ G3dShape.ScreenSequence; Shape: TYPE ~ G3dShape.Shape; ShapeRep: TYPE ~ G3dShape.ShapeRep; ShapeSequence: TYPE ~ G3dShape.ShapeSequence; SurfaceProc: TYPE ~ G3dShape.SurfaceProc; Vertex: TYPE ~ G3dShape.Vertex; VertexRep: TYPE ~ G3dShape.VertexRep; VertexSequence: TYPE ~ G3dShape.VertexSequence; VertexSequenceRep: TYPE ~ G3dShape.VertexSequenceRep; SplineSequence: TYPE ~ G3dSpline.SplineSequence; SplineSequenceRep: TYPE ~ G3dSpline.SplineSequenceRep; Context: TYPE ~ Imager.Context; ROPE: TYPE ~ Rope.ROPE; BoundsFault: ERROR ~ RuntimeError.BoundsFault; Error: PUBLIC SIGNAL [code: ATOM, reason: ROPE] = CODE; <> ShapeFromFile: PUBLIC PROC [ fileName: ROPE, points: BOOL ¬ TRUE, polygons: BOOL ¬ FALSE, faceCenters: BOOL ¬ TRUE, faceNormals: BOOL ¬ TRUE, edges: BOOL ¬ TRUE, curves: BOOL ¬ FALSE, normal: BOOL ¬ FALSE, center: BOOL ¬ FALSE, lines: BOOL ¬ FALSE, accs: BOOL ¬ FALSE, area: BOOL ¬ FALSE] RETURNS [s: Shape] ~ { s ¬ G3dShape.ShapeFromFile[fileName]; SetShape[s, points, polygons, faceCenters, faceNormals, edges, curves, normal, center, lines, accs, area]; }; <> GetModel: PUBLIC PROC [shape: Shape] RETURNS [m: Model] ~ { m ¬ SELECT TRUE FROM shape = NIL => NIL, shape.modelData # NIL => NARROW[shape.modelData], ENDCASE => (shape.modelData ¬ NEW[ModelRep]); }; SetShape: PUBLIC PROC [ shape: Shape, points: BOOL ¬ TRUE, polygons: BOOL ¬ FALSE, faceCenters: BOOL ¬ TRUE, faceNormals: BOOL ¬ TRUE, edges: BOOL ¬ TRUE, curves: BOOL ¬ FALSE, normal: BOOL ¬ FALSE, center: BOOL ¬ FALSE, lines: BOOL ¬ FALSE, accs: BOOL ¬ FALSE, area: BOOL ¬ FALSE] ~ { IF points THEN SetPoints[shape]; IF faceCenters THEN G3dShape.SetFaceCenters[shape]; IF faceNormals AND NOT G3dShape.FaceValid[shape, normal] THEN G3dShape.SetFaceNormals[shape, FALSE]; IF polygons THEN SetPolygons[shape, normal, center, area, accs, lines]; IF edges THEN shape.edges ¬ G3dShape.MakeEdges[shape ! BoundsFault => CONTINUE]; IF curves THEN SetCurves[shape]; }; <> SetPoints: PUBLIC PROC [shape: Shape] ~ { m: Model ¬ GetModel[shape]; m.points ¬ G3dShape.PointsFromShape[shape, m.points]; }; IsolateVertices: PUBLIC PROC [shape: Shape] ~ { model: Model ¬ GetModel[shape]; nvert, vertno: NAT ¬ 0; old: VertexSequence ¬ shape.vertices; <> FOR i: NAT IN [0..shape.surfaces.length) DO nvert ¬ nvert+shape.surfaces[i].vertices.length; ENDLOOP; shape.vertices ¬ NEW[VertexSequenceRep[nvert]]; -- allocate new vertices shape.vertices.valid ¬ old.valid; shape.vertices.length ¬ nvert; shape.verticesIsolated ¬ TRUE; <> FOR i: NAT IN [0..shape.surfaces.length) DO -- for each polygon poly: NatSequence ¬ shape.surfaces[i].vertices; -- this polygon FOR j: NAT IN [0..poly.length) DO -- for each vertex of poly shape.vertices[vertno] ¬ NEW[VertexRep ¬ old[poly[j]]^]; -- new vertex poly[j] ¬ vertno; -- modify index list vertno ¬ vertno+1; ENDLOOP; ENDLOOP; }; DeleteVertices: PUBLIC PROC [shape: Shape, vId0, vId1: NAT] ~ { nPoly: NAT ¬ 0; nDeleted: NAT ¬ ABS[vId1-vId0]+1; FOR n: NAT IN [MIN[vId0, vId1]..shape.vertices.length-nDeleted) DO shape.vertices[n] ¬ shape.vertices[n] ¬ shape.vertices[n+nDeleted]; ENDLOOP; WHILE nPoly < shape.surfaces.length DO poly: NatSequence ¬ shape.surfaces[nPoly].vertices; FOR p: NAT IN [0..poly.length) DO SELECT poly[p] FROM IN [vId0..vId1] => { -- this polygon now bogus FOR n: NAT IN [nPoly..shape.surfaces.length-1) DO shape.surfaces[n] ¬ shape.surfaces[n-1]; ENDLOOP; shape.surfaces.length ¬ shape.surfaces.length-1; LOOP; }; > vId1 => poly[p] ¬ poly[p]-nDeleted; ENDCASE; nPoly ¬ nPoly+1; ENDLOOP; ENDLOOP; }; <> SetPolygons: PUBLIC PROC [ shape: Shape, normal, center, area, accs, lines: BOOL, complyWithVertices: BOOL ¬ TRUE] ~ { model: Model ¬ GetModel[shape]; normalFromFace: BOOL ¬ normal AND G3dShape.FaceValid[shape, normal]; IF normalFromFace THEN normal ¬ FALSE; -- we'll get from shape.faces IF shape.vertices = NIL AND shape.surfaces = NIL THEN RETURN; IF model.polygons = NIL OR model.polygons.maxLength < shape.surfaces.length THEN model.polygons ¬ NEW[PolygonSequenceRep[shape.surfaces.length]]; model.polygons.length ¬ shape.surfaces.length; FOR n: NAT IN [0..shape.surfaces.length) DO poly: NatSequence ¬ shape.surfaces[n].vertices; polygon: Polygon ¬ model.polygons[n] ¬ NEW[G3dPolygon.PolygonRep]; polygon.points ¬ NEW[TripleSequenceRep[poly.length]]; polygon.points.length ¬ poly.length; polygon.indices ¬ poly; FOR n: NAT IN [0..poly.length) DO polygon.points[n] ¬ shape.vertices[poly[n]].point; ENDLOOP; IF normalFromFace THEN { -- if complyWithVertices, assume faces[n].normal complies v: Triple ¬ shape.faces[n].normal; polygon.plane ¬ [v.x, v.y, v.z, G3dPlane.DFromNormalAndPoints[v, polygon.points]]; IF normalFromFace THEN polygon.normal ¬ v; }; G3dPolygon.SetPolygon[polygon, polygon.points, normal, center, area, accs, lines]; ENDLOOP; IF complyWithVertices AND shape.vertices # NIL AND normal THEN <> FOR n: NAT IN [0..shape.surfaces.length) DO p: Polygon ¬ model.polygons[n]; v: Triple ¬ IF normal THEN p.normal ELSE [p.plane.x, p.plane.y, p.plane.z]; IF G3dVector.Dot[v, shape.vertices[p.indices[0]].normal] < 0.0 THEN { p.plane ¬ [-p.plane.x, -p.plane.y, -p.plane.z, -p.plane.w]; IF normal THEN p.normal ¬ [-p.plane.x, -p.plane.y, -p.plane.z]; }; ENDLOOP; }; CullBadPolygons: PUBLIC PROC [polygons: SurfaceSequence, maxNVertices: NAT] ~ { n: NAT ¬ 0; DO poly: NatSequence ¬ polygons[n].vertices; FOR nn: NAT IN [0..poly.length) DO IF poly[nn] >= maxNVertices THEN { -- overwrite this polygon polygons.length ¬ polygons.length-1; IF n # polygons.length THEN polygons[n] ¬ polygons[polygons.length]; EXIT; }; REPEAT FINISHED => n ¬ n+1; ENDLOOP; IF n >= polygons.length THEN RETURN; ENDLOOP; }; <> EnsureModelPolygons: PROC [s: Shape] RETURNS [m: Model] ~ { IF (m ¬ GetModel[s]) # NIL THEN { ok: BOOL ¬ m.polygons # NIL AND m.polygons.length # 0; IF ok THEN { p: Polygon ¬ m.polygons[0]; ok ¬ p.plane # [] AND p.accs # NIL AND p.lines # NIL; }; IF NOT ok THEN SetPolygons[s, TRUE, TRUE, FALSE, TRUE, TRUE]; }; }; IntersectWithRay: PUBLIC PROC [shape: Shape, ray: Ray] RETURNS [near: NearModel] ~ { model: Model ¬ EnsureModelPolygons[shape]; minSqDistance: REAL ¬ Real.LargestNumber; IF model = NIL THEN RETURN; FOR n: NAT IN [0..model.polygons.length) DO polygon: Polygon ¬ model.polygons[n]; pdDot: REAL ¬ G3dVector.Dot[polygon.normal, ray.axis]; -- see G3dPlaneImpl IF pdDot # 0.0 THEN { alpha: REAL ¬ (-polygon.plane.w-G3dVector.Dot[polygon.normal, ray.base])/pdDot; IF alpha >= 0.0 THEN { -- if < 0.0 then no intersection in direction of ray intersection: Triple ¬ G3dVector.ScaleRay[ray, alpha]; sqDistance: REAL ¬ G3dVector.SquareDistance[intersection, ray.base]; IF sqDistance < minSqDistance THEN { IF G3dPolygon.InsidePolygon[intersection, polygon] # outside THEN { minSqDistance ¬ sqDistance; near ¬ [intersection, FALSE, n]; }; }; }; }; ENDLOOP; }; ClosestPoint: PUBLIC PROC [shape: Shape, point: Triple] RETURNS [near: NearModel] ~ { model: Model ¬ EnsureModelPolygons[shape]; minDistance: REAL ¬ Real.LargestNumber; IF model # NIL THEN FOR n: NAT IN [0..model.polygons.length) DO polygon: Polygon ¬ model.polygons[n]; nearPolygon: G3dPolygon.NearPolygon ¬ G3dPolygon.NearestToPolygon[polygon, point]; IF nearPolygon.distance < minDistance THEN { near ¬ [nearPolygon.point, FALSE, n]; minDistance ¬ nearPolygon.distance; }; ENDLOOP; }; <> ShapeFromRevolvedCurve: PUBLIC PROC [ curve: PairSequence, axis: Ray, res: NAT, start: NAT ¬ 0, stop: NAT ¬ LAST[NAT]] RETURNS [s: Shape] ~ { newStop: NAT ¬ MIN[res, stop]; nStrips: NAT ¬ IF start < newStop THEN newStop-start ELSE res-start+newStop; mat: G3dMatrix.Matrix ¬ G3dMatrix.ObtainMatrix[]; nthVertex, nthPolygon: CARD ¬ 0; nPolygons: CARD ¬ nStrips*(curve.length-1); nVertices: CARD ¬ (IF nStrips = res THEN nStrips ELSE nStrips+1)*curve.length; curveNormals: PairSequence ¬ NormalsFromCurve[curve]; s ¬ NEW[ShapeRep]; s.surfaces ¬ NEW[SurfaceSequenceRep[nPolygons]]; s.vertices ¬ NEW[VertexSequenceRep[nVertices]]; s.surfaces.length ¬ nPolygons; s.vertices.length ¬ nVertices; s.vertices.valid[normal] ¬ TRUE; FOR n: NAT IN [0..IF nStrips = res THEN nStrips-1 ELSE nStrips] DO a: NAT ¬ (n+start) MOD res; theta: REAL ¬ 2.0*G3dBasic.PI*REAL[a]/REAL[res]; mat ¬ G3dMatrix.MakeRotateAbout[axis.axis, theta, FALSE, axis.base, mat]; FOR i: NAT IN [0..curve.length) DO p: Pair ¬ curve[i]; pn: Pair ¬ curveNormals[i]; v: Vertex ¬ s.vertices[nthVertex] ¬ NEW[VertexRep]; v.point ¬ G3dMatrix.Transform[[p.x, 0.0, p.y], mat]; v.normal ¬ G3dMatrix.TransformVec[[pn.x, 0.0, pn.y], mat]; nthVertex ¬ nthVertex+1; ENDLOOP; ENDLOOP; FOR n: NAT IN [0..nStrips) DO FOR i: NAT IN [0..curve.length-1) DO p: NatSequence ¬ s.surfaces[nthPolygon].vertices ¬ NEW[NatSequenceRep[4]]; p.length ¬ 4; p[0] ¬ n*curve.length+i; p[1] ¬ p[0]+1; p[2] ¬ (p[1]+curve.length) MOD nVertices; p[3] ¬ (p[0]+curve.length) MOD nVertices; nthPolygon ¬ nthPolygon+1; ENDLOOP; ENDLOOP; G3dMatrix.ReleaseMatrix[mat]; }; <<>> ShapeFromPointsPolys: PUBLIC PROC [ name: ROPE, points: TripleSequence, polys: SurfaceSequence] RETURNS [s: Shape] ~ { s ¬ NEW[ShapeRep ¬ [name: name, surfaces: polys, matrix: G3dMatrix.Identity[]]]; s.vertices ¬ NEW[VertexSequenceRep[points.length]]; s.vertices.length ¬ points.length; FOR n: NAT IN [0..points.length) DO s.vertices[n] ¬ NEW[VertexRep ¬ [point: points[n]]]; ENDLOOP; s.objectExtent ¬ G3dShape.BoundingBox[s]; }; PolyFromTwoPoints: PROC [n0, n1: NAT] RETURNS [poly: NatSequence] ~ { poly ¬ NEW[NatSequenceRep[2]]; poly[0] ¬ n0; poly[1] ¬ n1; poly.length ¬ 2; }; ShapeFrom2dCurve: PUBLIC PROC [name: ROPE, curve: PairSequence] RETURNS [s: Shape] ~ { IF curve # NIL AND curve.length > 1 THEN { triples: TripleSequence ¬ NEW[TripleSequenceRep[curve.length]]; triples.length ¬ curve.length; FOR n: NAT IN [0..curve.length) DO p: Pair ¬ curve[n]; triples[n] ¬ [p.x, 0.0, p.y]; ENDLOOP; s ¬ ShapeFrom3dCurve[name, triples]; }; }; ShapeFrom3dCurve: PUBLIC PROC [name: ROPE, curve: TripleSequence] RETURNS [s: Shape] ~ { IF curve # NIL AND curve.length > 1 THEN { polys: SurfaceSequence ¬ NEW[SurfaceSequenceRep[curve.length-1]]; polys.length ¬ curve.length-1; FOR n: NAT IN [0..curve.length-1) DO polys[n].vertices ¬ PolyFromTwoPoints[n, n+1]; ENDLOOP; s ¬ ShapeFromPointsPolys[name, curve, polys]; }; }; ShapeFromSegments: PUBLIC PROC [name: ROPE, segments: LIST OF Segment] RETURNS [s: Shape] ~ { nPoints, nPolys: NAT ¬ 0; FOR l: LIST OF Segment ¬ segments, l.rest WHILE l # NIL DO nPolys ¬ nPolys+1; nPoints ¬ nPoints+2; ENDLOOP; IF nPoints > 1 THEN { AddVertex: PROC [p: Triple] ~ { s.vertices[s.vertices.length] ¬ NEW[VertexRep ¬ [point: p]]; s.vertices.length ¬ s.vertices.length+1; }; s ¬ NEW[ShapeRep ¬ [name: name, matrix: G3dMatrix.Identity[]]]; s.surfaces ¬ NEW[SurfaceSequenceRep[nPolys]]; s.vertices ¬ NEW[VertexSequenceRep[nPoints]]; FOR l: LIST OF Segment ¬ segments, l.rest WHILE l # NIL DO AddVertex[l.first.p0]; AddVertex[l.first.p1]; s.surfaces[s.surfaces.length].vertices ¬ PolyFromTwoPoints[s.vertices.length-2,s.vertices.length-1]; s.surfaces.length ¬ s.surfaces.length+1; ENDLOOP; s.objectExtent ¬ G3dShape.BoundingBox[s]; }; }; ShapeFromPoints: PUBLIC PROC [name: ROPE, points: TripleSequence] RETURNS [Shape] ~ { ERROR; }; <> SkipNPolys: PUBLIC PROC [shape: Shape, nPolys: NAT] ~ { FOR n: NAT IN [nPolys..shape.surfaces.length) DO shape.surfaces[n-nPolys] ¬ shape.surfaces[n]; ENDLOOP; shape.surfaces.length ¬ shape.surfaces.length-nPolys; }; Hash: PROC [key: REF ANY] RETURNS [CARD] ~ { i: REF IntegerPair ~ NARROW[key]; RETURN[i.x+i.y]; }; Equal: PROC [key1, key2: REF ANY] RETURNS [BOOL] ~ { RETURN[NARROW[key1, REF IntegerPair]^ = NARROW[key2, REF IntegerPair]^]; }; Cleave: PUBLIC PROC [shape: Shape, plane: Plane, cap: BOOL ¬ FALSE] RETURNS [neg, pos: Shape ¬ NIL] ~ { CapInfo: TYPE ~ RECORD [vID, pID0, pID1: INTEGER ¬ -1]; Caps: TYPE ~ RECORD [len: CARD ¬ 0, s: SEQUENCE max: CARD OF REF CapInfo]; VertexState: TYPE ~ RECORD [v: Vertex, dist: REAL, sign: Border, old, pos, neg: INTEGER]; VertexStates: TYPE ~ RECORD [element: SEQUENCE max: CARD OF VertexState]; IF shape # NIL AND shape.vertices # NIL AND shape.vertices.length > 2 THEN { Init: PROC RETURNS [s: Shape] ~ { s ¬ NEW[ShapeRep]; s.vertices ¬ NEW[VertexSequenceRep[10]]; s.surfaces ¬ NEW[SurfaceSequenceRep[10]]; s.faces ¬ NEW[FaceSequenceRep[10]]; }; AddVertex: PROC [s: Shape, v: Vertex] ~ { s.vertices ¬ G3dShape.AddToVertexSequence[s.vertices, NEW[VertexRep ¬ v^]]; }; AddPoly: PROC [s: Shape, poly: NatSequence, old: NAT] ~ { s.surfaces ¬ G3dBasic.AddToSurfaceSequence[s.surfaces, [NIL, poly]]; IF shape.faces # NIL AND shape.faces.length > old THEN s.faces ¬ G3dShape.AddToFaceSequence[s.faces, NEW[FaceRep ¬ shape.faces[old]^]]; }; AllInPlane: PROC [poly: NatSequence] RETURNS [b: BOOL ¬ TRUE] ~ { FOR i: INTEGER IN [0..poly.length) DO IF vertexStates[poly[i]].sign # edge THEN RETURN[FALSE]; ENDLOOP; }; ToCaps: PROC [caps: REF Caps, vID, pID: INTEGER] RETURNS [ret: REF Caps] ~ { IF (ret ¬ caps) = NIL THEN ret ¬ NEW[Caps[1]]; FOR i: INTEGER IN [0..ret.len) DO IF ret[i].vID = vID THEN {ret[i].pID1 ¬ pID; RETURN}; ENDLOOP; IF ret.len = ret.max THEN { old: REF Caps ¬ ret; ret ¬ NEW[Caps[MAX[2*old.max, 3]]]; FOR i: INTEGER IN [0..(ret.len ¬ old.len)) DO ret[i] ¬ old[i]; ENDLOOP; }; ret[ret.len] ¬ NEW[CapInfo ¬ [vID, pID, -1]]; ret.len ¬ ret.len+1; }; PosNegToCaps: PROC [posVid, negVid, posPid, negPid: INTEGER] ~ { posCaps ¬ ToCaps[posCaps, posVid, posPid]; negCaps ¬ ToCaps[negCaps, negVid, negPid]; }; Cap: PROC [s: Shape, caps: REF Caps] ~ { <> GetAny: PROC RETURNS [r: REF CapInfo] ~ { FOR i: INTEGER IN [0..caps.len) DO IF (r ¬ caps[i]) # NIL THEN EXIT; ENDLOOP; }; GetForIDAndDelete: PROC [pID: INTEGER] RETURNS [r: REF CapInfo] ~ { FOR i: INTEGER IN [0..caps.len) DO IF (r¬caps[i]) # NIL AND (r.pID0=pID OR r.pID1=pID) THEN {caps[i]¬NIL; RETURN}; ENDLOOP; RETURN[NIL]; }; a, b: REF CapInfo; WHILE (a ¬ GetAny[]) # NIL DO poly: NatSequence ¬ NIL; start, id: INTEGER ¬ a.pID0; WHILE (b ¬ GetForIDAndDelete[id]) # NIL DO poly ¬ G2dBasic.AddToNatSequence[poly, b.vID]; id ¬ IF b.pID0 = id THEN b.pID1 ELSE b.pID0; ENDLOOP; IF id = start THEN s.surfaces ¬ G3dBasic.AddToSurfaceSequence[s.surfaces, [, poly]]; ENDLOOP; }; posCaps, negCaps: REF Caps ¬ NIL; hash: RefTab.Ref ¬ RefTab.Create[equal: Equal, hash: Hash]; vertexStates: REF VertexStates ¬ NEW[VertexStates[shape.vertices.length]]; pos ¬ Init[]; neg ¬ Init[]; FOR i: INTEGER IN [0..shape.vertices.length) DO v: Vertex ¬ shape.vertices[i]; dist: REAL ¬ G3dPlane.DistanceToPoint[v.point, plane, FALSE]; sign: Border ¬ SELECT dist FROM > 0.01 => inside, < -0.01 => outside, ENDCASE => edge; vertexStates[i] ¬ [v, dist, sign, i, pos.vertices.length, neg.vertices.length]; IF sign = outside OR sign = edge THEN AddVertex[neg, v]; IF sign = inside OR sign = edge THEN AddVertex[pos, v]; ENDLOOP; FOR n: INTEGER IN [0..shape.surfaces.length) DO << [Artwork node; type 'Artwork on' to command tool] >> oldPoly: NatSequence ¬ shape.surfaces[n].vertices; posPoly, negPoly: NatSequence ¬ NIL; IF AllInPlane[oldPoly] THEN { -- case 1 IF cap THEN FOR i: INTEGER IN [0..oldPoly.length) DO vs: VertexState ¬ vertexStates[oldPoly[i]]; posPoly ¬ G2dBasic.AddToNatSequence[posPoly, vs.pos]; negPoly ¬ G2dBasic.AddToNatSequence[negPoly, vs.neg]; ENDLOOP; } ELSE { -- case 2, 3, 4, or 5 nInside, nOutside, nEdge: INTEGER ¬ 0; s1: VertexState ¬ vertexStates[oldPoly[oldPoly.length-1]]; FOR i: INTEGER IN [0..oldPoly.length) DO SELECT vertexStates[oldPoly[i]].sign FROM inside => nInside ¬ nInside+1; outside => nOutside ¬ nOutside+1; ENDCASE => nEdge ¬ nEdge+1; ENDLOOP; FOR i: INTEGER IN [0..oldPoly.length) DO s0: VertexState ¬ s1; s1 ¬ vertexStates[oldPoly[i]]; IF s0.sign # s1.sign AND s0.sign # edge AND s1.sign # edge THEN { -- interp. indices: IntegerPair; -- pos and neg indices for interpolated vertex key: REF IntegerPair ¬NEW[IntegerPair¬[MIN[s0.old,s1.old],MAX[s0.old,s1.old]]]; ref: REF ¬ RefTab.Fetch[hash, key].val; IF ref # NIL THEN indices ¬ NARROW[ref, REF IntegerPair]^ -- previously stored ELSE { -- interpolate and store v: Vertex ¬ G3dShape.InterpolateVertex[s0.dist/(s0.dist-s1.dist), s0.v, s1.v]; indices ¬ [pos.vertices.length, neg.vertices.length]; [] ¬ RefTab.Store[hash, key, NEW[IntegerPair ¬ indices]]; AddVertex[pos, v]; AddVertex[neg, v]; }; IF cap THEN PosNegToCaps[indices.x, indices.y, pos.surfaces.length, neg.surfaces.length]; posPoly ¬ G2dBasic.AddToNatSequence[posPoly, indices.x]; -- case 2 negPoly ¬ G2dBasic.AddToNatSequence[negPoly, indices.y]; -- case 2 }; IF cap AND s1.sign = edge THEN SELECT TRUE FROM nInside > 0 AND nOutside > 0 => -- case 4 (rightmost poly in fig) PosNegToCaps[s1.pos, s1.neg, pos.surfaces.length, neg.surfaces.length]; nInside > 0 AND nEdge >= 2 => -- case 5 (leftmost poly in fig) posCaps ¬ ToCaps[posCaps, s1.pos, pos.surfaces.length]; nOutside > 0 AND nEdge >= 2 => -- case 5 (leftmost poly in fig) negCaps ¬ ToCaps[negCaps, s1.neg, neg.surfaces.length]; ENDCASE; -- case 3 IF nInside > 0 AND s1.sign # outside THEN posPoly ¬ G2dBasic.AddToNatSequence[posPoly, s1.pos]; IF nOutside > 0 AND s1.sign # inside THEN negPoly ¬ G2dBasic.AddToNatSequence[negPoly, s1.neg]; ENDLOOP; }; IF posPoly # NIL THEN AddPoly[pos, posPoly, n]; -- add poly if not fully clipped IF negPoly # NIL THEN AddPoly[neg, G3dPolygon.PolygonReverse[negPoly], n]; ENDLOOP; IF posCaps # NIL THEN Cap[pos, posCaps]; IF negCaps # NIL THEN Cap[neg, negCaps]; pos.edges ¬ G3dShape.MakeEdges[pos ! BoundsFault => CONTINUE]; neg.edges ¬ G3dShape.MakeEdges[neg ! BoundsFault => CONTINUE]; }; }; <> DrawCurves: PUBLIC PROC [ context: Context, shape: Shape, view: Matrix, viewport: Viewport ¬ [], type: DrawType ¬ solid, backFaces: BOOL ¬ TRUE, screens: ScreenSequence ¬ NIL, forceTransform: BOOL ¬ FALSE, forInterpress: BOOL ¬ FALSE] RETURNS [ScreenSequence] ~ { FrontFacingAction: SurfaceProc ~ { i0: CARD ¬ surface[surface.length-1]; FOR n: INTEGER IN [0..surface.length) DO i1: CARD ¬ surface[n]; index: INT ¬ G3dShape.FindEdge[shape.edges, i0, i1]; i0 ¬ i1; IF index = -1 THEN LOOP; IF type # solid THEN G3dDraw.DotCurve[context, model.curves[index], x, viewport] ELSE G3dDraw.Curve[context, model.curves[index], x, viewport,, forInterpress]; ENDLOOP; }; model: Model ¬ GetModel[shape]; mat: Matrix ¬ IF shape.matrix # NIL THEN shape.matrix ELSE G3dMatrix.Identity[]; x: Matrix ¬ G3dMatrix.Mul[shape.matrix ¬ mat, view, G3dMatrix.ObtainMatrix[]]; IF model = NIL THEN RETURN[NIL]; IF forceTransform OR screens = NIL OR NOT screens.screensValid THEN screens ¬ G3dDraw.SetScreenCoords[context, shape, x, viewport, screens]; IF shape.edges = NIL THEN shape.edges ¬ G3dShape.MakeEdges[shape]; IF backFaces THEN G3dDraw.Curves[context, model.curves, x, viewport, type # solid, forInterpress] ELSE G3dShape.DoWithFacingPolygons[shape, x, FrontFacingAction]; RETURN[screens]; }; <<>> SetCurves: PUBLIC PROC [shape: Shape] ~ { model: Model ¬ GetModel[shape]; IF G3dShape.VertexValid[shape, normal] AND shape.edges # NIL THEN { IF model.curves = NIL OR model.curves.maxLength < shape.edges.length THEN model.curves ¬ NEW[SplineSequenceRep[shape.edges.length]]; model.curves.length ¬ shape.edges.length; FOR n: NAT IN [0..shape.edges.length) DO e: Edge ¬ shape.edges[n]; v0: Vertex ¬ shape.vertices[e.v0]; v1: Vertex ¬ shape.vertices[e.v1]; model.curves[n] ¬ G3dSpline.SplineFromPointsAndNormals[ v0.point, v1.point, v0.normal, v1.normal,, model.curves[n]]; ENDLOOP; }; }; MakeCurves: PUBLIC PROC [ vertices, normals: TripleSequence, edges: EdgeSequence, curves: SplineSequence ¬ NIL] RETURNS [SplineSequence] ~ { IF vertices # NIL AND normals # NIL AND edges # NIL THEN { IF curves = NIL OR curves.maxLength < edges.length THEN curves ¬ NEW[SplineSequenceRep[edges.length]]; curves.length ¬ edges.length; FOR n: NAT IN [0..edges.length) DO e: Edge ¬ edges[n]; curves[n] ¬ G3dSpline.SplineFromPointsAndNormals[ vertices[e.v0], vertices[e.v1], normals[e.v0], normals[e.v1], , curves[n]]; ENDLOOP; }; RETURN[curves]; }; NormalsFromCurve: PUBLIC PROC [curve: PairSequence] RETURNS [curveNormals: PairSequence] <> <> <> ~ { curveNormals ¬ NEW[PairSequenceRep[curve.length]]; curveNormals.length ¬ curve.length; SELECT curve.length FROM = 2 => { tangent: Pair ¬ G2dVector.Unit[G2dVector.Sub[curve[1], curve[0]]]; curveNormals[0] ¬ curveNormals[1] ¬ [-tangent.y, tangent.x]; }; > 2 => { dm, dp, tangent: Pair; delm, delp, delSum: REAL; <> FOR i: NAT IN [1..curve.length-1) DO dm ¬ G2dVector.Sub[curve[i], curve[i-1]]; dp ¬ G2dVector.Sub[curve[i+1], curve[i]]; delm ¬ ABS[dm.x] + ABS[dm.y]; delp ¬ ABS[dp.x] + ABS[dp.y]; delSum ¬ delm+delp; tangent ¬ G2dVector.Combine[G2dVector.Unit[dm], delp/delSum, G2dVector.Unit[dp], delm/delSum]; curveNormals[i] ¬ G2dVector.Unit[[-tangent.y, tangent.x]]; ENDLOOP; <> tangent ¬ G2dVector.Combine[G2dVector.Unit[dp], 2.0*delSum, G2dVector.Unit[tangent], -1]; curveNormals[curve.length-1] ¬ G2dVector.Unit[[-tangent.y, tangent.x]]; <> dm ¬ G2dVector.Sub[curve[1], curve[0]]; dp ¬ G2dVector.Sub[curve[2], curve[1]]; delm ¬ ABS[dm.x] + ABS[dm.y]; delp ¬ ABS[dp.x] + ABS[dp.y]; delSum ¬ delm+delp; tangent ¬ G2dVector.Combine[G2dVector.Unit[dm], 2.0*delSum, [curveNormals[1].y, -curveNormals[1].x], -1]; curveNormals[0] ¬ G2dVector.Unit[[-tangent.y, tangent.x]]; }; ENDCASE; }; END. .. MakeEdges: PUBLIC PROC [polygons: SurfaceSequence, edges: EdgeSequence ¬ NIL] RETURNS [EdgeSequence] ~ { n0, n1, min, max: CARD; IF vertices = NIL OR polygons = NIL THEN RETURN[NIL]; IF edges = NIL OR edges.maxLength < vertices.length THEN edges ¬ NEW[EdgeSequence[vertices.length]]; FOR i: NAT IN [0..polygons.length) DO polygon: NatSequence ~ polygons[i]; n0 ¬ polygon[polygon.length-1]; FOR ii: NAT IN [0..polygon.length) DO n1 ¬ polygon[ii]; IF n0 < n1 THEN {min ¬ n0; max ¬ n1} ELSE {min ¬ n1; max ¬ n0}; IF min+1 > edges.length THEN edges.length ¬ min+1; FOR l: LIST OF NAT ¬ edges[min], l.rest WHILE l # NIL DO IF l.first = max THEN EXIT; REPEAT FINISHED => edges[min] ¬ CONS[max, edges[min]]; ENDLOOP; n0 ¬ n1; ENDLOOP; ENDLOOP; RETURN[edges]; };