<> <> 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 ]; ShapeTwiddleImpl: CEDAR PROGRAM IMPORTS Real, Vector3d, ThreeDScenes, CedarProcess, Matrix3d EXPORTS ShapeTwiddle ~ BEGIN <> Context: TYPE ~ ThreeDBasics.Context; ShapeTwiddleError: PUBLIC SIGNAL [reason: ATOM] = CODE; NatPair: TYPE ~ ShapeTwiddle.NatPair; -- RECORD [x, y: NAT]; PairSequence: TYPE ~ ThreeDBasics.PairSequence; Triple: TYPE ~ ThreeDBasics.Triple; TripleSequence: TYPE ~ ThreeDBasics.TripleSequence; RGB: TYPE ~ ImagerColor.RGB; NatSequence: TYPE ~ RECORD [SEQUENCE length: NAT OF NAT]; <> <> <> <> <> <> <> <> <<];>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<];>> <> <> <> <> <<];>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<[ [tx, ty, tz] ] _ Matrix3d.Transform[ [x, y, z], shape.position];>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<}; >> 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] ~ { <> 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; }; END.