Commit Graph
16 Commits
Author SHA1 Message Date
grabowski 2810477f1b Data centres: multi-storey shell, racks and pods, cooling, fire safety, power
Deploy web Studio / deploy (push) Successful in 1m4s
datacentre.rhai (palette groups Data centre, Cooling, Fire safety, Power):

- dc_building: multi-storey shell (storeys of storey_h), walls in pieces,
  slabs, roof with parapet, louvres, front door and loading dock; levels
  cut it open storey by storey
- raised_floor; rack_row and dc_pod (cabinets by U, width, depth,
  equipment, fill, doors, LEDs; in-row coolers; cold / hot aisle
  containment with vent tiles, roof, end doors, ceiling duct, airflow);
  dc_cage for colocation
- crah, chiller (air-cooled, dry, adiabatic), cooling_tower, pipe_run
- smoke_detectors (grid + coverage), fire_pipe (VESDA, sprinkler, gas),
  gas_bank (Novec 1230, FM-200, Inergen sized for its halls), fire_device
  (panels, call points, abort switches, beacons, extinguishers)
- power_cab (UPS, battery, PDU, switchgear, RPP), busway, cable_tray,
  transformer
- tooltips sum IT load, kW/m2, cooling redundancy per storey, UPS load,
  battery runtime, plant cooling and agent quantity (warning when short)

Engine and stdlib:

- part_hidden(o) in house.rhai: parts on closed storeys inside a
  dc_building are not drawn (they only made sort cycles under the slab
  above); house parts, furniture and people use it. Existing examples
  render byte-identical.
- overhead services sort in their own "ceiling" layer with tight boxes
- sort records which sprites each broken cycle involved; isokit check
  names them (Rendered.cycle_at)

examples/datacentre (three storeys and a roof plant), Data centre demo in
the web Studio, tests, SCRIPTING/README/ROADMAP, MCP instructions.
2026-10-27 17:08:39 +07:00
grabowski 540b37020c Object tree: groups and the drawing order
Deploy web Studio / deploy (push) Successful in 1m5s
Groups: objects of type "group" draw nothing; members carry the group's id
in a new `group` field (groups nest). A hidden group hides everything in it
(Doc::hidden_in_groups, skipped by the driver), a locked one locks it in
the Studio. Removing a group moves its members up into its parent group.
isokit check warns about members of missing groups. New `order` op: the
whole object order at once (each id once), one undo step.

Studio object list as a tree in the document order, front first: each
group sits just after its members, so it draws as one block, and ground
pieces higher in the list lie on top. Groups fold open and shut; every row
has an eye and a lock (shown on hover or when set); a click on a group
selects all of it, so a drag on the map moves it all. Group / Ungroup
(Ctrl+G, Ctrl+Shift+G), Order up/down (Ctrl+] Ctrl+[, with Shift to the
front / back), drag rows above, below or into a group. Group panel: name,
zoom, ungroup, to front / back, lock, hide, delete with contents.

MCP: group field in the object schema, order op, guidance.

Tests: Rust 81 (ops: groups, order, removal; driver: nested hidden
groups), Studio 131, web 100.
2026-10-25 18:06:04 +07:00
grabowski 5ec164a633 Routes: movers along waypoints (roadmap 8)
Deploy web Studio / deploy (push) Successful in 1m4s
New route prefab (routes.rhai): cars, buses, motorbikes, cyclists, people,
deer or elephants move along waypoints, round a loop or there and back.
Each way keeps to the left of the line (or right), corners are rounded
into pieces of at most 45 degrees; there-and-back routes become loops with
U-turns, so the two directions never share a strip.

Engine: Prim::Travel / sprite.begin_travel / script travel(a, b, dur,
windows) ... end(): the mover is drawn at a piece's start and slides to
its end while it is on that piece, hidden otherwise. The renderer writes
keyframes per piece (named by a hash of their content) into one <style>
per view. Each piece is its own sprite with the lane strip as its sort
extent (ext_reset), mitred at the corners, so movers pass in front of and
behind trees, lamps and houses. Movers that look alike and never meet on
a piece share one drawing. iso-travel pauses (not hides) in still,
reduced-motion and ghosted views.

Studio: meta shape "path" draws point by point (snapping like runs) and
saves one object with pts; ending on the first point closes the loop.
MCP: pts in the object schema, route guidance.

Demos: streets gets traffic round the block, a bus, motorbikes, cyclists
and strolling people (replacing the one-axis cars); country gets cars on
the lane.

Tests: Rust 79 (each mover on exactly one piece at every moment, in every
rotation), Studio 125, web 99.
2026-10-25 17:52:17 +07:00
grabowski ccfd619a99 Viewer: stay sharp while zooming
Deploy web Studio / deploy (push) Successful in 1m5s
Pan/zoom gestures move the drawing as a CSS-transformed layer, a picture
at the scale last drawn. A long touchpad pinch showed an ever more
stretched, blurry view (3.6x at the end of a 1.2 s pinch), and every zoom
ended with a blur-then-snap 180 ms after its last step.

