Command: Commander.CommandProc ~ {
ENABLE {
IsItError => {msg ¬ usage; GOTO Failure};
PFS.Error => {msg ¬ error.explanation; GOTO Failure};
Convert.Error => {msg ¬ "Conversion error"; GOTO Failure};
};
IsIt:
PROC [rope:
ROPE, argsNeeded:
INT]
RETURNS [b:
BOOL] ~ {
IF argIndex >= args.argc THEN RETURN[FALSE];
b ¬ Eq[rope, args[argIndex]];
IF b AND args.argc <= argIndex+argsNeeded THEN ERROR IsItError;
};
Eq: PROC [r1, r2: ROPE] RETURNS [BOOL] ~ {RETURN[Rope.Equal[r1, r2, FALSE]]};
SkipArgs: PROC [nSkip: INT] ~ {argIndex ¬ argIndex+nSkip};
GetReal: PROC [index: INT] RETURNS [r: REAL] ~ {r ¬ Convert.RealFromRope[args[index]]};
GetTriple:
PROC [index:
INT]
RETURNS [t: Triple] ~ {
t.x ¬ GetReal[index+1];
t.y ¬ GetReal[index+2];
t.z ¬ GetReal[index+3];
};
s: Shape;
argIndex: INT ¬ 2;
format: ATOM ¬ NIL;
in, out, convert: ROPE ¬ NIL;
args: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd];
rewrite, info, index, center, unitize, triangulate: BOOL ¬ FALSE;
reversePolys, negateNormals, exchangeYZ, addNormals, addTexture: BOOL ¬ FALSE;
scale: REAL ¬ 1.0;
translate: Triple ¬ [0.0, 0.0, 0.0];
IF args.argc < 3 THEN RETURN[$Failure, usage];
IF args.argc > 3
AND Eq[args[2], "¬"]
THEN {out ¬ args[1]; in ¬ args[3]; argIndex ¬ 4}
ELSE in ¬ args[1];
WHILE argIndex < args.argc
DO
SELECT
TRUE
FROM
IsIt["-translate", 3] => {translate ¬ GetTriple[argIndex+1]; SkipArgs[4]};
IsIt["-scale", 1] => {scale ¬ GetReal[argIndex+1]; SkipArgs[2]};
IsIt["-index", 0] => {index ¬ TRUE; SkipArgs[1]};
IsIt["-noIndex", 0] => {index ¬ FALSE; SkipArgs[1]};
IsIt["-reversePolys", 0] => {reversePolys ¬ TRUE; SkipArgs[1]};
IsIt["-negateNormals", 0] => {negateNormals ¬ TRUE; SkipArgs[1]};
IsIt["-triangulate", 0] => {triangulate ¬ TRUE; SkipArgs[1]};
IsIt["-addNormals", 0] => {addNormals ¬ TRUE; SkipArgs[1]};
IsIt["-addTexture", 0] => {addTexture ¬ TRUE; SkipArgs[1]};
IsIt["-center", 0] => {center ¬ TRUE; SkipArgs[1]};
IsIt["-unitize", 0] => {unitize ¬ TRUE; SkipArgs[1]};
IsIt["-exchangeYZ", 0] => {exchangeYZ ¬ TRUE; SkipArgs[1]};
IsIt["-info", 0] => {info ¬ TRUE; SkipArgs[1]};
IsIt["-convert", 1] => {convert ¬ args[argIndex+1]; SkipArgs[2]};
ENDCASE => {
IO.PutF1[cmd.err, "Bad option: %g\n", IO.rope[args[argIndex]]];
SkipArgs[1];
};
ENDLOOP;
rewrite ¬
convert # NIL OR translate # [0, 0, 0] OR scale # 1.0 OR triangulate OR reversePolys OR
negateNormals OR addNormals OR addTexture OR center OR unitize OR exchangeYZ;
IF out = NIL AND rewrite THEN RETURN[$Failure, "specify output file name"];
IF convert #
NIL
THEN
SELECT
TRUE
FROM
Eq[convert, "quad"] => format ¬ $Quad;
Eq[convert, "dotNone"] => format ¬ $DotNone;
ENDCASE => RETURN[$Failure, "bad convert type"];
IO.PutF1[cmd.out, "Reading %g . . . ", IO.rope[in]];
s ¬ G3dShape.ShapeFromFile[in];
IF reversePolys THEN G3dShape.ReversePolygons[s];
IF negateNormals THEN G3dShape.NegateVertexNormals[s];
IF triangulate THEN G3dShape.Triangulate[s];
IF addNormals THEN G3dShape.SetVertexNormals[s];
IF addTexture THEN G3dMappedAndSolidTexture.MakeTxtrCoordsFromNormals[s];
IF exchangeYZ
THEN
FOR n:
NAT
IN [0..s.vertices.length)
DO
v: G3dShape.Vertex ¬ s.vertices[n];
v.point ¬ [v.point.x, v.point.z, v.point.y];
IF s.vertices.valid[normal] THEN v.normal ¬ [v.normal.x, v.normal.z, v.normal.y];
ENDLOOP;
IF center
OR unitize
THEN {
-- takes precedence over scale or translate command
Translate and/or scale shape to be centered on (0,0,0) and to fit tightly within a 2x2x2 box.
The scaling is uniform (equal in x, y, and z).
mm: G3dBasic.Box ¬ s.objectExtent;
range: REAL ¬ MAX[mm.max.x-mm.min.x, mm.max.y-mm.min.y, mm.max.z-mm.min.z];
IF unitize AND range # 0.0 THEN scale ¬ 2.0/range;
IF center THEN translate ¬ G3dVector.Mul[G3dVector.Midpoint[mm.min, mm.max], scale];
};
IF scale # 1.0
OR translate # [0.0, 0.0, 0.0]
THEN {
G3dShape.TransformShape[shape: s, scale: scale, translate: translate];
G3dShape.TransformVertices[s, s.matrix];
};
IF info THEN {IO.PutRope[cmd.out, "\n"]; PrintInfo[s, cmd.out]};
IF rewrite
THEN {
stream: STREAM ¬ FS.StreamOpen[out, $create];
IO.PutF1[cmd.out, "writing %g . . . ", IO.rope[out]];
IF format =
NIL
THEN G3dShape.ShapeToStream[s, stream, index]
ELSE [] ¬ G3dShape.ShapeToStreamPerFormat[s, stream, format];
IO.Close[stream];
IO.PutRope[cmd.out, "\n"];
};
EXITS Failure => result ¬ $Failure;
};
PrintInfo:
PROC [s: Shape, out:
STREAM] ~ {
b: G3dBasic.Box ¬ s.objectExtent;
stats: ARRAY [0..100) OF NAT ¬ ALL[0];
nats: NatSequence ¬ NEW[G3dBasic.NatSequenceRep[MAX[s.vertices.length, s.surfaces.length]]];
edges: G3dShape.EdgeSequence ¬ G3dShape.MakeEdges[s];
IO.PutF[out, "\t%g vertices, %g polygons, %g edges\n",
IO.int[s.vertices.length], IO.int[s.surfaces.length], IO.int[edges.length]];
IO.PutF1[out, "\ttype = %g, ",
IF s.type # NIL THEN IO.atom[s.type] ELSE IO.rope["<unknown>"]];
IO.PutF[out, "%gtriangulated, %g backfaces\n",
IO.rope[IF s.triangulated THEN NIL ELSE "not "],
IO.rope[IF s.showBackfaces THEN "show" ELSE "hidden"]];
FOR l: Atom.PropList ¬ s.props, l.rest
WHILE l #
NIL
DO
IO.PutF1[out, "\tproperty [%g]\n", IO.atom[NARROW[l.first.key]]];
ENDLOOP;
FOR n: NAT IN [0..nats.length ¬ s.vertices.length) DO nats[n] ¬ 0; ENDLOOP;
FOR n:
NAT
IN [0..s.surfaces.length)
DO
poly: NatSequence ¬ s.surfaces[n].vertices;
FOR nn: NAT IN [0..poly.length) DO nats[poly[nn]] ¬ nats[poly[nn]]+1; ENDLOOP;
ENDLOOP;
FOR n: NAT IN [0..nats.length) DO stats[nats[n]] ¬ stats[nats[n]]+1; ENDLOOP;
FOR n:
NAT
IN [0..100)
DO
IF stats[n] # 0
THEN
IO.PutF[out, "\t%g vertices with %g polygons\n", IO.int[stats[n]], IO.int[n]];
ENDLOOP;
FOR n: NAT IN [0..100) DO stats[n] ¬ 0; ENDLOOP;
FOR n:
NAT
IN [0..s.surfaces.length)
DO
stats[s.surfaces[n].vertices.length] ¬ stats[s.surfaces[n].vertices.length]+1;
ENDLOOP;
FOR n:
NAT
IN [0..100)
DO
IF stats[n] # 0
THEN
IO.PutF[out, "\t%g polygons with %g vertices\n", IO.int[stats[n]], IO.int[n]];
ENDLOOP;
IO.PutFL[out, "\tbounds: min: (%g, %g, %g), max: (%g, %g, %g)\n", LIST[IO.real[b.min.x], IO.real[b.min.y], IO.real[b.min.z], IO.real[b.max.x], IO.real[b.max.y], IO.real[b.max.z]]];
IO.PutF[out, "\tcenter: (%g, %g, %g)\n", IO.real[0.5*(b.min.x+b.max.x)], IO.real[0.5*(b.min.y+b.max.y)], IO.real[0.5*(b.min.z+b.max.z)]];
IO.PutFL[out, "\t2G = 2+E-F-V = 2+%g-%g-%g = %g\n",
LIST[
IO.int[edges.length], IO.int[s.surfaces.length], IO.int[s.vertices.length],
IO.int[2+edges.length-INTEGER[s.vertices.length+s.surfaces.length]]]];
PrintDanglers[edges, out];
PrintMultiples[s, out];
};
PrintMultiples:
PROC [s: Shape, out:
STREAM] ~ {
GetKey:
PROC [a, b:
INTEGER]
RETURNS [
REF] ~ {
RETURN[NEW[IntegerPair ¬ [MIN[a, b], MAX[a, b]]]];
};
StoreEdge:
PROC [a, b:
INTEGER] ~ {
edgeFreq: INTEGER ¬ GetEdgeFrequency[a, b];
[] ¬ RefTab.Store[hash, GetKey[a, b], NEW[INTEGER ¬ edgeFreq+1]];
};
GetEdgeFrequency:
PROC [a, b:
INTEGER]
RETURNS [freq:
INTEGER ¬ 0] ~ {
ref: REF ¬ RefTab.Fetch[hash, GetKey[a, b]].val;
IF ref # NIL THEN freq ¬ NARROW[ref, REF INTEGER]^;
};
Enumerate: RefTab.EachPairAction ~ {
f: INTEGER ¬ NARROW[val, REF INTEGER]^;
IF f > 2
THEN {
i: IntegerPair ¬ NARROW[key, REF IntegerPair]^;
IO.PutF[out, "\terror: edge [%g, %g] frequency = %g\n", IO.int[i.x], IO.int[i.y], IO.int[f]];
};
};
hash: RefTab.Ref ¬ RefTab.Create[equal: Equal, hash: Hash];
FOR i:
INT
IN [0..s.surfaces.length)
DO
poly: G3dBasic.NatSequence ¬ s.surfaces[i].vertices;
v: INTEGER ¬ poly[poly.length-1];
FOR j:
INT
IN [0..poly.length)
DO
StoreEdge[v, poly[j]];
v ¬ poly[j];
ENDLOOP;
ENDLOOP;
[] ¬ RefTab.Pairs[hash, Enumerate];
};
usage:
ROPE ¬
"ModelOps [<output name> ¬] <input name> [-option]
Options include:
convert <type> output converted shape, types include:
quad: MinneView format (for the Iris)
none: U. of Calgary format (for GraphicsJungle)
info print number vertices, polygons, polygon stats, etc.
index output shape with indexing
noIndex output shape with no indexing
reversePolys reverse the polygon vertices
negateNormals negate the vertex normals
triangulate triangulate each polygon)
addNormals compute/recompute vertex normals
center translate to center to [0, 0, 0]
unitize scale to [-1..1] in x,y,z
scale <amount> scale by <amount>
translate <xyz> translate by <x>, <y>, <z>
exchangeYZ exchange the Y and Z coordinates";