DIRECTORY CoordSys, Imager, IO, RealFns, SV2d, SV3d, SVDraw3d, SVGraphics, SVModelTypes, SVPolygon2d, SVPolygon3d, SVSceneTypes, SVVector3d, SweepGeometry; SweepGeometryImpl: CEDAR PROGRAM IMPORTS CoordSys, IO, RealFns, SVDraw3d, SVGraphics, SVPolygon2d, SVPolygon3d, SVVector3d EXPORTS SweepGeometry = BEGIN Slice: TYPE = SVSceneTypes.Slice; Camera: TYPE = SVModelTypes.Camera; Matrix4by4: TYPE = SV3d.Matrix4by4; CoordSystem: TYPE = SVModelTypes.CoordSystem; LightSource: TYPE = SVModelTypes.LightSource; LightSourceList: TYPE = LIST OF LightSource; LinearMesh: TYPE = REF LinearMeshRecord; LinearMeshRecord: TYPE = SweepGeometry.LinearMeshRecord; MasterObject: TYPE = SVSceneTypes.MasterObject; Path: TYPE = SV2d.Path; PlanarSurface: TYPE = REF PlanarSurfaceObj; PlanarSurfaceObj: TYPE = SVSceneTypes.PlanarSurfaceObj; PlanarSurfaceList: TYPE = SVSceneTypes.PlanarSurfaceList; Point2d: TYPE = SV2d.Point2d; Point3d: TYPE = SV3d.Point3d; Poly3d: TYPE = SV3d.Poly3d; Polygon: TYPE = SV2d.Polygon; RevoluteMesh: TYPE = REF RevoluteMeshRecord; RevoluteMeshRecord: TYPE = SweepGeometry.RevoluteMeshRecord; Shape: TYPE = SVSceneTypes.Shape; ToroidalMesh: TYPE = REF ToroidalMeshRecord; ToroidalMeshRecord: TYPE = SweepGeometry.ToroidalMeshRecord; Vector3d: TYPE = SV3d.Vector3d; globalImpatient: BOOL _ FALSE; TooFewPointsForShadedSweep: PUBLIC SIGNAL = CODE; LinearSweep: PUBLIC PROC [poly: Polygon, frontDepth: REAL _ 0.5, backDepth: REAL _ -0.5] RETURNS [meshRecord: LinearMesh] = { frontToBack, clockWise, thisNorm: Vector3d; iPlusOne: NAT; IF poly.len <=2 THEN SIGNAL TooFewPointsForShadedSweep; IF NOT SVPolygon2d.IsClockwisePoly[poly] THEN SVPolygon2d.InvertPolyInPlace[poly]; meshRecord _ NEW[LinearMeshRecord]; FOR i: NAT IN[1..poly.len] DO FOR j: NAT IN[1..2] DO meshRecord.array[i][j][1] _ poly[i-1][1]; meshRecord.array[i][j][2] _ poly[i-1][2]; ENDLOOP; meshRecord.array[i][1][3] _ frontDepth; meshRecord.array[i][2][3] _ backDepth; ENDLOOP; meshRecord.len _ poly.len; meshRecord.surfaces.front.normal _ [0,0,1]; meshRecord.surfaces.back.normal _ [0,0,-1]; FOR i: NAT IN[1..poly.len] DO iPlusOne _ IF i = poly.len THEN 1 ELSE i + 1; frontToBack _ SVVector3d.Sub[meshRecord.array[i][2],meshRecord.array[i][1]]; clockWise _ SVVector3d.Sub[meshRecord.array[iPlusOne][1],meshRecord.array[i][1]]; thisNorm _ SVVector3d.CrossProduct[clockWise,frontToBack]; meshRecord.surfaces.sides[i].normal _ thisNorm; ENDLOOP; }; -- end of ShadedLinearSweep RevoluteSweep: PUBLIC PROC [path: Path, linesOfLongitude: NAT _ 10] RETURNS [meshRecord: RevoluteMesh] = { leftToRight, rightToLeft, thisNorm: Vector3d; degreesPerLong: REAL; thisAngle: REAL; jPlusOne: NAT; poly: Polygon; poly _ SVPolygon2d.CreatePoly[path.len+2]; poly _ SVPolygon2d.PutPolyPoint[poly, 0 , [0, path[0][2]]]; poly _ SVPolygon2d.PutPolyPoint[poly, path.len + 1, [0, path[path.len -1][2]]]; poly _ SVPolygon2d.PartPolyGetsPartPath[path, 0, poly, 1, path.len]; FOR i: NAT IN[0..poly.len) DO IF poly[i][1] < 0 THEN poly[i][1] _ -poly[i][1]; ENDLOOP; IF NOT SVPolygon2d.IsClockwisePoly[poly] THEN SVPolygon2d.InvertPolyInPlace[poly]; path _ SVPolygon2d.SubPathOfPoly[poly, 1, path.len]; degreesPerLong _ 360.0/linesOfLongitude; meshRecord _ NEW[RevoluteMeshRecord]; FOR i: NAT IN[1..path.len] DO FOR j: NAT IN[1..linesOfLongitude] DO sin, cos: REAL; thisAngle _ j*degreesPerLong; sin _ RealFns.SinDeg[thisAngle]; cos _ RealFns.CosDeg[thisAngle]; meshRecord.array[i][j][1] _ path[i-1][1]*cos;-- assign new x from source x meshRecord.array[i][j][3] _ -path[i-1][1]*sin;-- assign new z from source x meshRecord.array[i][j][2] _ path[i-1][2];-- assign new y directly from source y ENDLOOP; -- next point of longitude ENDLOOP; -- next line of latitude meshRecord.linesOfLongitude _ linesOfLongitude; meshRecord.linesOfLatitude _ path.len; meshRecord.surfaces.top.normal _ [0,1,0]; meshRecord.surfaces.bottom.normal _ [0,-1,0]; FOR i: NAT IN[1..path.len-1] DO FOR j: NAT IN[1..linesOfLongitude] DO jPlusOne _ IF j = linesOfLongitude THEN 1 ELSE j + 1; leftToRight _ SVVector3d.Sub[meshRecord.array[i+1][jPlusOne], meshRecord.array[i][j]]; rightToLeft _ SVVector3d.Sub[meshRecord.array[i+1][j], meshRecord.array[i][jPlusOne]]; thisNorm _ SVVector3d.CrossProduct[rightToLeft,leftToRight]; meshRecord.surfaces.sides[i][j].normal _ thisNorm; ENDLOOP; ENDLOOP; }; -- end of ShadedRevoluteSweep ToroidalSweep: PUBLIC PROC [poly: Polygon, linesOfLongitude: NAT _ 10] RETURNS [meshRecord: ToroidalMesh] = { leftToRight, rightToLeft, thisNorm: Vector3d; degreesPerLong: REAL; thisAngle: REAL; iPlusOne, jPlusOne: NAT; FOR i: NAT IN[0..poly.len) DO IF poly[i][1] < 0 THEN poly[i][1] _ -poly[i][1]; ENDLOOP; IF NOT SVPolygon2d.IsClockwisePoly[poly] THEN SVPolygon2d.InvertPolyInPlace[poly]; degreesPerLong _ 360.0/linesOfLongitude; meshRecord _ NEW[ToroidalMeshRecord]; FOR i: NAT IN[1..poly.len] DO FOR j: NAT IN[1..linesOfLongitude] DO sin, cos: REAL; thisAngle _ j*degreesPerLong; sin _ RealFns.SinDeg[thisAngle]; cos _ RealFns.CosDeg[thisAngle]; meshRecord.array[i][j][1] _ poly[i-1][1]*cos; -- assign new x from source x meshRecord.array[i][j][3] _ -poly[i-1][1]*sin; -- assign new z from source x meshRecord.array[i][j][2] _ poly[i-1][2]; -- assign new y directly from source y ENDLOOP; -- next point of longitude ENDLOOP; -- next line of latitude meshRecord.linesOfLongitude _ linesOfLongitude; meshRecord.linesOfLatitude _ poly.len; FOR i: NAT IN[1..poly.len] DO iPlusOne _ IF i = poly.len THEN 1 ELSE i + 1; FOR j: NAT IN[1..linesOfLongitude] DO jPlusOne _ IF j = linesOfLongitude THEN 1 ELSE j + 1; leftToRight _ SVVector3d.Sub[meshRecord.array[iPlusOne][jPlusOne], meshRecord.array[i][j]]; rightToLeft _ SVVector3d.Sub[meshRecord.array[iPlusOne][j], meshRecord.array[i][jPlusOne]]; thisNorm _ SVVector3d.CrossProduct[rightToLeft,leftToRight]; meshRecord.surfaces.sides[i][j].normal _ thisNorm; ENDLOOP; ENDLOOP; }; -- end of ToroidalSweep GetLinearPoly: PUBLIC PROC [linMesh: LinearMesh] RETURNS [poly: Polygon] = { poly _ SVPolygon2d.CreatePoly[linMesh.len]; FOR i: NAT IN[1..linMesh.len] DO poly[i-1] _ [linMesh.array[i][1][1], linMesh.array[i][1][2]]; ENDLOOP; poly.len _ linMesh.len; }; -- end of GetLinearPoly GetRevolutePath: PUBLIC PROC [revMesh: RevoluteMesh] RETURNS [path: Path] = { path _ SVPolygon2d.CreatePath[revMesh.linesOfLatitude]; FOR i: NAT IN[1..revMesh.linesOfLatitude] DO path[i - 1] _ [revMesh.array[i][revMesh.linesOfLongitude][1], revMesh.array[i][revMesh.linesOfLongitude][2]]; ENDLOOP; path.len _ revMesh.linesOfLatitude; }; -- end of GetRevolutePath LineDrawLinearSweep: PUBLIC PROC [dc: Imager.Context, meshRecord: LinearMesh, camera: Camera, localCS: CoordSystem] = { localCamera: Matrix4by4 _ CoordSys.WRTCamera[localCS, camera.coordSys]; IF meshRecord.len <= 0 THEN RETURN; SVGraphics.SetCP[dc, meshRecord.array[1][1], camera, localCamera]; -- First Point of front plane FOR i: NAT IN[2..meshRecord.len] DO -- Draw front plane SVGraphics.DrawTo[dc, meshRecord.array[i][1], camera, localCamera]; ENDLOOP; SVGraphics.DrawTo[dc, meshRecord.array[1][1], camera, localCamera]; SVGraphics.SetCP[dc, meshRecord.array[1][2], camera, localCamera]; -- First point of rear plane FOR i: NAT IN[2..meshRecord.len] DO-- Draw rear plane SVGraphics.DrawTo[dc, meshRecord.array[i][2], camera, localCamera]; ENDLOOP; SVGraphics.DrawTo[dc, meshRecord.array[1][2], camera, localCamera]; FOR i: NAT IN[1..meshRecord.len] DO -- Draw Lines between Planes SVGraphics.SetCP[dc, meshRecord.array[i][1], camera, localCamera]; SVGraphics.DrawTo[dc, meshRecord.array[i][2], camera, localCamera]; ENDLOOP; }; LineDrawRevoluteSweep: PUBLIC PROC [dc: Imager.Context, meshRecord: RevoluteMesh, camera: Camera, localCS: CoordSystem] = { localCamera: Matrix4by4 _ CoordSys.WRTCamera[localCS, camera.coordSys]; IF globalImpatient THEN FOR lat: NAT _ 1, lat+(meshRecord.linesOfLatitude-1) UNTIL lat > meshRecord.linesOfLatitude DO SVGraphics.SetCP[dc, meshRecord.array[lat][1], camera, localCamera]; -- First point of this line of latitude FOR long: NAT IN[2..meshRecord.linesOfLongitude] DO SVGraphics.DrawTo[dc, meshRecord.array[lat][long], camera, localCamera]; ENDLOOP; SVGraphics.DrawTo[dc, meshRecord.array[lat][1], camera, localCamera]; ENDLOOP ELSE FOR lat: NAT IN [1..meshRecord.linesOfLatitude] DO SVGraphics.SetCP[dc, meshRecord.array[lat][1], camera, localCamera]; -- First point of this line of latitude FOR long: NAT IN[2..meshRecord.linesOfLongitude] DO SVGraphics.DrawTo[dc, meshRecord.array[lat][long], camera, localCamera]; ENDLOOP; SVGraphics.DrawTo[dc, meshRecord.array[lat][1], camera, localCamera]; ENDLOOP; FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO SVGraphics.SetCP[dc, meshRecord.array[1][long], camera, localCamera]; -- First point of this line of longitude FOR lat: NAT IN[1..meshRecord.linesOfLatitude] DO SVGraphics.DrawTo[dc, meshRecord.array[lat][long], camera, localCamera]; ENDLOOP; ENDLOOP; }; LineDrawToroidalSweep: PUBLIC PROC [dc: Imager.Context, meshRecord: ToroidalMesh, camera: Camera, localCS: CoordSystem] = { localCamera: Matrix4by4 _ CoordSys.WRTCamera[localCS, camera.coordSys]; FOR lat: NAT IN [1..meshRecord.linesOfLatitude] DO SVGraphics.SetCP[dc, meshRecord.array[lat][1], camera, localCamera]; -- First point of this line of latitude FOR long: NAT IN[2..meshRecord.linesOfLongitude] DO SVGraphics.DrawTo[dc, meshRecord.array[lat][long], camera, localCamera]; ENDLOOP; SVGraphics.DrawTo[dc, meshRecord.array[lat][1], camera, localCamera]; ENDLOOP; FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO SVGraphics.SetCP[dc, meshRecord.array[1][long], camera, localCamera]; -- First point of this line of longitude FOR lat: NAT IN[1..meshRecord.linesOfLatitude] DO SVGraphics.DrawTo[dc, meshRecord.array[lat][long], camera, localCamera]; ENDLOOP; SVGraphics.DrawTo[dc, meshRecord.array[1][long], camera, localCamera]; ENDLOOP; }; -- end of LineDrawToroidalSweep AverageVertex: PRIVATE PROC [meshRecord: LinearMesh] RETURNS [pt: Point2d] = { pt _ [meshRecord.array[1][1][1], meshRecord.array[1][1][2]]; FOR i: NAT IN [1..meshRecord.len] DO pt[1] _ pt[1] + meshRecord.array[i][1][1]; pt[2] _ pt[2] + meshRecord.array[i][1][2]; ENDLOOP; pt[1] _ pt[1]/meshRecord.len; pt[2] _ pt[2]/meshRecord.len; }; DrawNormalsLinearSweep: PUBLIC PROC [dc: Imager.Context, meshRecord: LinearMesh, camera: Camera, localCS: CoordSystem] = { thisNorm: Vector3d; upperLeftPoint, lowerRightPoint, midPoint: Point3d; iPlusOne: NAT; midPoint2d: Point2d; thisNorm _ meshRecord.surfaces.front.normal; midPoint2d _ AverageVertex[meshRecord]; midPoint _ [midPoint2d[1], midPoint2d[2], meshRecord.array[1][1][3]]; SVDraw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS]; thisNorm _ meshRecord.surfaces.back.normal; midPoint _ [midPoint2d[1], midPoint2d[2], meshRecord.array[1][2][3]]; SVDraw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS]; FOR i: NAT IN[1..meshRecord.len] DO iPlusOne _ IF i = meshRecord.len THEN 1 ELSE i + 1; thisNorm _ meshRecord.surfaces.sides[i].normal; upperLeftPoint _ meshRecord.array[iPlusOne][1]; lowerRightPoint _ meshRecord.array[i][2]; midPoint _ SVVector3d.Add[upperLeftPoint,lowerRightPoint]; midPoint _ SVVector3d.Scale[midPoint,0.5]; SVDraw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS]; ENDLOOP; }; -- end of DrawLinearNormals DrawNormalsRevoluteSweep: PUBLIC PROC [dc: Imager.Context, meshRecord: RevoluteMesh, camera: Camera, localCS: CoordSystem] = { thisNorm: Vector3d; upperLeftPoint, lowerRightPoint, midPoint: Point3d; iPlusOne, jPlusOne: NAT; thisNorm _ meshRecord.surfaces.top.normal; midPoint _ [0, meshRecord.array[1][1][2], 0]; SVDraw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS]; thisNorm _ meshRecord.surfaces.bottom.normal; midPoint _ [0, meshRecord.array[meshRecord.linesOfLatitude][1][2], 0]; SVDraw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS]; FOR i: NAT IN[1..meshRecord.linesOfLatitude-1] DO iPlusOne _ i + 1; FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO jPlusOne _ IF j = meshRecord.linesOfLongitude THEN 1 ELSE j + 1; thisNorm _ meshRecord.surfaces.sides[i][j].normal; upperLeftPoint _ meshRecord.array[i][j]; lowerRightPoint _ meshRecord.array[iPlusOne][jPlusOne]; midPoint _ SVVector3d.Add[upperLeftPoint,lowerRightPoint]; midPoint _ SVVector3d.Scale[midPoint,0.5]; SVDraw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS]; ENDLOOP; ENDLOOP; }; -- end of DrawNormalsRevoluteSweep DrawNormalsToroidalSweep: PUBLIC PROC [dc: Imager.Context, meshRecord: ToroidalMesh, camera: Camera, localCS: CoordSystem] = {thisNorm: Vector3d; upperLeftPoint, lowerRightPoint, midPoint: Point3d; iPlusOne, jPlusOne: NAT; FOR i: NAT IN[1..meshRecord.linesOfLatitude] DO iPlusOne _ IF i = meshRecord.linesOfLatitude THEN 1 ELSE i + 1; FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO jPlusOne _ IF j = meshRecord.linesOfLongitude THEN 1 ELSE j + 1; thisNorm _ meshRecord.surfaces.sides[i][j].normal; upperLeftPoint _ meshRecord.array[i][j]; lowerRightPoint _ meshRecord.array[iPlusOne][jPlusOne]; midPoint _ SVVector3d.Add[upperLeftPoint,lowerRightPoint]; midPoint _ SVVector3d.Scale[midPoint,0.5]; SVDraw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS]; ENDLOOP; ENDLOOP; }; -- end of DrawNormalsToroidalSweep PolyListLinear: PUBLIC PROC [f: IO.STREAM, meshRecord: LinearMesh] = { postPlusOne: NAT; f.PutF["vertices [%g]:\n", IO.int[2*meshRecord.len]]; FOR i: NAT IN[1..meshRecord.len] DO f.PutF[" %g %g %g\n", IO.real[meshRecord.array[i][1][1]], IO.real[meshRecord.array[i][1][2]], IO.real[meshRecord.array[i][1][3]] ]; ENDLOOP; FOR i: NAT IN[1..meshRecord.len] DO f.PutF[" %g %g %g\n", IO.real[meshRecord.array[i][2][1]], IO.real[meshRecord.array[i][2][2]], IO.real[meshRecord.array[i][2][3]] ]; ENDLOOP; f.PutChar[IO.CR]; f.PutF["faces [%g]:\n", IO.int[2+meshRecord.len]]; f.PutF["f ("]; FOR i: NAT IN[1..meshRecord.len] DO f.PutF["%g ", IO.int[i]]; ENDLOOP; f.PutF[")\n"]; f.PutF["f ("]; FOR i: NAT DECREASING IN[1..meshRecord.len] DO f.PutF["%g ", IO.int[i+meshRecord.len]]; ENDLOOP; f.PutF[")\n"]; FOR post: NAT IN[1..meshRecord.len] DO postPlusOne _ IF post = meshRecord.len THEN 1 ELSE post + 1; f.PutF["f (%g %g %g %g )\n", IO.int[postPlusOne], IO.int[postPlusOne+meshRecord.len], IO.int[post+meshRecord.len], IO.int[post]]; ENDLOOP; f.PutChar[IO.CR]; }; -- end of PolyListLinear PolyListRevolute: PUBLIC PROC [f: IO.STREAM, meshRecord: RevoluteMesh] = { longPlusOne: NAT; f.PutF["vertices [%g]:\n", IO.int[meshRecord.linesOfLatitude*meshRecord.linesOfLongitude]]; FOR lat: NAT IN[1..meshRecord.linesOfLatitude] DO FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO f.PutF[" %g %g %g\n", IO.real[meshRecord.array[lat][long][1]], IO.real[meshRecord.array[lat][long][2]], IO.real[meshRecord.array[lat][long][3]] ]; ENDLOOP; ENDLOOP; f.PutChar[IO.CR]; f.PutF["faces [%g]:\n", IO.int[(meshRecord.linesOfLatitude-1)*meshRecord.linesOfLongitude+2]]; f.PutF["f ("]; FOR j: NAT DECREASING IN[1..meshRecord.linesOfLongitude] DO f.PutF["%g ", IO.int[j]]; ENDLOOP; f.PutF[")\n"]; f.PutF["f ("]; FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO f.PutF["%g ", IO.int[(meshRecord.linesOfLatitude-1)*meshRecord.linesOfLongitude + j]]; ENDLOOP; f.PutF[")\n"]; FOR lat: NAT IN[1..meshRecord.linesOfLatitude-1] DO FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO longPlusOne _ IF long = meshRecord.linesOfLongitude THEN 1 ELSE long + 1; f.PutF["f (%g %g ", IO.int[lat*meshRecord.linesOfLongitude + long], IO.int[lat*meshRecord.linesOfLongitude + longPlusOne]]; f.PutF["%g %g )\n", IO.int[(lat-1)*meshRecord.linesOfLongitude + longPlusOne], IO.int[(lat-1)*meshRecord.linesOfLongitude + long]]; ENDLOOP; ENDLOOP; f.PutChar[IO.CR]; }; -- end of PolyListRevolute PolyListToroidal: PUBLIC PROC [f: IO.STREAM, meshRecord: ToroidalMesh] = { longPlusOne, latPlusOne: NAT; f.PutF["vertices [%g]:\n", IO.int[meshRecord.linesOfLatitude*meshRecord.linesOfLongitude]]; FOR lat: NAT IN[1..meshRecord.linesOfLatitude] DO FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO f.PutF[" %g %g %g\n", IO.real[meshRecord.array[lat][long][1]], IO.real[meshRecord.array[lat][long][2]], IO.real[meshRecord.array[lat][long][3]] ]; ENDLOOP; ENDLOOP; f.PutChar[IO.CR]; f.PutF["faces [%g]:\n", IO.int[meshRecord.linesOfLatitude*meshRecord.linesOfLongitude]]; FOR lat: NAT IN[1..meshRecord.linesOfLatitude] DO FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO latPlusOne _ IF lat = meshRecord.linesOfLatitude THEN 1 ELSE lat + 1; longPlusOne _ IF long = meshRecord.linesOfLongitude THEN 1 ELSE long + 1; f.PutF["f (%g %g ", IO.int[(latPlusOne-1)*meshRecord.linesOfLongitude + long], IO.int[(latPlusOne-1)*meshRecord.linesOfLongitude + longPlusOne]]; f.PutF["%g %g )\n", IO.int[(lat-1)*meshRecord.linesOfLongitude + longPlusOne], IO.int[(lat-1)*meshRecord.linesOfLongitude + long]]; ENDLOOP; ENDLOOP; }; -- end of PolyListToroidal CountPlanarSurfacesLinearSweep: PUBLIC PROC [meshRecord: LinearMesh] RETURNS [len: NAT] = { len _ meshRecord.len + 2; }; CountVerticesLinearSweep: PUBLIC PROC [meshRecord: LinearMesh] RETURNS [len: NAT] = { len _ 2*meshRecord.len; }; SortedLinearSurface: TYPE = REF SortedLinearSurfaceObj; SortedLinearSurfaceObj: TYPE = RECORD [ localWorld, localCamera: Matrix4by4, post: NAT, meshRecord: LinearMesh ]; PlanarSurfacesLinearSweep: PUBLIC PROC [meshRecord: LinearMesh, assembly: Slice, cameraCS: CoordSystem] RETURNS [psl: PlanarSurfaceList] = { poly: Poly3d _ SVPolygon3d.CreatePoly[meshRecord.len]; iPlusOne: NAT; avgDepth: REAL; shape: Shape _ NARROW[assembly.shape, Shape]; mo: MasterObject _ shape.mo; localCS: CoordSystem _ shape.coordSys; thisSortedSurface: PlanarSurface; FOR i: NAT IN[1..meshRecord.len] DO poly _ SVPolygon3d.AddPolyPoint[poly, meshRecord.array[i][1]]; ENDLOOP; avgDepth _ AverageDepthInCamera[poly, localCS, cameraCS]; thisSortedSurface _ NEW[PlanarSurfaceObj _ [ whichSurface: NEW[SortedLinearSurfaceObj _ [CoordSys.WRTWorld[localCS], CoordSys.WRTCamera[localCS, cameraCS], 0, meshRecord]], assembly: assembly, normal: meshRecord.surfaces.front.normal, mo: mo, depth: avgDepth]]; psl _ CONS[thisSortedSurface, psl]; SVPolygon3d.ClearPoly[poly]; FOR i: NAT IN[1..meshRecord.len] DO poly _ SVPolygon3d.AddPolyPoint[poly, meshRecord.array[i][2]]; ENDLOOP; avgDepth _ AverageDepthInCamera[poly, localCS, cameraCS]; thisSortedSurface _ NEW[PlanarSurfaceObj _ [ whichSurface: NEW[SortedLinearSurfaceObj _ [CoordSys.WRTWorld[localCS], CoordSys.WRTCamera[localCS, cameraCS], meshRecord.len+1, meshRecord]], assembly: assembly, normal: meshRecord.surfaces.back.normal, mo: mo, depth: avgDepth]]; psl _ CONS[thisSortedSurface, psl]; FOR i: NAT IN[1..meshRecord.len] DO SVPolygon3d.ClearPoly[poly]; iPlusOne _ IF i = meshRecord.len THEN 1 ELSE i + 1; poly _ SVPolygon3d.AddPolyPoint[poly, meshRecord.array[i][1]]; poly _ SVPolygon3d.AddPolyPoint[poly, meshRecord.array[i][2]]; poly _ SVPolygon3d.AddPolyPoint[poly, meshRecord.array[iPlusOne][2]]; poly _ SVPolygon3d.AddPolyPoint[poly, meshRecord.array[iPlusOne][1]]; avgDepth _ AverageDepthInCamera[poly, localCS, cameraCS]; thisSortedSurface _ NEW[PlanarSurfaceObj _ [ whichSurface: NEW[SortedLinearSurfaceObj _ [CoordSys.WRTWorld[localCS], CoordSys.WRTCamera[localCS, cameraCS], i, meshRecord]], assembly: assembly, normal: meshRecord.surfaces.sides[i].normal, mo: mo, depth: avgDepth]]; psl _ CONS[thisSortedSurface, psl]; ENDLOOP; }; -- end of PlanarSurfacesLinearSweep DrawPlanarSurfaceLinearSweep: PUBLIC PROC [dc: Imager.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera] = { poly3d: Poly3d; post, postPlusOne: NAT; avgDepth: REAL; meshRecord: LinearMesh; thisLinSurface: SortedLinearSurface; thisLinSurface _ NARROW[ps.whichSurface]; meshRecord _ thisLinSurface.meshRecord; post _ thisLinSurface.post; avgDepth _ ps.depth; SELECT post FROM 0 => {-- draw front face poly3d _ SVPolygon3d.CreatePoly[meshRecord.len]; FOR i: NAT IN[1..meshRecord.len] DO poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][1]]; ENDLOOP; SVGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, thisLinSurface.localWorld, thisLinSurface.localCamera]; }; meshRecord.len + 1 => { -- draw back face poly3d _ SVPolygon3d.CreatePoly[meshRecord.len]; FOR i: NAT IN[1..meshRecord.len] DO poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][2]]; ENDLOOP; SVGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, thisLinSurface.localWorld, thisLinSurface.localCamera]; }; IN [1..meshRecord.len] => {-- draw side face poly3d _ SVPolygon3d.CreatePoly[4]; postPlusOne _ IF post = meshRecord.len THEN 1 ELSE post + 1; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[post][1]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[post][2]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[postPlusOne][2]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[postPlusOne][1]]; SVGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, thisLinSurface.localWorld, thisLinSurface.localCamera]; }; ENDCASE => ERROR; }; -- end of DrawPlanarSurfaceLinearSweep MaxDepth: PROC [poly: Poly3d] RETURNS [maxDepth: REAL] = { maxDepth _ poly[0][3]; FOR i: NAT IN[1..poly.len) DO IF poly[i][3] < maxDepth THEN maxDepth _ poly[i][3]; ENDLOOP; }; AverageDepthInCamera: PROC [poly: Poly3d, localCS, cameraCS: CoordSystem] RETURNS [avgDepth: REAL] = { sum: REAL _ 0; realLen: REAL _ poly.len; localPoint: Point3d; FOR i: NAT IN[0..poly.len) DO localPoint _ poly[i]; localPoint _ SVGraphics.LocalToCamera[localPoint, localCS, cameraCS]; sum _ sum + localPoint[3]; ENDLOOP; avgDepth _ sum/realLen; }; SortedRevoluteSurface: TYPE = REF SortedRevoluteSurfaceObj; SortedRevoluteSurfaceObj: TYPE = RECORD [lat, long: NAT, meshRecord: RevoluteMesh]; CountPlanarSurfacesRevoluteSweep: PUBLIC PROC [meshRecord: RevoluteMesh] RETURNS [len: NAT] = { len _ (meshRecord.linesOfLongitude)*(meshRecord.linesOfLatitude-1) + 2; }; CountVerticesRevoluteSweep: PUBLIC PROC [meshRecord: RevoluteMesh] RETURNS [len: NAT] = { len _ meshRecord.linesOfLongitude*meshRecord.linesOfLatitude; }; PlanarSurfacesRevoluteSweep: PUBLIC PROC [meshRecord: RevoluteMesh, assembly: Slice, cameraCS: CoordSystem] RETURNS [psl: PlanarSurfaceList] = { poly3d: Poly3d _ SVPolygon3d.CreatePoly[meshRecord.linesOfLongitude]; avgDepth: REAL; thisSortedSurface: PlanarSurface; shape: Shape _ NARROW[assembly.shape]; mo: MasterObject _ shape.mo; jPlusOne: NAT; localCS: CoordSystem _ shape.coordSys; psl _ NIL; SVPolygon3d.ClearPoly[poly3d]; FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[1][j]]; ENDLOOP; avgDepth _ AverageDepthInCamera[poly3d, localCS, cameraCS]; thisSortedSurface _ NEW[PlanarSurfaceObj _ [ whichSurface: NEW[SortedRevoluteSurfaceObj _ [0,0,meshRecord]], assembly: assembly, normal: meshRecord.surfaces.top.normal, mo: mo, depth: avgDepth]]; psl _ CONS[thisSortedSurface, psl]; SVPolygon3d.ClearPoly[poly3d]; FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[meshRecord.linesOfLatitude][j]]; ENDLOOP; avgDepth _ AverageDepthInCamera[poly3d, localCS, cameraCS]; thisSortedSurface _ NEW[PlanarSurfaceObj _ [ whichSurface: NEW[SortedRevoluteSurfaceObj _ [meshRecord.linesOfLatitude,0,meshRecord]], assembly: assembly, normal: meshRecord.surfaces.bottom.normal, mo: mo, depth: avgDepth]]; psl _ CONS[thisSortedSurface, psl]; FOR i: NAT IN[1..meshRecord.linesOfLatitude-1] DO FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO SVPolygon3d.ClearPoly[poly3d]; jPlusOne _ IF j = meshRecord.linesOfLongitude THEN 1 ELSE j + 1; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][j]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][jPlusOne]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i+1][jPlusOne]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i+1][j]]; avgDepth _ AverageDepthInCamera[poly3d, localCS, cameraCS]; thisSortedSurface _ NEW[PlanarSurfaceObj _ [whichSurface: NEW[SortedRevoluteSurfaceObj _ [i,j,meshRecord]], assembly: assembly, normal: meshRecord.surfaces.sides[i][j].normal, mo: mo, depth: avgDepth]]; psl _ CONS[thisSortedSurface, psl]; ENDLOOP; ENDLOOP; }; -- end of PlanarSurfacesRevoluteSweep DrawPlanarSurfaceRevoluteSweep: PUBLIC PROC [dc: Imager.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera] = { thisRevSurface: SortedRevoluteSurface; meshRecord: RevoluteMesh; lat, long, longPlusOne: NAT; poly3d: Poly3d; avgDepth: REAL; shape: Shape; localCS: CoordSystem; thisRevSurface _ NARROW[ps.whichSurface]; meshRecord _ thisRevSurface.meshRecord; shape _ NARROW[ps.assembly.shape]; localCS _ shape.coordSys; lat _ thisRevSurface.lat; long _ thisRevSurface.long; -- we have surface [lat][long] avgDepth _ ps.depth; IF long = 0 THEN { -- either top or bottom surface IF lat = 0 THEN { -- top surface poly3d _ SVPolygon3d.CreatePoly[meshRecord.linesOfLongitude]; FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[1][j]]; ENDLOOP; SVGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, CoordSys.WRTWorld[localCS], CoordSys.WRTCamera[localCS, camera.coordSys]] } ELSE { poly3d _ SVPolygon3d.CreatePoly[meshRecord.linesOfLongitude]; FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[meshRecord.linesOfLatitude][j]]; ENDLOOP; SVGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, CoordSys.WRTWorld[localCS], CoordSys.WRTCamera[localCS, camera.coordSys]]}; } ELSE { -- Draw the side face poly3d _ SVPolygon3d.CreatePoly[4]; longPlusOne _ IF long = meshRecord.linesOfLongitude THEN 1 ELSE long + 1; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat][long]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat][longPlusOne]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat+1][longPlusOne]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat+1][long]]; SVGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, CoordSys.WRTWorld[localCS], CoordSys.WRTCamera[localCS, camera.coordSys]]; }; }; -- end of DrawPlanarSurfaceRevoluteSweep SortedToroidalSurface: TYPE = REF SortedToroidalSurfaceObj; SortedToroidalSurfaceObj: TYPE = RECORD [lat, long: NAT, meshRecord: ToroidalMesh]; CountPlanarSurfacesToroidalSweep: PUBLIC PROC [meshRecord: ToroidalMesh] RETURNS [len: NAT] = { len _ (meshRecord.linesOfLongitude)*(meshRecord.linesOfLatitude); }; CountVerticesToroidalSweep: PUBLIC PROC [meshRecord: ToroidalMesh] RETURNS [len: NAT] = { len _ meshRecord.linesOfLongitude*meshRecord.linesOfLatitude; }; PlanarSurfacesToroidalSweep: PUBLIC PROC [meshRecord: ToroidalMesh, assembly: Slice, cameraCS: CoordSystem] RETURNS [psl: PlanarSurfaceList] = { iPlusOne, jPlusOne: NAT; poly3d: Poly3d _ SVPolygon3d.CreatePoly[4]; avgDepth: REAL; shape: Shape _ NARROW[assembly.shape, Shape]; mo: MasterObject _ shape.mo; localCS: CoordSystem _ shape.coordSys; thisSortedSurface: PlanarSurface; FOR i: NAT IN[1..meshRecord.linesOfLatitude] DO iPlusOne _ IF i = meshRecord.linesOfLatitude THEN 1 ELSE i + 1; FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO SVPolygon3d.ClearPoly[poly3d]; jPlusOne _ IF j = meshRecord.linesOfLongitude THEN 1 ELSE j + 1; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][j]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][jPlusOne]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[iPlusOne][jPlusOne]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[iPlusOne][j]]; avgDepth _ AverageDepthInCamera[poly3d, localCS, cameraCS]; thisSortedSurface _ NEW[PlanarSurfaceObj _ [ whichSurface: NEW[SortedToroidalSurfaceObj _ [i,j,meshRecord]], assembly: assembly, normal: meshRecord.surfaces.sides[i][j].normal, mo: mo, depth: avgDepth]]; psl _ CONS[thisSortedSurface, psl]; ENDLOOP; ENDLOOP; }; -- end of PlanarSurfacesToroidalSweep DrawPlanarSurfaceToroidalSweep: PUBLIC PROC [dc: Imager.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera] = { lat, long, longPlusOne, latPlusOne: NAT; poly3d: Poly3d _ SVPolygon3d.CreatePoly[4]; avgDepth: REAL; shape: Shape; localCS: CoordSystem; meshRecord: ToroidalMesh; thisTorSurface: SortedToroidalSurface; thisTorSurface _ NARROW[ps.whichSurface]; shape _ NARROW[ps.assembly.shape]; localCS _ shape.coordSys; meshRecord _ thisTorSurface.meshRecord; lat _ thisTorSurface.lat; long _ thisTorSurface.long; -- we have surface [lat][long] avgDepth _ ps.depth; latPlusOne _ IF lat = meshRecord.linesOfLatitude THEN 1 ELSE lat + 1; longPlusOne _ IF long = meshRecord.linesOfLongitude THEN 1 ELSE long + 1; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat][long]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat][longPlusOne]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[latPlusOne][longPlusOne]]; poly3d _ SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[latPlusOne][long]]; SVGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, CoordSys.WRTWorld[localCS], CoordSys.WRTCamera[localCS, camera.coordSys]]; }; -- end of DrawPlanarSurfaceToroidalSweep END. File: SweepGeometryImpl.mesa Last edited by Bier on May 5, 1987 0:40:50 am PDT Contents: Implementation of a simple, straight-line, sweep geometry package for fast interactive graphics Now iterate over all surfaces and find their normals. front and back surfaces are trivial since we know there normals are aligned with the z axis. If len <=2, then there are no front and back surfaces. For simplicity, I make the path be clockwise when determining outward pointing normal. With linear sweep shapes, the [i][1] to [i][2] and [i][1] to [i+1][1] vectors are orthogonal. Their cross product is the surface normal. It will have a zero z component, initially. Add two points to path (the extensions of the first and last points to the y axis) and make it a polygon. Correct the data if necessary so all x's are positive. Then make sure the poly is clockwise. CREATE A NEW REVOLUTE SWEEP from the points in poly For each line of latitude, for each point of longitude, calculate its position in 3-space by "sweeping" the source point of array2d by the appropriate angle. FIND THE NORMALS. I assume for now that the shape was drawn top to bottom, when determing normals The top and bottom surfaces are trivial since their normals are aligned with the y-axis. Now, iterate over the side faces The [i][j] (upper-left corner) to the [i+1][j+1] (lower-right corner) diagonal vector and [i][j+1] (upper right) to the [i+1][j] (lower left) diagonal vector are two vectors lying in the plane. Their cross-product is a surface normal. Correct the data if necessary so all x's are positive. Then make sure the poly is clockwise. CREATE A NEW TOROIDAL SWEEP from the points in poly For each line of latitude, for each point of longitude, calculate its position in 3-space by "sweeping" the source point of array2d by the appropriate angle. FIND THE NORMALS. I assume for now that the shape was drawn top to bottom, when determing normals Iterate over the side faces The [i][j] (upper-left corner) to the [i+1][j+1] (lower-right corner) diagonal vector and [i][j+1] (upper right) to the [i+1][j] (lower left) diagonal vector are two vectors lying in the plane. Their cross-product is a surface normal. Use the front (linMesh.array[i][1]) polygon ignoring z ([3]) values. Ignore z values in the [i][revMesh.linesOfLongitude] elements cx, cy: REAL;(to draw debugging numbers at vertices) To draw a revolute sweep shape, we simple draw a line from each meshArray[i][j] to each four mesh neighbors meshArray[i-1][j], meshArray[i+1][j], meshArray[i][j-1], meshArray[i][j+1] with provision for wraparound at the extremes of j but not i. Draw Lines Of Latitude There has to be a better way to phrase the above loop. Wrap around to first point of this line of latitude There has to be a better way to phrase the above loop. Wrap around to first point of this line of latitude Draw Lines Of Longitude No wrap around for longitude To draw a toroidal sweep shape, we simple draw a line from each meshArray[i][j] to each four mesh neighbors meshArray[i-1][j], meshArray[i+1][j], meshArray[i][j-1], meshArray[i][j+1] with provision for wraparound at the extremes of i and j. Draw Lines Of Latitude Wrap around to first point of this line of latitude Draw Lines Of Longitude Draw front vector Draw bottom vector Draw side vectors We have the normal in local coordinates. Draw top normal Draw bottom normal Draw the side normals We have the normal in local coordinates. we have the normal in local coordinates. OUTPUT THE VERTEX DESCRIPTIONS f.PutF["v%g %g %g %g\n", IO.int[i], IO.real[meshRecord.array[i][1][1]], IO.real[meshRecord.array[i][1][2]], IO.real[meshRecord.array[i][1][3]] ]; f.PutF["v%g %g %g %g\n", IO.int[i+meshRecord.len], IO.real[meshRecord.array[i][2][1]], IO.real[meshRecord.array[i][2][2]], IO.real[meshRecord.array[i][2][3]] ]; OUTPUT THE FACE DESCRIPTIONS (Clockwise) Describe the front face. Describe the back face. Describe the side faces. OUTPUT THE VERTEX DESCRIPTIONS f.PutF["v%g.%g %g %g %g\n", IO.int[lat], IO.int[long], IO.real[meshRecord.array[lat][long][1]], IO.real[meshRecord.array[lat][long][2]], IO.real[meshRecord.array[lat][long][3]] ]; IO.int[(lat-1)*meshRecord.linesOfLongitude + long], OUTPUT THE FACE DESCRIPTIONS (Clockwise) Describe the top face f.PutF["v%g.%g ", IO.int[1], IO.int[j]]; Describe the bottom face f.PutF["v%g.%g ", IO.int[meshRecord.linesOfLatitude], IO.int[j]]; Describe the side faces f.PutF["f (v%g.%g v%g.%g ", IO.int[lat+1], IO.int[long], IO.int[lat+1], IO.int[longPlusOne]]; f.PutF["v%g.%g v%g.%g )\n", IO.int[lat], IO.int[longPlusOne], IO.int[lat], IO.int[long]]; OUTPUT THE VERTEX DESCRIPTIONS f.PutF["v%g.%g %g %g %g\n", IO.int[lat], IO.int[long], IO.real[meshRecord.array[lat][long][1]], IO.real[meshRecord.array[lat][long][2]], IO.real[meshRecord.array[lat][long][3]] ]; OUTPUT THE FACE DESCRIPTIONS (Clockwise) Describe the side faces f.PutF["f (v%g.%g v%g.%g ", IO.int[latPlusOne], IO.int[long], IO.int[latPlusOne], IO.int[longPlusOne]]; f.PutF["v%g.%g v%g.%g )\n", IO.int[lat], IO.int[longPlusOne], IO.int[lat], IO.int[long]]; Top is [0]. Bottom is [len+1]. Put front face on the list Put back face on the list Put side faces on the list. Put the top surface on the list Put the bottom surface on the list Put side surfaces on the list Draw the top surface Draw the bottom surface Put side surfaces on the list Draw the side face ส V– "Mesa" style˜Iheadšœ™Iprocšœ1™1Lšœi™iL˜šฯk ˜ Jšœœ}˜‘—L˜šœ ˜ Jšœ œE˜YLšœ˜—Lš˜˜Lšœœ˜!Lšœœ˜#Lšœ œ˜#Lšœ œ˜-Lšœ œœ ˜-Lšœœœœ ˜,Lšœ œœ˜(Lšœœ"˜8Lšœœ˜/Lšœœ ˜Lšœœœ˜+Lšœœ!˜7Lšœœ"˜9Lšœ œ˜Lšœ œ˜Lšœœ˜Lšœ œ˜Lšœœœ˜,Lšœœ$˜ œ˜FLšœ˜—Lšœ"˜"—L˜šž œœœœ˜NLšœ<˜<šœœœ˜$Lšœ*˜*Lšœ*˜*Lšœ˜—Lšœ˜Lšœ˜Lšœ˜—L˜šžœœœW˜zLšœ˜Lšœ3˜3Lšœ œ˜šœ˜Lšœ™—Lšœ,˜,Lšœ'˜'LšœE˜Ešœ>˜>Lšœ™—Lšœ+˜+LšœE˜Ešœ>˜>Lšœ™—šœœœ˜#Lšœ œœœ˜3Lšœ/˜/Lšœ/˜/Lšœ)˜)Lšœ:˜:šœ*˜*Lšœ(™(—Lšœ>˜>—Lšœ˜Lšœ˜—šžœœœY˜~Lšœ˜Lšœ3˜3šœœ˜Lšœ™—Lšœ*˜*Lšœ-˜-šœ>˜>Lšœ™—Lšœ-˜-LšœF˜Fšœ>˜>Lšœ™—šœœœ"˜1Lšœ˜šœœœ!˜0Lšœ œ!œœ˜@Lšœ2˜2Lšœ(˜(Lšœ7˜7Lšœ:˜:šœ*˜*Lšœ(™(—Lšœ>˜>—Lšœ˜—Lšœ˜Lšœ%˜%—šžœœœl˜‘Lšœ3˜3Lšœœ˜šœœœ ˜/Lšœ œ œœ˜?šœœœ!˜0Lšœ œ!œœ˜@Lšœ2˜2Lšœ(˜(Lšœ7˜7Lšœ:˜:Lšœ*˜*Lšœ(™(Lšœ>˜>—Lšœ˜—Lšœ˜Lšœ%˜%—L˜L˜šžœ œœœ˜FLšœ œ˜L™L™L™Lšœœ˜5šœœœ˜#šœ˜Lšœ!˜#Lšœ!˜#Lšœ#˜%—šœœ™$Lšœ!™#Lšœ!™#Lšœ#™%——Lšœ˜šœœœ˜#šœ˜Lšœ!˜#Lšœ!˜#Lšœ#˜%—šœœ™3Lšœ!™#Lšœ!™#Lšœ#™%——Lšœ˜Lšœ œœ˜L˜™(L˜—Lšœœ˜2šœ™Lšœ˜šœœœ˜#Lšœœ ˜—Lšœ˜Lšœ˜L˜—šœ™Lšœ˜šœœ œ˜.Lšœœ˜(—Lšœ˜Lšœ˜L˜—šœ™šœœœ˜&Lšœœœœ ˜<šœ˜Lšœ˜Lšœ!˜#Lšœ˜Lšœ ˜——Lšœ˜L˜—Lšœ œœ˜L˜LšœŸ˜L˜—š žœœœœœ˜JLšœ œ˜L˜L™L™Lšœœ>˜[šœœœ ˜1šœœœ!˜3šœ˜Lšœ&˜(Lšœ&˜(Lšœ(˜*—Lš œœ œ œ'œ'œ(™ดLšœ1™3—Lšœ˜—Lšœ˜Lšœ œœ˜L˜L™(L™LšœœD˜^šœ™Lšœ˜šœœ œ!˜;Lšœœ ˜Lšœœ œ ™(—Lšœ˜Lšœ˜—L™šœ™Lšœ˜šœœœ!˜0LšœœF˜VLšœœ"œ ™A—Lšœ˜Lšœ˜L˜—šœ™šœœœ"˜3šœœœ!˜3Lšœœ$œœ ˜Išœ˜Lšœ-˜/Lšœ5˜7—šœ˜Lšœ8˜:Lšœ2˜4—šœ™Lšœ œ ™Lšœ œ™$—šœ™Lšœ œ™!Lšœ œ ™——Lšœ˜—Lšœ˜Lšœ œœ˜—LšœŸ˜L˜—š žœœœœœ˜JLšœœ˜L˜L™L™Lšœœ>˜[šœœœ ˜1šœœœ!˜3šœ˜Lšœ&˜(Lšœ&˜(Lšœ(˜*—Lš œœ œ œ'œ'œ(™ด—Lšœ˜—Lšœ˜Lšœ œœ˜L˜™(L™Lšœ™—Lšœœ>˜Xšœœœ ˜1šœœœ!˜3Lšœ œ"œœ ˜ELšœœ$œœ ˜Išœ˜Lšœ8˜:Lšœ@˜B—šœ˜Lšœ8˜:Lšœ2˜4—šœ™Lšœœ ™!Lšœœ™)—šœ™Lšœ œ™!Lšœ œ ™——Lšœ˜—Lšœ˜LšœŸ˜L˜—L˜š žœœœœœ˜[Lšœ˜Lšœ˜—š žœœœœœ˜ULšœ˜Lšœ˜—L˜Lšœœœ˜7šœœœ˜'Lšœ œ œ ˜$Lšœœ˜ Lšœ˜Lšœ˜—L˜L˜šžœœœBœ˜ŒLšœ7˜7Lšœ œ˜Lšœ œ˜Lšœœ˜-Lšœ˜Lšœ&˜&šœ!˜!Lšœ™—˜Lšœ™—šœœœ˜#Lšœ>˜>Lšœ˜—Lšœ9˜9šœœ˜,Lšœœn˜Lšœ˜Lšœ)˜)Lšœ˜Lšœ˜—Lšœœ˜#˜Lšœ™—Lšœ˜šœœœ˜#Lšœ>˜>Lšœ˜—Lšœ9˜9šœœ˜,Lšœœ}˜ŽLšœ˜Lšœ(˜(Lšœ˜Lšœ˜—Lšœœ˜#˜Lšœ™—šœœœ˜#Lšœ˜Lšœ œœœ˜3Lšœ>˜>Lšœ>˜>LšœE˜ELšœE˜ELšœ9˜9šœœ˜,Lšœœn˜Lšœ˜Lšœ,˜,Lšœ˜Lšœ˜—Lšœœ˜#—Lšœ˜Lšœ&˜&L˜—šžœœœ[˜„Lšœ˜Lšœœ˜Lšœ œ˜Lšœ˜Lšœ$˜$L˜Lšœœ˜)Lšœ'˜'Lšœ˜Lšœ˜L˜Lšœ˜šœŸ˜Lšœ0˜0šœœœ˜#LšœB˜BLšœ˜—Lšœx œ œ˜›Lšœ˜—šœŸ˜)Lšœ0˜0šœœœ˜#LšœB˜BLšœ˜—Lšœx œ œ˜›Lšœ˜—šœŸ˜,Lšœ#˜#Lšœœœœ ˜