DIRECTORY Real, RealFns, GGAngle; GGAngleImpl: CEDAR PROGRAM IMPORTS Real, RealFns EXPORTS GGAngle = BEGIN Normalize: PUBLIC PROC [anyRange: REAL] RETURNS [range180: REAL] = { IF anyRange > 180 THEN { realNumberOf360s: REAL _ anyRange/360; fixNumberOf360s: NAT _ Real.FixC[realNumberOf360s];-- FixC truncates numberToSubtract: REAL _ fixNumberOf360s*360.0; RETURN[anyRange-numberToSubtract]; }; IF anyRange <= -180 THEN { realNumberOf360s: REAL _ -anyRange/360; fixNumberOf360s: NAT _ Real.FixC[realNumberOf360s];-- FixC truncates numberToAdd: REAL _ (fixNumberOf360s + 1)*360.0; RETURN[anyRange+numberToAdd]; }; range180 _ anyRange }; -- end of Normalize Add: PUBLIC PROC [position, increment: REAL] RETURNS [finalPosition: REAL] = { finalPosition _ position + increment; -- -540 < finalPosition < 540 IF finalPosition > 180 THEN finalPosition _ finalPosition - 360 ELSE IF finalPosition <= -180 THEN finalPosition _ finalPosition + 360; }; -- end of Add ClockwiseAngle: PUBLIC PROC [fromPosition, toPosition: REAL] RETURNS [increment: REAL] = { increment _ 0; IF fromPosition < 0 THEN {-- start in lower semi-circle. IF -180 < toPosition AND toPosition <= fromPosition THEN RETURN[toPosition - fromPosition] ELSE increment _ increment + (-180 - fromPosition); increment _ increment + (toPosition-180); } ELSE { -- start in upper semi-circle. IF fromPosition < toPosition AND toPosition <= 180 THEN RETURN [toPosition - fromPosition - 360] ELSE RETURN[toPosition - fromPosition]; }; }; -- end of ClockwiseAngle CounterClockwiseAngle: PUBLIC PROC [fromPosition, toPosition: REAL] RETURNS [increment: REAL] = { increment _ 360 + ClockwiseAngle[fromPosition, toPosition]; IF increment = 360 THEN increment _ 0; }; ShortestDifference: PUBLIC PROC [position1, position2: REAL] RETURNS [pos1MinusPos2: REAL] = { clockwise: REAL _ ClockwiseAngle[position1, position2]; pos1MinusPos2 _ IF clockwise <= -180 THEN clockwise + 360 ELSE clockwise; }; Scale: PUBLIC PROC [angle: REAL, scalar: REAL] RETURNS [angleTimesScalar: REAL] = { angleTimesScalar _ angle*scalar; angleTimesScalar _ Normalize[angleTimesScalar]; }; ArcTan: PUBLIC PROC [numerator, denominator: REAL] RETURNS [degrees: REAL] = { degrees _ RealFns.ArcTanDeg[numerator, denominator]; degrees _ Normalize[degrees]; -- Normalize may not be needed. RealFns seems to do the right thing }; IsInCCWInterval: PUBLIC PROC [testPosition: REAL, fromPosition, toPosition: REAL] RETURNS [BOOL] = { IF fromPosition <= toPosition THEN -- the angle doesn't cross the -x axis RETURN[fromPosition<= testPosition AND testPosition <= toPosition] ELSE RETURN[ (fromPosition<= testPosition AND testPosition <= 180.0) OR (-180.0 < testPosition AND testPosition <= toPosition)]; }; IsInCCWInterval2: PUBLIC PROC [testPosition: REAL, fromPosition, increment: REAL] RETURNS [BOOL] = { toPosition: REAL; IF increment = 360.0 THEN RETURN[TRUE]; toPosition _ Add[fromPosition, increment]; RETURN[IsInCCWInterval[testPosition, fromPosition, toPosition]]; }; AlmostParallel: PUBLIC PROC [pos1, pos2: REAL, epsilon: REAL] RETURNS [BOOL] = { diff: REAL; diff _ ABS[pos1 - pos2]; IF diff < epsilon THEN RETURN[TRUE]; IF ABS[diff - 180.0] < epsilon THEN RETURN[TRUE]; RETURN[FALSE]; }; END. ®File: GGAngleImpl.mesa Last edited by Bier on June 4, 1985 6:15:52 pm PDT Author: Eric Bier on January 5, 1987 4:43:58 pm PST Contents: Gargoyle requires a precise set of angle operations defined with angle "theta" in the range -180 < theta <= 180. "theta" is an absolute angle (ie a position around the circle). Given two positions angles T1 and T2 we can find the incremental clockwise angle CT between them or the incremental counter-clockwise angle CCT where -360 < CT <= 0 and 0 <= CCT < 360. When we add two angles, we are adding an incremental angle to a position angle to get a new position angle. We subtract two position angles to get an incremental angle. Pier, December 6, 1985 10:06:16 am PST Kurlander August 25, 1986 4:45:40 pm PDT Takes an angle in degrees and puts it in -180 < theta <= 180 form. Find the integer number of times 360 goes into it and subtract that many 360's. Find the integer number of times 360 goes into its negative and add (that many + 1) 360's. All angles in degrees All angles in degrees. -360 < increment <= 0 Proceed clockwise until you encounter theta = 180 or "toPostion". Proceed clockwise until you encounter "toPostion" or theta = 180 All angles in degrees. 0 <= increment < 360. For example, if the clockwise angle is -90, the counter-clockwise angle will be 270. All angles in degrees. RETURNS ClockwiseAngle or CounterClockwiseAngle. Whichever is smaller. -180< pos1MinusPos2 <= 180. All angles in degrees. Think of angle as the increment from 0 degrees to angle degrees. Scale this and normalize. Has the effect of calling RealFns.ArcTanDegrees and normalizing the result. Consider the set of angles encountered when traveling counterclockwise from angle "fromPosition" to angle "toPosition" around a circle. Is testPosition one of the angles encountered? pos1 and pos2 are assumed to be in -180 < theta <= 180. Return TRUE if they are the same angle +- epsilon or if they differ by 180+- epsilon. Ê͘Icodešœ™Kšœ2™2Kšœ3™3šœ ™ K™&K™(—K˜šÏk ˜ Kšœ˜Kšœ˜Kšœ˜—K˜šœ œ˜Kšœ˜Kšœ ˜—Kš˜K˜š Ïn œœœ œœ œ˜DKšœB™Bšœœ˜KšœO™OKšœœ˜&KšœœÏc˜DKšœœ˜/Kšœ˜"Kšœ˜—šœœ˜KšœZ™ZKšœœ˜'KšœœŸ˜DKšœ œ˜0Kšœ˜Kšœ˜—Kšœ˜KšœŸ˜—K˜š žœœœœœœ˜NKšœ™Kšœ&Ÿ˜CKšœœ$˜?Kšœœœ%˜GKšœŸ ˜—K˜š žœœœœœ œ˜ZKšœ,™,Kšœ˜šœœŸ˜8KšœA™AKšœœœœ˜ZKšœ/˜3Kšœ œ˜)Kšœ˜—šœŸ˜%Kšœ@™@Kšœœœœ"˜`Kšœœ˜'Kšœ˜—KšœŸ˜K˜—š žœœœœœ œ˜aKšœ,™,KšœT™TKšœ;˜;Kšœœ˜&Kšœ˜—K˜š žœœœœœœ˜_Kšœ{™{Kšœ œ(˜7Kšœœœœ ˜IKšœ˜—K˜šžœœœ œ œœœ˜SKšœr™rKšœ ˜ Kšœ/˜/Kšœ˜—K˜š žœœœœœ œ˜NšœK™KKšœ4˜4KšœŸD˜bKšœ˜K˜——šžœœœœœœœ˜dKšœ·™·šœI˜IKšœB˜B—˜šœ˜Kšœ;˜;Kšœ8˜8——Kšœ˜K˜—šžœœœœœœœ˜dKšœ œ˜Kšœœœœ˜'Kšœ*˜*Kšœ:˜@K˜—K˜šžœœœœ œœœ˜PKšœ@œJ™ŽKšœœ˜ Kšœœ˜Kšœœœœ˜$Kš œœœœœ˜1Kšœœ˜K˜K˜—Kšœ˜K˜—…—