Now the view is drawn anew during the gesture once the picture is 30% off
in scale (at most every 120 ms; draw() keeps the gesture state), and at its
true scale 90 ms after the last step. Measured in Brave at 1.5x pixel
density: sharp ~130 ms instead of ~240 ms after a pinch, crisp on the way.
Cost at 6x CPU slowdown: 11.4 -> 14.6 ms per wheel step in the Studio,
9.1 -> 10.5 on a page, still no frame over 50 ms.

check_web: a long pinch is drawn anew on the way and sharp within 150 ms.
2026-10-25 16:53:10 +07:00
grabowski cddf834e56 check_web: the country demo has exactly 7 shaded groups and 13 grazing animals 2026-10-25 13:59:46 +07:00
grabowski e840d36000 Wildlife and nature: animals, beehives, bushes, flowers, rocks, logs
Deploy web Studio / deploy (push) Successful in 1m5s
New stdlib module wildlife.rhai:
- Animals (new palette category): Deer (stag with antlers, does, fawns),
  Wild boar, Fox, Elephant (bull with tusks, cow, calf), Rabbits. Low-poly
  solids from faces (loft, tube), parts drawn back to front by depth, faces
  edged in their own shade (class beast). Herds scatter round a spot, each
  animal its own sprite facing about the object's rotation. Grazing
  animals switch between two head poses (iso-pose2 groups), elephants curl
  their trunks, rabbits hop.
- Birds and Butterflies as flocks: swim() takes a shape (bird, fly, bee);
  wings flap out of step. Fish output unchanged.
- Beehives on stands with supers, lids, entrances and bees flying round;
  Bush (round, flowering, berries); Flowers (bed in rows or meadow); Rocks;
  Log or stump. g-meadow and g-soil ground materials.
- polyline() takes 3D points as documented (antlers).

Theme colours for dark and light, viewer CSS for poses, flapping, hopping.
examples/country: elephants in a paddock with a cut corner, deer at a
forest edge, boar, fox, rabbits, an apiary by a meadow, a pond, a lane,
fence and hedgerow at angles; Country demo in the web Studio and deploy.
MCP guidance, README, SCRIPTING, ROADMAP.

Tests: Rust 78, Studio 122; existing examples draw the same (only the
embedded theme CSS grew).
2026-10-25 13:57:23 +07:00
grabowski c91d63663b Streets: sidewalks, road curves, junctions and roundabouts
Deploy web Studio / deploy (push) Successful in 4m31s
Roads take sidewalks (none, both, left, right): 12 cm high paving with
kerb faces drawn where they face the viewer. Road curve (radius, angle,
left/right) carries a street round a bend; centre lines are true arcs
when cut and offset, so curve joints are seamless. A road ending on
another road's centre line opens that road's kerb and sidewalk (T
junction); two roads ending at one corner close it. Roundabout: ring
radius and width, one or two lanes, up to four arms with flared kerbs,
splitter islands, give-way lines on the entry side (traffic on the left
or right) and a central island with apron.

Studio: a road run drawn corner by corner gets a curve in each corner
(prefab meta `corner`). Tooltips list the streets each one meets.
Engine: open `polyline` primitive (Prim::Poly open) so dashes run on
round curves. examples/streets and a Streets demo in the web Studio;
MCP guidance, docs. ROADMAP: backlog 5-7 done; multi-select and the
hosted copy marked done.

Tests: Rust 72, Studio 113, web 95.
2026-10-25 12:32:52 +07:00
grabowski 2442966743 Reduced motion starts the view still instead of locking animation off
Deploy web Studio / deploy (push) Successful in 1m3s
With Windows "Animation effects" off, browsers report prefers-reduced-motion, and the viewer
turned every animation off and hid the animation button, so presses, belts and robot arms
stood still in Brave and Firefox with no way to start them. Now that preference only picks
the starting mode ("off"); the button stays (its tooltip says why it is off) and the
viewer's choice wins and is remembered. The CSS still-picture rules apply only until the
viewer turns animation on (.iso-anim-on).

Also: switching light -> off -> smooth redraws the scene so the animations paused by script
run again (they ignore CSS play states).

Checks: web 93/93 (reduced motion: starts still, one click runs it, kept after reload),
Studio 109/109, 70 Rust tests. Tried in real Brave (system setting) and Firefox.
2026-10-25 11:48:50 +07:00
grabowski f49798583e Robot arms pass goods from belt to belt; native beam primitive
Deploy web Studio / deploy (push) Successful in 1m3s
- Robot arm (category Industry): picks goods off the end of the nearest conveyor within its reach
  and puts them on the start of another conveyor within reach, or on a pallet a quarter turn on
  (pick / put: auto or a side). One move per item coming in (cycle 0) or a set cycle; waits while
  the line before it stands. The belt it feeds carries its goods at its pace and starts its loop
  when they land (feeds carry a time offset `ph`), so arm and belts stay in step.
