-- Compiler ShowObjects/n;bind griffin;griffin
-- mstone April 24, 1981 3:10 PM
-- geoff November 26, 1979 10:45 AM
-- Last Edited by: Stone, February 18, 1983 1:03 pm
-- Last Edited by: Pier, February 14, 1984 10:52:01 am PST

DIRECTORY
 ScreenDefs,
 GriffinViewer USING [PaintProc, DoPaint],
 SplineDefs: FROM "SplineDefs",
 StyleDefs: FROM "StyleDefs",
 GriffinMemoryDefs USING [CZone],
 Graphics USING [Context],
 EncodingDefs: FROM "EncodingDefs",
 GriffinDefs: FROM "GriffinDefs",
 PointDefs: FROM "PointDefs",
 MenuDefs: FROM "MenuDefs",
 DebuggerSwap: FROM "DebuggerSwap",
 ControllerDefs: FROM "ControllerDefs",
 ObjectDefs: FROM "ObjectDefs";

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

--upper level module has to start splines
Context: TYPE=Graphics.Context;
i: INTEGER; --the canonical loop counter
--put these somewhere as compile time constants
RuntimeBitmaps: ARRAY [0..3] OF ARRAY [0..8] OF CARDINAL;
Token: ScreenDefs.BlockPtr ← CZone.NEW[ScreenDefs.Block ← [NIL,1,0,0,9,9]];
SelectBitmaps: ARRAY [0..3] OF ARRAY [0..15] OF CARDINAL;
SelectToken: ScreenDefs.BlockPtr ← CZone.NEW[ScreenDefs.Block ← [NIL,1,3,3,10,10]];
selW: PUBLIC INTEGER ← 5;
HGrid,VGrid: ObjectHandle ← NIL;
hgridlx,hgridrx,vgridty,vgridby,gridt: INTEGER;
debug: BOOLEANFALSE;
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, dc: Context] =
BEGIN
obj: REF Object ← startObj;
FNC ← write;
UNTIL obj=NIL DO
 PlotObject[obj, dc];
 obj ← obj.link;
ENDLOOP;
END;

--calls the correct type of plotting routine, be it splines, encoding, whatever
PlotObject: PROCEDURE [object: ObjectHandle, dc: Context] =
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 object SELECT FROM
 obj: REF Object[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 EncodeTrajectory[obj,FALSE]; --sets chain encoding
   PlotChainEncoding[obj.chainEncoding, dc];
   RETURN;
   END;
--Areas
  IF obj.style.filled AND obj.closed THEN BEGIN
   SetAreaStyle[obj.style];
   IF obj.areaEncoding=NIL THEN EncodeArea[obj];
   PlotAreaEncoding[obj.areaEncoding, dc];
   END;
--Outlined Areas. TRUE = closed
  IF obj.style.outlined THEN BEGIN
   SetTrajectoryStyle[obj.style];
   IF obj.chainEncoding=NIL THEN EncodeTrajectory[obj,TRUE]; --sets chain encoding
   PlotChainEncoding[obj.chainEncoding, dc];
   END;
  END;

 obj: REF Object[caption] => DisplayCaption[obj, dc];

 obj: REF Object[menu] => MenuDefs.PlotMenu[obj, dc];

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

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

DisplayCP: PROCEDURE[token: REF token Object, dc: Context]=
BEGIN
i: CARDINALIF token.tokenType=CP THEN 0 ELSE 2;
IF ~token.validEncoding THEN BEGIN
 token.tl ← ObjToScr[token.p0];
 token.br ← [token.tl[X]+8,token.tl[Y]+8];
 token.validEncoding ← TRUE;
END;
--Token.base ← BASE[RuntimeBitmaps[i+1]];
--ScreenDefs.BLTBlockInScreen[Token,p0,erase];
ScreenDefs.EraseBox[token.tl,token.br, dc];
Token.base ← BASE[RuntimeBitmaps[i]];
ScreenDefs.BLTBlockInScreen[Token,token.tl,transparent, dc];
END;

DisplaySelectToken: PROCEDURE [token: REF token Object, dc: Context] =
BEGIN
selectedObject: ObjectHandle ← token.isSelected;
coverObj: ObjectHandle ← NIL;
covered: BOOLEANFALSE;
--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;
SelectToken.base ← BASE[SelectBitmaps[bm]];
--ScreenDefs.ResetClipEdges[];
ScreenDefs.BLTBlockInScreen[SelectToken,token.tl,opaque, dc];
--ScreenDefs.SetClipEdges[stl,sbr];
END;

LocateSelectToken: PROCEDURE [token: REF 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: REF EncodingDefs.ChainEncoding
   ← typedObj.chainEncoding;
  encoding1: REF 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 => DebuggerSwap.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: REF shape Object]=
BEGIN
tl,br: ScrPt;
num: CARDINAL ← 0;
encoding: REF EncodingDefs.ChainEncoding ← EncodingDefs.MakeChainEncoding[];
IF object.chainEncoding=NIL THEN object.chainEncoding ← encoding
ELSE BEGIN
 ptr: REF 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: REF EncodingDefs.ChainEncoding,width: REAL] RETURNS[tl,br: ScrPt]=
