Script-related

Master script

Master script is not a script context

When writing the master script you should keep in mind that it will be evaluated before the run of each script, in the context of that script. It doesn’t have a “memory” between different script runs, practically the master script is prepended to each script’s code.

This implies the following things:

  • Global variables can have different values in each script context, thus the same code can execute differently
  • Placing parameter definitions and calculations used by multiple scripts in the master script is a good idea: it reduces code complexity.
  • A non-empty parameter buffer will have an effect on all scripts.
  • Subroutines can be shared between scripts

Subroutines in master script

Subroutines in master script can be used from any other script too. A special coding pattern is needed to avoid running the subroutines without calling, but not ending the script execution prematurely:

...
! writing an end statement here would end execution
! without running non-master scripts
goto "MasterEnd"

! ==============================================================================
end ! end ! end ! end ! end ! end ! end ! end ! end ! end ! end ! end ! end !
! ==============================================================================

! ==============================================================================
"Subroutine":
! ==============================================================================

    ! can be called from other scripts, returns to other scripts
return

! ==============================================================================
"MasterEnd":
! ==============================================================================

    ! execution is continued in non-master scripts

2D/3D scripts

Fragments/binary model

These are a feature intended for automatic model conversion/generation. Using them for manually created objects is not recommended:

  • A script is much more customizable than a fragment. Models/drawings can be dragged from the plan into the scripts to generate code.
  • Binary symbols aren’t editable.
  • In a binary 2D symbol, the curved fills aren’t stretched correctly.

Hotspot IDs

If an object is supposed to be dimensionable, use an ID for each of its hotspots/hotlines/hotarcs – even the ones that make no sense to be dimensioned. Unidentified hotspots are given an ID by Archicad and can overlap IDs in the code.

IDs shouldn’t be affected by branch conditions. When using a continuously increased ID variable, increase it on else branches too.

if A > 1 then
    if B > 0 then
        hotspot2 A, B, unID : unID = unID + 1
    else
        unID = unID + 1
    endif
    hotspot2 A, 0, unID : unID = unID + 1
else
    unID = unID + 1
    hotspot2 0, 0, unID : unID = unID + 1
endif

If IDs are affected by loop length, let them be the largest ones, or reserve enough space for the maximum possible by input.

Undo transformations early

When you do not need a transformation anymore, undo it using DEL.

Temporary transformations done in a code block should be deleted before the end of the code block. Temporary transformations done in a loop can be deleted immediately after the end of the code block using a loop counter.

Do this for the top level of the script as well to make further modifications on the object easier.

Do not use DEL TOP command to make later modifications easier. Use variables to count conditional transformations to be undone late

addz -1
for n = 1 to 3
    if p then
        addz 1
        sphere 0.01
        del 1
    endif
    rotz 30
    addx 1
    sphere 0.1
next n
del 2 * (n - 1) ! the loop variable gets incremented after the last run

sphere 0.01
del 1

3D script

Wrap texture using transformation and coor{3}

For code clarity, use a fixed COOR{3} command with transformations defined before it.

In general, separate bodies which require different texture coordinate systems with a body -1 command.

define texture "Own Tile" "Tile Texture",
    1, 1, 128 + 256, 0

define material "Tile" 21,
    0.7, 0.7, 1,
    0.15, 0.95, 0, 0.0,
    0, 0,
    ind(fill, ""), 1,
    ind(texture, "Own Tile")

material "Tile"

block 1, 1, 1

add 0.5, 0.5, 0.5
_wrap = 2 ! box
gosub "AlignTexture"
del 1

end

"AlignTexture":
    coor{3} _wrap, 0,
        0, 0, 0, ! origin
        1, 0, 0, ! x
        0, 1, 0, ! y
        0, 0, 1 ! z
    body -1
return

Wrap texture often

On complicated models, texture wrapping should be specified after each modeling command. Archicad closes bodies after a specific number of primitive faces, and texture wrapping won’t affect those.

Close bodies between different materials

Use the body -1 command before changing the material. This is necessary for correct cut surfaces. The Internal Rendering Engine (used on sections, elevations) needs it for accurate shadow casting of solid and transparent parts of an object (e.g., window sash with grilles).

