GGUtilityImplA.mesa
Contents: Created from files GGUtilityImpl.mesa, GGCubic2Impl.mesa, GGContainerImpl.mesa, GGCircleCacheImpl.mesa, GGBuiltInShapesImpl.mesa, GGDescribeImpl.mesa October 12, 1987
Copyright Ó 1987, 1988, 1989 by Xerox Corporation. All rights reserved.
Last edited by Bier on October 28, 1987 7:51:27 pm PST.
Pier, August 15, 1991 3:57 pm PDT
Bier, June 27, 1991 12:04 pm PDT
Doug Wyatt, December 19, 1989 10:59:49 am PST
DIRECTORY
Basics, CodeTimer, CubicPaths, Feedback, FunctionCache, GGBasicTypes, GGBoundBox, GGBuiltinShapes, GGCircleCache, GGCircles, GGColorOps, GGContainer, GGCoreOps, GGCoreTypes, GGCubic2, GGDescribe, GGInterfaceTypes, GGModelTypes, GGOutline, GGParseIn, GGParseOut, GGSegment, GGSegmentTypes, GGShapes, GGSlice, GGSliceOps, GGTraj, GGTransform, GGUtility, Icons, Imager, ImagerBackdoor, ImagerBitmapContext, ImagerColor, ImagerColorFns, ImagerColorPrivate, ImagerSample, ImagerTransformation, IO, NamedColors, Real, RealFns, Rope, Vectors2d;
GGUtilityImplA: CEDAR PROGRAM
IMPORTS CodeTimer, Feedback, FunctionCache, GGBoundBox, GGCircles, GGColorOps, GGCoreOps, GGOutline, GGParseIn, GGParseOut, GGSegment, GGShapes, GGSlice, GGSliceOps, GGTraj, GGTransform, GGUtility, Imager, ImagerBackdoor, ImagerBitmapContext, ImagerColor, ImagerColorFns, ImagerColorPrivate, ImagerSample, IO, NamedColors, Real, RealFns, Rope, Vectors2d
EXPORTS GGDescribe, GGUtility, GGBuiltinShapes, GGCircleCache = BEGIN
AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint;
Bitmap: TYPE = Imager.SampleMap;
BitVector: TYPE = GGBasicTypes.BitVector;
Cache: TYPE = FunctionCache.Cache;
CachedCircle: TYPE = REF CachedCircleObj;
CachedCircleObj: TYPE = GGCircleCache.CachedCircleObj;
Color: TYPE = Imager.Color;
Context: TYPE = Imager.Context;
DefaultData: TYPE = GGModelTypes.DefaultData;
FeatureData: TYPE = GGModelTypes.FeatureData;
Path: TYPE = CubicPaths.Path;
Point: TYPE = GGBasicTypes.Point;
PointProc: TYPE = CubicPaths.PointProc;
Range: TYPE = FunctionCache.Range;
ROPE: TYPE = Rope.ROPE;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
Sequence: TYPE = GGModelTypes.Sequence;
SequenceOfReal: TYPE = REF SequenceOfRealObj;
SequenceOfRealObj: TYPE = GGCoreTypes.SequenceOfRealObj;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceParts: TYPE = GGModelTypes.SliceParts;
Traj: TYPE = GGModelTypes.Traj;
TrajData: TYPE = GGModelTypes.TrajData;
Vector: TYPE = GGBasicTypes.Vector;
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = CODE;
EntityNotFound: PUBLIC SIGNAL = CODE;
NotYetImplemented: PUBLIC SIGNAL = CODE;
Templates for List Operations
Two Finger List Construction
StartTypeList: PUBLIC PROC [] RETURNS [entityList, ptr: LIST OF Type] = {
ptr ← entityList ← NIL;
};
AddType: PUBLIC PROC [entity: Type, entityList, ptr: LIST OF Type] RETURNS [newList, newPtr: LIST OF Type] = {
IF ptr = NIL THEN {
IF NOT entityList = NIL THEN ERROR;
newPtr ← newList ← CONS[entity, NIL];
RETURN;
}
ELSE {
newList ← entityList;
ptr.rest ← CONS[entity, NIL];
newPtr ← ptr.rest;
};
};
AppendTypeList: PUBLIC PROC [list1, list2: LIST OF Type] RETURNS [result: LIST OF Type] = {
pos: LIST OF Type;
newCell: LIST OF Type;
Non-destructive (copies the first list).
IF list1 = NIL THEN RETURN[list2];
result ← CONS[list1.first, NIL];
pos ← result;
FOR l: LIST OF Type ← list1.rest, l.rest UNTIL l = NIL DO
newCell ← CONS[l.first, NIL];
pos.rest ← newCell;
pos ← newCell;
ENDLOOP;
pos.rest ← list2;
};
Destructive Delete
DeleteTypeFromList: PUBLIC PROC [entity: Type, entityList: LIST OF Type] RETURNS [smallerList: LIST OF Type] = {
beforeEnt, ent, afterEnt: LIST OF Type;
found: BOOLFALSE;
[beforeEnt, ent, afterEnt, found] ← FindTypeAndNeighbors[entity, entityList];
IF NOT found THEN RETURN[entityList];
IF beforeEnt = NIL THEN smallerList ← afterEnt
ELSE {
beforeEnt.rest ← afterEnt;
smallerList ← entityList;
};
};
FindTypeAndNeighbors: PROC [entity: Type, entityList: LIST OF Type] RETURNS [beforeEnt, ent, afterEnt: LIST OF Type, found: BOOLFALSE] = {
lastE: LIST OF Type ← NIL;
eList: LIST OF Type ← entityList;
IF eList = NIL THEN RETURN;
UNTIL eList = NIL DO
IF eList.first = entity THEN {
beforeEnt ← lastE;
ent ← eList;
afterEnt ← eList.rest;
found ← TRUE;
RETURN;
};
lastE ← eList;
eList ← eList.rest;
ENDLOOP;
};
Operations on LIST OF FeatureData
StartFeatureDataList: PUBLIC PROC [] RETURNS [entityList, ptr: LIST OF FeatureData] = {
ptr ← entityList ← NIL;
};
AddFeatureData: PUBLIC PROC [entity: FeatureData, entityList, ptr: LIST OF FeatureData] RETURNS [newList, newPtr: LIST OF FeatureData] = {
IF ptr = NIL THEN {
IF NOT entityList = NIL THEN ERROR;
newPtr ← newList ← CONS[entity, NIL];
RETURN;
}
ELSE {
newList ← entityList;
ptr.rest ← CONS[entity, NIL];
newPtr ← ptr.rest;
};
};
CopyFeatureDataList: PUBLIC PROC [l: LIST OF FeatureData] RETURNS [copyList: LIST OF FeatureData] = {
z: LIST OF FeatureData ← NIL;
IF l = NIL THEN RETURN[NIL];
copyList ← CONS[l.first, NIL];
z ← copyList;
UNTIL (l ← l.rest) = NIL DO
z.rest ← CONS[l.first, NIL];
z ← z.rest;
ENDLOOP;
};
FeatureDataNconc: PUBLIC PROC [l1, l2: LIST OF FeatureData] RETURNS [bigList: LIST OF FeatureData] = {
z: LIST OF FeatureData ← l1;
IF z = NIL THEN RETURN[l2];
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← l2;
RETURN[l1];
};
Operations on LIST OF Sequence
StartSequenceList: PUBLIC PROC [] RETURNS [entityList, ptr: LIST OF Sequence] = {
ptr ← entityList ← NIL;
};
AddSequence: PUBLIC PROC [entity: Sequence, entityList, ptr: LIST OF Sequence] RETURNS [newList, newPtr: LIST OF Sequence] = {
IF ptr = NIL THEN {
IF NOT entityList = NIL THEN ERROR;
newPtr ← newList ← CONS[entity, NIL];
RETURN;
}
ELSE {
newList ← entityList;
ptr.rest ← CONS[entity, NIL];
newPtr ← ptr.rest;
};
};
AppendSequenceList: PUBLIC PROC [list1, list2: LIST OF Sequence] RETURNS [result: LIST OF Sequence] = {
pos: LIST OF Sequence;
newCell: LIST OF Sequence;
Non-destructive (copies the first list).
IF list1 = NIL THEN RETURN[list2];
result ← CONS[list1.first, NIL];
pos ← result;
FOR l: LIST OF Sequence ← list1.rest, l.rest UNTIL l = NIL DO
newCell ← CONS[l.first, NIL];
pos.rest ← newCell;
pos ← newCell;
ENDLOOP;
pos.rest ← list2;
}; -- end of AppendSequenceList
DeleteSequenceFromList: PUBLIC PROC [seq: Sequence, seqList: LIST OF Sequence] RETURNS [smallerList: LIST OF Sequence] = {
beforeEnt, ent, afterEnt: LIST OF Sequence;
found: BOOLFALSE;
[beforeEnt, ent, afterEnt, found] ← FindSequenceAndNeighbors[seq, seqList];
IF NOT found THEN RETURN[seqList];
IF beforeEnt = NIL THEN smallerList ← afterEnt
ELSE {
beforeEnt.rest ← afterEnt;
smallerList ← seqList;
};
}; -- end of DeleteSequenceFromList
FindSequenceAndNeighbors: PROC [entity: Sequence, entityList: LIST OF Sequence] RETURNS [beforeEnt, ent, afterEnt: LIST OF Sequence, found: BOOLFALSE] = {
lastE: LIST OF Sequence ← NIL;
eList: LIST OF Sequence ← entityList;
IF eList = NIL THEN ERROR EntityNotFound;
IF eList = NIL THEN RETURN;
UNTIL eList = NIL DO
IF eList.first = entity THEN {
beforeEnt ← lastE;
ent ← eList;
afterEnt ← eList.rest;
found ← TRUE;
RETURN;
};
lastE ← eList;
eList ← eList.rest;
ENDLOOP;
};
Operations on LIST OF SliceDescriptor
StartSliceDescriptorList: PUBLIC PROC [] RETURNS [entityList, ptr: LIST OF SliceDescriptor] = {
ptr ← entityList ← NIL;
};
AddSliceDescriptor: PUBLIC PROC [entity: SliceDescriptor, entityList, ptr: LIST OF SliceDescriptor] RETURNS [newList, newPtr: LIST OF SliceDescriptor] = {
IF ptr = NIL THEN {
IF NOT entityList = NIL THEN ERROR;
newPtr ← newList ← CONS[entity, NIL];
RETURN;
}
ELSE {
newList ← entityList;
ptr.rest ← CONS[entity, NIL];
newPtr ← ptr.rest;
};
};
AppendSliceDescriptorList: PUBLIC PROC [list1, list2: LIST OF SliceDescriptor] RETURNS [result: LIST OF SliceDescriptor] = {
pos: LIST OF SliceDescriptor;
newCell: LIST OF SliceDescriptor;
Non-destructive (copies the first list).
IF list1 = NIL THEN RETURN[list2];
result ← CONS[list1.first, NIL];
pos ← result;
FOR l: LIST OF SliceDescriptor ← list1.rest, l.rest UNTIL l = NIL DO
newCell ← CONS[l.first, NIL];
pos.rest ← newCell;
pos ← newCell;
ENDLOOP;
pos.rest ← list2;
};
DeleteSliceDescriptorFromList: PUBLIC PROC [sliceD: SliceDescriptor, sliceDList: LIST OF SliceDescriptor] RETURNS [smallerList: LIST OF SliceDescriptor] = {
beforeEnt, ent, afterEnt: LIST OF SliceDescriptor;
found: BOOLFALSE;
[beforeEnt, ent, afterEnt, found] ← FindSliceDescriptorAndNeighbors[sliceD, sliceDList];
IF NOT found THEN RETURN[sliceDList];
IF beforeEnt = NIL THEN smallerList ← afterEnt
ELSE {
beforeEnt.rest ← afterEnt;
smallerList ← sliceDList;
};
};
FindSliceDescriptorAndNeighbors: PROC [sliceD: SliceDescriptor, sliceDList: LIST OF SliceDescriptor] RETURNS [beforeEnt, ent, afterEnt: LIST OF SliceDescriptor, found: BOOLFALSE] = {
lastE: LIST OF SliceDescriptor ← NIL;
eList: LIST OF SliceDescriptor ← sliceDList;
IF eList = NIL THEN RETURN;
UNTIL eList = NIL DO
IF eList.first = sliceD THEN {
beforeEnt ← lastE;
ent ← eList;
afterEnt ← eList.rest;
found ← TRUE;
RETURN;
};
lastE ← eList;
eList ← eList.rest;
ENDLOOP;
};
CopySliceDescriptorList: PUBLIC PROC [l1: LIST OF SliceDescriptor] RETURNS [val: LIST OF SliceDescriptor] = {
z: LIST OF SliceDescriptor ← NIL;
val ← NIL;
IF l1 = NIL THEN RETURN[val];
val ← CONS[l1.first, val];
z ← val;
UNTIL (l1 ← l1.rest) = NIL DO
z.rest ← CONS[l1.first, z.rest];
z ← z.rest;
ENDLOOP;
RETURN[val];
}; -- of Append
Operations on Bit Vectors
AllFalse: PUBLIC PROC [bitvec: BitVector] RETURNS [BOOL] = {
FOR i: NAT IN [0..bitvec.len) DO
IF bitvec[i] = TRUE THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
AllTrue: PUBLIC PROC [bitvec: BitVector] RETURNS [BOOL] = {
FOR i: NAT IN [0..bitvec.len) DO
IF bitvec[i] = FALSE THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
Dash Patterns
CopyPattern: PUBLIC PROC [pattern: SequenceOfReal] RETURNS [new: SequenceOfReal] = {
IF pattern=NIL THEN RETURN[NIL];
new ← NEW[SequenceOfRealObj[pattern.len]];
FOR index: NAT IN [0..pattern.len) DO
new[index] ← pattern[index];
ENDLOOP;
};
EquivalentPatterns: PUBLIC PROC [p1, p2: SequenceOfReal] RETURNS [BOOL ← FALSE] = {
IF p1.len # p2.len THEN RETURN[FALSE];
FOR i: NAT IN [0..p1.len) DO
IF p1[i] # p2[i] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
Colors
GetSpecialColor and TrackPatch moved to GGPortImpl
ChangeHue: PUBLIC PROC [startColor: Color, hueFrom: Color] RETURNS [newColor: Color] = {
startHSV: ImagerColorFns.HSV;
hue: REAL;
IF startColor = NIL OR hueFrom = NIL THEN RETURN[NIL];
WITH startColor SELECT FROM
startColorC: Imager.ConstantColor => {
hueFromC: Imager.ConstantColor ← NARROW[hueFrom];
startHSV ← ImagerColorFns.HSVFromRGB[ImagerColorPrivate.RGBFromColor[startColorC]];
hue ← ImagerColorFns.HSVFromRGB[ImagerColorPrivate.RGBFromColor[hueFromC]].H;
newColor ← ImagerColor.ColorFromRGB[ImagerColorFns.RGBFromHSV[[hue, startHSV.S, startHSV.V]]];
};
ENDCASE => newColor ← startColor;
};
ColorToRope: PUBLIC PROC [color: Imager.Color] RETURNS [rope: Rope.ROPE] = {
stream: IO.STREAM;
stream ← IO.ROS[];
GGParseOut.WriteColor[stream, color];
rope ← IO.RopeFromROS[stream];
};
ColorFromRope: PUBLIC PROC [rope: Rope.ROPE] RETURNS [color: Imager.Color] = {
stream: IO.STREAM;
version: REAL ← GGUtility.version;
stream ← IO.RIS[rope];
color ← GGParseIn.ReadColor[stream, version];
};
ColorToRGBRope: PUBLIC PROC [color: Imager.Color] RETURNS [rope: Rope.ROPE] = {
ENABLE Feedback.Problem, Imager.Error => GOTO Abort;
constantColor: Imager.ConstantColor ← NARROW[color];
red, green, blue: REAL;
[red,green,blue] ← GGCoreOps.ExtractRGB[constantColor];
rope ← IO.PutFR["RGB: %1.3f %1.3f %1.3f", [real[red]], [real[green]], [real[blue]] ];
EXITS
Abort => rope ← "RGB: none";
};
ColorToCMYKRope: PUBLIC PROC [color: Imager.Color] RETURNS [rope: Rope.ROPE] = {
ENABLE Feedback.Problem, Imager.Error => GOTO Abort;
constantColor: Imager.ConstantColor ← NARROW[color];
cyan, magenta, yellow, black: REAL;
IF NOT ISTYPE[constantColor, ImagerColor.OpConstantColor] THEN GOTO Abort;
[cyan, magenta, yellow, black] ← GGColorOps.ExtractCMYK[constantColor];
rope ← IO.PutFR["CMYK: %1.3f %1.3f %1.3f %1.3f", [real[cyan]], [real[magenta]], [real[yellow]], [real[black]] ];
EXITS
Abort => rope ← "CMYK: none";
};
ColorToIntensityRope: PUBLIC PROC [color: Imager.Color] RETURNS [rope: Rope.ROPE] = {
ENABLE Feedback.Problem, Imager.Error => GOTO Abort;
constantColor: Imager.ConstantColor ← NARROW[color];
intensity: REAL ← GGColorOps.ExtractIntensity[constantColor];
rope ← IO.PutFR["Intensity: %1.3f", [real[intensity]] ];
EXITS
Abort => rope ← "Intensity: none";
};
ColorToNameRope: PUBLIC PROC [color: Imager.Color] RETURNS [rope: Rope.ROPE] = {
ENABLE Feedback.Problem, Imager.Error => GOTO Abort;
constantColor: Imager.ConstantColor ← NARROW[color];
red, green, blue: REAL;
[red,green,blue] ← GGCoreOps.ExtractRGB[constantColor];
rope ← NamedColors.HSLToRope[ImagerColorFns.HSLFromRGB[[red,green,blue]]];
EXITS
Abort => rope ← "none";
};
GGBuiltInShapes
PolygonInCircle: PUBLIC PROC [sideCount: NAT, origin: Point, radius: REAL, defaults: DefaultData] RETURNS [outline: Slice] = {
The bottom-most edge of the returned polygon will be horizontal. The polygon will be inscribed inside the circle with radius "radius" centered at "origin".
traj: Traj;
theta0, theta, deltaTheta, sin, cos: REAL;
lastPoint, thisPoint: Point;
success: BOOL;
seg: Segment;
IF sideCount = 0 THEN RETURN[NIL];
deltaTheta ← 360.0/sideCount;
theta0 ← -90.0 + deltaTheta/2.0;
sin ← RealFns.SinDeg[theta0];
cos ← RealFns.CosDeg[theta0];
thisPoint ← Vectors2d.Add[Vectors2d.Scale[[cos, sin], radius], origin];
traj ← GGTraj.CreateTraj[thisPoint];
lastPoint ← thisPoint;
FOR i: NAT IN [1..sideCount) DO
theta ← theta0 + i*deltaTheta;
sin ← RealFns.SinDeg[theta];
cos ← RealFns.CosDeg[theta];
thisPoint ← Vectors2d.Add[Vectors2d.Scale[[cos, sin], radius], origin];
seg ← GGSegment.MakeLine[lastPoint, thisPoint, NIL];
GGSegment.SetDefaults[seg, defaults];
success ← GGTraj.AddSegment[traj, hi, seg, lo];
IF NOT success THEN SIGNAL Problem[msg: "Couldn't add segment"];
lastPoint ← thisPoint;
ENDLOOP;
sin ← RealFns.SinDeg[theta0];
cos ← RealFns.CosDeg[theta0];
thisPoint ← Vectors2d.Add[Vectors2d.Scale[[cos, sin], radius], origin];
seg ← GGSegment.MakeLine[lastPoint, thisPoint, NIL];
GGSegment.SetDefaults[seg, defaults];
GGTraj.CloseWithSegment[traj, seg, lo];
outline ← GGOutline.CreateOutline[traj, ImagerColor.ColorFromGray[0.5]];
GGSliceOps.SetStrokeJoint[outline, GGSliceOps.NewParts[outline, NIL, topLevel].parts, defaults.strokeJoint, NIL];
GGSliceOps.SetFillColor[outline, NIL, defaults.fillColor, $Set, NIL];
};
Box: PUBLIC PROC [origin: Point, sideLength: REAL, defaults: DefaultData] RETURNS [slice: Slice] = {
halfSide: REAL ← sideLength/2.0;
box: GGBoundBox.BoundBox ← GGBoundBox.CreateBoundBox[origin.x-halfSide, origin.y-halfSide, origin.x+halfSide, origin.y+halfSide];
sliceD: SliceDescriptor ← GGSlice.MakeBoxSlice[box, ul, GGTransform.Identity[] ];
completeSliceD: SliceDescriptor ← GGSliceOps.NewParts[sliceD.slice, NIL, slice];
slice ← sliceD.slice;
GGSliceOps.SetDefaults[slice, completeSliceD.parts, defaults, NIL];
};
Circle: PUBLIC PROC [origin: Point, radius: REAL, defaults: DefaultData] RETURNS [slice: Slice] = {
outerPoint: Point ← [origin.x + radius, origin.y];
sliceD: SliceDescriptor ← GGSlice.MakeCircleSlice[origin, outerPoint];
completeSliceD: SliceDescriptor ← GGSliceOps.NewParts[sliceD.slice, NIL, slice];
slice ← sliceD.slice;
GGSliceOps.SetDefaults[slice, completeSliceD.parts, defaults, NIL];
};
KnotchedLine: PUBLIC PROC [p0, p1: Point, segmentCount: NAT] RETURNS [outline: Slice] = {
Returns a trajectory with "segmentCount" straight line segments, all of the same length. The entire trajectory is a single straight line, from point p0 to p1. All of its segments are collinear. Knotched lines are useful as rulers.
delta: Vector;
lastPoint, thisPoint: Point;
traj: Traj;
seg: Segment;
success: BOOL;
delta ← Vectors2d.Scale[Vectors2d.Sub[p1, p0], 1.0/segmentCount];
lastPoint ← p0;
traj ← GGTraj.CreateTraj[lastPoint];
THROUGH [1..segmentCount) DO
thisPoint ← Vectors2d.Add[lastPoint, delta];
seg ← GGSegment.MakeLine[lastPoint, thisPoint, NIL];
success ← GGTraj.AddSegment[traj, hi, seg, lo];
IF NOT success THEN SIGNAL Problem[msg: "Couldn't add segment"];
lastPoint ← thisPoint;
ENDLOOP;
thisPoint ← p1;
seg ← GGSegment.MakeLine[lastPoint, thisPoint, NIL];
success ← GGTraj.AddSegment[traj, hi, seg, lo];
IF NOT success THEN SIGNAL Problem[msg: "Couldn't add segment"];
outline ← GGOutline.CreateOutline[traj, ImagerColor.ColorFromGray[0.5]];
};
Text Descriptions of Simple Data Types
DescribeTraj: PUBLIC PROC [traj: Traj] RETURNS [text: Rope.ROPE] = {
trajData: TrajData ← NARROW[traj.data];
text ← SELECT trajData.role FROM
open => IO.PutFR["a %g-segment open trajectory", [integer[trajData.segCount]]],
fence => IO.PutFR["a %g-segment fence", [integer[trajData.segCount]]],
hole => IO.PutFR["a %g-segment hole", [integer[trajData.segCount]]],
ENDCASE => "";
};
DescribeJoint: PUBLIC PROC [traj: Traj, jointNum: NAT] RETURNS [text: Rope.ROPE] = {
trajData: TrajData ← NARROW[traj.data];
end: BOOL ← FALSE;
end ← GGTraj.IsEndJoint[traj, jointNum];
IF end THEN text ← IO.PutFR["end joint %g on ", [integer[jointNum]] ]
ELSE text ← IO.PutFR["joint %g on ", [integer[jointNum]] ];
text ← Rope.Concat[text, DescribeSegment[traj, IF GGTraj.HiJoint[traj]=jointNum AND trajData.role=open THEN jointNum-1 ELSE jointNum]];
Describes one of the possible two segments this joint is on
};
DescribeControlPoint: PUBLIC PROC [traj: Traj, segNum: NAT, cpNum: NAT] RETURNS [text: Rope.ROPE] = {
text ← Rope.Cat[IO.PutFR["control point %g on ", [integer[cpNum]]], DescribeSegment[traj, segNum] ];
};
DescribeSegment: PUBLIC PROC [traj: Traj, segNum: NAT] RETURNS [text: Rope.ROPE] = {
IF segNum=LAST[NAT] THEN text ← Rope.Cat["a segment on ", DescribeTraj[traj]] ELSE {
seg: Segment ← GGTraj.FetchSegment[traj, segNum];
text ← Rope.Cat[seg.class.describe[seg, TRUE, TRUE, TRUE, NIL], IO.PutFR[" segment %g on ", [integer[segNum]]], DescribeTraj[traj]];
};
};
DescribeInterior: PUBLIC PROC [traj: Traj] RETURNS [text: Rope.ROPE] = {
text ← Rope.Concat["The interior of ", DescribeTraj[traj]];
};
DescribeSequence: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [text: Rope.ROPE] = {
text ← Rope.Cat[IO.PutFR["one or more parts of "], DescribeTraj[sliceD.slice]];
};
DescribeColor: PUBLIC PROC [color: Imager.Color] RETURNS [rope: Rope.ROPE] = {
WITH color SELECT FROM
constantColor: ImagerColor.ConstantColor => {
op: ImagerColor.ColorOperator;
colorType: GGColorOps.ColorType;
[colorType, op] ← GGColorOps.GetColorType[color];
IF op#NIL THEN SELECT GGColorOps.GetOperatorType[op] FROM
grayLinear, grayDensity, grayVisual =>
rope ← ColorAsIntensityAndNameRope[color];
map, buildMap, rgbLinear, colorMap =>
rope ← ColorAsRGBAndNameRope[color];
cmyk => rope ← ColorAsCMYKAndNameRope[color];
ENDCASE => rope ← "an unknown constant color operator type"
ELSE SELECT colorType FROM
constantSpecial => {
special: ImagerColor.SpecialColor ← NARROW[constantColor];
rope ← SpecialColorAsRGBAndNameRope[special];
};
ENDCASE => rope ← "an unknown constant color type";
};
special: ImagerColor.SpecialColor => {
rope ← special.name
};
sampledBlack: ImagerColor.SampledBlack => {
rope ← "a sampled black";
};
sampledColor: ImagerColor.SampledColor => {
rope ← "a sampled color";
};
ENDCASE => {
rope ← "an unknown color type";
};
};
SpecialColorAsRGBAndNameRope: PROC [color: Imager.SpecialColor] RETURNS [rope: Rope.ROPE] = {
IF color#NIL THEN rope ← IO.PutFR["%g Name: %g %g %g",
[rope[IF color.substitute=NIL THEN "No equivalent RGB " ELSE ColorToRGBRope[color.substitute]]],
[rope[color.name]],
[rope[ColorToCMYKRope[color.substitute]]],
[rope[ColorToIntensityRope[color.substitute]]]
]
ELSE rope ← "None";
};
ColorAsRGBAndNameRope: PROC [color: Imager.Color] RETURNS [rope: Rope.ROPE] = {
cc: Imager.ConstantColor ← NARROW[color];
IF cc#NIL THEN rope ← IO.PutFR["%g CNS: %g %g %g",
[rope[ColorToRGBRope[cc]]],
[rope[ColorToNameRope[cc]]],
[rope[ColorToCMYKRope[cc]]],
[rope[ColorToIntensityRope[cc]]]
]
ELSE rope ← "None";
};
ColorAsCMYKAndNameRope: PROC [color: Imager.Color] RETURNS [rope: Rope.ROPE] = {
cc: Imager.ConstantColor ← NARROW[color];
IF cc#NIL THEN rope ← IO.PutFR["%g CNS: %g %g %g",
[rope[ColorToCMYKRope[cc]]],
[rope[ColorToNameRope[cc]]],
[rope[ColorToRGBRope[cc]]],
[rope[ColorToIntensityRope[cc]]]
]
ELSE rope ← "None";
};
ColorAsIntensityAndNameRope: PROC [color: Imager.Color] RETURNS [rope: Rope.ROPE] = {
cc: Imager.ConstantColor ← NARROW[color];
IF cc#NIL THEN rope ← IO.PutFR["%g CNS: %g %g %g",
[rope[ColorToIntensityRope[cc]]],
[rope[ColorToNameRope[cc]]],
[rope[ColorToRGBRope[cc]]],
[rope[ColorToCMYKRope[cc]]]
]
ELSE rope ← "None";
};
ScalarButtonValuesToRope: PUBLIC PROC [names: LIST OF Rope.ROPE, values: LIST OF REAL, on: LIST OF BOOL] RETURNS [rope: Rope.ROPE] = {
stream: IO.STREAM;
stream ← IO.ROS[];
GGParseOut.WriteScalarButtonValues[stream, names, values, on];
rope ← IO.RopeFromROS[stream];
};
ScalarButtonValuesFromRope: PUBLIC PROC [rope: Rope.ROPE] RETURNS [names: LIST OF Rope.ROPE, values: LIST OF REAL, on: LIST OF BOOL] = {
stream: IO.STREAM;
version: REAL ← GGUtility.version;
stream ← IO.RIS[rope];
[names, values, on] ← GGParseIn.ReadScalarButtonValues[stream, version];
};
FactoredTransformationToRope: PUBLIC PROC [transform: ImagerTransformation.Transformation] RETURNS [rope: Rope.ROPE] = {
f: IO.STREAMIO.ROS[];
GGParseOut.WriteFactoredTransformation[f, transform];
rope ← IO.RopeFromROS[f];
};
FactoredTransformationFromRope: PUBLIC PROC [rope: Rope.ROPE] RETURNS [transform: ImagerTransformation.Transformation] = {
f: IO.STREAMIO.RIS[rope];
transform ← GGParseIn.ReadFactoredTransformation[f];
};
GGCircleCache
alignmentColor: Imager.Color ← ImagerBackdoor.MakeStipple[145065B];
Create: PUBLIC PROC [] RETURNS [Cache] = {
RETURN [FunctionCache.GlobalCache[] ];
};
Insert: PUBLIC PROC [x: Cache, radius: REAL] = {
IF Lookup[x, radius]#NIL OR radius NOT IN [1.0..512.0] THEN RETURN
ELSE {
context: Imager.Context;
argument: REF REALNEW[REAL ← radius];
circle: GGBasicTypes.Circle;
dummySize: NAT ← 0;
side: INT ← 2*(Real.Fix[radius+1.0]+1); -- round up and add 1
bitmap: Bitmap ← ImagerSample.NewSampleMap[[max: [side, side]]];
cachedCircle: CachedCircle ← NEW[CachedCircleObj ← [bitmap: bitmap, cw: side, ch: side]];
context ← ImagerBitmapContext.Create[deviceSpaceSize: [side, side], scanMode: [down, right], surfaceUnitsPerInch: [72.0, 72.0], pixelUnits: TRUE];
ImagerBitmapContext.SetBitmap[context, bitmap];
context.SetColor[Imager.white];
context.MaskRectangle[ImagerBackdoor.GetBounds[context]];
context.SetColor[alignmentColor];
context.SetStrokeWidth[1.0];
circle ← GGCircles.CircleFromPointAndRadius[ [side/2, side/2], radius];
GGShapes.DrawCircle[dc: context, circle: circle ];
FunctionCache.Insert[x: x, argument: argument, value: cachedCircle, size: ImagerSample.WordsForMap[[side, side], 1] + 2, clientID: $GGCircle];
};
};
Lookup: PUBLIC PROC [x: Cache, radius: REAL] RETURNS [CachedCircle] = {
Returns NIL if lookup fails
FindRadius: FunctionCache.CompareProc = {
arg: REALNARROW[argument, REF REAL]^;
RETURN [ABS[arg-radius] < 1.0E-3];
};
value: Range;
ok: BOOL;
[value, ok] ← FunctionCache.Lookup[x: x, compare: FindRadius, clientID: $GGCircle];
RETURN [IF ok THEN NARROW[value] ELSE NIL];
};
Remove: PUBLIC PROC [x: Cache, radius: REAL] = {
FindRadius: FunctionCache.CompareProc = {
arg: REALNARROW[argument, REF REAL]^;
RETURN [ABS[arg- radius] < 1.0E-3];
};
[] ← FunctionCache.Obtain[x: x, compare: FindRadius, limit: 1, clientID: $GGCircle];
};
RemoveAll: PUBLIC PROC [x: Cache] = {
list: LIST OF FunctionCache.CacheEntry ← FunctionCache.Obtain[x: x, compare: FunctionCache.Any, limit: LAST[INT], clientID: $GGCircle];
UNTIL list=NIL DO
list ← FunctionCache.Obtain[x: x, compare: FunctionCache.Any, limit: LAST[INT], clientID: $GGCircle];
ENDLOOP;
};
DrawCachedCircle: PUBLIC PROC [context: Context, point: Point, circle: CachedCircle] = {
DoDrawCachedCircle: PROC = {
context.SetColor[Imager.black];
Imager.MaskBitmap[
context: context,
bitmap: circle.bitmap,
referencePoint: [circle.ch, 0],
position: [Real.Round[point.x]-(circle.cw/2),
Real.Round[point.y]-(circle.ch/2)]
];
};
Imager.DoSave[context, DoDrawCachedCircle];
};
Init: PROC [] ~ {
[] ← CodeTimer.CreateTable[$Gargoyle];
versionRope ← "8610.29";
versionRope ← "8612.04";
versionRope ← "8701.13";
versionRope ← "8701.135";
versionRope ← "8701.23";
versionRope ← "8701.26";
versionRope ← "8701.28";
versionRope ← "8701.30";
versionRope ← "8702.11";
versionRope ← "8702.26";
versionRope ← "8704.03";
versionRope ← "8705.14";
versionRope ← "8706.08";
versionRope ← "8706.16";
versionRope ← "8706.25";
versionRope ← "8708.21";
versionRope ← "8710.19";
versionRope ← "8802.04";
versionRope ← "8803.08";
versionRope ← "8803.24";
versionRope ← "8810.24";
versionRope ← "8811.30";
versionRope ← "8905.19";
versionRope ← "8906.16";
versionRope ← "9106.27"; -- black text should not have NIL fill color
version ← 9106.27;
};
versionRope: PUBLIC Rope.ROPE;
version: PUBLIC REAL;
Init[];
END.