9.3. Pascal Scripting

This chapter is the complete reference for IQcad’s embedded Pascal Script host: every script-facing class, global, command, and event, with a text class diagram and worked examples.

For the big-picture scripting concepts (the trust model, autoload, custom commands, events) see Python scripting in chapter 18 and the Python Scripting Reference in chapter 19. The Pascal host shares the same Layer-2 drawing model as Python, so those concepts carry over directly; this chapter documents the Pascal-specific surface.

9.3.1. Intro

Pascal Script is a Pascal-syntax interpreter that compiles your script into IQcad at run time. Unlike the Python host – which embeds a bundled CPython interpreter – the Pascal host has zero external runtime dependency: there is nothing to install and nothing ships alongside IQcad.exe. If you already know Object Pascal (or Delphi), this is the lowest-friction way to automate IQcad.

Both hosts talk to the same Layer-2 drawing model: a surface, alignment, or entity created from Pascal is identical to one created from Python or from the GUI. The two languages differ only in syntax and in how they pass collections (see The object model).

9.3.2. Running Pascal

Four command-line entry points drive the Pascal host:

Command

Description

PASCAL <file.pas> (alias PAS)

Run a .pas file once. Anything it registers is discarded when it finishes (unless it registers a command – then it stays resident, like PASLOAD).

PASEVAL <expr>

Evaluate a single Pascal expression and print its value at the command line (e.g. PASEVAL ActiveDrawing.Name).

PASLOAD <file.pas>

Run a file and leave it session-resident: any commands or event handlers it registers stay active until you close IQcad.

PASSCRIPTS

List the Pascal-defined commands currently registered.

A script is a normal Pascal program:

program demo;
begin
  WriteLine('hello from Pascal');
end.

A bare snippet without a program ... begin ... end. header is auto-wrapped, so WriteLine('hi'); runs on its own.

Note

