ShapeTwiddleImpl.mesa
Last Edited by: Crow, October 2, 1986 5:24:58 pm PDT
DIRECTORY
Real USING [ FixC ],
Rope USING [ ROPE ],
CedarProcess USING [ DoWithPriority ],
ImagerColor USING [ RGB ],
Vector3d USING [ Sub, Length ],
Matrix3d USING [ Transform ],
ThreeDBasics USING [ Context, PairSequence, Quad, ShadingSequence, ShapeInstance,
ShapeSequence, ShadingValue, Triple, TripleSequence, Vertex,
VertexSequence ],
ThreeDScenes USING [ Error, FindShape, NewShape, PutShading, SetPosition ],
ThreeDSurfaces USING [ PtrPatchSequence ],
ShapeTwiddle USING [ NatPair ];
Shape Manipulations
WriteOutShape: PUBLIC PROC [context: REF Context, name, ext: Rope.ROPE ← NIL,
numbered: BOOLEAN ← FALSE] ~ {
Caution!!! Writes old format!!!
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
surface: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[shape.surface];
pInfo: REF ThreeDBasics.ShadingSequence ← NARROW[
ThreeDScenes.GetShading[ shape, $PatchColors ]
];
vtxNormals: BOOLEAN ← ThreeDScenes.GetShading[ shape, $VertexNormalsInFile ] # NIL;
vtxColors: BOOLEAN ← ThreeDScenes.GetShading[ shape, $VertexColorsInFile ] # NIL;
vtxTexture: BOOLEAN ← ThreeDScenes.GetShading[ shape, $VertexTextureInFile ] # NIL;
vtxTrans: BOOLEAN ← ThreeDScenes.GetShading[ shape, $VertexTransInFile ] # NIL;
ptchNormals: BOOLEAN ← ThreeDScenes.GetShading[ shape, $PatchNormalsInFile ] # NIL;
ptchColors: BOOLEAN ← ThreeDScenes.GetShading[ shape, $PatchColorsInFile ] # NIL;
ptchTrans: BOOLEAN ← ThreeDScenes.GetShading[ shape, $PatchTransInFile ] # NIL;
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\n",
IO.rope[name],
IO.rope[UserCredentials.Get[].name],
IO.time[]
];
out.PutRope["SurfaceType ~ "];
out.PutRope[Atom.GetPName[shape.type]];
IF shape.insideVisible THEN out.PutRope[", InsideVisible"];
out.PutRope["\n\n"];
out.PutRope["Vertices ~ "];
IF numbered THEN out.PutRope["index: integer, "];
out.PutRope["xyz: triple"];
IF vtxNormals THEN out.PutRope[", normal: triple"];
IF vtxColors THEN out.PutRope[", color: triple"];
IF vtxTrans THEN out.PutRope[", trans: real"];
IF vtxTexture THEN out.PutRope[", texture: triple"];
out.PutF[" -- %g Vertices %g Patches\n\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];
s: REF ThreeDBasics.ShadingValue ← shape.shade[i];
tx, ty, tz: REAL;
[ [tx, ty, tz] ] ← Matrix3d.Transform[ [x, y, z], shape.position];
IF numbered THEN out.PutF["%g ", IO.int[i] ];
out.PutF["\t%g %g %g", IO.real[tx], IO.real[ty], IO.real[tz] ];
IF vtxNormals THEN out.PutF["\t%g %g %g", IO.real[s.xn], IO.real[s.yn], IO.real[s.zn] ];
IF vtxColors THEN out.PutF["\t%g %g %g", IO.real[s.r], IO.real[s.g], IO.real[s.b] ];
IF vtxTrans THEN out.PutF["\t%g", IO.real[s.t] ];
IF vtxTexture
THEN out.PutF["\t%g %g %g", IO.real[s.txtrX], IO.real[s.txtrY], IO.real[s.txtrZ] ];
out.PutRope["\n"];
ENDLOOP;
out.PutRope["\n"];
IF shape.type = $ConvexPolygon THEN out.PutRope["Polygons ~ "]
ELSE out.PutRope["Patches ~ "];
IF numbered THEN out.PutRope["index: integer, "];
IF ptchNormals THEN out.PutRope["normal: triple, "];
IF ptchColors THEN out.PutRope["color: triple, "];
IF ptchTrans THEN out.PutRope["trans: real, "];
out.PutRope["vertices: nats\n\n"];
FOR i: NAT IN [0..shape.numSurfaces) DO
IF numbered THEN out.PutF["%g ", IO.int[i] ];
IF ptchNormals THEN
out.PutF["\t%g %g %g", IO.real[pInfo[i].xn], IO.real[pInfo[i].yn], IO.real[pInfo[i].zn] ];
IF ptchColors THEN
out.PutF["\t%g %g %g", IO.real[pInfo[i].r], IO.real[pInfo[i].g], IO.real[pInfo[i].b] ];
IF ptchTrans THEN out.PutF["\t%g", IO.real[pInfo[i].t] ];
out.PutRope["\t"];
FOR j: NAT IN [0..surface[i].nVtces) DO
out.PutF[" %g", IO.int[ surface[i][j] ] ];
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 ThreeDBasics.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;
shape.boundingRadius ← shape.boundingRadius * scale * MAX[xRatio, yRatio, zRatio];
ThreeDScenes.PutShading[
shape,
$Scale,
NEW[ ThreeDBasics.Quad ← [scale, xRatio, yRatio, zRatio] ]
];
};
ScaleTexture:
PUBLIC PROC [context:
REF Context, name: Rope.
ROPE, scale:
REAL,
xRatio, yRatio, zRatio:
REAL ← 1.0] ~ {
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
IF shape.positionInValid THEN ThreeDScenes.SetPosition[shape]; -- set position matrix
WITH shape.auxInfo
SELECT
FROM
pair:
REF PairSequence =>
FOR i:
NAT
IN [0..pair.length)
DO
pair[i].x ← pair[i].x * scale * xRatio;
pair[i].y ← pair[i].y * scale * yRatio;
ENDLOOP;
triple:
REF TripleSequence =>
FOR i:
NAT
IN [0..triple.length)
DO
triple[i].x ← triple[i].x * scale * xRatio;
triple[i].y ← triple[i].y * scale * yRatio;
triple[i].z ← triple[i].z * scale * zRatio;
ENDLOOP;
ENDCASE => SIGNAL ThreeDScenes.Error[[$Unimplemented, "Unknown texture type"]];
ThreeDScenes.PutShading[
shape,
$TextureScale,
NEW[ ThreeDBasics.Quad ← [scale, xRatio, yRatio, zRatio] ]
];
};
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 ThreeDBasics.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 ThreeDBasics.VertexSequence;
newShade: REF ThreeDBasics.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 ThreeDBasics.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.Length[ 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].vtxPtr[j] ← table[surface[i].vtxPtr[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].vtxPtr[j] # surface[i].vtxPtr[k]
THEN {
surface[i].vtxPtr[vtx] ← surface[i].vtxPtr[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].vtxPtr[j]] ← table[surface[i].vtxPtr[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[ ThreeDBasics.VertexSequence[currPos] ];
newShade ← NEW[ ThreeDBasics.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].vtxPtr[j] ← table[surface[i].vtxPtr[j]];
ENDLOOP;
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];
};
CopyShape:
PUBLIC PROC [context:
REF Context, dstName, srcName: Rope.
ROPE]
RETURNS[
REF ThreeDBasics.ShapeInstance] ~ {
dstShape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.NewShape[dstName];
srcShape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[
context.shapes, srcName ];
srcSurface: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[srcShape.surface];
dstSurface: REF ThreeDSurfaces.PtrPatchSequence;
currPos: NAT ← 0;
shape: REF ThreeDBasics.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[ThreeDBasics.VertexSequence[srcShape.vertex.length] ];
FOR i:
NAT
IN [0..srcShape.vertex.length)
DO
dstShape.vertex[i] ← srcShape.vertex[i];
ENDLOOP;
dstShape.shade ← NEW[ ThreeDBasics.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 ThreeDBasics.ShapeInstance] ~ {
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.NewShape[dstName];
shape1: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, src1 ];
shape2: REF ThreeDBasics.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
ThreeDBasics.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
ThreeDBasics.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].vtxPtr[j] ← surface[i+base].vtxPtr[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 ThreeDBasics.ShapeInstance] ~ {
currPos: NAT ← 0;
oldShape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[
context.shapes, srcName ];
shape: REF ThreeDBasics.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 ThreeDBasics.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;
};