Files
isokit/docs/SCRIPTING.md
T

5.8 KiB

Scripting and prefabs

Scenes and prefabs are written in Rhai, a small scripting language embedded in isokit. Nothing is compiled: save the file and the Studio redraws.

A prefab

A prefab is an object type that can be placed in the Studio. It is two functions in a module that is listed in isokit.toml under prelude:

fn meta_kiosk() {                      // label, palette group and property form
    #{ label: "Gate kiosk", category: "Site", icon: "⌂", help: "…",
       props: [
         #{ key: "h", type: "float", def: 2.8, min: 2.2, max: 4.0, step: 0.1, label: "Height (m)" },
         #{ key: "boom", type: "bool", def: true, label: "Boom barrier" },
       ] }
}

fn prefab_kiosk(o) {                   // draw around (0, 0) in the object's own frame
    let s = sprite();                  // sprite ids default to the object id
    s.box(-1.4, -1.1, 1.4, 1.1, 0.0, o.p.h, "brick", #{ top: "roof3" });
}
  • Property types: float, int, bool, choice (with choices), material (a theme material, optionally with choices), text. Write def: for the default (default is a reserved word in Rhai).
  • o holds id, type, name, x, y, z, rot, p (the props with defaults filled in), pts, tag, info, level.
  • The engine sets the object's frame before calling the prefab. All coordinates are local: the origin is the object's position, and local x and y turn with rot. vis(), dv(), tag() and focus() use the frame too.
  • A prefab with the same name in the project overrides the built-in one.
  • If a prefab fails, the engine logs a warning for that object and still draws the rest.

Drawing API

Numbers can be ints or floats. Classes are CSS classes without the iso- prefix ("wall" becomes iso-wall). A material sets the face colour, which is shaded automatically by face direction.

Scene

Function
sprite() / sprite(id) / sprite(id, cls) new sprite (the unit of the depth sort) in the current layer
ground() the shared unsorted ground sprite
layer("ground" | "floor" | "main"), layer(name, #{sort, zfirst, cls, before}) where new sprites go
tag(x, y, z, text[, prio[, id[, kind]]]) label; kind "signtag" / "warntag" always shows
info(id, name[, detail]), info(id, #{name, detail, rows: [[k, v], …]}) tooltip
focus(x, y, z), bounds(x0, y0, x1, y1[, depth]), defs(svg), warn(msg) framing and extras
level(), rot(), cfg(), cfg(name), objects() inputs
vis(nx, ny) is a face with that outward normal turned to the viewer?
dv(dx, dy, dz) screen vector of a world vector (CSS motion)
along_x() should a mover run along x in this rotation?
hash(x), fmt(n), round1(x), fmin, fmax, clamp, px() helpers
rect(x0, y0, x1, y1), ellipse(cx, cy, a, b, n), rsegs(r), sides(x0, y0, x1, y1) geometry

Sprite methods (chainable)

Method
box(x0, y0, x1, y1, z0, z1, mat[, #{id, top, side, no_top, no_side}]) axis-aligned box
prism(pts, z0, z1, mat[, opts]) convex counter-clockwise footprint; z0/z1 one value or one per vertex
cyl(cx, cy, r, z0, z1, mat[, opts]), frust(cx, cy, r0, r1, z0, z1, mat[, sides[, opts]]) round things
slope(x0, x1, y0, y1, za, zb, th, mat) ramp / stair flight
face(pts3, mat[, #{id, two_sided}]) any planar polygon, shaded and culled by its normal (roof slopes, gables)
poly(polys3, cls[, id]) 3D polygons as one path (solar modules)
flat(poly or polys, z, cls[, id]) flat polygon(s); later ones cut holes
open(a, b, [[u0, u1, z0, z1], …], cls[, id]) windows / doors on the face a→b (outward normal on the right)
lines(segs, z, cls), disc(cx, cy, z, r, cls[, id]) markings
circle(x, y, z, r_px, cls[, dx, dy]), stem(x, y, z0, z1, w_px, cls) screen-space details (crowns, poles)
person(x, y, z[, cls]), sit(x, y, z, cls), head(x, y, z, r), led(x, y, z[, cls]) figures
puffs(x, y, z[, kind[, n, dur]]), fan(x, y, z, r) animated steam / smoke / fans
seq([[x, y, |q| …], …]) items drawn back to front by their anchor
group(cls[, css], |q| …), begin(cls[, css]) … end() <g> around parts (animation)
ext(x, y, z), ext_box(…) widen the sort box
raw([x, y, z,] svg) raw SVG ({uid} becomes the view's id)

Library helpers (stdlib/*.rhai): s.tree_at(x, y, r), s.car(x0, y0, x1, y1, cls), s.drive(dx, dy, dur, delay) … s.end(), cut_walls(...), win_list(...), floor_of(level), building_windows, gable_roof, flat_roof_kit.

Rules that keep the depth sort right

These are lessons from the CNX/Mooban views, and the engine relies on them:

  • One sprite per thing that can stand in front of another. Split long walls, fences and solar rows into pieces (≤ 30 m). One big sprite overlapping many others creates sort cycles (isokit check reports them).
  • Things under a cover belong in the cover's sprite: draw cars under a carport before its posts and roof (see prefab_carport).
  • Floors of cut-open storeys go in the floor layer (ground floor) or in the same sprite as the storey below. Start walls and furniture standing on them a little above the floor (+0.02 m).
  • Movers (drive) run along the axis the sort compares second (along_x()). Along the other axis they stay still.
  • Street furniture stays off drive areas, and pipe runs end on walls or supports.

Themes

theme.toml stacks on the built-in palette:

[palette.dark]
brick = "#8a4f3e"
[palette.light]
brick = "#b8664c"
[materials]
brick = "brick"          # class iso-brick, colour var(--iso-brick)
[rules]
".iso-svg .iso-sign2" = "fill: #ffcc00; stroke: none"

isokit check warns about any class in the drawing that has no style.