-- 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 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