DIRECTORY CoordSys, Feedback, Matrix3d, Rope, SV3d, SVAssembly, SVCoordSysType, SVMatrixOps, SVModelTypes, SVSceneTypes, SVTransforms, SVVector3d; SVTransformsImpl: CEDAR PROGRAM IMPORTS CoordSys, Feedback, Matrix3d, Rope, SVAssembly, SVMatrixOps, SVVector3d EXPORTS SVTransforms, SVModelTypes = BEGIN Slice: TYPE = SVSceneTypes.Slice; SliceList: TYPE = SVSceneTypes.SliceList; Camera: TYPE = SVModelTypes.Camera; CoordSystem: TYPE = REF CoordSysObj; CoordSysObj: PUBLIC TYPE = SVCoordSysType.CoordSysObj; CoordSysList: TYPE = SVModelTypes.CoordSysList; Matrix4by4: TYPE = SV3d.Matrix4by4; Point3d: TYPE = SV3d.Point3d; Scene: TYPE = SVSceneTypes.Scene; Shape: TYPE = SVSceneTypes.Shape; Vector3d: TYPE = SV3d.Vector3d; IncTransfMatrix: PUBLIC PROC [c: CoordSystem, dWorld: Matrix4by4, m: Matrix4by4] = { p: CoordSystem; cWorld, pWorld: Matrix4by4; IF MatrixHasScaling[m] THEN { SIGNAL Feedback.Problem[msg: "SVTransform.IncTransfMatrix got illegal matrix. Ignored."]; RETURN}; p _ CoordSys.Parent[c]; IF p = NIL THEN ERROR; -- cannot translate WORLD for now cWorld _ CoordSys.WRTWorld[c]; pWorld _ CoordSys.WRTWorld[p]; CoordSys.SetMat[c, SVMatrixOps.IncTransf[cWorld, pWorld, dWorld, m]]; }; PlaneOriginRotate: PUBLIC PROC [c: CoordSystem, d: CoordSystem, degrees: REAL, plane: NAT] = { m, PivotWORLD, CWORLD, DWORLD: Matrix4by4; origin: Point3d; xAxis, yAxis, zAxis: Vector3d; CWORLD _ CoordSys.WRTWorld[c]; DWORLD _ CoordSys.WRTWorld[d]; origin _ Matrix3d.OriginOfMatrix[CWORLD]; xAxis _ Matrix3d.XAxisOfMatrix[DWORLD]; yAxis _ Matrix3d.YAxisOfMatrix[DWORLD]; zAxis _ Matrix3d.ZAxisOfMatrix[DWORLD]; PivotWORLD _ Matrix3d.MakeMatFromAxes[xAxis, yAxis, zAxis, origin]; SELECT plane FROM 1 => m _ Matrix3d.MakeRotateXMat[degrees]; 2 => m _ Matrix3d.MakeRotateYMat[degrees]; 3 => m _ Matrix3d.MakeRotateZMat[degrees]; ENDCASE => ERROR; IncTransfMatrix[c, PivotWORLD, m]; }; AbsTransfScreen: PUBLIC PROC [c: CoordSystem, n: Matrix4by4] = { CoordSys.SetMat[c, n]; }; AbsTransfMatrix: PUBLIC PROC [c: CoordSystem, dWorld: Matrix4by4, n: Matrix4by4] = { p: CoordSystem; pWORLD: Matrix4by4; IF Rope.Equal[c.name, "SCREEN", TRUE] THEN {CoordSys.SetMat[c, n]; RETURN}; p _ CoordSys.Parent[c]; IF p = NIL THEN ERROR; -- cannot translate WORLD for now IF MatrixHasScaling[n] THEN { SIGNAL Feedback.Problem[msg: "SVTransform.AbsTransf got illegal matrix. Ignored."]; RETURN}; pWORLD _ CoordSys.WRTWorld[p]; CoordSys.SetMat[c, SVMatrixOps.AbsTransf[pWORLD, dWorld, n]]; }; Translate: PUBLIC PROC [c: Slice, scene: Scene, vec: Point3d, d: CoordSystem _ NIL, dFixed: BOOL _ FALSE] = { m: Matrix4by4; m _ Matrix3d.MakeTranslateMat[vec[1], vec[2], vec[3]]; SVAssembly.IncTransf[c, scene, m, d, dFixed]; DirtyAssembly[c]; }; Rotate: PUBLIC PROC [c: Slice, scene: Scene, axis: [1..3], degrees: REAL, d: CoordSystem _ NIL, dFixed: BOOL _ FALSE] = { m: Matrix4by4; SELECT axis FROM 1 => m _ Matrix3d.MakeRotateXMat[degrees]; 2 => m _ Matrix3d.MakeRotateYMat[degrees]; 3 => m _ Matrix3d.MakeRotateZMat[degrees]; ENDCASE => ERROR; SVAssembly.IncTransf[c, scene, m, d, dFixed]; DirtyAssembly[c]; }; AbsTranslateOnly: PUBLIC PROC [c: Slice, scene: Scene, d: CoordSystem, vD: Point3d] = { CD: Matrix4by4; CD _ CoordSys.FindAInTermsOfB[c.coordSys, d]; CD[1][4] _ vD[1]; CD[2][4] _ vD[2]; CD[3][4] _ vD[3]; SVAssembly.AbsTransf[c, scene, CD, d]; DirtyAssembly[c]; }; Align: PUBLIC PROC [c: Slice, scene: Scene, d: CoordSystem] = { CD: Matrix4by4; xAxisChoices, zAxisChoices: ARRAY[1..3] OF NAT; xAxis, zAxis, newXAxis, newYAxis, newZAxis: Vector3d; xNegArray, zNegArray: ARRAY[1..3] OF BOOL; origin: Point3d; CD _ CoordSys.FindAInTermsOfB[c.coordSys, d]; xAxis _ Matrix3d.XAxisOfMatrix[CD]; zAxis _ Matrix3d.ZAxisOfMatrix[CD]; origin _ Matrix3d.OriginOfMatrix[CD]; [xAxisChoices, xNegArray] _ IndicesOfMax[xAxis]; -- Find largest component [zAxisChoices, zNegArray] _ IndicesOfMax[zAxis]; IF zAxisChoices[1] = xAxisChoices[1] THEN zAxisChoices[1] _ zAxisChoices[2]; newXAxis _ [0,0,0]; newXAxis[xAxisChoices[1]] _ SVVector3d.Magnitude[xAxis]; IF xNegArray[xAxisChoices[1]] THEN newXAxis[xAxisChoices[1]] _ -newXAxis[xAxisChoices[1]]; newZAxis _ [0,0,0]; newZAxis[zAxisChoices[1]] _ SVVector3d.Magnitude[zAxis]; IF zNegArray[zAxisChoices[1]] THEN newZAxis[zAxisChoices[1]] _ -newZAxis[zAxisChoices[1]]; newYAxis _ SVVector3d.CrossProduct[newZAxis, newXAxis]; CD _ Matrix3d.MakeMatFromAxes[newXAxis, newYAxis, newZAxis, origin]; SVAssembly.AbsTransf[c, scene, CD, d]; DirtyAssembly[c]; }; -- end of Align IndicesOfMax: PRIVATE PROC [v: Vector3d] RETURNS [indices: ARRAY[1..3] OF NAT, negArray: ARRAY[1..3] OF BOOL] = { temp: REAL; tempIndex: NAT; IF v[1] < 0 THEN {negArray[1] _ TRUE; v[1] _ -v[1]} ELSE negArray[1] _ FALSE; IF v[2] < 0 THEN {negArray[2] _ TRUE; v[2] _ -v[2]} ELSE negArray[2] _ FALSE; IF v[3] < 0 THEN {negArray[3] _ TRUE; v[3] _ -v[3]} ELSE negArray[3] _ FALSE; indices _ [1,2,3]; IF v[1] < v[2] THEN { temp _ v[1]; v[1] _ v[2]; v[2] _ temp; tempIndex _ indices[1]; indices[1] _ indices[2]; indices[2] _ tempIndex}; IF v[2] < v[3] THEN { temp _ v[2]; v[2] _ v[3]; v[3] _ temp; tempIndex _ indices[2]; indices[2] _ indices[3]; indices[3] _ tempIndex}; IF v[1] < v[2] THEN { temp _ v[1]; v[1] _ v[2]; v[2] _ temp; tempIndex _ indices[1]; indices[1] _ indices[2]; indices[2] _ tempIndex}; }; Abut: PUBLIC PROC [c: Slice, scene: Scene, d: CoordSystem] = { AbsTranslateOnly[c, scene, d, [0,0,0]]; }; AbutAxis: PUBLIC PROC [c: Slice, scene: Scene, axis: [1..3], d: CoordSystem] = { vD: Vector3d; vD _ CoordSys.FindTranslationOfAinTermsOfB[c.coordSys, d]; SELECT axis FROM 1 => AbsTranslateOnly[c, scene, d, [0, vD[2], vD[3]]]; 2 => AbsTranslateOnly[c, scene, d, [vD[1], 0, vD[3]]]; 3 => AbsTranslateOnly[c, scene, d, [vD[1], vD[2], 0]]; ENDCASE; }; NormalizeRot: PUBLIC PROC [c: Slice, scene: Scene, d: CoordSystem] = { oldMat, newMat: Matrix4by4; oldMat _ CoordSys.FindAInTermsOfB[a: c.coordSys, b: d]; newMat _ Matrix3d.NormalizeNoRotation[oldMat]; SVAssembly.AbsTransf[c, scene, newMat, d]; DirtyAssembly[c]; }; Normalize: PUBLIC PROC [c: Slice, scene: Scene, d: CoordSystem] = { SVAssembly.AbsTransf[c, scene, Matrix3d.Identity[], d]; DirtyAssembly[c]; }; NormalizeRotTug: PUBLIC PROC [c: Slice, scene: Scene, tugBoat: Slice, d: CoordSystem] = { f: Matrix4by4 _ CoordSys.FindAInTermsOfB[c.coordSys, tugBoat.coordSys]; NormalizeRot[tugBoat, scene, d]; SVAssembly.AbsTransf[c, scene, f, tugBoat.coordSys, TRUE]; DirtyAssembly[c]; }; NormalizeTug: PUBLIC PROC [c: Slice, scene: Scene, tugBoat: Slice, d: CoordSystem] = { f: Matrix4by4 _ CoordSys.FindAInTermsOfB[c.coordSys, tugBoat.coordSys]; Normalize[tugBoat, scene, d]; SVAssembly.AbsTransf[c, scene, f, tugBoat.coordSys, TRUE]; DirtyAssembly[c]; }; AlignTug: PUBLIC PROC [c: Slice, scene: Scene, tugBoat: Slice, d: CoordSystem] = { f: Matrix4by4 _ CoordSys.FindAInTermsOfB[c.coordSys, tugBoat.coordSys]; Align[tugBoat, scene, d]; SVAssembly.AbsTransf[c, scene, f, tugBoat.coordSys, TRUE]; DirtyAssembly[c]; }; AbutTug: PUBLIC PROC [c: Slice, scene: Scene, tugBoat: Slice, d: CoordSystem] = { f: Matrix4by4 _ CoordSys.FindAInTermsOfB[c.coordSys, tugBoat.coordSys]; Abut[tugBoat, scene, d]; SVAssembly.AbsTransf[c, scene, f, tugBoat.coordSys, TRUE]; DirtyAssembly[c]; }; AbutTugAxis: PUBLIC PROC [c: Slice, scene: Scene, axis: [1..3], tugBoat: Slice, d: CoordSystem] = { f: Matrix4by4 _ CoordSys.FindAInTermsOfB[c.coordSys, tugBoat.coordSys]; AbutAxis[tugBoat, scene, axis, d]; SVAssembly.AbsTransf[c, scene, f, tugBoat.coordSys, TRUE]; DirtyAssembly[c]; }; DirtyAssembly: PUBLIC PROC [a: Slice] = { }; -- end of DirtyAssembly TidyUpCoordSysTree: PUBLIC PROC [root: CoordSystem] = { parent: CoordSystem _ root.parent; IF root.scalarsOnly THEN { IF root.children # NIL THEN ERROR; -- scalars-only coordinate systems must be leaves. root.scalars _ Matrix3d.TidyUpVector[root.scalars]; } ELSE { root.mat _ Matrix3d.TidyUpMatrix[root.mat]; IF root.children # NIL THEN { -- Process the children as well FOR list: CoordSysList _ root.children, list.rest UNTIL list = NIL DO TidyUpCoordSysTree[list.first]; ENDLOOP; }; }; }; -- end of TidyUpCoordSysTree ScalePrimitives: PUBLIC PROC [a: Slice, sx, sy, sz: REAL] = { WITH a.shape SELECT FROM shape: Shape => SVAssembly.ScalePrimitive[a, sx, sy, sz]; alist: SliceList => { FOR list: LIST OF Slice _ alist.list, list.rest UNTIL list = NIL DO ScalePrimitives[list.first, sx, sy, sz]; ENDLOOP; }; ENDCASE; -- IF a.shape = NIL or something else, do nothing. }; ScaleEven: PUBLIC PROC [a: Slice, scene: Scene, scalar: REAL, d: CoordSystem _ NIL] = { displacements: Vector3d; WITH a.shape SELECT FROM shape: Shape => { SVAssembly.ScalePrimitive[a, scalar, scalar, scalar]; displacements _ CoordSys.FindTranslationOfAinTermsOfB[a.coordSys, d]; displacements _ SVVector3d.Scale[displacements, scalar]; AbsTranslateOnly[a, scene, d, displacements]; }; alist: SliceList => { FOR list: LIST OF Slice _ alist.list, list.rest UNTIL list = NIL DO ScaleEven[list.first, scene, scalar, a.coordSys]; ENDLOOP; displacements _ CoordSys.FindTranslationOfAinTermsOfB[a.coordSys, d]; displacements _ SVVector3d.Scale[displacements, scalar]; AbsTranslateOnly[a, scene, d, displacements]; }; ENDCASE; -- IF a.shape = NIL or something else, do nothing. }; AssemblyHasNoChildren: PUBLIC ERROR = CODE; ScaleEvenChildren: PUBLIC PROC [a: Slice, scene: Scene, scalar: REAL, d: CoordSystem _ NIL] ~ { WITH a.shape SELECT FROM shape: Shape => ERROR AssemblyHasNoChildren; alist: SliceList => { FOR list: LIST OF Slice _ alist.list, list.rest UNTIL list = NIL DO ScaleEven[list.first, scene, scalar, list.first.coordSys]; ENDLOOP; }; ENDCASE => ERROR AssemblyHasNoChildren; -- IF a.shape = NIL or something else, report a problem. }; ScaleDistancesChildren: PUBLIC PROC [c: Slice, scene: Scene, sx, sy, sz: REAL, d: CoordSystem _ NIL] = { displacements: Vector3d; WITH c.shape SELECT FROM shape: Shape => ERROR AssemblyHasNoChildren; alist: SliceList => { FOR list: LIST OF Slice _ alist.list, list.rest UNTIL list = NIL DO displacements _ CoordSys.FindTranslationOfAinTermsOfB[list.first.coordSys, d]; displacements[1] _ displacements[1]*sx; displacements[2] _ displacements[2]*sy; displacements[3] _ displacements[3]*sz; AbsTranslateOnly[list.first, scene, d, displacements]; ENDLOOP; }; ENDCASE => ERROR AssemblyHasNoChildren; -- Problem IF a.shape = NIL or something else. }; Scale: PUBLIC PROC [c: Slice, scene: Scene, sx, sy, sz: REAL, d: CoordSystem _ NIL] = { IF ISTYPE[c.shape, Shape] AND d = c.coordSys THEN SVAssembly.ScalePrimitive[c, sx, sy, sz] ELSE ScaleEven[c, scene, sx, d]; }; MatrixHasScaling: PRIVATE PROC [mat: Matrix4by4] RETURNS [BOOL] = { sx, sy, sz: REAL; almostZero: REAL _ 1.0e-4; [sx, sy, sz] _ Matrix3d.ScaleFromMatrix[mat]; IF ABS[sx - 1.0] > almostZero OR ABS[sy-1.0] > almostZero OR ABS[sz - 1.0] > almostZero THEN RETURN[TRUE] ELSE RETURN [FALSE]; }; END. ΆFile: SVTransformsImpl.mesa Last edited by Bier on May 5, 1987 12:50:06 pm PDT Copyright c 1984 by Xerox Corporation. All rights reserved. Contents: Procedures for Scaling, Translating, and Rotating assemblies with respect to an arbitrary coordinate system Rotates c about an axis parallel to the plane axis of d, passing thru the origin of c. A set of routines which call IncTransf Simple rotate C's coordsys wrt D by degrees about the x, y, or z axis A set of routines which call AbsTransf Places C so that the rotation from C to D remains as it is, but the translation becomes vD. This requres finding CD, modifying its translation, and performing AbsTransF on the result. Find CD. Compute the magnitude of its axes. Make CD have axes of the same magnitude, aligned with respect to the nearest axes of D. This direction is now taken. z axis must find another. zAxis gets second choice if xAxis has its first choice Bubble sort Leaving the rotation of C as it is, put C's origin coincident with the origin of D. Leave the rotation of C as it is. Put C's origin in the x = 0 plane of D. Leave translation between C and D as is. The axes of C become parallel to the axes of D. C becomes identical to D. A set of routines which specialize TugTransf Routines for updating cached matrices: This assembly has moved. Mark its coordinate system and those of its children as dirty. WITH a.shape SELECT FROM shape: Shape => { a.coordSys.worldOK _ FALSE; a.coordSys.inverseOK _ FALSE; }; alist: SliceList => { a.coordSys.worldOK _ FALSE; a.coordSys.inverseOK _ FALSE; DirtyAssemblyList[alist]; }; ENDCASE => ERROR; DirtyAssemblyList: PRIVATE PROC [alist: SliceList] = { FOR list: LIST OF Slice _ alist.list, list.rest UNTIL list = NIL DO DirtyAssembly[list.first]; ENDLOOP; }; If a component is almost a multiple of .5, make it exactly so. TellAboutCameraAndWorld: PUBLIC PROC [root: CoordSystem, camera: Camera] = { Tree walk through the tree and concatenate all matrices (will later concatenate matrices as it goes). This computes values for intermediate matrices as well as primitive matrices. Can be started using any assembly as root. camera.coordSys.wrtWorld _ CoordSys.WRTWorld[camera.coordSys]; TellAboutCameraAndWorldAux[root, camera]; }; TellAboutCameraAndWorldAux: PRIVATE PROC [root: CoordSystem, camera: Camera] = { parent: CoordSystem _ root.parent; IF root.scalarsOnly THEN { IF root.children # NIL THEN ERROR; -- scalars only coordinate systems must be leaves. Our matrices result from scaling our parent's matrices. root.wrtCamera _ Matrix3d.LocalScale[CoordSys.WRTCamera[parent, camera.coordSys], root.scalars[1], root.scalars[2], root.scalars[3]]; root.wrtWorld _ Matrix3d.LocalScale[CoordSys.WRTWorld[parent], root.scalars[1], root.scalars[2], root.scalars[3]]; root.cameraWRTlocal _ Matrix3d.Inverse[root.wrtCamera]; root.worldWRTlocal _ Matrix3d.Inverse[root.wrtWorld]; } ELSE { root.wrtCamera _ CoordSys.WRTCamera[root, camera.coordSys]; root.wrtWorld _ CoordSys.WRTWorld[root]; root.cameraWRTlocal _ Matrix3d.Inverse[root.wrtCamera]; root.worldWRTlocal _ Matrix3d.Inverse[root.wrtWorld]; IF root.children # NIL THEN { -- Process the children as well FOR list: CoordSysList _ root.children, list.rest UNTIL list = NIL DO TellAboutCameraAndWorldAux[list.first, camera]; ENDLOOP; }; }; }; -- end of TellAboutCameraAndWorldAux TellAboutParent: PUBLIC PROC [cs: CoordSystem] = { Updates the wrtWorld, and wrtCamera fields of cs by premultiplying cs.mat by cs.parent.wrtWorld and cs.parent.wrtCamera respectively cs.wrtWorld _ Matrix3d.MatMult[cs.parent.wrtWorld, cs.mat]; cs.wrtCamera _ Matrix3d.MatMult[cs.parent.wrtCamera, cs.mat]; cs.worldWRTlocal _ Matrix3d.Inverse[cs.wrtWorld]; cs.cameraWRTlocal _ Matrix3d.Inverse[cs.wrtCamera]; }; Scaling operations (IncTransf derivatives but more complicated). There are five ways to scale a cluster assembly 1) (ScalePrimitives) Scale each primitive in place, evenly or differentially. 2) (ScaleEven) If sx = sy = sz. Scale primitives in place and then translate all intermediate assemblies so the cluster assembly scales evenly as a unit 3) (ScaleNoShear) IF sx # sy or sy # sz. Scale the primitives evenly so that their dimension in the direction of scaling increases or decreases to the same quantity it would have reached by method 3. This preserves shape of primitives. Global shape is not preserved (but almost). 4) (ScaleEvenChildren) Perform a ScaleEven on each of the children of the cluster assembly but leave their relative translations fixed. This is equivalent to performing a ScaleEven (child, child's cs, scalar) on each child individually. 5) (ScaleDistancesChildren) Doesn't change the size or shape of any of the children of assembly. Simply translates all of the children as they would be translated by a Scale even of assembly. Just a bit tricky. If a is a Primitive and cs is any coordinate frame, just scale and translate it from the center of cs by scalar times its current translation. If a is a cluster, and cs is any coordinate frame, evenly scale each component of a about a's center, then translate a as a unit, the appropriate distance from the center of cs. now translate a's coordSys so that its displacement from the origin of cs is scaled by scalar Recursively scale subassemblies with respect to their own coordinate systems Now translate a's coordSys so that its displacement from the origin of cs is scaled by scalar For each child of a, find its current displacement from the center of cs. Scale this displacement by [sx, sy, sz]. Κ΄˜Iheadšœ™Jšœ2™2Jšœ Οmœ1™L™SLšœ'˜'Lšœ˜L˜—šŸœžœžœ;˜PL™JLšœ‘œ ˜ L˜Lšœ‘œ8˜:šžœž˜Lš œ&‘œ‘œ‘œ‘œ˜6Lš œ%‘œ‘œ‘œ‘œ˜6Lš œ%‘œ‘œ‘œ‘œ˜6Lšžœ˜—Lšœ˜L˜—šŸ œž œ-˜FL™YJšœ˜Jšœ7˜7Jšœ.˜.Jšœ*˜*L˜J˜L˜—šŸ œž œ-˜CL™Lšœ7˜7L˜L˜—J™Jšœ#Ÿ ™,J™šŸœžœžœ=˜YJšœG˜GJšœ ˜ Jšœ4žœ˜:L˜Jšœ˜—šŸ œžœžœ=˜VJšœG˜GJšœ˜Jšœ4žœ˜:L˜Jšœ˜—šŸœžœžœ=˜RJšœG˜GJšœ˜Jšœ4žœ˜:L˜Jšœ˜—šŸœžœžœ=˜QJšœG˜GJšœ˜Jšœ4žœ˜:L˜Jšœ˜—šŸ œžœžœK˜cJšœG˜GJšœ"˜"Jšœ4žœ˜:L˜Jšœ˜J˜—J™Jšœ£œ™&J™šŸ œžœžœ˜)J™Xšžœ žœž™šœ™Jšœžœ™Jšœžœ™Jšœ™—šœ™Jšœžœ™Jšœžœ™Jšœ™Jšœ™—Jšžœžœ™—Jšœ’˜J˜—šŸœžœžœ™6š žœžœžœžœžœž™CJšœ™Jšžœ™—Jšœ™—J˜šŸœžœžœ˜7J™>Jšœ"˜"šžœžœ˜Jš žœžœžœžœ’2˜UJšœ3˜3J˜—šžœ˜Jšœ+˜+šžœžœžœ’˜=šžœ/žœžœž˜EJš£œ ˜Jšžœ˜—Jšœ˜—Jšœ˜—Jšœ’˜J˜—šŸœžœžœ(™LJšœΰ™ΰJšœ>™>Jšœ)™)J™J™—šŸœžœžœ(™PJšœ"™"šžœžœ™š žœžœžœžœ’2™UJ™7—Jšœ…™…Jšœr™rJšœ7™7Jšœ5™5J™—šžœ™Jšœ;™;Jšœ(™(Jšœ7™7Jšœ5™5šžœžœžœ’™=šžœ/žœžœž™EJš£œ™/Jšžœ™—Jšœ™—Jšœ™—Jšœ’$™'J™—šŸœžœžœ™2Lšœ„™„Lšœ;™;Lšœ=™=Lšœ1™1Lšœ3™3Lšœ™—J˜Jš£œ9™@˜Jšœ/™/JšœN™NJšœš™šJšœœ™œJšœν™νJ™ΐ—J˜šŸœžœžœžœ˜=šžœ žœž˜Jšœ9˜9šœ˜š žœžœžœžœžœž˜CJšœ(˜(Jšžœ˜—Jšœ˜—Jšžœ’3˜;—Jšœ˜J˜—š Ÿ œžœžœ"žœžœ˜WJ™J™ŽJ™±Jšœ˜šžœ žœž˜šœ˜Jšœ5˜5Jšœ]™]JšœE˜EJšœ8˜8Jšœ-˜-Jšœ˜—šœ˜š žœžœžœžœžœž˜Cšœ1˜1JšœL™L—Jšžœ˜—Jšœ]™]JšœE˜EJšœ8˜8Jšœ-˜-Jšœ˜—Jšžœ’2˜;—Jšœ˜J˜—Jšœžœžœžœ˜+š Ÿœžœžœ"žœžœ˜_šžœ žœž˜Jšœžœ˜,šœ˜š žœžœžœžœžœž˜CJšœ:˜:Jšžœ˜—Jšœ˜—Jšžœžœ’8˜`—Jšœ˜—š Ÿœžœžœ&žœžœ˜hJšœs™sJšœ˜šžœ žœž˜Jšœžœ˜,šœ˜š žœžœžœžœžœž˜CJšœN˜NJšœ'˜'Jšœ'˜'Jšœ'˜'Jšœ6˜6Jšžœ˜—Jšœ˜—Jšžœžœ’/˜V—Jšœ˜—š Ÿœžœžœ&žœžœ˜WJšžœžœžœžœ)˜ZJšžœ˜ Jšœ˜—J˜š Ÿœžœžœžœžœ˜CJšœ žœ˜Jšœ žœ ˜Jšœ-˜-Jš žœžœžœžœžœžœ˜WJšžœžœžœ˜Jšžœžœžœ˜Jšœ˜—J˜Jšžœ˜—…—*„Oξ