.. _pascal-scripting: .. index:: single: Pascal Scripting 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 :ref:`python-scripting` in chapter 18 and the :ref:`python-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. .. index:: single: Intro 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 :ref:`pascal-object-model`). .. index:: single: Running Pascal Running Pascal -------------- Four command-line entry points drive the Pascal host: .. index:: single: PASSCRIPTS (command) .. list-table:: :header-rows: 1 :widths: 24 76 * - Command - Description * - ``PASCAL `` (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 `` - Evaluate a **single** Pascal expression and print its value at the command line (e.g. ``PASEVAL ActiveDrawing.Name``). * - ``PASLOAD `` - 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. .. index:: single: Autoload and trust single: Autoload single: trust 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. .. index:: single: Responsiveness and Esc single: Responsiveness 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). .. _pascal-object-model: .. index:: single: The object model 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. .. code-block:: text 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. .. index:: single: Class and global reference single: Class single: global reference Class and global reference -------------------------- .. index:: single: Drawing Drawing ~~~~~~~ Obtained from ``ActiveDrawing``. The factory for entities, surfaces, and alignments. .. list-table:: :header-rows: 1 :widths: 30 40 18 38 * - 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. .. index:: single: Entity Entity ~~~~~~ A single drawing object returned by the OO ``Add*`` methods or by ``EntityByHandle``. .. list-table:: :header-rows: 1 :widths: 24 34 20 40 * - 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. .. index:: single: Surface Surface ~~~~~~~ A TIN surface created by ``Drawing.CreateSurface`` (or looked up via ``FindSurface`` / ``SurfaceAt``). .. list-table:: :header-rows: 1 :widths: 24 40 18 40 * - 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*. .. index:: single: Alignment Alignment ~~~~~~~~~ A horizontal alignment created by ``CreateAlignmentFromPolyline`` (or looked up via ``FindAlignment`` / ``AlignmentAt``). .. list-table:: :header-rows: 1 :widths: 26 40 18 38 * - 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. .. index:: single: Points single: Profile single: Elements Points, Profile, Elements ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The array-free helper objects. ``Points`` is the input builder; ``Profile`` and ``Elements`` are result collections. .. list-table:: :header-rows: 1 :widths: 18 24 36 22 * - 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`` .. index:: single: Module-level globals Module-level globals ~~~~~~~~~~~~~~~~~~~~~ Call these unqualified. The flat creators mirror the ``Drawing`` methods but return a handle **string** instead of an object. .. list-table:: :header-rows: 1 :widths: 30 42 16 38 * - 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. .. index:: single: Commands and events single: Commands single: events Commands and events -------------------- .. index:: single: Custom commands 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. .. code-block:: pascal 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')``. .. index:: single: Events Events ~~~~~~ ``OnEvent`` attaches a handler procedure (by name) to a drawing event. .. list-table:: :header-rows: 1 :widths: 28 34 38 * - 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``). .. index:: single: Dialogs 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. .. list-table:: :header-rows: 1 :widths: 32 40 28 * - 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. .. index:: single: Available RTL 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))``. 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. .. index:: single: Surfaces Surfaces ~~~~~~~~ Create a TIN from seed points, interpolate it, take a long-section, then compare two surfaces by volume (``04_surface.pas``): .. code-block:: pascal 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. .. index:: single: Alignments Alignments ~~~~~~~~~~ Build an alignment from a polyline (fillet radius required for 3+ vertices), then query it (``05_alignment.pas``): .. code-block:: pascal 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. .. index:: single: Combined civil workflow Combined civil workflow ~~~~~~~~~~~~~~~~~~~~~~~~~ The highlight: step chainage along an alignment and sample the ground surface at each station (``06_civil_workflow.pas``): .. code-block:: pascal 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. .. index:: single: Dialogs and message boxes single: Dialogs single: message boxes 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: .. code-block:: pascal 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``): .. code-block:: pascal if MessageBox('Delete selection?', 'Confirm', 2, 4) = 2 then { YesNo, question → Yes } WriteLn('confirmed'); .. index:: single: Live forms 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 :ref:`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: .. code-block:: pascal 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 } .. index:: single: Ribbon & menu single: Ribbon single: menu 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 :ref:`plugin-ribbon-menu`. .. code-block:: pascal AddRibbonButton('Demo', 'GREET', 'Greet', 'Say hello', '', True); AddMenuItem('Demo', 'GREET', 'Greet'); .. index:: single: Limitations and tips single: tips 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.