ReadVAXCadFiles:
PUBLIC
PROC [cadFile, csFile: Rope.
ROPE]
RETURNS [info: CADTypes.Scad] ~
BEGIN
Declarations
cadStream, csStream: IO.STREAM;
qeCad: Cads.Cad;
qeCadData: Cads.CadData;
oldCellsData: Sequences.SequenceData;
polySeqData: Sequences.SequenceData;
cellData: Cells.CellData;
pointData: Points.PointData;
inputPoly: Polynomials.Polynomial;
cadStructure: AlgebraClasses.Structure ← Cads.MakeCadStructure[];
surfaceRope: Rope.ROPE;
oldCells: Sequences.Sequence;
numberOfOldCells: NAT;
newCells: REF CADTypes.CellSequence;
newCellIndex: NAT;
compressedNewCells: REF CADTypes.CellSequence;
Here is a procedure that translates quantifier-free-formulas between representations.
TranslateQFF:
PROC [old: Formulas.Formula]
RETURNS [new: CADTypes.QuantifierFreeFormula] ~
BEGIN
oldData: Formulas.FormulaData ← NARROW[old.data];
mainOp: FormulaOperators.Op ← NARROW[oldData.mainOperator.data, FormulaOperators.OperatorData]^;
Translate an atomic formula.
IF oldData.atomicMatrix
THEN
BEGIN
newOperator: CADTypes.SimpleOperator;
SELECT mainOp
FROM
lt => newOperator ← isNegative;
gt => newOperator ← isPositive;
eq => newOperator ← isZero;
le, ge, ne => ERROR Error[$UnimplementedOperator];
ENDCASE => ERROR Error[$BadOperator];
RETURN [[
isCompound: FALSE,
compoundOperator: none,
compoundArguments: NIL,
simpleOperator: newOperator,
simpleArgument: MultiPolynomial.RefFromRope[Polynomials.ToRope[oldData.polynomial]]
]];
END
Or translate a compound formula.
ELSE
BEGIN
newOperator: CADTypes.CompoundOperator;
newOperands: REF CADTypes.QuantifierFreeFormulaSequence;
oldOperands: LIST OF Formulas.Formula;
nOperands: NAT;
SELECT mainOp
FROM
and => newOperator ← and;
or => newOperator ← or;
not => ERROR Error[$UnimplementedOperator];
ENDCASE => ERROR Error[$BadOperator];
nOperands ← 0;
oldOperands ← oldData.operands;
UNTIL oldOperands =
NIL
DO
nOperands ← nOperands + 1;
oldOperands ← oldOperands.rest;
ENDLOOP;
newOperands ← NEW[CADTypes.QuantifierFreeFormulaSequence[nOperands]];
oldOperands ← oldData.operands;
FOR i:
NAT
IN [0..nOperands)
DO
newOperands[i] ← TranslateQFF[oldOperands.first];
oldOperands ← oldOperands.rest;
ENDLOOP;
RETURN [[
isCompound: TRUE,
compoundOperator: newOperator,
compoundArguments: newOperands,
simpleOperator: none,
simpleArgument: NIL
]];
END;
Read the CAD and covering set files.
cadStream ← FS.StreamOpen[cadFile];
qeCad ← cadStructure.class.read[cadStream, cadStructure];
csStream ← FS.StreamOpen[csFile];
Cads.ReadCSets[csStream, qeCad];
Get the surface equation.
qeCadData ← NARROW[qeCad.data];
polySeqData ← NARROW[qeCadData.inputPolynomials.data];
inputPoly ← polySeqData[1];
surfaceRope ← inputPoly.structure.class.toRope[inputPoly];
Get the old cells and instantiate the new ones.
oldCells ← qeCadData.cells;
oldCellsData ← NARROW[oldCells.data];
numberOfOldCells ← oldCellsData.lengthPlus1 - 1;
newCells ← NEW[CADTypes.CellSequence[numberOfOldCells]];
newCellIndex ← 0;
Iterate over the old cells.
FOR oldCellIndex:
NAT
IN [1..numberOfOldCells]
DO
Only do the cell if it is part of the surface.
cellData ← NARROW[oldCellsData[oldCellIndex].data];
pointData ← NARROW[cellData.index.data];
IF NARROW[pointData[3].data, Ints.IntData]^ MOD 2 = 0 THEN BEGIN
IF QEIO.CellDimension[oldCells[oldCellIndex].index ] < 3 THEN BEGIN
9/11/86 Do the cell if it has dimension less than 3; clearly wrong; the intent here was just to pick up nasty vertical lines.
Translate its indices and dimension.
pointData: Points.PointData ← NARROW[oldCells[oldCellIndex].index.data];
newCells[newCellIndex].indexX ← NARROW[pointData[1].data, Ints.IntData]^;
newCells[newCellIndex].indexY ← NARROW[pointData[2].data, Ints.IntData]^;
newCells[newCellIndex].indexZ ← NARROW[pointData[3].data, Ints.IntData]^;
newCells[newCellIndex].dimension ← cellData.dimension;
Interim calculation of dimension; replace when ReadCad computes it.
newCells[newCellIndex].dimension ←
(NARROW[oldCells[oldCellIndex].index.data, Points.PointData][1] MOD 2) +
(NARROW[oldCells[oldCellIndex].index.data, Points.PointData][2] MOD 2) +
(NARROW[oldCells[oldCellIndex].index.data, Points.PointData][3] MOD 2);
Translate its defining formula.
newCells[newCellIndex].definingFormula ← TranslateQFF[cellData.definingFormula];
Translate the vertex and polygon sequences.
SELECT newCells[newCellIndex].dimension
FROM
0 =>
BEGIN
xReal, yReal, zReal: REAL;
coveringSetData: CoveringSets.CoveringSetData ← NARROW[cellData.coveringSet.data];
setData: Sequences.SequenceData ← NARROW[coveringSetData.dataPoints.data];
columnData: Sequences.SequenceData ← NARROW[setData[1].data];
pointData: Points.PointData ← NARROW[columnData[1].data ];
newCells[newCellIndex].vertices ← NEW[CADTypes.VertexSequence[1]];
xReal ← NARROW[pointData[1].data, Reals.RealData]^;
yReal ← NARROW[pointData[2].data, Reals.RealData]^;
zReal ← NARROW[pointData[3].data, Reals.RealData]^;
newCells[newCellIndex].vertices[0] ← [
x: xReal,
y: yReal,
z: zReal
];
newCells[newCellIndex].polygons ← NEW[CADTypes.TriangleSequence[0]];
END;
1 =>
BEGIN
coveringSetData: CoveringSets.CoveringSetData ← NARROW[cellData.coveringSet.data];
setData: Sequences.SequenceData ← NARROW[coveringSetData.dataPoints.data];
columnData: Sequences.SequenceData ← NARROW[setData[1].data];
numberOfPoints: NAT ← columnData.lengthPlus1 - 1;
newCells[newCellIndex].vertices ← NEW[CADTypes.VertexSequence[numberOfPoints]];
FOR j:
NAT
IN [0..numberOfPoints)
DO
xReal, yReal, zReal: REAL;
pointData: Points.PointData ← NARROW[columnData[j + 1].data ];
xReal ← NARROW[pointData[1].data, Reals.RealData]^;
yReal ← NARROW[pointData[2].data, Reals.RealData]^;
zReal ← NARROW[pointData[3].data, Reals.RealData]^;
newCells[newCellIndex].vertices[j] ← [
x: xReal,
y: yReal,
z: zReal
];
ENDLOOP;
newCells[newCellIndex].polygons ← NEW[CADTypes.TriangleSequence[0]];
END;
2 =>
BEGIN
numberOfPoints, pointIndex: NAT;
triangulation: Triangles.TriangleSeq;
coveringSetData: CoveringSets.CoveringSetData ← NARROW[cellData.coveringSet.data];
setData: Sequences.SequenceData ← NARROW[coveringSetData.dataPoints.data];
Count the number of points.
numberOfPoints ← 0;
numberOfPoints ← 1;
FOR i:
NAT
IN [1..setData.lengthPlus1)
DO
columnData: Sequences.SequenceData ← NARROW[setData[i].data];
numberOfPoints ← numberOfPoints + columnData.lengthPlus1 - 1;
ENDLOOP;
newCells[newCellIndex].vertices ← NEW[CADTypes.VertexSequence[numberOfPoints]];
newCells[newCellIndex].vertices[0] ← [6, 1, 70];
Translate the points.
pointIndex ← 0;
FOR i:
NAT
IN [1..setData.lengthPlus1)
DO
columnData: Sequences.SequenceData ← NARROW[setData[i].data];
FOR j:
NAT
IN [1..columnData.lengthPlus1)
DO
xReal, yReal, zReal: REAL;
pointData: Points.PointData ← NARROW[columnData[j].data ];
xReal ← NARROW[pointData[1].data, Reals.RealData]^;
yReal ← NARROW[pointData[2].data, Reals.RealData]^;
zReal ← NARROW[pointData[3].data, Reals.RealData]^;
newCells[newCellIndex].vertices[pointIndex] ← [
x: xReal,
y: yReal,
z: zReal
];
pointIndex ← pointIndex + 1;
ENDLOOP;
ENDLOOP;
Generate a triangulation of the points.
triangulation ← Triangles.GenerateTriangles[cellData.coveringSet];
Translate the triangles.
IF triangulation =
NIL
THEN
newCells[newCellIndex].polygons ← NEW[CADTypes.TriangleSequence[0]]
ELSE {
newCells[newCellIndex].polygons ← NEW[CADTypes.TriangleSequence [triangulation.trianglesPlusOne - 1]];
FOR i:
NAT
IN [1..triangulation.trianglesPlusOne)
DO
newCells[newCellIndex].polygons[i - 1] ← [
firstVertex: triangulation[i].firstVertex,
secondVertex: triangulation[i].secondVertex,
thirdVertex: triangulation[i].thirdVertex
];
ENDLOOP;
};
Note! These "triangulations" number the vertices from 1, 2, ..., while the vertices are indexed from 0, 1, .... For example, triangle [firstVertex: 2, secondVertex: 3, thirdVertex: 4] refers to vertices[1], vertices[2], and vertices[3]. The discrepancy is remedied when the 2-cells are written
to a temporary file by ThreeDHacks.
END;
ENDCASE =>
BEGIN
ERROR Error[$DegreeOutOfRange];
END;
Increment the index into the new cells.
newCellIndex ← newCellIndex + 1;
Do the next cell.
ENDLOOP;
Compress the new cells.
compressedNewCells ← NEW[CADTypes.CellSequence[newCellIndex]];
FOR i:
NAT
IN [0..newCellIndex)
DO
compressedNewCells[i] ← newCells[i];
ENDLOOP;
Construct and return the Scad.
info ← [
surface: MultiPolynomial.RefFromRope[surfaceRope],
named: FALSE,
name: "(none)",
color: [1, 1, 1],
cells: compressedNewCells];
END;
ReadInfoFile:
PUBLIC
PROC [filename: Rope.
ROPE]
RETURNS [info: CADTypes.Scad] ~
BEGIN
No guarantees for incorrectly formatted files. Will raise syntax errors. It won't make some checks, such as if vertices listed in the Triangles section actually exist.
Declarations
infoStream: IO.STREAM ← FS.StreamOpen[filename];
token: Rope.ROPE ← "";
InfoFileError: ERROR = CODE;
TestError: ERROR = CODE;
Break: SIGNAL = CODE;
BreakProc: IO.BreakProc ~ IO.IDProc;
numberOfCells: NAT;
Define a break procedure that gets a token.
TokenBreakProc:
IO.BreakProc ~
BEGIN
RETURN [
SELECT char
FROM
IN [IO.NUL .. IO.SP], '$ => sepr,
ENDCASE => other
];
Define a break procedure that gets a polynomial.
PolynomialBreakProc:
IO.BreakProc ~
BEGIN
RETURN [
SELECT char
FROM
'$, IO.CR => break,
ENDCASE => other
];
END;
Define a procedure to get the next token or polynomial.
NextToken:
PROC [proc:
IO.BreakProc ← TokenBreakProc]
RETURNS [Rope.
ROPE] ~
BEGIN
token: Rope.ROPE;
[token: token] ← IO.GetTokenRope[infoStream, proc];
RETURN [token];
END;
Recursive procedure to get a defining formula.
GetDefiningFormula:
PROC
RETURNS [CADTypes.QuantifierFreeFormula] ~
BEGIN
operatorRope: Rope.ROPE;
simpleOperator: CADTypes.SimpleOperator;
compoundOperator: CADTypes.CompoundOperator;
isCompound: BOOLEAN;
operatorRope ← NextToken[];
IF Rope.Compare[operatorRope, "IsPositive"] = equal
THEN
{simpleOperator ← isPositive; isCompound ← FALSE}
ELSE
IF Rope.Compare[operatorRope, "IsZero"] = equal
THEN
{simpleOperator ← isZero; isCompound ← FALSE}
ELSE
IF Rope.Compare[operatorRope, "IsNegative"] = equal
THEN
{simpleOperator ← isNegative; isCompound ← FALSE}
ELSE
IF Rope.Compare[operatorRope, "And"] = equal
THEN
{compoundOperator ← and; isCompound ← TRUE}
ELSE
IF Rope.Compare[operatorRope, "Or"] = equal
THEN
{compoundOperator ← or; isCompound ← TRUE}
ELSE ERROR InfoFileError;
IF ~isCompound
THEN
BEGIN
polyRope: Rope.ROPE ← NextToken[PolynomialBreakProc];
RETURN [[
isCompound: FALSE,
compoundOperator: none,
compoundArguments: NIL,
simpleOperator: simpleOperator,
simpleArgument: MultiPolynomial.RefFromRope[polyRope]
]];
END
ELSE
BEGIN
numberOfArguments: NAT ← Convert.IntFromRope[NextToken[]];
qff: CADTypes.QuantifierFreeFormula ← [
isCompound: TRUE,
compoundOperator: compoundOperator,
compoundArguments: NEW[CADTypes.QuantifierFreeFormulaSequence[numberOfArguments]],
simpleOperator: none,
simpleArgument: NIL];
FOR argument:
NAT
IN [0..numberOfArguments)
DO
qff.compoundArguments[argument] ← GetDefiningFormula[];
ENDLOOP;
RETURN [qff];
END;
END;
Get the BEGIN token
UNTIL Rope.Compare[token, "BEGIN"] = equal
DO
token ← NextToken[];
ENDLOOP;
Get the CAD DESCRIPTION tokens.
IF Rope.Compare[NextToken[], "CAD"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "DESCRIPTION"] # equal THEN ERROR InfoFileError;
Get the surface polynomial.
IF Rope.Compare[NextToken[], "Surface"] # equal THEN ERROR InfoFileError;
token ← NextToken[PolynomialBreakProc];
info.surface ← MultiPolynomial.RefFromRope[token];
Get the name of the surface.
IF Rope.Compare[NextToken[], "Name"] # equal THEN ERROR InfoFileError;
token ← NextToken[];
IF Rope.Compare[token, "UNNAMED"] = equal
THEN
BEGIN
info.named ← FALSE;
info.name ← "(unnamed)";
END
ELSE
BEGIN
info.named ← TRUE;
info.name ← token;
END;
Get the color of the surface.
IF Rope.Compare[NextToken[], "Color"] # equal THEN ERROR InfoFileError;
info.color.R ← Convert.RealFromRope[NextToken[]];
info.color.G ← Convert.RealFromRope[NextToken[]];
info.color.B ← Convert.RealFromRope[NextToken[]];
Get the number of cells and create the cell sequence in the Scad.
IF Rope.Compare[NextToken[], "Cells"] # equal THEN ERROR InfoFileError;
numberOfCells ← Convert.IntFromRope[NextToken[]];
info.cells ← NEW[CADTypes.CellSequence[numberOfCells]];
Get the BEGIN CELL DEFINITIONS tokens.
IF Rope.Compare[NextToken[], "BEGIN"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "CELL"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "DEFINITIONS"] # equal THEN ERROR InfoFileError;
Iterate over the cells.
FOR cell:
NAT
IN [0..numberOfCells)
DO
Declarations
dimension: CARDINAL;
Get the indices.
IF Rope.Compare[NextToken[], "CellIndices"] # equal THEN ERROR InfoFileError;
info.cells[cell].indexX ← Convert.IntFromRope[NextToken[]];
info.cells[cell].indexY ← Convert.IntFromRope[NextToken[]];
info.cells[cell].indexZ ← Convert.IntFromRope[NextToken[]];
Get the dimension.
IF Rope.Compare[NextToken[], "CellDegree"] # equal THEN ERROR InfoFileError;
dimension ← Convert.CardFromRope[NextToken[]];
IF dimension > 2 THEN ERROR InfoFileError;
info.cells[cell].dimension ← dimension;
Get the defining formula.
IF Rope.Compare[NextToken[], "DefiningFormula"] # equal THEN ERROR InfoFileError;
info.cells[cell].definingFormula ← GetDefiningFormula[];
Do the next cell.
ENDLOOP;
Get the END CELL DEFINITIONS tokens.
IF Rope.Compare[NextToken[], "END"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "CELL"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "DEFINITIONS"] # equal THEN ERROR InfoFileError;
Get the BEGIN CELL DISPLAY INFORMATION tokens.
IF Rope.Compare[NextToken[], "BEGIN"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "CELL"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "DISPLAY"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "INFORMATION"] # equal THEN ERROR InfoFileError;
Iterate over the cells.
FOR cell:
NAT
IN [0..numberOfCells)
DO
Declarations
dimension: CARDINAL;
nVertices, nTriangles: NAT;
Get the indices for comparison.
IF Rope.Compare[NextToken[], "Indices"] # equal THEN ERROR InfoFileError;
IF Convert.IntFromRope[NextToken[]] # info.cells[cell].indexX THEN ERROR InfoFileError;
IF Convert.IntFromRope[NextToken[]] # info.cells[cell].indexY THEN ERROR InfoFileError;
IF Convert.IntFromRope[NextToken[]] # info.cells[cell].indexZ THEN ERROR InfoFileError;
Get the dimension for comparison.
IF Rope.Compare[NextToken[], "Degree"] # equal THEN ERROR InfoFileError;
dimension ← Convert.CardFromRope[NextToken[]];
IF dimension # info.cells[cell].dimension THEN ERROR InfoFileError;
Get the vertex sequence. For zero-cells, there is only one vertex, the Position; for one- and two-cells, the number of vertices is specified in the file.
IF dimension = 0
THEN
BEGIN
IF Rope.Compare[NextToken[], "Position"] # equal THEN ERROR InfoFileError;
info.cells[cell].vertices ← NEW[CADTypes.VertexSequence[1]];
info.cells[cell].vertices[0].x ← Convert.RealFromRope[NextToken[]];
info.cells[cell].vertices[0].y ← Convert.RealFromRope[NextToken[]];
info.cells[cell].vertices[0].z ← Convert.RealFromRope[NextToken[]];
END
ELSE
IF dimension = 1
OR dimension = 2
THEN
BEGIN
IF Rope.Compare[NextToken[], "Vertices"] # equal THEN ERROR InfoFileError;
nVertices ← Convert.IntFromRope[NextToken[]];
info.cells[cell].vertices ← NEW[CADTypes.VertexSequence[nVertices]];
FOR i:
NAT
IN [0..nVertices)
DO
info.cells[cell].vertices[i].x ← Convert.RealFromRope[NextToken[]];
info.cells[cell].vertices[i].y ← Convert.RealFromRope[NextToken[]];
info.cells[cell].vertices[i].z ← Convert.RealFromRope[NextToken[]];
ENDLOOP;
END
ELSE
ERROR InfoFileError;
Get the triangle sequence. For two-cells, the number of vertices is specified in the file; for zero- and one-cells, there are none.
IF dimension = 2
THEN
BEGIN
IF Rope.Compare[NextToken[], "Triangles"] # equal THEN ERROR InfoFileError;
nTriangles ← Convert.IntFromRope[NextToken[]];
info.cells[cell].polygons ← NEW[CADTypes.TriangleSequence[nTriangles]];
FOR i:
NAT
IN [0..nTriangles)
DO
info.cells[cell].polygons[i].firstVertex ← Convert.IntFromRope[NextToken[]];
info.cells[cell].polygons[i].secondVertex ← Convert.IntFromRope[NextToken[]];
info.cells[cell].polygons[i].thirdVertex ← Convert.IntFromRope[NextToken[]];
ENDLOOP;
END
ELSE
BEGIN
info.cells[cell].polygons ← NEW[CADTypes.TriangleSequence[0]];
END;
Do the next cell.
ENDLOOP;
Get the END CELL DISPLAY INFORMATION tokens.
IF Rope.Compare[NextToken[], "END"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "CELL"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "DISPLAY"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "INFORMATION"] # equal THEN ERROR InfoFileError;
Get the END CAD DESCRIPTION tokens.
IF Rope.Compare[NextToken[], "END"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "CAD"] # equal THEN ERROR InfoFileError;
IF Rope.Compare[NextToken[], "DESCRIPTION"] # equal THEN ERROR InfoFileError;
END;
WriteInfoFile:
PUBLIC
PROC [filename: Rope.
ROPE, info: CADTypes.Scad] ~
BEGIN
Declarations
infoStream: IO.STREAM ← FS.StreamOpen[filename, $create];
InternalError: ERROR = CODE;
Procedure to write a defining formula.
WriteDefiningFormula:
PROC[df: CADTypes.QuantifierFreeFormula, nesting:
NAT ← 2] ~
BEGIN
Get the operator
operatorRope: Rope.
ROPE ←
IF df.isCompound
THEN
SELECT df.compoundOperator
FROM
and => "And",
or => "Or",
ENDCASE => ERROR InternalError
ELSE
SELECT df.simpleOperator
FROM
isPositive => "IsPositive",
isZero => "IsZero",
isNegative => "IsNegative"
ENDCASE => ERROR InternalError;
Indent unless the nesting level is 2.
IF nesting # 2
THEN
FOR i:
NAT
IN [1..nesting]
DO
IO.PutF[infoStream, "\t"];
ENDLOOP;
Write the defining formula.
IF df.isCompound
THEN
BEGIN
IO.PutF[
infoStream,
"%g %g\n",
IO.rope[operatorRope],
IO.int[df.compoundArguments.length]];
FOR formula:
NAT
IN [0..df.compoundArguments.length)
DO
WriteDefiningFormula[df.compoundArguments[formula], nesting + 1];
ENDLOOP;
END
ELSE
BEGIN
IO.PutF[
infoStream,
"%g %g\n",
IO.rope[operatorRope],
IO.rope[MultiPolynomial.RopeFromRef[df.simpleArgument]]];
END;
Write the header.
IO.PutF[infoStream, "BEGIN CAD DESCRIPTION\n\n"];
IO.PutF[
infoStream,
"\tSurface %g\n",
IO.rope[MultiPolynomial.RopeFromRef[info.surface]]];
IF info.named
THEN
IO.PutF[infoStream, "\tName %g\n",
IO.rope[info.name]]
ELSE IO.PutF[infoStream, "\tName UNNAMED\n"];
IO.PutF[
infoStream,
"\tColor %g %g %g\n",
IO.real[info.color.R],
IO.real[info.color.G],
IO.real[info.color.B]];
IO.PutF[infoStream, "\tCells %g\n", IO.int[info.cells.nCells]];
IO.PutF[infoStream, "\n\tBEGIN CELL DEFINITIONS\n"];
Iterate over the cells, writing the indices, dimension, and defining formula.
FOR cell:
NAT
IN [0..info.cells.nCells)
DO
IO.PutF[
infoStream,
"\n\t\tCellIndices %g %g %g\n",
IO.int[info.cells[cell].indexX],
IO.int[info.cells[cell].indexY],
IO.int[info.cells[cell].indexZ]];
IO.PutF[infoStream, "\t\tCellDegree %g\n", IO.int[info.cells[cell].dimension]];
IO.PutF[infoStream, "\t\tDefiningFormula "];
WriteDefiningFormula[info.cells[cell].definingFormula, 2];
ENDLOOP;
Write the end of the cell definitions block and the start of the display information block.
IO.PutF[infoStream, "\n\tEND CELL DEFINITIONS\n"];
IO.PutF[infoStream, "\n\tBEGIN CELL DISPLAY INFORMATION\n"];
Iterate over the cells, writing the display information.
FOR cell:
NAT
IN [0..info.cells.nCells)
DO
Extract some useful quantities.
dimension: CARDINAL ← info.cells[cell].dimension;
nVertices: NAT ← info.cells[cell].vertices.nVertices;
nTriangles: NAT ← info.cells[cell].polygons.nTriangles;
Write the indices and dimension.
IO.PutF[
infoStream,
"\n\t\tIndices %g %g %g\n",
IO.int[info.cells[cell].indexX],
IO.int[info.cells[cell].indexY],
IO.int[info.cells[cell].indexZ]];
IO.PutF[infoStream, "\t\tDegree %g\n", IO.int[dimension]];
Write the vertex sequence.
IF dimension = 0
THEN
IO.PutF[
infoStream,
"\t\tPosition %g %g %g\n",
IO.real[info.cells[cell].vertices[0].x],
IO.real[info.cells[cell].vertices[0].y],
IO.real[info.cells[cell].vertices[0].z]]
ELSE
BEGIN
IO.PutF[infoStream, "\t\tVertices %g\n", IO.int[nVertices]];
FOR i:
NAT
IN [0..nVertices)
DO
IO.PutF[
infoStream,
"\t\t\t%g %g %g\n",
IO.real[info.cells[cell].vertices[i].x],
IO.real[info.cells[cell].vertices[i].y],
IO.real[info.cells[cell].vertices[i].z]];
ENDLOOP;
END;
Write the triangle sequence if the cell has dimension 2.
IF dimension = 2
THEN
BEGIN
IO.PutF[infoStream, "\t\tTriangles %g\n", IO.int[nTriangles]];
FOR i:
NAT
IN [0..nTriangles)
DO
IO.PutF[
infoStream,
"\t\t\t%g %g %g\n",
IO.int[info.cells[cell].polygons[i].firstVertex],
IO.int[info.cells[cell].polygons[i].secondVertex],
IO.int[info.cells[cell].polygons[i].thirdVertex]];
ENDLOOP;
END;
Do the next cell.
ENDLOOP;
Write the "END"s and close the file.
IO.PutF[infoStream, "\n\tEND CELL DISPLAY INFORMATION\n"];
IO.PutF[infoStream, "\nEND CAD DESCRIPTION\n"];
IO.Close[infoStream];
END;
SelectCell:
PUBLIC
PROC [scad: CADTypes.Scad, mask:
REF CADTypes.VisibleMask, title: Rope.
ROPE]
RETURNS [index:
INT] ~
BEGIN
Declarations.
menuRopes: LIST OF Rope.ROPE ← NIL;
mousePosition: REF Interminal.MousePosition;
selection: INT;
Ensure that the scad and mask have the same number of elements.
IF scad.cells.nCells # mask.length THEN ERROR Error[$WrongNumberOfCells];
Build the list of ropes, backwards (of course), for the menu.
FOR i:
INT
IN [0..scad.cells.nCells)
DO
j: NAT ← scad.cells.nCells - i - 1;
cell: CADTypes.CellRec ← scad.cells[j];
cellRope: Rope.ROPE ← IO.PutFR["(%g, %g, %g)", IO.int[cell.indexX], IO.int[cell.indexY], IO.int[cell.indexZ]];
IF mask[j] THEN cellRope ← Rope.Concat[cellRope, " V"];
menuRopes ← CONS[cellRope, menuRopes];
ENDLOOP;
Get a selection from the pop-up menu.
mousePosition ← NEW[Interminal.MousePosition ← [mouseX: 0, color: FALSE, mouseY: 0]];
selection ← PopUpMenu.RequestSelection[
label: title,
choice: menuRopes,
timeOut: 0,
mouse: mousePosition];
Return the selection (minus 1 so the counts start at zero), or return -1 on timeout/abort.
IF (selection = 0) OR (selection = -1) THEN RETURN[-1]
ELSE RETURN[selection - 1];
END;
WhatCellIsItOn:
PUBLIC
PROC[point: Geometry3dVector.Triple, variables: CADTypes.VariableRec, scad: CADTypes.Scad]
RETURNS [cell:
NAT] ~
BEGIN
FOR i:
NAT
IN [0..scad.cells.nCells)
DO
IF SatisfiesQFF[point, variables, scad.cells[i].definingFormula] THEN RETURN[i];
ENDLOOP;
ERROR Error[$NotOnAnyCell];
END;
SatisfiesQFF:
PROC[point: Geometry3dVector.Triple, variables: CADTypes.VariableRec, qff: CADTypes.QuantifierFreeFormula]
RETURNS [satisfiesQFF:
BOOLEAN] ~
BEGIN
Returns a boolean value indicating if point satisfies the qff.
whoops: ERROR = CODE;
epsilon: REAL ~ 0.0001;
IF qff.isCompound
THEN
SELECT qff.compoundOperator
FROM
and =>
BEGIN
FOR i:
NAT
IN [0..qff.compoundArguments.length)
DO
IF ~SatisfiesQFF[point, variables, qff.compoundArguments[i]] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
END;
or =>
BEGIN
FOR i:
NAT
IN [0..qff.compoundArguments.length)
DO
IF SatisfiesQFF[point, variables, qff.compoundArguments[i]] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
END;
ENDCASE => ERROR whoops
ELSE
BEGIN
bindings: MultiPolynomial.EvaluationBindings ←
LIST [
[variable: variables.x, value: point.x],
[variable: variables.y, value: point.y],
[variable: variables.z, value: point.z]
];
evaluatedPol: REAL ← MultiPolynomial.TotallyEvaluate[qff.simpleArgument, bindings];
SELECT qff.simpleOperator
FROM
isPositive => RETURN[evaluatedPol > 0];
isZero => RETURN[ABS[evaluatedPol] < epsilon];
isNegative => RETURN[evaluatedPol < 0];
ENDCASE => ERROR whoops;
END;