- Drawn as a 16-pose flipbook: two-link inverse kinematics per pose, each pose shown for a
  sixteenth of the cycle by a stepped CSS animation (iso-flip; pose 0 alone when unanimated).
  Poses pointing away from the viewer are drawn before the base, the others after it.
- New sprite method beam(a, b, n, w, mat): a square bar between two 3D points, six faces culled
  and shaded like face(). Native, so the factory demo renders in 4.7 ms instead of 18.8 ms.
- Factory demo: a palletiser at the end of the carton line, a loading robot passing drums from the
  incline onto the loading belt into the truck. MCP instructions on robot arms.

Checks: 70 Rust tests (arms and belts in step, waiting arm, beam faces from every side),
Studio 109/109, web 90/90.
2026-10-24 22:05:46 +07:00
grabowski 18dd324543 Conveyors and producers: goods flow from presses down the belts
Deploy web Studio / deploy (push) Successful in 1m4s
- Producer (category Industry): a press making cartons, crates, drums, sacks or parts at a set
  rate per minute; its ram goes down once per item, a green / red light shows running / stopped.
- Conveyor: drawn the way it runs (new prefab meta `directed`: the Studio turns the piece from
  where the drag starts), belt width, height, rise (inclines), speed, goods and spacing; legs,
  rails and a drive motor. Corners join without overlap (the receiving belt runs through, the
  feeding one stops at its side).
- A conveyor starting on a producer's output side carries its goods at its rate; one starting at
  another's end carries on with what that one carries. The gap is speed / rate. A stopped press or
  belt stops the goods after it. Tooltips show speed, rate and source.
