ShapeTwiddleImpl.mesa
Last Edited by: Crow, March 28, 1986 4:20:57 pm PST
DIRECTORY
Atom USING [ GetPropFromList ],
Real USING [ FixC ],
UserCredentials USING [ Get ],
IO USING [ STREAM, PutF, rope, time, int , real, Close ],
FS USING [ StreamOpen ],
Rope USING [ Cat, ROPE ],
CedarProcess USING [ DoWithPriority ],
ImagerColor USING [ RGB ],
Vector3d USING [ Sub, Mag, Triple ],
Matrix3d USING [ Transform ],
ThreeDScenes USING [ Context, FindShape, NewShape, ShadingSequence, SetPosition,
ShapeInstance, ShapeSequence, Vertex, VertexSequence ],
ThreeDSurfaces USING [ PtrPatchSequence ],
ShapeTwiddle USING [ NatPair ];
Types
Context: TYPE ~ ThreeDScenes.Context;
ShapeTwiddleError: PUBLIC SIGNAL [reason: ATOM] = CODE;
NatPair: TYPE ~ ShapeTwiddle.NatPair; -- RECORD [x, y: NAT];
PairSequence: TYPE ~ RECORD [SEQUENCE length: NAT OF NatPair];
Triple: TYPE ~ Vector3d.Triple;
RGB: TYPE ~ ImagerColor.RGB;
NatSequence: TYPE ~ RECORD [SEQUENCE length: NAT OF NAT];
Shape Manipulations
WriteOutShape:
PUBLIC PROC [context:
REF Context, name, ext: Rope.
ROPE ←
NIL] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
surface: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[shape.surface];
numbered: BOOLEAN ← FALSE;
out: IO.STREAM;
IF name = NIL THEN SIGNAL ShapeTwiddleError[$UnNamedObject];
IF ext = NIL THEN ext ← "shape";
out ← FS.StreamOpen[
fileName: Rope.Cat[name, ".", ext],
accessOptions: create,
wDir: NARROW[ Atom.GetPropFromList[context.props, $WDir] ]
];
out.PutF[" %g - Created by: %g at %g\n",
IO.rope[name],
IO.rope[UserCredentials.Get[].name],
IO.time[]
];
out.PutF[" %g %g \n", IO.int[shape.vertex.length], IO.int[shape.numSurfaces] ];
IF shape.positionInValid THEN ThreeDScenes.SetPosition[shape]; -- set position matrix
FOR i:
NAT
IN [0..shape.vertex.length)
DO
OPEN shape.vertex[i];
tx, ty, tz: REAL;
[ [tx, ty, tz] ] ← Matrix3d.Transform[ [x, y, z], shape.position];
IF numbered
THEN out.PutF[" %g %g %g %g\n", IO.int[i], IO.real[tx], IO.real[ty], IO.real[tz] ]
ELSE out.PutF[" %g %g %g\n", IO.real[tx], IO.real[ty], IO.real[tz] ];
ENDLOOP;
FOR i:
NAT
IN [0..shape.numSurfaces)
DO
out.PutF["%g ", IO.int[surface[i].nVtces] ];
FOR j:
NAT
IN [0..surface[i].nVtces)
DO
out.PutF[" %g", IO.int[ surface[i][j] + 1 ] ]; -- start count from one to avoid zeroes
ENDLOOP;
out.PutF["\n"];
ENDLOOP;
IO.Close[out];
};
ScaleShape:
PUBLIC PROC [context:
REF Context, name: Rope.
ROPE, scale:
REAL,
xRatio, yRatio, zRatio:
REAL ← 1.0] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
IF shape.positionInValid THEN ThreeDScenes.SetPosition[shape]; -- set position matrix
FOR i:
NAT
IN [0..shape.vertex.length)
DO
OPEN shape.vertex[i];
tx, ty, tz: REAL;
[ [tx, ty, tz] ] ← Matrix3d.Transform[ [x, y, z], shape.position];
x ← tx * scale * xRatio;
y ← ty * scale * yRatio;
z ← tz * scale * zRatio;
ENDLOOP;
};
SortPair: TYPE ~ RECORD [vtx, next: NAT];
SortSequence: TYPE ~ RECORD[SEQUENCE length: NAT OF REF SortPair];
CleanUp:
PUBLIC PROC [context:
REF Context, name: Rope.
ROPE, deSeam:
BOOLEAN ←
FALSE,
tolerance:
REAL ← 0.0] ~ {
delete excess vertices, join identical ones, etc.
currPos: NAT ← 0;
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
surface: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[shape.surface];
table: REF NatSequence ← NEW[ NatSequence[shape.vertex.length] ];
buckets: REF SortSequence ← NEW[SortSequence[5 * shape.vertex.length / 4]];
nextBucket: NAT ← (shape.vertex.length / 4) + 1;
newVtx: REF ThreeDScenes.VertexSequence;
newShade: REF ThreeDScenes.ShadingSequence;
Action:
PROC ~ {
IF deSeam
THEN {
minX, maxX, scale: REAL;
[minX, maxX] ← Bounds[context, name];
scale ← (shape.vertex.length / 4) / (maxX - minX);
IF 1.0 / scale < tolerance THEN SIGNAL ShapeTwiddleError[$ToleranceTooLarge];
FOR i:
NAT
IN [0..shape.vertex.length)
DO
-- bucket sort vertices
index: NAT ← Real.FixC[ (shape.vertex[i].x - minX) * scale ];
IF buckets[index] #
NIL
THEN {
buckets[nextBucket] ← NEW[SortPair];
buckets[nextBucket].next ← buckets[index].next; -- put on next-to-head of list
buckets[index].next ← nextBucket; -- reset first pointer
index ← nextBucket; nextBucket ← nextBucket + 1;
}
ELSE {
buckets[index] ← NEW[SortPair]; -- bucket hit for first time
buckets[index].next ← 0;
};
buckets[index].vtx ← i; -- store vertex pointer
ENDLOOP;
FOR i:
NAT
IN [1..table.length)
DO
-- Coalesce nearby vertices
vtx: REF ThreeDScenes.Vertex ← shape.vertex[i];
xBucket: NAT ← MAX[1, MIN[shape.vertex.length-1, Real.FixC[ (vtx.x - minX)*scale]]];
table[i] ← i; -- set to identity value
FOR j:
NAT
IN [xBucket-1 .. xBucket+1]
DO
-- search this and last and next buckets
index: NAT ← j;
WHILE buckets[index] #
NIL DO
k: NAT ← buckets[index].vtx;
diff:
REAL ← Vector3d.Mag[ Vector3d.Sub[
-- check distance
[vtx.x, vtx.y, vtx.z],
[shape.vertex[k].x, shape.vertex[k].y, shape.vertex[k].z]
] ];
IF diff <= tolerance THEN IF k < table[i] THEN table[i] ← k; -- reset if smaller
index ← buckets[index].next; IF index = 0 THEN EXIT;
ENDLOOP;
ENDLOOP;
ENDLOOP;
FOR i:
NAT
IN [0..shape.numSurfaces)
DO
-- retarget vertex pointers
IF surface[i] #
NIL
THEN
FOR j:
NAT
IN [0..surface[i].nVtces)
DO
surface[i][j] ← table[surface[i][j]];
ENDLOOP;
ENDLOOP;
IF shape.type = $ConvexPolygon
THEN {
FOR i:
NAT
IN [0..shape.numSurfaces)
DO
-- eliminate duplicated polygon vertices
vtx: NAT ← 0;
IF surface[i] #
NIL
THEN
FOR j:
NAT
IN [0..surface[i].nVtces)
DO
k: NAT ← (j-1 + surface[i].nVtces) MOD surface[i].nVtces;
IF surface[i][j] # surface[i][k]
THEN {
surface[i][vtx] ← surface[i][j];
vtx ← vtx + 1;
};
ENDLOOP;
surface[i].nVtces ← vtx;
ENDLOOP;
};
}; -- end deSeaming block
FOR i: NAT IN [0..table.length) DO table[i] ← 0; ENDLOOP;
FOR i:
NAT
IN [0..shape.numSurfaces)
DO
-- build vertex reference count
IF surface[i] #
NIL
THEN
FOR j:
NAT
IN [0..surface[i].nVtces)
DO
table[surface[i][j]] ← table[surface[i][j]] + 1;
ENDLOOP;
ENDLOOP;
currPos ← 0;
FOR i:
NAT
IN [0..table.length)
DO
-- collapse vertex array and keep audit trail
IF table[i] # 0
THEN {
shape.vertex[currPos] ← shape.vertex[i];
shape.shade[currPos] ← shape.shade[i];
table[i] ← currPos;
currPos ← currPos + 1;
};
ENDLOOP;
newVtx ← NEW[ ThreeDScenes.VertexSequence[currPos] ];
newShade ← NEW[ ThreeDScenes.ShadingSequence[currPos] ];
FOR i:
NAT
IN [0..currPos)
DO
-- copy into new sequences of proper length
newVtx[i] ← shape.vertex[i];
newShade[i] ← shape.shade[i];
ENDLOOP;
shape.vertex ← newVtx;
shape.shade ← newShade;
FOR i:
NAT
IN [0..shape.numSurfaces)
DO
-- retarget vertex pointers
FOR j:
NAT
IN [0..surface[i].nVtces)
DO
surface[i][j] ← table[surface[i][j]];
ENDLOOP;
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];
};
CopyShape:
PUBLIC PROC [context:
REF Context, dstName, srcName: Rope.
ROPE]
RETURNS[
REF ThreeDScenes.ShapeInstance] ~ {
dstShape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.NewShape[dstName];
srcShape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[
context.shapes, srcName ];
srcSurface: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[srcShape.surface];
dstSurface: REF ThreeDSurfaces.PtrPatchSequence;
currPos: NAT ← 0;
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.NewShape[dstName];
dstShape.orientation ← srcShape.orientation;
dstShape.location ← srcShape.location;
dstShape.rotation ← srcShape.rotation;
dstShape.axisBase ← srcShape.axisBase;
dstShape.axisEnd ← srcShape.axisEnd;
dstShape.centroid ← srcShape.centroid;
dstShape.boundingRadius ← srcShape.boundingRadius;
dstShape.vertex ← NEW[ThreeDScenes.VertexSequence[srcShape.vertex.length] ];
FOR i:
NAT
IN [0..srcShape.vertex.length)
DO
dstShape.vertex[i] ← srcShape.vertex[i];
ENDLOOP;
dstShape.shade ← NEW[ ThreeDScenes.ShadingSequence[srcShape.shade.length] ];
FOR i:
NAT
IN [0..srcShape.shade.length)
DO
dstShape.shade[i] ← srcShape.shade[i];
ENDLOOP;
dstShape.surface ← NEW[ ThreeDSurfaces.PtrPatchSequence[srcShape.numSurfaces] ];
dstSurface ← NARROW[dstShape.surface, REF ThreeDSurfaces.PtrPatchSequence];
FOR i:
NAT
IN [0..srcShape.numSurfaces)
DO
dstSurface[i] ← srcSurface[i];
ENDLOOP;
dstShape.numSurfaces ← srcShape.numSurfaces;
dstShape.shadingProps ← srcShape.shadingProps;
dstShape.props ← srcShape.props;
RETURN[dstShape];
};
Combine:
PUBLIC PROC [context:
REF Context, dstName, src1, src2: Rope.
ROPE]
RETURNS[
REF ThreeDScenes.ShapeInstance] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.NewShape[dstName];
shape1: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, src1 ];
shape2: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, src2 ];
surface1: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[shape1.surface];
surface2: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[shape2.surface];
surface: REF ThreeDSurfaces.PtrPatchSequence;
shape.vertex ← NEW[ -- copy vertices
ThreeDScenes.VertexSequence[shape1.vertex.length + shape2.vertex.length] ];
FOR i:
NAT
IN [0..shape1.vertex.length)
DO
shape.vertex[i] ← shape1.vertex[i];
[[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z]] ← Matrix3d.Transform[
[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z],
shape1.position
];
ENDLOOP;
FOR i:
NAT
IN [0..shape2.vertex.length)
DO
shape.vertex[i+shape1.vertex.length] ← shape2.vertex[i];
[[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z]] ← Matrix3d.Transform[
[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z],
shape2.position
];
ENDLOOP;
shape.shade ← NEW[ -- copy shading values
ThreeDScenes.ShadingSequence[shape1.shade.length + shape2.shade.length] ];
FOR i:
NAT
IN [0..shape1.shade.length)
DO
shape.shade[i] ← shape1.shade[i];
ENDLOOP;
FOR i:
NAT
IN [0..shape2.shade.length)
DO
shape.shade[i+shape1.shade.length] ← shape2.shade[i];
ENDLOOP;
shape.numSurfaces ← shape1.numSurfaces + shape2.numSurfaces;
shape.surface ← NEW[ -- copy surfaces
ThreeDSurfaces.PtrPatchSequence[shape1.numSurfaces + shape2.numSurfaces] ];
surface ← NARROW[shape.surface, REF ThreeDSurfaces.PtrPatchSequence];
FOR i:
NAT
IN [0..shape1.numSurfaces)
DO
surface[i] ← surface1[i];
ENDLOOP;
FOR i:
NAT
IN [0..shape2.numSurfaces)
DO
base: NAT ← shape1.numSurfaces;
surface[i+base] ← surface2[i];
FOR j:
NAT
IN [0..surface[i+base].nVtces)
DO
-- redirect vertex pointers
surface[i+base][j] ← surface[i+base][j] + shape1.vertex.length
ENDLOOP;
ENDLOOP;
shape.shadingProps ← shape1.shadingProps;
shape.props ← shape1.props;
RETURN[shape];
};
DeletePatches:
PUBLIC PROC [context:
REF Context, dstName, srcName: Rope.
ROPE,
patchList:
LIST
OF NatPair]
RETURNS[
REF ThreeDScenes.ShapeInstance] ~ {
currPos: NAT ← 0;
oldShape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[
context.shapes, srcName ];
shape: REF ThreeDScenes.ShapeInstance ← CopyShape[context, dstName, srcName];
surface: REF ThreeDSurfaces.PtrPatchSequence;
surface ← NARROW[shape.surface, REF ThreeDSurfaces.PtrPatchSequence];
FOR list:
LIST
OF NatPair ← patchList, list.rest
UNTIL list =
NIL
DO
FOR i:
NAT
IN [list.first.x..list.first.y]
DO
surface[i] ← NIL;
ENDLOOP;
ENDLOOP;
FOR i:
NAT
IN [0..shape.numSurfaces)
DO
-- collapse array
IF surface[i] #
NIL
THEN {
surface[currPos] ← surface[i];
currPos ← currPos + 1;
};
ENDLOOP;
shape.numSurfaces ← currPos;
RETURN[ shape ];
};
Bounds:
PUBLIC PROC [context:
REF Context, name: Rope.
ROPE]
RETURNS[xMin, xMax, yMin, yMax, zMin, zMax:
REAL] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
xMin ← xMax ← shape.vertex[0].x;
yMin ← yMax ← shape.vertex[0].y;
zMin ← zMax ← shape.vertex[0].z;
FOR i:
NAT
IN (0..shape.vertex.length)
DO
-- get min and max in x, y, and z
IF shape.vertex[i].x < xMin
THEN xMin ← shape.vertex[i].x
ELSE IF shape.vertex[i].x > xMax THEN xMax ← shape.vertex[i].x;
IF shape.vertex[i].y < yMin
THEN yMin ← shape.vertex[i].y
ELSE IF shape.vertex[i].y > yMax THEN yMax ← shape.vertex[i].y;
IF shape.vertex[i].z < zMin
THEN zMin ← shape.vertex[i].z
ELSE IF shape.vertex[i].z > zMax THEN zMax ← shape.vertex[i].z;
ENDLOOP;
};