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 ];
ShapeTwiddleImpl: CEDAR PROGRAM
IMPORTS Atom, Real, FS, IO, Vector3d, Rope, ThreeDScenes, UserCredentials, CedarProcess, Matrix3d
EXPORTS ShapeTwiddle
~ BEGIN
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];
Global Variables
Shape Manipulations
WriteOutShape: PUBLIC PROC [context: REF Context, name, ext: Rope.ROPENIL] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
surface: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[shape.surface];
numbered: BOOLEANFALSE;
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: BOOLEANFALSE,
  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: NATMAX[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.vertexnewVtx;
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;
};
END.