-- File: SVFacesImpl.mesa
-- Last edited by Bier on December 18, 1982 1:37 am
-- Author: Eric Bier on July 3, 1983 1:36 pm
-- Contents: Definitions of various face types such as Cone, Disk Ring, and Cylinder and procedures which make them from simpler data
DIRECTORY
SV2d,
SVBoundBox,
SVFaces,
SVLines2d,
SVVector2d,
SVVector3d;
SVFacesImpl: PROGRAM
IMPORTS SVBoundBox, SVLines2d, SVVector2d, SVVector3d
EXPORTS SVFaces =
BEGIN
TrigLineSeg: TYPE = REF TrigLineSegObj;
TrigLineSegObj: TYPE = SV2d.TrigLineSegObj;
Vector: TYPE = SVVector3d.Vector;
Vector2d: TYPE = SVVector2d.Vector2d;
-- the cone described here is revolute around the y-axis. Otherwise it is very general, allowing for an arbitrary apex point (on the y-axis) and an arbitrary spread ratio.
Cone: TYPE = REF ConeObj;
ConeObj: TYPE = SVFaces.ConeObj;
-- the disk ring described here is revolute around the y-axis. Otherwise it is very general. It may be in an arbitrary horizontal plane (y = yPlane), may have any two radii, rIn and rOut subject to rIn, rOut >= 0 AND rOut > rIn, and may have an upward or downward pointing normal.
DiskRing: TYPE = REF DiskRingObj;
DiskRingObj: TYPE = SVFaces.DiskRingObj;
-- the cylinder described here is revolute around the y-axis. Otherwise it is very general. It may have its top and bottom in arbitrary horizontal planes (y = yHigh and y = yLow) subject to yHigh > yLow and may have an arbitrary radius r.
Cylinder: TYPE = REF CylinderObj;
CylinderObj: TYPE = SVFaces.CylinderObj;
-- the edge on rectangle has a surface normal parallel to the xy plane. That is, it is edge on when viewed from the z direction.
EdgeOnRect: TYPE = REF EdgeOnRectObj;
EdgeOnRectObj: TYPE = SVFaces.EdgeOnRectObj;
Abs: PROC [r: REAL] RETURNS [REAL] = {
RETURN[IF r >= 0 THEN r ELSE -r];
}; -- end of Abs
ConeFromTrigLineSeg: PUBLIC PROC [seg: TrigLineSeg] RETURNS [cone: Cone] = {
isParallel: BOOL;
x, y: REAL;
signHi, signLo: PosNeg;
cone ← NEW[ConeObj];
[cone.h, isParallel] ← SVLines2d.TrigLineMeetsYAxis[seg.line];
IF isParallel THEN ERROR AttemptToCreateDegenerateCone;
-- seg cannot be vertical
IF seg.pLo[2] = seg.pHi[2] THEN ERROR AttemptToCreateDegenerateCone;
-- seg cannot be horizontal
-- to find the spread ratio r/|y-h|, we consider the intersection of the cone (meaning the two nose-to-nose "cones" meeting at the apex) with the z = 0 plane. This gives us two lines intersecting at (0,h). Consider the intersection of the line x = seg.pLo[1] or x = seg.pHi[1] (whichever is larger) (an arbitrary choice) with the negatively sloped line (corresponding to the nose-up cone at this point). The cone radius here is x. We can get the y of intersection from the line equation:
-- y * cos(theta) - x*sin(theta) - d = 0;
-- y = (x*sin(theta) + d)/cos(theta);
-- Finally, M = 1/Abs[y - h] .
x ← IF Abs[seg.pLo[1]] > Abs[seg.pHi[1]] THEN Abs[seg.pLo[1]] ELSE Abs[seg.pHi[1]];
y ← (x*seg.line.s + seg.line.d)/seg.line.c;
cone.M ← x/Abs[y - cone.h];
-- debugging. Find the distance from the cone to each of the points of seg:
BEGIN
coneSeg: TrigLineSeg ← SVLines2d.CreateTrigLineSeg[[x, y], [0, cone.h]];
d1: REAL ← SVLines2d.DistancePointToTrigLineSeg[seg.pLo, coneSeg];
d2: REAL ← SVLines2d.DistancePointToTrigLineSeg[seg.pHi, coneSeg];
d2 ← SVLines2d.DistancePointToTrigLineSeg[seg.pHi, coneSeg];
END;
-- make sure the y's are on the same side of the apex (or on the apex)
cone.yHi ← seg.pHi[2];
cone.yLo ← seg.pLo[2];
signHi ← Sign[cone.yHi - cone.h];
signLo ← Sign[cone.yLo - cone.h];
IF signHi # signLo AND signHi # zero AND signLo # zero THEN ERROR AttemptToCreateDegenerateCone;
IF cone.yHi > cone.h THEN cone.noseIsUp ← FALSE ELSE cone.noseIsUp ← TRUE;
-- IF seg goes down, normal should point out, else normal should point in.
-- By down, I mean second point is below first point, or equivalently, theta is negative.
-- IF y2<y1 THEN cone.normalPointsOut ← TRUE
-- ELSE cone.normalPointsOut ← FALSE;
IF seg.line.theta < 0 THEN cone.normalPointsOut ← TRUE ELSE cone.normalPointsOut ← FALSE;
-- made the boundHedron for this cone.
cone.boundHedron ← SVBoundBox.GeneralConeBoundHedron[seg.pHi[1], seg.pHi[2], seg.pLo[1], seg.pLo[2]];
}; -- end of ConeFromTrigLineSeg
AttemptToCreateDegenerateCone: PUBLIC ERROR = CODE;
PosNeg: TYPE = {pos, neg, zero};
Sign: PROC [r: REAL] RETURNS [sign: PosNeg] = {
RETURN[IF r > 0 THEN pos ELSE IF r < 0 THEN neg ELSE zero];
};
DiskRingFromTrigLineSeg: PUBLIC PROC [seg: TrigLineSeg] RETURNS [diskRing: DiskRing] = {
-- a disk ring is the region in the plane y = yPlane inside the disk x^2 + z^2 = rOut^2 and outside the disk x^2 + z^2 = rIn^2.
x1, x2: REAL;
diskRing ← NEW[DiskRingObj];
-- the line segment must be horizontal
IF seg.pLo[2] # seg.pHi[2] THEN ERROR AttemptToCreateDegenerateDiskRing;
diskRing.yPlane ← seg.pLo[2];
IF seg.pLoIsFirst THEN
{x1 ← seg.pLo[1]; x2 ← seg.pHi[1]} ELSE {x1 ← seg.pHi[1]; x2 ← seg.pLo[1]};
-- to find out if this segment points toward the y axis or away from it, make sure both points are on the same side of the y axis, then mirror the segment to the positive x side (if necessary) and test for x2 > x1;
IF Sign[x1] # Sign[x2] AND Sign[x1] # zero AND Sign[x2] # zero THEN ERROR AttemptToCreateDegenerateDiskRing;
x1 ← Abs[x1];
x2 ← Abs[x2];
-- if segment points out, normal points up, else normal points down.
IF x2 > x1 THEN {diskRing.normal ← [0,1,0];
diskRing.rInSquared ← x1*x1;
diskRing.rOutSquared ← x2*x2;
diskRing.boundHedron ← SVBoundBox.DiskBoundHedron[r: x2, h: seg.pLo[2]]}
ELSE {diskRing.normal ← [0,-1,0];
diskRing.rInSquared ← x2*x2;
diskRing.rOutSquared ← x1*x1;
diskRing.boundHedron ← SVBoundBox.DiskBoundHedron[r: x1, h: seg.pLo[2]]};
}; -- end of DiskRingFromTrigLineSeg
AttemptToCreateDegenerateDiskRing: PUBLIC ERROR = CODE;
CylinderFromTrigLineSeg: PUBLIC PROC [seg: TrigLineSeg] RETURNS [cylinder: Cylinder] = {
cylinder ← NEW[CylinderObj];
-- seg must be vertical
IF seg.pLo[1] # seg.pHi[1] THEN ERROR AttemptToCreateDegenerateCylinder;
-- IF seg goes down normal should point out, else normal should point in.
IF seg.line.theta < 0 THEN cylinder.normalPointsOut ← TRUE
ELSE cylinder.normalPointsOut ← FALSE;
cylinder.yHi ← seg.pHi[2];
cylinder.yLo ← seg.pLo[2];
cylinder.r ← seg.pLo[1];
cylinder.rSquared ← cylinder.r*cylinder.r;
cylinder.boundHedron ← SVBoundBox.GeneralConeBoundHedron[seg.pLo[1], seg.pHi[2], seg.pLo[1], seg.pLo[2]];
};
AttemptToCreateDegenerateCylinder: PUBLIC ERROR = CODE;
EdgeOnRectFromTrigLineSeg: PUBLIC PROC [seg: TrigLineSeg, frontZ, backZ: REAL] RETURNS [edgeOnRect: EdgeOnRect] = {
-- we wish to find the line equation in the from Ax + By + D = 0.
-- trig line segments have the form: y*cos(theta) - x*sin(theta) -d = 0;
-- so A = -sin(theta), B = cos(theta), D = -d;
-- To set valHi and valLo, we look at theta. If pi/4 <= theta < = 3pi/4 or -3pi/4 <=theta <= -pi/4 THEN use y else use x.
normal2d: Vector2d;
edgeOnRect ← NEW[EdgeOnRectObj];
edgeOnRect.frontZ ← frontZ;
edgeOnRect.backZ ← backZ;
edgeOnRect.A ← -seg.line.s;
edgeOnRect.B ← seg.line.c;
edgeOnRect.D ← -seg.line.d;
-- assume that the interior of the polygon is to the right of this line segment so that normals should point to the left.
normal2d ← SVVector2d.LeftNormalOfTrigLineSeg[seg];
edgeOnRect.normal ← SVVector3d.Vector2DAsXYVector[vXY: normal2d];
IF (45 <= seg.line.theta AND seg.line.theta <= 135) OR (-135 <= seg.line.theta AND seg.line.theta <= -45) THEN {
edgeOnRect.valIsY ← TRUE;
edgeOnRect.valLo ← seg.pLo[2];
edgeOnRect.valHi ← seg.pHi[2];
}
ELSE {
edgeOnRect.valIsY ← FALSE;
edgeOnRect.valLo ← Min[seg.pLo[1], seg.pHi[1]];
edgeOnRect.valHi ← Max[seg.pLo[1], seg.pHi[1]];
};
}; -- end of EdgeOnRectFromTrigLineSeg
Max: PRIVATE PROC [a, b: REAL] RETURNS [REAL] = {
RETURN [IF a>b THEN a ELSE b];
};
Min: PRIVATE PROC [a, b: REAL] RETURNS [REAL] = {
RETURN [IF a<b THEN a ELSE b];
};
AttemptToCreateDegenerateEdgeOnRect: PUBLIC ERROR = CODE;
END.