The wrapper detects a full program by the first program (or unit) keyword, skipping any leading comments. You can open a full-program file with a header comment block ({ ... }, // or (* ... *)) before the program line, as the bundled samples do.

9.3.2.1. Autoload and trust

Any .pas file dropped in %APPDATA%\IQcad\scripts\ is loaded automatically at startup, using the same content-hash trust model as Python. The first time IQcad sees a script (and again whenever its SHA-256 content hash changes) it shows a one-time trust dialog offering Always, This session, or Never; your choice is remembered in %APPDATA%\IQcad\scripts\script_trust.json. A script you have never trusted is not executed.

9.3.2.2. Responsiveness and Esc

Scripts run on the GUI thread. In any loop that may run for a while, call Yield periodically: it pumps the message queue so the window stays responsive and lets Esc cancel the script. Cancellation is cooperative – it takes effect at the next Yield (or at the next script line between statements).

9.3.3. The object model

ActiveDrawing returns the current Drawing, which is the factory for everything else: it creates and returns Entity, Surface, and Alignment objects. Three helper objects – Points, Profile, and Elements – carry collections.

globals
|
|-- flat creators ...... AddLine/AddCircle/AddArc/AddEllipse/
|                        AddRectangle/AddText/AddSurveyPoint/
|                        AddPolyline/AddHatch/AddTunnelSectionLine
|                        (return a handle STRING),
|                        CreateLayer/SetActiveLayer/ZoomExtents
|-- WriteLine / Cmd / Yield
|-- RegisterCommand / UnregisterCommand
|-- OnEvent / OffEvent
|-- dialogs ............ ShowMessage/Confirm/InputString/SelectFile
|-- NewPoints  --------> Points   (the array-free input builder)
|
`-- ActiveDrawing  ----> Drawing
                          |
                          |  create/returns
                          |
           +--------------+----------------+
           |              |                |
           v              v                v
        Entity         Surface          Alignment
        (geometry)     (TIN)            (horizontal)

        AddLine/Circle/Arc/Ellipse/Rectangle/Polyline/Text/
        SurveyPoint/Hatch/TunnelSectionLine     --> Entity
        CreateSurface / FindSurface / SurfaceAt --> Surface
        CreateAlignmentFromPolyline /
        FindAlignment / AlignmentAt             --> Alignment

        Surface.ProfileAlong  --> Profile  (Count + Station(i)/Z(i))
        Alignment.Elements    --> Elements (Count + Kind(i)/...)

The array-free design. PascalScript handles dynamic arrays poorly, so the Pascal API never takes or returns an array. Point input uses a Points builder (NewPoints then Add(x, y, z)); collection results come back as Profile and Elements objects that expose a Count plus indexer methods (Station(i) / Z(i) / Kind(i) …). This is the key difference from Python, which uses native lists and tuples.

Note

A flat global creator (e.g. AddCircle(...)) returns the new entity’s handle string, whereas the OO method ActiveDrawing.AddCircle(...) returns an Entity object you can keep editing.

9.3.4. Class and global reference

9.3.4.1. Drawing

Obtained from ActiveDrawing. The factory for entities, surfaces, and alignments.

Member

Signature

Returns

Description

Name

property (read-only)

string

Drawing name.

ActiveLayer

property (read/write)

string

Name of the current layer.

AddLine

AddLine(x1, y1, z1, x2, y2, z2)

Entity

Line between two points.

AddCircle

AddCircle(cx, cy, cz, r)

Entity

Circle, centre + radius.

AddArc

AddArc(sx, sy, sz, mx, my, mz, ex, ey, ez)

Entity

Three-point arc.

AddEllipse

AddEllipse(cx, cy, cz, radx, rady, rot)

Entity

Ellipse with semi-axes and rotation.

AddRectangle

AddRectangle(x1, y1, x2, y2)

Entity

Rectangle from opposite corners.

AddText

AddText(x, y, z, s, h)

Entity

Text s at the point, height h.

AddSurveyPoint

AddSurveyPoint(x, y, z, num)

Entity

Survey point numbered num.

AddPolyline

AddPolyline(Pts, closed)

Entity

Polyline through a Points list.

AddHatch

AddHatch(Pts, pattern, angle, scale)

Entity

Hatch over a Points boundary.

AddTunnelSectionLine

AddTunnelSectionLine(Pts, closed)

Entity

Tunnel section line.

CreateLayer

CreateLayer(n, color)

Boolean

Create a layer (color is an ACI index).

EntityByHandle

EntityByHandle(h)

Entity

Look up an entity by handle (nil if absent).

ZoomExtents

ZoomExtents

–

Zoom to drawing extents.

CreateSurface

CreateSurface(name, Pts)

Surface

Build a TIN from a Points list of (x, y, z).

FindSurface

FindSurface(name)

Surface

Look up a surface by name (nil if absent).

SurfaceCount

SurfaceCount

Integer

Number of surfaces.

SurfaceAt

SurfaceAt(i)

Surface

Surface by zero-based index.

CreateAlignmentFromPolyline

CreateAlignmentFromPolyline(polyHandle, radius, name)

Alignment

Build an alignment from a polyline entity.

CreateAlignmentFromPolylineZ

CreateAlignmentFromPolylineZ(polyHandle, radius, name, vcurveLen)

Alignment

Like CreateAlignmentFromPolyline, but also builds the vertical profile from the polyline’s vertex elevations (one PVI per vertex; vcurveLen applied to interior PVIs, 0 = angular profile).

FindAlignment

FindAlignment(name)

Alignment

Look up an alignment by name (nil if absent).

AlignmentCount

AlignmentCount

Integer

Number of alignments.

AlignmentAt

AlignmentAt(i)

Alignment

Alignment by zero-based index.

9.3.4.2. Entity

A single drawing object returned by the OO Add* methods or by EntityByHandle.

Member

Signature

Returns

Description

Handle

property (read-only)

string

Persistent handle.

TypeName

property (read-only)

string

'Line' | 'Circle' | 'Text' | …

Layer

property (read/write)

string

Layer name.

Color

property (read/write)

Integer

ACI colour index.

GetProperty

GetProperty(n)

string

Read a typed property (e.g. circle 'Radius').

SetProperty

SetProperty(n, v)

–

Write a typed property.

Delete

Delete

–

Remove the entity; later access is a no-op guard.

9.3.4.3. Surface

A TIN surface created by Drawing.CreateSurface (or looked up via FindSurface / SurfaceAt).

Member

Signature

Returns

Description

Name

property (read-only)

string

Surface name.

Handle

property (read-only)

string

Persistent handle.

ElevationAt

ElevationAt(x, y, out z)

Boolean

Interpolated Z in z; False outside the TIN.

AddPoint

AddPoint(x, y, z)

–

Insert a vertex and rebuild the TIN.

ProfileAlong

ProfileAlong(Pts)

Profile

Long-section along a Points polyline.

VolumeTo

VolumeTo(Other, cell, out cut, out fill)

Boolean

Grid-approx cut/fill; cut where self is above Other.

9.3.4.4. Alignment

A horizontal alignment created by CreateAlignmentFromPolyline (or looked up via FindAlignment / AlignmentAt).

Member

Signature

Returns

Description

Name

property (read-only)

string

Alignment name.

Handle

property (read-only)

string

Persistent handle.

Length

property (read-only)

Double

Total length.

StartStation

property (read-only)

Double

Start chainage.

EndStation

property (read-only)

Double

End chainage.

PointAtStation

PointAtStation(s, out x, out y, out z)

–

World point at chainage s.

DirectionAtStation

DirectionAtStation(s)

Double

Bearing at s, in radians.

ElevationAtStation

ElevationAtStation(s)

Double

Profile elevation at s.

StationOffsetAt

StationOffsetAt(x, y, out station, out offset)

–

Nearest station + offset (- left / + right).

Elements

Elements

Elements

Geometry-elements collection.

9.3.4.5. Points, Profile, Elements

The array-free helper objects. Points is the input builder; Profile and Elements are result collections.

Object

Member

Signature

Returns

Points

Add

Add(x, y, z)

–

Points

Count

Count

Integer

Profile

Count

Count

Integer

Profile

Station

Station(i)

Double

Profile

Z

Z(i)

Double

Elements

Count

Count

Integer

Elements

Kind

Kind(i)

string

Elements

Length

Length(i)

Double

Elements

Radius

Radius(i)

Double

Elements

StartStation

StartStation(i)

Double

9.3.4.6. Module-level globals

Call these unqualified. The flat creators mirror the Drawing methods but return a handle string instead of an object.

Member

Signature

Returns

Description

ActiveDrawing

ActiveDrawing

Drawing

The current drawing.

NewPoints

NewPoints

Points

A fresh, empty Points builder.

WriteLine

WriteLine(s)

–

Print a line to the command line.

Cmd

Cmd(s)

Boolean

Run a command-line string.

Yield

Yield

–

Pump the UI; allow Esc to cancel.

AddLine

AddLine(x1, y1, z1, x2, y2, z2)

string

Flat line; returns a handle.

AddCircle

AddCircle(cx, cy, cz, r)

string

Flat circle; returns a handle.

AddArc

AddArc(sx, sy, sz, mx, my, mz, ex, ey, ez)

string

Flat arc; returns a handle.

AddEllipse

AddEllipse(cx, cy, cz, radx, rady, rot)

string

Flat ellipse; returns a handle.

AddRectangle

AddRectangle(x1, y1, x2, y2)

string

Flat rectangle; returns a handle.

AddText

AddText(x, y, z, s, h)

string

Flat text; returns a handle.

AddSurveyPoint

AddSurveyPoint(x, y, z, num)

string

Flat survey point; returns a handle.

AddPolyline

AddPolyline(Pts, closed)

string

Flat polyline; returns a handle.

AddHatch

AddHatch(Pts, pattern, angle, scale)

string

Flat hatch; returns a handle.

AddTunnelSectionLine

AddTunnelSectionLine(Pts, closed)

string

Flat tunnel section line; returns a handle.

CreateLayer

CreateLayer(n, color)

Boolean

Create a layer (ACI color).

SetActiveLayer

SetActiveLayer(n)

–

Set the current layer.

ZoomExtents

ZoomExtents

–

Zoom to drawing extents.

RegisterCommand

RegisterCommand(name, aliases, category, desc, procname)

–

Register a command bound to a script proc.

UnregisterCommand

UnregisterCommand(name)

–

Remove a registered command.

OnEvent

OnEvent(eventName, procname)

–

Attach an event handler by proc name.

OffEvent

OffEvent(eventName, procname)

–

Detach an event handler.

9.3.5. Commands and events

9.3.5.1. Custom commands

RegisterCommand binds a new IQcad command to a script procedure by name. Load the script resident with PASLOAD (or drop it in the autoload folder) and the command can be typed at the prompt like any built-in.

program ext;
procedure DoGrid(arg: string);
begin
  { arg arrives as a string -- parse it yourself }
  AddCircle(0, 0, 0, StrToFloatDef(arg, 1.0));
end;
begin
  RegisterCommand('PASGRID', 'PG', 'Tools', 'Draw a grid', 'DoGrid');
end.

Warning

Command arguments arrive as a string. Parse them yourself with StrToFloatDef / StrToIntDef.

Remove a registration with UnregisterCommand('PASGRID').

9.3.5.2. Events

OnEvent attaches a handler procedure (by name) to a drawing event.

Event name

Handler signature

Fires when

'entity_created'

procedure(handle: string)

An entity is created.

'entity_deleted'

procedure(handle: string)

An entity is deleted.

'selection_changed'

procedure (no args)

The selection changes.

Detach a handler with OffEvent(eventName, procname).

Note

Handlers run synchronously on the GUI thread. An exception inside a handler is logged to the command line, not fatal. A handler that itself creates entities re-fires entity_created and can recurse – guard with a module-level boolean (see samples\pascal\03_events.pas).

9.3.6. Dialogs

Four dialog globals – shared with the Python host – prompt the user. Run headlessly (no GUI), they return safe defaults so an unattended script never blocks.

Function

Signature

Headless default

ShowMessage

ShowMessage(s)

no-op

Confirm

Confirm(s): Boolean

False

InputString

InputString(prompt, def: string; out v: string): Boolean

False (v = def)

SelectFile

SelectFile(title, filter: string; save: Boolean): string

''

SelectFile’s filter is a pipe-delimited Caption|Mask pair (e.g. 'CSV files|*.csv'); save selects an Open vs Save dialog.

9.3.7. Available RTL

The host registers a subset of the Pascal RTL plus a few additions. Locale-invariant float conversions are provided so that scripts behave identically regardless of the system decimal separator.

  • Strings: Copy, Pos, Length, UpperCase, LowerCase, Trim.

  • Numbers: IntToStr, StrToInt, StrToIntDef, FloatToStr, StrToFloat, StrToFloatDef (the float ones are locale-invariant – always . as the decimal point).

  • Math: Sin, Cos, Sqrt, Round, Trunc, Abs, Pi, ArcTan, Power.

  • Formatting: FmtFloat(v: Double; decimals: Integer): string – a locale-invariant fixed-decimal formatter.

Warning

Format (the array of const variant) is not available. Build strings with FmtFloat + IntToStr + concatenation, e.g. WriteLine('STA ' + FmtFloat(s, 1) + '  Z=' + FmtFloat(z, 3)).

9.3.8. Examples

Seven worked scripts ship under samples\pascal\:

  • samples\pascal\01_primitives.pas – geometry, layers, the OO vs flat creators, Points for polyline + hatch.

  • samples\pascal\02_command.pas – registering a custom command.

  • samples\pascal\03_events.pas – wiring up event handlers.

  • samples\pascal\04_surface.pas – TIN surfaces: create, query, profile, volume.

  • samples\pascal\05_alignment.pas – alignments from a polyline.

  • samples\pascal\06_civil_workflow.pas – chainage along an alignment while sampling a surface (the highlight).

  • samples\pascal\07_dialogs.pas – the four user-interaction dialogs.

9.3.8.1. Surfaces

Create a TIN from seed points, interpolate it, take a long-section, then compare two surfaces by volume (04_surface.pas):

program surface;
var
  d: Drawing;
  pts, line: Points;
  eg, fg: Surface;
  prof: Profile;
  z, cut, fill: Double;
  i: Integer;
begin
  d := ActiveDrawing;

  pts := NewPoints;
  pts.Add(0, 0, 10.0);  pts.Add(50, 0, 11.0);  pts.Add(50, 40, 12.5);
  pts.Add(0, 40, 10.5); pts.Add(25, 20, 11.2);
  eg := d.CreateSurface('EG', pts);

  if eg.ElevationAt(25, 20, z) then       { False outside the TIN }
    WriteLine('Z at (25,20) = ' + FmtFloat(z, 3));

  eg.AddPoint(25, 40, 11.8);              { rebuilds the TIN }

  line := NewPoints;
  line.Add(0, 20, 0); line.Add(50, 20, 0);
  prof := eg.ProfileAlong(line);
  for i := 0 to prof.Count - 1 do
    WriteLine('STA ' + FmtFloat(prof.Station(i), 3) +
              '  Z ' + FmtFloat(prof.Z(i), 3));

  pts := NewPoints;
  pts.Add(0, 0, 11.0);  pts.Add(50, 0, 12.0);  pts.Add(50, 40, 13.5);
  pts.Add(0, 40, 11.5); pts.Add(25, 20, 12.2);
  fg := d.CreateSurface('FG', pts);
  if eg.VolumeTo(fg, 1.0, cut, fill) then
    WriteLine('cut=' + FmtFloat(cut, 2) + '  fill=' + FmtFloat(fill, 2));
end.

9.3.8.2. Alignments

Build an alignment from a polyline (fillet radius required for 3+ vertices), then query it (05_alignment.pas):

program alignment;
var
  d: Drawing;
  pts: Points;
  e: Entity;
  al: Alignment;
  el: Elements;
  x, y, z, dir, st, off: Double;
  i: Integer;
begin
  d := ActiveDrawing;

  pts := NewPoints;
  pts.Add(0, 0, 0); pts.Add(100, 0, 0);
  pts.Add(100, 50, 0); pts.Add(160, 50, 0);
  e := d.AddPolyline(pts, False);

  al := d.CreateAlignmentFromPolyline(e.Handle, 20.0, 'CL-1');
  WriteLine('len=' + FmtFloat(al.Length, 3) +
            '  STA ' + FmtFloat(al.StartStation, 3) +
            '..' + FmtFloat(al.EndStation, 3));

  al.PointAtStation(50.0, x, y, z);
  dir := al.DirectionAtStation(50.0);             { radians }
  al.StationOffsetAt(50.0, 5.0, st, off);         { offset - left / + right }

  el := al.Elements;
  for i := 0 to el.Count - 1 do
    WriteLine(el.Kind(i) +
              ' len=' + FmtFloat(el.Length(i), 3) +
              ' R=' + FmtFloat(el.Radius(i), 3));
end.

9.3.8.3. Combined civil workflow

The highlight: step chainage along an alignment and sample the ground surface at each station (06_civil_workflow.pas):

program civil_workflow;
var
  d: Drawing;
  pts: Points;
  e: Entity;
  surf: Surface;
  al: Alignment;
  step, s, x, y, z, gz: Double;
begin
  d := ActiveDrawing;

  pts := NewPoints;
  pts.Add(-20, -20, 10.0); pts.Add(180, -20, 14.0); pts.Add(180, 80, 16.0);
  pts.Add(-20, 80, 11.0);  pts.Add(80, 25, 13.0);
  surf := d.CreateSurface('EG', pts);

  pts := NewPoints;
  pts.Add(0, 0, 0); pts.Add(100, 0, 0);
  pts.Add(100, 50, 0); pts.Add(160, 50, 0);
  e := d.AddPolyline(pts, False);
  al := d.CreateAlignmentFromPolyline(e.Handle, 20.0, 'CL-1');

  step := 10.0;
  s := al.StartStation;
  while s <= al.EndStation + 1e-6 do
  begin
    al.PointAtStation(s, x, y, z);
    if surf.ElevationAt(x, y, gz) then     { False outside the TIN }
      WriteLine('STA ' + FmtFloat(s, 1) + '  surf=' + FmtFloat(gz, 3))
    else
      WriteLine('STA ' + FmtFloat(s, 1) + '  surf=(outside TIN)');
    s := s + step;
    Yield;                                 { keep responsive / allow Esc }
  end;
end.

9.3.9. Dialogs and message boxes

Pascal scripts can pop a host-built parameter dialog or message box — the same UI the plugin SDK uses.

CreateDialog(title): TScriptDialog returns a dialog; describe fields, Execute, then read the values:

var
  dlg: TScriptDialog;
  shape: Integer; size: Double; layer: string; col: Integer; filled: Boolean;
begin
  dlg := CreateDialog('Draw a shape');
  dlg.AddChoice('shape', 'Shape', 'Rectangle'#9'Circle', 0);  { TAB separates options }
  dlg.AddFloat ('size',  'Size (m)', 10, 0, 0, 2);            { Min=Max=0 → unbounded }
  dlg.AddText  ('layer', 'Layer name', 'SHAPES');
  dlg.AddColor ('color', 'Color', $00B400);                   { 0x00BBGGRR }
  dlg.AddBool  ('filled','Filled?', False);
  if dlg.Execute then
  begin
    shape  := dlg.GetChoice('shape');   { selected index }
    size   := dlg.GetFloat('size');
    layer  := dlg.GetText('layer');
    col    := dlg.GetColor('color');
    filled := dlg.GetBool('filled');
  end;
end.

Builders: AddText, AddMultiline, AddInt(…, Min, Max), AddFloat(…, Min, Max, Decimals), AddBool, AddChoice(Key, Caption, Options, DefaultIndex), AddFile(…, Filter, SaveDlg), AddFolder, AddColor. Getters: GetText (also the selected choice’s text), GetInt, GetFloat, GetBool, GetChoice (index), GetColor. Execute returns False if the user cancels.

MessageBox(Text, Caption: string; Buttons, Icon: Integer): Integer shows a message box and returns the pressed button. Integer codes — buttons: 0 OK, 1 OK/Cancel, 2 Yes/No, 3 Yes/No/Cancel; icons: 0 none, 1 info, 2 warning, 3 error, 4 question; results: 0 OK, 1 Cancel, 2 Yes, 3 No (a dismissed box returns 1):

if MessageBox('Delete selection?', 'Confirm', 2, 4) = 2 then  { YesNo, question → Yes }
  WriteLn('confirmed');

9.3.10. Live forms

A live form is a non-modal, host-owned tool window that stays open: you build controls, keep references, mutate them at runtime, and react to the user through event handlers. It is the scripting face of the plugin SDK’s live tool windows.

CreateForm(title): TScriptForm returns a live form. Its Add* builders (AddButton, AddLabel, AddText, AddMultiline, AddInt, AddFloat, AddBool, AddChoice, AddColor, AddFile, AddFolder, AddList) each return a TScriptControl with typed Get* / Set* accessors and list ops; the form also has BeginRow / EndRow, FindControl, Show / Hide / Close / IsVisible / SetTitle. Assign the name of a script procedure to OnButtonClicked / OnValueChanged / OnFormClosed and the host calls it back:

var frm: TScriptForm;

procedure HandleClick(const Key: string);
var n: Integer;
begin
  if Key = 'add' then
  begin
    n := frm.FindControl('n').GetInt;
    frm.FindControl('log').AppendLine('Added: ' + IntToStr(n));
    frm.FindControl('status').SetText('Last added: ' + IntToStr(n));
  end;
end;

begin
  frm := CreateForm('Form demo');
  frm.AddLabel('status', 'Click Add to append a value');
  frm.AddInt('n', 'Value', 1, 0, 1000);
  frm.AddButton('add', 'Add');
  frm.AddList('log', 'Log');
  frm.OnButtonClicked := 'HandleClick';   { name of the script procedure }
  frm.Show;
end.

CreatePanel(title): TScriptForm is identical to CreateForm but docks the panel into the main window — it appears as a tab in the right-side dock strip instead of a floating window. The Add* builders and On* event handlers are exactly the same; only the container differs:

frm := CreatePanel('Form demo');   { docked twin of CreateForm }

9.3.11. Ribbon & menu

AddRibbonButton(group, command, caption, hint, iconfile, large): Boolean and AddMenuItem(submenu, command, caption): Boolean add GUI entries that run a registered command — a button in the Plugins ribbon tab and an item in the Plugins menu. Scripts appear under a single Scripts group/submenu; iconfile is an absolute path to a PNG (empty for a generic icon). See Ribbon & menu contributions.

AddRibbonButton('Demo', 'GREET', 'Greet', 'Say hello', '', True);
AddMenuItem('Demo', 'GREET', 'Greet');

9.3.12. Limitations and tips

  • PASEVAL is single-expression. It evaluates one Pascal expression and prints its value; it is not a place for statements.

  • No ``Format`` / ``array of const``. Build strings with FmtFloat + IntToStr + concatenation.

  • Array-free API. Pass points via a Points builder; read collection results via Profile / Elements Count + indexers. There are no array parameters or array results.

  • Alignment fillet radius. CreateAlignmentFromPolyline with radius = 0 (the default) builds straight tangents through every vertex, including polylines with 3 or more vertices. Pass a non-zero radius (at least 0.01) only if you want the interior corners filleted into arcs.

  • ``ElevationAt`` returns False outside the TIN. Always check the result before using the out-parameter.

  • Offset sign. StationOffsetAt returns an offset that is negative to the left and positive to the right of the alignment direction.

  • Command args are strings. Parse with StrToFloatDef / StrToIntDef.

  • Limited event coverage. Only entity_created, entity_deleted, and selection_changed exist; there is no on_entity_modified and no drawing-lifecycle or command events yet.

  • No background / async execution. Scripts run on the GUI thread; call Yield to keep long loops responsive and Esc-cancelable.