File: SVMasterObjectImplC.mesa
Last edited by: Eric Bier on May 30, 1987 5:29:33 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
CoordSys, Imager, IO, Matrix3d, SVObjectCast, PredefSweeps, Real, Rope, SV2d, SV3d, SVAssembly, SVBasicTypes, SVBoundBox, SVFaces, SVGraphics, SVLines3d, SVMasterObject, SVMasterObjectTypes, SVModelTypes, SVPolygon3d, SVRayTypes, SVScene, SVSceneTypes, SVVector3d, SweepCast, SweepGeometry, TFI3d;
SVMasterObjectImplC:
CEDAR PROGRAM
IMPORTS CoordSys, IO, Matrix3d, SVObjectCast, PredefSweeps, Rope, SVAssembly, SVBoundBox, SVGraphics, SVLines3d, SVMasterObject, SVPolygon3d, SVScene, SVVector3d, SweepGeometry, TFI3d
EXPORTS SVMasterObject =
BEGIN
OPEN SVMasterObject;
GENERAL TYPES
Slice: TYPE = SVSceneTypes.Slice;
Scene: TYPE = SVSceneTypes.Scene;
BoundBox: TYPE = SVBasicTypes.BoundBox;
BoundHedron: TYPE = SVBasicTypes.BoundHedron;
Camera: TYPE = SVModelTypes.Camera;
Classification: TYPE = SVRayTypes.Classification;
Composite: TYPE = SVRayTypes.Composite;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
Edge3d: TYPE = SV3d.Edge3d;
LightSourceList: TYPE = SVModelTypes.LightSourceList;
LinearMesh: TYPE = REF LinearMeshRecord;
LinearMeshArray: TYPE = SweepGeometry.LinearMeshArray;
LinearMeshRecord: TYPE = SweepGeometry.LinearMeshRecord;
Line3d: TYPE = SV3d.Line3d;
MasterObject: TYPE = SVSceneTypes.MasterObject;
MasterObjectClass: TYPE = SVSceneTypes.MasterObjectClass;
MasterObjectClassList: TYPE = SVSceneTypes.MasterObjectClassList;
MasterObjectClassObj: TYPE = SVSceneTypes.MasterObjectClassObj;
MasterObjectList: TYPE = SVSceneTypes.MasterObjectList;
Matrix4by4: TYPE = SV3d.Matrix4by4;
Path: TYPE = SV2d.Path;
PlanarSurface: TYPE = SVSceneTypes.PlanarSurface;
PlanarSurfaceObj: TYPE = SVSceneTypes.PlanarSurfaceObj;
PlanarSurfaceList: TYPE = SVSceneTypes.PlanarSurfaceList;
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 = SVRayTypes.Primitive;
Ray: TYPE = SVRayTypes.Ray;
RectSurface: TYPE = REF RectSurfaceObj;
RectSurfaceObj: TYPE = SVObjectCast.RectSurfaceObj;
RevoFace: TYPE = SweepCast.RevoFace;
SelectMode: TYPE = SVSceneTypes.SelectMode;
SelectionClass: TYPE = SVSceneTypes.SelectionClass;
Shape: TYPE = SVSceneTypes.Shape;
SliceDescriptor: TYPE = SVSceneTypes.SliceDescriptor;
SliceParts: TYPE = SVSceneTypes.SliceParts;
SubBoxesBody: TYPE = REF SubBoxesBodyObj;
SubBoxesBodyObj: TYPE = SweepCast.SubBoxesBodyObj;
SubSpheresBody: TYPE = REF SubSpheresBodyObj;
SubSpheresBodyObj: TYPE = SweepCast.SubSpheresBodyObj;
SurfaceArray: TYPE = REF SurfaceArrayObj;
SurfaceArrayObj: TYPE = SVRayTypes.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: NoOpPreprocess,
rayCast: BlockRayCast,
rayCastNoBBoxes: BlockRayCastNoBBoxes,
rayCastBoundingSpheres: BlockRayCastBoundingSpheres,
drawSurf: BlockDrawSurf,
drawSubBoxes: NoOpDrawSubBoxes,
drawSubSpheres: NoOpDrawSubSpheres,
Transforming
transform: BlockTransform,
Textual Description
describe: BlockDescribe,
describeHit: BlockDescribeHit,
filein: BlockFilein,
fileout: BlockFileout,
fileoutPoly: BlockFileoutPoly,
Parts
emptyParts: BlockEmptyParts,
newParts: BlockNewParts,
unionParts: BlockUnionParts,
differenceParts: BlockDifferenceParts,
movingParts: BlockMovingParts,
augmentParts: BlockAugmentParts,
Part Generators
pointsInDescriptor: BlockPointsInDescriptor,
nextPoint: BlockNextPoint,
Hit Testing
closestPointToPoint: BlockClosestPointToPoint,
closestPointToLine: BlockClosestPointToLine,
closestSegmentToLine: BlockClosestSegmentToLine,
Miscellaneous
};
BlockData: TYPE = REF BlockDataObj;
BlockDataObj: TYPE = SVMasterObjectTypes.BlockDataObj;
BlockCopy:
PROC [mo: MasterObject, newName: Rope.
ROPE]
RETURNS [newMO: MasterObject] = {
blockData: BlockData ← NARROW[mo.mainBody];
box: Box3d ← blockData.box;
newMO ← BlockMakeMasterObject[newName, box.loX, box.loY, box.loZ, box.hiX, box.hiY, box.hiZ];
};
BlockBody:
PROC [loX, loY, loZ, hiX, hiY, hiZ:
REAL]
RETURNS [blockData: BlockData] = {
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..12)
DO
blockData.segments[i] ← NEW[SegmentObj];
blockData.segments[i].edge ← SVLines3d.CreateEmptyEdge[];
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 ← PredefSweeps.GetUnitCube[];
rayCastBody: REF ANY ← BlockGetRayCastBody[];
mo ← SVScene.CreateMasterObject[name, blockClass, mainBody, lineBody, shadeBody, rayCastBody];
};
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
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
Box3d: TYPE = SVMasterObjectTypes.Box3d;
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;
};
AverageDepthInCamera:
PROC [poly: Poly3d, localCS, cameraCS: CoordSystem]
RETURNS [avgDepth:
REAL] = {
sum: REAL ← 0;
realLen: REAL ← poly.len;
localPoint: Point3d;
FOR i:
NAT
IN[0..poly.len)
DO
localPoint ← poly[i];
localPoint ← SVGraphics.LocalToCamera[localPoint, localCS, cameraCS];
sum ← sum + localPoint[3];
ENDLOOP;
avgDepth ← sum/realLen;
};
BoxSurface: TYPE = REF BoxSurfaceObj;
BoxSurfaceObj:
TYPE =
RECORD [
poly: Poly3d,
localWorld, localCamera: Matrix4by4
];
GetSurfFromBox:
PROC [box: Box3d, slice: Slice, cameraCS: CoordSystem]
RETURNS [psl: PlanarSurfaceList] = {
BuildSurf:
PROC [slice: Slice, shapeCS: CoordSystem, mo: MasterObject, cameraCS: CoordSystem, normal: Vector3d, box: Box3d, v:
ARRAY[0..3]
OF
NAT]
RETURNS [ps: PlanarSurface] = {
poly: Poly3d ← SVPolygon3d.CreatePoly[4];
avgDepth: REAL;
boxSurface: BoxSurface;
poly ← SVPolygon3d.AddPolyPoint[poly, GetBox3dPoint[box, v[0]]];
poly ← SVPolygon3d.AddPolyPoint[poly, GetBox3dPoint[box, v[1]]];
poly ← SVPolygon3d.AddPolyPoint[poly, GetBox3dPoint[box, v[2]]];
poly ← SVPolygon3d.AddPolyPoint[poly, GetBox3dPoint[box, v[3]]];
avgDepth ← AverageDepthInCamera[poly, shapeCS, cameraCS];
boxSurface ← NEW[BoxSurfaceObj ← [poly, CoordSys.WRTWorld[shapeCS], CoordSys.WRTCamera[shapeCS, cameraCS]]];
ps ←
NEW[PlanarSurfaceObj ← [
whichSurface: boxSurface,
assembly: slice,
normal: normal,
mo: mo,
depth: avgDepth]];
};
verts: ARRAY[0..3] OF NAT;
shape: Shape ← NARROW[slice.shape];
shapeCS: CoordSystem ← shape.coordSys;
mo: MasterObject ← shape.mo;
normal: Vector3d;
psl ← NIL;
FOR i:
NAT
IN [0..5]
DO
verts ← CornersOfFace[i];
normal ← blockNormals[i];
psl ← CONS[BuildSurf[slice, shapeCS, mo, cameraCS, normal, box, verts], psl];
ENDLOOP;
};
GetSurfFromBoxTransform:
PROC [box: Box3d, slice: Slice, cameraCS: CoordSystem, transform: Matrix4by4]
RETURNS [psl: PlanarSurfaceList] = {
BuildSurfTransform:
PROC [slice: Slice, shapeCS: CoordSystem, mo: MasterObject, cameraCS: CoordSystem, normal: Vector3d, box: Box3d, v:
ARRAY[0..3]
OF
NAT, t: Matrix4by4]
RETURNS [ps: PlanarSurface] = {
poly: Poly3d ← SVPolygon3d.CreatePoly[4];
avgDepth: REAL;
boxSurface: BoxSurface;
poly ← SVPolygon3d.AddPolyPoint[poly, Matrix3d.Update[GetBox3dPoint[box, v[0]], t]];
poly ← SVPolygon3d.AddPolyPoint[poly, Matrix3d.Update[GetBox3dPoint[box, v[1]], t]];
poly ← SVPolygon3d.AddPolyPoint[poly, Matrix3d.Update[GetBox3dPoint[box, v[2]], t]];
poly ← SVPolygon3d.AddPolyPoint[poly, Matrix3d.Update[GetBox3dPoint[box, v[3]], t]];
avgDepth ← AverageDepthInCamera[poly, shapeCS, cameraCS];
boxSurface ← NEW[BoxSurfaceObj ← [poly, CoordSys.WRTWorld[shapeCS], CoordSys.WRTCamera[shapeCS, cameraCS]]];
ps ←
NEW[PlanarSurfaceObj ← [
whichSurface: boxSurface,
assembly: slice,
normal: normal,
mo: mo,
depth: avgDepth]];
};
verts: ARRAY[0..3] OF NAT;
shape: Shape ← NARROW[slice.shape];
shapeCS: CoordSystem ← shape.coordSys;
mo: MasterObject ← shape.mo;
normal: Vector3d;
psl ← NIL;
FOR i:
NAT
IN [0..5]
DO
verts ← CornersOfFace[i];
normal ← blockNormals[i];
psl ← CONS[BuildSurfTransform[slice, shapeCS, mo, cameraCS, normal, box, verts, transform], psl];
ENDLOOP;
};
DrawBoxSurf:
PROC [dc: Imager.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera] = {
boxSurface: BoxSurface ← NARROW[ps.whichSurface];
poly: Poly3d ← boxSurface.poly;
SVGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly, ps.assembly.artwork, lightSources, camera, boxSurface.localWorld, boxSurface.localCamera];
};
DrawWireBox3d:
PROC [dc: Imager.Context, box: Box3d, camera: Camera, local
Camera: 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 ← CoordSys.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 ← CoordSys.WRTCamera[shapeCS, camera.coordSys];
localWorld: Matrix4by4 ← CoordSys.WRTWorld[shapeCS];
worldCamera: Matrix4by4 ← CoordSys.FindWorldInTermsOf[camera.coordSys];
mo: MasterObject ← NARROW[slice.shape, Shape].mo;
blockData: BlockData ← NARROW[mo.lineBody];
tempBox: Box3d;
BEGIN
fullTransform: BOOL ← FALSE;
IF IsComplete[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, Matrix3d.Cat[localWorld, transform, worldCamera]];
};
END;
};
TranslationInShape:
PROC [transform, world
Shape: Matrix4by4]
RETURNS [t
Shape: Vector3d] = {
tWorld: Vector3d;
tWorld ← Matrix3d.OriginOfMatrix[transform];
tShape ← Matrix3d.UpdateDisplacement[worldShape, tWorld];
};
TransformedBox:
PROC [sliceD: SliceDescriptor, blockData: BlockData, shape
World: Matrix4by4, transform: Matrix4by4]
RETURNS [tBox: Box3d, fullTransform:
BOOL ←
FALSE] = {
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 ← Matrix3d.Inverse[shapeWorld];
shapeShape: Matrix4by4 ← Matrix3d.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 ← Matrix3d.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 ← Matrix3d.Update[thisP, shapeShape];
opP ← Matrix3d.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 ← SweepGeometry.CountPlanarSurfacesLinearSweep[linMesh];
};
BlockCountVert:
PUBLIC
PROC [masterObject: MasterObject]
RETURNS [len:
NAT] = {
linMesh: LinearMesh ← NARROW[masterObject.shadeBody];
len ← SweepGeometry.CountVerticesLinearSweep[linMesh];
};
BlockGetSurf:
PUBLIC
PROC [slice: Slice, camera: CoordSystem]
RETURNS [psl: PlanarSurfaceList] = {
shape: Shape ← NARROW[slice.shape];
mo: MasterObject ← shape.mo;
blockData: BlockData ← NARROW[mo.mainBody];
psl ← GetSurfFromBox[blockData.box, slice, camera];
};
BlockGetSurfTransform:
PUBLIC
PROC [sliceD: SliceDescriptor, camera: CoordSystem, transform: Matrix4by4]
RETURNS [psl: PlanarSurfaceList] = {
shape: Shape ← NARROW[sliceD.slice.shape];
mo: MasterObject ← shape.mo;
blockData: BlockData ← NARROW[mo.mainBody];
shapeWorld: Matrix4by4 ← CoordSys.WRTWorld[shape.coordSys];
tBox: Box3d;
fullTransform: BOOL ← TRUE;
[tBox, fullTransform] ← TransformedBox[sliceD, blockData, shapeWorld, transform];
IF fullTransform
THEN {
worldShape: Matrix4by4 ← Matrix3d.Inverse[shapeWorld];
shapeShape: Matrix4by4 ← Matrix3d.Cat[shapeWorld, transform, worldShape];
psl ← GetSurfFromBoxTransform[blockData.box, sliceD.slice, camera, shapeShape];
}
ELSE psl ← GetSurfFromBox[tBox, sliceD.slice, camera];
};
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;
SweepGeometry.DrawNormalsLinearSweep[dc, linMesh, camera, localCS];
};
BlockRayCast:
PUBLIC
PROC [cameraPoint: Point2d, localRay: Ray, masterObject:
REF
ANY, prim: Primitive]
RETURNS [class: Classification] = {
mo: MasterObject ← NARROW[masterObject];
blockData: BlockData ← NARROW[mo.mainBody];
RETURN[SVObjectCast.BlockCast[localRay, prim, blockData, NARROW[mo.rayCastBody]]];
};
BlockRayCastNoBBoxes:
PUBLIC
PROC [localRay: Ray, masterObject:
REF
ANY, prim: Primitive]
RETURNS [class: Classification] = {
mo: MasterObject ← NARROW[masterObject];
blockData: BlockData ← NARROW[mo.mainBody];
RETURN[SVObjectCast.BlockCast[localRay, prim, blockData, NARROW[mo.rayCastBody]]];
};
BlockRayCastBoundingSpheres:
PUBLIC
PROC [localRay: Ray, masterObject:
REF
ANY, prim: Primitive]
RETURNS [class: Classification] = {
mo: MasterObject ← NARROW[masterObject];
blockData: BlockData ← NARROW[mo.mainBody];
RETURN[SVObjectCast.BlockCast[localRay, prim, blockData, NARROW[mo.rayCastBody]]];
};
BlockDrawSurf:
PUBLIC
PROC [dc: Imager.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera] = {
DrawBoxSurf[dc, ps, lightSources, camera];
};
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 ← CoordSys.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 IsComplete[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 ← Matrix3d.Inverse[shapeWorld];
shapeShape: Matrix4by4 ← Matrix3d.Cat[shapeWorld, transform, worldShape];
thisP ← GetBox3dPoint[blockData.box, cornerNum];
opP ← GetBox3dPoint[blockData.box, OppositeCorner[cornerNum]];
thisP ← Matrix3d.Update[thisP, shapeShape];
}
ELSE
IF faceCount = 0
AND edgeCount = 1
AND cornerCount = 2
THEN {
worldShape: Matrix4by4 ← Matrix3d.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 ← Matrix3d.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 ← CoordSys.WRTWorld[slice.coordSys];
newAssemblyWorld: Matrix4by4 ← Matrix3d.Mult[assemblyWorld, transform];
SVAssembly.AbsTransf[slice, scene, newAssemblyWorld];
};
END;
};
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"
];
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: 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, version:
REAL]
RETURNS [mo: MasterObject] = {
IF version >= 7.02
THEN {
loX, loY, loZ, hiX, hiY, hiZ: REAL;
TFI3d.ReadRope[f, "["];
loX ← TFI3d.ReadBlankAndReal[f];
loY ← TFI3d.ReadBlankAndReal[f];
loZ ← TFI3d.ReadBlankAndReal[f];
hiX ← TFI3d.ReadBlankAndReal[f];
hiY ← TFI3d.ReadBlankAndReal[f];
hiZ ← TFI3d.ReadBlankAndReal[f];
TFI3d.ReadBlankAndRope[f, "]"];
TFI3d.ReadBlank[f];
mo ← BlockMakeMasterObject[name, loX, loY, loZ, hiX, hiY, hiZ];
}
ELSE {
TFI3d.ReadRope[f, "data: procedural"];
TFI3d.ReadBlank[f];
mo ← BlockMakeMasterObject[name];
};
};
BlockFileout:
PUBLIC
PROC [f:
IO.
STREAM, mo: MasterObject] = {
blockData: BlockData ← NARROW[mo.mainBody];
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]]];
};
BlockFileoutPoly:
PUBLIC
PROC [f:
IO.
STREAM, mo: MasterObject] = {
linMesh: LinearMesh ← NARROW[mo.shadeBody];
SweepGeometry.PolyListLinear[f, linMesh];
};
Parts
BlockHitData: TYPE = REF BlockHitDataObj;
BlockHitDataObj:
TYPE =
RECORD [
corner: [-1..7] ← -1,
center: [-1..5] ← -1,
centroid: [-1..0] ← -1,
edge: [-1..11] ← -1,
face: [-1..5] ← -1,
hitPoint: Point3d ← [0,0,0]
];
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;
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;
};
IsComplete:
PROC [sliceD: SliceDescriptor]
RETURNS [
BOOL] = {
blockParts: BlockParts ← NARROW[sliceD.parts];
RETURN[blockParts.corners=ALL[TRUE] AND blockParts.centers=ALL[TRUE] AND blockParts.centroid = TRUE AND blockParts.edges=ALL[TRUE] AND blockParts.faces = ALL[TRUE]];
};
BlockEmptyParts:
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]
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]
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;
};
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;
};
BlockMovingParts:
PROC [slice: Slice, selectedParts: SliceParts]
RETURNS [background, overlay, rubber, drag: SliceDescriptor] = {
GGModelTypes.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: 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 (cornerCount+centerCount+edgeCount+faceCount)>1
OR centroidCount > 0
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];
};
blockCorner: ARRAY [0..8) OF Point3d = [
[-1, 1, 1],
[-1, 1, -1],
[1, 1, -1],
[1, 1, 1],
[-1, -1, 1],
[-1, -1, -1],
[1, -1, -1],
[1, -1, 1]];
Part Generators
BlockGenData: TYPE = REF BlockGenDataObj;
BlockGenDataObj:
TYPE =
RECORD [
blockParts: BlockParts,
blockData: BlockData
];
BlockPointsInDescriptor:
PROC [sliceD: SliceDescriptor]
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 THEN 1 ELSE 0;
blockGenData ← NEW[BlockGenDataObj ← [blockParts, blockData]];
pointGen ←
NEW[PointGeneratorObj ← [
sliceD: sliceD,
toGo: MAX[0, cornerCount] + MAX[0, centroidCount],
index: 0,
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]
IF index IN [0..7] THEN pointAndDone.point ← blockCorner[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
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
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]
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];
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, criticalR:
REAL]
RETURNS [bestDist:
REAL, point
WORLD: Point3d, hitData:
REF
ANY, success:
BOOL ← TRUE] = {
cornerIndex, centerIndex, centroid: INTEGER;
[cornerIndex, centerIndex, centroid, pointWORLD, bestDist] ← NearestBlockPoint[sliceD.slice, testPoint];
hitData ← NEW[BlockHitDataObj ← [corner: cornerIndex, center: centerIndex, centroid: centroid, hitPoint: pointWORLD]];
};
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: BOOL ← TRUE;
BlockClosestPointToLine:
PROC [sliceD: SliceDescriptor, cameraPoint: Point2d, line3d: Line3d, criticalR:
REAL, camera: Camera]
RETURNS [bestDist:
REAL, point
WORLD: Point3d, hitData:
REF
ANY, success:
BOOL ←
FALSE] = {
moWORLD: 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-criticalR, sliceBBox.loY-criticalR, sliceBBox.hiX+criticalR, sliceBBox.hiY+criticalR, FALSE, FALSE];
IF
NOT useBBoxes
OR PointIsInBox[cameraPoint, toleranceBox]
THEN {
blockHitData: BlockHitData;
shape ← NARROW[sliceD.slice.shape];
moWORLD ← CoordSys.WRTWorld[shape.coordSys];
bestDist ← Real.LargestNumber;
pointGen ← BlockPointsInDescriptor[sliceD];
FOR pointAndDone: PointAndDone ← BlockNextPoint[pointGen], BlockNextPoint[pointGen]
UNTIL pointAndDone.done =
TRUE
DO
thisPoint ← Matrix3d.Update[pointAndDone.point, moWORLD];
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]];
}
};
SegmentGenerator: TYPE = REF SegmentGeneratorObj;
SegmentGeneratorObj:
TYPE =
RECORD [
data: REF ANY,
index: NAT,
toGo: NAT
];
Segment: TYPE = REF SegmentObj;
SegmentObj: TYPE = SVMasterObjectTypes.SegmentObj;
SegmentAndDone:
TYPE =
RECORD[
seg: Segment,
done: BOOL
];
BlockClosestSegmentToLine:
PROC [sliceD: SliceDescriptor, cameraPoint: Point2d, line3d: Line3d, criticalR:
REAL, camera: Camera]
RETURNS [bestDist:
REAL, point
WORLD: Point3d, 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-criticalR, sliceBBox.loY-criticalR, sliceBBox.hiX+criticalR, sliceBBox.hiY+criticalR, FALSE, FALSE];
IF
NOT useBBoxes
OR PointIsInBox[cameraPoint, toleranceBox]
THEN {
shape: Shape ← NARROW[sliceD.slice.shape];
blockData: BlockData ← NARROW[shape.mo.mainBody];
moWORLD ← CoordSys.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]];
};
};
Miscellaneous
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,
emptyParts: NoOpEmptyParts,
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 ← SVBoundBox.BoundBoxFromValues[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 ← CoordSys.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];
};
END.