prism_ 10, 0.1,
    0, 0, 15,
    1, 0, 15,
    1, 1, 15,
    0, 1, 15,
    0, 0, -1,
    0.1, 0.1, 15,
    0.9, 0.1, 15,
    0.9, 0.9, 15,
    0.1, 0.9, 15,
    0.1, 0.1, -1

body -1

material "Blue Glass"

prism_ 5, 0.1,
    0.1, 0.1, 15,
    0.9, 0.1, 15,
    0.9, 0.9, 15,
    0.1, 0.9, 15,
    0.1, 0.1, -1

Parameter script

Hide the old parameter list

When all parameters appear on the graphical interface, hide the All parameters… tabpage. Make its visibility dependent on GLOB_DIAGNOSTICS_MODE for easier debugging.

if not(GLOB_DIAGNOSTICS_MODE) then hideparameter all "A", "B", "ZZYZX"

Run only once

Always set parameter scripts to run only once. To make the parameter script compatible with this setting, follow the rules described in the sub-sections.

Parameter changes are not immediate

Parameter names written in the script can be thought of as variables copied at script start with the same name. These variables aren’t affected by the values and parameters commands, and parameters aren’t affected by the variables’ value changes. VALUES and PARAMETERS commands only take effect after the end of the parameter script.
Using parameter-named-variables in commands that only change parameter values can quickly lead to confusion.
When parameter values need to be changed, copy them to variables prefixed with _ and use VALUES and PARAMETERS commands to alter the parameter’s value when the calculations are complete. Because the parameters will only change after the end of the parameter script, use the variables in the script. This is the key to running the parameter script only once.

Setting parameters

When calculating parameter connections, calculate the variable’s value first, then make the parameter equal to its variable value.

_A = A
...
_halfA = _A / 2      ! Use _A to make clear it contains a value copied from the
                     ! parameter A before parameters A = ... commands.
                     ! A might change after the end of the script
                     ! even if it is on a previous line.
parameters halfA = _halfA
...                  ! use _halfA later in the script, halfA will change only
                     ! after the end of the script

Defining parameter restrictions

When limiting a parameter’s value with the range keyword, use variables to determine its minimum and/or maximum values. After the range command, overwrite the variable copied from the parameter if it exceeds the limits defined by these values.

_A = A
...
_minA = 0.2
values "A" range [_minA, )
if _A < _minA then _A = _minA

Similarly, when distinct VALUES are defined, make sure the variable’s value in the script matches the parameter’s possibilities.

Range open intervals

As well as with closed interval range commands, the variables have to be calculated when using open intervals. When setting a parameter lower than its open interval minimum (or higher than its open interval maximum), Archicad sets the value to be above (or below) the open interval minimum/maximum with 10 mm (or 0.01 degrees for angle type parameters). To keep variables in sync with this method, use 0.01 to calculate the modified values.

_A = A
...
_minA = 0.2
values "A" range (_minA, )
if not(_A > _minA) then _A = _minA + 0.01     ! entering 0.205 would be allowed,
                                              ! it is just a default offset

Try to group commands in this order

  1. Set value ranges
  2. Calculate parameter connections
  3. Lock/hide parameters
  4. Handle UI jump buttons

Define all ranges

Define the valid value range for these parameters using the VALUES command:

  • length (probably not 0)
  • used in divisions (definitely not 0)
  • integer used for resolution (always greater than 3)
  • angle (with care about 90-degree increment validity)
  • integer used for pens
  • pen (disable background colors for foreground/contour pens)

Parameter name string comparison

Parameter names are case-insensitive, but string contents and string comparisons aren’t. Take care when using GLOB_MODPAR_NAME: always use parameter names with the same case.

if GLOB_MODPAR_NAME = "a" then ...  ! always false

Font type names

If you want to have a string parameter – named stFont in the sample – for setting the font type for a text, use the following value list definition to get a platform-independent sound solution. CUSTOM value is needed to deal with missing or unexpected font types.

dim _stFontNames[]
request ("FONTNAMES_LIST", "", _stFontNames)
values "stFont" _stFontNames, CUSTOM

