DIRECTORY Draw2d, FileNames, FS, G2dContour, G3dDraw, G3dBasic, G3dIO, G3dMatrix, G3dQuaternion, G3dShape, G3dSpline, G3dVector, G3dTube, Imager, ImagerColor, ImagerFont, ImagerInterpress, IO, Real, Rope; G3dTubeMiscImpl: CEDAR PROGRAM IMPORTS Draw2d, FileNames, FS, G2dContour, G3dDraw, G3dIO, G3dMatrix, G3dQuaternion, G3dSpline, G3dVector, G3dTube, Imager, ImagerColor, ImagerFont, ImagerInterpress, IO, Real, Rope EXPORTS G3dTube ~ BEGIN DrawType: TYPE ~ Draw2d.DrawType; MarkType: TYPE ~ Draw2d.MarkType; Hull: TYPE ~ G3dBasic.Hull; Pair: TYPE ~ G3dBasic.Pair; PairSequence: TYPE ~ G3dBasic.PairSequence; Triple: TYPE ~ G3dBasic.Triple; Matrix: TYPE ~ G3dMatrix.Matrix; Viewport: TYPE ~ G3dMatrix.Viewport; Quaternion: TYPE ~ G3dQuaternion.Quaternion; Shape: TYPE ~ G3dShape.Shape; Bezier: TYPE ~ G3dSpline.Bezier; Details: TYPE ~ G3dTube.Details; DetailsRep: TYPE ~ G3dTube.DetailsRep; XSection: TYPE ~ G3dTube.XSection; XSectionSequence: TYPE ~ G3dTube.XSectionSequence; PointProc: TYPE ~ G3dTube.PointProc; PolyProc: TYPE ~ G3dTube.PolyProc; Tube: TYPE ~ G3dTube.Tube; TubeProc: TYPE ~ G3dTube.TubeProc; TubeRep: TYPE ~ G3dTube.TubeRep; TubeSequence: TYPE ~ G3dTube.TubeSequence; TubeSequenceRep: TYPE ~ G3dTube.TubeSequenceRep; Context: TYPE ~ Imager.Context; STREAM: TYPE ~ IO.STREAM; ROPE: TYPE ~ Rope.ROPE; PI: REAL ~ G3dBasic.PI; origin: Triple ~ G3dBasic.origin; Line: TYPE ~ G3dIO.Line; ReadTube: PUBLIC PROC [stream: STREAM] RETURNS [tube: Tube] ~ { IF stream = NIL THEN RETURN[NIL] ELSE { InnerReadTube: PROC [prev: Tube] RETURNS [tube: Tube] ~ { TubeLineOK: PROC RETURNS [ok: BOOL ฌ TRUE] ~ { DO line ฌ G3dIO.GetLine[stream, line ! IO.EndOfStream => GOTO eof]; IF Rope.Equal[G3dIO.GetWord[line], "tube:", FALSE] THEN RETURN; REPEAT eof => RETURN[FALSE]; ENDLOOP; }; IF TubeLineOK[] THEN { next: BOOL; tube ฌ NEW[TubeRep]; tube.prev ฌ prev; tube.p0 ฌ G3dIO.GetTriple[line]; tube.p1 ฌ G3dIO.GetTriple[line]; tube.v0 ฌ G3dIO.GetTriple[line]; tube.v1 ฌ G3dIO.GetTriple[line]; tube.r0 ฌ G3dIO.GetReal[line]; tube.r1 ฌ G3dIO.GetReal[line]; tube.tw0 ฌ G3dIO.GetReal[line]; tube.tw1 ฌ G3dIO.GetReal[line]; tube.scale ฌ G3dIO.GetReal[line]; tube.taper ฌ G3dIO.GetReal[line]; tube.epsilon ฌ G3dIO.GetReal[line]; tube.circleRes ฌ G3dIO.GetInteger[line]; next ฌ Rope.Equal[G3dIO.GetWord[line], "next", FALSE]; tube.branches ฌ NEW[TubeSequenceRep[G3dIO.GetInteger[line]]]; tube.branches.length ฌ tube.branches.maxLength; G3dTube.SetSpline[tube]; IF next THEN tube.next ฌ InnerReadTube[tube]; IF tube.branches # NIL THEN FOR n: NAT IN [0..tube.branches.length) DO tube.branches[n] ฌ InnerReadTube[tube]; ENDLOOP; }; }; line: Line ฌ G3dIO.ObtainLine[]; tube ฌ InnerReadTube[NIL]; G3dIO.ReleaseLine[line]; }; }; WriteTube: PUBLIC PROC [stream: STREAM, tube: Tube, m: Matrix ฌ NIL] ~ { Write: TubeProc ~ { IO.PutRope[stream, "Tube: "]; G3dIO.WriteTriple[stream, IF m = NIL THEN tube.p0 ELSE G3dMatrix.Transform[tube.p0, m]]; G3dIO.WriteTriple[stream, IF m = NIL THEN tube.p1 ELSE G3dMatrix.Transform[tube.p1, m]]; G3dIO.WriteTriple[stream, IF m = NIL THEN tube.v0 ELSE G3dMatrix.TransformVec[tube.v0, m]]; G3dIO.WriteTriple[stream, IF m = NIL THEN tube.v1 ELSE G3dMatrix.TransformVec[tube.v1, m]]; IO.PutF[stream, "%5.4f %5.4f\t", IO.real[tube.r0], IO.real[tube.r1]]; IO.PutF[stream, "%5.4f %5.4f\t", IO.real[tube.tw0], IO.real[tube.tw1]]; IO.PutF[stream, "%5.4f %5.4f\t", IO.real[tube.scale], IO.real[tube.taper]]; IO.PutF[stream, "%5.4f %g ", IO.real[tube.epsilon], IO.int[tube.circleRes]]; IO.PutRope[stream, IF tube.next # NIL THEN "next " ELSE "no "]; IO.PutF1[stream, "%g", IO.int[NBranches[tube]]]; IO.PutRope[stream, "\n"]; }; ApplyToTube[tube, Write]; }; WritePointsPolys: PUBLIC PROC [tube: Tube, fileName: ROPE, m: Matrix ฌ NIL] RETURNS [nPoints, nPolys: INT] ~ { Point: PointProc ~ { IO.PutF1[out, "%g\t", IO.int[id]]; G3dIO.WriteTriple[out, position]; G3dIO.WriteTriple[out, normal]; IO.PutF[out, "%6.5f %6.5f\n", IO.real[texture.x], IO.real[texture.y]]; }; Poly: PolyProc ~ { IO.PutFL[out,"%g\t%4g %4g %4g\n", LIST[IO.int[id], IO.int[p0], IO.int[p1], IO.int[p2]]]; }; out: STREAM ฌ FS.StreamOpen[fileName, $create]; nPoints ฌ NPoints[tube]; nPolys ฌ NPolys[tube]; IO.PutF1[out, "-- %g\n\n", IO.rope[tube.name]]; IO.PutF[out, "DataSize~ vertices: %g, surfaces: %g\n\n", IO.int[nPoints], IO.int[nPolys]]; IO.PutRope[out, "Vertices~ index: integer xyzCoords: triple normalVec: triple textureCoords: pair\n\n"]; [] ฌ Points[tube, Point, m]; IO.PutRope[out, "\nSurfaces~ index: integer vertices: nats\n\n"]; [] ฌ Polys[tube, Poly]; IO.Close[out]; }; WriteIP: PUBLIC PROC [ref: ImagerInterpress.Ref, tube: Tube, details: Details, m: Matrix] ~ { font: ImagerFont.Font ฌ ImagerFont.Scale[ImagerFont.Find["xerox/pressfonts/helvetica-mrr"], 12.0]; ContextProc: PROC [context: Context] ~ { metersPerPoint: REAL ~ .0254/72.0; Imager.ScaleT[context, metersPerPoint]; Imager.SetStrokeWidth[context, 1.0]; Imager.SetStrokeEnd[context, round]; Imager.SetFont[context, font]; Imager.TranslateT[context, [0.0, 0.5*11.0*72.0]]; G3dTube.DrawTube[context, tube, details, m]; }; ImagerInterpress.DoPage[ref, ContextProc]; }; TubeFromFile: PUBLIC PROC [fileName: ROPE] RETURNS [tube: Tube] ~ { name: ROPE ฌ FileNames.ResolveRelativePath[fileName]; IF name # NIL THEN { stream: STREAM ฌ FS.StreamOpen[name ! FS.Error => GOTO noOpen]; IF (tube ฌ ReadTube[stream]) = NIL THEN RETURN; IO.Close[stream]; name ฌ FileNames.GetShortName[name]; tube.name ฌ Rope.Substr[name, 0, Rope.Index[name, 0, "."]]; EXITS noOpen => NULL; }; RETURN[tube]; }; TubeToFile: PUBLIC PROC [tube: Tube, fileName: ROPE, matrix: Matrix ฌ NIL] ~ { IF fileName # NIL THEN { s: STREAM ฌ FS.StreamOpen[FileNames.ResolveRelativePath[fileName], $create]; IO.PutF1[s, "\n-- %g, format for each line:\n", IO.rope[fileName]]; IO.PutRope[s, "--Tube: p0 p1 v0 v1 r0 r1 tw0 tw1 scale taper eps cres nBranches\n\n"]; WriteTube[s, tube, matrix]; IO.Close[s]; }; }; Points: PUBLIC PROC [tube: Tube, pointProc: PointProc, view: Matrix ฌ NIL] RETURNS [nPoints: INT] ~ { pointId: INT ฌ 0; uRangeStart: REAL ฌ 2.0*PI*tube.r0; OneSpline: PROC [tube: Tube, iStart: NAT, uRange0, v: REAL] ~ { IF tube # NIL AND tube.xSections # NIL THEN { uRange1: REAL ฌ uRange0*(0.5+0.5*(tube.r1/tube.r0)); -- a compromise duRange: REAL ฌ (uRange1-uRange0)/(tube.xSections.length-1.0); circle: PairSequence ฌ G2dContour.CirclePairs[tube.circleRes]; tapered: BOOL ฌ tube.r1 # tube.r0; vFactor: REAL ฌ IF tapered THEN (tube.r0-tube.r1)/G3dVector.Distance[tube.p0, tube.p1] ELSE 1.0; pNow, pPrev: Triple ฌ tube.p0; odd: BOOL ฌ tube.circleRes MOD 2 = 1; FOR i: NAT IN[iStart..NXSections[tube]) DO uRange: REAL ฌ uRange0+i*duRange; u: REAL ฌ 0.0; du: REAL ฌ 0.5*uRange/tube.circleRes; m: Matrix ฌ tube.xSections[i].matrix; mm: Matrix ฌ IF view # NIL THEN G3dMatrix.Mul[m, view] ELSE m; pPrev ฌ pNow; pNow ฌ [m[3][0], m[3][1], m[3][2]]; IF i # 0 THEN v ฌ v+G3dVector.Distance[pNow, pPrev]; FOR j: NAT IN[0..tube.circleRes) DO p: Triple ฌ G3dMatrix.TransformPair[circle[j], mm]; n: Triple ฌ G3dMatrix.TransformVec[[circle[j].x, circle[j].y, 0.0], mm]; IF tapered THEN { vv: Triple ฌ G3dSpline.Velocity[tube.spline, tube.xSections[i].t]; len: REAL ฌ G3dVector.Length[n]; n ฌ G3dVector.Add[n, G3dVector.Mul[vv, len*vFactor/G3dVector.Length[vv]]]; }; n ฌ G3dVector.Unit[n]; [] ฌ pointProc[pointId, p, n, [u, v]]; pointId ฌ pointId+1; u ฌ IF j < tube.circleRes/2 OR odd THEN u+du ELSE u-du; -- u radial symmetry ENDLOOP; ENDLOOP; IF tube.next # NIL AND tube.next.circleRes = tube.circleRes AND tube.next.r0 = tube.r1 THEN OneSpline[tube.next, 1, uRange1, v] -- skip first xSection ELSE OneSpline[tube.next, 0, uRange1, v]; -- do first xSection FOR n: NAT IN [0..NBranches[tube]) DO OneSpline[tube.branches[n], 0, uRangeStart, v]; -- use 1st xSection of 1st branch tube ENDLOOP; }; }; OneSpline[tube, 0, uRangeStart, 0.0]; -- start with first xSection of first tube RETURN[pointId]; }; Polys: PUBLIC PROC [tube: Tube, polyProc: PolyProc] RETURNS [nPolys: INT] ~ { polyId, pointId: INTEGER ฌ 0; OneSpline: PROC [tube: Tube] ~ { IF tube # NIL THEN { cRes: INTEGER ฌ tube.circleRes; FOR i: NAT IN [0..NXSections[tube]-1) DO n: NAT ฌ pointId; FOR ii: NAT IN[0..cRes) DO nn: NAT ฌ IF ii = cRes-1 THEN pointId ELSE n+1; r: NAT ฌ n+cRes; rr: NAT ฌ nn+cRes; [] ฌ polyProc[polyId, n, nn, r]; -- reversed r and n, per Crow, 10/7/86 polyId ฌ polyId+1; [] ฌ polyProc[polyId, r, nn, rr]; -- reversed r and rr, per Crow, 10/7/86 n ฌ n+1; polyId ฌ polyId+1; ENDLOOP; pointId ฌ pointId+cRes; ENDLOOP; IF tube.next = NIL OR tube.next.circleRes # tube.circleRes OR tube.next.r0 # tube.r1 THEN pointId ฌ pointId+cRes; -- end of branch or discontinuity from t to t.next OneSpline[tube.next]; FOR n: NAT IN [0..NBranches[tube]) DO OneSpline[tube.branches[n]]; ENDLOOP; }; }; OneSpline[tube]; RETURN[polyId]; }; ShapeFromTube: PUBLIC PROC [tube: Tube] RETURNS [shape: Shape] ~ { Point: PointProc ~ { v: G3dShape.Vertex ฌ shape.vertices[id] ฌ NEW[G3dShape.VertexRep]; vญ ฌ [point: position, normal: normal, texture: texture]; }; Poly: PolyProc ~ { p: G3dBasic.NatSequence ฌ NEW[G3dBasic.NatSequenceRep[3]]; p.length ฌ 3; p[0] ฌ p0; p[1] ฌ p1; p[2] ฌ p2; }; shape ฌ NEW[G3dShape.ShapeRep ฌ [name: tube.name]]; shape.vertices ฌ NEW[G3dShape.VertexSequenceRep[NPoints[tube]]]; shape.vertices.length ฌ shape.vertices.maxLength; [] ฌ Points[tube, Point]; shape.surfaces ฌ NEW[G3dBasic.SurfaceSequenceRep[NPolys[tube]]]; shape.surfaces.length ฌ shape.surfaces.maxLength; [] ฌ Polys[tube, Poly]; }; Find: PUBLIC PROC [searchee, search: Tube] RETURNS [found: BOOL] ~ { tubeProc: TubeProc ~ {IF tube = searchee THEN found ฌ TRUE; RETURN[NOT found]}; found ฌ FALSE; ApplyToTube[search, tubeProc]; }; Where: PUBLIC PROC [searchee, search: Tube] RETURNS [rope: ROPE] ~ { whereError: ERROR = CODE; Inner: PROC [tube: Tube] ~ { IF tube.prev = NIL THEN ERROR whereError; IF tube.prev.next = tube THEN rope ฌ Rope.Concat[".next", rope] ELSE FOR n: NAT IN [0..NBranches[tube.prev]) DO IF tube.prev.branches[n] = tube THEN { rope ฌ IO.PutFR[".branches[%g]%g", IO.int[n], IO.rope[rope]]; EXIT; }; REPEAT FINISHED => ERROR whereError; ENDLOOP; IF tube.prev # search THEN Inner[tube.prev]; }; IF NOT Find[searchee, search] THEN RETURN[".not found"]; IF searchee = search THEN RETURN[".same"]; Inner[searchee ! whereError => {rope ฌ ".error findng tube"; CONTINUE}]; }; lastTubePicked: Tube ฌ NIL; SetLastTubePicked: PUBLIC PROC [tube: Tube] ~ { lastTubePicked ฌ tube; }; LastTubePicked: PUBLIC PROC [copy: BOOL ฌ TRUE] RETURNS [Tube] ~ { RETURN[IF copy THEN CopyEntireTube[lastTubePicked] ELSE lastTubePicked]; }; First: PUBLIC PROC [tube: Tube] RETURNS [t: Tube] ~ { IF (t ฌ tube) = NIL THEN RETURN; WHILE t.prev # NIL DO t ฌ t.prev; ENDLOOP; }; Last: PUBLIC PROC [tube: Tube] RETURNS [t: Tube] ~ { IF (t ฌ tube) = NIL THEN RETURN; WHILE t.next # NIL DO t ฌ t.next; ENDLOOP; }; Divide: PUBLIC PROC [tube: Tube, t: REAL] ~ { new: Tube ฌ CopyEntireTube[tube]; new ฌ G3dTube.ExtractPartTube[new, 0.0, t]; tube ฌ G3dTube.ExtractPartTube[tube, t, 1.0, tube]; IF tube.prev # NIL THEN { IF tube.prev.next = tube THEN tube.prev.next ฌ new ELSE FOR n: NAT IN [0..tube.prev.branches.length) DO IF tube.prev.branches[n] # tube THEN LOOP; tube.prev.branches[n] ฌ new; EXIT; ENDLOOP; }; new.prev ฌ tube.prev; new.next ฌ tube; tube.prev ฌ new; }; Delete: PUBLIC PROC [tube: Tube] ~ { IF tube # NIL AND tube.prev # NIL THEN { IF tube.prev.next = tube THEN tube.prev.next ฌ NIL ELSE FOR n: NAT IN [0..NBranches[tube.prev]) DO IF tube.prev.branches[n] # tube THEN LOOP; tube.prev.branches[n] ฌ tube.prev.branches[NBranches[tube.prev]-1]; tube.prev.branches.length ฌ tube.prev.branches.length-1; EXIT; ENDLOOP; }; }; SelectAll: PUBLIC PROC [tube: Tube] ~ { tubeProc: TubeProc ~ {tube.selected ฌ TRUE}; ApplyToTube[tube, tubeProc]; }; UnSelectAll: PUBLIC PROC [tube: Tube] ~ { tubeProc: TubeProc ~ {tube.selected ฌ FALSE}; ApplyToTube[tube, tubeProc]; }; ExtractTubeName: PUBLIC PROC [fileName: ROPE] RETURNS [name: ROPE] ~ { rope: ROPE ฌ FileNames.ResolveRelativePath[fileName]; rope ฌ FileNames.GetShortName[rope]; name ฌ Rope.Substr[rope, 0, Rope.Index[rope, 0, "."]]; }; SetTubeColors: PUBLIC PROC [tube: Tube] ~ { SetColor: G3dTube.TubeProc ~ { hue: REAL ฌ 0.15*Depth[tube]; hue ฌ hue-Real.Floor[hue]; tube.color ฌ ImagerColor.ColorFromHSV[hue, 1.0, 1.0]; }; ApplyToTube[tube, SetColor]; }; Average: PUBLIC PROC [tube0, tube1: Tube] RETURNS [tube: Tube] ~ { tube ฌ NEW[TubeRep]; tube.v0 ฌ G3dVector.Midpoint[tube0.v0, tube1.v0]; tube.v1 ฌ G3dVector.Midpoint[tube0.v1, tube1.v1]; tube.p0 ฌ G3dVector.Midpoint[tube0.p0, tube1.p0]; tube.p1 ฌ G3dVector.Midpoint[tube0.p1, tube1.p1]; tube.circleRes ฌ (tube0.circleRes+tube1.circleRes)/2; tube.epsilon ฌ 0.5*(tube0.epsilon+tube1.epsilon); tube.tw0 ฌ 0.5*(tube0.tw0+tube1.tw0); tube.tw1 ฌ 0.5*(tube0.tw1+tube1.tw1); tube.tens0 ฌ 0.5*(tube0.tens0+tube1.tens0); tube.tens1 ฌ 0.5*(tube0.tens1+tube1.tens1); tube.r0 ฌ 0.5*(tube0.r0+tube1.r0); tube.r1 ฌ 0.5*(tube0.r1+tube1.r1); tube.scale ฌ 0.5*(tube0.scale+tube1.scale); tube.taper ฌ 0.5*(tube0.taper+tube1.taper); tube.refVec ฌ G3dVector.Unit[G3dVector.Midpoint[tube0.refVec, tube1.refVec]]; G3dTube.SetSpline[tube]; }; InterpTubes: PUBLIC PROC [tube1, tube2, interp: Tube, alpha: REAL] ~ { GetQuaternion: PROC [t: Tube] RETURNS [Quaternion] ~ { z: Triple ฌ G3dVector.Unit[G3dVector.Sub[t.p1, t.p0]]; x: Triple ฌ G3dVector.Unit[G3dVector.V90[z, t.refVec]]; y: Triple ฌ G3dVector.Unit[G3dVector.Cross[z, x]]; m ฌ G3dMatrix.MakeFromTriad[x, y, z,, FALSE, m]; RETURN[G3dQuaternion.FromMatrix[m]]; }; Set: PROC [t, t1, t2: Tube] ~ { q1: Quaternion ฌ GetQuaternion[t1]; q2: Quaternion ฌ GetQuaternion[t2]; q: Quaternion ฌ G3dQuaternion.Slerp[q1, q2, alpha]; len1: REAL ฌ G3dVector.Distance[t1.p0, t1.p1]; len2: REAL ฌ G3dVector.Distance[t2.p0, t2.p1]; len: REAL ฌ len1+alpha*(len2-len1); temp: Matrix ฌ m ฌ G3dQuaternion.ToMatrix[q, m]; z: Triple ฌ G3dMatrix.TransformVec[[0.0, 0.0, 1.0], m]; IF t.prev # NIL THEN t.p0 ฌ t.prev.p1; t.p1 ฌ G3dVector.Add[t.p0, G3dVector.SetVectorLength[z, len]]; }; DoTube: PROC [t, t1, t2: Tube] ~ { Set[t, t1, t2]; IF t.next # NIL THEN DoTube[t.next, t1.next, t2.next]; IF t.branches # NIL THEN FOR n: NAT IN [0..t.branches.length) DO DoTube[t.branches[n], t1.branches[n], t2.branches[n]]; ENDLOOP; }; m: Matrix; DoTube[interp, tube1, tube2]; }; NewBranch: PUBLIC PROC [tube: Tube] RETURNS [Tube] ~ { branch: Tube ~ NEW[TubeRep ฌ [p0: tube.p1, v0: tube.v1]]; IF tube.next # NIL THEN { vDif: Triple ~ G3dVector.Sub[tube.next.p1, tube.next.p0]; v: Triple ~ G3dVector.SameLength[vDif, tube.v1]; branch.p1 ฌ G3dVector.Add[branch.p0, G3dVector.Combine[v, 1.5, vDif, -0.5]]; } ELSE branch.p1 ฌ G3dVector.Add[branch.p0, G3dVector.Sub[branch.p0, tube.p0]]; branch.v1 ฌ G3dVector.SameLength[branch.v0, G3dVector.Sub[branch.p1, branch.p0]]; G3dTube.SetSpline[branch]; RETURN[branch]; }; AddBranch: PUBLIC PROC [tube, branch: Tube] ~ { IF tube.branches = NIL THEN tube.branches ฌ NEW[TubeSequenceRep[1]]; IF tube.branches.length = tube.branches.maxLength THEN { old: TubeSequence ~ tube.branches; tube.branches ฌ NEW[TubeSequenceRep[old.length+1]]; tube.branches.length ฌ old.length; FOR i: NAT IN [0..old.length) DO tube.branches[i] ฌ old[i]; ENDLOOP; }; tube.branches[tube.branches.length] ฌ branch; tube.branches.length ฌ tube.branches.length+1; branch.prev ฌ tube; }; GetBranch: PUBLIC PROC [tube: Tube, n: NAT] RETURNS [branch: Tube ฌ NIL] ~ { IF tube.next = NIL THEN { IF tube.branches # NIL AND n < tube.branches.length THEN RETURN[tube.branches[n]]; } ELSE { IF n = 0 THEN RETURN[tube.next]; IF tube.branches # NIL AND n-1 < tube.branches.length THEN RETURN[tube.branches[n-1]]; }; }; TubeHull: PUBLIC PROC [tube: Tube] RETURNS [hull: Hull] ~ { TubeMinMax: PROC [tube: Tube] RETURNS [hull: Hull] ~ { tubeProc: TubeProc ~ { b: Bezier ฌ G3dSpline.BezierFromSpline[tube.spline]; points: ARRAY [0..4) OF Triple ฌ [b.b0, b.b1, b.b2, b.b3]; hull.rMin ฌ MIN[hull.rMin, tube.r0, tube.r1]; hull.rMax ฌ MAX[hull.rMax, tube.r0, tube.r1]; FOR n: NAT IN [0..4) DO p: Triple ฌ points[n]; hull.pMin ฌ [MIN[p.x, hull.pMin.x], MIN[p.y, hull.pMin.y], MIN[p.z, hull.pMin.z]]; hull.pMax ฌ [MAX[p.x, hull.pMax.x], MAX[p.y, hull.pMax.y], MAX[p.z, hull.pMax.z]]; ENDLOOP; }; hull ฌ [origin, [10000.0, 10000.0, 10000.0], [-10000.0, -10000.0, -10000.0], 10000.0, 0.0]; ApplyToTube[tube, tubeProc]; }; hull ฌ TubeMinMax[tube]; hull.center ฌ G3dVector.Mul[G3dVector.Add[hull.pMin, hull.pMax], 0.5]; }; InvalidateTubeAttributes: PUBLIC PROC [tube: Tube] ~ { tubeProc: TubeProc ~ { tube.splineValid ฌ tube.lengthsValid ฌ tube.xSectionsValid ฌ FALSE; }; ApplyToTube[tube, tubeProc]; }; NTubes: PUBLIC PROC [tube: Tube] RETURNS [INTEGER] ~ { nTubes: INTEGER ฌ 0; tubeProc: TubeProc ~ {nTubes ฌ nTubes+1}; ApplyToTube[tube, tubeProc]; RETURN[nTubes]; }; NBranches: PUBLIC PROC [tube: Tube, nextAlso: BOOL ฌ FALSE] RETURNS [n: INTEGER ฌ0] ~ { IF tube = NIL THEN RETURN; IF tube.branches # NIL THEN n ฌ tube.branches.length; IF nextAlso AND tube.next # NIL THEN n ฌ n+1; }; NSiblings: PUBLIC PROC [tube: Tube, tubeAlso: BOOL ฌ TRUE] RETURNS [n: INTEGER] ~ { tubeProc: TubeProc ~ {n ฌ n+1}; n ฌ 0; ApplyToSiblings[tube, tubeProc, tubeAlso]; }; NXSections: PUBLIC PROC [tube: Tube] RETURNS [INTEGER] ~ { RETURN[IF tube.xSections # NIL THEN tube.xSections.length ELSE 0]; }; NPoints: PUBLIC PROC [tube: Tube] RETURNS [INT] ~ { OneSpline: PROC [tube: Tube, iStart: NAT] ~ { IF tube # NIL AND tube.xSections # NIL THEN { nPoints ฌ nPoints+(tube.xSections.length-iStart)*tube.circleRes; IF tube.next # NIL AND tube.next.circleRes = tube.circleRes AND tube.next.r0 = tube.r1 THEN OneSpline[tube.next, 1] -- skip first xSection ELSE OneSpline[tube.next, 0]; -- do first xSection FOR n: NAT IN [0..NBranches[tube]) DO OneSpline[tube.branches[n], 0]; -- use 1st xSection of 1st branch tube ENDLOOP; }; }; nPoints: INT ฌ 0; OneSpline[tube, 0]; -- start with first xSection of first tube RETURN[nPoints]; }; NPolys: PUBLIC PROC [tube: Tube] RETURNS [INT] ~ { n: INT ฌ 0; FOR t: Tube ฌ tube, t.next WHILE t # NIL DO IF t.xSections # NIL THEN n ฌ n+2*(t.xSections.length-1)*t.circleRes; ENDLOOP; RETURN[MAX[0, n]]; }; NSplinesInBranch: PUBLIC PROC [tube: Tube] RETURNS [INT] ~ { n: INT ฌ 0; FOR t: Tube ฌ tube, t.next WHILE t # NIL DO n ฌ n+1; ENDLOOP; RETURN[n]; }; Depth: PUBLIC PROC [tube: Tube] RETURNS [n: NAT ฌ 0] ~ { IF tube = NIL THEN RETURN; WHILE tube.prev # NIL DO tube ฌ tube.prev; n ฌ n+1; ENDLOOP; }; Info: PUBLIC PROC [tube: Tube] RETURNS [nPoints, nPolys, minCres, maxCres: INT] ~ { InnerResInfo: TubeProc ~ { IF tube.circleRes > maxCres THEN maxCres ฌ tube.circleRes; IF tube.circleRes < minCres THEN minCres ฌ tube.circleRes; }; minCres ฌ 10000; maxCres ฌ nPolys ฌ nPoints ฌ 0; ApplyToTube[tube, InnerResInfo]; nPoints ฌ NPoints[tube]; nPolys ฌ NPolys[tube]; }; CopyTubeSection: PUBLIC PROC [tube: Tube, copy: Tube ฌ NIL] RETURNS [Tube] ~ { IF tube = NIL THEN RETURN[NIL]; IF copy = NIL THEN copy ฌ NEW[TubeRep]; copyญ ฌ tubeญ; copy.xSections ฌ G3dTube.CopyXSections[tube.xSections]; copy.spline ฌ G3dSpline.CopySpline[tube.spline]; copy.xSpline ฌ G3dSpline.CopySpline[tube.xSpline]; IF tube.branches # NIL THEN { copy.branches ฌ NEW[TubeSequenceRep[tube.branches.length]]; copy.branches.length ฌ tube.branches.length; FOR n: NAT IN [0..copy.branches.length) DO copy.branches[n] ฌ tube.branches[n]; ENDLOOP; }; RETURN[copy]; }; CopyEntireTube: PUBLIC PROC [tube: Tube] RETURNS [copy: Tube] ~ { InnerCopy: PROC [tube, prev: Tube] RETURNS [copy: Tube] ~ { IF tube = NIL THEN RETURN[NIL]; copy ฌ NEW[TubeRep ฌ tubeญ]; copy.xSections ฌ G3dTube.CopyXSections[tube.xSections]; copy.spline ฌ G3dSpline.CopySpline[tube.spline]; copy.xSpline ฌ G3dSpline.CopySpline[tube.xSpline]; copy.prev ฌ prev; IF tube.branches # NIL THEN { copy.branches ฌ NEW[TubeSequenceRep[tube.branches.length]]; copy.branches.length ฌ tube.branches.length; FOR n: NAT IN [0..copy.branches.length) DO copy.branches[n] ฌ InnerCopy[tube.branches[n], copy]; ENDLOOP; }; copy.next ฌ InnerCopy[tube.next, copy]; }; copy ฌ InnerCopy[tube, NIL]; }; ApplyToTube: PUBLIC PROC [tube: Tube, tubeProc: TubeProc] ~ { Inner: PROC [tube: Tube] ~ { IF tube = NIL OR NOT tubeProc[tube] THEN RETURN; Inner[tube.next]; FOR n: NAT IN [0..NBranches[tube]) DO Inner[tube.branches[n]]; ENDLOOP; }; Inner[tube]; }; ApplyToBranches: PUBLIC PROC [tube: Tube, tubeProc: TubeProc, nextAlso: BOOL ฌ TRUE] ~{ IF tube = NIL THEN RETURN; IF nextAlso AND tube.next # NIL AND NOT tubeProc[tube.next] THEN RETURN; FOR n: NAT IN [0..NBranches[tube]) DO IF tube.branches[n] # NIL AND NOT tubeProc[tube.branches[n]] THEN RETURN; ENDLOOP; }; ApplyToSiblings: PUBLIC PROC [tube: Tube, tubeProc: TubeProc, tubeAlso: BOOL ฌ TRUE] ~ { IF tube # NIL THEN { prev: Tube ~ tube.prev; IF prev = NIL THEN { IF tubeAlso THEN [] ฌ tubeProc[tube]; RETURN; }; IF (tubeAlso OR prev.next # tube) AND prev.next # NIL AND NOT tubeProc[prev.next] THEN RETURN; FOR n: NAT IN [0..NBranches[prev]) DO IF (tubeAlso OR prev.branches[n] # tube) AND prev.branches[n] # NIL AND NOT tubeProc[prev.branches[n]] THEN RETURN; ENDLOOP; }; }; SetColor: PROC [context: Context, tube: Tube] ~ INLINE { Imager.SetColor[context, IF tube.color = NIL THEN Imager.black ELSE tube.color]; }; DrawTube: PUBLIC PROC [ context: Context, tube: Tube, details: Details, view: Matrix, viewport: Viewport ฌ [], drawType: DrawType ฌ solid] ~ { IF details = NIL THEN RETURN; IF details[skeleton] THEN DrawSkeleton[context, tube, view, viewport, drawType]; IF details[spline] THEN DrawSplines[context, tube, view, viewport, drawType]; IF details[lines] THEN DrawLines[context, tube, view, viewport, drawType]; IF details[contours] THEN DrawContours[context, tube, view, viewport, drawType]; IF details[xSections] THEN DrawXSections[context, tube, view, viewport, details[label], drawType]; IF details[curvature] THEN DrawCurvature[context, tube, view, viewport, details[label], drawType]; IF details[velocity] THEN DrawVelocity[context, tube, view, viewport, details[label], drawType]; IF details[acceleration] THEN DrawAcceleration[context, tube, view, viewport, details[label], drawType]; IF details[normals] THEN DrawNormals[context, tube, view, viewport, details[label], drawType]; IF details[points] THEN DrawPoints[context, tube, view, viewport, dot]; }; DrawSkeleton: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], drawType: DrawType ฌ solid] ~ { ApplyDraw: TubeProc ~ { SetColor[context, tube]; G3dDraw.Segment[context, tube.p0, tube.p1, view, viewport, IF tube.selected THEN drawType ELSE dotted]; }; G3dTube.ApplyToTube[tube, ApplyDraw]; }; DrawSplines: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], drawType: DrawType ฌ solid] ~ { ApplyDraw: TubeProc ~ { IF tube.selected THEN G3dDraw.Curve[context, tube.spline, view, viewport] ELSE G3dDraw.DotCurve[context, tube.spline, view, viewport]; }; G3dTube.ApplyToTube[tube, ApplyDraw]; }; DrawSplineEnds: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], mark: MarkType ฌ dot] ~ { ApplyDraw: TubeProc ~ { SetColor[context, tube]; G3dDraw.Mark[context, tube.p0, view, viewport,, mark]; G3dDraw.Mark[context, tube.p1, view, viewport,, mark]; }; G3dTube.ApplyToTube[tube, ApplyDraw]; }; AxialDrawRes: PROC [tube: Tube] RETURNS [INTEGER] ~ { nXSections: NAT ฌ G3dTube.NXSections[tube]; RETURN[IF tube.next = NIL THEN nXSections ELSE nXSections-1]; }; DrawPoints: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], markType: MarkType ฌ dot] ~ { ApplyDraw: TubeProc ~ { SetColor[context, tube]; FOR i: NAT IN [0..AxialDrawRes[tube]) DO f: XSection ฌ tube.xSections[i]; IF f.contour # NIL AND f.contour.pairs # NIL THEN { pairs: PairSequence ฌ f.contour.pairs; FOR j: NAT IN [0..pairs.length) DO G3dDraw.Mark[ context, G3dMatrix.TransformPair[pairs[j], f.matrix], view, viewport,, markType]; ENDLOOP; }; ENDLOOP; }; G3dTube.ApplyToTube[tube, ApplyDraw]; }; DrawContours: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], drawType: DrawType ฌ solid] ~ { ApplyDraw: TubeProc ~ { VP: PROC [p: Pair] RETURNS [x: Pair] ~ { x ฌ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y]; }; SetColor[context, tube]; FOR i: NAT IN [0..AxialDrawRes[tube]) DO f: XSection ฌ tube.xSections[i]; IF f.contour # NIL AND f.contour.pairs # NIL THEN { pairs: PairSequence ฌ f.contour.pairs; p0, psave: Pair ฌ VP[G3dMatrix.TransformD[G3dMatrix.TransformPair[pairs[0], f.matrix], view]]; FOR j: NAT IN [1..pairs.length) DO p1: Pair ฌ VP[G3dMatrix.TransformD[G3dMatrix.TransformPair[pairs[j], f.matrix], view]]; Draw2d.Line[context, p0, p1, drawType]; p0 ฌ p1; ENDLOOP; Draw2d.Line[context, p0, psave]; }; ENDLOOP; }; G3dTube.ApplyToTube[tube, ApplyDraw]; }; DrawLines: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], drawType: DrawType ฌ solid] ~ { ApplyDraw: TubeProc ~ { VP: PROC [p: Pair] RETURNS [x: Pair] ~ { x ฌ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y]; }; Xform: PROC [p: Pair, m: Matrix] RETURNS [Pair] ~ { RETURN[VP[G3dMatrix.TransformD[G3dMatrix.TransformPair[p, m], view]]]; }; allCircles: BOOL ฌ TRUE; xSections: XSectionSequence ฌ tube.xSections; nXSections: NAT ฌ G3dTube.NXSections[tube]; SetColor[context, tube]; FOR i: NAT IN [0..nXSections) DO IF xSections[i].contour # NIL AND NOT xSections[i].contour.circle THEN {allCircles ฌ FALSE; EXIT}; ENDLOOP; IF allCircles THEN { circle: PairSequence ฌ G2dContour.CirclePairs[tube.circleRes]; FOR i: NAT IN [0..tube.circleRes) DO p0: Pair ฌ Xform[circle[i], xSections[0].matrix]; FOR j: NAT IN [1..nXSections) DO p1: Pair ฌ Xform[circle[i], xSections[j].matrix]; Draw2d.Line[context, p0, p1, drawType]; p0 ฌ p1; ENDLOOP; ENDLOOP; } ELSE { pc: REAL ฌ 0.0; dpc: REAL ฌ 1.0/MAX[1, tube.circleRes]; FOR i: NAT IN [0..tube.circleRes) DO p0: Pair ฌ Xform[G2dContour.PercentPair[xSections[0].contour, pc], xSections[0].matrix]; FOR j: NAT IN [1..nXSections) DO p1: Pair ฌ Xform[G2dContour.PercentPair[xSections[j].contour, pc], xSections[j].matrix]; Draw2d.Line[context, p0, p1, drawType]; p0 ฌ p1; ENDLOOP; pc ฌ pc+dpc; ENDLOOP; }; }; G3dTube.ApplyToTube[tube, ApplyDraw]; }; DrawXSections: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], label: BOOL, drawType: DrawType ฌ solid] ~ { ApplyDraw: TubeProc ~ { VP: PROC [p: Pair] RETURNS [x: Pair] ~ { x ฌ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y]; }; DrawMatrix: PROC [m: Matrix] ~ { o: Pair ฌ VP[G3dMatrix.TransformD[[m[3][0], m[3][1], m[3][2]], view]]; FOR i: NAT IN [0..2] DO p: Pair ฌ VP[G3dMatrix.TransformD[ [m[3][0]+scale*m[i][0], m[3][1]+scale*m[i][1], m[3][2]+scale*m[i][2]], view]]; Draw2d.Line[context, o, p, drawType]; IF label THEN Draw2d.Label[context, p, names[i]]; ENDLOOP; }; SetColor[context, tube]; FOR i: NAT IN [0..AxialDrawRes[tube]) DO IF tube.xSections[i].matrix # NIL THEN DrawMatrix[tube.xSections[i].matrix]; ENDLOOP; }; scale: REAL ~ 0.8; names: ARRAY[0..2] OF Rope.ROPE ฌ ["x", "y", "z"]; G3dTube.ApplyToTube[tube, ApplyDraw]; }; DrawNormals: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], label: BOOL, drawType: DrawType ฌ solid] ~ { tapered: BOOL; vFactor: REAL; ApplyDraw: TubeProc ~ { DrawN: PROC [f: XSection] ~ { FOR j: NAT IN [0..f.contour.pairs.length) DO p: Triple ฌ G3dMatrix.TransformPair[f.contour.pairs[j], f.matrix]; n: Triple ฌ G3dMatrix.TransformVec[[f.normals[j].x, f.normals[j].y, 0], f.matrix]; IF tapered THEN { v: Triple ฌ G3dSpline.Velocity[tube.spline, f.t]; len: REAL ฌ G3dVector.Length[n]; n ฌ G3dVector.Add[n, G3dVector.Mul[v, len*vFactor/G3dVector.Length[v]]]; }; n ฌ G3dVector.Unit[n]; G3dDraw.Vector[context, p, n, view, viewport, IF label THEN "n" ELSE NIL, 0.1]; ENDLOOP; }; tapered ฌ tube.r1 # tube.r0; IF tapered THEN vFactor ฌ (tube.r0-tube.r1)/G3dVector.Distance[tube.p0, tube.p1]; SetColor[context, tube]; FOR i: NAT IN [0..AxialDrawRes[tube]) DO IF tube.xSections[i].matrix # NIL THEN DrawN[tube.xSections[i]]; ENDLOOP; }; G3dTube.ApplyToTube[tube, ApplyDraw]; }; DrawCurvature: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], label: BOOL, drawType: DrawType ฌ solid] ~ { ApplyDraw: TubeProc ~ { DrawK: PROC [f: XSection] ~ { p: Triple ฌ [f.matrix[3][0], f.matrix[3][1], f.matrix[3][2]]; k: Triple ฌ G3dSpline.Curvature[tube.spline, f.t]; G3dDraw.Vector[context, p, k, view, viewport, IF label THEN "k" ELSE NIL]; }; SetColor[context, tube]; FOR i: NAT IN [0..AxialDrawRes[tube]) DO IF tube.xSections[i].matrix # NIL THEN DrawK[tube.xSections[i]]; ENDLOOP; }; G3dTube.ApplyToTube[tube, ApplyDraw]; }; DrawVelocity: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], label: BOOL, drawType: DrawType ฌ solid] ~ { ApplyDraw: TubeProc ~ { DrawV: PROC [f: XSection] ~ { p: Triple ฌ [f.matrix[3][0], f.matrix[3][1], f.matrix[3][2]]; v: Triple ฌ G3dSpline.Velocity[tube.spline, f.t]; G3dDraw.Vector[context, p, v, view, viewport, IF label THEN "v" ELSE NIL]; }; SetColor[context, tube]; FOR i: NAT IN [0..AxialDrawRes[tube]) DO IF tube.xSections[i].matrix # NIL THEN DrawV[tube.xSections[i]]; ENDLOOP; }; G3dTube.ApplyToTube[tube, ApplyDraw]; }; DrawAcceleration: PUBLIC PROC [ context: Context, tube: Tube, view: Matrix, viewport: Viewport ฌ [], label: BOOL, drawType: DrawType ฌ solid] ~ { ApplyDraw: TubeProc ~ { DrawA: PROC [f: XSection] ~ { p: Triple ฌ [f.matrix[3][0], f.matrix[3][1], f.matrix[3][2]]; a: Triple ฌ G3dSpline.Acceleration[tube.spline, f.t]; G3dDraw.Vector[context, p, a, view, viewport, IF label THEN "a" ELSE NIL]; }; SetColor[context, tube]; FOR i: NAT IN [0..AxialDrawRes[tube]) DO IF tube.xSections[i].matrix # NIL THEN DrawA[tube.xSections[i]]; ENDLOOP; }; G3dTube.ApplyToTube[tube, ApplyDraw]; }; NVectors: PUBLIC PROC [tube: Tube, details: Details] RETURNS [INTEGER] ~ { sum: INTEGER ฌ 0; IF details[enabled] THEN FOR t: Tube ฌ tube, t.next WHILE t # NIL DO nXSections: INTEGER ฌ G3dTube.NXSections[t]; sum ฌ sum +(IF details[lines] THEN t.circleRes*nXSections ELSE 0) +(IF details[xSections] THEN 3*nXSections ELSE 0); FOR n: NAT IN [0..nXSections) DO f: XSection ฌ t.xSections[n]; sum ฌ sum+(IF f.contour # NIL THEN f.contour.pairs.length ELSE t.circleRes); ENDLOOP; ENDLOOP; RETURN[sum]; }; END. .. WriteMaxPointsPolys: PUBLIC PROC [tube: Tube, fileName: ROPE, m: Matrix ฌ NIL] RETURNS [nPoints, nPolys: INT] ~ { MaxPoint: PointProc ~ { IO.PutF[out, "%g,", IO.int[id]]; IO.PutF[out, "%6.5f,%6.5f,%6.5f,", IO.real[position.x], IO.real[position.y], IO.real[position.z]]; IO.PutF[out, "%6.5f,%6.5f,%6.5f,", IO.real[normal.x], IO.real[normal.y], IO.real[normal.z]]; IO.PutF[out, "%6.5f,%6.5f,\n", IO.real[texture.x], IO.real[texture.y]]; }; MaxPoly: PolyProc ~ { IO.PutF[out, "%g,%g,%g,%g,\n", IO.int[id], IO.int[p0], IO.int[p1], IO.int[p2]]; }; out: STREAM ฌ FS.StreamOpen[fileName, $create]; nPoints ฌ Points[tube, MaxPoint, m]; IO.PutF[out, "-1\n"]; nPolys ฌ Polys[tube, MaxPoly]; IO.PutF[out, "-1\n"]; IO.Close[out]; };  G3dTubeMiscImpl.mesa Copyright ำ 1985, 1992 by Xerox Corporation. All rights reserved. Bloomenthal, August 26, 1992 5:31 pm PDT Types IO Points and Polygons connect ith xSection to (i+1)th xSection: Miscellany Interpolation Branching Attributes Information Copying Call Backs Drawing SetColor[context, tube]; tube.selected _ TRUE; -- for filming day Imager.SetStrokeWidth[context, 2.0]; -- for film day Imager.SetStrokeWidth[context, 1.0]; -- for film day See TubeIO.Points Writing Tube in Nelson Max Format ส$[•NewlineDelimiter –"cedarcode" style™™Jšœ ฯeœ6™BJ™(J˜Jšฯk œร˜ฬJ˜—šะblœžœž˜Jšžœฎ˜ตJšžœ˜—J˜Jšœž˜headšฯl™Jšœ žœ˜#Jšœ žœ˜#Jšœž œ˜Jšœž œ˜Jšœ žœ˜,Jšœžœ˜"Jšœžœ˜#Jšœ žœ˜'Jšœ žœ˜.Jšœž œ˜!Jšœžœ˜#Jšœžœ˜#Jšœ žœ˜(Jšœ žœ˜%Jšœžœ˜2Jšœ žœ˜&Jšœ žœ˜%Jšœž œ˜Jšœ žœ˜%Jšœžœ˜#Jšœ žœ˜+Jšœžœ˜0Jšœ žœ˜"Jšžœžœžœžœ˜šžœž œžœ˜J˜—Jšžœžœ žœ˜J˜$—š ™šœžœ˜J˜—š ะbnœžœžœ žœžœ ฯbœ˜?šžœ ž˜Jšžœžœžœ˜šžœ˜šฯn œžœžœ˜9š ฃ œžœžœžœžœ˜.šž˜Jšœ$žœžœ˜@Jšžœ*žœžœžœ˜?šž˜Jšœžœžœ˜—Jšžœ˜—J˜—šžœžœ˜Jšœžœ˜ Jšœžœ ˜J˜J˜ J˜ J˜ J˜ J˜J˜J˜J˜J˜!J˜!J˜#J˜(Jšœ/žœ˜6Jšœžœ*˜=J˜/J˜Jšžœžœ!˜-š žœžœžœžœžœžœž˜FJ˜'Jšžœ˜—J˜—J˜—J˜ Jšœžœ˜J˜J˜——J˜J˜—š ฃ œžœžœ žœžœ˜Hšฃœ˜Jšžœ˜Jšœฯsžœžคžคœคžคœคœ˜XJšœคžœžคžคœคžคœคœ˜Xšœ˜Jšžœžœžœ žœ%˜A—šœ˜Jšžœžœžœ žœ%˜A—Jšžœžœžœ˜EJšžœžœžœ˜GJšžœžœžœ˜KJšžœžœžœ˜LJš žœžœ žœžœ žœ˜?Jšžœžœ˜0Jšžœ˜J˜—J˜J˜J˜—š ฃœžœžœžœžœ˜KJšžœžœ˜"šกœ˜Jšžœžœ ˜"J˜!J˜Jšžœžœžœ˜FJ˜—šกœ˜Jš žœ žœžœ žœ žœ žœ ˜XJ˜—Jšœžœžœ˜/J˜J˜Jšžœžœ˜/Jšžœ7žœžœ˜Zšžœ ˜J˜X—J˜Jšžœ?˜AJ˜Jšžœ ˜J˜J˜—šฃœžœžœI˜]Jšœ:คœ ˜bšฃ œžœ˜(Jšœžœ˜"J˜'J˜$J˜$J˜J˜1J˜,J˜—J˜*J˜J˜—š ก œžœžœ žœžœ˜CJšœžœ+˜5šžœžœžœ˜Jš œžœžœžœ žœ ˜?Jšžœžœžœžœ˜/Jšžœ˜J˜$J˜;Jšžœ žœ˜J˜—Jšžœ˜ J˜J˜—š ก œžœžœžœžœ˜Nšžœ žœžœ˜Jšœžœžœ>˜LJšžœ.žœ˜Cšžœ ˜ Jšœคœคœคœคœคœคœคœคœคœคœคœคœคœ˜R—Jšœ˜Jšžœ ˜ J˜—J˜——š ™šฃœžœžœ3žœ˜JJšžœ žœ˜J˜Jšœ žœ˜Jšœ žœžœ ˜#šฃ œžœžœžœ˜?š žœžœžœžœžœ˜.Jšœ žœ(ฯc˜DJšœ žœ1˜>J˜>Jšœ žœ˜"šœ žœžœ˜Jšžœ7˜;Jšžœ˜ —J˜Jšœžœžœ˜%šžœžœžœž˜*Jšœžœ˜!Jšœžœ˜Jšœžœ˜%J˜%Jš œ žœžœžœžœ˜>J˜ J˜#Jšžœžœ'˜4šžœžœžœž˜#J˜3J˜Hšžœ žœ˜J˜BJšœžœ˜ J˜JJ˜—J˜J˜&J˜Jš œžœžœžœžœฅ˜MJšžœ˜—Jšžœ˜—šžœ žœžœ&žœ˜VJšžœ&ฅ˜@Jšžœ'ฅ˜?—šžœžœžœž˜%Jšœ/ฅ'˜VJšžœ˜—J˜—J˜—Jšœ*ฅ*˜TJšžœ ˜J˜J˜—š ฃœžœžœ!žœ žœ˜MJšœžœ˜šฃ œžœ˜ šžœžœžœ˜Jšœžœ˜J˜J™)šžœžœžœž˜(Jšœžœ ˜šžœžœžœ ž˜Jš œžœžœ žœ žœ˜/Jšœžœ ˜Jšœžœ ˜Jšœ"ฅ&˜HJ˜Jšœ#ฅ'˜JJ˜J˜Jšžœ˜—J˜Jšžœ˜—šžœ žœžœ&žœ˜TJšžœฅ2˜P—J˜šžœžœžœž˜%J˜Jšžœ˜—J˜—J˜—J˜Jšžœ ˜J˜J˜—šฃ œžœžœžœ˜Bšฃœ˜Jšœ*žœ˜BJ˜9J˜—šฃœ˜Jšœžœ˜:J˜ J˜ J˜ J˜ J˜—Jšœžœ(˜3Jšœžœ,˜@J˜1J˜Jšœžœ,˜@J˜1J˜J˜——š  ™ š ฃœžœžœžœ žœ˜DJš ฃœžœžœ žœžœžœ ˜OJšœžœ˜J˜J˜J˜—š ฃœžœžœžœžœ˜DJšœ žœžœ˜šขœžœ˜Jšžœ žœžœžœ ˜)šžœ˜Jšžœ"˜&š žœžœžœžœž˜/šžœžœ˜&Jšœžœžœ žœ ˜=Jšžœ˜J˜—šž˜Jšžœžœ ˜—Jšžœ˜——Jšžœžœ˜,J˜—Jšžœžœžœžœ˜8Jšžœžœžœ ˜*Jšœ=žœ˜HJ˜J˜—šœžœ˜J˜—šฃœžœžœ˜/J˜J˜J˜—š ฃœžœžœžœžœžœ ˜BJšžœžœžœ žœ˜HJ˜J˜—šฃœžœžœžœ˜5Jšžœžœžœžœ˜ Jšžœ žœžœ žœ˜*J˜J˜—šฃœžœžœžœ˜4Jšžœžœžœžœ˜ Jšžœ žœžœ žœ˜*J˜J˜—šฃœž œžœ˜-J˜!J˜+J˜3šžœ žœžœ˜šžœ˜Jšžœ˜š žœžœžœžœ ž˜4Jšžœžœžœ˜*J˜Jšžœ˜Jšžœ˜—J˜——J˜J˜J˜J˜J˜—šฃœžœžœ˜$š žœžœžœ žœžœ˜(šžœ˜Jšžœž˜š žœžœžœžœž˜/Jšžœžœžœ˜*J˜CJ˜8Jšžœ˜Jšžœ˜——J˜—J˜J˜—šฃ œžœžœ˜'Jšขœžœ˜,J˜J˜J˜—šฃ œžœžœ˜)Jšขœžœ˜-J˜J˜J˜—š ฃœž œ žœžœžœ˜FJšœžœ+˜5J˜$J˜6J˜J˜—šฃ œžœžœ˜+šฃœ˜Jšœžœ˜J˜J˜5J˜—J˜J˜——š  ™ šฃœžœžœžœ˜BJšœžœ ˜J˜1J˜1J˜1J˜1J˜5J˜1J˜%J˜%J˜+J˜+J˜"J˜"J˜+J˜+J˜MJ˜J˜J˜—šฃ œž œ%žœ˜Fšฃ œžœ žœ˜6J˜6Jšœ%žœ˜7J˜2Jšœ&žœ˜0Jšžœ˜$J˜—šฃœžœ˜J˜#J˜#J˜3Jšœžœ$˜.Jšœžœ$˜.Jšœžœ˜#J˜0J˜7Jšžœ žœžœ˜&J˜>J˜—šฃœžœ˜"J˜Jšžœ žœžœขœ˜6šžœžœž˜šžœžœžœž˜'Jšขœ0˜6Jšžœ˜——J˜—J˜ J˜J˜——š  ™ šฃ œžœžœžœ ˜6Jšœžœ'˜9šžœ ž˜šžœ˜J˜9J˜0J˜LJ˜—JšžœI˜M—J˜QJ˜Jšžœ ˜J˜J˜—šฃ œžœžœ˜/Jšžœžœžœžœ˜Dšžœ0žœ˜8J˜"Jšœžœ ˜3J˜"Jš žœžœžœžœžœ˜DJ˜—J˜-J˜.J˜J˜J˜—š ฃ œžœžœžœžœžœ˜Lšžœ ž˜šžœ˜šžœžœžœ˜3Jšžœžœ˜—J˜—šžœ˜Jšžœžœžœ ˜ šžœžœžœ˜5Jšžœžœ˜ —J˜——J˜——š  ™ šฃœžœžœžœ˜;šฃ œžœžœ˜6šฃœ˜J˜4Jšœžœžœ#˜:Jšœ žœ˜-Jšœ žœ˜-šžœžœžœž˜J˜Jšœ žœžœžœ˜RJšœ žœžœžœ˜RJšžœ˜—J˜—J˜[J˜J˜—J˜J˜FJ˜J˜—šฃœžœžœ˜6šขœ˜Jšœ=žœ˜CJ˜—J˜J˜——š  ™ šฃœž œžœžœ˜6Jšœžœ˜J˜)J˜Jšžœ ˜J˜J˜—šฃ œžœžœžœžœžœžœ˜WJšžœžœžœžœ˜Jšžœžœžœ˜5Jšžœ žœ žœžœ ˜-J˜J˜—šฃ œžœžœžœžœžœžœ˜SJšฃœ˜J˜J˜*J˜J˜—šฃ œž œžœžœ˜:Jš žœžœžœžœžœ˜BJ˜J˜—š ฃœžœžœ žœžœ˜3šฃ œžœžœ˜-š žœžœžœžœžœ˜.J˜@J˜šžœ žœžœ&žœ˜VJšžœฅ˜6Jšžœฅ˜5—J˜šžœžœžœž˜%Jšœฅ*˜IJšžœ˜—J˜—J˜—Jšœ žœ˜Jšœฅ*˜FJšžœ ˜J˜J˜—šฃœž œžœžœ˜2Jšœžœ˜ šžœžœžœž˜+Jšžœžœžœ,˜EJšžœ˜ —Jšžœžœ˜J˜J˜—šฃœž œžœžœ˜šžœžœžœž˜$J˜1šžœžœžœž˜ J˜1J˜'J˜Jšžœ˜—Jšžœ˜—J˜—šžœ˜Jšœžœ˜Jšœžœžœ˜'šžœžœžœž˜$˜ J˜M—šžœžœžœž˜ J˜XJ˜'J˜Jšžœ˜—J˜ Jšžœ˜——J˜—J˜—J˜%J˜J˜—šฃ œžœžœ˜J˜J˜ J˜ J˜Jšœžœ˜ J˜J˜šฃ œ˜šขœžœ žœ˜(J˜[J˜—šฃ œžœ˜ J˜Fšžœžœžœž˜˜"J˜N—J˜%Jšžœžœ$˜1Jšžœ˜—J˜—J˜šžœžœžœž˜(Jšžœžœ&˜LJšžœ˜—J˜—Jšœžœ˜Jšœžœžœžœ˜2J˜%J˜J˜—šฃ œžœžœ˜J˜J˜ J˜ J˜Jšœžœ˜ J˜J˜J™Jšœ žœ˜Jšœ ž˜šฃ œ˜šฃœžœ˜šžœžœžœž˜,J˜BJ˜Ršžœ žœ˜J˜1Jšœžœ˜ J˜HJ˜—J˜Jš œ.žœžœžœžœ˜OJšžœ˜—J˜—J˜Jšžœ žœB˜QJ˜šžœžœžœž˜(Jšžœžœžœ˜@Jšžœ˜—J˜—J˜%J˜J˜—šฃ œžœžœ˜J˜J˜ J˜ J˜Jšœžœ˜ J˜J˜šฃ œ˜šฃœžœ˜J˜=J˜2Jš œ.žœžœžœžœ˜JJ˜—J˜šžœžœžœž˜(Jšžœžœžœ˜@Jšžœ˜—J˜—J˜%J˜J˜—šฃ œžœžœ˜J˜J˜ J˜ J˜Jšœžœ˜ J˜J˜šฃ œ˜šฃœžœ˜J˜=J˜1Jš œ.žœžœžœžœ˜JJ˜—J˜šžœžœžœž˜(Jšžœžœžœ˜@Jšžœ˜—J˜—J˜%J˜J˜—šฃœžœžœ˜J˜J˜ J˜ J˜Jšœžœ˜ J˜J˜šฃ œ˜šฃœžœ˜J˜=J˜5Jš œ.žœžœžœžœ˜JJ˜—J˜šžœžœžœž˜(Jšžœžœžœ˜@Jšžœ˜—J˜—J˜%J˜J˜—š ฃœžœžœ žœžœ˜JJšœžœ˜š žœžœžœžœžœž˜DJšœ žœ˜,˜ Jšœžœžœžœ˜7Jšœžœžœžœ˜2—šžœžœžœž˜ J˜Jšœ žœ žœžœ˜LJšžœ˜—Jšžœ˜—Jšžœ˜ J˜—J˜—Jšžœ˜˜š !™!š ฃœžœžœžœžœ˜NJšžœžœ˜"šกœ˜Jšžœžœ ˜ Jšžœ!žœžœžœ˜bJšžœ!žœžœžœ˜\Jšžœžœžœ˜GJ˜—šกœ˜Jš žœžœ žœ žœ žœ ˜OJ˜—Jšœžœžœ˜/J˜$Jšžœ˜J˜Jšžœ˜Jšžœ ˜J˜—J˜—J™——…—zF ซ