DIRECTORY Basics USING [Comparison], Buttons USING [Button, ButtonProc, Create], Commander USING [CommandProc, Register], Containers USING [Container, Create], Imager USING [black, Context, Color, ScaleT, RotateT, TranslateT, MaskFillTrajectory, MaskStrokeTrajectory, SetColor, SetStrokeWidth, metersPerInch], ImagerInterpress USING [DoPage, Create, Close, Ref], 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, AlmostEqual, 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, ImagerInterpress, ImagerPath, ImagerColor, Menus, Real, RealFns, Rope, Sweep, ViewerOps, ViewerTools = BEGIN OPEN Sweep; leafBucketSize: CARDINAL _ 12; branchingFactor: CARDINAL _ 4; shade: BOOLEAN _ TRUE; diffuse: REAL _ .30; tolerance: INT = -12; 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] * Scale --Spots per inch ENDLOOP; q[1] _ q[1] + 5*Scale; q[2] _ q[2] + 5*Scale }; 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; }; ScalarMult: PROC [p: Point3, s: REAL] RETURNS [q: Point3] ~ { FOR i: CARDINAL IN [1..3] DO q[i] _ p[i]*s; ENDLOOP; }; Multiply: PROC [m1, m2: Matrix33] RETURNS [mp: Matrix33] ~ { FOR i: CARDINAL IN [1..3] DO FOR j: CARDINAL IN [1..3] DO mp[i][j] _ 0; FOR k: CARDINAL IN [1..3] DO mp[i][j] _ mp[i][j] + m1[i][k]*m2[k][j]; ENDLOOP; ENDLOOP; ENDLOOP; }; Facet: TYPE = REF FacetRec; FacetRec: TYPE = RECORD [ number: CARDINAL, a, b, c, n: Point3, color: ImagerColor.RGB]; Scene: TYPE = LIST OF Facet; Scale: INT = 1000; State: TYPE = REF StateRec; StateRec: TYPE = MONITORED RECORD [ outer: Containers.Container _ NIL, menu: Menus.Menu, fileInNameInput, fileOutNameInput, rotateXInput, rotateYInput, rotateZInput, eyeInput, screenSizeInput, eyeToScreenInput: ViewerClasses.Viewer, sticks: BOOLEAN _ TRUE, surfaces: BOOLEAN _ TRUE, inner: ViewerClasses.Viewer, scene: Scene _ NIL, output: Graph]; ShowHide: Commander.CommandProc = { my: State _ NEW[StateRec]; fileInNameButton, fileOutNameButton, rotateXButton, rotateYButton, rotateZButton, eyeButton, screenSizeButton, eyeToScreenButton: Buttons.Button; my.menu _ Menus.CreateMenu[]; my.menu.AppendMenuEntry[Menus.CreateEntry["ClearView", ClearProc, my]]; my.menu.AppendMenuEntry[Menus.CreateEntry["ComputeView", ViewProc, my]]; my.menu.AppendMenuEntry[Menus.CreateEntry["PrintView", PrintProc, my]]; my.menu.AppendMenuEntry[Menus.CreateEntry["ToggleSticks", SticksProc, my]]; my.menu.AppendMenuEntry[Menus.CreateEntry["ToggleSurfaces", SurfacesProc, my]]; my.outer _ Containers.Create[[ name: "Hide", menu: my.menu, scrollable: FALSE]]; fileInNameButton _ Buttons.Create[ info: [name: "Input File Name:", wx: 10, wy: 10, wh: 15, parent: my.outer, border: FALSE], proc: ForceInName, clientData: my]; my.fileInNameInput _ ViewerTools.MakeNewTextViewer[[ parent: my.outer, wx: fileInNameButton.wx + fileInNameButton.ww + 20, wy: 10, wh: 15, ww: 200, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.fileInNameInput, "[]<>sweep>Test.facets"]; fileOutNameButton _ Buttons.Create[ info: [name: "Output File Name:", wx: 300, wy: 10, wh: 15, parent: my.outer, border: FALSE], proc: ForceOutName, clientData: my]; my.fileOutNameInput _ ViewerTools.MakeNewTextViewer[[ parent: my.outer, wx: fileOutNameButton.wx + fileOutNameButton.ww + 20, wy: 10, wh: 15, ww: 200, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.fileOutNameInput, "[]<>sweep>Test.ip"]; rotateXButton _ Buttons.Create[ info: [name: "X:", wx: 530, wy: 50, wh: 15, parent: my.outer, border: FALSE], proc: ForceRotateX, clientData: my]; my.rotateXInput _ ViewerTools.MakeNewTextViewer[[ parent: my.outer, wx: rotateXButton.wx + rotateXButton.ww + 20, wy: 50, wh: 15, ww: 200, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.rotateXInput, "10.0"]; rotateYButton _ Buttons.Create[ info: [name: "Y:", wx: 530, wy: 80, 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: 80, wh: 15, ww: 200, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.rotateYInput, "-60.0"]; rotateZButton _ Buttons.Create[ info: [name: "Z:", wx: 530, wy: 110, wh: 15, parent: my.outer, border: FALSE], proc: ForceRotateZ, clientData: my]; my.rotateZInput _ ViewerTools.MakeNewTextViewer[[ parent: my.outer, wx: rotateZButton.wx + rotateZButton.ww + 20, wy: 110, wh: 15, ww: 200, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.rotateZInput, "5.0"]; eyeButton _ Buttons.Create[ info: [name: "Eye:", wx: 530, wy: 300, wh: 15, parent: my.outer, border: FALSE], proc: ForceEye, clientData: my]; my.eyeInput _ ViewerTools.MakeNewTextViewer[[ parent: my.outer, wx: eyeButton.wx + eyeButton.ww + 20, wy: 300, wh: 15, ww: 200, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.eyeInput, "-6.0"]; screenSizeButton _ Buttons.Create[ info: [name: "S. Size:", wx: 530, wy: 330, wh: 15, parent: my.outer, border: FALSE], proc: ForceScreenSize, clientData: my]; my.screenSizeInput _ ViewerTools.MakeNewTextViewer[[ parent: my.outer, wx: screenSizeButton.wx + screenSizeButton.ww + 20, wy: 330, wh: 15, ww: 200, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.screenSizeInput, ".2"]; eyeToScreenButton _ Buttons.Create[ info: [name: "Eye to S.:", wx: 530, wy: 360, wh: 15, parent: my.outer, border: FALSE], proc: ForceEyeToScreen, clientData: my]; my.eyeToScreenInput _ ViewerTools.MakeNewTextViewer[[ parent: my.outer, wx: eyeToScreenButton.wx + eyeToScreenButton.ww + 20, wy: 360, wh: 15, ww: 200, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.eyeToScreenInput, "1.0"]; my.inner _ ViewerOps.CreateViewer[ flavor: $ShowHide, info: [wx: 5, wy: 30, wh: 400, ww: 500, parent: my.outer, data: my]]; ViewerOps.PaintViewer[viewer: my.inner, hint: all]; }; ForceInName: 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.fileInNameInput]; }; ForceOutName: 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.fileOutNameInput]; }; ForceRotateX: 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.rotateXInput]; }; 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]; }; ForceRotateZ: 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.rotateZInput]; }; ForceEye: 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.eyeInput]; }; ForceScreenSize: 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.screenSizeInput]; }; ForceEyeToScreen: 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.eyeToScreenInput]; }; 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; }; SticksProc: Menus.ClickProc = { my: State _ NARROW[clientData]; SticksLocked[my]; ViewerOps.PaintViewer[viewer: my.inner, hint: all]; }; SticksLocked: ENTRY PROC[my: State] ~ { ENABLE UNWIND => NULL; my.sticks _ NOT my.sticks; }; SurfacesProc: Menus.ClickProc = { my: State _ NARROW[clientData]; SurfacesLocked[my]; ViewerOps.PaintViewer[viewer: my.inner, hint: all]; }; SurfacesLocked: ENTRY PROC[my: State] ~ { ENABLE UNWIND => NULL; my.surfaces _ NOT my.surfaces; }; PrintProc: Menus.ClickProc = { my: State _ NARROW[clientData]; Action: PROC [dc: Imager.Context] = { dc.TranslateT[[0.0, 10.0]]; dc.RotateT[-90.0]; dc.ScaleT[.9/Scale]; PaintLocked[my, dc]; }; ip: ImagerInterpress.Ref _ ImagerInterpress.Create[ViewerTools.GetContents[my.fileOutNameInput] ]; ImagerInterpress.DoPage[ip, Action, Imager.metersPerInch]; ImagerInterpress.Close[ip]; }; ViewProc: Menus.ClickProc = { my: State _ NARROW[clientData]; ViewLocked[my]; ViewerOps.PaintViewer[viewer: my.inner, hint: all]; }; overlapLimit: INTEGER = 12; HideLine: TYPE = REF HideLineRec; HideLineRec: TYPE = RECORD [ size: INTEGER _ 0, front: Facet _ NIL, changing: ARRAY [0..overlapLimit] OF Facet, next: HideLine ]; hideInfinityRegion: HideLine = NEW[HideLineRec _ [size: 0]]; ViewLocked: ENTRY PROC[my: State] ~ { ENABLE UNWIND => NULL; freeHL: HideLine; NewHL: PROC [size: INTEGER _ 0] RETURNS [alive: HideLine] ~ INLINE { IF freeHL = NIL THEN RETURN[NEW[HideLineRec _ [size: size]]]; alive _ freeHL; alive.size _ size; freeHL _ freeHL.next; }; HideCopy: CopyLineProc = { sI: HideLine _ NARROW[stateIn]; sO: HideLine _ NewHL[sI.size]; FOR i: INTEGER IN [0..sO.size) DO sO.changing[i] _ sI.changing[i]; ENDLOOP; RETURN[sO]; }; HideFlip: FlipLineProc = { RETURN[stateIn] }; HideCombine: CombineLineProc = { s1: HideLine _ NARROW[state1]; s2: HideLine _ NARROW[state2]; RETURN[Union[s1, s2]]; }; Union: PROC [a, b: HideLine] RETURNS [c: HideLine] ~ { aPoint, bPoint: INTEGER _ 0; c _ NewHL[]; UNTIL aPoint = a.size AND bPoint = b.size DO BEGIN IF aPoint = a.size THEN GOTO MoveFromB; IF bPoint = b.size THEN GOTO MoveFromA; IF a.changing[aPoint] = b.changing[bPoint] THEN { aPoint _ aPoint + 1; --cancel duplicates bPoint _ bPoint + 1; LOOP}; IF LOOPHOLE[a.changing[aPoint], LONG CARDINAL] < LOOPHOLE[b.changing[bPoint], LONG CARDINAL] THEN GOTO MoveFromA ELSE GOTO MoveFromB; EXITS MoveFromA => { c.changing[c.size] _ a.changing[aPoint]; c.size _ c.size + 1; aPoint _ aPoint + 1; }; MoveFromB => { c.changing[c.size] _ b.changing[bPoint]; c.size _ c.size + 1; bPoint _ bPoint + 1; }; END; ENDLOOP; c _ c; }; ComputeFront: PROC [r: HideLine] ~ { IF r.size = 0 THEN RETURN; r.front _ r.changing[0]; FOR i: INTEGER IN [1..r.size) DO IF InFront[r.changing[i], r.front] THEN r.front _ r.changing[i]; ENDLOOP; }; InFront: PROC [f,g: Facet] RETURNS [BOOLEAN] ~ { zF, zG: REAL; cumPoint: Point3 _ [0.0, 0.0, 0.0]; cumCount: INT _ 0; ProjZ: PROC [p: Point3, f: Facet] RETURNS [z: REAL] ~ { z _ - ((p[1] - f.a[1])*f.n[1] + (p[2] - f.a[2])*f.n[2])/ f.n[3] + f.a[3]; }; Inside: PROC [p: Point3, f: Facet] ~ { v: Point3 _ Sub[p, f.a]; IF Cross[Sub[f.b, f.a], v][3] * f.n[3] <= 0 THEN RETURN; IF Cross[v, Sub[f.c, f.a]][3] * f.n[3] <= 0 THEN RETURN; IF Cross[Sub[f.c, f.b], Sub[p, f.b]][3] * f.n[3] <= 0 THEN RETURN; cumPoint _ Add[cumPoint, p]; cumCount _ cumCount + 1; }; Intersect: PROC [p1, p2, q1, q2: Point3] ~ { dQ, dP, base, cross: Point3; numer1, numer2: REAL; OutsideZeroOne: PROC [r: REAL] RETURNS [BOOLEAN] ~ { IF RealFns.AlmostZero[r, tolerance] THEN RETURN[FALSE]; IF RealFns.AlmostEqual[r, 1.0, tolerance] THEN RETURN[FALSE]; RETURN[ r < 0 OR r > 1.0 ]; }; dP _ Sub[p1, p2]; dQ _ Sub[q2, q1]; cross _ Cross[dP, dQ]; IF RealFns.AlmostZero[cross[3], tolerance] THEN RETURN; base _ Sub[q2, p2]; numer1 _ Cross[base, dQ][3] / cross[3]; numer2 _ Cross[dP, base][3] / cross[3]; IF OutsideZeroOne[numer1] THEN RETURN; IF OutsideZeroOne[numer2] THEN RETURN; cumPoint _ Add[cumPoint, Add[p2, ScalarMult[dP, numer1]]]; cumCount _ cumCount + 1; }; Inside[f.a, g]; Inside[f.b, g]; Inside[f.c, g]; Inside[g.a, f]; Inside[g.b, f]; Inside[g.c, f]; Intersect[f.a, f.b, g.a, g.b]; Intersect[f.a, f.b, g.b, g.c]; Intersect[f.a, f.b, g.c, g.a]; Intersect[f.b, f.c, g.a, g.b]; Intersect[f.b, f.c, g.b, g.c]; Intersect[f.b, f.c, g.c, g.a]; Intersect[f.c, f.a, g.a, g.b]; Intersect[f.c, f.a, g.b, g.c]; Intersect[f.c, f.a, g.c, g.a]; IF cumCount = 0 THEN RETURN[FALSE]; cumPoint _ ScalarMult[cumPoint, 1.0/ cumCount]; zF _ ProjZ[cumPoint, f]; zG _ ProjZ[cumPoint, g]; IF RealFns.AlmostEqual[zF, zG, tolerance] THEN RETURN[FALSE]; IF zF > zG THEN RETURN[FALSE] ELSE RETURN[TRUE]; }; HideStart: StartRegionProc = { rP: HideLine _ 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: HideLine _ Union[rP, lR]; ComputeFront[rC]; FixLineState[rC, lineRight, rP]; RETURN[rC]; }; HideStop: StopRegionProc = {UselessTest[lineLeft]}; HideSplit: SplitRegionProc = { FixLineState[NARROW[regionRight], lineLeft, NARROW[regionPrevious]]; RETURN[regionRight]; }; HideMerge: MergeRegionProc = { IF NARROW[regionLeft, HideLine].size > 0 AND NARROW[regionLeft, HideLine].front # NARROW[regionRight, HideLine].front THEN ERROR; UselessTest[lineRight]; RETURN[regionRight]; }; HideLineChange: LineChangeRegionProc = { IF side = left THEN { UselessTest[lineOld]; } ELSE FixLineState[NARROW[regionCenter], lineNew, NARROW[regionPrevious]]; }; UselessTest: PROC [line: Line] ~ INLINE { hideLine: HideLine _ NARROW[line.state]; IF hideLine.size = 0 THEN { hideLine.next _ freeHL; freeHL _ hideLine; RemoveLineFromEndPoints[line]; }; }; FixLineState: PROC [regionLeft: HideLine, lineCenter: Line, regionRight: HideLine] ~ { lC: HideLine _ NARROW[lineCenter.state]; IF regionLeft.size = 0 THEN { IF regionRight.size = 0 THEN {lC.size _ 0} ELSE {lC.size _ 1; lC.changing[0] _ regionRight.front} } ELSE { IF regionRight.size = 0 THEN {lC.size _ 1; lC.changing[0] _ regionLeft.front} ELSE { IF regionLeft.front = regionRight.front THEN lC.size _ 0 ELSE { lC.size _ 2; IF LOOPHOLE[regionRight.front, LONG CARDINAL] < LOOPHOLE[regionLeft.front, LONG CARDINAL] THEN { lC.changing[0] _ regionRight.front; lC.changing[1] _ regionLeft.front } ELSE { lC.changing[0] _ regionLeft.front; lC.changing[1] _ regionRight.front }; }; }; }; }; input: Graph _ NewGraph[]; workLoad, newWorkLoad: LIST OF Graph _ NIL; facetCount: LONG CARDINAL; inputFile: IO.STREAM _ FS.StreamOpen[ViewerTools.GetContents[my.fileInNameInput]]; scanScene: Scene; master: Matrix33; sin, cos: REAL; eye: REAL _ RealFromRope[ViewerTools.GetContents[my.eyeInput]]; --Eye is at (0, 0, eye) eyeToScreen: REAL _ RealFromRope[ViewerTools.GetContents[my.eyeToScreenInput]]; screenSize: REAL _ RealFromRope[ViewerTools.GetContents[my.screenSizeInput]]; largestEyeToObject: REAL _ 200000.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) }; 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.rotateXInput]]]; cos _ RealFns.CosDeg[RealFromRope[ViewerTools.GetContents[my.rotateXInput]]]; master _ [[1.0, 0.0, 0.0], [0.0, cos, -sin], [0.0, sin, cos]]; sin _ RealFns.SinDeg[RealFromRope[ViewerTools.GetContents[my.rotateYInput]]]; cos _ RealFns.CosDeg[RealFromRope[ViewerTools.GetContents[my.rotateYInput]]]; master _ Multiply[ [[cos, 0.0, sin], [0.0, 1.0, 0.0], [-sin, 0.0, cos]], master]; sin _ RealFns.SinDeg[RealFromRope[ViewerTools.GetContents[my.rotateZInput]]]; cos _ RealFns.CosDeg[RealFromRope[ViewerTools.GetContents[my.rotateZInput]]]; master _ Multiply[ [[cos, -sin, 0.0], [sin, cos, 0.0], [0.0, 0.0, 1.0]], master]; 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 _ [size: 1]]; nf.number _ facetCount; lineState.changing[0] _ 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); }; 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]], HideCopy[lineState], HideFlip]; input _ input.LineTo[Round[nf.c[1]], Round[nf.c[2]], HideCopy[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]; my.output _ StraightenLines[my.output]; newWorkLoad _ CONS[my.output, newWorkLoad]; REPEAT FreePass => { newWorkLoad _ CONS[input, newWorkLoad]; my.output _ input; }; 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]; f: Facet; IF rR.facet = NIL THEN { IF lR.size = 0 THEN f _ NIL ELSE {IF lR.size # 1 THEN ERROR; f _ lR.changing[0]} } ELSE { IF lR.size = 1 THEN f _ NIL ELSE { IF lR.size # 2 THEN ERROR; IF lR.changing[0] = rR.facet THEN f _ lR.changing[1] ELSE {IF lR.changing[1] # rR.facet THEN ERROR; f _ lR.changing[0]} }; }; IF f # NIL THEN RETURN[NEW[TrapRegionRec _ [facet: f, 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]; }; DrawLine: LineActionProc ~ { traj: Trajectory; traj _ MoveTo[Vfp[l.above]].LineTo[Vfp[l.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 my.surfaces THEN my.output _ Sweep[my.output, trapInfinityRegion, TrapStart, TrapStop, TrapSplit, TrapMerge, TrapLineChange]; IF my.sticks THEN { context.SetColor[Imager.black]; context.SetStrokeWidth[0.015*Scale]; [] _ EnumerateLines[my.output, DrawLine]; }; }; 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. BHideSweepImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Greene, November 20, 1986 5:13:39 pm PST merge according to pointer order, cancel duplicates End Testing Code Trapaziods are easy to paint directly, the following is a slightly inacurate, and extremely inefficient way of painting them. ʘšœ™Icodešœ Ïmœ1™šžœžœžœž˜K˜ šžœžœžœž˜K˜Kšžœ˜—Kšžœ˜—K˜—K˜šŸœžœ žœ˜8šžœžœžœž˜KšœÏc˜%Kšžœ˜—K˜-K˜—K˜šŸœžœžœžœ ˜4šžœžœžœž˜K˜Kšžœ˜—K˜—K˜šŸœžœžœ˜2šžœžœžœž˜Kšœžœžœ˜Kšœžœžœ˜Kšœ˜Kšžœ˜—K˜—K˜šŸœžœžœ˜0šžœžœžœž˜K˜Kšžœ˜—K˜K˜—šŸœžœžœ˜0šžœžœžœž˜K˜Kšžœ˜—K˜—K˜šŸ œžœžœ˜=šžœžœžœž˜K˜Kšžœ˜—K˜—K˜šŸœžœžœ˜<šžœžœžœž˜šžœžœžœž˜K˜ šžœžœžœž˜K˜(Kšžœ˜—Kšžœ˜—Kšžœ˜—K˜—J˜Jšœžœžœ ˜J˜šœ žœžœ žœ˜-J˜Jšœžœ˜—J˜šœžœžœžœ˜K˜K˜—JšŸœžœ˜J˜Jšœžœžœ ˜Jšœ žœž œžœ!žœ¬žœžœ žœžœ.žœ˜ÛJ˜J˜JšŸœ˜#Jšœ žœ ˜J˜$J˜,Jšœ?˜?J˜J˜J˜J˜GJ˜HJ˜GJ˜KJ˜OJ˜šœ˜Jšœ ˜ J˜Jšœ žœ˜—J˜˜"JšœSžœ˜ZJ˜J˜—J˜šœ4˜4Jšœ˜Jšœ3˜3Jšœ˜Jšœ žœ˜Jšœžœ˜—J˜JšœE˜EJ˜˜#JšœUžœ˜\J˜J˜—J˜šœ5˜5Jšœ˜Jšœ5˜5Jšœ˜Jšœ žœ˜Jšœžœ˜—J˜JšœB˜BJ˜˜JšœFžœ˜MJ˜J˜—J˜šœ1˜1Jšœ˜Jšœ-˜-Jšœ˜Jšœ žœ˜Jšœžœ˜—J˜Jšœ1˜1J˜˜JšœFžœ˜MJ˜J˜—J˜šœ1˜1Jšœ˜Jšœ-˜-Jšœ˜Jšœ žœ˜Jšœžœ˜—J˜Jšœ2˜2J˜J˜˜JšœGžœ˜NJ˜J˜—J˜šœ1˜1Jšœ˜Jšœ-˜-Jšœ˜Jšœ žœ˜Jšœžœ˜—J˜Jšœ0˜0J˜J˜˜JšœIžœ˜PJ˜J˜—J˜šœ-˜-Jšœ˜Jšœ%˜%Jšœ˜Jšœ žœ˜Jšœžœ˜—J˜Jšœ-˜-J˜šœ"˜"JšœMžœ˜TJ˜J˜—J˜šœ4˜4Jšœ˜Jšœ3˜3Jšœ˜Jšœ žœ˜Jšœžœ˜—J˜Jšœ2˜2J˜˜#JšœOžœ˜VJ˜J˜—J˜šœ5˜5Jšœ˜Jšœ5˜5Jšœ˜Jšœ žœ˜Jšœžœ˜—J˜Jšœ4˜4J˜˜J˜—˜"J˜JšœE˜E—J˜Jšœ3˜3J˜J˜šÐbn Ïbœ „œ˜©Kšœ žœ ˜K˜-K˜—K˜š¡ ¢œ „œ˜ªKšœ žœ ˜K˜.K˜—K˜š¡ ¢œ „œ˜ªKšœ žœ ˜K˜*K˜—K˜š¡ ¢œ „œ˜ªKšœ žœ ˜K˜*K˜—K˜š¡ ¢œ „œ˜ªKšœ žœ ˜Kšœ*˜*K˜—K˜š¡¢œ „œ˜¦Kšœ žœ ˜K˜&K˜—K˜š¡¢œ „œ˜­Kšœ žœ ˜K˜-K˜—K˜š¡¢œ „œ˜®Kšœ žœ ˜Kšœ.˜.K˜—K˜K˜J˜J˜JšŸ œ˜Jšœ žœ ˜J˜Jšœ3˜3J˜J˜JšŸ œžœžœ˜&Jšžœžœžœ˜K˜Jšœ žœ˜J˜J˜J˜JšŸ œ˜ Jšœ žœ ˜J˜Jšœ3˜3J˜J˜JšŸ œžœžœ˜'Jšžœžœžœ˜Jšœ žœ ˜Jšœ˜J˜JšŸ œ˜"Jšœ žœ ˜J˜Jšœ3˜3J˜J˜JšŸœžœžœ˜)Jšžœžœžœ˜Jšœžœ ˜Jšœ˜J˜J˜J˜JšŸ œ˜Jšœ žœ ˜J˜šŸœžœ˜%K˜K˜K˜K˜K˜K˜—Jšœb˜bJ˜:J˜Jšœ˜J˜J˜JšŸœ˜Jšœ žœ ˜J˜Jšœ3˜3J˜J˜Jšœžœ˜J˜Jšœ žœžœ ˜!šœ žœžœ˜Jšœžœ˜J˜Jšœ žœžœ˜+J˜J˜—J˜J˜Jšœžœ˜˜OJšœ žœ=˜MJšœžœ ˜$J˜šŸ œžœ žœ˜5Kšœžœ+˜2K˜K˜K˜GK˜—K˜Kšœ žœ˜šžœž˜Kšœ žœ ˜Kšœ žœ˜K˜K˜K˜K˜*Kšžœžœžœžœ˜'K˜K˜K˜K˜*Kšžœžœžœžœ˜'K˜K˜K˜K˜*Kšžœžœžœžœ˜'K˜ K˜ K˜ Kšœ žœ˜Kšžœ˜—K˜KšœN˜NKšœN˜NK˜Kšœ>˜>K˜KšœN˜NKšœN˜NK˜KšœQ˜QK˜KšœN˜NKšœN˜NK˜KšœQ˜QK˜Kšœ%žœ˜)K˜K˜K˜šžœ žœž˜Kšžœ˜ K˜Kšœ žœ ˜Kšœžœ˜3K˜Kšœ˜K˜kK˜1šžœžœ˜Kšœžœžœ˜2Kš œžœ!žœžœžœ˜KK˜8K˜8K˜8K˜—K˜M˜1K˜—K˜VKšœ9˜9KšœT˜TKšœT˜TKšœJ˜Jšžœ žœžœ˜+K˜>Kšœl˜lKšœ žœ˜%Kšœ˜K˜—K˜K˜Kšžœ˜—šžœžœ˜K˜>Kšœl˜lKšœ žœ˜%Kšœ˜K˜—J˜J˜šž˜Kšœžœ˜šžœ žœž˜Kšœžœ˜ šžœžœžœž˜)Kšžœ žœžœžœžœžœ žœžœ˜?K˜+K˜Kšžœ˜—K˜>Kšœl˜lKšœ'˜'Kšœžœ˜+šž˜˜ Kšœžœ˜'K˜K˜——Kšžœ˜—Kš žœžœžœžœžœ˜@Kšžœ˜—J˜J˜J˜šŸ œžœžœžœžœžœžœ žœ˜JKš žœžœžœžœžœ˜šžœ žœž˜Kšœ ˜ Kšžœ˜—Kšžœ˜ K˜—J˜š ¡ œžœžœžœžœžœ˜AKšžœžœ˜Kšžœ žœ˜"—J˜J˜JšŸ œ˜*Jšœ žœ ˜K˜K˜J˜J˜K˜K˜Kšœ žœžœ˜%šœžœžœ˜Kšœžœ˜Kšœžœ˜Kšœž˜K˜—K˜Kšœ!žœžœ˜CK˜KšŸ œžœžœ)˜@Kšžœ˜Kšžœžœžœ˜K˜K˜šŸ œ˜Kšœžœ˜'Kšœžœ˜(K˜ šžœ žœžœ˜Kšžœ žœžœžœžœ žœžœ˜R—šžœ˜šžœ žœžœžœ˜"Kšžœ žœžœ˜Kš žœžœžœžœžœžœ˜wK˜—K˜—Kšžœžœž˜Kšžœžœf˜pKšžœžœ˜ —K˜K˜šŸœ˜Kšœžœ˜&šžœžœ˜"K˜UKšœžœ˜!K˜——K˜K˜šŸ œ˜Kšœžœ˜%K˜K˜šžœžœ˜!K˜RK˜Kšœžœf˜nK˜Kšžœ˜ K˜—šž˜Kšžœ˜ ——K˜K˜šŸ œ˜Kšœžœ ˜$Kšœžœ˜%K˜šžœžœžœ˜AK˜RK˜RK˜K˜Kšœžœ˜!K˜——Kšžœžœžœžœ˜3K˜K˜šŸœ˜(Kšœžœ˜&K˜šžœžœ˜#KšœR˜RKšœ˜Kšžœ žœžœ˜FKšœ˜——K˜K˜K˜KšŸ œžœžœžœ ˜Xšœ}™}K˜Kšœžœžœ%˜4Kšœžœžœ'˜7Kšœ žœžœ+žœ%˜xKšœžœžœ+žœ%˜{K˜2Kšžœžœžœ˜K˜K˜.K˜0K˜4K˜6K˜eK˜.K˜"K˜—K˜K˜šŸœ˜K˜K˜1K˜#K˜—K˜š Ÿœžœ žœ žœžœ˜5Kšžœ%˜+K˜—K˜š Ÿœžœ žœ žœžœ˜6Kšžœ'˜-K˜—K˜šžœ ž˜K˜l—K˜šžœ žœ˜K˜K˜$K˜)K˜—K˜K˜K˜šŸ œžœžœ˜+Kšœ žœžœžœ˜)Kšœ žœžœžœ˜4Kšœžœ žœžœ˜4Kšœ)žœ˜.Kšœ žœ žœ ˜*K˜šŸœžœ˜Kšœžœ žœ žœ ˜IK˜K˜—K˜4šžœž˜Kšžœžœžœ˜"šžœž˜$šž˜K˜ šž˜K˜*K˜*K˜*K˜4Kš žœžœžœžœžœ˜EK˜Kšžœ˜—Kš žœžœžœžœžœ˜Hšžœžœžœž˜K˜ Kšžœ˜—K˜4Kš žœžœžœžœžœ˜Hšžœžœžœž˜K˜%Kšžœ˜—K˜4Kš žœžœžœžœžœ˜HK˜ šžœžœžœž˜Kšœ žœ˜&Kšœžœžœ˜7Kšžœ˜—Kšžœžœžœ5˜Ršžœžœžœž˜%K˜K˜