This request disables background conversion for the objects which use it, so do this in a “Library Master” subtype. Every loaded library part with the same “stFont” parameter will automatically receive the same value list.

Use font parameters already defined in the Archicad Library or the built-in library’s Library Master objects.

UI script

Hierarchical tabpage IDs

To allow easier addition/removal of tabpages in a hierarchical structure, define the IDs of different levels on a logarithmic scale:

! =============================================================================
! Tabpage Structure Definition
! =============================================================================
TABID_ROOT = -1

TABID_LEVEL1_PAGE1             = 1000
    TABID_LEVEL2_PAGE1         = 1100
        TABID_LEVEL3_PAGE1     = 1110
        TABID_LEVEL3_PAGE2     = 1120
            TABID_LEVEL4_PAGE1 = 1121
            TABID_LEVEL4_PAGE2 = 1122
    TABID_LEVEL2_PAGE2         = 1200
TABID_LEVEL1_PAGE2             = 2000

Tabpages as subroutines

When a script creates multiple tabpages, organize them into separate subroutines:

ui_page TABID_PAGE1, TABID_ROOT, _stTabTitle1, _stTabIcon1
if gs_ui_current_page = TABID_PAGE1 then gosub "TabPage1"

Many tabpages might be easier to handle in a loop:

dict _tabs : i = 1
_tabs.id[i] = TABID_1 : _tabs.title[i]= _(`Page1`)  : _tabs.sub[i] = "TabPage1"
                        _tabs.parent[i]= TABID_ROOT : _tabs.icon[i] = "TabIcon1"
i = i + 1
_tabs.id[i] = TABID_2 : _tabs.title[i] = _(`Page2`) : _tabs.sub[i] = "TabPage2"
                        _tabs.parent[i]= TABID_1    : _tabs.icon[i] = "TabIcon2"
i = i + 1
...
for i = 1 to vardim1(_tabs.id)
    ui_page _tabs.id[i], _tabs.parent[i], _tabs.title[i], _tabs.icon[i]
    if gs_ui_current_page = _tabs.id[i] then gosub _tabs.sub[i]
next i

Use relative coordinates

Position UI elements relative to each other to allow easier layout changes, e.g. increasing the size of a picture or adding a new control in the middle of the dialog.

Outfields

Always define width/height.

Infields

The recommended style for ui_infield commands is:

  • position and size in first row
  • next lines start one ident deeper
  • comment method and picture IDs
  • write 0 or “” for parameters which are unused by the infield method
ui_infield{3} "bBool",         _xPos1, _yCurr - 4, _boolWdt, _inFldHgt,
    7,                         ! Checkbox with text
    "", 0, 0,
    0, 0, 0, 0,
    "", _(`Boolean Parameter Description`), 0,
    "", _(`Boolean Parameter Description`), 1
ui_infield{3} "iType",         _xPos2, _yCurr - 4, _boolWdt, _inFldHgt,
    1,                         ! List view
    2, 3, 1,                   ! 2: panel_type_hg_hl(1)
    62, 110, 36, 80,
    1, _(`Rectangular`),       DOOR_RECTANGULAR,
    2, _(`Curved Top`),        DOOR_CURVED_TOP,
    3, _(`Custom Door Leaf`),  DOOR_CUSTOM

Baseline alignment

To align the baseline of text in infields and outfields on both platforms without writing platform-dependent code, the best practice is a 15 px high outfield and a 19 px high infield aligned at the bottom.

DYIO = 4    ! infield-outfield y position difference
ui_outfield "Example Input",    _xStart,                            _yCurr,
                                _outFieldLength,                    _outfieldHeight
ui_infield  "exampleInput",     _xStart + _outFieldLength + _xGap,  _yCurr - DYIO,
                                _inFieldLength,                     _infieldHeight

Using images

When using an external image referred to by its file name, omit the file extension to avoid errors stemming from svg-tif conversion. Add the FILE_DEPENDENCE command to make sure they are saved in archive format with the object.
When using internal image referred to by its index, write a comment with its source file name on the same line.

Migration scripts

These scripts have a recommended form of separators and structures. Use the templates of Basic Technical Standards: FWM script, BWM script.