# KADCAD – instructions for AI agents

KADCAD (kadcad.cz) is a browser-based 3D planner for plots and gardens. It shows a real Czech/Slovak cadastral parcel with its terrain, **or a free rectangular plot of your own**, and lets you place a house, pool, walls, stairs, paving, garden elements and plants, then export drawings (DXF, IFC, PDF, ...).

This page tells an AI agent (Claude, ChatGPT, a voice bot, ...) how to drive the planner on behalf of a user. The application exposes a JavaScript API on the page: **`window.k3d`**.

## 1. How to connect

1. Open `https://kadcad.cz` (or the address the user gave you) in a browser you can control (Claude in Chrome, ChatGPT agent/Operator, Playwright, ...). The user may be logged in; guests can build and view everything, **exports require login**.
2. Wait until `typeof window.k3d === 'object'` (a second or two after load).
3. Call the functions below with your browser's "run JavaScript" tool, e.g. `await k3d.plot()`. Every function returns plain JSON (or a Promise of it). Errors are thrown as `Error` with a readable message; read it and fix the call.
4. After each change call `await k3d.screenshot()` (PNG data URL) and look at it, or `k3d.plot()` to read facts. Tell the user what you did in their language (usually Czech or Ukrainian).

If you cannot run JavaScript on the page, tell the user the page must be opened in an agent-capable browser.

## 2. Conventions

- Units: metres. Heights are **metres above sea level** (m n.m.).
- Local plot coordinates are written `[y, x]`. **y grows to the WEST, x grows to the SOUTH.** (Looking at the plan with north up: east is at small y, north is at small x.)
- Always read the plot first: `k3d.plot().bbox` gives `{yMin,yMax,xMin,xMax}` of the plot boundary; place objects inside it. `k3d.heightAt(y, x)` returns the terrain height at a point.
- Directions as words: `west, southwest, south, southeast, east, northeast, north, northwest`.
- Everything the user sees is saved automatically into the open project (every few seconds).

## 3. Quick start (free plot, 20 x 30 m, house, pool, terrace)

```js
await k3d.createFreePlot({ width: 20, length: 30 });            // flat rectangle
const b = k3d.plot().bbox;                                       // where is the plot?
const house = await k3d.addHouse({ width: 10, depth: 8, roof: 'gable',
  eaveHeight: 3, ridgeHeight: 6.2, frontSetback: 5, sideSetback: 3, frontDir: 'north', sideDir: 'west' });
await k3d.addPool({ y: b.yMin + 5, x: b.xMax - 7, len: 8, wid: 4, depth: 1.5 });
await k3d.addPaving({ preset: 'zamk6', ring: [[b.yMin+3,b.xMin+14],[b.yMin+10,b.xMin+14],[b.yMin+10,b.xMin+19],[b.yMin+3,b.xMin+19]] });
await k3d.view({ mode: '3d', azimuth: 40, elevation: 35 });
const png = await k3d.screenshot();                              // data:image/png;base64,...
```

## 4. API reference

### Reading
- `k3d.plot()` – current plot: `{loaded, id, name, free, areaM2, boundary, bbox, terrain, buildings[], objects{}, project, axes}`. `free` is `{w,d,slope,angle,z}` for a free plot, otherwise `null`. `buildings[]` has `{id,name,originLocal,baseZ,warning}` (a non-null `warning` means e.g. the house crosses the boundary).
- `k3d.heightAt(y, x)` – terrain height in m a.s.l.
- `k3d.catalog({ plants: 'dub' })` – allowed values: roofs, directions, wall materials, paving presets, garden element types, pool options; `plants` searches the plant database by Latin or Czech name.
- `k3d.list(kind)` – objects of a kind: `pool | paving | element | wall | stairs | pond | plant | hedge`, with their `uid` (plants and hedges are identified by `sd` instead).
- `k3d.getScene()` – the whole scene state as JSON (advanced; same data that is saved in the project).
- `k3d.help()` – this document as Markdown text.

### Plot
- `await k3d.createFreePlot({ width, length, slopePercent = 0, direction = 'west', baseZ = 300 })` – flat or evenly sloped rectangle, no cadastre. `width` = east-west size (3-300 m), `length` = north-south size (3-300 m). `slopePercent` 0-60: the terrain falls toward `direction` (word or degrees 0-359; 0 = west, 90 = south, 180 = east, 270 = north). `baseZ` = height at the plot centre. Calling it again while a free plot is open **resizes the same project** (houses and objects stay).
- `await k3d.loadParcel(ku, parcelNumber)` – real cadastral parcel, e.g. `('Černošice', '3830/53')`. Needs internet access to the national cadastre; can take 10-60 s.

