File: TFI3dImpl.mesa (Text File-In for 3d scenes)
Last edited by Bier on March 1, 1987 6:08:51 pm PST
Copyright © 1984 by Xerox Corporation. All rights reserved.
Contents: convenience functions for parsing low-level parts of a 3d fileout produced by fileout3d.mesa
DIRECTORY
AtomButtonsTypes, SVGraphics, Feedback, Imager, ImagerColor, IO, Matrix3d, Rope, SV2d, SV3d, SVArtwork, SVMappings, SVMatrix2d, SVModelTypes, SVPolygon3d, SVScene, SVSceneTypes, TFI3d, ViewerClasses;
TFI3dImpl: CEDAR PROGRAM
IMPORTS SVGraphics, ImagerColor, IO, Matrix3d, SVArtwork, Feedback, SVMappings, SVMatrix2d, SVPolygon3d, SVScene, Rope
EXPORTS TFI3d =
BEGIN
Slice: TYPE = SVSceneTypes.Slice;
Color: TYPE = Imager.Color;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
DrawStyle: TYPE = SVModelTypes.DrawStyle;
FeedbackData: TYPE = AtomButtonsTypes.FeedbackData;
FileCamera: TYPE = SVSceneTypes.FileCamera;
FrameBox: TYPE = REF FrameBoxObj;
FrameBoxObj: TYPE = SVModelTypes.FrameBoxObj;
Matrix3by3: TYPE = SV2d.Matrix3by3;
Matrix4by4: TYPE = SV3d.Matrix4by4;
OMap: TYPE = SVModelTypes.OMap;
Plane: TYPE = SV3d.Plane;
Point3d: TYPE = SV3d.Point3d;
Point2d: TYPE = SV2d.Point2d;
Projection: TYPE = SVModelTypes.Projection;
Scene: TYPE = SVSceneTypes.Scene;
Vector3d: TYPE = SV3d.Vector3d;
Viewer: TYPE = ViewerClasses.Viewer;
Material: TYPE = SVModelTypes.Material;
SMap: TYPE = SVModelTypes.SMap;
CharClass: TYPE = IO.CharClass;
{break, sepr, other}
FileinMatrix: PUBLIC PROC [f: IO.STREAM] RETURNS [mat: Matrix4by4] = {
mat ← Matrix3d.Identity[];
ReadRope[f, "["];
ReadBlankAndRope[f, "["];
mat[1][1] ← ReadBlankAndReal[f];
FOR j: NAT IN[2..4] DO
ReadBlankAndRope[f,","];
mat[1][j] ← ReadBlankAndReal[f];
ENDLOOP;
ReadBlank[f];
ReadBlankAndRope[f, "]"];
FOR i: NAT IN[2..3] DO
ReadBlankAndRope[f, ","];
ReadBlankAndRope[f, "["];
mat[i][1] ← ReadBlankAndReal[f];
FOR j: NAT IN[2..4] DO
ReadBlankAndRope[f, ","];
mat[i][j] ← ReadBlankAndReal[f];
ENDLOOP;
ReadBlankAndRope[f, "]"];
ENDLOOP;
ReadBlankAndRope[f, "]"];
}; -- end of FileinMatrix
FileinMatrix3by3: PUBLIC PROC [f: IO.STREAM] RETURNS [mat: Matrix3by3] = {
mat ← SVMatrix2d.Identity[];
ReadRope[f, "["];
ReadBlankAndRope[f, "["];
mat[1][1] ← ReadBlankAndReal[f];
FOR j: NAT IN[2..3] DO
ReadBlankAndRope[f,","];
mat[1][j] ← ReadBlankAndReal[f];
ENDLOOP;
ReadBlank[f];
ReadBlankAndRope[f, "]"];
ReadBlankAndRope[f, ","];
ReadBlankAndRope[f, "["];
mat[2][1] ← ReadBlankAndReal[f];
FOR j: NAT IN[2..3] DO
ReadBlankAndRope[f, ","];
mat[2][j] ← ReadBlankAndReal[f];
ENDLOOP;
ReadBlankAndRope[f, "]"];
ReadBlankAndRope[f, "]"];
}; -- end of FileinMatrix3by3
ReadRope: PUBLIC PROC [f: IO.STREAM, rope: Rope.ROPE] = {
Removes the given rope from the top of the stream. Used to remove formatting words and phrases from 3d files. We are not interested in these strings but only in the data in between them.
Signals RopeNotOnTop if some other rope is on top.
c: CHAR;
endofstream: BOOLFALSE;
FOR i: INT IN[1..Rope.Length[rope]] DO
c ← IO.GetChar[f
! IO.EndOfStream => {endofstream ← TRUE; CONTINUE}];
IF endofstream THEN
SIGNAL RopeNotOnTop [IO.GetIndex[f], NIL, rope];
IF NOT c = Rope.Fetch[rope,i-1] THEN
SIGNAL RopeNotOnTop [IO.GetIndex[f], Rope.FromChar[c], rope];
ENDLOOP;
};
RopeNotOnTop: PUBLIC SIGNAL [position: NAT, wasThere: Rope.ROPE, notThere: Rope.ROPE] = CODE;
ReadChar: PUBLIC PROC [f: IO.STREAM, c: CHAR] = {
streamC: CHARIO.GetChar[f];
IF NOT c = streamC THEN SIGNAL RopeNotOnTop[IO.GetIndex[f], Rope.FromChar[streamC], Rope.FromChar[c]];
};
ReadReturn: PUBLIC PROC [f: IO.STREAM] = {
Convenience function. Equivalent to ReadChar[f, IO.CR];
ReadChar[f, IO.CR];
};
WordBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = CHECKED {
SELECT char FROM
IO.TAB => RETURN [break];
IO.CR =>RETURN [break];
IO.SP => RETURN [break];
', => RETURN [break];
'] => RETURN [break];
') => RETURN [break];
ENDCASE => RETURN [other];
};
KeyWordBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = CHECKED {
SELECT char FROM
IO.CR =>RETURN [break];
': => RETURN [break];
ENDCASE => RETURN [other];
};
ReadWord: PUBLIC PROC [f: IO.STREAM] RETURNS [word: Rope.ROPE] = {
Reads a rope until <SPACE>, <CR>, <COMMA> or <TAB> are encountered. Used to read in a rope which is data. ie the name of a coordinate system from a 3d file.
[word, ----] ← IO.GetTokenRope[f, WordBreakProc
!IO.EndOfStream => {word ← NIL; CONTINUE}];
};
ReadKeyWord: PUBLIC PROC [f: IO.STREAM] RETURNS [keyWord: Rope.ROPE, good: BOOL] = {
Reads a rope until a ':' or <CR> are encountered. If CR is encountered first, then good is FALSE since ":" is expected after a keyword.
end: BOOLFALSE;
nextChar: Rope.ROPE;
[keyWord, ----] ← IO.GetTokenRope[f, KeyWordBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {good ← FALSE; keyWord ← NIL; RETURN};
[nextChar, ----] ← IO.GetTokenRope[f, KeyWordBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {good ← FALSE; RETURN};
good ← Rope.Equal[nextChar, ":", TRUE];
};
LineBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = CHECKED {
SELECT char FROM
IO.CR =>RETURN [break];
ENDCASE => RETURN [other];
};
ReadLine: PUBLIC PROC [f: IO.STREAM] RETURNS [line: Rope.ROPE] = {
Reads a rope UNTIL <CR> is encountered. Used to read Solidviews version rope
end: BOOLFALSE;
[line, ----] ← IO.GetTokenRope[f, LineBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {line ← NIL; RETURN};
[----, ----] ← IO.GetTokenRope[f, LineBreakProc]; -- remove <CR>
};
WhiteSpaceProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = {
SELECT char FROM
IO.TAB => RETURN [other];
IO.CR =>RETURN [other];
IO.SP => RETURN [other];
ENDCASE => RETURN [break];
};
ReadWhiteSpace: PUBLIC PROC [f: IO.STREAM] = {
Reads, <SPACE>'s, <CR>'s, and <TAB>'s until something else is encountered. Signals RopeNotOnTop if no white space characters are found.
whiteSpace: Rope.ROPE;
end: BOOLFALSE;
[whiteSpace, ----] ← IO.GetTokenRope[f, WhiteSpaceProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end OR Rope.Size[whiteSpace] = 0 THEN SIGNAL RopeNotOnTop[IO.GetIndex[f], "null", "<whitespace>"];
};
ReadBlank: PUBLIC PROC [f: IO.STREAM] = {
Reads, <SPACE>'s, <CR>'s, and <TAB>'s until something else is encountered. Doesn't mind if no white space characters are found. Treats comments as white space.
[] ← IO.SkipWhitespace[f, TRUE];
};
ReadHorizontalBlank: PUBLIC PROC [f: IO.STREAM] RETURNS [good: BOOL] = {
Reads <SPACE>'s, and <TABS>'s until something else is encountered. Returns good = FALSE if a CR is encountered before anything else
whiteSpace: Rope.ROPE;
c: CHAR;
end: BOOLFALSE;
good ← TRUE;
[whiteSpace, ----] ← IO.GetTokenRope[f, HorizontalBlankProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {good ← FALSE; RETURN};
c ← Rope.Fetch[whiteSpace, 0];
SELECT c FROM
IO.CR => {good ← FALSE; RETURN};
IO.TAB, IO.SP => {good ← TRUE; RETURN};
ENDCASE => {good ← TRUE; IO.Backup[f, c]; RETURN};
};
HorizontalBlankProc: SAFE PROC [char: CHAR] RETURNS [CharClass] = {
SELECT char FROM
IO.TAB, IO.SP => RETURN [other];
ENDCASE => RETURN [break];
};
ReadBlankAndRope: PUBLIC PROC [f: IO.STREAM, rope: Rope.ROPE] = {
ReadBlank[f];
ReadRope[f, rope];
};
ReadBlankAndReal: PUBLIC PROC [f: IO.STREAM] RETURNS [r: REAL] = {
A convenience function. Equivalent to ReadBlank[f]; r ← ReadReal[f];
ReadBlank[f];
r ← ReadReal[f];
};
ReadBlankAndWord: PUBLIC PROC [f: IO.STREAM] RETURNS [word: Rope.ROPE] = {
A convenience function. Equivalent to ReadBlank[f]; word ← ReadWord[f];
ReadBlank[f];
word ← ReadWord[f];
};
ReadBlankAndNAT: PUBLIC PROC [f: IO.STREAM] RETURNS [n: NAT] = {
A convenience function. Equivalent to ReadBlank[f]; n ← ReadNAT[f];
ReadBlank[f];
n ← ReadNAT[f];
};
ReadPoint3d: PUBLIC PROC [f: IO.STREAM] RETURNS [point3d: Point3d] = {
Assumes the next rope on the stream will be of the form "[<real1>,<real2>,<real3>]".
ReadRope[f, "["];
point3d[1] ← ReadBlankAndReal[f];
ReadRope[f, ","];
point3d[2] ← ReadBlankAndReal[f];
ReadRope[f, ","];
point3d[3] ← ReadBlankAndReal[f];
ReadRope[f, "]"];
};
ReadPoint2d: PUBLIC PROC [f: IO.STREAM] RETURNS [point2d: Point2d] = {
Assumes the next rope on the stream will be of the form "[<real1>,<real2>]".
ReadRope[f, "["];
point2d[1] ← ReadBlankAndReal[f];
ReadRope[f, ","];
point2d[2] ← ReadBlankAndReal[f];
ReadRope[f, "]"];
};
ReadPoint2dAsPoint3d: PUBLIC PROC [f: IO.STREAM] RETURNS [point3d: Point3d] = {
Assumes the next rope on the stream will be of the form "[<real1>,<real2>]". Fills in point3d[3] with zero.
ReadRope[f, "["];
point3d[1] ← ReadBlankAndReal[f];
ReadRope[f, ","];
point3d[2] ← ReadBlankAndReal[f];
point3d[3] ← 0;
ReadRope[f, "]"];
};
RealBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = {
SELECT char FROM
'], ', => RETURN [break];
IO.CR =>RETURN [break];
IO.SP => RETURN [break];
ENDCASE => RETURN [other];
};
ReadReal: PUBLIC PROC [f: IO.STREAM] RETURNS [r: REAL] = {
Reads digits up to the next ], <CR>, <SPACE> or <COMMA>. Leaves these terminators on the stream.
realRope: Rope.ROPE;
end: BOOLFALSE;
[realRope, ----] ← IO.GetTokenRope[f, RealBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {r ← 0.0; RETURN};
IF Rope.Find[realRope, ".", 0, FALSE] = -1 THEN realRope ← Rope.Concat[realRope, ".0"];
r ← IO.GetReal[IO.RIS[realRope]];
};
NATBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = {
SELECT char FROM
'], IO.CR, IO.SP, '., ', => RETURN [break];
ENDCASE => RETURN [other];
};
ReadNAT: PUBLIC PROC [f: IO.STREAM] RETURNS [n: NAT] = {
Reads digits up to the next ], <CR>, <SPACE>. Leaves these terminators on the stream.
end: BOOLFALSE;
intRope: Rope.ROPE;
[intRope, ----] ← IO.GetTokenRope[f, NATBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {n ← 1; RETURN};
n ← IO.GetInt[IO.RIS[intRope]];
};
BoolBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = {
SELECT char FROM
'], IO.CR, IO.SP, '., ', => RETURN [break];
ENDCASE => RETURN [other];
};
ReadBool: PUBLIC PROC [f: IO.STREAM] RETURNS [truth: BOOL, good: BOOL] = {
Tries to read TRUE or FALSE from the stream. If it encounters another word, good = FALSE;
end: BOOLFALSE;
boolRope: Rope.ROPE;
[boolRope, ----] ← IO.GetTokenRope[f, BoolBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {good ← FALSE; truth ← FALSE; RETURN};
good ← TRUE;
IF Rope.Equal[boolRope, "TRUE", TRUE] THEN truth ← TRUE
ELSE IF Rope.Equal[boolRope, "FALSE", TRUE] THEN truth ← FALSE
ELSE {truth ← FALSE; good ← FALSE};
};
ReadColor: PUBLIC PROC [f: IO.STREAM] RETURNS [color: Color] = {
r, g, b: REAL;
ReadChar[f, '[];
r ← ReadBlankAndReal[f];
ReadChar[f, ',];
g ← ReadBlankAndReal[f];
ReadChar[f, ',];
b ← ReadBlankAndReal[f];
ReadChar[f, ']];
color ← ImagerColor.ColorFromRGB[[r, g, b]];
}; -- end of ReadColor
ReadSurface: PUBLIC PROC [f: IO.STREAM] RETURNS [surface: REF ANY] = {
surfType: Rope.ROPE ← ReadBlankAndWord[f];
SELECT TRUE FROM
Rope.Equal[surfType, "tube", FALSE] => {
radius, height: REAL;
ReadBlankAndRope[f, "[radius:"];
radius ← ReadBlankAndReal[f];
ReadBlankAndRope[f, ","];
ReadBlankAndRope[f, "height:"];
height ← ReadBlankAndReal[f];
ReadBlankAndRope[f, "]"];
surface ← SVMappings.CreateTube[radius, height];
};
Rope.Equal[surfType, "box", FALSE] => {
boxSide: Vector3d;
ReadBlank[f];
boxSide ← ReadPoint3d[f];
surface ← SVMappings.CreateBox[boxSide[1], boxSide[2], boxSide[3]];
};
Rope.Equal[surfType, "NIL"] => {};
ENDCASE => ERROR;
}; -- end of ReadSurface
ReadMaterial: PUBLIC PROC [f: IO.STREAM] RETURNS [material: Material] = {
matRope: Rope.ROPE ← ReadWord[f];
success: BOOL;
[material, success] ← SVArtwork.RopeToMaterial[matRope];
IF NOT success THEN ERROR;
};
ReadSMap: PUBLIC PROC [f: IO.STREAM] RETURNS [sMap: SMap] = {
mapRope: Rope.ROPE ← ReadWord[f];
success: BOOL;
[sMap, success] ← SVArtwork.RopeToSMap[mapRope];
IF NOT success THEN ERROR;
};
ReadOMap: PUBLIC PROC [f: IO.STREAM] RETURNS [oMap: OMap] = {
mapRope: Rope.ROPE ← ReadWord[f];
success: BOOL;
[oMap, success] ← SVArtwork.RopeToOMap[mapRope];
IF NOT success THEN ERROR;
};
ReadCamera: PUBLIC PROC [f: IO.STREAM, worldCS: CoordSystem, scene: Scene, version: REAL] RETURNS [fileCamera: FileCamera] = {
name: Rope.ROPE;
origin, focusPoint: Point3d;
slant, focalLength, resolution: REAL;
projection: Projection;
projectionRope: Rope.ROPE;
clippingPlanes: LIST OF Plane;
visibleAssemblies: LIST OF Rope.ROPE;
frame: FrameBox;
success: BOOL;
ReadBlankAndRope[f, "Camera:"];
name ← ReadBlankAndWord[f];
ReadBlankAndRope[f, "origin:"];
ReadBlank[f];
origin ← ReadPoint3d[f];
ReadBlankAndRope[f, "focusPoint:"];
ReadBlank[f];
focusPoint ← ReadPoint3d[f];
ReadBlankAndRope[f, "slant:"];
slant ← ReadBlankAndReal[f];
ReadBlankAndRope[f, "resolution:"];
resolution ← ReadBlankAndReal[f];
ReadBlankAndRope[f, "focalLength:"];
focalLength ← ReadBlankAndReal[f];
IF version > 5.1 THEN {
ReadBlankAndRope[f, "projection:"];
projectionRope ← ReadBlankAndWord[f];
[projection, success] ← SVGraphics.RopeToProjection[projectionRope];
IF NOT success THEN projection ← perspective;
}
ELSE projection ← perspective;
ReadBlankAndRope[f, "frame:"];
ReadBlank[f];
frame ← ReadFrame[f];
ReadBlankAndRope[f, "clippingPlanes:"];
clippingPlanes ← ReadClippingPlanes[f];
ReadBlankAndRope[f, "visibleAssemblies:"];
visibleAssemblies ← ReadListOfRope[f];
ReadBlank[f];
fileCamera ← SVScene.CreateFileCamera[name, origin, focusPoint, slant, resolution, focalLength, projection, frame, clippingPlanes, visibleAssemblies];
};
ReadClippingPlanes: PUBLIC PROC [f: IO.STREAM] RETURNS [clippingPlanes: LIST OF Plane] = {
plane: Plane;
continue: BOOL;
clippingPlanes ← NIL;
continue ← ReadHorizontalBlank[f];
IF NOT continue THEN RETURN;
plane ← ReadPlane[f];
clippingPlanes ← AppendPlaneToList[plane, clippingPlanes];
WHILE TRUE DO
continue ← ReadHorizontalBlank[f];
IF NOT continue THEN RETURN;
ReadRope[f, ","];
ReadBlank[f];
plane ← ReadPlane[f];
clippingPlanes ← AppendPlaneToList[plane, clippingPlanes];
ENDLOOP;
};
AppendPlaneToList: PROC [plane: Plane, list: LIST OF Plane] RETURNS [LIST OF Plane] = {
A copy of List.Nconc1 for LIST OF Plane instead of LIST OF REF ANY
z: LIST OF Plane ← list;
IF z = NIL THEN RETURN[CONS[plane,NIL]];
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← CONS[plane,NIL];
RETURN[list];
};
ReadPlane: PUBLIC PROC [f: IO.STREAM] RETURNS [plane: Plane] = {
A, B, C, D: REAL;
ReadBlankAndRope[f, "["];
A ← ReadBlankAndReal[f];
ReadBlankAndRope[f, ","];
B ← ReadBlankAndReal[f];
ReadBlankAndRope[f, ","];
C ← ReadBlankAndReal[f];
ReadBlankAndRope[f, ","];
D ← ReadBlankAndReal[f];
ReadBlankAndRope[f, "]"];
plane ← SVPolygon3d.PlaneFromCoefficients[A, B, C, D];
};
ReadStyle: PUBLIC PROC [f: IO.STREAM] RETURNS [style: DrawStyle] = {
styleName: Rope.ROPE ← ReadBlankAndWord[f];
success: BOOL;
[style, success] ← SVGraphics.RopeToDrawStyle[styleName];
IF NOT success THEN style ← wire;
}; -- end of ReadStyle
ReadFrame: PUBLIC PROC [f: IO.STREAM] RETURNS [frame: FrameBox] = {
success: BOOL;
point: Point2d;
frame ← NEW[FrameBoxObj];
ReadBlankAndRope[f, "["];
ReadBlank[f];
point ← ReadPoint2d[f];
frame.downLeft[1] ← point[1];
frame.upRight[2] ← point[2];
ReadBlankAndRope[f, ","];
ReadBlank[f];
point ← ReadPoint2d[f];
frame.upRight[1] ← point[1];
frame.downLeft[2] ← point[2];
ReadBlankAndRope[f, "fullScreen:"];
ReadBlank[f];
[frame.fullScreen, success] ← ReadBool[f];
IF NOT success THEN frame.fullScreen ← TRUE;
ReadBlankAndRope[f, "]"];
}; -- end of ReadFrame
RopesOnOneLineProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = CHECKED {
SELECT char FROM
IO.CR =>RETURN [break];
IO.SP, IO.TAB, ', , '; => RETURN [sepr];
ENDCASE => RETURN [other];
};
ReadListOfRope: PUBLIC PROC [f: IO.STREAM] RETURNS [ropeList: LIST OF Rope.ROPE] = {
rope: Rope.ROPE;
end: BOOLFALSE;
ropeList ← NIL;
WHILE TRUE DO
[rope, ----] ← IO.GetTokenRope[f, RopesOnOneLineProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end OR rope = NIL THEN RETURN;
IF Rope.Equal[rope, Rope.FromChar[IO.CR]] THEN RETURN
ELSE ropeList ← AppendRopeToRopeList[rope, ropeList];
ENDLOOP;
};
AppendRopeToRopeList: PROC [rope: Rope.ROPE, list: LIST OF Rope.ROPE] RETURNS [LIST OF Rope.ROPE] = {
A copy of List.Nconc1 for LIST OF Rope.ROPE instead of LIST OF REF ANY
z: LIST OF Rope.ROPE ← list;
IF z = NIL THEN RETURN[CONS[rope,NIL]];
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← CONS[rope,NIL];
RETURN[list];
};
ReadVisibleAssemblies: PUBLIC PROC [f: IO.STREAM, scene: Scene, feedback: FeedbackData] RETURNS [assemblyList: LIST OF Slice] = {
end: BOOLFALSE;
assembly: Slice;
assemblyName: Rope.ROPE;
continue, found: BOOL;
assemblyList ← NIL;
found ← TRUE;
WHILE continue DO
[assemblyName, ----] ← IO.GetTokenRope[f, RopesOnOneLineProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end OR assemblyName = NIL OR Rope.Equal[assemblyName, Rope.FromChar[IO.CR]] THEN continue ← FALSE
ELSE {
[assembly, ----] ← SVScene.FindAssemblyFromName[assemblyName, scene
! SVScene.AssemblyNotFound => {found ← FALSE; CONTINUE}];
IF NOT found THEN {
index: NATIO.GetIndex[f];
Feedback.PutF[feedback, oneLiner, "Slice %g not found at: %g.", [rope[assemblyName]], [integer[index]]];
Feedback.Blink[feedback];
}
ELSE assemblyList ← AppendAssemblyToList[assembly, assemblyList];
};
ENDLOOP;
};
AppendAssemblyToList: PROC [assembly: Slice, list: LIST OF Slice] RETURNS [LIST OF Slice] = {
A copy of List.Nconc1 for LIST OF Slice instead of LIST OF REF ANY
z: LIST OF Slice ← list;
IF z = NIL THEN RETURN[CONS[assembly,NIL]];
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← CONS[assembly,NIL];
RETURN[list];
};
END.