BEGIN
halfwidth: INTEGER ← (ObjValToScrVal[width]+1)/2;
ptr: REF 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] 𡤋r[Y]+halfwidth;
END;

PlotChainEncoding: PROCEDURE[encoding: REF EncodingDefs.ChainEncoding, dc: Context]=
BEGIN OPEN encoding;
cState: ScreenDefs.ClipperState ← clip;
paint: GriffinViewer.PaintProc = {
 UNTIL
encoding=NIL DO
  cState ← ScreenDefs.PrimeClipper[tl,br];
  IF cState#cull THEN EncodingDefs.PlotChainChunk[encoding, dc];
  encoding ← encoding.link;
  ENDLOOP;
  };
IF encoding=NIL THEN RETURN;
IF dc=NIL THEN GriffinViewer.DoPaint[paint] ELSE paint[dc];
ScreenDefs.ClipOn[];
END;

PlotAreaEncoding: PROCEDURE[encoding: REF EncodingDefs.AreaEncoding, dc: Context]=
BEGIN OPEN encoding;
cState: ScreenDefs.ClipperState ← clip;
paint: GriffinViewer.PaintProc = {
 UNTIL
encoding=NIL DO
  cState ← ScreenDefs.PrimeClipper[tl,br];
  IF cState#cull THEN EncodingDefs.PlotAreaChunk[encoding,dc];
  encoding ← encoding.link;
  ENDLOOP;
  };
IF encoding=NIL THEN RETURN;
IF dc=NIL THEN GriffinViewer.DoPaint[paint] ELSE paint[dc];
ScreenDefs.ClipOn[];
END;

SetAreaStyle: PROCEDURE [style: REF StyleDefs.Style] =
BEGIN OPEN ScreenDefs;
IF FNC=write THEN ScreenDefs.SetFunction[opaque] ELSE DebuggerSwap.CallDebugger["erase case"];
ScreenDefs.SetFillParms[ControllerDefs.GreyOfColor[style.fillcolor]];
END;

SetTrajectoryStyle: PROCEDURE [style: REF StyleDefs.Style] =
BEGIN
IF FNC=write THEN ScreenDefs.SetFunction[opaque];
ScreenDefs.SetLineParms[
 MAX
[ObjValToScrVal[style.width],1],ControllerDefs.GreyOfColor[style.color]];
END;

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

--Draws a trajectory
EncodeTrajectory: PROCEDURE [shape: REF shape Object,closed: BOOLEAN] =
BEGIN
traj: REF Trajectory ← shape.trajectory;
WITH traj SELECT FROM
 thistraj: REF Trajectory[linked] => BEGIN
  linkPtr: REF Link ← thistraj.links;
  UNTIL linkPtr = NIL DO
   EncodeLink[linkPtr,traj.splineType];
   linkPtr ← linkPtr.link;
   SetChainEncoding[shape]; --make it a link at a time
   ENDLOOP;
  END;
 thistraj: REF Trajectory[cyclic] => BEGIN
  EncodeCurve[thistraj.knots,traj.splineType,TRUE];
  SetChainEncoding[shape];
  END;
