-- color showobjects
-- mstone April 1, 1981 10:52 PM
-- geoff November 26, 1979 10:45 AM

DIRECTORY
ScreenDefs: FROM "ScreenDefs",
SplineDefs: FROM "SplineDefs",
StyleDefs: FROM "StyleDefs",
GriffinStartDefs: FROM "GriffinStartDefs",
GriffinMemoryDefs: FROM "GriffinMemoryDefs",
EncodingDefs: FROM "EncodingDefs",
GriffinDefs: FROM "GriffinDefs",
RefreshDefs: FROM "RefreshDefs",
PointDefs: FROM "PointDefs",
MenuDefs: FROM "MenuDefs",
MiscDefs: FROM "MiscDefs",
ControllerDefs: FROM "ControllerDefs",
InlineDefs: FROM "InlineDefs",
ObjectDefs: FROM "ObjectDefs";

ShowObjects: PROGRAM IMPORTS ScreenDefs, ObjectDefs, PointDefs, SplineDefs, MenuDefs, GriffinMemoryDefs, EncodingDefs, MiscDefs, ControllerDefs
EXPORTS ObjectDefs, GriffinStartDefs =
BEGIN OPEN ObjectDefs,PointDefs,GriffinMemoryDefs;

--upper level module has to start splines

i: INTEGER;
--the canonical loop counter
--put these somewhere as compile time constants
RuntimeBitmaps: ARRAY [0..3] OF ARRAY [0..8] OF CARDINAL;
TokenMap: ScreenDefs.Bitmap ← [0,1,9,9,NIL];
Token: ScreenDefs.Block ← [@TokenMap,0,0,9,9];
SelectBitmaps: ARRAY [0..3] OF ARRAY [0..15] OF CARDINAL;
SelectTokenMap: ScreenDefs.Bitmap ← [0,1,16,16,NIL];
SelectToken: ScreenDefs.Block ← [@SelectTokenMap,3,3,10,10];
selW: PUBLIC INTEGER ← 5;
HGrid,VGrid: ObjectHandle ← NIL;
hgridlx,hgridrx,vgridty,vgridby,gridt: INTEGER;
debug: BOOLEAN ← FALSE;
MoveTo: PROCEDURE[pt: PointDefs.ScrPt];
FNC: DisplayFnc ← write;
clipperState: ScreenDefs.ClipperState ← clip;

GridOn: PUBLIC PROCEDURE =
BEGIN
IF HGrid=NIL THEN
BEGIN
center,tl: ScrPt;
[center,hgridlx,hgridrx,vgridty,vgridby,gridt] ← ScreenDefs.GetGridParameters[];
VGrid ← StartObject[token];
HGrid ← StartObject[token];
tl ← [hgridlx,center[Y]-gridt/2];--doing the obvious makes a compiler error
HGrid.body ← token[hgrid,ScrToObj[tl],NIL];
tl ← [center[X]-gridt/2,vgridty];--doing the obvious makes a compiler error
VGrid.body ← token[vgrid,ScrToObj[tl],NIL];
HGrid.validEncoding ← VGrid.validEncoding ← FALSE;
HGrid.visible ← VGrid.visible ← FALSE;
END;
HGrid.visible ← TRUE;
WriteObject[HGrid];
VGrid.visible ← TRUE;
WriteObject[VGrid];
END;

GridOff: PUBLIC PROCEDURE =
BEGIN
EraseObject[HGrid];
HGrid.visible ← FALSE;
EraseObject[VGrid];
VGrid.visible ← FALSE;
END;


--replots all objects linked to allObjects
ReplotFromObject: PUBLIC PROCEDURE[startObj: ObjectHandle] =
BEGIN
obj: POINTER TO Object ← startObj;
FNC ← write;
UNTIL obj=NIL DO
PlotObject[obj];
obj ← obj.link;
ENDLOOP;
END;

