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(withchoices),material(a theme material, optionally withchoices),text. Writedef:for the default (defaultis a reserved word in Rhai). oholdsid, 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()andfocus()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 checkreports 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
floorlayer (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.