DIRECTORY Commander, Controls, ControlsPrivate, FS, G2dBasic, G2dContour, G2dTool, Imager, IO, Rope, TIPUser, ViewerClasses, ViewerOps; G2dContourCmdsImpl: CEDAR PROGRAM IMPORTS Controls, ControlsPrivate, FS, G2dContour, G2dTool, Imager, IO, TIPUser, ViewerOps ~ BEGIN Control: TYPE ~ Controls.Control; IntegerPair: TYPE ~ G2dBasic.IntegerPair; IntegerPairSequence: TYPE ~ G2dBasic.IntegerPairSequence; Pair: TYPE ~ G2dBasic.Pair; Contour: TYPE ~ G2dContour.Contour; ROPE: TYPE ~ Rope.ROPE; ProgramData: TYPE ~ REF ProgramDataRec; ProgramDataRec: TYPE ~ RECORD [ outerData: Controls.OuterData ¬ NIL, -- some good stuff here alpha: Control ¬ NIL, -- contour interpolator scaler: Control ¬ NIL, -- contour scaler contour: Control ¬ NIL, -- the contour control contour0: Control ¬ NIL, -- key contour one contour1: Control ¬ NIL, -- key contour two save: IntegerPairSequence ¬ NIL, -- permit undo center: Pair ¬ [0, 0], -- of contour normals: BOOL ¬ FALSE -- show contour normals? ]; ContoursTest: Commander.CommandProc ~ { p: ProgramData ¬ NEW[ProgramDataRec]; p.contour ¬ Controls.NewControl[ name: "Contour and Normals", type: contour, clientData: p, w: 180, flavor: $Normals]; p.scaler ¬ Controls.NewControl["Scale", vSlider, p, 0.0, 5.0, 1.0, Scale,,,,,,, 138]; p.outerData ¬ Controls.OuterViewer[ name: "Contours Test", column: left, controls: LIST[p.contour, p.scaler], buttons: LIST[ Controls.ClickButton["Normals-Off", ToggleNormals, p, 0], Controls.ClickButton["Read", Read, p, 0], Controls.ClickButton["Write", Write, p, 0], Controls.ClickButton["Undo", Undo, p, 1], Controls.ClickButton["Center", Center, p, 1], Controls.ClickButton["Thin", Thin, p, 1], Controls.ClickButton["Smooth", Smooth, p, 1]], typescriptHeight: 18, clientData: p]; }; Scale: Controls.ControlProc ~ { p: ProgramData ¬ NARROW[control.clientData]; IF control.mouse.state = down THEN { ips: IntegerPairSequence ¬ Controls.GetContour[p.contour]; IF ips # NIL AND (p.save = NIL OR p.save.length # ips.length) THEN { p.save ¬ Controls.GetContour[p.contour]; p.center ¬ G2dContour.Centroid[G2dContour.FromIntegerPairs[p.save, TRUE]]; }; }; IF p.save # NIL THEN { c: Contour ¬ G2dContour.FromIntegerPairs[p.save, TRUE]; c ¬ G2dContour.Offset[c, [-p.center.x, -p.center.y]]; c ¬ G2dContour.Scale[c, [p.scaler.value, p.scaler.value]]; c ¬ G2dContour.Offset[c, p.center]; ToControl[p.contour, c]; }; }; Center: Controls.ClickProc ~ { p: ProgramData ¬ NARROW[clientData]; p.center ¬ [p.contour.w/2, p.contour.h/2]; ToControl[p.contour,G2dContour.Offset[G2dContour.Center[FromControl[p.contour]],p.center]]; p.save ¬ Controls.GetContour[p.contour]; }; Thin: Controls.ClickProc ~ { p: ProgramData ¬ NARROW[clientData]; p.save ¬ Controls.GetContour[p.contour]; ToControl[p.contour, G2dContour.Thin[FromControl[p.contour]]]; }; Smooth: Controls.ClickProc ~ { p: ProgramData ¬ NARROW[clientData]; p.save ¬ Controls.GetContour[p.contour]; ToControl[p.contour, G2dContour.Smooth[FromControl[p.contour]]]; }; Undo: Controls.ClickProc ~ { p: ProgramData ¬ NARROW[clientData]; Controls.SetContour[p.contour, p.save, TRUE]; p.center ¬ G2dContour.Centroid[FromControl[p.contour]]; }; ToggleNormals: Controls.ClickProc ~ { p: ProgramData ¬ NARROW[clientData]; Controls.ButtonToggle[p.outerData, p.normals ¬ NOT p.normals, "Normals-On", "Normals-Off"]; ViewerOps.PaintViewer[p.contour.viewer, client, FALSE, NIL]; }; PaintNormals: ViewerClasses.PaintProc ~ { control: Control ¬ NARROW[self.data]; p: ProgramData ¬ NARROW[control.clientData]; IF p.normals THEN { contour: Contour ¬ FromControl[p.contour]; IF contour = NIL THEN RETURN; contour.normals ¬ G2dContour.Normals[contour]; G2dContour.Paint[contour, context, TRUE]; } ELSE [] ¬ ControlsPrivate.PaintContour[self, context, whatChanged, clear]; }; Read: Controls.ClickProc ~ { p: ProgramData ¬ NARROW[clientData]; ts: Controls.Typescript ¬ p.outerData.typescript; name: ROPE ¬ Controls.TypescriptReadFileName[ts]; IF name # NIL THEN { stream: IO.STREAM ¬ FS.StreamOpen[name ! FS.Error => GOTO Bad]; ToControl[p.contour, G2dContour.Read[stream]]; EXITS Bad => Controls.TypescriptWrite[ts, IO.PutFR1["Can't open %g\n", IO.rope[name]]]; }; }; Write: Controls.ClickProc ~ { p: ProgramData ¬ NARROW[clientData]; r: ROPE ¬ Controls.TypescriptReadFileName[p.outerData.typescript]; IF r # NIL THEN G2dContour.Write[FS.StreamOpen[r, $create], FromControl[p.contour]]; }; FromControl: PROC [control: Control, t: REAL ¬ 0.0] RETURNS [contour: Contour] ~ { ips: IntegerPairSequence ¬ Controls.GetContour[control]; IF ips # NIL THEN contour ¬ G2dContour.FromIntegerPairs[ips, TRUE, t]; }; ToControl: PROC [control: Control, contour: Contour, repaint: BOOL ¬ TRUE] ~ { IF contour = NIL THEN Controls.Reset[control] ELSE Controls.SetContour[ control, G2dContour.IntegerPairsFromPairs[contour.pairs], TRUE, repaint]; }; ContoursInterpolate: Commander.CommandProc ~ { p: ProgramData ¬ NEW[ProgramDataRec]; p.alpha ¬ Controls.NewControl["Alpha", vSlider, p, 0.0, 1.0, 0.5, Interpolate,,,,,,, 120]; p.contour0 ¬ Controls.NewControl[type: contour, w: 160, proc: Interpolate, clientData: p]; p.contour1 ¬ Controls.NewControl[type: contour, w: 160, proc: Interpolate, clientData: p]; p.contour ¬ Controls.NewControl[type: contour, w: 160, clientData: p]; p.outerData ¬ Controls.OuterViewer[ name: "Contours Interpolate", controls: LIST[p.contour0, p.contour1, p.contour, p.alpha], clientData: p ]; }; Interpolate: Controls.ControlProc ~ { IF control.mouse.state # up THEN { p: ProgramData ¬ NARROW[control.clientData]; contour0: Contour ¬ FromControl[p.contour0]; contour1: Contour ¬ FromControl[p.contour1]; ToControl[p.contour, G2dContour.Interpolate[contour0, contour1, p.alpha.value]]; }; }; testUsage: ROPE ¬ "\nDraw a contour."; interpolateUsage: ROPE ¬ "\nDraw two contours and interpolate between them."; ViewerOps.RegisterViewerClass[$Normals, NEW[ViewerClasses.ViewerClassRec ¬ [ notify: Controls.NotifyControl, paint: PaintNormals, tipTable: TIPUser.InstantiateNewTIPTable["../Controls/Controls.tip"]]]]; G2dTool.Register["ContoursTest", ContoursTest, testUsage]; G2dTool.Register["ContoursInterpolate", ContoursInterpolate, interpolateUsage]; END. .. Outline: Controls.ClickProc ~ { p: ProgramData ¬ NARROW[clientData]; IF ColorOk[p] THEN G2dContour.Outline[p.cd, FromControl[p.contour], p.color]; }; ColorOk: PROC [p: ProgramData] RETURNS [ok: BOOL ¬ TRUE] ~ { }; \Π G2dContourCmdsImpl.mesa Copyright Σ 1985, 1992 by Xerox Corporation. All rights reserved. Bloomenthal, July 20, 1992 3:37 pm PDT Types General Testing Program Contour Interpolation Command Contour Similarity Command SimilarData: TYPE ~ REF SimilarDataRec; SimilarDataRec: TYPE ~ RECORD [ outerData: Controls.OuterData _ NIL, -- some good stuff here alpha: Control _ NIL, -- interpolation value box0: Control _ NIL, -- control for contour0 box1: Control _ NIL, -- control for contour1 boxI0: Control _ NIL, -- control for misc. display boxI1: Control _ NIL, -- control for misc. display contour: Contour _ NIL, -- interpolated contour contour0: Contour _ NIL, -- contour0 contour1: Contour _ NIL, -- contour1 save0: Contour _ NIL, -- to undo contour0 save1: Contour _ NIL -- to undo contour1 ]; ContoursSimilar: Commander.CommandProc ~ { NewControl: PROC [name: ROPE, row: NAT, proc: Controls.ControlProc, flavor: ATOM] RETURNS [Control] ~ { RETURN[Controls.NewControl[ name: name, type: contour, w: 150, proc: proc, clientData: p, row: row, flavor: flavor]]; }; p: SimilarData _ NEW[SimilarDataRec]; p.alpha _ Controls.NewControl["Alpha", vSlider, p, 0.0, 1.0, 0.5, Alpha, , , , , , , 120]; p.boxI0 _ NewControl["Interp Vs. Contour0", 0, NIL, $Similar]; p.boxI1 _ NewControl["Interp Vs. Contour1", 0, NIL, $Similar]; p.box0 _ NewControl["Contour0", 1, Contour0Changed, $Nil]; p.box1 _ NewControl["Contour1", 1, Contour1Changed, $Nil]; p.outerData _ Controls.OuterViewer[ name: "Contours Similar", controls: LIST[p.boxI0, p.boxI1, p.box0, p.box1, p.alpha], buttons: LIST[ Controls.ClickButton["Undo", SimilarUndo], Controls.ClickButton["Circles", SimilarCircles], Controls.ClickButton["Smooth", SimilarSmooth], Controls.ClickButton["Thin", SimilarThin]], clientData: p]; }; PrepContour: PROC [contour: Contour] RETURNS [Contour] ~ { IF contour = NIL THEN RETURN[NIL]; contour _ G2dContour.Orient[contour]; contour.normals _ G2dContour.Normals[contour]; contour.percents _ G2dContour.Percents[contour]; RETURN[contour]; }; Contour0Changed: Controls.ControlProc ~ { p: SimilarData _ NARROW[control.clientData]; p.contour0 _ PrepContour[FromControl[p.box0]]; Similar[p]; }; Contour1Changed: Controls.ControlProc ~ { p: SimilarData _ NARROW[control.clientData]; p.contour1 _ PrepContour[FromControl[p.box1]]; Similar[p]; }; Alpha: Controls.ControlProc ~ { IF control.mouse.state # up THEN Similar[NARROW[control.clientData, SimilarData]]; }; Similar: PROC [p: SimilarData] ~ { IF G2dContour.ContourOK[p.contour0] AND G2dContour.ContourOK[p.contour1] THEN { p.contour _ G2dContour.Interpolate[p.contour0, p.contour1, p.alpha.value]; p.contour.normals _ G2dContour.Normals[p.contour]; } ELSE p.contour _ NIL; ViewerOps.PaintViewer[p.boxI0.viewer, client, FALSE, NIL]; ViewerOps.PaintViewer[p.boxI1.viewer, client, FALSE, NIL]; }; PaintSimilar: ViewerClasses.PaintProc ~ { ShowNormalsRange: PROC [c, cc: Contour] ~ { Label: PROC [y: NAT, rope: ROPE] ~ {Draw2d.Label[context, [5, y], rope]}; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[wScale*(p.x+1), hScale*(p.y+1)]]}; DrawNormal: PROC [base, normal: Pair, scale: REAL] ~ { Draw2d.Arrow[context, base, Vector2.Add[base, Vector2.Mul[normal, scale]]]; }; IF G2dContour.ContourOK[c] AND G2dContour.ContourOK[cc] THEN { min: REAL _ 10000.0; p0: Pair _ Xform[c.pairs[c.pairs.length-1]]; FOR i: NAT IN [0..c.pairs.length) DO p1: Pair _ Xform[c.pairs[i]]; n: Pair _ c.normals[i]; nn: Pair _ G2dContour.PercentNormal[cc, c.percents[i]]; min _ MIN[min, Vector2.Dot[n, nn]]; DrawNormal[p1, n, 20.0]; DrawNormal[p1, nn, 30.0]; Draw2d.Line[context, p0, p1, dotted]; p0 _ p1; ENDLOOP; Label[135, IO.PutFR["min = %6.3f", IO.real[min]]]; Label[125, IO.PutFR["%g vs %g points", IO.int[c.pairs.length], IO.int[cc.pairs.length]]]; }; }; p: SimilarData _ NARROW[NARROW[self.data, Control].clientData]; bounds: Imager.Rectangle _ ImagerBackdoor.GetBounds[context]; wScale: REAL _ 0.5*(bounds.w-1.0); hScale: REAL _ 0.5*(bounds.h-1.0); Draw2d.Clear[context]; SELECT self FROM p.boxI0.viewer => ShowNormalsRange[p.contour, p.contour0]; p.boxI1.viewer => ShowNormalsRange[p.contour, p.contour1]; ENDCASE; }; ActionProc: TYPE ~ PROC [contour: Contour] RETURNS [Contour]; DoToContours: PROC [p: SimilarData, action: ActionProc] ~ { p.save0 _ G2dContour.Copy[p.contour0]; p.save1 _ G2dContour.Copy[p.contour1]; p.contour0 _ PrepContour[action[p.contour0]]; p.contour1 _ PrepContour[action[p.contour1]]; ToControl[p.box0, p.contour0]; ToControl[p.box1, p.contour1]; Similar[p]; }; SimilarUndo: Controls.ClickProc ~ { p: SimilarData _ NARROW[NARROW[clientData, Controls.OuterData].clientData]; ToControl[p.box0, p.contour0 _ p.save0]; ToControl[p.box1, p.contour1 _ p.save1]; Similar[p]; }; SimilarClear: Controls.ClickProc ~ { Action: ActionProc ~ {RETURN[NIL]}; DoToContours[NARROW[clientData], Action]; }; SimilarCircles: Controls.ClickProc ~ { action: ActionProc ~ { return: Contour _ G2dContour.Scale[G2dContour.Circle[4], [0.5, 0.5]]; return.circle _ FALSE; RETURN[return]; }; DoToContours[NARROW[NARROW[clientData, Controls.OuterData].clientData], action]; }; SimilarThin: Controls.ClickProc ~ { Action: ActionProc ~ {RETURN[G2dContour.Thin[contour]]}; DoToContours[NARROW[clientData], Action]; }; SimilarSmooth: Controls.ClickProc ~ { Action: ActionProc ~ {RETURN[G2dContour.Smooth[contour]]}; DoToContours[NARROW[clientData], Action]; }; ViewerOps.RegisterViewerClass[$Similar, NEW[ViewerClasses.ViewerClassRec _ [ notify: Controls.NotifyControl, paint: PaintSimilar, tipTable: TIPUser.InstantiateNewTIPTable["Controls.tip"]]]]; G2dTool.Register["ContoursSimilar", ContoursSimilar, "Test Similar[]\n"]; Fun and Hue ok _ CtBasic.InsureColorDisplayOn[8]; vt: Terminal.Virtual _ InterminalBackdoor.terminal; IF p.cd = NIL THEN { IF ColorDisplayManager.NextState[].level = off THEN { MessageWindow.Append["Turn on 8bpp color display"]; MessageWindow.Blink[]; RETURN[FALSE]; } ELSE p.cd _ IF ColorDisplayManager.NextState[].type = $Dither8 THEN ImagerDitherContext.FromTerminal[vt, TRUE] ELSE ImagerFullColorContext.FromTerminal[vt, TRUE]; }; Fun: Controls.ControlProc ~ { p: ProgramData _ NARROW[control.clientData]; hue: REAL _ p.hue.value+0.02; IF control.mouse.state = up OR NOT ColorOk[p] THEN RETURN; IF hue > p.hue.max THEN hue _ p.hue.min; Controls.SetSliderDialValue[p.hue, hue]; Controls.SetSliderDialValue[p.scaler, p.fun.value]; ScaleContour[NARROW[control.clientData, ProgramData]]; p.color _ ImagerColor.ColorFromRGB[ImagerColorFns.RGBFromHSL[[p.hue.value, 1.0, 0.5]]]; FillContour[p]; }; FillContour: PROC [p: ProgramData] ~ { IF ColorOk[p] THEN G2dContour.Fill[p.cd, FromControl[p.contour], p.color]; }; Fill: Controls.ClickProc ~ {FillContour[NARROW[clientData]]}; FillMap: Controls.ClickProc ~ { p: ProgramData _ NARROW[clientData]; IF ColorOk[p] THEN { map: SampleMap _ ImagerSample.MapFromFrameBuffer[ Terminal.GetColorFrameBufferA[InterminalBackdoor.terminal]]; G2dContour.FillMap[map, FromControl[p.contour], 100]; }; }; Hue: Controls.ControlProc ~ { p: ProgramData _ NARROW[control.clientData]; IF control.mouse.state = up THEN RETURN; p.color _ ImagerColor.ColorFromRGB[ImagerColorFns.RGBFromHSL[[p.hue.value, 1.0, 0.5]]]; FillContour[p]; }; Compose Comand SplineData: TYPE ~ SplineStructure.SplineData; ComposeData: TYPE ~ REF ComposeDataRec; ComposeDataRec: TYPE ~ RECORD [ out: IO.STREAM _ NIL, outer, graphics: Controls.Viewer _ NIL, entries: LIST OF Controls.Entry _ NIL, cam: Controls3d.Camera, -- camera c: Coeffs _ NIL, composed: BOOL _ FALSE, graphicsData: Controls.GraphicsData _ NIL, view: Matrix3d.Matrix _ NIL ]; ContoursCompose: Commander.CommandProc ~ { d: ComposeData _ NEW[ComposeDataRec]; d.out _ cmd.err; d.view _ NEW[Matrix3d.MatrixRep]; d.cam _ Controls3d.InitCamera[scale: 3.0, proc: Controller, data: d]; d.c _ Spline3d.CoeffsFromHermite[[[-0.5,0.,0.], [1.,1.,0.], [0.5,0.,0.], [1.,-1.,0.]]]; Controls.ControlRow[d.cam.pan, 2]; d.outer _ Controls.OuterViewer[ name: "Cotour Composer", column: left, entries: LIST[["Compose", Compose], ["Reset", Reset]], controls: LIST[ d.cam.xRot, d.cam.yRot, d.cam.zRot, d.cam.scale, d.cam.x, d.cam.y, d.cam.z, d.cam.fov], graphicsHeight: 300, graphicsShow: Display, data: d]; d.entries _ NARROW[d.outer.data, Controls.OuterData].entries; d.graphics _ NARROW[d.outer.data, Controls.OuterData].graphics; d.graphicsData _ NARROW[d.graphics.data]; }; Controller: PUBLIC Controls.ControlProc ~ { d: ComposeData _ NARROW[control.data]; SELECT control FROM d.cam.pan, d.cam.tilt, d.cam.roll, d.cam.zoom, d.cam.dx, d.cam.dy, d.cam.dz, d.cam.fov => Controls3d.SetCamera[d.cam]; ENDCASE => NULL; ViewerOps.PaintViewer[control.graphics, client, FALSE, control]; }; Reset: Controls.ClickProc ~ { d: ComposeData _ NARROW[NARROW[clientData, Controls.OuterData].data]; d.c _ Spline3d.CoeffsFromHermite[[[-0.5,0.,0.], [1.,1.,0.], [0.5,0.,0.], [1.,-1.,0.]]]; ViewerOps.PaintViewer[d.graphics, client, FALSE, d]; }; Compose: Controls.ClickProc ~ { d: ComposeData _ NARROW[NARROW[clientData, Controls.OuterData].data]; d.composed _ TRUE; ViewerOps.PaintViewer[d.graphics, client, FALSE, d]; d.composed _ FALSE; }; Display: Controls.GraphicsShow ~ { d: ComposeData _ NARROW[data]; Action: PROC ~ { d.view _ Controls3d.InitPix[context, w, h]; IF NOT d.composed THEN FOR s: SplineData _ SplineStructure.contour, s.next WHILE s # NIL DO Draw3d.DrawBezierPolygon[Spline3d.BezierFromCoeffs[s.c], context, d.view, dot]; Draw3d.DrawCurve[s.c, context, d.view]; ENDLOOP ELSE Draw3d.DrawCurve[d.c, context, d.view]; }; Draw2d.DoWithBuffer[context, Action, w, h] }; Miscellany NegateSegment: PROC [pm: PixelMap, s0, s1: Spot] ~ { x: REAL _ s0.x; xMax: INTEGER _ pm.fMin+pm.fSize; dx: REAL _ IF s0.y # s1.y THEN (s1.x-s0.x)/(s1.y-s0.y) ELSE 1.0; color: CARDINAL _ IF dx < 0 THEN 100 ELSE p.color; FOR y: INTEGER IN [s0.y..s1.y) DO xx: INTEGER _ Real.RoundI[x]; dest: PixelMap _ ImagerPixelMap.SetWindow[p.pm, [y, xx, 1, xMax-xx]]; ImagerPixelMap.Transfer[dest, p.pm, [null, complement]]; x _ x+dx; ENDLOOP; }; Fill: Controls.ControlProc ~ { Presumes contour is clockwise and its coordinates are B [-1..1]. p: ProgramData _ NARROW[control.data]; contour: Contour _ Contours.FromControl[p.contour]; SpotsStart: PROC [spots: SpotSequence] RETURNS [INTEGER] ~ { start, ymin: INTEGER _ 10000; FOR n: NAT IN [0..spots.length) DO IF spots[n].y < ymin THEN {ymin _ spots[n].y; start _ n}; ENDLOOP; RETURN[start]; }; FloodSegment: PROC [pm: PixelMap, s0, s1: Spot] ~ { x: REAL _ s0.x; xMax: INTEGER _ pm.fMin+pm.fSize; dx: REAL _ IF s0.y # s1.y THEN (s1.x-s0.x)/(s1.y-s0.y) ELSE 1.0; color: CARDINAL _ IF dx < 0 THEN 100 ELSE p.color; FOR y: INTEGER IN [s0.y..s1.y] DO xx: INTEGER _ Real.RoundI[x]; ImagerPixelMap.Fill[pm, [y, xx, 1, xMax-xx], color]; x _ x+dx; ENDLOOP; }; IF contour.closed THEN { spots: SpotSequence _ PmSpotsFromContour[contour, p.pm]; n, start: INTEGER _ SpotsStart[spots]; s0, s1: Spot _ spots[start]; ImagerPixelMap.Fill[p.pm, [p.pm.sMin, p.pm.fMin, p.pm.sSize, p.pm.fSize], 60]; DO s0 _ s1; s1 _ spots[n _ (n+1) MOD spots.length]; FloodSegment[p.pm, s0, s1]; IF n = start THEN EXIT; ENDLOOP; }; }; PmSpotsFromContour: PROC [contour: Contour, pm: PixelMap, invert: BOOL _ TRUE] RETURNS [SpotSequence] ~ { spots: SpotSequence _ NEW[SpotSequenceRec[contour.pairs.length]]; scale: REAL _ (MIN[pm.sSize, pm.fSize]-1)/2.0; xOffset: REAL _ pm.fMin+scale-1.0; yOffset: REAL _ pm.sMin+scale-1.0; xMin: INTEGER _ 10000; spots.length _ spots.maxLength; FOR n: NAT IN [0..spots.length) DO spots[n] _ [ Real.RoundI[xOffset+scale*contour.pairs[n].x], Real.RoundI[yOffset+scale*contour.pairs[n].y]]; ENDLOOP; IF invert THEN FOR n: NAT IN [0..spots.length) DO spots[n].y _ pm.sMin+pm.sSize-spots[n].y; ENDLOOP; RETURN[spots]; }; Negate: Controls.ControlProc ~ { Presumes contour is clockwise and its coordinates are B [-1..1]. p: ProgramData _ NARROW[control.data]; yLast: INTEGER _ -1; xMax: INTEGER _ p.pm.fMin+p.pm.fSize; contour: Contour _ Contours.FromControl[p.contour]; NegateSegment: PROC [pm: PixelMap, s0, s1: Spot] ~ { NegateToEnd: PROC [x, y: INTEGER] ~ { IF y # yLast THEN { dest: PixelMap _ ImagerPixelMap.SetWindow[pm, [y, x, 1, xMax-x]]; ImagerPixelMap.Transfer[dest, pm, [null, complement]]; yLast _ y; }; }; IF s0.y = s1.y THEN NegateToEnd[s0.x, s0.y] ELSE { Dot: Draw2d.PixelProc ~ {NegateToEnd[x, y]}; Draw2d.DoWithLine[s0.x, s0.y, s1.x, s1.y, Dot]; }; }; IF contour.closed THEN { spots: SpotSequence _ PmSpotsFromContour[contour, p.pm]; s0, s1: Spot _ spots[spots.length-1]; CtBasic.FillPm[p.pm, 100]; FOR n: NAT IN [0..spots.length) DO s0 _ s1; s1 _ spots[n]; CtBasic.PutLine[p.pm, s0.x, s0.y, s1.x, s1.y, 200]; NegateSegment[p.pm, s0, s1]; ENDLOOP; }; }; Negate: Controls.ControlProc ~ { Presumes contour is clockwise and its coordinates are B [-1..1]. p: ProgramData _ NARROW[control.data]; yLast: INTEGER _ -1; xMax: INTEGER _ p.pm.fMin+p.pm.fSize; contour: Contour _ Contours.FromControl[p.contour]; NegateSegment: PROC [pm: PixelMap, s0, s1: Spot] ~ { NegateToEnd: PROC [x, y: INTEGER] ~ { IF y # yLast THEN { dest: PixelMap _ ImagerPixelMap.SetWindow[pm, [y, x, 1, xMax-x]]; ImagerPixelMap.Transfer[dest, pm, [null, complement]]; yLast _ y; }; }; IF s0.y = s1.y THEN NegateToEnd[s0.x, s0.y] ELSE { Dot: Draw2d.PixelProc ~ {NegateToEnd[x, y]}; Draw2d.DoWithLine[s0.x, s0.y, s1.x, s1.y, Dot]; }; }; IF contour.closed THEN { spots: SpotSequence _ PmSpotsFromContour[contour, p.pm]; s0, s1: Spot _ spots[spots.length-1]; CtBasic.FillPm[p.pm, 100]; FOR n: NAT IN [0..spots.length) DO s0 _ s1; s1 _ spots[n]; CtBasic.PutLine[p.pm, s0.x, s0.y, s1.x, s1.y, 200]; NegateSegment[p.pm, s0, s1]; ENDLOOP; }; }; Random Contours gaussArray: Spline3d.RealSequence _ InitGaussSequence[1024]; Triple: TYPE ~ Vector3d.Triple; Coeffs: TYPE ~ Spline3d.Coeffs; Bezier: TYPE ~ Spline3d.Bezier; Box: TYPE ~ RECORD [a, b, c, d: Triple]; BoxSequence: TYPE ~ REF BoxSequenceRec; BoxSequenceRec: TYPE ~ RECORD [element: SEQUENCE maxLength: NAT OF Box]; SplineData: TYPE ~ SplineStructure.SplineData; SplineDataRec: TYPE ~ SplineStructure.SplineDataRec; ProgramData: TYPE ~ REF ProgramDataRec; ProgramDataRec: TYPE ~ RECORD [ out: IO.STREAM _ NIL, outer, graphics: Controls.Viewer _ NIL, entries: LIST OF Controls.Entry _ NIL, mouse: SplineStructure.Mouse _ [0, 0, none, left], cam: Controls3d.Camera, -- camera hold: Controls3d.Hold, -- handle on a point and its tangent nSpl: Controls.ControlData _ NIL, -- number of new splines from old one nRec: Controls.ControlData _ NIL, -- number of recursive levels graphicsData: Controls.GraphicsData _ NIL, view: Matrix3d.Matrix _ NIL, recurse: BOOL _ FALSE, noPick: BOOL _ TRUE, s: SplineData _ NIL, -- all the contour splines pd: SplineStructure.PickData _ NIL ]; Contour: Commander.CommandProc ~ { d: ProgramData _ NEW[ProgramDataRec]; d.out _ cmd.err; d.pd _ NEW[SplineStructure.PickDataRec]; d.view _ NEW[Matrix3d.MatrixRep]; d.s _ NEW[SplineDataRec]; d.cam _ Controls3d.InitCamera[dz: 0.5, zoom: 3., fov: 0., proc: Controller, data: d]; d.hold _ Controls3d.InitHold[Controller, d]; d.pd.s _ SplineStructure.contour _ d.s; d.s.c _ Spline3d.CoeffsFromHermite[[[-0.5, 0., 0.], [0.5, 0., 0.], [1., 1., 0.], [1., -1., 0.]]]; d.nSpl _ Controls.NewControl["nSpl", vert, , 2.0, 15.0, 4.0, TRUE, , , Controller, d]; d.nRec _ Controls.NewControl["nRec", vert, , 1.0, 15.0, 1.0, TRUE, , , Controller, d]; SplineStructure.PointPick[d.pd]; Controls3d.FocusHold[d.pd.tan, d.hold]; Controls.ControlRow[d.cam.pan, 2]; d.outer _ Controls.OuterViewer[ name: "Contour", column: left, entries: LIST[ ["Recurse", Recurse], ["Randomize", Randomize], ["PickToggle", PickToggle], ["Reset", Reset], ["PrintC", PrintC]], controls: LIST[ d.hold.x, d.hold.y, d.hold.z, d.hold.lng, d.hold.lat, d.hold.mag, d.nSpl, d.nRec, d.cam.xRot, d.cam.yRot, d.cam.zRot, d.cam.scale, d.cam.x, d.cam.y, d.cam.z, d.cam.fov], graphicsHeight: 300, graphicsProc: ScreenPick, graphicsShow: Display, data: d]; d.entries _ NARROW[d.outer.data, Controls.OuterData].entries; d.graphics _ NARROW[d.outer.data, Controls.OuterData].graphics; d.graphicsData _ NARROW[d.graphics.data]; }; Controller: PUBLIC Controls.ControlProc ~ { d: ProgramData _ NARROW[control.data]; IF d.pd.dividePending THEN { SplineStructure.DivideSpline[d.pd]; Controls.ControlVal[d.hold.mag, Vector3d.Mag[d.pd.sPt0.v1]]; }; SELECT control FROM d.hold.x, d.hold.y, d.hold.z => SplineStructure.ChangePosition[d.s, d.pd, [d.hold.x.val, d.hold.y.val, d.hold.z.val]]; d.hold.lng, d.hold.lat, d.hold.mag => IF NOT d.noPick THEN SplineStructure.ChangeTangent[ d.s, d.pd, Vector3d.CartesianFromPolar[[d.hold.lng.val, d.hold.lat.val, d.hold.mag.val]]]; d.cam.xRot, d.cam.yRot, d.cam.zRot, d.cam.scale, d.cam.x, d.cam.y, d.cam.z, d.cam.fov => Controls3d.UpDateCamera[d.cam]; ENDCASE => NULL; ViewerOps.PaintViewer[control.graphics, client, FALSE, control]; }; ScreenPick: Controls.GraphicsProc ~ { d: ProgramData _ NARROW[graphics.data]; IF graphics.mouse.state = up THEN RETURN; IF graphics.mouse.button = left THEN { SplineStructure.ScreenPick[d.s, graphics.mouse, d.view, d.pd]; Controls3d.FocusHold[d.pd.tan, d.hold]; }; }; PickToggle: Controls.ClickProc ~ { d: ProgramData _ NARROW[NARROW[clientData, Controls.OuterData].data]; d.noPick _ NOT d.noPick; ViewerOps.PaintViewer[d.graphics, client, FALSE, d]; }; PrintC: Controls.ClickProc ~ { d: ProgramData _ NARROW[NARROW[clientData, Controls.OuterData].data]; FOR s: SplineData _ d.s, s.next WHILE s # NIL DO Print3d.Matrix[d.out, s.c]; ENDLOOP; }; Reset: Controls.ClickProc ~ { d: ProgramData _ NARROW[NARROW[clientData, Controls.OuterData].data]; SplineStructure.contour _ d.s _ NEW[SplineDataRec]; (d.pd.s _ d.s).c _ Spline3d.CoeffsFromHermite[[[-0.5,0.,0.], [0.5,0.,0.], [1.,1.,0.], [1.,-1.,0.]]]; ViewerOps.PaintViewer[d.graphics, client, FALSE, d]; }; Recurse: Controls.ClickProc ~ { d: ProgramData _ NARROW[NARROW[clientData, Controls.OuterData].data]; d.recurse _ TRUE; ViewerOps.PaintViewer[d.graphics, client, FALSE, d]; d.recurse _ FALSE; }; Randomize: Controls.ClickProc ~ { d: ProgramData _ NARROW[NARROW[clientData, Controls.OuterData].data]; s: SplineData _ d.s; WHILE s # NIL DO r: SplineData _ RandomizeSpline[s.c, Real.RoundI[d.nSpl.val]]; r.prev _ s.prev; IF s.prev # NIL THEN s.prev.next _ r ELSE SplineStructure.contour _ d.s _ r; WHILE r.next # NIL DO r _ r.next; ENDLOOP; IF s.next # NIL THEN s.next.prev _ r; s _ r.next _ s.next; ENDLOOP; ViewerOps.PaintViewer[d.graphics, client, FALSE, d]; }; InsertSpline: PROC [insert, splines: SplineData] RETURNS [SplineData] ~ { Insert new SplineData (insert) at end of splines: s: SplineData _ splines; IF splines = NIL THEN RETURN[insert]; WHILE s.next # NIL DO s _ s.next; ENDLOOP; insert.prev _ s; insert.next _ s.next; s.next _ insert; IF insert.next # NIL THEN insert.next.prev _ insert; RETURN[splines]; }; RandomizeSpline: PROC [c: Coeffs, nSplines: INTEGER _ 2] RETURNS [SplineData] ~ { splines: SplineData _ NIL; coeffs: Spline3d.CoeffsSequence; bez: Bezier _ Spline3d.BezierFromCoeffs[c]; boxes: BoxSequence _ BoxesFromBezier[bez, nSplines]; knots: Spline3d.KnotSequence _ NEW[Spline3d.KnotSequenceRep[nSplines+1]]; scale: REAL _ 3.0/REAL[MAX[1, nSplines]]; tan0: Triple _ Vector3d.Mul[Vector3d.Sub[bez.b1, bez.b0], scale]; tan1: Triple _ Vector3d.Mul[Vector3d.Sub[bez.b3, bez.b2], scale]; IF nSplines = 0 THEN RETURN[splines]; knots[0] _ bez.b0; FOR n: NAT IN [1..nSplines) DO knots[n] _ RandomPtInBox[boxes[n-1]]; ENDLOOP; knots[nSplines] _ bez.b3; knots.length _ nSplines+1; coeffs _ Spline3d.Interpolate[knots, tan0, tan1]; FOR n: NAT IN [0..nSplines) DO insert: SplineData _ NEW[SplineDataRec]; insert.c _ coeffs[n]; splines _ InsertSpline[insert, splines]; ENDLOOP; RETURN[splines]; }; BoxesFromBezier: PROC [bez: Bezier, nBoxes: NAT] RETURNS [BoxSequence] ~ { boxes: BoxSequence _ NEW[BoxSequenceRec[nBoxes]]; p0: Triple _ bez.b0; p1: Triple _ bez.b1; dp0: Triple _ Vector3d.Div[Vector3d.Sub[bez.b3, bez.b0], REAL[nBoxes]]; dp1: Triple _ Vector3d.Div[Vector3d.Sub[bez.b2, bez.b1], REAL[nBoxes]]; FOR n: NAT IN [0..nBoxes) DO pp0: Triple _ Vector3d.Add[p0, dp0]; pp1: Triple _ Vector3d.Add[p1, dp1]; boxes[n] _ [p0, p1, pp1, pp0]; p0 _ pp0; p1 _ pp1; ENDLOOP; RETURN[boxes]; }; RanR: PROC RETURNS [REAL] ~ {RETURN[gaussArray[Random.ChooseInt[min: 0, max: 1023]]]}; RandomPtInSegment: PROC [p0, p1: Triple] RETURNS[Triple] ~ { w: REAL _ RanR[]; RETURN[Vector3d.Add[Vector3d.Mul[p0, w], Vector3d.Mul[p1, 1.0-w]]]; }; RandomPtInBox: PROC [box: Box] RETURNS[t: Triple] ~ { w0: REAL _ RanR[]; w1: REAL _ RanR[]; w2: REAL _ RanR[]; w3: REAL _ RanR[]; n: REAL _ 1.0/(w0+w1+w2+w3); w0 _ w0*n; w1 _ w1*n; w2 _ w2*n; w3 _ w3*n; t.x _ box.a.x*w0+box.b.x*w1+box.c.x*w2+box.d.x*w3; t.y _ box.a.y*w0+box.b.y*w1+box.c.y*w2+box.d.y*w3; t.z _ box.a.z*w0+box.b.z*w1+box.c.z*w2+box.d.z*w3; }; Function: PROC [x: REAL] RETURNS [REAL] ~ { Input and output range [0..1]. x _ 2.0*3.1415926535*(x-0.5); RETURN[0.5*(1.0+RealFns.Sin[x]+RealFns.Cos[x])]; }; Display: Controls.GraphicsShow ~ { d: ProgramData _ NARROW[data]; Action: PROC ~ { d.view _ Controls3d.InitPix[context, w, h]; IF d.recurse THEN { p, q: Triple _ [0.0, 0.0, 0.0]; FOR i: NAT IN [0..100) DO q.x _ REAL[i-50]/100.0; q.y _ 0.5+q.x; FOR n: NAT IN[1..Real.RoundI[d.nRec.val]] DO q.y _ Function[q.y]; ENDLOOP; q.y _ 0.5*q.y; IF i > 0 THEN Draw3d.PP[p, q, context, d.view]; p _ q; ENDLOOP; } ELSE FOR s: SplineData _ d.s, s.next WHILE s # NIL DO Draw3d.DrawBezierPolygon[Spline3d.BezierFromCoeffs[s.c], context, d.view, dot]; Draw3d.DrawCurve[s.c, context, d.view]; ENDLOOP; IF NOT d.noPick THEN Draw3d.PV[d.pd.pos, d.pd.tan,, context, d.view, IF d.pd.t IN(0.0..1.0) THEN cross ELSE none]; }; Draw2d.DoWithBuffer[context, Action, w, h] }; InitGaussSequence: PROC [nEntries: NAT] RETURNS [Spline3d.RealSequence] ~ { x: REAL _ 0.0; sigma: REAL ~ 0.16; dx: REAL _ 1.0/REAL[nEntries]; ret: Spline3d.RealSequence _ NEW[Spline3d.RealSequenceRec[nEntries]]; FOR i: NAT IN [0..1024) DO xx: REAL _ x-0.5; ret[i] _ RealFns.Exp[-xx*xx/(2.0*sigma*sigma)]; x _ x+dx; ENDLOOP; RETURN[ret]; }; Commander.Register["Contour", Contour, "\nTest some contour ideas."]; RandomizeSpline: PROC [c: Coeffs, nSplines: INTEGER _ 2] RETURNS [SplineData] ~ { splines: SplineData _ NIL; nBoxes: NAT _ 2*nSplines-2; bez: Bezier _ Spline3d.BezierFromCoeffs[c]; boxes: BoxSequence _ BoxesFromBezier[bez, nBoxes]; pts: TripleSequence _ NEW[TripleSequenceRec[nBoxes+2]]; ends: TripleSequence _ NEW[TripleSequenceRec[nSplines+1]]; pts[0] _ RandomPtInSegment[bez.b0, bez.b1]; pts[nBoxes+1] _ RandomPtInSegment[bez.b2, bez.b3]; FOR n: NAT IN [0..nBoxes) DO pts[n+1] _ RandomPtInBox[boxes[n]]; ENDLOOP; ends[0] _ bez.b0; ends[nSplines] _ bez.b3; FOR n: NAT IN [1..nSplines) DO ends[n] _ Ave[pts[n+n-1], pts[n+n]]; ENDLOOP; FOR n: NAT IN [0..nSplines) DO insert: SplineData _ NEW[SplineDataRec]; insert.c _ Spline3d.CoeffsFromBezier[[ends[n], pts[n+n], pts[n+n+1], ends[n+1]]]; splines _ InsertSpline[insert, splines]; ENDLOOP; RETURN[splines]; }; MinMaxOfCurve: PROC [c: Coeffs, i: NAT] RETURNS [min, max: REAL] ~ { CheckI: PROC [t: REAL] ~ { t2: REAL _ t*t; y: REAL _ t2*t*c[0][i]+t2*c[1][i]+t*c[2][i]+c[3][i]; min _ MIN[min, y]; max _ MAX[max, y]; }; roots: Quadratic.Roots _ Quadratic.RealRoots[3.0*c[0][i], 2.0*c[1][i], c[2][i]]; min _ max _ c[3][i]; CheckI[1.0]; IF roots.nRoots > 0 AND roots.r1 IN [0.0..1.0] THEN CheckI[roots.r1]; IF roots.nRoots > 1 AND roots.r2 IN [0.0..1.0] THEN CheckI[roots.r2]; }; Ave: PROC [a, b: Triple] RETURNS [Triple] ~ { RETURN[[0.5*(a.x+b.x), 0.5*(a.y+b.y), 0.5*(a.z+b.z)]]; }; Κw•NewlineDelimiter –"cedarcode" style™šœ™Jšœ Οeœ6™BJ™&—J˜JšΟk œ'žœ)žœ*˜‡J˜šΡblnœžœž˜!Jšžœžœžœ˜Z—J˜Jšœž˜headšΟl™Jšœ žœ˜%Jšœžœ˜,Jšœžœ ˜9Jšœž œ˜ Jšœ žœ˜'šžœžœžœ˜J˜—Jšœžœžœ˜*šœžœžœ˜!Jšœ%žœΟc˜BJšœžœ‘˜8Jšœžœ‘˜3Jšœžœ‘˜8Jšœžœ‘˜5Jšœžœ‘˜5Jšœ#žœ‘˜6Jšœ"‘ ˜/Jšœžœžœ‘˜8Jšœ˜——š ™šΠbn œ˜'Jšœžœ˜%˜ JšœU˜U—J˜V˜#Jšœ˜J˜ Jšœ žœ˜$šœ žœ˜Jšœ9˜9Jšœ)˜)Jšœ+˜+Jšœ)˜)Jšœ-˜-Jšœ)˜)Jšœ.˜.—Jšœ˜Jšœ˜—J˜—J˜šΟnœ˜Jšœžœ˜,šžœžœ˜$J˜:š žœžœžœ žœžœžœ˜DJ˜(JšœCžœ˜JJ˜—J˜—šžœ žœžœ˜Jšœ1žœ˜7J˜5J˜:J˜#Jšœ˜J˜—J˜J˜—š£œ˜Jšœžœ ˜$J˜*Jšœ[˜[J˜(J˜J˜—š£œ˜Jšœžœ ˜$J˜(Jšœ>˜>J˜J˜—š£œ˜Jšœžœ ˜$J˜(Jšœ@˜@J˜J˜—š£œ˜Jšœžœ ˜$Jšœ'žœ˜-J˜7J˜J˜—š£ œ˜%Jšœžœ ˜$Jšœ/žœ)˜[Jšœ0žœžœ˜Jšœ/žœ ™>Jšœ:™:Jšœ:™:šœ#™#Jšœ™Jšœ žœ,™:šœ žœ™Jšœ*™*Jšœ0™0Jšœ.™.Jšœ+™+—Jšœ™—J™J™—š£ œžœžœ™:Jš žœ žœžœžœžœ™"Jšœ%™%Jšœ.™.Jšœ0™0Jšžœ ™J™J™—š£œ™)Jšœžœ™,J™.J™ J™J™—š£œ™)Jšœžœ™,J™.J™ J™J™—š’œ™Jšžœžœ žœ#™RJšœ™J™—š’œžœ™"šžœ"žœ"žœ™OJšœJ™JJšœ2™2J™—Jšžœ žœ™Jšœ.žœžœ™:Jšœ.žœžœ™:J™J™—š£ œ™)š£œžœ™+Jš£œžœžœžœ*™IJš£œžœ žœ žœ$™Rš£ œžœžœ™6JšœK™KJ™—šžœžœžœ™>Jšœžœ ™Jšœ,™,šžœžœžœž™$J™Jšœ™Jšœ7™7Jšœžœ™#Jšœ™Jšœ™J™%J™Jšžœ™—Jšœ žœžœ ™2Jšœ žœžœžœ™YJ™—Jšœ™—Jšœžœžœ!™?J™=Jšœžœ™"Jšœžœ™"J™šžœž™J™:J™:Jšžœ™—Jšœ™J™—Jšœ žœžœžœ ™=J™š£ œžœ)™;Jšœ&™&Jšœ&™&Jšœ-™-Jšœ-™-Jšœ™Jšœ™J™ J™J™—š’ œ™#Jšœžœžœ-™KJšœ(™(Jšœ(™(J™ J™J™—š’ œ™$Jš’œžœžœ™#Jšœ žœ™)J™J™—š’œ™&šœ™JšœE™EJšœžœ™Jšžœ ™Jšœ™—Jšœ žœžœ6™PJ™J™—š£ œ™#Jš£œžœ™8Jšœ žœ™)J™J™—š£ œ™%Jš£œžœ™:Jšœ žœ™)J™J™—Jšœ žœ˜&Jšœžœ7˜MJ˜šœ(žœ!˜LJšœ˜Jšœ˜J˜HJ˜—šœ(žœ!™LJšœ™Jšœ™Jšœ<™Jšžœ&žœ™/Jšžœ)žœ™3——J™—J˜J˜—š£œ™Jšœžœ™,Jšœžœ™Jš žœžœžœ žœžœ™:Jšžœžœ™(Jšœ(™(Jšœ3™3Jšœ žœ#™6J™WJšœ™Jšœ™J™—š£ œžœ™&Jšžœ žœ8™JJšœ™J™—š£œ$žœ™=J™—š£œ™Jšœžœ ™$šžœ žœ™šœ1™1Jšœ<™<—Jšœ5™5J™—J™J™—š£œ™Jšœžœ™,Jšžœžœžœ™(JšœW™WJšœ™Jšœ™——š ™Jšœžœ™0J™Jšœžœžœ™(šœžœžœ™ Jšœžœžœžœ™Jšœ$žœ™(Jšœ žœžœžœ™)Jšœ‘ ™(Jšœžœ™Jšœ žœžœ™Jšœ'žœ™+Jšœž™Jšœ™J™—šΟbœ™*Jšœžœ™%Jšœ™Jšœ žœ™!JšœE™EJšœW™WJ™"šœ™Jšœ™Jšœ ™ Jšœ žœ)™6šœ žœ™JšœW™W—J™J™Jšœ ™ —Jšœ žœ+™=Jšœ žœ,™?Jšœžœ™)Jšœ™—š’ œžœ™+Jšœžœ™&šžœ ž™J™vJšžœžœ™—Jšœ0žœ ™@J™J™—š€œ™Jšœžœžœ'™EJšœW™WJšœ*žœ™4Jšœ™J™—š€œ™Jšœžœžœ'™EJšœ žœ™Jšœ*žœ™4Jšœ žœ™Jšœ™J™—š€œ™"Jšœžœ™š£œžœ™Jšœ+™+šžœžœ ™š žœžœ1žœžœž™IJšœO™OJšœ'™'Jšž™——Jšžœ(™,Jšœ™—Jšœ*™*J™——š  ™ š’£œžœ!™4Jšœžœ™Jšœžœ™!Jš œžœžœ žœžœ™@Jš œžœžœžœžœ ™2šžœžœžœž™!Jšœžœ™J™EJšœ8™8J™ Jšžœ™—J™J™—š€œ™Jšœ6Οmœ ™@Jšœžœ™&J™3š£ œžœžœžœ™™>J™Jšœ žœ™"Jšœ žœ™"Jšœ žœ™"J™Jšœžœžœ™,Jšœ žœžœ™)Jš œžœžœ žœ žœžœ™IJ™Jšœ žœ™0Jšœžœ!™5J™Jšœžœžœ™)šœžœžœ™ Jšœžœžœžœ™Jšœ$žœ™(Jšœ žœžœžœ™)Jšœ5™5Jšœ‘ ™(Jšœ‘$™CJšœ!žœ‘%™KJšœ!žœ‘™CJšœ'žœ™+Jšœžœ™ Jšœ žœžœ™Jšœ žœžœ™Jšœžœ‘™7Jšœ#ž™&Jšœ™J™—š€œ™"Jšœžœ™%Jšœ™Jšœžœ™(Jšœ žœ™!Jšœžœ™JšœU™UJšœ,™,Jšœ'™'Jšœa™aJšœ=žœ™VJšœ=žœ™VJ™ J™'J™"šœ™Jšœ™Jšœ ™ šœ žœ™Jšœ/™/JšœB™B—šœ žœ™JšœQ™QJšœW™W—J™J™J™Jšœ ™ —Jšœ žœ+™=Jšœ žœ,™?Jšœžœ™)Jšœ™—J™š’ œžœ™+Jšœžœ™&šžœžœ™Jšœ#™#Jšœ<™™>Jšœ'™'J™—J™J™—š€ œ™"Jšœžœžœ'™EJšœ žœ ™Jšœ*žœ™4Jšœ™J™—š€œ™Jšœžœžœ'™EJš žœžœžœžœž ™UJšœ™J™—š€œ™Jšœžœžœ'™EJšœ žœ™3Jšœd™dJšœ*žœ™4Jšœ™J™—š€œ™Jšœžœžœ'™EJšœ žœ™Jšœ*žœ™4Jšœ žœ™Jšœ™J™—š€ œ™!Jšœžœžœ'™EJšœ™J™šžœžœž™Jšœ>™>J™Jšžœ žœžœžœ#™LJšžœ žœžœ žœ™*Jšžœ žœžœ™%J™Jšžœ™J™—Jšœ*žœ™4J™J™—š£ œžœžœ™IJ™1Jšœ™Jšžœ žœžœžœ ™%Jšžœ žœžœ žœ™*Jšœ™Jšœ™Jšœ™Jšžœžœžœ™4Jšžœ ™J™J™—š£œžœžœžœ™QJšœžœ™Jšœ ™ J™+Jšœ4™4Jšœžœ'™IJšœžœžœžœ™)JšœA™AJšœA™A—™Jšžœžœžœ ™%J™Jšœ™Jš žœžœžœžœ&žœ™MJšœ™J™J™J™1J™šžœžœžœž™Jšœžœ™(J™J™(Jšžœ™—J™Jšžœ ™J™—J™š£œžœžœžœ™JJšœžœ™1J™J™Jšœ9žœ ™GJšœ9žœ ™GJ™šžœžœžœ ž™J™$J™$Jšœ™J™ J™ Jšžœ™—J™Jšžœ™J™J™—Jš £œžœžœžœžœ3™VJ™š£œžœžœ ™