Overview and conventions
Four things worth knowing before calling a command.
getState().URL parametersSpecifying data, invite links, relay connections, view state.Coordinate systems
There are two systems. Mix them up and no error is raised — you just touch the wrong place.
| System | Used for |
|---|---|
| Real coordinates (real, m) | Measurement values, the camera API, click coordinates, finding pins, exports |
| Aligned (scene) frame | selectBox's min/max, alignedBounds, querySelection's return value, section extent |
The transform is:
scene = Rz(-alignYaw) · (real - offset)offset and alignYaw are available from getState().frame and queryCrs. They're decided once the first source is loaded and stay fixed until every source is removed.
Why two systems — a plane-rectangular-coordinate-system point cloud has coordinate values on the order of tens of thousands of meters, and loading those onto the GPU as raw float32 makes points jitter. The COPC header's cube center is subtracted as a fixed offset and normalized near the origin before rendering.
When building a box in the aligned frame, always start from alignedBounds.
const { alignedBounds: b } = oniyanma.getState() // [minX,minY,minZ, maxX,maxY,maxZ]
const zTop = b[5] - (b[5] - b[2]) * 0.2 // the top 20%
await oniyanma.execute('selectBox', { min: [b[0], b[1], zTop], max: [b[3], b[4], b[5]] })CRS is recorded only
The coordinate reference system (EPSG) and vertical datum (orthometric = elevation / ellipsoidal = ellipsoidal height / unknown) can be checked via queryCrs, but it is never reprojected.
In Japan the geoid height runs 30–40 m, so check the datum before reporting an elevation value.
Resident basis
Anything that returns a point count is resident-based unless noted otherwise. It's "the number of points satisfying the condition within the LOD sample currently on screen", not a scan of every point in the source.
Applies to: querySelection · queryBoxCount · queryElevation · queryClasses · queryLayers · exportSelection · the wouldAffect returned by the confirmation gate
Move closer and LOD gets finer, so the count grows even for the same range. Usable for judging order of magnitude, but don't treat it as an exact point count.
Surfaces
The read/write split, per entry point.
| Surface | What can run |
|---|---|
all (default) | All 61 commands |
read | Only the 18 readonly commands |
Calling a write command from a read entry point is refused at execution time (trimming the tool definitions alone wouldn't stop it if the name is already known). The refusal message includes the list of commands that are available. → The command layer as a design
Actor
Injected on every dispatch, and edit commands bake it into the Command log.
kind | Source | Confirmation gate |
|---|---|---|
human | UI / ⌘K / shortcuts | Doesn't apply |
ai | The AI console (name holds the model name, prompt the start of the instruction) | Applies |
api | Via window.oniyanma / MCP | Applies |
Return values and error conventions
Success. A command with only side effects returns { ok: true }. One that returns a value returns a JSON object. When something "couldn't be done, but it's not an exception", it returns { ok: false } (e.g. zoomToSelection with no selection).
Dry run. When a machine calls a destructive command without confirm, it gets back { preview: true, command, wouldAffect, note }. Nothing was executed.
Errors. Thrown as an exception (a Promise rejection). Messages are written so "what to do next" is readable directly.
No finding type "crack" (defined: damage). You can create one with defineFindingTypedeleteSelection is a write command and can't run on a read-only surface.
This entry point can only query state (available: getCamera, measureDistance, …)Rather than ending with just "not found", the cause and an alternative are attached, so the AI can pick its next move directly from the message.
Async
executeCommand returns a Promise. await it even for commands that finish synchronously (loadData / addData / addSurface / setBackend are genuinely asynchronous).