ENDCASE;
END;

EncodeLink: PROCEDURE[link: REF Link,spline: SplineDefs.SplineType]=
BEGIN
IF link.knots=NIL OR link.knots.length <=0 THEN RETURN;
SELECT link.degree FROM
 D0 => NULL;
 D1 => EncodeLines[link.knots];
 D2 => NULL;
 D3 => EncodeCurve[link.knots,spline,FALSE];
ENDCASE;
END;

--Encode Lines.
EncodeLines: PROCEDURE [knots: PointDefs.ObjPtSequence] =
BEGIN
i: INTEGER;
lastknot: INTEGER ← knots.length-1;
EncodingDefs.AddChainPoint[ObjToScr[knots[0]]];
FOR i IN [1..lastknot] DO
 EncodingDefs.AddChainLine[ObjToScr[knots[i]]];
ENDLOOP;
END;

--Encodes a curve. description is in the knots and the splinetype
EncodeCurve: PROCEDURE [knots: PointDefs.ObjPtSequence,type: SplineDefs.SplineType,cyclic: BOOLEAN] =
BEGIN --makes an array. Don't forget to deallocate it
newknots: SplineDefs.KnotSequence ← XFormKnots[knots];
coeffs: SplineDefs.CoeffsSequence;
coeffs ← IF cyclic THEN MakeCyclicSpline[newknots,type]
ELSE SplineDefs.MakeSpline[newknots,type];
FOR i IN [0..coeffs.length) DO
 SplineDefs.DisplayCubic[coeffs[i],EncodingDefs.AddChainPoint,EncodingDefs.AddChainLine];
ENDLOOP;
END;

XFormKnots: PROCEDURE[knots: PointDefs.ObjPtSequence ] RETURNS[ newknots: SplineDefs.KnotSequence]=
BEGIN
length: INTEGER ← knots.length;
newknots ← CZone.NEW[SplineDefs.KnotSequenceRec[length]];
FOR i IN [0..length) DO
 newknots[i] ← PointDefs.ObjToScrReal[knots[i]];
ENDLOOP;
END;

MakeCyclicSpline: PROCEDURE[knots: SplineDefs.KnotSequence, type: SplineDefs.SplineType] RETURNS[coeffs: SplineDefs.CoeffsSequence]=
BEGIN
cycknots: SplineDefs.KnotSequence;
numknots: INTEGER ← (IF knots=NIL THEN 0 ELSE knots.length);
newLength: INTEGER;
IF numknots <= 0 THEN RETURN[NIL];
SELECT type FROM
 naturalUM, naturalAL,bezier=> newLength ← numknots+1;
 bspline, crspline=> newLength ← numknots+3;
ENDCASE;
cycknots ← CZone.NEW[SplineDefs.KnotSequenceRec[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];
RETURN;
END;

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

--Fills an area
EncodeArea: PROCEDURE [area: REF shape Object] =
BEGIN
--need chain encoding to make area encoding
IF area.chainEncoding=NIL THEN BEGIN
 SetFastStyle;
 EncodeTrajectory[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, NIL];
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, NIL];
END;

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

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

EraseObject: PUBLIC PROCEDURE [object: ObjectHandle] =
BEGIN
Screen: ObjectProc = BEGIN
IF obj#object THEN PlotObject[obj, NIL];
END;
tl,br: ScrPt;
erased: BOOLEANFALSE;
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[NIL];
FNC ← write;
ForAllObjects[Screen];
--reset them
ScreenDefs.ResetClipEdges[];
END;

WriteLink: PUBLIC PROCEDURE[obj: REF shape Object,link: REF Link]=
BEGIN
paint: GriffinViewer.PaintProc = {PlotChainEncoding[obj.chainEncoding, dc]};
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;
EncodeLink[link,obj.trajectory.splineType];
SetChainEncoding[obj];
GriffinViewer.DoPaint[paint];
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.