### Buildings
- `await k3d.addHouse({ width, depth, roof, eaveHeight, ridgeHeight, pitchDeg, eaveOverhang, frontSetback, sideSetback, frontDir, sideDir, rotationDeg, baseZ, name, buildingId })` – rectangular house. `roof`: `flat | hip | gable`. For `flat`, `eaveHeight` is the top height above the floor; for `hip`/`gable`, `eaveHeight` and `ridgeHeight` are heights of the eaves and the ridge above the floor. Placement by distances: the house sits `frontSetback` m from the `frontDir` boundary and `sideSetback` m from the `sideDir` boundary. **Or** give exact placement: `at: { y, x }` (reference corner in local coordinates). `baseZ` omitted = automatic (highest terrain point under the house). Pass `buildingId` to modify an existing house. Returns `{id, baseZ, originLocal, warning}`.
- `await k3d.removeBuilding(id)` – deletes a house, returns the remaining list.

### Objects (all return the new object's id: `uid`, or `sd` for plants and hedges)
- `k3d.addPool({ y, x, ang, len, wid, depth, kind, rad, cop, water, ... })` – pool with its centre point at `[y,x]`, rotated `ang` degrees. `len` 1-40, `wid` 1-25, `depth` 0.3-4. `kind`: `std | bio`. `cop` = coping material, `water` = water colour (see `catalog().pool`).
- `k3d.addPaving({ preset, ring })` – paved area (terrace, path, driveway) with polygon `ring` `[[y,x],...]` (at least 3 points). `preset` from `catalog().paving` (e.g. `zamk6` interlocking paving, `beton`, `kamen`, `drevo`, `mlat`, `sterk`).
- `k3d.addWall({ pts, h, t, mat, closed, mode, cap })` – wall / fence along the polyline `pts` `[[y,x],...]`; `h` height, `t` thickness (m), `mat` from `catalog().wallMaterials`; `closed: true` closes the loop; `mode: 'follow'` follows terrain, `'level'` stays horizontal.
- `k3d.addStairs({ y1, x1, y2, x2, w, h, rise })` – stairs from point 1 to point 2, width `w`.
- `k3d.addElement({ type, y, x, ang, w, d, h, col })` – garden element, `type` from `catalog().elements` (garden shed, raised bed, sandbox, boulder, swing frame, kitchen, lamp, block).
- `k3d.addPond({ ring, depth, bank, water })` – pond with polygon `ring`.
- `k3d.addPlant({ species, y, x, height, width })` – a tree/shrub; `species` is the Latin name from `catalog({plants:'...'})`.
- `k3d.addHedge({ species, pts, height, width, closed })` – hedge along a polyline.
- `k3d.remove(kind, id)` – delete one object by `uid` (or `sd`). `k3d.clear(kind)` – delete all of a kind.

### View, output
- `k3d.view({ mode: '3d'|'2d', azimuth, elevation, distance })` – camera. `azimuth` degrees, `elevation` 0-90 degrees above the horizon, `distance` metres.
- `await k3d.screenshot({ delayMs: 400 })` – PNG data URL of the 3D view.
- `k3d.exportUrl('dxf')` – URL of the terrain export of the current plot; formats `xyz`, `txt`, `dxf`, `xml` (LandXML). Download it with the user's logged-in browser session; exports need an account. Plan exports of the designed objects (DXF/IFC/PDF) are available in the app's Export menu.
- `await k3d.setScene(state)` – replace the whole scene state with a JSON obtained from `getScene()` (undo helper / advanced).

## 5. Good practice for a voice assistant

1. Ask the user for the plot size (or cadastral parcel) first, then the house size and roof, then extras. Do not guess dimensions silently; state your assumptions.
2. Create things in this order: plot, house, paved areas, pool, walls/fence, stairs, plants.
3. After every 2-3 changes take a screenshot, check it, and describe the result briefly. If `plot().buildings[].warning` is set, fix it (smaller house or setbacks) before continuing.
4. Keep objects inside `plot().bbox`; keep a distance of at least 1.5 m between a pool and a wall, and 3 m between the house and the boundary unless the user asks otherwise.
5. Never delete or `clear` anything the user did not ask about. Use `list(kind)` to show what exists.
6. If a call throws, read the message, correct the argument and retry once; if it still fails, tell the user.

## 6. Notes and limits

- This API drives the page the user (or you) opened; it has no separate server session. Use one tab.
- Free plots are flat or evenly sloped planes with no cadastre, neighbours, aerial photo or utilities layers.
- Terrain from the real cadastre is loaded from public data and may take time; the planner shows a loading indicator.
- Calculations (areas, prices of paving, plant lists) are indicative.
- Contact: the operator of kadcad.cz (Barkov Studio, barkov.studio).