--calls the correct type of plotting routine, be it splines, encoding, whatever
PlotObject: PROCEDURE [object: ObjectHandle] =
BEGIN
IF object=NIL THEN RETURN;
IF ~Visible[object] THEN RETURN;
IF object.validEncoding
THEN clipperState ← ScreenDefs.PrimeClipper[object.tl,object.br];
IF clipperState=cull THEN {clipperState ← clip; RETURN};
WITH obj: object SELECT FROM
shape => BEGIN
IF NOT object.validEncoding THEN BEGIN
EncodingDefs.DeleteChainEncoding[obj.chainEncoding];
EncodingDefs.DeleteAreaEncoding[obj.areaEncoding];
obj.chainEncoding ← NIL;
obj.areaEncoding ← NIL;
END;
--Curves
IF NOT obj.closed THEN BEGIN
IF object.cluster=OpenCluster AND obj.style.outlined=FALSE
THEN SetFastStyle[] --half finished linked obj
ELSE SetTrajectoryStyle[obj.style];
IF obj.chainEncoding=NIL THEN BEGIN
PlotTrajectory[@obj,FALSE];--displays and sets chain encoding
END
ELSE PlotChainEncoding[obj.chainEncoding];
RETURN;
END;
--Areas
IF obj.style.filled AND obj.closed THEN BEGIN
SetAreaStyle[obj.style];
IF obj.areaEncoding=NIL THEN FillArea[@obj]
ELSE PlotAreaEncoding[obj.areaEncoding];
END;
--Outlined Areas. TRUE = closed
IF obj.style.outlined THEN BEGIN
SetTrajectoryStyle[obj.style];
IF obj.chainEncoding=NIL THEN BEGIN
PlotTrajectory[@obj,TRUE];--displays and sets chain encoding
END
ELSE PlotChainEncoding[obj.chainEncoding];
END;
END;

caption => DisplayCaption[@obj];

menu => MenuDefs.PlotMenu[@obj];

token => SELECT obj.tokenType FROM
CP,open => DisplayCP[@obj];
selected => DisplaySelectToken[@obj];
vgrid,hgrid => DisplayGrid[@obj];
ENDCASE;
ENDCASE;
ScreenDefs.ClipOn[];
--clears PrimeClipper
clipperState ← clip;
END;

DisplayGrid: PROCEDURE[token: POINTER TO token Object]=
BEGIN
pt: ScrPt ← ObjToScr[token.p0];
IF token.tokenType=vgrid THEN BEGIN
ScreenDefs.BltVGrid[pt[X]] ;
token.tl ← [pt[X],vgridty];
token.br ← [pt[X]+gridt-1,vgridby];
END
ELSE BEGIN
ScreenDefs.BltHGrid[pt[Y]];
token.tl ← [hgridlx,pt[Y]];
token.br ← [hgridrx,pt[Y]+gridt-1];
END;
token.validEncoding ← TRUE;
END;

DisplayCP: PROCEDURE[token: POINTER TO token Object]=
BEGIN
p0: ScrPt ← ObjToScr[token.p0];
i: CARDINAL ← IF token.tokenType=CP THEN 0 ELSE 2;
p0[X] ← p0[X]-4;
p0[Y] ← p0[Y]-4;
IF ~token.validEncoding THEN BEGIN
token.tl ← p0;
token.br ← [p0[X]+8,p0[Y]+8];
token.validEncoding ← TRUE;
END;
TokenMap.bits ← BASE[RuntimeBitmaps[i+1]];
ScreenDefs.BLTBlockInScreen[@Token,p0,erase];
TokenMap.bits ← BASE[RuntimeBitmaps[i]];
ScreenDefs.BLTBlockInScreen[@Token,p0,paint];
END;

