DIRECTORY SVCoordSys, Feedback, GGBoundBox, GGCoreOps, GGParseIn, GGParseOut, Imager, ImagerColor, IO, SVMatrix3d, SVPredefSweeps, Real, Rope, SV2d, SV3d, SVArtwork, SVAssembly, SVBasicTypes, SVBoundBox, SVDraw3d, SVFaces, SVGraphics, SVLines3d, SVMasterObject, SVMasterObjectTypes, SVModelTypes, SVObjectCast, SVPolygon3d, SVScene, SVSceneTypes, SVVector3d, SVSweepCast, SVSweepGeometry, SVParseIn, SVParseOut; SVMasterObjectImplC: CEDAR PROGRAM IMPORTS GGBoundBox, GGCoreOps, GGParseIn, GGParseOut, Imager, ImagerColor, IO, Rope, SVArtwork, SVAssembly, SVBoundBox, SVCoordSys, SVDraw3d, SVGraphics, SVLines3d, SVMasterObject, SVMatrix3d, SVObjectCast, SVParseIn, SVParseOut, SVPolygon3d, SVPredefSweeps, SVScene, SVSweepGeometry, SVVector3d EXPORTS SVMasterObject = BEGIN OPEN SVMasterObject; Artwork: TYPE = SVModelTypes.Artwork; ArtworkClass: TYPE = SVModelTypes.ArtworkClass; BoundBlock: TYPE = SVBasicTypes.BoundBlock; BoundBox: TYPE = SVBasicTypes.BoundBox; BoundHedron: TYPE = SVBasicTypes.BoundHedron; Box3d: TYPE = SVMasterObjectTypes.Box3d; Camera: TYPE = SVModelTypes.Camera; Classification: TYPE = SVSceneTypes.Classification; Color: TYPE = Imager.Color; Composite: TYPE = SVSceneTypes.Composite; CoordSystem: TYPE = SVModelTypes.CoordSystem; CoordSysList: TYPE = SVModelTypes.CoordSysList; Edge3d: TYPE = SV3d.Edge3d; Face: TYPE = REF FaceObj; FaceObj: TYPE = SVMasterObjectTypes.FaceObj; FeedbackData: TYPE = Feedback.FeedbackData; LightSourceList: TYPE = SVModelTypes.LightSourceList; Line3d: TYPE = SV3d.Line3d; LinearMesh: TYPE = REF LinearMeshRecord; LinearMeshArray: TYPE = SVSweepGeometry.LinearMeshArray; LinearMeshRecord: TYPE = SVSweepGeometry.LinearMeshRecord; MasterObject: TYPE = SVSceneTypes.MasterObject; MasterObjectClass: TYPE = SVSceneTypes.MasterObjectClass; MasterObjectClassList: TYPE = SVSceneTypes.MasterObjectClassList; MasterObjectClassObj: TYPE = SVSceneTypes.MasterObjectClassObj; MasterObjectList: TYPE = SVSceneTypes.MasterObjectList; Material: TYPE = SVModelTypes.Material; Matrix4by4: TYPE = SV3d.Matrix4by4; Path: TYPE = SV2d.Path; PlanarSurface: TYPE = SVSceneTypes.PlanarSurface; PlanarSurfaceList: TYPE = SVSceneTypes.PlanarSurfaceList; PlanarSurfaceObj: TYPE = SVSceneTypes.PlanarSurfaceObj; Plane: TYPE = REF PlaneObj; PlaneObj: TYPE = SV3d.PlaneObj; Point2d: TYPE = SV2d.Point2d; Point3d: TYPE = SV3d.Point3d; PointAndDone: TYPE = SVSceneTypes.PointAndDone; PointGenerator: TYPE = REF PointGeneratorObj; PointGeneratorObj: TYPE = SVSceneTypes.PointGeneratorObj; Poly3d: TYPE = SV3d.Poly3d; Polygon: TYPE = SV2d.Polygon; Primitive: TYPE = SVSceneTypes.Primitive; Ray: TYPE = SVSceneTypes.Ray; RectSurface: TYPE = REF RectSurfaceObj; RectSurfaceObj: TYPE = SVObjectCast.RectSurfaceObj; RevoFace: TYPE = SVSweepCast.RevoFace; Scene: TYPE = SVSceneTypes.Scene; Segment: TYPE = REF SegmentObj; SegmentObj: TYPE = SVMasterObjectTypes.SegmentObj; SelectionClass: TYPE = SVSceneTypes.SelectionClass; SelectMode: TYPE = SVSceneTypes.SelectMode; Shape: TYPE = SVSceneTypes.Shape; Slice: TYPE = SVSceneTypes.Slice; SliceDescriptor: TYPE = SVSceneTypes.SliceDescriptor; SliceParts: TYPE = SVSceneTypes.SliceParts; SubBoxesBody: TYPE = REF SubBoxesBodyObj; SubBoxesBodyObj: TYPE = SVSweepCast.SubBoxesBodyObj; SubSpheresBody: TYPE = REF SubSpheresBodyObj; SubSpheresBodyObj: TYPE = SVSweepCast.SubSpheresBodyObj; SurfaceArray: TYPE = REF SurfaceArrayObj; SurfaceArrayObj: TYPE = SVSceneTypes.SurfaceArrayObj; Vector3d: TYPE = SV3d.Vector3d; GetHedronProc: TYPE = SVSceneTypes.GetHedronProc; LineDrawProc: TYPE = SVSceneTypes.LineDrawProc; CountSurfProc: TYPE = SVSceneTypes.CountSurfProc; GetSurfProc: TYPE = SVSceneTypes.GetSurfProc; NormalsDrawProc: TYPE = SVSceneTypes.NormalsDrawProc; PreprocessProc: TYPE = SVSceneTypes.PreprocessProc; RayCastProc: TYPE = SVSceneTypes.RayCastProc; RayCastNoBBoxesProc: TYPE = SVSceneTypes.RayCastNoBBoxesProc; RayCastBoundingSpheresProc: TYPE = SVSceneTypes.RayCastBoundingSpheresProc; DrawPlanarSurfaceProc: TYPE = SVSceneTypes.DrawPlanarSurfaceProc; DrawSubBoxesProc: TYPE = SVSceneTypes.DrawSubBoxesProc; DrawSubSpheresProc: TYPE = SVSceneTypes.DrawSubSpheresProc; FileinProc: TYPE = SVSceneTypes.FileinProc; FileoutProc: TYPE = SVSceneTypes.FileoutProc; FileoutPolyProc: TYPE = SVSceneTypes.FileoutPolyProc; PointsInDescriptorProc: TYPE = SVSceneTypes.PointsInDescriptorProc; NextPointProc: TYPE = SVSceneTypes.NextPointProc; UpdateProc: TYPE = SVSceneTypes.UpdateProc; BuildBlockClass: PUBLIC PROC [] RETURNS [class: MasterObjectClass] = { class _ NEW[MasterObjectClassObj _ [ name: "block", getHedron: BlockBoundHedron, copy: BlockCopy, lineDraw: BlockLineDraw, lineDrawTransform: BlockLineDrawTransform, countSurf: BlockCountSurf, getSurf: BlockGetSurf, getSurfTransform: BlockGetSurfTransform, normalsDraw: BlockDrawNormals, preprocess: BlockPreprocess, rayCast: BlockRayCast, rayCastNoBBoxes: BlockRayCastNoBBoxes, rayCastBoundingSpheres: BlockRayCastBoundingSpheres, drawSurf: BlockDrawSurf, drawSubBoxes: NoOpDrawSubBoxes, drawSubSpheres: NoOpDrawSubSpheres, drawSelectionFeedback: BlockDrawSelectionFeedback, drawAttractorFeedback: NoOpDrawAttractorFeedback, transform: BlockTransform, describe: BlockDescribe, describeHit: BlockDescribeHit, filein: BlockFilein, fileout: BlockFileout, fileoutPoly: BlockFileoutPoly, isEmptyParts: BlockIsEmptyParts, isCompleteParts: BlockIsCompleteParts, newParts: BlockNewParts, unionParts: BlockUnionParts, differenceParts: BlockDifferenceParts, movingParts: BlockMovingParts, augmentParts: BlockAugmentParts, pointsInDescriptor: BlockPointsInDescriptor, nextPoint: BlockNextPoint, pointPairsInDescriptor: NoOpPointPairsInDescriptor, nextPointPair: NoOpNextPointPair, closestPointToPoint: BlockClosestPointToPoint, closestPointToLine: BlockClosestPointToLine, closestSegmentToLine: BlockClosestSegmentToLine, withinBoundBlock: BlockWithinBoundBlock, update: BlockUpdate, setStrokeColor: BlockSetStrokeColor, getStrokeColor: BlockGetStrokeColor, setFillColor: BlockSetFillColor, getFillColor: BlockGetFillColor ]]; }; BlockData: TYPE = REF BlockDataObj; BlockDataObj: TYPE = SVMasterObjectTypes.BlockDataObj; BlockHitData: TYPE = REF BlockHitDataObj; BlockHitDataObj: TYPE = SVMasterObject.BlockHitDataObj; BlockParts: TYPE = REF BlockPartsObj; BlockPartsObj: TYPE = RECORD [ corners: CornerArray, -- which corners of box are selected. centers: CenterArray, -- is the middle joint selected? centroid: BOOL, edges: EdgeArray, -- which edges of box are selected. faces: FaceArray ]; CornerArray: TYPE = ARRAY [0..8) OF BOOL; -- top (lf, lb, rb, rf), bottom (lf, lb, rb, rf) CenterArray: TYPE = ARRAY [0..6) OF BOOL; EdgeArray: TYPE = ARRAY [0..12) OF BOOL; FaceArray: TYPE = ARRAY [0..6) OF BOOL; BoxSurface: TYPE = REF BoxSurfaceObj; BoxSurfaceObj: TYPE = RECORD [ faceNum: [0..5], blockData: BlockData, localWorld, localCamera: Matrix4by4 ]; BlockCopy: PROC [mo: MasterObject, newName: Rope.ROPE] RETURNS [newMO: MasterObject] = { blockData: BlockData _ NARROW[mo.mainBody]; box: Box3d _ blockData.box; newBlockData: BlockData; newMO _ BlockMakeMasterObject[newName, box.loX, box.loY, box.loZ, box.hiX, box.hiY, box.hiZ]; newBlockData _ NARROW[newMO.mainBody]; FOR i: NAT IN [0..11] DO newBlockData.segments[i].color _ blockData.segments[i].color; ENDLOOP; FOR i: NAT IN [0..5] DO newBlockData.faces[i].artwork _ SVArtwork.Copy[blockData.faces[i].artwork]; ENDLOOP; }; BlockBody: PROC [loX, loY, loZ, hiX, hiY, hiZ: REAL] RETURNS [blockData: BlockData] = { artwork: Artwork _ SVArtwork.CreateColorArtwork[ImagerColor.ColorFromRGB[[.6,.6,.8]], plastic]; blockData _ NEW[BlockDataObj]; blockData.box.loX _ loX; blockData.box.loY _ loY; blockData.box.loZ _ loZ; blockData.box.hiX _ hiX; blockData.box.hiY _ hiY; blockData.box.hiZ _ hiZ; FOR i: NAT IN [0..6) DO blockData.faces[i] _ NEW[FaceObj]; blockData.faces[i].artwork _ artwork; ENDLOOP; FOR i: NAT IN [0..12) DO blockData.segments[i] _ NEW[SegmentObj]; blockData.segments[i].edge _ SVLines3d.CreateEmptyEdge[]; blockData.segments[i].color _ NIL; ENDLOOP; }; BlockMakeMasterObject: PUBLIC PROC [name: Rope.ROPE, loX, loY, loZ: REAL _ -1.0, hiX, hiY, hiZ: REAL _ 1.0] RETURNS [mo: MasterObject] = { mainBody: BlockData _ BlockBody[loX, loY, loZ, hiX, hiY, hiZ]; lineBody: REF ANY _ mainBody; shadeBody: REF ANY _ SVPredefSweeps.GetUnitCube[]; rayCastBody: REF ANY _ BlockGetRayCastBody[]; mo _ SVScene.CreateMasterObject[name, blockClass, mainBody, lineBody, shadeBody, rayCastBody]; }; MakeBlockSlice: PUBLIC PROC [name: Rope.ROPE, loX, loY, loZ: REAL _ -1.0, hiX, hiY, hiZ: REAL _ 1.0, corner: [0..7]] RETURNS [sliceParts: SliceParts, mo: MasterObject] = { blockParts: BlockParts; mo _ BlockMakeMasterObject[name, loX, loY, loZ, hiX, hiY, hiZ]; sliceParts _ blockParts _ NEW[BlockPartsObj _ [corners: ALL[FALSE], centers: ALL[FALSE], centroid: FALSE, edges: ALL[FALSE], faces: ALL[FALSE]]]; blockParts.corners[corner] _ TRUE; }; BlockGetFillArtwork: PUBLIC PROC [surf: Surface, mo: MasterObject] RETURNS [artwork: Artwork] = { hitData: BlockHitData _ NARROW[surf]; faceNum: [0..5] _ hitData.face; blockData: BlockData _ NARROW[mo.mainBody]; artwork _ blockData.faces[faceNum].artwork; }; BlockGetRayCastBody: PROC [] RETURNS [surfaceArray: SurfaceArray] = { surfaceArray _ NEW[SurfaceArrayObj]; FOR i: NAT IN [0..5] DO surfaceArray[i+1] _ NEW[BlockHitDataObj _ [face: i]]; ENDLOOP; }; -- end of BlockGetRayCastBody BlockPreprocess: PROC [prim: Primitive, camera: Camera] = { mo: MasterObject _ prim.mo; surfaceArray: SurfaceArray _ NARROW[mo.rayCastBody]; stillD: SliceDescriptor _ prim.sliceD; stillParts: BlockParts _ NARROW[stillD.parts]; surface: BlockHitData; FOR i: NAT IN [0..5] DO surface _ NARROW[surfaceArray[i+1]]; surface.still _ IF stillParts = NIL THEN FALSE ELSE stillParts.faces[i]; ENDLOOP; }; -- end of BlockGetRayCastBody BlockBoundHedron: PUBLIC PROC [mo: MasterObject] RETURNS [hedron: BoundHedron] = { blockData: BlockData _ NARROW[mo.mainBody]; box: Box3d _ blockData.box; hedron _ SVBoundBox.RectangularBoundHedron2[box.loX, box.hiX, box.loY, box.hiY, box.loZ, box.hiZ]; }; GetBox3dPoint: PROC [box: Box3d, vert: NAT] RETURNS [point: Point3d] = { point _ SELECT vert FROM 0 => [box.loX, box.hiY, box.hiZ], 1 => [box.loX, box.hiY, box.loZ], 2 => [box.hiX, box.hiY, box.loZ], 3 => [box.hiX, box.hiY, box.hiZ], 4 => [box.loX, box.loY, box.hiZ], 5 => [box.loX, box.loY, box.loZ], 6 => [box.hiX, box.loY, box.loZ], 7 => [box.hiX, box.loY, box.hiZ], ENDCASE => ERROR }; GetBox3dCentroid: PROC [box: Box3d] RETURNS [point: Point3d] = { point[1] _ (box.loX+box.hiX)/2.0; point[2] _ (box.loY+box.hiY)/2.0; point[3] _ (box.loZ+box.hiZ)/2.0; }; ExtremeDepthsInCamera: PROC [poly: Poly3d] RETURNS [frontDepth, backDepth, avgDepth: REAL] = { sum: REAL _ 0; realLen: REAL _ poly.len; thisDepth: REAL; pointCamera: Point3d; frontDepth _ -Real.LargestNumber; backDepth _ Real.LargestNumber; FOR i: NAT IN[0..poly.len) DO pointCamera _ poly[i]; thisDepth _ pointCamera[3]; frontDepth _ MAX[frontDepth, thisDepth]; backDepth _ MIN[backDepth, thisDepth]; sum _ sum + thisDepth; ENDLOOP; avgDepth _ sum/realLen; }; GetSurfFromBox: PROC [box: Box3d, slice: Slice, cameraCS: CoordSystem, eyeWorld: Point3d] RETURNS [psl: PlanarSurfaceList] = { BuildSurf: PROC [slice: Slice, shapeCS: CoordSystem, mo: MasterObject, cameraCS: CoordSystem, normal: Vector3d, box: Box3d, faceNum: [0..5], v: ARRAY[0..3] OF NAT] RETURNS [ps: PlanarSurface, backfacing: BOOL _ FALSE] = { poly: Poly3d; avgDepth, frontDepth, backDepth: REAL; pointCamera: Point3d; cameraNormal: Vector3d; plane: Plane; boxSurface: BoxSurface; BEGIN OPEN SVMatrix3d, SVVector3d; worldPoint: Point3d; worldNormal: Vector3d _ UpdateVectorWithInverse[worldShape, normal]; worldPoint _ SVGraphics.LocalToWorld[GetBox3dPoint[box, v[0]], cameraCS]; IF DotProduct[Sub[worldPoint, eyeWorld], worldNormal] >=0 THEN RETURN[NIL, TRUE]; END; poly _ SVPolygon3d.CreatePoly[4]; FOR i: NAT IN [0..3] DO pointCamera _ SVGraphics.LocalToCamera[GetBox3dPoint[box, v[i]], shapeCS, cameraCS]; poly _ SVPolygon3d.AddPolyPoint[poly, pointCamera]; ENDLOOP; [frontDepth, backDepth, avgDepth] _ ExtremeDepthsInCamera[poly]; plane _ SVPolygon3d.PlaneFromPoly3d[poly]; boxSurface _ NEW[BoxSurfaceObj _ [faceNum, blockData, SVCoordSys.WRTWorld[shapeCS], SVCoordSys.WRTCamera[shapeCS, cameraCS]]]; cameraNormal _ SVMatrix3d.UpdateVectorWithInverse[cameraShape, normal]; ps _ NEW[PlanarSurfaceObj _ [ whichSurface: boxSurface, assembly: slice, normal: cameraNormal, plane: plane, poly: poly, mo: mo, depth: avgDepth, frontDepth: frontDepth, backDepth: backDepth]]; }; verts: ARRAY[0..3] OF NAT; shape: Shape _ NARROW[slice.shape]; shapeCS: CoordSystem _ shape.coordSys; worldShape: Matrix4by4 _ SVCoordSys.FindWorldInTermsOf[shapeCS]; cameraShape: Matrix4by4 _ SVCoordSys.FindCameraInTermsOf[shapeCS, cameraCS]; mo: MasterObject _ shape.mo; blockData: BlockData _ NARROW[mo.mainBody]; normal: Vector3d; artwork: Artwork; surface: PlanarSurface; backfacing: BOOL _ FALSE; psl _ NIL; FOR i: NAT IN [0..5] DO verts _ CornersOfFace[i]; normal _ blockNormals[i]; artwork _ blockData.faces[i].artwork; [surface, backfacing] _ BuildSurf[slice, shapeCS, mo, cameraCS, normal, box, i, verts]; IF NOT backfacing THEN psl _ CONS[surface, psl]; ENDLOOP; }; GetSurfFromBoxTransform: PROC [box: Box3d, slice: Slice, cameraCS: CoordSystem, transform: Matrix4by4, eyeWorld: Point3d] RETURNS [psl: PlanarSurfaceList] = { BuildSurfTransform: PROC [slice: Slice, shapeCS: CoordSystem, mo: MasterObject, cameraCS: CoordSystem, normal: Vector3d, box: Box3d, faceNum: [0..5], v: ARRAY[0..3] OF NAT, t: Matrix4by4] RETURNS [ps: PlanarSurface, backfacing: BOOL] = { poly: Poly3d _ SVPolygon3d.CreatePoly[4]; avgDepth, frontDepth, backDepth: REAL; boxSurface: BoxSurface; pointCamera: Point3d; cameraNormal: Vector3d; plane: Plane; BEGIN OPEN SVMatrix3d, SVVector3d; worldPoint: Point3d; worldNormal: Vector3d _ UpdateVectorWithInverse[worldShape, normal]; worldPoint _ SVGraphics.LocalToWorld[SVMatrix3d.Update[GetBox3dPoint[box, v[0]], t], cameraCS]; IF DotProduct[Sub[worldPoint, eyeWorld], worldNormal] >=0 THEN RETURN[NIL, TRUE]; END; FOR i: NAT IN [0..3] DO pointCamera _ SVGraphics.LocalToCamera[SVMatrix3d.Update[GetBox3dPoint[box, v[i]], t], shapeCS, cameraCS]; poly _ SVPolygon3d.AddPolyPoint[poly, pointCamera]; ENDLOOP; [frontDepth, backDepth, avgDepth] _ ExtremeDepthsInCamera[poly]; plane _ SVPolygon3d.PlaneFromPoly3d[poly]; boxSurface _ NEW[BoxSurfaceObj _ [faceNum, blockData, SVCoordSys.WRTWorld[shapeCS], SVCoordSys.WRTCamera[shapeCS, cameraCS]]]; cameraNormal _ SVMatrix3d.UpdateVectorWithInverse[cameraShape, normal]; ps _ NEW[PlanarSurfaceObj _ [ whichSurface: boxSurface, assembly: slice, normal: cameraNormal, plane: plane, poly: poly, mo: mo, depth: avgDepth, frontDepth: frontDepth, backDepth: backDepth]]; }; verts: ARRAY[0..3] OF NAT; shape: Shape _ NARROW[slice.shape]; shapeCS: CoordSystem _ shape.coordSys; worldShape: Matrix4by4 _ SVCoordSys.FindWorldInTermsOf[shapeCS]; cameraShape: Matrix4by4 _ SVCoordSys.FindCameraInTermsOf[shapeCS, cameraCS]; inverseT: Matrix4by4 _ SVMatrix3d.Inverse[transform]; mo: MasterObject _ shape.mo; blockData: BlockData _ NARROW[mo.mainBody]; normal: Vector3d; artwork: Artwork; surface: PlanarSurface; backfacing: BOOL _ FALSE; psl _ NIL; FOR i: NAT IN [0..5] DO verts _ CornersOfFace[i]; normal _ blockNormals[i]; normal _ SVMatrix3d.UpdateVectorComputeInverse[vec: normal, mat: transform]; artwork _ blockData.faces[i].artwork; [surface, backfacing] _ BuildSurfTransform[slice, shapeCS, mo, cameraCS, normal, box, i, verts, transform]; IF NOT backfacing THEN psl _ CONS[surface, psl]; ENDLOOP; }; DrawBoxSurf: PROC [dc: Imager.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera, hiddenLine: BOOL] = { boxSurface: BoxSurface _ NARROW[ps.whichSurface]; poly: Poly3d _ ps.poly; artwork: Artwork _ boxSurface.blockData.faces[boxSurface.faceNum].artwork; strokeColor: Color _ boxSurface.blockData.segments[0].color; SVGraphics.DrawAreaAbsolute[dc, ps.normal, poly, artwork, lightSources, camera, hiddenLine, strokeColor]; }; DrawWireBox3d: PROC [dc: Imager.Context, box: Box3d, camera: Camera, localCamera: Matrix4by4] = { DrawRect: PROC [dc: Imager.Context, box: Box3d, v: ARRAY[0..3] OF NAT] = { SVGraphics.SetCP[dc, GetBox3dPoint[box, v[0]], camera, localCamera]; SVGraphics.DrawTo[dc, GetBox3dPoint[box, v[1]], camera, localCamera]; SVGraphics.DrawTo[dc, GetBox3dPoint[box, v[2]], camera, localCamera]; SVGraphics.DrawTo[dc, GetBox3dPoint[box, v[3]], camera, localCamera]; SVGraphics.DrawTo[dc, GetBox3dPoint[box, v[0]], camera, localCamera]; }; verts: ARRAY[0..3] OF NAT; FOR i: NAT IN [0..5] DO verts _ CornersOfFace[i]; DrawRect[dc, box, verts]; ENDLOOP; }; BlockLineDraw: PUBLIC PROC [slice: Slice, dc: Imager.Context, camera: Camera] = { shape: Shape _ NARROW[slice.shape]; localCS: CoordSystem _ shape.coordSys; mo: MasterObject _ shape.mo; blockData: BlockData _ NARROW[mo.lineBody]; localCamera: Matrix4by4 _ SVCoordSys.WRTCamera[localCS, camera.coordSys]; DrawWireBox3d[dc, blockData.box, camera, localCamera]; }; BlockLineDrawTransform: PUBLIC PROC [sliceD: SliceDescriptor, dc: Imager.Context, camera: Camera, transform: Matrix4by4] = { slice: Slice _ sliceD.slice; shape: Shape _ NARROW[slice.shape]; shapeCS: CoordSystem _ shape.coordSys; localCamera: Matrix4by4 _ SVCoordSys.WRTCamera[shapeCS, camera.coordSys]; localWorld: Matrix4by4 _ SVCoordSys.WRTWorld[shapeCS]; worldCamera: Matrix4by4 _ SVCoordSys.FindWorldInTermsOf[camera.coordSys]; mo: MasterObject _ NARROW[slice.shape, Shape].mo; blockData: BlockData _ NARROW[mo.lineBody]; tempBox: Box3d; BEGIN fullTransform: BOOL _ FALSE; IF BlockIsCompleteParts[sliceD] THEN GOTO FullTransform; [tempBox, fullTransform] _ TransformedBox[sliceD, blockData, localWorld, transform]; IF fullTransform THEN GOTO FullTransform ELSE DrawWireBox3d[dc, tempBox, camera, localCamera]; EXITS FullTransform => { DrawWireBox3d[dc, blockData.box, camera, SVMatrix3d.Cat[localWorld, transform, worldCamera]]; }; END; }; TranslationInShape: PROC [transform, worldShape: Matrix4by4] RETURNS [tShape: Vector3d] = { tWorld: Vector3d; tWorld _ SVMatrix3d.OriginOfMatrix[transform]; tShape _ SVMatrix3d.UpdateDisplacement[worldShape, tWorld]; }; TransformedBox: PROC [sliceD: SliceDescriptor, blockData: BlockData, shapeWorld: Matrix4by4, transform: Matrix4by4] RETURNS [tBox: Box3d, fullTransform: BOOL _ FALSE] = { blockParts: BlockParts _ NARROW[sliceD.parts]; faceCount, faceNum, edgeCount, edgeNum, cornerCount, cornerNum: INTEGER; thisP, opP: Point3d; worldShape: Matrix4by4 _ SVMatrix3d.Inverse[shapeWorld]; shapeShape: Matrix4by4 _ SVMatrix3d.Cat[shapeWorld, transform, worldShape]; BEGIN [faceCount, faceNum] _ CountFaces[blockParts.faces]; [edgeCount, edgeNum] _ CountEdges[blockParts.edges]; [cornerCount, cornerNum] _ CountCorners[blockParts.corners]; IF faceCount > 1 OR edgeCount > 4 OR cornerCount > 4 THEN GOTO FullTransform; IF faceCount = 0 AND edgeCount = 0 AND cornerCount = 1 THEN { thisP _ GetBox3dPoint[blockData.box, cornerNum]; opP _ GetBox3dPoint[blockData.box, OppositeCorner[cornerNum]]; thisP _ SVMatrix3d.Update[thisP, shapeShape]; } ELSE IF faceCount = 0 AND edgeCount = 1 AND cornerCount = 2 THEN { tShape: Vector3d _ TranslationInShape[transform, worldShape]; lo, hi: NAT; [lo, hi] _ CornersOfEdge[edgeNum]; thisP _ GetBox3dPoint[blockData.box, lo]; opP _ GetBox3dPoint[blockData.box, OppositeCorner[lo]]; SELECT edgeNum FROM 0, 2, 6, 4 => -- front to back -- thisP _ [thisP[1] + tShape[1], thisP[2] + tShape[2], thisP[3]]; 3, 1, 5, 7 => -- left to right -- thisP _ [thisP[1], thisP[2] + tShape[2], thisP[3] + tShape[3]]; 8, 9, 10, 11 => -- top to bottom -- thisP _ [thisP[1] + tShape[1], thisP[2], thisP[3] + tShape[3]]; ENDCASE => ERROR; } ELSE IF faceCount = 1 AND edgeCount = 4 AND cornerCount = 4 THEN { tShape: Vector3d _ TranslationInShape[transform, worldShape]; corners: ARRAY[0..3] OF NAT; corners _ CornersOfFace[faceNum]; thisP _ GetBox3dPoint[blockData.box, corners[0]]; opP _ GetBox3dPoint[blockData.box, OppositeCorner[corners[0]]]; SELECT faceNum FROM 0, 1 => -- top and bottom -- thisP _ [thisP[1], thisP[2] + tShape[2], thisP[3]]; 2, 4 => -- left and right -- thisP _ [thisP[1] + tShape[1], thisP[2], thisP[3]]; 3, 5 => -- top and bottom -- thisP _ [thisP[1], thisP[2], thisP[3] + tShape[3]]; ENDCASE => ERROR; } ELSE GOTO FullTransform; EXITS FullTransform => { fullTransform _ TRUE; thisP _ GetBox3dPoint[blockData.box, 0]; opP _ GetBox3dPoint[blockData.box, OppositeCorner[0]]; thisP _ SVMatrix3d.Update[thisP, shapeShape]; opP _ SVMatrix3d.Update[opP, shapeShape]; }; END; tBox.loX _ MIN[thisP[1], opP[1]]; tBox.hiX _ MAX[thisP[1], opP[1]]; tBox.loY _ MIN[thisP[2], opP[2]]; tBox.hiY _ MAX[thisP[2], opP[2]]; tBox.loZ _ MIN[thisP[3], opP[3]]; tBox.hiZ _ MAX[thisP[3], opP[3]]; }; BlockCountSurf: PUBLIC PROC [masterObject: MasterObject] RETURNS [len: NAT] = { linMesh: LinearMesh _ NARROW[masterObject.shadeBody]; len _ SVSweepGeometry.CountPlanarSurfacesLinearSweep[linMesh]; }; BlockCountVert: PUBLIC PROC [masterObject: MasterObject] RETURNS [len: NAT] = { linMesh: LinearMesh _ NARROW[masterObject.shadeBody]; len _ SVSweepGeometry.CountVerticesLinearSweep[linMesh]; }; BlockGetSurf: PUBLIC PROC [slice: Slice, camera: CoordSystem, eyeWorld: Point3d] RETURNS [psl: PlanarSurfaceList] = { shape: Shape _ NARROW[slice.shape]; mo: MasterObject _ shape.mo; blockData: BlockData _ NARROW[mo.mainBody]; psl _ GetSurfFromBox[blockData.box, slice, camera, eyeWorld]; }; BlockGetSurfTransform: PUBLIC PROC [sliceD: SliceDescriptor, camera: CoordSystem, eyeWorld: Point3d, transform: Matrix4by4] RETURNS [psl: PlanarSurfaceList] = { shape: Shape _ NARROW[sliceD.slice.shape]; mo: MasterObject _ shape.mo; blockData: BlockData _ NARROW[mo.mainBody]; shapeWorld: Matrix4by4 _ SVCoordSys.WRTWorld[shape.coordSys]; tBox: Box3d; fullTransform: BOOL _ TRUE; [tBox, fullTransform] _ TransformedBox[sliceD, blockData, shapeWorld, transform]; IF fullTransform THEN { worldShape: Matrix4by4 _ SVMatrix3d.Inverse[shapeWorld]; shapeShape: Matrix4by4 _ SVMatrix3d.Cat[shapeWorld, transform, worldShape]; psl _ GetSurfFromBoxTransform[blockData.box, sliceD.slice, camera, shapeShape, eyeWorld]; } ELSE psl _ GetSurfFromBox[tBox, sliceD.slice, camera, eyeWorld]; }; BlockDrawNormals: PUBLIC PROC[dc: Imager.Context, data: REF ANY, camera: Camera, localCS: CoordSystem] = { linMesh: LinearMesh; IF ISTYPE[data, LinearMesh] THEN linMesh _ NARROW[data] ELSE SIGNAL SVMasterObject.WrongTypeOfData; SVSweepGeometry.DrawNormalsLinearSweep[dc, linMesh, camera, localCS]; }; BlockRayCast: PUBLIC PROC [cameraPoint: Point2d, localRay: Ray, sliceD: SliceDescriptor, prim: Primitive, positiveTOnly: BOOL _ TRUE] RETURNS [class: Classification] = { mo: MasterObject _ prim.mo; blockData: BlockData _ NARROW[mo.mainBody]; RETURN[SVObjectCast.BlockCast[localRay, prim, blockData, NARROW[mo.rayCastBody], positiveTOnly]]; }; BlockRayCastNoBBoxes: PUBLIC PROC [localRay: Ray, sliceD: SliceDescriptor, prim: Primitive, positiveTOnly: BOOL _ TRUE] RETURNS [class: Classification] = { mo: MasterObject _ prim.mo; blockData: BlockData _ NARROW[mo.mainBody]; RETURN[SVObjectCast.BlockCast[localRay, prim, blockData, NARROW[mo.rayCastBody], positiveTOnly]]; }; BlockRayCastBoundingSpheres: PUBLIC PROC [localRay: Ray, sliceD: SliceDescriptor, prim: Primitive, positiveTOnly: BOOL _ TRUE] RETURNS [class: Classification] = { mo: MasterObject _ prim.mo; blockData: BlockData _ NARROW[mo.mainBody]; RETURN[SVObjectCast.BlockCast[localRay, prim, blockData, NARROW[mo.rayCastBody], positiveTOnly]]; }; BlockDrawSurf: PUBLIC PROC [dc: Imager.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera, eyeWorld: Point3d, hiddenLine: BOOL] = { DrawBoxSurf[dc, ps, lightSources, camera, hiddenLine]; }; BlockDrawSelectionFeedback: PUBLIC PROC [slice: Slice, selectedParts: SliceParts, hotParts: SliceParts, dc: Imager.Context, camera: Camera, dragInProgress, caretIsMoving, hideHot, quick: BOOL] = { DoDrawFeedback: PROC = { box: Box3d _ blockData.box; cc: Point3d; thisCPisHot, thisCPisSelected: BOOL; pts: ARRAY[0..7] OF Point3d; FOR i: NAT IN [0..7] DO pts[i] _ SVMatrix3d.Update[GetBox3dPoint[box, i], blockWORLD]; ENDLOOP; FOR corner: INTEGER IN [0..7] DO thisCPisHot _ hotBlockParts#NIL AND hotBlockParts.corners[corner]; thisCPisSelected _ normalBlockParts#NIL AND normalBlockParts.corners[corner]; IF thisCPisHot THEN SVDraw3d.DrawSelectedJoint[dc, pts[corner], camera, hot]; IF thisCPisSelected THEN SVDraw3d.DrawSelectedJoint[dc, pts[corner], camera, normal]; IF NOT thisCPisHot AND NOT thisCPisSelected THEN SVDraw3d.DrawCP[dc, pts[corner], camera]; ENDLOOP; thisCPisHot _ hotBlockParts#NIL AND hotBlockParts.centroid; thisCPisSelected _ normalBlockParts#NIL AND normalBlockParts.centroid; cc _ SVMatrix3d.Update[[(box.loX+box.hiX)/2.0, (box.loY+box.hiY)/2.0, (box.loZ+box.hiZ)/2.0], blockWORLD]; IF thisCPisHot THEN SVDraw3d.DrawSelectedJoint[dc, cc, camera, hot]; IF thisCPisSelected THEN SVDraw3d.DrawSelectedJoint[dc, cc, camera, normal]; IF NOT thisCPisHot AND NOT thisCPisSelected THEN SVDraw3d.DrawCP[dc, cc, camera]; }; shape: Shape _ NARROW[slice.shape]; blockData: BlockData _ NARROW[shape.mo.mainBody]; blockWORLD: Matrix4by4 _ SVCoordSys.WRTWorld[shape.coordSys]; normalBlockParts, hotBlockParts: BlockParts; IF caretIsMoving OR dragInProgress THEN RETURN; IF selectedParts = NIL AND hotParts = NIL THEN RETURN; normalBlockParts _ NARROW[selectedParts]; hotBlockParts _ NARROW[hotParts]; IF camera.quality#quality THEN Imager.DoSave[dc, DoDrawFeedback]; }; BlockTransform: PROC [sliceD: SliceDescriptor, scene: Scene, transform: Matrix4by4] = { slice: Slice _ sliceD.slice; shape: Shape _ NARROW[slice.shape]; thisP, opP: Point3d; shapeCS: CoordSystem _ shape.coordSys; shapeWorld: Matrix4by4 _ SVCoordSys.WRTWorld[shapeCS]; mo: MasterObject _ NARROW[slice.shape, Shape].mo; blockData: BlockData _ NARROW[mo.mainBody]; blockParts: BlockParts _ NARROW[sliceD.parts]; faceCount, faceNum, edgeCount, edgeNum, cornerCount, cornerNum: INTEGER; BEGIN IF BlockIsCompleteParts[sliceD] THEN GOTO FullTransform; [faceCount, faceNum] _ CountFaces[blockParts.faces]; [edgeCount, edgeNum] _ CountEdges[blockParts.edges]; [cornerCount, cornerNum] _ CountCorners[blockParts.corners]; IF faceCount > 1 OR edgeCount > 4 OR cornerCount > 4 THEN GOTO FullTransform; IF faceCount = 0 AND edgeCount = 0 AND cornerCount = 1 THEN { worldShape: Matrix4by4 _ SVMatrix3d.Inverse[shapeWorld]; shapeShape: Matrix4by4 _ SVMatrix3d.Cat[shapeWorld, transform, worldShape]; thisP _ GetBox3dPoint[blockData.box, cornerNum]; opP _ GetBox3dPoint[blockData.box, OppositeCorner[cornerNum]]; thisP _ SVMatrix3d.Update[thisP, shapeShape]; } ELSE IF faceCount = 0 AND edgeCount = 1 AND cornerCount = 2 THEN { worldShape: Matrix4by4 _ SVMatrix3d.Inverse[shapeWorld]; tShape: Vector3d _ TranslationInShape[transform, worldShape]; lo, hi: NAT; [lo, hi] _ CornersOfEdge[edgeNum]; thisP _ GetBox3dPoint[blockData.box, lo]; opP _ GetBox3dPoint[blockData.box, OppositeCorner[lo]]; SELECT edgeNum FROM 0, 2, 6, 4 => -- front to back -- thisP _ [thisP[1] + tShape[1], thisP[2] + tShape[2], thisP[3]]; 3, 1, 5, 7 => -- left to right -- thisP _ [thisP[1], thisP[2] + tShape[2], thisP[3] + tShape[3]]; 8, 9, 10, 11 => -- top to bottom -- thisP _ [thisP[1] + tShape[1], thisP[2], thisP[3] + tShape[3]]; ENDCASE => ERROR; } ELSE IF faceCount = 1 AND edgeCount = 4 AND cornerCount = 4 THEN { worldShape: Matrix4by4 _ SVMatrix3d.Inverse[shapeWorld]; tShape: Vector3d _ TranslationInShape[transform, worldShape]; corners: ARRAY[0..3] OF NAT; corners _ CornersOfFace[faceNum]; thisP _ GetBox3dPoint[blockData.box, corners[0]]; opP _ GetBox3dPoint[blockData.box, OppositeCorner[corners[0]]]; SELECT faceNum FROM 0, 1 => -- top and bottom -- thisP _ [thisP[1], thisP[2] + tShape[2], thisP[3]]; 2, 4 => -- left and right -- thisP _ [thisP[1] + tShape[1], thisP[2], thisP[3]]; 3, 5 => -- top and bottom -- thisP _ [thisP[1], thisP[2], thisP[3] + tShape[3]]; ENDCASE => ERROR; } ELSE GOTO FullTransform; blockData.box.loX _ MIN[thisP[1], opP[1]]; blockData.box.hiX _ MAX[thisP[1], opP[1]]; blockData.box.loY _ MIN[thisP[2], opP[2]]; blockData.box.hiY _ MAX[thisP[2], opP[2]]; blockData.box.loZ _ MIN[thisP[3], opP[3]]; blockData.box.hiZ _ MAX[thisP[3], opP[3]]; EXITS FullTransform => { assemblyWorld: Matrix4by4 _ SVCoordSys.WRTWorld[slice.coordSys]; newAssemblyWorld: Matrix4by4 _ SVMatrix3d.Mult[assemblyWorld, transform]; SVAssembly.AbsTransf[slice, scene, newAssemblyWorld]; }; END; }; BlockDescribeHit: PROC [mo: MasterObject, hitData: REF ANY] RETURNS [rope: Rope.ROPE] = { blockHitData: BlockHitData; prefix: Rope.ROPE; IF hitData = NIL THEN RETURN["a BLOCK"]; blockHitData _ NARROW[hitData]; IF blockHitData.corner#-1 THEN prefix _ blockCornerRopes[blockHitData.corner] ELSE IF blockHitData.center#-1 THEN prefix _ blockCenterRopes[blockHitData.center] ELSE IF blockHitData.centroid#-1 THEN prefix _ "centroid" ELSE IF blockHitData.edge#-1 THEN prefix _ blockEdgeRopes[blockHitData.edge] ELSE IF blockHitData.face#-1 THEN prefix _ blockCenterRopes[blockHitData.face] ELSE ERROR; rope _ Rope.Concat[prefix, " of a block"]; }; BlockDescribe: PROC [sliceD: SliceDescriptor] RETURNS [rope: Rope.ROPE] = { prefix: Rope.ROPE; blockParts: BlockParts _ NARROW[sliceD.parts]; cornerCount, edgeCount, centerCount, cornerIndex, edgeIndex, centerIndex: INTEGER; [cornerCount, cornerIndex] _ CountCorners[blockParts.corners]; [edgeCount, edgeIndex] _ CountEdges[blockParts.edges]; [centerCount, centerIndex] _ CountCenters[blockParts.centers]; IF cornerCount+centerCount+edgeCount>1 THEN RETURN["multiple parts of a Box slice"] ELSE { centroidOnly: BOOL _ blockParts.centroid AND edgeCount=0 AND cornerCount=0 AND centerCount=0; oneEdge: BOOL _ NOT blockParts.centroid AND edgeCount=1 AND cornerCount=0 AND centerCount=0; oneCorner: BOOL _ NOT blockParts.centroid AND edgeCount=0 AND cornerCount=1 AND centerCount=0; oneCenter: BOOL _ NOT blockParts.centroid AND edgeCount=0 AND cornerCount=0 AND centerCount=1; noParts: BOOL _ NOT blockParts.centroid AND edgeCount=0 AND cornerCount=0 AND centerCount=0; SELECT TRUE FROM oneCorner => prefix _ blockCornerRopes[cornerIndex]; oneCenter => prefix _ blockCenterRopes[centerIndex]; oneEdge => prefix _ blockEdgeRopes[edgeIndex]; centroidOnly => prefix _ "centroid"; noParts => prefix _ "NO parts"; ENDCASE => ERROR; rope _ Rope.Concat[prefix, " of a Block"]; }; }; BlockFilein: PUBLIC PROC [f: IO.STREAM, name: Rope.ROPE, csList: CoordSysList, defaultCS: CoordSystem, wdir: Rope.ROPE, version: REAL, feedback: FeedbackData] RETURNS [mo: MasterObject] = { IF version >= 7.02 THEN { loX, loY, loZ, hiX, hiY, hiZ: REAL; GGParseIn.ReadWRope[f, "["]; loX _ GGParseIn.ReadWReal[f]; loY _ GGParseIn.ReadWReal[f]; loZ _ GGParseIn.ReadWReal[f]; hiX _ GGParseIn.ReadWReal[f]; hiY _ GGParseIn.ReadWReal[f]; hiZ _ GGParseIn.ReadWReal[f]; GGParseIn.ReadWRope[f, "]"]; GGParseIn.ReadBlank[f]; mo _ BlockMakeMasterObject[name, loX, loY, loZ, hiX, hiY, hiZ]; IF version >= 7.03 THEN { artworkOK, strokeColorOK, good: BOOL _ FALSE; artwork: Artwork; color: Color; blockData: BlockData _ NARROW[mo.mainBody]; IF version >= 8708.06 THEN { GGParseIn.ReadWRope[f, "sc:"]; GGParseIn.ReadBlank[f]; [strokeColorOK, good] _ GGParseIn.ReadBool[f, version]; IF NOT good THEN ERROR; GGParseIn.ReadBlank[f]; color _ GGParseIn.ReadColor[f, version]; FOR i: NAT IN [0..11] DO IF NOT strokeColorOK THEN { GGParseIn.ReadBlank[f]; color _ GGParseIn.ReadColor[f, version]; }; blockData.segments[i].color _ color; ENDLOOP; } ELSE color _ NIL; GGParseIn.ReadWRope[f, "a:"]; GGParseIn.ReadBlank[f]; [artworkOK, good] _ GGParseIn.ReadBool[f, version]; IF NOT good THEN ERROR; GGParseIn.ReadBlank[f]; artwork _ SVParseIn.ReadArtwork[f, csList, defaultCS, wdir, version, feedback]; FOR i: NAT IN [0..5] DO IF NOT artworkOK THEN { GGParseIn.ReadBlank[f]; artwork _ SVParseIn.ReadArtwork[f, csList, defaultCS, wdir, version, feedback]; }; blockData.faces[i].artwork _ artwork; ENDLOOP; }; } ELSE { GGParseIn.ReadWRope[f, "data: procedural"]; GGParseIn.ReadBlank[f]; mo _ BlockMakeMasterObject[name]; }; }; AllArtworksEqual: PROC [blockData: BlockData] RETURNS [allEqual: BOOL, artwork: Artwork] = { firstArtwork: Artwork _ blockData.faces[0].artwork; class: ArtworkClass; material: Material; surface: REF ANY; color: Color; IF firstArtwork # NIL THEN { class _ firstArtwork.class; material _ firstArtwork.material; surface _ firstArtwork.surface; color _ firstArtwork.color; }; FOR i: NAT IN [0..5] DO IF firstArtwork # NIL THEN { SELECT blockData.faces[i].artwork.class FROM justColor => IF NOT GGCoreOps.EquivalentColors[color, blockData.faces[i].artwork.color] THEN RETURN[FALSE, firstArtwork]; ENDCASE => RETURN[FALSE, firstArtwork]; } ELSE { IF blockData.faces[i].artwork # NIL THEN RETURN[FALSE, firstArtwork]; }; ENDLOOP; RETURN[TRUE, firstArtwork]; }; AllStrokeColorsEqual: PROC [blockData: BlockData] RETURNS [allEqual: BOOL, color: Color] = { firstColor: Color _ blockData.segments[0].color; FOR i: NAT IN [0..11] DO IF NOT GGCoreOps.EquivalentColors[firstColor, blockData.segments[i].color] THEN RETURN[FALSE, firstColor]; ENDLOOP; RETURN[TRUE, firstColor]; }; BlockFileout: PUBLIC PROC [f: IO.STREAM, mo: MasterObject] = { blockData: BlockData _ NARROW[mo.mainBody]; artworkOK, strokeColorOK: BOOL; artwork: Artwork; strokeColor: Color; f.PutF["[%g %g %g ", [real[blockData.box.loX]], [real[blockData.box.loY]], [real[blockData.box.loZ]]]; f.PutF["%g %g %g] ", [real[blockData.box.hiX]], [real[blockData.box.hiY]], [real[blockData.box.hiZ]]]; [strokeColorOK, strokeColor] _ AllStrokeColorsEqual[blockData]; f.PutRope["sc: "]; GGParseOut.WriteBool[f, strokeColorOK]; f.PutChar[IO.SP]; GGParseOut.WriteColor[f, strokeColor]; [artworkOK, artwork] _ AllArtworksEqual[blockData]; f.PutRope[" a: "]; GGParseOut.WriteBool[f, artworkOK]; f.PutChar[IO.SP]; SVParseOut.WriteArtwork[f, artwork]; f.PutChar[IO.SP]; FOR i: NAT IN [0..11] DO seg: Segment _ blockData.segments[i]; IF NOT strokeColorOK THEN { GGParseOut.WriteColor[f, seg.color]; f.PutChar[IO.SP]; }; ENDLOOP; FOR i: NAT IN [0..5] DO face: Face _ blockData.faces[i]; IF NOT artworkOK THEN { SVParseOut.WriteArtwork[f, face.artwork]; f.PutChar[IO.SP]; }; ENDLOOP; }; BlockFileoutPoly: PUBLIC PROC [f: IO.STREAM, mo: MasterObject] = { linMesh: LinearMesh _ NARROW[mo.shadeBody]; SVSweepGeometry.PolyListLinear[f, linMesh]; }; OppositeCorner: PROC [corner: NAT] RETURNS [opposite: NAT] = { SELECT corner FROM IN [0..3] => opposite _ ((corner + 2) MOD 4) + 4; IN [4..7] => opposite _ ((corner + 2) MOD 4); ENDCASE => ERROR; }; CornersOfEdge: PROC [edge: NAT] RETURNS [lo, hi: NAT] = { SELECT edge FROM IN [0..3] => {lo _ edge; hi _ (edge + 1) MOD 4;}; IN [4..7] => {lo _ edge; hi _ ((edge + 1) MOD 4) + 4;}; IN [8..11] => {lo _ edge MOD 4; hi _ (edge MOD 4) + 4;}; ENDCASE => ERROR; }; CornersOfFace: PROC [face: NAT] RETURNS [corners: ARRAY[0..3] OF NAT] = { SELECT face FROM IN [0..1] => FOR i: NAT IN [0..3] DO corners[i] _ i + face*4 ENDLOOP; IN [2..5] => { corners[0] _ face-2; -- 0,1,2,3 corners[1] _ face+2; -- 4,5,6,7 corners[2] _ ((face-1) MOD 4) + 4; -- 5,6,7,4 corners[3] _ (face-1) MOD 4; -- 1,2,3,0 }; ENDCASE => ERROR; }; OppositeEdge: PROC [edge: NAT] RETURNS [opposite: NAT] = { SELECT edge FROM IN [0..3] => opposite _ ((edge + 2) MOD 4) + 4; IN [4..7] => opposite _ ((edge + 2) MOD 4); IN [8..11] => opposite _ ((edge +2) MOD 4) + 8; ENDCASE => ERROR; }; FacesOfEdge: PROC [edge: NAT] RETURNS [faceNums: ARRAY [0..1] OF NAT] = { SELECT edge FROM IN [0..3] => faceNums[0] _ 0; IN [4..7] => faceNums[0] _ 1; IN [8..11] => faceNums[0] _ edge - 6; ENDCASE => ERROR; SELECT edge FROM IN [0..3] => faceNums[1] _ edge + 2; IN [4..7] => faceNums[1] _ edge - 2; IN [9..11] => faceNums[1] _ edge - 7; 8 => faceNums[1] _ 5; ENDCASE => ERROR; }; FacesOfCorner: PROC [corner: NAT] RETURNS [faceNums: ARRAY [0..2] OF NAT] = { SELECT corner FROM IN [0..3] => faceNums[0] _ 0; IN [4..7] => faceNums[0] _ 1; ENDCASE => ERROR; SELECT corner FROM IN [0..3] => faceNums[1] _ corner + 2; IN [4..7] => faceNums[1] _ corner - 2; ENDCASE => ERROR; SELECT corner FROM 0, 4 => faceNums[2] _ 5; IN [1..3] => faceNums[2] _ corner + 1; IN [5..7] => faceNums[2] _ corner - 3; ENDCASE => ERROR; }; EdgesOfFace: PROC [face: NAT] RETURNS [edgeNums: ARRAY [0..3] OF NAT] = { SELECT face FROM 0, 1 => FOR i: NAT IN [0..3] DO edgeNums[i] _ face*4 + i; ENDLOOP; 2,3,4,5 => { edgeNums[0] _ face-2; -- 0,1,2,3 edgeNums[1] _ face+6; -- 8,9,10,11 edgeNums[3] _ face+2; -- 4,5,6,7 edgeNums[2] _ ((face-1 ) MOD 4) + 8; -- (1,2,3,0) + 8 = 9,10,11,8 }; ENDCASE => ERROR; }; OppositeFace: PROC [face: NAT] RETURNS [opposite: NAT] = { SELECT face FROM IN [0..1] => opposite _ ((face + 1) MOD 2); IN [2..5] => opposite _ ((face + 2) MOD 4); ENDCASE => ERROR; }; CountCorners: PROC [a: CornerArray] RETURNS [count: INTEGER, cornerNum: INTEGER] = { count _ 0; cornerNum _ -1; FOR corner: INTEGER IN [0..8) DO IF a[corner] THEN {count _ count+1; cornerNum _ corner}; ENDLOOP; }; CountCenters: PROC [a: CenterArray] RETURNS [count: INTEGER, centerNum: INTEGER] = { count _ 0; centerNum _ -1; FOR center: INTEGER IN [0..6) DO IF a[center] THEN {count _ count+1; centerNum _ center}; ENDLOOP; }; CountEdges: PROC [a: EdgeArray] RETURNS [count: INTEGER, edgeNum: INTEGER] = { count _ 0; edgeNum _ -1; FOR edge: INTEGER IN [0..12) DO IF a[edge] THEN {count _ count+1; edgeNum _ edge}; ENDLOOP; }; CountFaces: PROC [a: FaceArray] RETURNS [count: INTEGER, faceNum: INTEGER] = { count _ 0; faceNum _ -1; FOR face: INTEGER IN [0..6) DO IF a[face] THEN {count _ count+1; faceNum _ face}; ENDLOOP; }; blockNormals: ARRAY [0..6) OF Vector3d = [ [0,1,0], [0,-1,0], [-1,0,0], [0,0,-1], [1,0,0], [0,0,1] ]; blockCornerRopes: ARRAY [0..8) OF Rope.ROPE = [ "top front left", "top back left", "top back right", "top front right", "bottom front left", "bottom back left", "bottom back right", "bottom front right" ]; blockEdgeRopes: ARRAY [0..12) OF Rope.ROPE = [ "top left", "top back", "top right", "top front", "bottom left", "bottom back", "bottom right", "bottom front", "front left", "back left", "back right", "front right" ]; blockCenterRopes: ARRAY [0..6) OF Rope.ROPE = [ "top", "bottom", "left", "back", "right", "front" ]; BlockNormalWRTCamera: PROC [face: NAT, shape: Shape, camera: Camera] RETURNS [normalCamera: Vector3d] = { cameraShape: Matrix4by4; shapeNormal: Vector3d _ blockNormals[face]; cameraShape _ SVCoordSys.FindCameraInTermsOf[shape.coordSys, camera.coordSys]; normalCamera _ SVMatrix3d.UpdateVectorWithInverse[cameraShape, shapeNormal]; }; BlockNormalWRTWorld: PROC [face: NAT, shape: Shape] RETURNS [normalWorld: Vector3d] = { worldShape: Matrix4by4; shapeNormal: Vector3d _ blockNormals[face]; worldShape _ SVCoordSys.FindWorldInTermsOf[shape.coordSys]; normalWorld _ SVMatrix3d.UpdateVectorWithInverse[worldShape, shapeNormal]; }; DistanceFaceToPoint: PROC [shape: Shape, face: NAT, testWorld: Point3d] RETURNS [dist: REAL] = { testShape: Point3d; mo: MasterObject _ shape.mo; blockData: BlockData _ NARROW[mo.mainBody]; worldShape: Matrix4by4 _ SVCoordSys.FindWorldInTermsOf[shape.coordSys]; shapeWorld: Matrix4by4 _ SVCoordSys.WRTWorld[shape.coordSys]; shapeVec, worldVec: Vector3d; testShape _ SVMatrix3d.Update[testWorld, worldShape]; SELECT face FROM 0 => shapeVec _ [0, testShape[2] - blockData.box.hiY, 0]; 1 => shapeVec _ [0, testShape[2] - blockData.box.loY, 0]; 2 => shapeVec _ [testShape[1] - blockData.box.loX, 0, 0]; 3 => shapeVec _ [0, 0, testShape[3] - blockData.box.loZ]; 4 => shapeVec _ [testShape[1] - blockData.box.hiX, 0, 0]; 5 => shapeVec _ [0, 0, testShape[3] - blockData.box.hiZ]; ENDCASE => ERROR; worldVec _ SVMatrix3d.UpdateDisplacement[shapeWorld, shapeVec]; dist _ SVVector3d.Magnitude[worldVec]; }; MakeComplete: PROC [parts: SliceParts] = { WITH parts SELECT FROM blockParts: BlockParts => { blockParts.corners _ ALL[TRUE]; blockParts.centers _ ALL[TRUE]; blockParts.centroid _ TRUE; blockParts.edges _ ALL[TRUE]; blockParts.faces _ ALL[TRUE]; }; ENDCASE => ERROR; }; BlockIsCompleteParts: PROC [sliceD: SliceDescriptor] RETURNS [BOOL] = { blockParts: BlockParts _ NARROW[sliceD.parts]; RETURN[blockParts.corners=ALL[TRUE] AND blockParts.centroid = TRUE AND blockParts.edges=ALL[TRUE] AND blockParts.faces = ALL[TRUE]]; }; BlockIsEmptyParts: PROC [sliceD: SliceDescriptor] RETURNS [BOOL] = { blockParts: BlockParts _ NARROW[sliceD.parts]; RETURN[blockParts.corners=ALL[FALSE] AND blockParts.centers=ALL[FALSE] AND blockParts.centroid = FALSE AND blockParts.edges=ALL[FALSE] AND blockParts.faces = ALL[FALSE]]; }; BlockNewParts: PROC [slice: Slice, hitData: REF ANY, hitPoint: Point3d, mode: SelectMode] RETURNS [sliceD: SliceDescriptor] = { blockHitData: BlockHitData _ NARROW[hitData]; blockParts: BlockParts; cornerIndex, centerIndex, centroid, face: INT; blockParts _ NEW[BlockPartsObj _ [corners: ALL[FALSE], centers: ALL[FALSE], centroid: FALSE, edges: ALL[FALSE], faces: ALL[FALSE]]]; SELECT mode FROM joint => { IF blockHitData.corner#-1 THEN blockParts.corners[blockHitData.corner] _ TRUE ELSE IF blockHitData.center#-1 THEN blockParts.centers[blockHitData.center] _ TRUE ELSE IF blockHitData.centroid#-1 THEN blockParts.centroid _ TRUE ELSE IF blockHitData.edge#-1 OR blockHitData.face#-1 THEN { [cornerIndex, centerIndex, centroid] _ NearestBlockPoint[slice, hitPoint]; IF centroid#-1 THEN blockParts.centroid _ TRUE ELSE IF centerIndex#-1 THEN blockParts.centers[centerIndex] _ TRUE ELSE blockParts.corners[cornerIndex] _ TRUE; }; }; segment => { IF blockHitData.centroid#-1 THEN MakeComplete[blockParts] ELSE IF blockHitData.center#-1 THEN MakeComplete[blockParts] ELSE IF blockHitData.corner#-1 THEN MakeComplete[blockParts] ELSE IF blockHitData.edge#-1 THEN blockParts.edges[blockHitData.edge] _ TRUE; }; traj => { IF blockHitData.face#-1 THEN {face _ blockHitData.face; GOTO MakeFaceParts} ELSE IF blockHitData.center#-1 THEN {face _ blockHitData.center; GOTO MakeFaceParts} ELSE MakeComplete[blockParts]; EXITS MakeFaceParts => { corners: ARRAY[0..3] OF NAT; edgeNums: ARRAY [0..3] OF NAT; blockParts.faces[face] _ TRUE; corners _ CornersOfFace[face]; FOR i: NAT IN [0..3] DO blockParts.corners[corners[i]] _ TRUE ENDLOOP; edgeNums _ EdgesOfFace[face]; FOR i: NAT IN [0..3] DO blockParts.edges[edgeNums[i]] _ TRUE ENDLOOP; }; }; topLevel, slice => MakeComplete[blockParts]; none => { MakeComplete[blockParts]; }; ENDCASE => ERROR; sliceD _ SVAssembly.DescriptorFromParts[slice, blockParts]; }; BlockUnionParts: PROC [partsA: SliceDescriptor, partsB: SliceDescriptor] RETURNS [aPlusB: SliceDescriptor] = { blockPartsA: BlockParts _ NARROW[partsA.parts]; blockPartsB: BlockParts _ NARROW[partsB.parts]; newParts: BlockParts; IF partsA = NIL OR partsB = NIL THEN ERROR; IF partsA.parts = NIL THEN RETURN[partsB]; IF partsB.parts = NIL THEN RETURN[partsA]; newParts _ NEW[BlockPartsObj _ [corners: ALL[FALSE], centers: ALL[FALSE], centroid: FALSE, edges: ALL[FALSE], faces: ALL[FALSE] ] ]; FOR i: INTEGER IN [0..8) DO newParts.corners[i] _ blockPartsA.corners[i] OR blockPartsB.corners[i]; ENDLOOP; FOR i: INTEGER IN [0..6) DO newParts.centers[i] _ blockPartsA.centers[i] OR blockPartsB.centers[i]; newParts.faces[i] _ blockPartsA.faces[i] OR blockPartsB.faces[i]; ENDLOOP; FOR i: INTEGER IN [0..12) DO newParts.edges[i] _ blockPartsA.edges[i] OR blockPartsB.edges[i]; ENDLOOP; newParts.centroid _ blockPartsA.centroid OR blockPartsB.centroid; aPlusB _ SVAssembly.DescriptorFromParts[partsA.slice, newParts]; }; BlockDifferenceParts: PROC [partsA: SliceDescriptor, partsB: SliceDescriptor] RETURNS [aMinusB: SliceDescriptor] = { blockPartsA: BlockParts _ NARROW[partsA.parts]; blockPartsB: BlockParts _ NARROW[partsB.parts]; newParts: BlockParts; IF partsA = NIL OR partsB = NIL THEN ERROR; IF partsA.parts = NIL THEN RETURN[NIL]; IF partsB.parts = NIL THEN RETURN[partsA]; newParts _ NEW[BlockPartsObj _ [corners: ALL[FALSE], centers: ALL[FALSE], centroid: FALSE, edges: ALL[FALSE], faces: ALL[FALSE] ] ]; FOR i: INTEGER IN [0..8) DO newParts.corners[i] _ IF blockPartsB.corners[i] THEN FALSE ELSE blockPartsA.corners[i]; ENDLOOP; FOR i: INTEGER IN [0..6) DO newParts.centers[i] _ IF blockPartsB.centers[i] THEN FALSE ELSE blockPartsA.centers[i]; newParts.faces[i] _ IF blockPartsB.faces[i] THEN FALSE ELSE blockPartsA.faces[i]; ENDLOOP; FOR i: INTEGER IN [0..12) DO newParts.edges[i] _ IF blockPartsB.edges[i] THEN FALSE ELSE blockPartsA.edges[i]; ENDLOOP; newParts.centroid _ IF blockPartsB.centroid THEN FALSE ELSE blockPartsA.centroid; aMinusB _ SVAssembly.DescriptorFromParts[partsA.slice, newParts]; }; BlockMovingParts: PROC [slice: Slice, selectedParts: SliceParts] RETURNS [background, overlay, rubber, drag: SliceDescriptor] = { blockParts: BlockParts _ NARROW[selectedParts]; overlayParts, dragParts: BlockParts; filled: BOOL _ TRUE; nullD: SliceDescriptor _ SVAssembly.DescriptorFromParts[slice, NIL]; newParts: BlockParts _ NEW[BlockPartsObj _ [corners: ALL[TRUE], centers: ALL[TRUE], centroid: TRUE, edges: ALL[TRUE], faces: ALL[TRUE] ] ]; cornerCount, cornerNum, centerCount, centerNum, edgeCount, edgeNum, faceCount, faceNum: INTEGER; centroidCount: INTEGER _ IF blockParts.centroid THEN 1 ELSE 0; [cornerCount, cornerNum] _ CountCorners[blockParts.corners]; [centerCount, centerNum] _ CountCenters[blockParts.centers]; [edgeCount, edgeNum] _ CountEdges[blockParts.edges]; [faceCount, faceNum] _ CountFaces[blockParts.faces]; IF faceCount=1 AND edgeCount = 4 AND cornerCount = 4 THEN { -- one face is moving statFace: INTEGER _ OppositeFace[faceNum]; statEdges: ARRAY[0..3] OF NAT _ EdgesOfFace[statFace]; statCorners: ARRAY[0..3] OF NAT _ CornersOfFace[statFace]; moveCorners: ARRAY[0..3] OF NAT _ CornersOfFace[faceNum]; dragEdges: ARRAY[0..3] OF NAT _ EdgesOfFace[faceNum]; overlayParts _ NEW[BlockPartsObj _ [corners: ALL[FALSE], centers: ALL[FALSE], centroid: FALSE, edges: ALL[FALSE], faces: ALL[FALSE] ] ]; background _ nullD; overlayParts.faces[statFace] _ TRUE; overlayParts.centers[statFace] _ TRUE; FOR i: NAT IN [0..3] DO overlayParts.edges[statEdges[i]] _ TRUE; overlayParts.corners[statCorners[i]] _ TRUE; ENDLOOP; overlay _ SVAssembly.DescriptorFromParts[slice, overlayParts]; newParts.faces[faceNum] _ FALSE; newParts.centers[faceNum] _ FALSE; newParts.faces[statFace] _ FALSE; newParts.centers[statFace] _ FALSE; FOR i: NAT IN [0..3] DO newParts.edges[statEdges[i]] _ FALSE; newParts.edges[dragEdges[i]] _ FALSE; newParts.corners[statCorners[i]] _ FALSE; newParts.corners[moveCorners[i]] _ FALSE; ENDLOOP; rubber _ SVAssembly.DescriptorFromParts[slice, newParts]; dragParts _ NEW[BlockPartsObj _ [corners: ALL[FALSE], centers: ALL[FALSE], centroid: FALSE, edges: ALL[FALSE], faces: ALL[FALSE] ] ]; dragParts.faces[faceNum] _ TRUE; dragParts.centers[faceNum] _ TRUE; FOR i: NAT IN [0..3] DO dragParts.edges[dragEdges[i]] _ TRUE; dragParts.corners[moveCorners[i]] _ TRUE; ENDLOOP; drag _ SVAssembly.DescriptorFromParts[slice, dragParts]; RETURN; }; IF centroidCount>0 OR faceCount>0 OR edgeCount > 1 OR (edgeCount=0 AND cornerCount > 1) OR (edgeCount=1 AND cornerCount#2) THEN { -- everything moving background _ overlay _ rubber _ nullD; drag _ SVAssembly.DescriptorFromParts[slice, newParts]; RETURN; }; IF cornerCount=1 THEN { -- all but the opposite corner newParts.corners[OppositeCorner[cornerNum]] _ FALSE; background _ drag _ nullD; rubber _ SVAssembly.DescriptorFromParts[slice, newParts]; overlayParts _ NEW[BlockPartsObj _ [corners: ALL[FALSE], centers: ALL[FALSE], centroid: FALSE, edges: ALL[FALSE], faces: ALL[FALSE] ] ]; overlayParts.corners[cornerNum] _ TRUE; overlay _ SVAssembly.DescriptorFromParts[slice, overlayParts]; IF NOT filled THEN {background _ overlay; overlay _ nullD}; } ELSE IF edgeCount=1 THEN { -- turn off the opposite edge and two corners wholeD: SliceDescriptor _ SVAssembly.NewParts[slice, NIL, [0,0,0], slice]; lo, hi, oppositeEdge: NAT; oppositeEdge _ OppositeEdge[edgeNum]; [lo, hi] _ CornersOfEdge[oppositeEdge]; newParts.edges[oppositeEdge] _ FALSE; newParts.corners[lo] _ FALSE; newParts.corners[hi] _ FALSE; background _ drag _ nullD; rubber _ SVAssembly.DescriptorFromParts[slice, newParts]; overlay _ BlockDifferenceParts[wholeD, rubber]; IF NOT filled THEN {background _ overlay; overlay _ nullD}; } ELSE { -- nothing is moving background _ SVAssembly.DescriptorFromParts[slice, newParts]; rubber _ overlay _ drag _ nullD; }; }; BlockCopyParts: PROC [blockParts: BlockParts] RETURNS [copyParts: BlockParts] = { copyParts _ NEW[BlockPartsObj]; copyParts^ _ blockParts^; }; BlockAugmentParts: PUBLIC PROC [sliceD: SliceDescriptor, selectClass: SelectionClass] RETURNS [more: SliceDescriptor] = { blockParts: BlockParts _ NARROW[sliceD.parts]; newParts: BlockParts; lo, hi: NAT; edgeNums: ARRAY [0..3] OF NAT; newParts _ BlockCopyParts[blockParts]; FOR i: NAT IN [0..5] DO IF newParts.faces[i] THEN { edgeNums _ EdgesOfFace[i]; FOR j: NAT IN [0..3] DO newParts.edges[edgeNums[j]] _ TRUE; ENDLOOP; }; ENDLOOP; FOR i: NAT IN [0..11] DO IF newParts.edges[i] THEN { [lo, hi] _ CornersOfEdge[i]; newParts.corners[lo] _ TRUE; newParts.corners[hi] _ TRUE; }; ENDLOOP; more _ SVAssembly.DescriptorFromParts[sliceD.slice, newParts]; }; BlockGenData: TYPE = REF BlockGenDataObj; BlockGenDataObj: TYPE = RECORD [ blockParts: BlockParts, blockData: BlockData ]; BlockPointsInDescriptor: PROC [sliceD: SliceDescriptor, centroids: BOOL] RETURNS [pointGen: PointGenerator] = { blockParts: BlockParts _ NARROW[sliceD.parts]; blockData: BlockData _ NARROW[NARROW[sliceD.slice.shape, Shape].mo.mainBody]; blockGenData: BlockGenData; cornerCount, centroidCount: INTEGER; [cornerCount, ----] _ CountCorners[blockParts.corners]; centroidCount _ IF blockParts.centroid AND centroids THEN 1 ELSE 0; blockGenData _ NEW[BlockGenDataObj _ [blockParts, blockData]]; pointGen _ NEW[PointGeneratorObj _ [ sliceD: sliceD, toGo: MAX[0, cornerCount] + MAX[0, centroidCount], index: 0, centroids: centroids, classSpecific: blockGenData ]]; }; BlockNextPoint: PROC [pointGen: PointGenerator] RETURNS [pointAndDone: PointAndDone] = { IF pointGen.toGo = 0 THEN pointAndDone _ [[0,0,0], TRUE] ELSE { index: INTEGER; blockGenData: BlockGenData _ NARROW[pointGen.classSpecific]; blockParts: BlockParts _ blockGenData.blockParts; blockData: BlockData _ blockGenData.blockData; FOR index _ pointGen.index, index+1 UNTIL index >=8 DO IF blockParts.corners[index] THEN EXIT; -- index will point to next available point REPEAT FINISHED => { IF NOT blockParts.centroid THEN ERROR; }; ENDLOOP; IF index IN [0..7] THEN pointAndDone.point _ GetBox3dPoint[blockData.box, index] ELSE IF index = 8 THEN pointAndDone.point _ GetBox3dCentroid[blockData.box] ELSE ERROR; pointGen.toGo _ pointGen.toGo-1; pointGen.index _ IF pointGen.toGo=0 THEN 99 ELSE index+1; pointAndDone.done _ FALSE; }; }; BlockSegmentsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [segGen: SegmentGenerator] = { blockParts: BlockParts _ NARROW[sliceD.parts]; blockData: BlockData _ NARROW[NARROW[sliceD.slice.shape, Shape].mo.mainBody]; blockGenData: BlockGenData; edgeCount: INTEGER; [edgeCount, ----] _ CountEdges[blockParts.edges]; blockGenData _ NEW[BlockGenDataObj _ [blockParts, blockData]]; segGen _ NEW[SegmentGeneratorObj _ [ toGo: MAX[0, edgeCount], index: 0, data: blockGenData ]]; }; BlockNextSegment: PROC [segGen: SegmentGenerator] RETURNS [segment: Segment] = { IF segGen.toGo = 0 THEN RETURN[NIL] ELSE { blockGenData: BlockGenData _ NARROW[segGen.data]; blockParts: BlockParts _ blockGenData.blockParts; blockData: BlockData _ blockGenData.blockData; index: INTEGER; FOR index _ segGen.index, index+1 UNTIL index >=12 DO IF blockParts.edges[index] THEN EXIT; -- index will point to next available point REPEAT FINISHED => { ERROR; }; ENDLOOP; segment _ blockData.segments[index]; segGen.toGo _ segGen.toGo - 1; segGen.index _ IF segGen.toGo=0 THEN 99 ELSE index+1; }; }; SegAndIndex: TYPE = RECORD [ seg: Segment, index: INTEGER ]; BlockNextSegmentAndIndex: PROC [segGen: SegmentGenerator] RETURNS [next: SegAndIndex] = { IF segGen.toGo = 0 THEN RETURN[[NIL, -1]] ELSE { blockGenData: BlockGenData _ NARROW[segGen.data]; blockParts: BlockParts _ blockGenData.blockParts; blockData: BlockData _ blockGenData.blockData; index: INTEGER; FOR index _ segGen.index, index+1 UNTIL index >=12 DO IF blockParts.edges[index] THEN EXIT; -- index will point to next available point REPEAT FINISHED => { ERROR; }; ENDLOOP; next.seg _ blockData.segments[index]; next.index _ index; segGen.toGo _ segGen.toGo - 1; segGen.index _ IF segGen.toGo=0 THEN 99 ELSE index+1; }; }; NearestBlockPoint: PROC [slice: Slice, point: Point3d, centroids: BOOL _ FALSE] RETURNS [cornerIndex, centerIndex, centroid: INT, bestPoint: Point3d, bestDist: REAL] = { mo: MasterObject; pointGen: PointGenerator; wholeD: SliceDescriptor; shape: Shape; shape _ NARROW[slice.shape]; mo _ shape.mo; wholeD _ SVAssembly.NewParts[slice, NIL, [0,0,0], slice]; pointGen _ mo.class.pointsInDescriptor[wholeD, centroids]; BEGIN thisDist: REAL; thisPoint: Point3d; bestCount, thisCount: NAT; thisCount _ 0; bestDist _ Real.LargestNumber; bestCount _ LAST[NAT]; FOR pointAndDone: PointAndDone _ SVAssembly.NextPoint[pointGen], SVAssembly.NextPoint[pointGen] UNTIL pointAndDone.done DO thisPoint _ pointAndDone.point; thisDist _ SVVector3d.Distance[thisPoint, point]; IF thisDist < bestDist THEN { bestDist _ thisDist; bestPoint _ thisPoint; bestCount _ thisCount; }; thisCount _ thisCount + 1; ENDLOOP; IF bestCount IN [0..7] THEN {cornerIndex _ bestCount; centerIndex _ -1; centroid _ -1} ELSE IF bestCount = 8 THEN {cornerIndex _ -1; centerIndex _ -1; centroid _ 0} ELSE ERROR; END; }; BlockClosestPointToPoint: PROC [sliceD: SliceDescriptor, testPoint: Point3d, t: REAL, centroids: BOOL] RETURNS [bestDist: REAL, pointWorld: Point3d, normalWorld: Vector3d, hitData: REF ANY, success: BOOL _ TRUE] = { cornerIndex, centerIndex, centroid, bestFace: INTEGER; thisFaceDist, bestFaceDist: REAL; shape: Shape _ NARROW[sliceD.slice.shape]; [cornerIndex, centerIndex, centroid, pointWorld, bestDist] _ NearestBlockPoint[sliceD.slice, testPoint]; hitData _ NEW[BlockHitDataObj _ [corner: cornerIndex, center: centerIndex, centroid: centroid, hitPoint: pointWorld]]; bestFaceDist _ Real.LargestNumber; bestFace _ -1; FOR i: NAT IN [0..5] DO thisFaceDist _ DistanceFaceToPoint[shape, i, testPoint]; IF thisFaceDist < bestFaceDist THEN { bestFaceDist _ thisFaceDist; bestFace _ i; }; ENDLOOP; normalWorld _ BlockNormalWRTWorld[bestFace, shape]; }; PointIsInBox: PROC [cameraPoint: Point2d, boundBox: SVBasicTypes.BoundBoxObj] RETURNS [BOOL] = { IF boundBox.infinite THEN RETURN[TRUE]; -- NIL means infinite IF boundBox.null THEN RETURN[FALSE]; RETURN[ boundBox.loX <= cameraPoint[1] AND cameraPoint[1]<=boundBox.hiX AND boundBox.loY <= cameraPoint[2] AND cameraPoint[2]<=boundBox.hiY] }; -- end of PointIsInBox useBBoxes: BOOL _ TRUE; BlockClosestPointToLine: PROC [sliceD: SliceDescriptor, cameraPoint: Point2d, line3d: Line3d, t: REAL, camera: Camera, centroids: BOOL] RETURNS [bestDist: REAL, pointWorld: Point3d, normalWorld: Vector3d, hitData: REF ANY, success: BOOL _ FALSE] = { shapeWorld: Matrix4by4; pointGen: PointGenerator; shape: Shape; thisDist: REAL; thisPoint: Point3d; bestCount, count: NAT _ 0; corner, centroid: INTEGER; sliceBBox: BoundBox _ SVAssembly.GetBoundBox[sliceD.slice, sliceD.parts, camera]; toleranceBox: SVBasicTypes.BoundBoxObj; toleranceBox _ [sliceBBox.loX-t, sliceBBox.loY-t, sliceBBox.hiX+t, sliceBBox.hiY+t, FALSE, FALSE]; IF NOT useBBoxes OR PointIsInBox[cameraPoint, toleranceBox] THEN { vectorToEyepoint, thisNormalCamera: Vector3d; faces: ARRAY[0..2] OF NAT; pointCamera: Point3d; thisDot, bestDot: REAL; worldCamera: Matrix4by4 _ SVCoordSys.FindWorldInTermsOf[camera.coordSys]; shape _ NARROW[sliceD.slice.shape]; shapeWorld _ SVCoordSys.WRTWorld[shape.coordSys]; bestDist _ Real.LargestNumber; pointGen _ BlockPointsInDescriptor[sliceD, centroids]; FOR pointAndDone: PointAndDone _ BlockNextPoint[pointGen], BlockNextPoint[pointGen] UNTIL pointAndDone.done = TRUE DO thisPoint _ SVMatrix3d.Update[pointAndDone.point, shapeWorld]; thisDist _ SVLines3d.DistancePointToLine[thisPoint, line3d]; IF thisDist < bestDist THEN { bestDist _ thisDist; pointWorld _ thisPoint; bestCount _ count; }; count _ count + 1; ENDLOOP; IF bestCount IN [0..7] THEN {corner _ bestCount; centroid _ -1} ELSE IF bestCount = 8 THEN {corner _ -1; centroid _ 0} ELSE ERROR; success _ TRUE; hitData _ NEW[BlockHitDataObj _ [corner: corner, centroid: centroid, hitPoint: pointWorld]]; IF centroid = 0 THEN { normalWorld _ SVMatrix3d.ZAxisOfMatrix[shapeWorld]; } ELSE { pointCamera _ SVMatrix3d.Update[pointWorld, worldCamera]; faces _ FacesOfCorner[corner]; bestDot _ 0.0; vectorToEyepoint _ SVVector3d.Sub[[0,0,camera.focalLength], pointCamera]; FOR i: NAT IN [0..2] DO thisNormalCamera _ BlockNormalWRTCamera[faces[i], shape, camera]; thisDot _ SVVector3d.DotProduct[vectorToEyepoint, thisNormalCamera]; IF thisDot > bestDot THEN { bestDot _ thisDot; normalWorld _ BlockNormalWRTWorld[faces[i], shape]; }; ENDLOOP; IF bestDot = 0.0 THEN normalWorld _ BlockNormalWRTWorld[0, shape]; }; }; }; BlockClosestSegmentToLine: PROC [sliceD: SliceDescriptor, cameraPoint: Point2d, line3d: Line3d, t: REAL, camera: Camera] RETURNS [bestDist: REAL, pointWorld: Point3d, normalWorld: Vector3d, hitData: REF ANY, success: BOOL _ FALSE] = { moWORLD: Matrix4by4; segGen: SegmentGenerator; thisDist: REAL; thisPoint: Point3d; bestCount: NAT _ 0; lo, hi: NAT; sliceBBox: BoundBox _ SVAssembly.GetBoundBox[sliceD.slice, sliceD.parts, camera]; toleranceBox: SVBasicTypes.BoundBoxObj; toleranceBox _ [sliceBBox.loX-t, sliceBBox.loY-t, sliceBBox.hiX+t, sliceBBox.hiY+t, FALSE, FALSE]; IF NOT useBBoxes OR PointIsInBox[cameraPoint, toleranceBox] THEN { shape: Shape _ NARROW[sliceD.slice.shape]; blockData: BlockData _ NARROW[shape.mo.mainBody]; pointCamera: Point3d; vectorToEyepoint, thisNormalCamera: Vector3d; faces: ARRAY[0..1] OF NAT; thisDot, bestDot: REAL; worldCamera: Matrix4by4 _ SVCoordSys.FindWorldInTermsOf[camera.coordSys]; moWORLD _ SVCoordSys.WRTWorld[shape.coordSys]; bestDist _ Real.LargestNumber; segGen _ BlockSegmentsInDescriptor[sliceD]; FOR next: SegAndIndex _ BlockNextSegmentAndIndex[segGen], BlockNextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO [lo, hi] _ CornersOfEdge[next.index]; SVLines3d.FillEdge[GetBox3dPoint[blockData.box, lo], GetBox3dPoint[blockData.box, hi], next.seg.edge]; SVLines3d.FillEdgeTransform[next.seg.edge, moWORLD, next.seg.edge]; [thisDist, thisPoint] _ SVLines3d.DistanceAndPointLineToEdge[line3d, next.seg.edge]; IF thisDist < bestDist THEN { bestDist _ thisDist; pointWorld _ thisPoint; bestCount _ next.index; }; ENDLOOP; success _ TRUE; hitData _ NEW[BlockHitDataObj _ [edge: bestCount, hitPoint: pointWorld]]; pointCamera _ SVMatrix3d.Update[pointWorld, worldCamera]; faces _ FacesOfEdge[bestCount]; bestDot _ 0.0; vectorToEyepoint _ SVVector3d.Sub[[0,0,camera.focalLength], pointCamera]; FOR i: NAT IN [0..1] DO thisNormalCamera _ BlockNormalWRTCamera[faces[i], shape, camera]; thisDot _ SVVector3d.DotProduct[vectorToEyepoint, thisNormalCamera]; IF thisDot > bestDot THEN { bestDot _ thisDot; normalWorld _ BlockNormalWRTWorld[faces[i], shape]; }; ENDLOOP; IF bestDot = 0.0 THEN normalWorld _ BlockNormalWRTWorld[0, shape]; }; }; epsilon: REAL _ 0.01; BlockWithinBoundBlock: PROC [slice: Slice, boundBlockWorld: BoundBlock, parts: SliceParts _ NIL] RETURNS [withinParts: SliceDescriptor] = { shape: Shape _ NARROW[slice.shape]; blockData: BlockData _ NARROW[shape.mo.mainBody]; box: Box3d _ blockData.box; point: Point3d; blockParts: BlockParts _ NEW[BlockPartsObj _ [corners: ALL[FALSE], centers: ALL[FALSE], centroid: FALSE, edges: ALL[FALSE], faces: ALL[FALSE]]]; lo, hi: NAT; edgeNums: ARRAY [0..3] OF NAT; cornerCount: NAT _ 0; shapeWorld: Matrix4by4 _ SVCoordSys.WRTWorld[shape.coordSys]; FOR i: NAT IN [0..7] DO point _ GetBox3dPoint[box, i]; point _ SVMatrix3d.Update[point, shapeWorld]; IF PointInBoundBlock[point, boundBlockWorld, epsilon] THEN { blockParts.corners[i] _ TRUE; cornerCount _ cornerCount + 1; }; ENDLOOP; IF cornerCount = 0 THEN RETURN[NIL]; FOR i: NAT IN [0..11] DO [lo, hi] _ CornersOfEdge[i]; IF blockParts.corners[lo] AND blockParts.corners[hi] THEN blockParts.edges[i] _ TRUE; ENDLOOP; FOR i: NAT IN [0..5] DO edgeNums _ EdgesOfFace[i]; IF blockParts.edges[edgeNums[0]] AND blockParts.edges[edgeNums[1]] AND blockParts.edges[edgeNums[2]] AND blockParts.edges[edgeNums[3]] THEN blockParts.faces[i] _ TRUE; ENDLOOP; FOR i: NAT IN [0..5] DO IF NOT blockParts.faces[i] THEN EXIT; REPEAT FINISHED => blockParts.centroid _ TRUE; ENDLOOP; withinParts _ SVAssembly.DescriptorFromParts[slice, blockParts]; }; PointInBoundBlock: PROC [point: Point3d, block: BoundBlock, epsilon: REAL] RETURNS [BOOL] = { RETURN[ point[1] >= block.loX-epsilon AND point[1] <= block.hiX+epsilon AND point[2] >= block.loY-epsilon AND point[2] <= block.hiY+epsilon AND point[3] >= block.loZ-epsilon AND point[3] <= block.hiZ+epsilon ]; }; BlockSetStrokeColor: PROC [slice: Slice, parts: SliceParts, color: Color _ NIL] = { blockParts: BlockParts _ NARROW[parts]; shape: Shape _ NARROW[slice.shape]; blockData: BlockData _ NARROW[shape.mo.mainBody]; FOR edge: INTEGER IN [0..12) DO IF blockParts = NIL OR blockParts.edges[edge] THEN blockData.segments[edge].color _ color; ENDLOOP; }; BlockGetStrokeColor: PROC [slice: Slice, parts: SliceParts] RETURNS [color: Color _ NIL] = { blockParts: BlockParts _ NARROW[parts]; shape: Shape _ NARROW[slice.shape]; blockData: BlockData _ NARROW[shape.mo.mainBody]; FOR edge: INTEGER IN [0..12) DO IF blockParts.edges[edge] THEN RETURN[blockData.segments[edge].color]; ENDLOOP; }; BlockSetFillColor: PROC [slice: Slice, parts: SliceParts, artwork: Artwork] = { blockParts: BlockParts _ NARROW[parts]; shape: Shape _ NARROW[slice.shape]; blockData: BlockData _ NARROW[shape.mo.mainBody]; FOR face: INTEGER IN [0..6) DO IF blockParts = NIL OR blockParts.faces[face] THEN blockData.faces[face].artwork _ artwork; ENDLOOP; }; BlockGetFillColor: PROC [slice: Slice, parts: SliceParts] RETURNS [artwork: Artwork] = { blockParts: BlockParts _ NARROW[parts]; shape: Shape _ NARROW[slice.shape]; blockData: BlockData _ NARROW[shape.mo.mainBody]; FOR face: INTEGER IN [0..6) DO IF blockParts.faces[face] THEN RETURN[blockData.faces[face].artwork]; ENDLOOP; }; SegmentGenerator: TYPE = REF SegmentGeneratorObj; SegmentGeneratorObj: TYPE = RECORD [ data: REF ANY, index: NAT, toGo: NAT ]; SegmentAndDone: TYPE = RECORD[ seg: Segment, done: BOOL ]; BlockUpdate: PUBLIC PROC [mo: MasterObject, updateData: REF ANY] = {}; BuildBoxClass: PROC RETURNS [class: MasterObjectClass] = { class _ NEW[MasterObjectClassObj _ [ name: "box", getHedron: BoxBoundHedron, lineDraw: BoxLineDraw, normalsDraw: NoOpNormalsDraw, preprocess: NoOpPreprocess, rayCast: NoOpRayCast, rayCastNoBBoxes: NoOpRayCastNoBBoxes, rayCastBoundingSpheres: NoOpRayCastBoundingSpheres, drawSurf: NoOpDrawPlanarSurface, drawSubBoxes: NoOpDrawSubBoxes, drawSubSpheres: NoOpDrawSubSpheres, describe: NoOpDescribe, describeHit: NoOpDescribeHit, filein: NoOpFilein, fileout: NoOpFileout, fileoutPoly: NoOpFileoutPoly, countSurf: NoOpCountPlanarSurfaces, getSurf: NoOpGetPlanarSurfaces, isEmptyParts: NoOpIsEmptyParts, newParts: NoOpNewParts, unionParts: NoOpUnionParts, differenceParts: NoOpDifferenceParts, movingParts: NoOpMovingParts, augmentParts: NoOpAugmentParts, pointsInDescriptor: NoOpPointsInDescriptor, nextPoint: NoOpNextPoint, closestPointToPoint: NoOpClosestPointToPoint, closestPointToLine: NoOpClosestPointToLine, closestSegmentToLine: NoOpClosestSegmentToLine, update: NoOpUpdate ]]; }; BoxRec: TYPE = REF BoxRecObj; BoxRecObj: TYPE = RECORD [ poly: Poly3d, bBox: BoundBox ]; BoxBody: PROC [w, h: REAL] RETURNS [boxRec: BoxRec] = { boxRec _ NEW[BoxRecObj]; boxRec.poly _ SVPolygon3d.CreatePoly[len: 4]; boxRec.poly _ SVPolygon3d.AddPolyPoint[boxRec.poly, [0,0,0]]; boxRec.poly _ SVPolygon3d.AddPolyPoint[boxRec.poly, [0,h,0]]; boxRec.poly _ SVPolygon3d.AddPolyPoint[boxRec.poly, [w,h,0]]; boxRec.poly _ SVPolygon3d.AddPolyPoint[boxRec.poly, [w,0,0]]; boxRec.bBox _ GGBoundBox.CreateBoundBox[0,0,w,h]; }; BoxMakeMasterObject: PUBLIC PROC [name: Rope.ROPE, w, h: REAL] RETURNS [mo: MasterObject] = { mainBody: BoxRec _ BoxBody[w, h]; lineBody: REF ANY _ mainBody; shadeBody: REF ANY _ mainBody; rayCastBody: REF ANY _ mainBody; mo _ SVScene.CreateMasterObject[name, blockClass, mainBody, lineBody, shadeBody, rayCastBody]; }; BoxBoundHedron: PUBLIC PROC [mo: MasterObject] RETURNS [hedron: BoundHedron] = { boxRec: BoxRec; bBox: BoundBox; boxRec _ NARROW[mo.mainBody]; bBox _ boxRec.bBox; hedron _ SVBoundBox.RectangularBoundHedron2[bBox.loX,bBox.hiX,bBox.loY,bBox.hiY,0,0]; }; BoxLineDraw: PUBLIC PROC [slice: Slice, dc: Imager.Context, camera: Camera] = { mo: MasterObject _ NARROW[slice.shape, Shape].mo; localCS: CoordSystem _ slice.coordSys; boxRec: BoxRec _ NARROW[mo.lineBody]; poly: Poly3d _ boxRec.poly; localCamera: Matrix4by4 _ SVCoordSys.WRTCamera[localCS, camera.coordSys]; SVGraphics.SetCP[dc, poly[0], camera, localCamera]; SVGraphics.DrawTo[dc, poly[1], camera, localCamera]; SVGraphics.DrawTo[dc, poly[2], camera, localCamera]; SVGraphics.DrawTo[dc, poly[3], camera, localCamera]; SVGraphics.DrawTo[dc, poly[0], camera, localCamera]; }; blockClass, boxClass: MasterObjectClass; Init: PROC = { block: MasterObject; blockClass _ BuildBlockClass[]; SVMasterObject.RegisterMasterObjectClass[blockClass]; block _ BlockMakeMasterObject["block"]; RegisterMasterObject[block]; boxClass _ BuildBoxClass[]; SVMasterObject.RegisterMasterObjectClass[boxClass]; }; Init[]; END. äFile: SVMasterObjectImplC.mesa Last edited by: Eric Bier on September 23, 1987 5:29:57 pm PDT Copyright c 1984 by Xerox Corporation. All rights reserved. Contents: Defines a simple set of objects which can be display with ray casting, line drawing, or shaded planar-surface approximation. GENERAL TYPES Fundamentals Drawing Transforming Textual Description Parts Part Generators Hit Testing Miscellaneous Style Make a light blue plastic box. Fundamentals Drawing Returns the surfaces in shape coordinates. The normal is a unit normal. avgDepth is camera dependent. Backfacing test Compute Polygon normal in transformed shape coordinates blockData.box is the current position of our block in shape coordinates. This procedure perturbs some of the vertices in shape coordinates by an amount specified in transform. This is only appropriate if the transformation is a translation or scaling. Rotations should be handled by other means. Transformations of the entire box may be handled by other means as well. All of this data is in shape coordinates. Transforming All of this data is in shape coordinates. Textual Description Stroke Colors Fill Colors Fileout stroke colors Fileout face artworks Parts [Artwork node; type 'Artwork on' to command tool] [Artwork node; type 'Artwork on' to command tool] cornerNum is the number of the last corner counted, if any. Otherwise cornerNum is undefined. centerNum is the number of the last edge counted, if any. Otherwise edgeNum is centerNum. edgeNum is the number of the last edge counted, if any. Otherwise edgeNum is undefined. edgeNum is the number of the last edge counted, if any. Otherwise edgeNum is undefined. Currently, this routine finds the distance from the testpoint to the plane of the face and only works if worldShape scales evenly. [Artwork node; type 'Artwork on' to command tool] SVModelTypes.SliceMovingPartsProc If anything other than only one corner or only one edge or only one face is moving, everything is moving If only one corner is moving, everything except its opposite corner is moving. If only one edge is moving, everything excepts its opposite edge and the opposite edge corners are moving. If only one face is moving, everything excepts its opposite face and its edges and corners are moving. If the block is drawn shaded and only partly moving, its stationary parts go on the overlay plane. If wireframe, they may be left in the background. For each selected face, select all of its edges. For each selected edge, select all of its vertices. Part Generators Hit Testing RETURN[0, -1, FALSE]; Choose the surface normal of the face that is nearest to testPoint in 3-space. Boundbox is in camera coords so this is easy. Compute the surface normal, normalWorld thisPointCamera _ SVMatrix3d.Update[thisPoint, worldCamera]; thisDist _ thisDist * (camera.focalLength / (camera.focalLength - thisPointCamera[3])); Compute the surface normal, normalWorld Style Miscellaneous Fundamentals Drawing Textual Description Parts Part Generators Hit Testing Miscellaneous We represent the box with a Poly3d. Our box stays in the z=0 plane of its own coordinate system. Its left, top, right, and bottom edges may move about in this plane but left and right remain vertical and top and bottom horizontal. Êaì˜Iheadšœ™Iprocšœ>™>Jšœ Ïmœ1™˜>Lšœ žœžœ ˜Lšœ žœžœ ˜2Lšœ žœžœ˜-Lšœ^˜^Lšœ˜L˜—š œžœžœ žœžœžœžœ/˜«Lšœ˜Lšœ?˜?Mšœžœžœžœ žœžœ žœ žœžœ žœžœ˜‘Mšœžœ˜"L˜L˜—š œžœžœ#žœ˜aLšœžœ˜%Lšœ˜Lšœžœ˜+Lšœ+˜+L˜L˜—š œžœžœ!˜ELšœžœ˜%šžœžœžœž˜Lšœžœ˜5Lšžœ˜—Lšœ¡˜ L˜—š œžœ&˜;Lšœ˜Lšœžœ˜4Lšœ&˜&Lšœžœ˜.Lšœ˜šžœžœžœž˜Lšœ žœ˜$Lš œžœžœžœžœžœ˜HLšžœ˜—Lšœ¡˜ L˜—M™Mšœ ™ š œžœžœžœ˜RLšœžœ˜+Lšœ˜Lšœb˜bLšœ˜L˜—M™Mšœ™š  œžœžœžœ˜Hšœžœž˜Lšœ!˜!Lšœ!˜!Lšœ!˜!Lšœ!˜!Lšœ!˜!Lšœ!˜!Lšœ!˜!Lšœ!˜!Lšžœž˜—Lšœ˜L˜—š œžœžœ˜@Lšœ!˜!Lšœ!˜!Lšœ!˜!L˜L˜—š œžœžœ#žœ˜^Lšœžœ˜Lšœ žœ ˜Lšœ žœ˜Lšœ¢œ ˜Lšœ!˜!L˜šžœžœžœž˜Lšœ¢œ ˜Lšœ¢œ˜Lšœ žœ˜(Lšœ žœ˜&Lšœ˜Lšžœ˜—Lšœ˜Lšœ˜L˜—š œžœ6¢œ žœ˜~Lšœg™gš  œžœžœžœžœžœ!ž œ˜ÝLšœ ˜ Lšœ!žœ˜&Lšœ¢œ ˜Lšœ˜L˜ Lšœ˜LšŸ™šž˜Jšžœ˜Jšœ˜Lšœ5¢œ ˜DLšœI˜ILš žœ¢œžœžœžœžœ˜QLšžœ˜—LšŸ™Lšœ!˜!šžœžœžœž˜Lšœ¢œI˜TLšœ+¢œ˜3Lšžœ˜—Lšœ@˜@Lšœ*˜*Lšœ žœn˜~Lšœ8¢œ ˜Gšœžœ˜Lšœ˜Lšœ˜Lšœ˜L˜ Lšœ ˜ Lšœ˜Lšœ˜Lšœ˜Lšœ˜—L˜—Lšœžœžœžœ˜Lšœžœ˜#Lšœ&˜&Jšœ¢œ6˜@Lšœ¢œB˜MLšœ˜Lšœžœ˜+Lšœ˜L˜Lšœ˜Lšœ žœžœ˜L˜Lšœžœ˜ šžœžœžœž˜L˜Lšœ˜Lšœ%˜%LšœW˜WLšžœžœ žœžœ˜0Lšžœ˜—L˜L˜—š œžœM¢œ žœ˜žš œžœžœžœžœžœ!žœ˜íLšœ)˜)Lšœ!žœ˜&Lšœ˜Lšœ¢œ ˜Lšœ˜L˜ šž˜Jšžœ˜Jšœ˜Lšœ5¢œ ˜DLšœ_˜_Lš žœ¢œžœžœžœžœ˜QLšžœ˜—šžœžœžœž˜Lšœ¢œ_˜jLšœ+¢œ˜3Lšžœ˜—Lšœ@˜@Lšœ*˜*Lšœ žœn˜~Lšœ8¢œ ˜Gšœžœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ ˜ L˜ Lšœ˜Lšœ˜Lšœ˜Lšœ˜—L˜—Lšœžœžœžœ˜Lšœžœ˜#Lšœ&˜&Jšœ¢œ6˜@Lšœ¢œB˜MLšœ5˜5Lšœ˜Lšœžœ˜+Lšœ˜L˜Lšœ˜Lšœ žœžœ˜L˜Lšœžœ˜ šžœžœžœž˜L˜Lšœ˜L•StartOfExpansion.[vec: SV3d.Vector3d, mat: SV3d.Matrix4by4]šœL˜LLšœ'™'Lšœ%˜%Lšœk˜kLšžœžœ žœžœ˜0Lšžœ˜—L˜L˜—š  œžœdžœ˜~Lšœžœ˜1Lšœ˜LšœJ˜JLšœ<˜˜ILšœ.¢œ˜6Lšœ˜L˜—š œžœžœY˜|Lšœ˜Lšœžœ˜#Lšœ&˜&Lšœ¢œ>˜ILšœ¢œ,˜6Lšœ¢œ>˜ILšœžœ˜1Lšœžœ˜+L˜L˜šž˜Lšœžœžœ˜Lšžœžœžœ˜8LšœB¢œ ˜TLšžœžœžœ˜(Lšžœ)¢œ˜5šž˜˜Mšœ=¢œ¢œ˜]M˜——Mšžœ˜—Lšœ˜L˜—š  œžœ¢œžœ¢œ˜[Mšœ¢œ ˜Mšœ¢œ(˜.Mšœ¢œ&¢œ¢œ˜;L˜L˜—š  œžœ6¢œ%žœžœžœ˜ªLšœô™ôLšœžœ˜.Lšœ@žœ˜HMšœ˜Mšœ¢œ'¢œ˜8Mšœ¢œ#¢œ¢œ˜Kšž˜Mšœ4˜4Mšœ4˜4Mšœ<˜˜>Mšœ&¢œ˜-M˜—š žœžœžœžœžœ˜BMšœ¢œ0¢œ˜=Mšœžœ˜ M˜"Mšœ)˜)Mšœ7˜7šžœ ž˜Mšœ¡œ¢œ¢œ˜aMšœ¡œ ¢œ¢œ˜aMšœ¡œ¢œ¢œ˜cMšžœžœ˜—M˜—š žœžœžœžœžœ˜BMšœ¢œ0¢œ˜=Mšœ žœžœžœ˜Mšœ!˜!Mšœ1˜1Mšœ?˜?šžœ ž˜Mšœ¡œ ¢œ˜PMšœ¡œ¢œ˜PMšœ¡œ*¢œ˜PMšžœžœ˜—M˜—Mšžœžœ˜šž˜šœ˜Mšœž˜Mšœ(˜(Mšœ6˜6Mšœ&¢œ˜-Mšœ"¢œ˜)M˜——Mšžœ˜—Mšœ žœ˜!Mšœ žœ˜!Mšœ žœ˜!Mšœ žœ˜!Mšœ žœ˜!Mšœ žœ˜!L˜L˜—š  œžœžœžœžœ˜OLšœžœ˜5Lšœ>˜>Lšœ˜—š  œžœžœžœžœ˜OLšœžœ˜5Lšœ8˜8Lšœ˜L˜—š   œžœžœ(¢œ žœ˜uLšœžœ˜#Lšœ˜Lšœžœ˜+Lšœ6¢œ˜=Lšœ˜—š  œžœžœ3¢œ"žœ˜ Lšœžœ˜*Lšœ˜Lšœžœ˜+Lšœ¢œ3˜=Lšœ ˜ Lšœžœžœ˜Lšœ?¢œ ˜Qšžœžœ˜Mšœ¢œ'¢œ˜8Mšœ¢œ#¢œ¢œ˜KLšœH¢œ¢œ˜YM˜—Lšžœ5¢œ˜@Lšœ˜L˜—š  œžœžœžœžœ+˜jLšœ˜Lšžœžœ˜Lšžœ žœ˜Lšžœžœ ˜+LšœE˜ELšœ˜—š   œžœžœ`žœžœžœ˜©Lšœ˜Lšœžœ˜+Lšžœ3žœ"˜aLšœ˜—š  œžœžœJžœžœžœ˜›Lšœž˜Lšœžœ˜+Lšžœ3žœ"˜aLšœ˜—š  œžœžœJžœžœžœ˜¢Lšœ˜Lšœžœ˜+Lšžœ3žœ"˜aLšœ˜—š   œžœžœ[¢œžœ˜šLšœ6˜6Lšœ˜—M™š  Ðbnœžœžœ”žœ˜Äš œžœ˜Mšœ˜M˜ Mšœžœ˜$Mšœžœžœ ˜šžœžœžœž˜Mšœ7¢œ˜>Mšžœ˜—šžœ žœžœž˜ Mšœžœžœ˜BMšœ$žœžœ"˜MMšžœ žœ:˜MMšžœžœ=˜UMš žœžœ žœžœžœ*˜ZMšžœ˜—Mšœžœžœ˜;Mšœ$žœžœ˜FMšœc¢œ˜jMšžœ žœ1˜DMšžœžœ4˜LMš žœžœ žœžœžœ!˜QM˜—Mšœžœ˜#Mšœžœ˜1Mšœ¢œ3˜=M˜,M˜Mšžœžœžœžœ˜/Mš žœžœžœ žœžœžœ˜6Mšœžœ˜)Mšœžœ ˜!Mšžœžœ#˜AM˜M˜—M™ š œžœC˜WLšœ˜Lšœžœ˜#Mšœ˜Lšœ&˜&Lšœ¢œ,˜6Lšœžœ˜1Lšœžœ˜+Lšœžœ˜.Lšœ@žœ˜HL˜Mšž˜Lšžœžœžœ˜8Mšœ4˜4Mšœ4˜4Mšœ<˜˜>Mšœ&¢œ˜-M˜—š žœžœžœžœžœ˜BMšœ¢œ'¢œ˜8Mšœ¢œ0¢œ˜=Mšœžœ˜ M˜"Mšœ)˜)Mšœ7˜7šžœ ž˜Mšœ¡œ¢œ¢œ˜aMšœ¡œ ¢œ¢œ˜aMšœ¡œ¢œ¢œ˜cMšžœžœ˜—M˜—š žœžœžœžœžœ˜BMšœ¢œ'¢œ˜8Mšœ¢œ0¢œ˜=Mšœ žœžœžœ˜Mšœ!˜!Mšœ1˜1Mšœ?˜?šžœ ž˜Mšœ¡œ ¢œ˜PMšœ¡œ¢œ˜PMšœ¡œ*¢œ˜PMšžœžœ˜—M˜—Mšžœžœ˜Mšœžœ˜*Mšœžœ˜*Mšœžœ˜*Mšœžœ˜*Mšœžœ˜*Mšœžœ˜*šž˜˜Jšœ¢œ3˜@Jšœ ¢œ'¢œ ˜IJšœ.¢œ˜5M˜——Mšžœ˜M˜M˜—M˜Mšœ™š £œžœžœžœžœ žœ˜YMšœ˜Mšœ žœ˜Mšžœ žœžœžœ ˜(Mšœžœ ˜Mšžœžœ/˜MMšžœžœžœ/˜RMšžœžœžœ˜9Mšžœžœžœ+˜LMšžœžœžœ-˜NMšžœžœ˜ Mšœ*˜*M˜M˜—šŸ œžœžœ žœ˜KMšœ žœ˜Mšœžœ˜.MšœJž˜RMšœ>˜>Mšœ6˜6Mšœ>˜>šžœ%žœžœ"žœ˜ZMš œžœžœ žœžœ˜]Mš œ žœžœžœ žœžœ˜\Mš œ žœžœžœ žœžœ˜^Mš œ žœžœžœ žœžœ˜^Mš œ žœžœžœ žœžœ˜\šžœžœž˜Mšœ4˜4Mšœ4˜4Mšœ.˜.Mšœ$˜$Mšœ˜Mšžœžœ˜—Mšœ*˜*M˜—M˜M˜—š  œžœžœžœžœ žœ;žœ žœžœ˜½šžœžœ˜Lšœžœ˜#Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ?˜?LšŸ ™ šžœžœ˜Lšœ žœžœ˜-Lšœ˜L˜ Lšœžœ˜+šžœžœ˜Lšœ˜Lšœ˜Lšœ7˜7Lšžœžœžœžœ˜Lšœ˜Lšœ(˜(šžœžœžœ ž˜šžœžœžœ˜Lšœ˜Lšœ(˜(L˜—Lšœ$˜$Lšžœ˜—L˜—Lšžœ žœ˜LšŸ ™ Lšœ˜Lšœ˜Lšœ3˜3Lšžœžœžœžœ˜Lšœ˜LšœO˜Ošžœžœžœž˜šžœžœ žœ˜Lšœ˜LšœO˜OL˜—Lšœ%˜%Lšžœ˜—L˜—L˜—šžœ˜Lšœ+˜+Lšœ˜Lšœ!˜!L˜—Lšœ˜—L˜š œžœžœ žœ˜\Lšœ3˜3Lšœ˜Lšœ˜Lšœ žœž˜Lšœ ˜ L˜šžœžœžœ˜Lšœ˜Lšœ!˜!Lšœ˜Lšœ˜L˜—šžœžœžœž˜šžœžœžœ˜šžœ"ž˜,Lš œ žœžœEžœžœžœ˜yLšžœžœžœ˜'—L˜—šžœ˜Lš žœžœžœžœžœ˜EL˜—Lšžœ˜—Lšžœžœ˜L˜L˜—š œžœžœ žœ˜\Lšœ0˜0šžœžœžœ ž˜Lš žœžœEžœžœžœ˜jLšžœ˜—Lšžœžœ˜L˜L˜—š   œžœžœžœžœ˜>Lšœžœ˜+Lšœžœ˜L˜Lšœ˜Lšœf˜fLšœf˜fLšœ?˜?L˜Lšœ'˜'Lšœ žœžœ˜Lšœ&˜&Lšœ3˜3L˜Lšœ#˜#Lšœ žœžœ˜Lšœ$˜$Lšœ žœžœ˜L™šžœžœžœ ž˜Lšœ%˜%šžœžœžœ˜Mšœ$˜$Mšœ žœžœ˜L˜—Lšžœ˜—L™šžœžœžœž˜L˜ šžœžœ žœ˜Mšœ)˜)Mšœ žœžœ˜L˜—Lšžœ˜—Lšœ˜L˜—š  œžœžœžœžœ˜BLšœžœ˜+Lšœ+˜+L˜—J™Jšœ™š  œžœ žœžœ žœ˜>šžœž˜Mšœ&žœ˜1Mšœ&žœ˜-Mšžœžœ˜—M˜M˜—š   œžœžœžœ žœ˜9šžœž˜Mšœ)žœ˜1Mšœ*žœ ˜7Mšœžœžœ ˜9Mšžœžœ˜—M˜M˜—š  œžœžœžœ žœžœžœ˜Išžœž˜Mš žœ žœžœžœžœžœ˜Ešžœ ˜Mšœ ˜ Mšœ ˜ Mšœžœ˜-Mšœžœ˜'M˜—Mšžœžœ˜—˜ artworkFigure–Ó49.71277 mm topLeading 49.71277 mm topIndent 1.411111 mm bottomLeading 0.5 0.3 1.0 backgroundColor the topLeading 6 pt .sub backgroundAscent 3 pt backgroundDescent 6 pt outlineBoxThickness 1 pt outlineBoxBearoff•Bounds:0.0 mm xmin 0.0 mm ymin 60.61588 mm xmax 46.89055 mm ymax •Artwork Interpress• InterpressŽ Interpress/Xerox/3.0  f j k j¡¥“ļ= ¤ ¨  Ä&B9Äxuè¡£ r jÄÿY­ÄIJíF ¢ ¨¡¡¨ ¡¨Ä©cÄÄ4K&™Ä&-%Ä$—ÄSÄ©cÄÄ‘[—Ä4K&¡¡¡¡™¢¯“¢°“¢·“¡¡¨Ä©cÄÄ4K&™Ä&-%Ä$—ÄSÄ©cÄÄ‘[—Ä4K&¡¸ ¡¨Ä᧲ÄN+F™ÄqÇ´Ä_’K—ÄdÁYŽÄ7ñ:ÄN+F—Ä᧲Ž¡¡¡¡™¢¯“¢°“¢·“¡¡¨Ä᧲ÄN+F™ÄqÇ´Ä_’K—ÄdÁYŽÄ7ñ:ÄN+F—Ä᧲Ž¡¸ ¡¨Ä|ßmÄ\,E™Ä`²=ij§|ŽÄ\,EÄ|ßmŽ¡¡¡¡™¢¯“¢°“¢·“¡¡¨Ä|ßmÄ\,E™Ä`²=ij§|ŽÄ\,EÄ|ßmŽ¡¸ ¡¨ÄÎ×ÚÄmR_™Ät5SÄØ™­ŽÄmR_ÄÎ×ÚŽ¡¡¡¡™¢¯“¢°“¢·“¡¡¨ÄÎ×ÚÄmR_™Ät5SÄØ™­ŽÄmR_ÄÎ×ÚŽ¡¸ ¡¨ÄVáAÄ4K&™Ä/×ÊÄ$—ÄSÄVáAÄ‘[—Ä4K&¡¡¡¡™¢¯“¢°“¢·“¡¡¨ÄVáAÄ4K&™Ä/×ÊÄ$—ÄSÄVáAÄ‘[—Ä4K&¡¸ ¡¨Ä᧲Ä—ñi™ÄqÇ´Ä}—ÄdÁYŽÄ7ñ:Ä—ñi—Ä᧲Ž¡¡¡¡™¢¯“¢°“¢·“¡¡¨Ä᧲Ä—ñi™ÄqÇ´Ä}—ÄdÁYŽÄ7ñ:Ä—ñi—Ä᧲Ž¡¸ r jª ¤Ä“âÄÏT ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á0– k é r jª ¤Äk7SÄ,Ó% ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á1– k é r jª ¤Ä3¶9ÄqáW ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á2– k é r jª ¤ÄCÐ3Ä—ñi ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á3– k é r jª ¤ÄS;Ä8Þ+ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á4– k é r jª ¤¶ä ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á5– k é k é k gšœ3™3M˜——N–Ó71.96666 mm topLeading 71.96666 mm topIndent 1.411111 mm bottomLeading 0.5 0.3 1.0 backgroundColor the topLeading 6 pt .sub backgroundAscent 3 pt backgroundDescent 6 pt outlineBoxThickness 1 pt outlineBoxBearoff–:0.0 mm xmin 0.0 mm ymin 68.74785 mm xmax 69.14444 mm ymax – Interpress–€ Interpress/Xerox/3.0  f j k j¡¥“ļ= ¤ ¨  Ätx™d¡£ r jèÄþ+ ¢ ¨¡¡¨¢¯“¢°“¢·“¡¡¨Ä‰m¯Äº ™Ä[gYÄd:?—Äà ™Ž˜¢¯“¢°“¢·“¡¡¨Ùĺ ™Äà ™Äd:?—˜¢¯“¢°“¢·“¡¡¨Ä‰m¯Äº ™ÙŽ˜¢¯“¢°“¢·“¡¡¨Ä‰m¯¡™ÙŽ˜¢¯“¢°“¢·“¡¡¨Ù¡™Äà ™Äf1S—˜¢¯“¢°“¢·“¡¡¨Ä‰m¯¡™Ä[gYÄf1S—Äà ™Ž˜¢¯“¢°“¢·“¡¡¨Ä‰m¯Äº ™¡˜¢¯“¢°“¢·“¡¡¨Ä[gYÄd:?™Äf1S˜¢¯“¢°“¢·“¡¡¨Äà ™Äd:?™Äf1S˜¢¯“¢°“¢·“¡¡¨Ùĺ ™¡˜ r jª ¤xÄÿ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á0– k é r jª ¤ÖÄA ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á1– k é r jª ¤æÄõ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á2– k é r jª ¤¯ÄÇ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á3– k é r jª ¤ƒÄG ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á4– k é r jª ¤òÄ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á5– k é r jª ¤þÄ- ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á6– k é r jª ¤©Äã ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á7– k é r jª ¤\Ö ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á8– k é r jª ¤œÄí ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á9– k é r jª ¤ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á10– k é r jª ¤ÅÄ3 ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á11– k é k é k gš 3™3—M˜š   œžœžœžœ žœ˜:šžœž˜Mšœ$žœ˜/Mšœ$žœ˜+Mšœ$žœ˜/Mšžœžœ˜—M˜M˜—š  œžœžœžœ žœžœžœ˜Išžœž˜Mšžœ˜Mšžœ˜Mšžœ#˜%Mšžœžœ˜—šžœž˜Mšžœ"˜$Mšžœ"˜$Mšžœ#˜%Mšœ˜Mšžœžœ˜—M˜M˜—š  œžœ žœžœ žœžœžœ˜Mšžœž˜Mšžœ˜Mšžœ˜Mšžœžœ˜—šžœž˜Mšžœ$˜&Mšžœ$˜&Mšžœžœ˜—šžœž˜Mšœ˜Mšžœ$˜&Mšžœ$˜&Mšžœžœ˜—M˜M˜—š  œžœžœžœ žœžœžœ˜Išžœ ˜Mš œžœžœžœžœžœ˜B˜ Mšœ"˜"Mšœ$˜$Mšœ"˜"MšœA˜AM˜—Mšžœž˜—˜M˜—M˜—š   œžœžœžœ žœ˜:šžœž˜Mšœ$žœ˜+Mšœ$žœ˜+Mšžœžœ˜—M˜M˜—š   œžœžœ žœ žœ˜TMšœ^™^M˜ Mšœ˜šžœ žœžœž˜ Mšžœ žœ'˜8Mšžœ˜—Mšœ˜M˜—š   œžœžœ žœ žœ˜TMšœZ™ZM˜ Mšœ˜šžœ žœžœž˜ Mšžœ žœ'˜8Mšžœ˜—Mšœ˜M˜—š   œžœžœ žœ žœ˜NMšœX™XM˜ Mšœ ˜ šžœžœžœ ž˜Mšžœ žœ#˜2Mšžœ˜—Mšœ˜M˜—š   œžœžœ žœ žœ˜NMšœX™XM˜ Mšœ ˜ šžœžœžœž˜Mšžœ žœ#˜2Mšžœ˜—Mšœ˜M˜—šœžœžœ ˜*M˜7M˜—šœžœžœžœ˜/M˜GM˜RM˜—šœžœžœžœ˜.M˜1M˜=M˜6M˜—šœžœžœžœ˜/M˜1M˜M˜—M˜š  œžœžœ žœ¢œ˜iJšœ¢œ ˜Jšœ+˜+Jšœ¢œC˜NJšœ¢œ,¢œ˜LM˜M˜—š  œžœžœžœ¢œ˜WJšœ¢œ ˜Jšœ+˜+Jšœ¢œ1˜;Jšœ¢œ+¢œ˜JM˜M˜—š  œžœžœ¢œ žœžœ˜`Mšœn¢œ™‚Mšœ¢œ ˜Mšœ˜Mšœžœ˜+Mšœ¢œ=˜GMšœ¢œ3˜=Mšœ˜M–/[point: SV3d.Point3d, mat: SV3d.Matrix4by4]šœ¢œ¢œ¢œ˜5M˜šžœž˜Mšœ¢œ˜9Mšœ¢œ˜9Mšœ¢œ˜9Mšœ¢œ˜9Mšœ¢œ˜9Mšœ¢œ˜9Mšžœžœ˜—Mšœ.¢œ ˜?Mšœ&˜&M˜M˜—š  œžœ˜+šžœžœž˜šœ˜Mšœžœžœ˜Mšœžœžœ˜Mšœžœ˜Mšœžœžœ˜Mšœžœžœ˜M˜—Mšžœžœ˜—M˜M˜—š œžœžœžœ˜GMšœžœ˜.Mšžœžœžœžœžœžœžœžœžœžœžœ˜„M˜M˜—š œžœžœžœ˜DMšœžœ˜.Mšžœžœžœžœžœžœžœžœžœžœžœžœžœžœ˜ªM˜M˜—š   œžœžœžœ'žœ˜Mšœžœ ˜-Mšœ˜Mšœ*žœ˜.Mšœ žœžœžœ žœžœ žœ žœžœ žœžœ˜„šžœž˜šœ ˜ Mšžœžœ+ž˜MMšžœžœžœ+ž˜RMšžœžœžœž˜@šžœžœžœžœ˜;MšœJ˜JMšžœ žœž˜.Mšžœžœžœ#ž˜BMšžœ#žœ˜,M˜—M˜—šœ ˜ Mšžœžœ˜9Mšžœžœžœ˜M˜Mšœ<˜Mšœžœ˜ Mšœžœ˜"Mšœžœ˜!Mšœžœ˜#šžœžœžœž˜Mšœžœ˜%Mšœžœ˜%Mšœ#žœ˜)Mšœ#žœ˜)Mšžœ˜—MšŸœ3˜9Mšœ žœžœžœ žœžœ žœ žœžœ žœžœ˜…Mšœžœ˜ Mšœžœ˜"šžœžœžœž˜Mšœ žœ˜%Mšœ$žœ˜)Mšžœ˜—MšŸœ4˜8Mšžœ˜M˜—šžœžœ žœžœžœžœžœžœ¡˜—Mšœ&˜&Mšœ7˜7Mšžœ˜Mšœ˜—šžœžœ¡˜6Mšœ.žœ˜4Mšœ˜Mšœ9˜9Mšœžœžœžœ žœžœ žœ žœžœ žœžœ˜ˆMšœ"žœ˜'Mšœ>˜>Mšžœžœžœ)˜;M˜—šžœžœ žœ¡-˜HMšœ5žœ˜JMšœžœ˜Mšœ%˜%Mšœ'˜'Mšœžœ˜%Mšœžœ˜Mšœžœ˜Mšœž˜Mšœ9˜9Mšœ/˜/Mšžœžœžœ)˜;M˜—šžœ¡˜Mšœ=˜=Mšœ ˜ Mšœ˜—M˜M˜—M™š œžœžœ˜QMšœ žœ˜Mšœ˜M˜M˜—š£œžœžœ8žœ˜yM™eMšœžœ˜.Mšœ˜Mšœžœ˜ Mšœ žœžœžœ˜Mšœ&˜&šžœžœžœž˜šžœžœ˜J˜šžœžœžœž˜Jšœžœ˜#Jšžœ˜—J˜—Jšžœ˜—šžœžœžœ ž˜šžœžœ˜J˜Jšœžœ˜Jšœžœ˜J˜—Jšžœ˜—Jšœ>˜>M˜M˜—M™Mšœ™Lšœžœžœ˜)šœžœžœ˜ Lšœ˜Lšœ˜L˜L˜—š œžœ&žœžœ˜oLšœžœ˜.Lšœžœžœ)˜ML˜Mšœžœ˜$Mšœ¡œ%˜7Mš œžœžœ žœžœ˜CMšœžœ,˜>šœ žœ˜$Lšœ˜Lšœžœžœ˜2L˜ Lšœ˜Lšœ˜L˜—L˜L˜—š œžœžœ!˜XLšžœžœžœ˜8šžœ˜Mšœžœ˜Lšœžœ˜šœ žœ˜$Mšœžœ˜Mšœ ˜ Mšœ˜Mšœ˜—M˜M˜—š œžœžœ˜PMšžœžœžœžœ˜#šžœ˜Lšœžœ˜1Lšœ1˜1Mšœ.˜.Mšœžœ˜šžœžœ ž˜5Mšžœžœžœ¡+˜Qšž˜šžœ˜ Mšžœ˜M˜——Mšžœ˜—Mšœ$˜$Mšœ˜Mšœžœžœžœ ˜5M˜—M˜—šœ žœžœ˜M˜ Mšœž˜M˜M˜—š œžœžœ˜YMšžœžœžœžœ˜)šžœ˜Lšœžœ˜1Lšœ1˜1Mšœ.˜.Mšœžœ˜šžœžœ ž˜5Mšžœžœžœ¡+˜Qšž˜šžœ˜ Mšžœ˜M˜——Mšžœ˜—Mšœ%˜%Mšœ˜Mšœ˜Mšœžœžœžœ ˜5M˜—M˜M˜M˜—M™Mšœ ™ š  œžœ+ž œžœ&žœ žœ˜©Mšžœžœ™M˜Mšœ˜M˜M˜ Mšœžœ˜Mšœ˜M˜Mšœ$žœ˜9Mšœ:˜:šž˜Mšœ žœ˜Mšœ˜Mšœžœ˜Mšœ˜M˜Mšœ žœžœ˜šžœ]žœž˜zMšœ˜Mšœ1˜1šžœžœ˜Mšœ˜Mšœ˜Mšœ˜M˜—Mšœ˜Mšžœ˜—Mšžœ žœžœ;˜VMšžœžœžœ3˜MMšžœžœ˜ Mšžœ˜—Mšœ˜M˜—š œžœ2žœ žœžœ žœ¢œ¢œžœžœ žœžœ˜×Mšœ.žœ˜6Mšœžœ˜!Mšœžœ˜*Mšœ*¢œ9˜hMšœ žœa¢œ˜vMšŸN™NMšœ"˜"M˜šžœžœžœž˜Mšœ8˜8šžœžœ˜%Mšœ˜M˜ M˜—Mšžœ˜—Mšœ¢œ(˜3M˜M˜—š  œžœ<žœžœ˜`Lšœ-™-Lš žœžœžœžœ¡˜=Lšžœžœžœžœ˜$šžœ˜Lšœžœ˜?Lšž˜Lšœžœ˜@—Lšœ¡˜L˜—Mšœ žœžœ˜š œžœDžœžœžœ žœ¢œ¢œžœžœ žœžœ˜ùMšœ¢œ ˜Mšœ˜M˜ Mšœ žœ˜M˜Mšœžœ˜Mšœžœ˜M–^[assem: SVSceneTypes.Slice, parts: SVSceneTypes.SliceParts, camera: SVModelTypes.Camera]šœQ˜QMšœ'˜'MšœTžœžœ˜bM˜šžœžœ žœ)žœ˜BMšœ¢œ ˜-Mšœžœžœžœ˜Mšœ¢œ ˜Mšœžœ˜Mšœ¢œ>˜IM˜Mšœžœ˜#Mšœ¢œ'˜1M˜Mšœ˜Mšœ6˜6šžœQžœžœž˜uMšœ7¢œ˜>Mšœ<˜<šžœžœ˜Mšœ˜Mšœ¢œ ˜Mšœ˜M˜—Mšœ˜Mšžœ˜—Mšžœ žœžœ$˜?Mšžœžœžœ˜6Mšžœžœ˜ Mšœ žœ˜Mšœ žœG¢œ˜\MšŸ"Ðbu™'šžœžœ˜Mšœ¢œ!¢œ˜3M˜—šžœ˜Mšœ¢œ¢œ¢œ˜9Mšœ˜M˜MšœA¢œ˜Išžœžœžœž˜Mšœ ¢œ1˜AMšœ<¢œ˜Dšžœžœ˜Mšœ˜Mšœ¢œ(˜3M˜—Mšžœ˜—Mšžœžœ¢œ!˜BM˜—M˜—M˜M˜—š œžœDžœžœ žœ¢œ¢œžœžœ žœžœ˜êMšœ¢œ ˜Mšœ˜Mšœ žœ˜M˜Mšœ žœ˜Mšœžœ˜ M–^[assem: SVSceneTypes.Slice, parts: SVSceneTypes.SliceParts, camera: SVModelTypes.Camera]šœQ˜QMšœ'˜'MšœTžœžœ˜bM˜šžœžœ žœ)žœ˜BMšœžœ˜*Mšœžœ˜1Mšœ¢œ ˜Mšœ¢œ ˜-Mšœžœžœžœ˜Mšœžœ˜Mšœ¢œ>˜IMšœ¢œ'˜.M˜Mšœ˜Mšœ+˜+šžœXžœ žœž˜rM˜%Mšœf˜fMšœ-¢œ˜CMšœT˜TMšœ ¢œ%¢œ™˜IL˜Lšœ+¢œ˜3Lšœ,¢œ˜4Lšœ,¢œ˜4Lšœ,¢œ˜4Lšœ,¢œ˜4Lšœ˜L˜—Lšœ(˜(L˜š œžœ˜Lšœ˜L˜Lšœ5˜5Lšœ'˜'Lšœ˜L˜Lšœ˜Lšœ3˜3L˜L˜—šœ˜L˜—Jšžœ˜—…—fp6