<> <> <> DIRECTORY Buttons USING [Button, ButtonProc, Create], Commander USING [CommandProc, Register], Containers USING [Container, Create], Imager USING [black, Context, Color, ScaleT, MaskFillTrajectory, MaskStrokeTrajectory, SetColor, SetStrokeWidth], ImagerColor USING [ColorFromRGB, RGB], ImagerPath USING[LineTo, MoveTo, Trajectory], FS USING [StreamOpen], IO USING [Close, GetCedarTokenRope, GetReal, PutF, PutFR, PutRope, real, RIS, STREAM, TokenKind, EndOf], Menus USING[CreateMenu, AppendMenuEntry, CreateEntry, Menu, ClickProc], Real USING [Float, Round], RealFns USING [AlmostZero, SinDeg, CosDeg, SqRt], Rope USING [Cat, Equal, ROPE], Sweep, Vector2 USING [VEC], ViewerClasses USING [ViewerClass, ViewerClassRec, Viewer, PaintProc, NotifyProc], ViewerOps USING [CreateViewer, RegisterViewerClass, PaintViewer], ViewerTools USING [MakeNewTextViewer, SetSelection, SetContents, GetContents]; HideSweepImpl: CEDAR MONITOR LOCKS my USING my: State IMPORTS Buttons, Containers, Commander, FS, IO, Imager, ImagerPath, ImagerColor, Menus, Real, RealFns, Rope, Sweep, ViewerOps, ViewerTools = BEGIN OPEN Sweep; leafBucketSize: CARDINAL _ 12; branchingFactor: CARDINAL _ 4; sticks: BOOLEAN _ FALSE; surfaces: BOOLEAN _ TRUE; shade: BOOLEAN _ TRUE; diffuse: REAL _ .30; perspective: BOOLEAN _ TRUE; eye: REAL _ -6.0; --Eye is at (0, 0, eye) eyeToScreen: REAL _ 1.0; screenSize: REAL _ .2; largestEyeToObject: REAL _ 20.0; Perspective: PROC [i: Point3] RETURNS [o: Point3] ~ { f: REAL _ screenSize * (i[3] - eye) / eyeToScreen; o[1] _ i[1]/f; o[2] _ i[2]/f; o[3] _ (f - screenSize) / ( (1.0 - eyeToScreen/largestEyeToObject) * f) }; Point3: TYPE = ARRAY [1..3] OF REAL; Matrix33: TYPE = ARRAY [1..3] OF ARRAY [1..3] OF REAL; Rotate: PROC [p: Point3, m: Matrix33 ] RETURNS [q: Point3] ~ { FOR i: CARDINAL IN [1..3] DO q[i] _ 0; FOR j: CARDINAL IN [1..3] DO q[i] _ q[i] + m[i][j]*p[j]; ENDLOOP; ENDLOOP; }; ScaleTranslate: PROC [p: Point3] RETURNS [q: Point3] ~ { FOR i: CARDINAL IN [1..3] DO q[i] _ p[i] * 2000.0 --Spots per inch ENDLOOP; q[1] _ q[1] + 10000.0; q[2] _ q[2] + 10000.0 }; Dot: PROC [p, q: Point3] RETURNS [s: REAL _ 0.0] ~ { FOR i: CARDINAL IN [1..3] DO s _ s + p[i] * q[i]; ENDLOOP; }; Cross: PROC [p, q: Point3] RETURNS [r: Point3] ~ { FOR i: CARDINAL IN [1..3] DO n: CARDINAL _ (i MOD 3) + 1; nn: CARDINAL _ (n MOD 3) + 1; r[i] _ p[n]*q[nn] - p[nn]*q[n]; ENDLOOP; }; Sub: PROC [p, q: Point3] RETURNS [r: Point3] ~ { FOR i: CARDINAL IN [1..3] DO r[i] _ p[i] - q[i]; ENDLOOP; }; Add: PROC [p, q: Point3] RETURNS [r: Point3] ~ { FOR i: CARDINAL IN [1..3] DO r[i] _ p[i] + q[i]; ENDLOOP; }; Facet: TYPE = REF FacetRec; FacetRec: TYPE = RECORD [ a, b, c, n: Point3, color: ImagerColor.RGB]; Scene: TYPE = LIST OF Facet; Union: PROC [a, b: LIST OF Facet] RETURNS [c: LIST OF Facet _ NIL] ~ { <> t: LIST OF Facet _ NIL; DO IF a = NIL THEN { IF b # NIL THEN IF t = NIL THEN c _ b ELSE t.rest _ b; RETURN}; IF b = NIL THEN { IF a # NIL THEN IF t = NIL THEN c _ a ELSE t.rest _ a; RETURN}; IF a.first = b.first THEN {a _ a.rest; b _ b.rest} --cancel duplicates ELSE { IF LOOPHOLE[a.first, LONG CARDINAL] < LOOPHOLE[b.first, LONG CARDINAL] THEN { IF t = NIL THEN {c _ LIST[a.first]; t _ c} ELSE {t.rest _ LIST[a.first]; t _ t.rest}; a _ a.rest} ELSE { IF t = NIL THEN {c _ LIST[b.first]; t _ c} ELSE {t.rest _ LIST[b.first]; t _ t.rest}; b _ b.rest}; }; ENDLOOP; }; Copy: PROC [a: LIST OF Facet] RETURNS [b: LIST OF Facet _ NIL] ~ { <> t: LIST OF Facet; IF a = NIL THEN RETURN ELSE b _ t _ CONS[a.first, NIL]; a _ a.rest; UNTIL a = NIL DO t.rest _ CONS[a.first, NIL]; a _ a.rest; t _ t.rest; ENDLOOP; }; Change: PROC [a, b: LIST OF Facet, x, y: REAL] RETURNS [c: LIST OF Facet _ NIL] ~ { <> IF b = NIL THEN RETURN[a]; c _ Copy[a]; UNTIL b = NIL DO s, p: LIST OF Facet; BEGIN s _ c; p _ NIL; DO IF s = NIL THEN GOTO Insert; IF s.first = b.first THEN EXIT; p _ s; s _ s.rest; ENDLOOP; IF p = NIL THEN c _ s.rest ELSE p.rest _ s.rest; EXITS Insert => { s _ c; p _ NIL; IF s # NIL THEN UNTIL InFront[b.first, s.first, x, y] DO p _ s; s _ s.rest; IF s = NIL THEN EXIT; ENDLOOP; IF p = NIL THEN c _ CONS[b.first, s] ELSE p.rest _ CONS[b.first, s]; } END; b _ b.rest; ENDLOOP; }; InFront: PROC [f,g: Facet, x, y: REAL] RETURNS [BOOLEAN] ~ { fz, gz: REAL; IF f.n[3] = 0 THEN RETURN[TRUE]; --f is end on IF g.n[3] = 0 THEN RETURN[FALSE]; --g is end on fz _ - ((x - f.a[1])*f.n[1] + (y - f.a[2])*f.n[2])/ f.n[3] + f.a[3]; gz _ - ((x - g.a[1])*g.n[1] + (y - g.a[2])*g.n[2])/ g.n[3] + g.a[3]; RETURN[ fz < gz ]; }; Scale: INT _ 2000; State: TYPE = REF StateRec; StateRec: TYPE = MONITORED RECORD [ outer: Containers.Container _ NIL, menu: Menus.Menu, fileNameInput, rotateYInput, inner: ViewerClasses.Viewer, scene: Scene _ NIL, output: Graph]; ShowHide: Commander.CommandProc = { my: State _ NEW[StateRec]; fileNameButton, rotateYButton: Buttons.Button; my.menu _ Menus.CreateMenu[]; my.menu.AppendMenuEntry[Menus.CreateEntry["Clear", ClearProc, my]]; my.menu.AppendMenuEntry[Menus.CreateEntry["View", ViewProc, my]]; my.outer _ Containers.Create[[ name: "Hide", menu: my.menu, scrollable: FALSE]]; fileNameButton _ Buttons.Create[ info: [name: "Input File Name:", wx: 10, wy: 10, wh: 15, parent: my.outer, border: FALSE], proc: ForceFileName, clientData: my]; my.fileNameInput _ ViewerTools.MakeNewTextViewer[[ parent: my.outer, wx: fileNameButton.wx + fileNameButton.ww + 20, wy: 10, wh: 15, ww: 500, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.fileNameInput, "[]<>sweep>Test.facets"]; rotateYButton _ Buttons.Create[ info: [name: "Rotate Around Y:", wx: 400, wy: 10, wh: 15, parent: my.outer, border: FALSE], proc: ForceRotateY, clientData: my]; my.rotateYInput _ ViewerTools.MakeNewTextViewer[[ parent: my.outer, wx: rotateYButton.wx + rotateYButton.ww + 20, wy: 10, wh: 15, ww: 500, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.rotateYInput, "-60.0"]; my.inner _ ViewerOps.CreateViewer[ flavor: $ShowHide, info: [wx: 5, wy: 30, wh: 400, ww: 630, parent: my.outer, data: my]]; ViewerOps.PaintViewer[viewer: my.inner, hint: all]; }; ForceFileName: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = { my: State _ NARROW[clientData]; ViewerTools.SetSelection[my.fileNameInput]; }; ForceRotateY: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = { my: State _ NARROW[clientData]; ViewerTools.SetSelection[my.rotateYInput]; }; ClearProc: Menus.ClickProc = { my: State _ NARROW[clientData]; ClearLocked[my]; ViewerOps.PaintViewer[viewer: my.inner, hint: all]; }; ClearLocked: ENTRY PROC[my: State] ~ { ENABLE UNWIND => NULL; DestroyGraph[my.output]; my.output _ NIL; }; ViewProc: Menus.ClickProc = { my: State _ NARROW[clientData]; ViewLocked[my]; ViewerOps.PaintViewer[viewer: my.inner, hint: all]; }; HideLine: TYPE = REF HideLineRec; HideLineRec: TYPE = RECORD [ changing: LIST OF Facet ]; HideRegion: TYPE = REF HideRegionRec; HideRegionRec: TYPE = RECORD[ facets: LIST OF Facet _ NIL]; hideInfinityRegion: HideRegion = NEW[HideRegionRec _ [facets: NIL]]; HideCopy: CopyLineProc = { RETURN[stateIn] }; HideFlip: FlipLineProc = { RETURN[stateIn] }; HideCombine: CombineLineProc = { s1: HideLine _ NARROW[state1]; s2: HideLine _ NARROW[state2]; sO: HideLine _ NEW[HideLineRec _ [ changing: Union[s1.changing, s2.changing]]]; RETURN[sO]; }; ViewLocked: ENTRY PROC[my: State] ~ { ENABLE UNWIND => NULL; HideStart: StartRegionProc = { rP: HideRegion _ NARROW[regionPrevious]; lR: HideLine _ NARROW[lineRight.state]; x: REAL _ (lineRight.above.x + lineRight.below.x + lineLeft.below.x) / 3.0; y: REAL _ (lineRight.above.y + lineRight.below.y + lineLeft.below.y) / 3.0; rC: HideRegion _ NEW[HideRegionRec _ [facets: Change[rP.facets, lR.changing, x, y] ] ]; FixLineState[rC, lineRight, rP]; RETURN[rC]; }; HideStop: StopRegionProc = { IF UselessTest[NARROW[lineLeft.state]] THEN RemoveLineFromEndPoints[lineLeft]; }; HideSplit: SplitRegionProc = { FixLineState[NARROW[regionRight], lineLeft, NARROW[regionPrevious]]; RETURN[regionRight]; }; HideMerge: MergeRegionProc = { IF UselessTest[NARROW[lineRight.state]] THEN RemoveLineFromEndPoints[lineRight]; RETURN[regionRight]; }; HideLineChange: LineChangeRegionProc = { IF side = left THEN { IF UselessTest[NARROW[lineOld.state]] THEN RemoveLineFromEndPoints[lineOld]; } ELSE FixLineState[NARROW[regionCenter], lineNew, NARROW[regionPrevious]]; }; UselessTest: PROC [line: HideLine] RETURNS [BOOL] ~ INLINE { RETURN[line.changing = NIL] }; FixLineState: PROC [regionLeft: HideRegion, lineCenter: Line, regionRight: HideRegion] ~ { lF: LIST OF Facet _ IF regionLeft.facets = NIL THEN NIL ELSE LIST[regionLeft.facets.first]; rF: LIST OF Facet _ IF regionRight.facets = NIL THEN NIL ELSE LIST[regionRight.facets.first]; lineCenter.state _ NEW[HideLineRec _ [changing: Union[lF, rF]]]; }; input: Graph _ NewGraph[]; workLoad, newWorkLoad: LIST OF Graph _ NIL; facetCount: LONG CARDINAL; inputFile: IO.STREAM _ FS.StreamOpen[ViewerTools.GetContents[my.fileNameInput]]; scanScene: Scene; master: Matrix33; sin, cos: REAL; my.scene _ NIL; UNTIL inputFile.EndOf[] DO f: Facet _ NEW[FacetRec]; sep: Rope.ROPE; f.a[1] _ inputFile.GetReal[]; f.a[2] _ inputFile.GetReal[]; f.a[3] _ inputFile.GetReal[]; sep _ inputFile.GetCedarTokenRope[].token; IF NOT Rope.Equal[sep, ","] THEN ERROR; f.b[1] _ inputFile.GetReal[]; f.b[2] _ inputFile.GetReal[]; f.b[3] _ inputFile.GetReal[]; sep _ inputFile.GetCedarTokenRope[].token; IF NOT Rope.Equal[sep, ","] THEN ERROR; f.c[1] _ inputFile.GetReal[]; f.c[2] _ inputFile.GetReal[]; f.c[3] _ inputFile.GetReal[]; sep _ inputFile.GetCedarTokenRope[].token; IF NOT Rope.Equal[sep, "/"] THEN ERROR; f.color.R _ inputFile.GetReal[]; f.color.G _ inputFile.GetReal[]; f.color.B _ inputFile.GetReal[]; my.scene _ CONS[f, my.scene]; ENDLOOP; sin _ RealFns.SinDeg[RealFromRope[ViewerTools.GetContents[my.rotateYInput]]]; cos _ RealFns.CosDeg[RealFromRope[ViewerTools.GetContents[my.rotateYInput]]]; master _ [[cos, 0.0, -sin], [0.0, 1.0, 0.0], [sin, 0.0, cos]]; DestroyGraph[my.output]; my.output _ NIL; scanScene _ my.scene; facetCount _ 1; UNTIL scanScene = NIL DO OPEN Real; of: Facet _ scanScene.first; nf: Facet _ NEW[FacetRec]; lineState: HideLine _ NEW[HideLineRec _ [LIST[nf]]]; nf.a _ Rotate[of.a, master]; nf.b _ Rotate[of.b, master]; nf.c _ Rotate[of.c, master]; nf.color _ of.color; nf.n _ Cross[ Sub[nf.b, nf.a], Sub[nf.c, nf.a] ]; IF shade THEN { length: REAL _ RealFns.SqRt[ABS[Dot[nf.n, nf.n]]]; cos _ IF RealFns.AlmostZero[length, -50] THEN 0.0 ELSE ABS[nf.n[3]/length]; nf.color.R _ nf.color.R*(cos*(1.0 - diffuse) + diffuse); nf.color.G _ nf.color.G*(cos*(1.0 - diffuse) + diffuse); nf.color.B _ nf.color.B*(cos*(1.0 - diffuse) + diffuse); }; IF perspective THEN { nf.a _ Perspective[nf.a]; nf.b _ Perspective[nf.b]; nf.c _ Perspective[nf.c]; nf.n _ Cross[ Sub[nf.b, nf.a], Sub[nf.c, nf.a] ]; }; nf.a _ ScaleTranslate[nf.a]; nf.b _ ScaleTranslate[nf.b]; nf.c _ ScaleTranslate[nf.c]; input _ NewPoint[input, Round[nf.a[1]], Round[nf.a[2]] ]; input _ input.LineTo[Round[nf.b[1]], Round[nf.b[2]], lineState, HideFlip]; input _ input.LineTo[Round[nf.c[1]], Round[nf.c[2]], lineState, HideFlip]; input _ input.LineTo[Round[nf.a[1]], Round[nf.a[2]], lineState, HideFlip]; IF facetCount MOD leafBucketSize = 0 THEN { my.output _ Intersect[input, HideCopy, HideCombine, HideFlip]; my.output _ Sweep[my.output, hideInfinityRegion, HideStart, HideStop, HideSplit, HideMerge, HideLineChange]; workLoad _ CONS[my.output, workLoad]; input _ NewGraph[]; }; scanScene _ scanScene.rest; facetCount _ facetCount + 1; ENDLOOP; IF input.points.size # 0 THEN { my.output _ Intersect[input, HideCopy, HideCombine, HideFlip]; my.output _ Sweep[my.output, hideInfinityRegion, HideStart, HideStop, HideSplit, HideMerge, HideLineChange]; workLoad _ CONS[my.output, workLoad]; input _ NewGraph[]; }; DO newWorkLoad _ NIL; UNTIL workLoad = NIL DO input _ NIL; FOR i:CARDINAL IN [1..branchingFactor] DO IF workLoad = NIL THEN {IF i = 2 THEN GOTO FreePass ELSE EXIT}; input _ MergeGraphs[input, workLoad.first]; workLoad _ workLoad.rest; ENDLOOP; my.output _ Intersect[input, HideCopy, HideCombine, HideFlip]; my.output _ Sweep[my.output, hideInfinityRegion, HideStart, HideStop, HideSplit, HideMerge, HideLineChange]; newWorkLoad _ CONS[my.output, newWorkLoad]; REPEAT FreePass => { newWorkLoad _ CONS[input, newWorkLoad]; }; ENDLOOP; IF newWorkLoad.rest = NIL THEN EXIT ELSE workLoad _ newWorkLoad; ENDLOOP; }; EndOfLines: PROC [in: LIST OF Line] RETURNS [out: LIST OF Line] ~ INLINE { IF in = NIL THEN RETURN[NIL]; UNTIL in.rest = NIL DO in _ in.rest; ENDLOOP; RETURN[in]; }; RealFromRope: PROC [rawRope: Rope.ROPE] RETURNS [REAL] = INLINE { OPEN IO; RETURN [GetReal[RIS[rawRope]]] }; ShowHidePaint: ViewerClasses.PaintProc = { my:State _ NARROW[self.data]; context.ScaleT[40.0/Scale]; PaintLocked[my, context]; }; TrapRegion: TYPE = REF TrapRegionRec; TrapRegionRec: TYPE = RECORD [ facet: Facet _ NIL, lastPointSeen: Point _ NIL, lineLeft, lineRight: Line _ NIL ]; trapInfinityRegion: TrapRegion = NEW[TrapRegionRec _ [facet: NIL]]; PaintLocked: ENTRY PROC [my: State, context: Imager.Context] ~ { OPEN Imager, ImagerPath; ENABLE UNWIND => NULL; TrapStart: StartRegionProc = { lR: HideLine _ NARROW[lineRight.state]; rR: TrapRegion _ NARROW[regionPrevious]; cF: LIST OF Facet _ Change[IF rR.facet = NIL THEN NIL ELSE LIST[rR.facet], lR.changing, -1.0, -1.0]; IF cF # NIL THEN RETURN[NEW[TrapRegionRec _ [facet: cF.first, lastPointSeen: lineLeft.above, lineLeft: lineLeft, lineRight: lineRight]]] ELSE RETURN[trapInfinityRegion]; }; TrapStop: StopRegionProc = { rC: TrapRegion _ NARROW[regionCenter]; IF rC # trapInfinityRegion THEN { PaintTrap[rC.facet.color, rC.lastPointSeen.y, lineLeft.below.y, lineLeft, lineRight]; rC.lineLeft _ rC.lineRight _ NIL; }; }; TrapSplit: SplitRegionProc = { rR: TrapRegion _ NARROW[regionRight]; rL: TrapRegion; point: Point _ lineRight.above; IF rR # trapInfinityRegion THEN { PaintTrap[rR.facet.color, rR.lastPointSeen.y, point.y, rR.lineLeft, rR.lineRight]; rR.lastPointSeen _ point; rL _ NEW[TrapRegionRec _ [facet: rR.facet, lastPointSeen: point, lineLeft: rR.lineLeft, lineRight: lineLeft]]; rR.lineLeft _ lineRight; RETURN[rL]; } ELSE RETURN[rR]; }; TrapMerge: MergeRegionProc = { rL: TrapRegion _ NARROW[regionLeft]; rR: TrapRegion _ NARROW[regionRight]; point: Point _ lineLeft.below; IF (rL # trapInfinityRegion) AND (rR # trapInfinityRegion) THEN { PaintTrap[rL.facet.color, rL.lastPointSeen.y, point.y, rL.lineLeft, rL.lineRight]; PaintTrap[rR.facet.color, rR.lastPointSeen.y, point.y, rR.lineLeft, rR.lineRight]; rR.lastPointSeen _ point; rR.lineLeft _ rL.lineLeft; rL.lineLeft _ rL.lineRight _ NIL; }; RETURN[IF rL = trapInfinityRegion THEN rL ELSE rR]; }; TrapLineChange: LineChangeRegionProc = { rC: TrapRegion _ NARROW[regionCenter]; point: Point _ lineNew.above; IF (rC # trapInfinityRegion) THEN { PaintTrap[rC.facet.color, rC.lastPointSeen.y, point.y, rC.lineLeft, rC.lineRight]; rC.lastPointSeen _ point; IF side = left THEN rC.lineLeft _ lineNew ELSE rC.lineRight _ lineNew; }; }; PaintTrap: PROC [c: ImagerColor.RGB, aboveY, belowY: INT, lineLeft, lineRight: Line] ~ { <> traj: Trajectory; lowX: INT _ MIN[lineLeft.above.x, lineLeft.below.x]; highX: INT _ MAX[lineRight.above.x, lineRight.below.x]; top: Line _ NEW[LineRec _ [above: NEW[PointRec _ [x: highX, y: aboveY]], below: NEW[PointRec _ [x: lowX, y: aboveY]] ]]; bottom: Line _ NEW[LineRec _ [above: NEW[PointRec _ [x: highX, y: belowY]], below: NEW[PointRec _ [x: lowX, y: belowY]] ]]; topLeft, topRight, bottomLeft, bottomRight: Point; IF aboveY = belowY THEN RETURN; [topLeft, ] _ PairIntersection[top, lineLeft]; [topRight, ] _ PairIntersection[top, lineRight]; [bottomLeft, ] _ PairIntersection[bottom, lineLeft]; [bottomRight, ] _ PairIntersection[bottom, lineRight]; traj _ MoveTo[Vfp[topLeft]].LineTo[VfpR[topRight]].LineTo[VfpR[bottomRight]].LineTo[Vfp[bottomLeft]]; context.SetColor[ImagerColor.ColorFromRGB[c]]; MaskFillTrajectory[context, traj]; }; StickStart: StartRegionProc = { DrawLine[lineRight]; }; StickSplit: SplitRegionProc = { DrawLine[lineLeft]; }; StickLineChange: LineChangeRegionProc = { IF side = right THEN DrawLine[lineNew]; }; DrawLine: PROC [line: Line] ~ { traj: Trajectory; traj _ MoveTo[Vfp[line.above]].LineTo[Vfp[line.below]]; context.MaskStrokeTrajectory[traj]; }; Vfp: PROC [p: Point] RETURNS [Vector2.VEC] ~ INLINE { RETURN[[Real.Float[p.x], Real.Float[p.y]]]; }; VfpR: PROC [p: Point] RETURNS [Vector2.VEC] ~ INLINE { RETURN[[Real.Float[p.x+1], Real.Float[p.y]]]; }; IF surfaces THEN my.output _ Sweep[my.output, trapInfinityRegion, TrapStart, TrapStop, TrapSplit, TrapMerge, TrapLineChange]; IF sticks THEN { context.SetColor[Imager.black]; context.SetStrokeWidth[20.0]; my.output _ Sweep[my.output, trapInfinityRegion, StickStart, NilStop, StickSplit, NilMerge, StickLineChange]; }; }; FacetCompile: PROC [in, out: Rope.ROPE] ~ { inputFile: IO.STREAM _ FS.StreamOpen[in]; outputFile: IO.STREAM _ FS.StreamOpen[out, $create]; curve: ARRAY [0..100] OF Point3; position: CARDINAL; origin, destination: Point3; color: Rope.ROPE; token: Rope.ROPE; tokenKind: IO.TokenKind; PutPoint: PROC [p: Point3] ~ { outputFile.PutF["%g %g %g", IO.real[p[1]], IO.real[p[2]], IO.real[p[3]]]; }; [tokenKind, token,] _ inputFile.GetCedarTokenRope[]; UNTIL inputFile.EndOf[] DO IF tokenKind # tokenID THEN ERROR; IF Rope.Equal[token, "STRETCH"] THEN BEGIN position _ 0; DO curve[position][1] _ inputFile.GetReal[]; curve[position][2] _ inputFile.GetReal[]; curve[position][3] _ inputFile.GetReal[]; [tokenKind, token,] _ inputFile.GetCedarTokenRope[]; IF tokenKind # tokenSINGLE OR (NOT Rope.Equal[token, ","]) THEN EXIT; position _ position + 1; ENDLOOP; IF (tokenKind # tokenID) OR (NOT Rope.Equal[token, "ALONG"]) THEN ERROR; FOR i: CARDINAL IN [1..3] DO origin[i] _ inputFile.GetReal[]; ENDLOOP; [tokenKind, token,] _ inputFile.GetCedarTokenRope[]; IF (tokenKind # tokenSINGLE) OR (NOT Rope.Equal[token, ","]) THEN ERROR; FOR i: CARDINAL IN [1..3] DO destination[i] _ inputFile.GetReal[]; ENDLOOP; [tokenKind, token,] _ inputFile.GetCedarTokenRope[]; IF (tokenKind # tokenSINGLE) OR (NOT Rope.Equal[token, "/"]) THEN ERROR; color _ "/"; FOR i: CARDINAL IN [1..3] DO component: REAL _ inputFile.GetReal[]; color _ color.Cat[IO.PutFR[" %g", IO.real[component]]]; ENDLOOP; IF NOT inputFile.EndOf[] THEN [tokenKind, token,] _ inputFile.GetCedarTokenRope[]; FOR pos: CARDINAL IN [0..position] DO outputFile.PutRope[" "]; PutPoint[Add[origin, curve[pos]]]; outputFile.PutRope[", "]; PutPoint[Add[origin, curve[(pos + 1) MOD (position + 1)]]]; outputFile.PutRope[", "]; PutPoint[Add[destination, curve[(pos + 1) MOD (position + 1)]]]; outputFile.PutRope[color]; outputFile.PutRope[" "]; PutPoint[Add[destination, curve[pos]]]; outputFile.PutRope[", "]; PutPoint[Add[origin, curve[pos]]]; outputFile.PutRope[", "]; PutPoint[Add[destination, curve[(pos + 1) MOD (position + 1)]]]; outputFile.PutRope[color]; ENDLOOP; END; ENDLOOP; outputFile.Close[]; }; displayerClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [paint: ShowHidePaint]]; ViewerOps.RegisterViewerClass[$ShowHide, displayerClass]; Commander.Register[key: "ShowHide", proc: ShowHide, doc: "To Debug Hidden Lines"]; END.