DisplaySelectToken: PROCEDURE [token: POINTER TO token Object] =
BEGIN
selectedObject: ObjectHandle ← token.selected;
coverObj: ObjectHandle ← NIL;
covered: BOOLEAN ← FALSE;
screen: ScreenDefs.ScreenPtr ← ScreenDefs.GetCurrentScreen[];
stl: ScrPt ← [screen.lx,screen.ty];
sbr: ScrPt ← [screen.rx,screen.by];
bm: CARDINAL;
IF selectedObject.validEncoding=FALSE THEN RETURN;
IF token.validEncoding = FALSE
THEN covered ← LocateSelectToken[token, selectedObject]
ELSE BEGIN
--is token covered?
coverObj ← GetObjectHandleBetweenObjects
[PointDefs.ObjToScr[token.p0], NIL, selectedObject] ;
--if not, is object covered at position of token?
IF coverObj=token THEN coverObj ←
GetObjectHandleBetweenObjects
[PointDefs.ObjToScr[token.p0],GetTopPictureObj[],selectedObject];
covered ← (coverObj#token AND coverObj#NIL);
END;

-- routine to relocate covered tokens... not used right now
-- IF covered THEN BEGIN
--
token.tl ← token.br ← [0,0];
-- so token doesn’t interfere with search
--
covered ← LocateSelectToken[token, selectedObject];
--
END;

IF covered THEN bm ← 1 ELSE bm ← 0;
IF selectedObject.cluster # 0 THEN bm ← bm + 2;
SelectTokenMap.bits ← BASE[SelectBitmaps[bm]];
ScreenDefs.ResetClipEdges[];
ScreenDefs.BLTBlockInScreen[@SelectToken,PointDefs.ObjToScr[token.p0],replace];
ScreenDefs.SetClipEdges[stl,sbr];
END;

LocateSelectToken: PROCEDURE [token: POINTER TO token Object,
object: ObjectHandle] RETURNS [covered: BOOLEAN] =
BEGIN
screenLoc: PointDefs.ScrPt;
covered ← FALSE;
WITH typedObj: object SELECT FROM
caption => screenLoc ← ObjToScr[typedObj.p0];
shape => BEGIN
-- put token over second encoding chunk (encoding1.p0) if
-- it exists and isnt covered or if encoding0.p0 is covered.
encoding0: POINTER TO EncodingDefs.ChainEncoding
← typedObj.chainEncoding;
encoding1: POINTER TO EncodingDefs.ChainEncoding
← encoding0.link;
IF encoding1 = NIL THEN
BEGIN
covered ← (GetObjectHandleBetweenObjects
[encoding0.p0, NIL,object] # NIL);
screenLoc ← encoding0.p0;
END
ELSE IF covered ← (GetObjectHandleBetweenObjects
[encoding1.p0, NIL,object] # NIL)
THEN IF covered ←
(GetObjectHandleBetweenObjects
[encoding0.p0, NIL,object] # NIL)
THEN screenLoc ← encoding1.p0
ELSE screenLoc ← encoding0.p0
ELSE screenLoc ← encoding1.p0;
END;
ENDCASE => MiscDefs.CallDebugger
["Selected object is not caption or shape (in LocateSelectToken)"];
screenLoc[X] ← screenLoc[X] - 5;
screenLoc[Y] ← screenLoc[Y] - 5;
token.p0 ← PointDefs.ScrToObj[screenLoc];
token.tl ← screenLoc;
token.br ← [screenLoc[X]+10,screenLoc[Y]+10];
token.validEncoding ← TRUE;
RETURN;
END;

SetChainEncoding: PROCEDURE[object: POINTER TO shape Object]=
BEGIN
tl,br: ScrPt;
num: CARDINAL ← 0;
encoding: POINTER TO EncodingDefs.ChainEncoding ← EncodingDefs.MakeChainEncoding[];
IF object.chainEncoding=NIL THEN object.chainEncoding ← encoding
ELSE BEGIN
ptr: POINTER TO EncodingDefs.ChainEncoding ← object.chainEncoding;
UNTIL ptr.link=NIL DO ptr ← ptr.link; num ← ptr.linknumber; ENDLOOP;
ptr.link ← encoding;
--mark encoding chunks here
num ← num+1;
FOR ptr ← encoding, ptr ← ptr.link UNTIL ptr=NIL DO
ptr.linknumber ← num;
ENDLOOP;
END;
[tl,br] ← GetMbb[object.chainEncoding, object.style.width];
object.tl ← tl;
object.br ← br;
object.validEncoding ← TRUE;
EncodingDefs.DeleteAreaEncoding[object.areaEncoding];
object.areaEncoding ← NIL;
END;

GetMbb: PROCEDURE[encoding: POINTER TO EncodingDefs.ChainEncoding,width: REAL] RETURNS[tl,br: ScrPt]=
BEGIN
halfwidth: INTEGER ← (ObjValToScrVal[width]+1)/2;
ptr: POINTER TO EncodingDefs.ChainEncoding ← encoding;
IF ptr=NIL THEN RETURN[[0,0],[0,0]];
tl ← ptr.tl;
br ← ptr.br;
UNTIL ptr=NIL DO
IF ptr.tl[X] < tl[X] THEN tl[X] ← ptr.tl[X];
IF ptr.tl[Y] < tl[Y] THEN tl[Y] ← ptr.tl[Y];
IF ptr.br[X] > br[X] THEN br[X] ← ptr.br[X];
IF ptr.br[Y] > br[Y] THEN br[Y] ← ptr.br[Y];
ptr ← ptr.link;
ENDLOOP;
tl[X] ← tl[X]-halfwidth; tl[Y] ←tl[Y]-halfwidth;
br[X] ← br[X]+halfwidth; br[Y] ←br[Y]+halfwidth;
END;

PlotChainEncoding: PROCEDURE[encoding: POINTER TO EncodingDefs.ChainEncoding]=
BEGIN OPEN encoding;
cState: ScreenDefs.ClipperState ← clip;
IF encoding=NIL THEN RETURN;
UNTIL encoding=NIL DO
IF clipperState#inside THEN cState ← ScreenDefs.PrimeClipper[tl,br];
IF cState#cull
THEN EncodingDefs.PlotChainChunk[encoding,ScreenDefs.DrawSingleDot];
encoding ← encoding.link;
ENDLOOP;
--ScreenDefs.EndLine[];
ScreenDefs.ClipOn[];
END;

PlotAreaEncoding: PROCEDURE[encoding: POINTER TO EncodingDefs.AreaEncoding]=
BEGIN OPEN encoding;
cState: ScreenDefs.ClipperState ← clip;
IF encoding=NIL THEN RETURN;
UNTIL encoding=NIL DO
IF clipperState#inside THEN cState ← ScreenDefs.PrimeClipper[tl,br];
IF cState#cull
THEN EncodingDefs.PlotAreaChunk[encoding,ScreenDefs.HFill];
encoding ← encoding.link;
ENDLOOP;
ScreenDefs.ClipOn[];
END;

SetAreaStyle: PROCEDURE [style: POINTER TO StyleDefs.Style] =
BEGIN OPEN ScreenDefs;
IF FNC=write THEN ScreenDefs.SetFunction[replace] ELSE SetFunction[erase];
ScreenDefs.SetFillParms[ControllerDefs.GreyOfColor[style.fillcolor], ControllerDefs.GetColorMapIndex[style.fillcolor]];
END;

SetTrajectoryStyle: PROCEDURE [style: POINTER TO StyleDefs.Style] =
BEGIN OPEN ScreenDefs;
jnc: JunctionType ← SELECT style.junctiontype FROM
round => round,
square => square,
angled => angled,
ENDCASE => round;

IF FNC=write THEN SetFunction[replace] ELSE SetFunction[erase];

SetLineParms[jnc,MAX[ObjValToScrVal[style.width],1],ControllerDefs.GreyOfColor[style.color],ControllerDefs.GetColorMapIndex[style.color],ConvertEnd[style.firstend],ConvertEnd[style.lastend]];
END;

SetFastStyle: PROCEDURE =
BEGIN OPEN ScreenDefs;
IF FNC=write THEN SetFunction[paint] ELSE SetFunction[erase];
--set it to plot oneline wide grey. grey is arbitray pick here
SetLineParms[round,1,ControllerDefs.GreyOfColor[[0,255,255]],ControllerDefs.GetColorMapIndex[[0,255,255]],[round[]],[round[]]];
END;

ConvertEnd: PROCEDURE [end: StyleDefs.LineEnd] RETURNS[ScreenDefs.EndType]=
BEGIN
RETURN[[round[]]];
END;

--Draws a trajectory
PlotTrajectory: PROCEDURE [shape: POINTER TO shape Object,closed: BOOLEAN] =
BEGIN
traj: Trajectory ← shape.trajectory;
MoveTo ← StartLine;
WITH thistraj: traj SELECT FROM
linked => BEGIN
linkPtr: POINTER TO Link ← thistraj.links;
UNTIL linkPtr = NIL DO
PlotLink[linkPtr,traj.splineType];
linkPtr ← linkPtr.link;
SetChainEncoding[shape];--make it a link at a time
ENDLOOP;
END;
cyclic => BEGIN
PlotCurve[thistraj.knots,traj.splineType,TRUE];
SetChainEncoding[shape];
END;
ENDCASE;
--everybody needs an endline. rework for different end conditions
ScreenDefs.EndLine[];

END;

PlotLink: PROCEDURE[link: POINTER TO Link,spline: SplineDefs.SplineType]=
BEGIN
IF LENGTH[link.knots]<=0 THEN RETURN;
--debug
IF debug THEN BEGIN
length: INTEGER ← LENGTH[link.knots];
knots: DESCRIPTOR FOR ARRAY OF ScrPt
← DESCRIPTOR[Allocate[length*SIZE[ObjPt]],length];
FOR i IN [0..length) DO
knots[i] ← ObjToScr[link.knots[i]];
ENDLOOP;
Free[BASE[knots]];
END;
SELECT link.degree FROM
D0 => PlotDots[link.knots];
D1 => PlotLines[link.knots];
D2 => NULL;
D3 => PlotCurve[link.knots,spline,FALSE];
ENDCASE;
END;

--Plots Dots
PlotDots: PROCEDURE [knots: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt] =
BEGIN
i: INTEGER;
FOR i IN [0..LENGTH[knots]) DO
ScreenDefs.DrawSingleDot[ObjToScr[knots[i]]];
ENDLOOP;
END;

--Plots Lines.
PlotLines: PROCEDURE [knots: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt] =
BEGIN
i: INTEGER;
lastknot: INTEGER ← LENGTH[knots]-1;
MoveTo[ObjToScr[knots[0]]];
FOR i IN [1..lastknot] DO
ScreenDefs.DrawTo[ObjToScr[knots[i]]];
EncodingDefs.AddChainLine[ObjToScr[knots[i]]];
ENDLOOP;
END;

--Plots a curve. description is in the knots and the splinetype
PlotCurve: PROCEDURE [knots: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt,type: SplineDefs.SplineType,cyclic: BOOLEAN] =
BEGIN --makes an array. Don’t forget to deallocate it
newknots: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt ← XFormKnots[knots];
coeffs: DESCRIPTOR FOR ARRAY OF SplineDefs.Coeffs;
coeffs ← IF cyclic THEN MakeCyclicSpline[newknots,type]
ELSE SplineDefs.MakeSpline[newknots,type ! SplineDefs.TooFewKnots => BEGIN
Free[BASE[newknots]]; CONTINUE; END
];
FOR i IN [0..LENGTH[coeffs]) DO
SplineDefs.DisplayCubic[@coeffs[i],MoveTo,DrawToShort];
ENDLOOP;
Free[BASE[newknots]];
--free the base
SplineDefs.FreeCoeffs[coeffs];
END;

MoveToNull: PROCEDURE[pt:PointDefs.ScrPt]=
BEGIN
END;

StartLine: PROCEDURE[pt:PointDefs.ScrPt]=
BEGIN
ScreenDefs.StartLine[pt];
EncodingDefs.AddChainPoint[pt];
END;

DrawToShort: PROCEDURE[pt:PointDefs.ScrPt]=
BEGIN
ScreenDefs.DrawToShort[pt];

EncodingDefs.AddChainLine[pt];
END;

XFormKnots: PROCEDURE[knots: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt ] RETURNS[ newknots: DESCRIPTOR FOR ARRAY OF PointDefs.ObjPt]=
BEGIN
length: INTEGER ← LENGTH[knots];
newknots ← DESCRIPTOR[Allocate[length*SIZE[PointDefs.ObjPt]],length];
FOR i IN [0..length) DO
newknots[i] ← PointDefs.ObjToScrReal[knots[i]];
ENDLOOP;
END;

MakeCyclicSpline: PROCEDURE[knots: DESCRIPTOR FOR ARRAY OF ObjPt, type: SplineDefs.SplineType] RETURNS[coeffs: DESCRIPTOR FOR ARRAY OF SplineDefs.Coeffs]=
BEGIN
cycknots: DESCRIPTOR FOR ARRAY OF ObjPt;
numknots: INTEGER ← LENGTH[knots];
newLength: INTEGER;
IF numknots <= 0 THEN RETURN[DESCRIPTOR[NIL,0]];
SELECT type FROM
naturalUM, naturalAL,bezier=> newLength ← numknots+1;
bspline, crspline=> newLength ← numknots+3;
ENDCASE;
cycknots ← DESCRIPTOR[Allocate[newLength*SIZE[ObjPt]],newLength];
FOR i IN [0..numknots) DO
cycknots[i] ← knots[i];
ENDLOOP;
cycknots[numknots] ← cycknots[0];
SELECT type FROM
naturalUM=> type ← cyclicUM;
naturalAL=> type ← cyclicAL;
bspline=> BEGIN
cycknots[numknots +1] ← cycknots[1];
cycknots[numknots+2] ← cycknots[2];
END;
crspline=> BEGIN
cycknots[numknots +1] ← cycknots[1];
cycknots[numknots+2] ← cycknots[2];
END;
ENDCASE;
coeffs ← SplineDefs.MakeSpline[cycknots,type];
Free[BASE[cycknots]];
RETURN;
END;

--makes a new linked trajectory with knuth splines in gaps
FillGaps: PROCEDURE[traj: POINTER TO linked Trajectory,closed: BOOLEAN] RETURNS[POINTER TO linked Trajectory] =
BEGIN
RETURN[traj];
END;

--Fills an area
FillArea: PROCEDURE [area: POINTER TO shape Object] =
BEGIN
--need chain encoding to make area encoding
IF area.chainEncoding=NIL THEN BEGIN
SetFastStyle;
PlotTrajectory[area,TRUE];
--object doesn’t have an outline (happens as it’s being drawn or modified)
IF area.chainEncoding=NIL THEN RETURN;
END;
SetAreaStyle[area.style];
area.areaEncoding ← EncodingDefs.MakeAreaEncoding[area.chainEncoding];
area.validEncoding ← TRUE;
END;

--writes the object. Screen function set to replace or paint depending on type of obj
PlotOneObject: PUBLIC PROCEDURE [object: ObjectHandle,function: DisplayFnc] =
BEGIN
FNC ← function;
PlotObject[object];
END;


WriteObject: PUBLIC PROCEDURE [object: ObjectHandle] =
BEGIN
tl,br: ScrPt;
FNC ← write;
IF object=NIL THEN RETURN;
IF NOT object.validEncoding THEN BEGIN
PlotOneObject[object,write];
object ← object.link;--now clean up above it
IF object=NIL THEN RETURN;
END;
--a little slop to catch the select token
tl ← object.tl;
br ← object.br;
IF object.objectType#token AND object.objectType#menu THEN BEGIN
tl ← [tl[X]-selW,tl[Y]-selW];
br ← [br[X]+selW,br[Y]+selW];
END;
ReplotBoxFromObject[tl, br, object];
END;

ReplotBoxFromObject: PUBLIC PROCEDURE[tl,br: ScrPt, object: ObjectHandle]=
BEGIN
FNC ← write;
ScreenDefs.SetClipEdges[tl,br];
ReplotFromObject[object];
ScreenDefs.ResetClipEdges[];
END;

ReplotBox: PUBLIC PROCEDURE[tl,br: ScrPt]=
BEGIN
FNC ← write;
ScreenDefs.SetClipEdges[tl,br];
ReplotAllObjects[];
--reset them
ScreenDefs.ResetClipEdges[];
END;

EraseObject: PUBLIC PROCEDURE [object: ObjectHandle] =
BEGIN
Screen: ObjectProc = BEGIN
IF obj#object THEN PlotObject[obj];
END;
tl,br: ScrPt;
erased: BOOLEAN ← FALSE;
IF object=NIL THEN RETURN;
IF NOT object.validEncoding THEN BEGIN
PlotOneObject[object,erase];
erased ← TRUE;
END;
--a little slop to catch the select token
tl ← object.tl;
br ← object.br;
IF object.objectType#token AND object.objectType#menu THEN BEGIN
tl ← [tl[X]-selW,tl[Y]-selW];
br ← [br[X]+selW,br[Y]+selW];
END;
ScreenDefs.SetClipEdges[tl, br];
IF NOT erased THEN ScreenDefs.ClearScreen[];
FNC ← write;
ForAllObjects[Screen];
--reset them
ScreenDefs.ResetClipEdges[];
END;

WriteLink: PUBLIC PROCEDURE[obj: POINTER TO shape Object,link: POINTER TO Link]=
BEGIN
FNC ← write;
--first link
WITH traj: obj.trajectory SELECT FROM
linked => IF traj.links=link THEN BEGIN
EncodingDefs.DeleteChainEncoding[obj.chainEncoding];
EncodingDefs.DeleteAreaEncoding[obj.areaEncoding];
obj.chainEncoding ← NIL;
obj.areaEncoding ← NIL;
END;
ENDCASE => ERROR;
IF obj.style.outlined THEN SetTrajectoryStyle[obj.style] ELSE SetFastStyle;
MoveTo ← StartLine;
PlotLink[link,obj.trajectory.splineType];
ScreenDefs.EndLine[];
SetChainEncoding[obj];
END;

--no EraseLink. It’s all in DeleteLink for now

RuntimeBitmaps[0] ←
[16000B,12000B,12000B,173600B,104200B,173600B,12000B,12000B,16000B]; --CP
RuntimeBitmaps[1] ←
[16000B,16000B,16000B,177600B,177600B,177600B,16000B,16000B,16000B]; --erase cp
RuntimeBitmaps[
2] ← [37000B,61400B,140600B,100200B,104200B,100200B,140600B,61400B,37000B]; --open
RuntimeBitmaps[3] ← [37000B,77400B,177600B,177600B,177600B,177600B,177600B,77400B,37000B]; --erase open
SelectBitmaps[0] ← [0,0,0,17770B,17770B,14030B,14030B,14630B,14630B,14030B,
14030B,17770B,17770B,0,0,0]; --selected not hidden.
SelectBitmaps[1] ← [0,0,0,17770B,17770B,17030B,17430B,15630B,14730B,14370B,
14170B,17770B,17770B,0,0,0]; --selected and hidden.
SelectBitmaps[2] ← [0,0,0,17770B,17770B,14030B,15730B,15030B,15030B,15730B,
14030B,17770B,17770B,0,0,0]; --selected not hidden cluster.
SelectBitmaps[3] ← [0,0,0,17770B,17770B,14070B,15730B,15230B,15430B,15730B,
16030B,17770B,17770B,0,0,0]; --selected and hidden cluster.

--block for local variables
END.