File: SVMasterObjectImplC.mesa
Last edited by: Eric Bier on September 23, 1987 5:29:57 pm PDT
Copyright © 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.
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;
GENERAL TYPES
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",
Fundamentals
getHedron: BlockBoundHedron,
copy: BlockCopy,
Drawing
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,
Transforming
transform: BlockTransform,
Textual Description
describe: BlockDescribe,
describeHit: BlockDescribeHit,
filein: BlockFilein,
fileout: BlockFileout,
fileoutPoly: BlockFileoutPoly,
Parts
isEmptyParts: BlockIsEmptyParts,
isCompleteParts: BlockIsCompleteParts,
newParts: BlockNewParts,
unionParts: BlockUnionParts,
differenceParts: BlockDifferenceParts,
movingParts: BlockMovingParts,
augmentParts: BlockAugmentParts,
Part Generators
pointsInDescriptor: BlockPointsInDescriptor,
nextPoint: BlockNextPoint,
pointPairsInDescriptor: NoOpPointPairsInDescriptor,
nextPointPair: NoOpNextPointPair,
Hit Testing
closestPointToPoint: BlockClosestPointToPoint,
closestPointToLine: BlockClosestPointToLine,
closestSegmentToLine: BlockClosestSegmentToLine,
withinBoundBlock: BlockWithinBoundBlock,
Miscellaneous
update: BlockUpdate,
Style
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] = {
Make a light blue plastic box.
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
Fundamentals
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];
};
Drawing
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] = {
Returns the surfaces in shape coordinates. The normal is a unit normal. avgDepth is camera dependent.
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;
Backfacing test
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;
Compute Polygon
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: BOOLFALSE;
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: BOOLFALSE;
psl ← NIL;
FOR i: NAT IN [0..5] DO
verts ← CornersOfFace[i];
normal ← blockNormals[i];
normal ← SVMatrix3d.UpdateVectorComputeInverse[vec: normal, mat: transform];
normal in transformed shape coordinates
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: BOOLFALSE;
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: BOOLFALSE] = {
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.
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 {
All of this data is in shape coordinates.
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: BOOLTRUE;
[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: BOOLTRUE] 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: BOOLTRUE] 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: BOOLTRUE] 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];
};
Transforming
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 {
All of this data is in shape coordinates.
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;
};
Textual Description
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: BOOLNOT blockParts.centroid AND edgeCount=1 AND cornerCount=0 AND centerCount=0;
oneCorner: BOOLNOT blockParts.centroid AND edgeCount=0 AND cornerCount=1 AND centerCount=0;
oneCenter: BOOLNOT blockParts.centroid AND edgeCount=0 AND cornerCount=0 AND centerCount=1;
noParts: BOOLNOT 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];
Stroke Colors
IF version >= 7.03 THEN {
artworkOK, strokeColorOK, good: BOOLFALSE;
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;
Fill Colors
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];
Fileout stroke colors
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;
Fileout face artworks
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];
};
Parts
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;
};
[Artwork node; type 'Artwork on' to command tool]
[Artwork node; type 'Artwork on' to command tool]
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] = {
cornerNum is the number of the last corner counted, if any. Otherwise cornerNum is undefined.
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] = {
centerNum is the number of the last edge counted, if any. Otherwise edgeNum is centerNum.
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] = {
edgeNum is the number of the last edge counted, if any. Otherwise edgeNum is undefined.
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] = {
edgeNum is the number of the last edge counted, if any. Otherwise edgeNum is undefined.
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] = {
Currently, this routine finds the distance from the testpoint to the plane of the face and only works if worldShape scales evenly.
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];
};
[Artwork node; type 'Artwork on' to command tool]
BlockMovingParts: PROC [slice: Slice, selectedParts: SliceParts] RETURNS [background, overlay, rubber, drag: SliceDescriptor] = {
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.
blockParts: BlockParts ← NARROW[selectedParts];
overlayParts, dragParts: BlockParts;
filled: BOOLTRUE;
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: INTEGERIF 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] = {
For each selected face, select all of its edges. For each selected edge, select all of its vertices.
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];
};
Part Generators
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;
};
};
Hit Testing
NearestBlockPoint: PROC [slice: Slice, point: Point3d, centroids: BOOL ← FALSE] RETURNS [cornerIndex, centerIndex, centroid: INT, bestPoint: Point3d, bestDist: REAL] = {
RETURN[0, -1, FALSE];
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: BOOLTRUE] = {
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]];
Choose the surface normal of the face that is nearest to testPoint in 3-space.
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] = {
Boundbox is in camera coords so this is easy.
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: BOOLTRUE;
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: BOOLFALSE] = {
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]];
Compute the surface normal, normalWorld
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: BOOLFALSE] = {
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];
thisPointCamera ← SVMatrix3d.Update[thisPoint, worldCamera];
thisDist ← thisDist * (camera.focalLength / (camera.focalLength - thisPointCamera[3]));
IF thisDist < bestDist THEN {
bestDist ← thisDist;
pointWorld ← thisPoint;
bestCount ← next.index;
};
ENDLOOP;
success ← TRUE;
hitData ← NEW[BlockHitDataObj ← [edge: bestCount, hitPoint: pointWorld]];
Compute the surface normal, normalWorld
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
];
};
Style
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;
};
Miscellaneous
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",
Fundamentals
getHedron: BoxBoundHedron,
Drawing
lineDraw: BoxLineDraw,
normalsDraw: NoOpNormalsDraw,
preprocess: NoOpPreprocess,
rayCast: NoOpRayCast,
rayCastNoBBoxes: NoOpRayCastNoBBoxes,
rayCastBoundingSpheres: NoOpRayCastBoundingSpheres,
drawSurf: NoOpDrawPlanarSurface,
drawSubBoxes: NoOpDrawSubBoxes,
drawSubSpheres: NoOpDrawSubSpheres,
Textual Description
describe: NoOpDescribe,
describeHit: NoOpDescribeHit,
filein: NoOpFilein,
fileout: NoOpFileout,
fileoutPoly: NoOpFileoutPoly,
Parts
countSurf: NoOpCountPlanarSurfaces,
getSurf: NoOpGetPlanarSurfaces,
isEmptyParts: NoOpIsEmptyParts,
newParts: NoOpNewParts,
unionParts: NoOpUnionParts,
differenceParts: NoOpDifferenceParts,
movingParts: NoOpMovingParts,
augmentParts: NoOpAugmentParts,
Part Generators
pointsInDescriptor: NoOpPointsInDescriptor,
nextPoint: NoOpNextPoint,
Hit Testing
closestPointToPoint: NoOpClosestPointToPoint,
closestPointToLine: NoOpClosestPointToLine,
closestSegmentToLine: NoOpClosestSegmentToLine,
Miscellaneous
update: NoOpUpdate
]];
};
BoxRec: TYPE = REF BoxRecObj;
BoxRecObj: TYPE = RECORD [
poly: Poly3d,
bBox: BoundBox
];
BoxBody: PROC [w, h: REAL] RETURNS [boxRec: BoxRec] = {
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.
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.