- The goods ride a group moving one gap per loop (no reordering), first fading in, last out.
- Script API: world_of(o, u, v), info_row(key, value); objects_on falls back to half of p.d.
- Theme: industry colours, LED colours (the marker's LED had none).
- examples/factory + Factory demo in the web Studio; MCP instructions on production lines.

Checks: 68 Rust tests (conveyor joints, feed and spacing, every view of the factory demo),
Studio 108/108 (draw direction, feed from a producer, rate change), web 89/89.
2026-10-24 21:46:10 +07:00
grabowski a202283a60 Web performance: light animation, gestures on a compositor layer, no forced layouts
Deploy web Studio / deploy (push) Successful in 3m46s
Measured with the new tools/bench_web.py on the web Studio's house demo, CPU slowed 6x:

- Idle: running CSS animations repainted the whole drawing every frame (99% of the main
  thread in a window). Animations now step on about ten times a second by default
  ("light", 5% idle); the new toolbar button switches light / smooth / off and the browser
  remembers it; [build] animation sets the default for built pages; view.animation().
  Pan and zoom gestures pause them. Status LEDs pause off-screen and with the rest now.
- Pan 28 -> 10 ms, zoom 69 -> 11 ms per step: gestures move the drawing as one compositor
  layer (transform) and set the viewBox once they end; --iso-k is set on the tags group
  only and only when it changes; the stage box is read once per frame.
- Hover 22 -> 15 ms per move: the tooltip is placed once per frame on its own layer and
  measured only when its text changes; the Studio's x/y readout updates ten times a second
  in a contained box; hover work in the Studio runs once per frame.
- Long object lists skip layout of rows out of sight (content-visibility).
- WASM built with opt-level 3: engine render 11.7 -> 9.4 ms for 10% more download.

Checks: Studio 102/102 (animation modes, pan as a layer, contained readout), web 87/87.
2026-10-24 20:46:35 +07:00
grabowski 95ea4a8512 Build your own house: walls, windows, doors, floors, roof, stairs, furniture
Deploy web Studio / deploy (push) Successful in 3m27s
House parts (stdlib isokit/house): house_wall drawn along a line, with ends
that adapt at corners, T-junctions and straight runs so no two walls
overlap; window and door that sit on a wall and cut its opening (door
styles panel, glass, double, sliding, opening; swing and hinge by
rotation); floor (boards, tiles, carpet, stone); roof (hip, gable, flat,
optional solar); stairs.

Furniture (stdlib isokit/furniture): chair, sofa / armchair, table with
chairs, desk with monitor and chair, TV, bed, cupboard, bookshelf, kitchen
run, bathtub, shower, toilet, washbasin, plant, rug, floor lamp.

Cut-open levels: on gf / 1f the walls in front of a room are cut low
(rooms are found by their floors), the roof becomes an outline on the
floor, and storeys above are left out. A part's storey comes from z
(storey_h, default 3 m).

Scripting: objects_of, objects_at, objects_on, mount_of, mounted, frame,
to_world, to_local, local_of. Prefab meta: shape (line / rect), mount,
storey.

Studio: drag to draw lines and rectangles (walls, roads, fences, hedges,
floors, areas, buildings, roofs). The Room tool draws a floor plus walls,
without doubling walls that already stand on an edge. Windows and doors
snap onto walls with a hover preview, and they move, turn, copy and
delete with their wall in one undo step. New parts land on the storey
the view shows. Palette categories collapse, and the palette scrolls
apart from the object list.

The MCP instructions explain house building, and list_prefabs shows
sits_on. examples/house is a furnished bungalow; the web Studio gets a
House demo button. Tests: wall corners and junctions, zero depth-sort
cycles in every view, cut levels, and the house demo in every view;
check_studio covers the room / window / door / storey flow, check_web
the House demo.
2026-10-24 17:39:04 +07:00
grabowski 41554801ea agent window: a site's relay stands in for the endpoint it relays
Deploy web Studio / deploy (push) Successful in 3m53s
On isokit.buildfor.life, entering https://inference.p0.contact/v1 in the
agent window's settings made the page call the inference server directly,
which browsers refuse (its nginx answers the CORS preflight with 401):
"The browser could not reach ... (Failed to fetch)".

- the relay meta names its upstream (data-upstream; build_web.py
  --agent-upstream, the deploy passes the inference server): that URL now
  goes through the relay and is saved as "follow the site's default"
- an unreachable endpoint says what to do: the relay's URL on a site with
  one, else isokit studio or isokit.buildfor.life
- check_web.py: relayed upstream and the unreachable message
2026-10-23 19:51:29 +07:00
grabowski 1a1b62d475 studio: agent window, a chat with a model that edits the scene
Deploy web Studio / deploy (push) Successful in 3m3s
✦ Agent in the Studio's top bar opens a floating chat window. The model
(any OpenAI-compatible endpoint with tool calling; default B4L's inference
server, https://inference.p0.contact/v1) gets the 15 MCP tools plus two page
tools (select_object, set_view) and the Studio's state (selection, level,
rotation) in its system prompt. Answers and reasoning stream in; tool calls
show as rows with arguments and results; object ids link to the object;
Stop ends a turn; Undo takes the agent's edits back. The window moves by
its title bar and resizes at its top-left corner.

- studio/agent.js: shared by both front ends (waits for studio.js's
  isokit:studio event); MCP over /mcp or isokit.mcp in the web Studio
- isokit studio: /api/agent/{config,models,chat} relay the calls on their
  own threads (ureq, chunked and flushed per read), so the key stays on the
  machine and the endpoint needs no CORS; settings from ISOKIT_LLM_URL /
  _KEY / _MODEL or agent.json in the user's config folder
- web Studio: calls the endpoint from the page with a key kept in the
  browser; build_web.py --agent-url names a same-site relay as default; the
  deploy builds with /llm/v1, which deploy/Caddyfile forwards to the
  inference server (its nginx refuses the CORS preflight)
- tools/mock_llm.py + tools/agent_checks.py: the agent window checks in both
  check_studio.py and check_web.py
2026-10-23 19:35:53 +07:00
grabowski c647f39059 studio: version text in the status bar, favicon
Deploy web Studio / deploy (push) Successful in 3m15s
The status bar ends with "isokit <engine version>"; the web build adds
the commit (linked on Gitea), its date as a tooltip and a "+" when built
from a tree with uncommitted changes (build_web.py writes a
<meta name="isokit-build"> from GITHUB_SHA or git). /api/meta now carries
"engine". studio/favicon.svg (isometric cube in the accent green) is
served by `isokit studio` (also on /view) and copied into the web build.
check_web.py and check_studio.py check both.
2026-10-23 17:08:48 +07:00
grabowski 5eb60d2a0c isokit: web Studio (engine as WASM, projects in the browser or a live folder)
- isokit-wasm: wasm-bindgen build of the engine; a project in memory behind
  the same JSON API as `isokit studio`, plus the MCP tools
- isokit-project: Store trait (FsStore folder, MemStore memory) under a
  generic Workspace; Project::from_files; shared Studio API module (api.rs)
  used by serve.rs and the WASM build; web_time for clocks in the browser
- studio.js runs on a pluggable backend (window.IsoKitBackend); no Studio
  keys while a dialog is open
- web/: projects in IndexedDB, new/demo/delete, zip export/import (stored and
  deflated), folder import by drop or picker, folders opened live via File
  System Access with outside edits followed, code editor, page build and
  preview, two tabs in step, isokit.mcp() and WebMCP registration
- tools/build_web.py -> dist/web (static), tools/check_web.py (44 checks)
2026-10-22 17:00:42 +07:00