# Agent building workflow Source: https://docs.ploterr.com/floorkit/agents/agent-building-workflow Validate an agent's building JSON, open an editable project, apply a door proposal, and save the design without losing history. An agent proposes data. Floorkit checks that data and builds the geometry. Your app then lets people edit the same project. This guide shows that boundary without choosing an AI provider. ## Before you start * Complete the [quickstart](/floorkit/quickstart) in a browser app on `localhost`. * Your app supplies the AI model, parses its response as JSON, and treats the result as `unknown`. * Give the agent the [building model contract](/floorkit/concepts/building-model) and the requested dimensions, in metres. Start the geometry kernel once when your app starts: ```typescript theme={null} import { assertValidBuildingModel, Floorkit, ProjectManager, validateBuildingModel, } from '@opengeometry/floorkit'; import wasmURL from 'opengeometry/opengeometry_bg.wasm?url'; export async function initialize(): Promise { await new Floorkit().setupOpenGeometry(wasmURL); } ``` A deployed app also needs a valid key before views and exports. See [Licensing](/floorkit/licensing). ## Ask for building data For the room in the quickstart, give your agent a request such as: ```text theme={null} Describe a room with a six-by-four-metre wall centre line and a three-metre storey height. Use metre units, Y up, and [x, z] plan points. Do not repeat the first point of a closed wall. Use building ID "building", storey ID "ground", and wall ID "outer-walls". Add a door with ID "front-door", width 0.9 m, height 2.1 m, and offset 1.5 m along the wall. Return building JSON using the Floorkit BuildingModel contract, not meshes or SVG. ``` The [quickstart](/floorkit/quickstart) supplies a complete valid document for this request. Use it as a known example before connecting your model provider. ## Return validation feedback Validate the parsed result. Send the returned issues to your agent if the document is invalid. ```typescript theme={null} export function checkCandidate(input: unknown) { const result = validateBuildingModel(input); return { ok: result.ok, errors: result.errors, warnings: result.warnings, }; } ``` Each issue has a `path`, a `code`, and a `message`. For example, an opening that extends past its wall is rejected. Ask the agent to correct the reported fields and validate again. Limit retries in your app and show unresolved errors to the person. Do not accept a TypeScript cast as validation. Do not invent missing IDs, change units, or remove an invalid element silently to make the document pass. ## Open the proposed project After initialization, this function checks unknown input and creates a project with a plan of its first storey: ```typescript theme={null} export function openCandidate(input: unknown): ProjectManager { const buildingModel = assertValidBuildingModel(input); const storey = buildingModel.buildings[0]?.storeys[0]; if (!storey) throw new Error('The proposal needs a building with a storey.'); return new ProjectManager({ schema: 'floorkit.project', version: 3, buildingModel, views: [ { id: 'agent-plan', name: 'Agent plan', kind: 'plan', storeyId: storey.id, cutHeightAboveStorey: 1.2, }, ], paper: { version: 1, unitSystem: 'metric', sheets: [] }, }); } ``` A document that passes validation can still fail to build its geometry. Catch the error from `openCandidate` and keep the previous project open until the new one succeeds. A `BmBuildError` includes a path and a code; other build failures can be ordinary errors. Add `manager.root` to your Three.js scene. For a plan that people can edit, call `await manager.mountPlanEditor(container, 'agent-plan')`. The container needs a width and height. See [Add a plan editor](/floorkit/guides/add-a-plan-editor). ## Apply a small edit proposal Keep the current manager when the user asks to move a door. Your app chooses the door target from the current model or selection. The agent proposes only the new offset: ```json theme={null} { "offset": 2.5 } ``` Capture `manager.revision` before you send the request to the agent. Pass that number back when the response arrives. The host checks it before applying the edit: ```typescript theme={null} export interface DoorTarget { buildingId: string; storeyId: string; wallId: string; openingId: string; } export function applyDoorProposal( manager: ProjectManager, target: DoorTarget, input: unknown, expectedRevision: number, ): number { if (manager.revision !== expectedRevision) { throw new Error('The project changed. Request a new proposal from the current state.'); } if ( typeof input !== 'object' || input === null || !('offset' in input) || typeof input.offset !== 'number' || !Number.isFinite(input.offset) || Object.keys(input).length !== 1 ) { throw new Error('The proposal must contain only a finite numeric offset.'); } return manager.apply({ kind: 'move-hosted-opening', ...target, offset: input.offset, }); } ``` For the quickstart model, the target is `building`, `ground`, `outer-walls`, and `front-door`. The offset is measured in metres along that wall from its first point. Floorkit checks that the door fits between the joints of the wall and does not overlap another opening. A refused move has an `opening-does-not-fit` issue with the offsets where the door fits. If a door of the opened model does not fit at its saved offset, `manager.session.currentBuild.warnings` has an `opening-does-not-fit` warning with the same numbers. Give the agent its `openingFit.nearestOffset`; do not compute the fit in your app. The revision comparison above belongs to your host application: `ProjectManager.apply` takes one command, not an expected revision argument. There is no `await` between the comparison and the edit. If you use `BuildingSession` directly, its `apply` takes the originating session revision. See [Editing and history](/floorkit/concepts/editing-and-history#the-revision-number). An accepted proposal is one step in the manager's history. Use `manager.undo()` and `manager.redo()` for that same history. A rejected command leaves the saved model unchanged. Use the error's path and code when available; do not replace the whole project to hide a rejected edit. ## Save and reopen Save on a user action or a timer, rather than after every frame: ```typescript theme={null} export function saveDesign(manager: ProjectManager): string { return manager.save(); } export function reopenDesign(text: string): ProjectManager { return ProjectManager.open(text); } ``` Your app stores the text. Reopening needs the kernel to be initialized and starts a new undo history. Before closing a project, remove its root from your scene and call `manager.dispose()`. To produce drawings, [compose a sheet](/floorkit/guides/compose-a-sheet) and [export a PDF](/floorkit/guides/export-a-pdf). A GLB or PDF does not preserve the editable building document; keep the project file too. ## Let people continue editing The plan and 3D editors use the same project as the agent. Both change the building through commands, so the person can inspect, adjust, undo, and save the result. This existing example shows the plan editor you can connect to the workflow. It does not call an AI model. For agent documentation access, see [Build with agents](/floorkit/agents/index). For other application routes, see [What you can build](/floorkit/use-cases). # Build with agents Source: https://docs.ploterr.com/floorkit/agents/index Choose how to use Floorkit with a coding agent or build an agent that generates and edits building data inside your app. Floorkit is a Ploterr product powered by OpenGeometry. People and agents can work with the same saved building data. Your app supplies the AI model and the user interface. Choose the path that matches your task. Give an agent the documentation and instructions to integrate the SDK into your app. Validate an agent's proposed building data, apply edits, and let people continue working. ## Give a coding agent the docs The [coding-agent guide](/floorkit/agents/use-a-coding-agent) connects the public package, documentation index, Markdown pages, examples, and installed API declarations. The SDK source repository is not required. For an app that compares building options on a parcel, use the [Site massing playbook](/floorkit/playbooks/site-massing). It includes a worked study, a copyable agent brief and the expected floor-area and zoning results. Follow [documentation MCP setup](/floorkit/agents/use-a-coding-agent#connect-documentation-search-through-mcp) to search and retrieve the docs. The server does not edit a building. Copy the [starting instructions](/floorkit/agents/use-a-coding-agent#a-starting-instruction-for-a-coding-agent) and add your app requirements. ## Build an agent into your app An agent proposes building JSON or a supported edit. Floorkit validates the data and builds the geometry. People then inspect, edit, undo, and save the same project. Follow [Agent building workflow](/floorkit/agents/agent-building-workflow) for validation, stale proposals, commands, and saving. Your app handles inference and design requirements; Floorkit does not generate a floor plan from a prompt by itself. ## Choose the right package Use **Floorkit** for building elements, architectural editors, saved projects, and sheets. Use the **[OpenGeometry kernel](https://docs.opengeometry.io/OpenGeometry/what-is-opengeometry)** for lower-level solid geometry in your own modelling system. Each package has its own API and license. ## Before you deploy Local development and non-browser runs need no key. Every deployed hostname needs a valid Floorkit key, including a non-commercial demo. See [Licensing](/floorkit/licensing). Choose other application routes in [What you can build](/floorkit/use-cases). Check [current exports](/floorkit/concepts/exports) before promising an output to your users. # Use a coding agent Source: https://docs.ploterr.com/floorkit/agents/use-a-coding-agent Give a coding agent Floorkit documentation, MCP search, Markdown pages, and reusable instructions for integrating the public SDK. A coding agent helps you write the application that uses Floorkit. Give it your requirements and the public documentation. Your app chooses its UI framework, storage, and AI provider. For an agent that proposes building data inside your app, follow [Agent building workflow](/floorkit/agents/agent-building-workflow). [Build with agents](/floorkit/agents/index) explains both paths. ## Give a coding agent the docs Use these public resources. No access to the SDK source repository is needed. | Resource | When to use it | | - | - | | [Documentation index](https://docs.ploterr.com/llms.txt) | Find the guide or API for a task | | [Quickstart as Markdown](https://docs.ploterr.com/floorkit/quickstart.md) | Create a browser app from an empty folder | | [API overview as Markdown](https://docs.ploterr.com/floorkit/api/overview.md) | Find constructors, commands, and related types | | [Live examples](https://floorkit-examples.ploterr.com/floorkit) | See the result of a guide and inspect its code | | [Example index](https://floorkit-examples.ploterr.com/floorkit/llms.txt) | Find example descriptions and Markdown source pages | | [Full docs bundle](https://docs.ploterr.com/llms-full.txt) | Supply the complete docs when your tool needs them | Append `.md` to a documentation page to read its Markdown version. Start with the index and fetch the pages you need. The full bundle is optional. ### Connect documentation search through MCP In an MCP client, add an HTTP server with this address: ```text theme={null} https://docs.ploterr.com/mcp ``` The server searches and retrieves the public Floorkit documentation. Ask it for the guide or API that matches your task. This is a documentation server. It does not open, edit, save, or export your building. An agent inside your app uses the SDK through the tools that your app provides. ### A starting instruction for a coding agent Give the agent your app requirements and these instructions: ```text theme={null} Use the public @opengeometry/floorkit package. Read https://docs.ploterr.com/llms.txt and the relevant guides before writing code. Check my installed package version and its index.d.ts against the examples. Use ProjectManager for a project with editors, views, sheets, and saving. Initialize the OpenGeometry WebAssembly kernel once before building geometry. Use supported edit commands; do not change meshes to save a design edit. Keep model lengths in metres, Y up, and plan points as [x, z]. Preserve authored element IDs. Validate unknown model JSON before building it. Run the example on localhost. A deployed hostname needs a valid license key. Use PDF, GLB, or the JSON project file today. IFC and DXF are next-release features. ``` Your installed `index.d.ts` gives the available API names and types. The docs explain their meaning. If a guide uses an API that your version lacks, check the [changelog](/floorkit/changelog) before upgrading or choosing a different route. ## Next Run your integration through the [quickstart](/floorkit/quickstart). Before deploying it, read [Licensing](/floorkit/licensing). For generated models and supported edits, continue with [Agent building workflow](/floorkit/agents/agent-building-workflow). # AngleDimension Source: https://docs.ploterr.com/floorkit/api/annotations/angle-dimension A dimension that shows the angle between two lines in the model. ## Import ```typescript theme={null} import { AngleDimension } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends Arc implements IPrimitive` ## Constructor ```typescript theme={null} constructor(angleDimensionConfig?: AngleDimensionOptions); ``` ## Properties ## Methods ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): AngleDimensionOptions; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` A single handle for the arc `center`, free on its plane. ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### createWitnessLines ```typescript theme={null} createWitnessLines(startAngle: number, endAngle: number, baseV1: Vector3, baseV2: Vector3): void; ``` ### createLabel ```typescript theme={null} createLabel(position: { x: number; y: number; z: number; }, angleY: number, text: string): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ## Types ### AngleDimensionOptions The package does not export this type by name. ```typescript theme={null} interface AngleDimensionOptions { ogid?: string; center: Array; vector1: Array; vector2: Array; radius: number; color: number; label?: string; fontSize: number; fontColor: number; font?: string; lineWidth: number; textOffset?: number; opposite?: boolean; } ``` ## Related * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`IPrimitive`](/floorkit/api/primitives/line-primitive#iprimitive) # Dimension geometry Source: https://docs.ploterr.com/floorkit/api/annotations/dimension-geometry Calculate the lines, terminators and text of one dimension. ## Import ```typescript theme={null} import { dimensionGeometry, measureDimension, resolveMeasure, resolveAngular, resolveRadial, leaderGeometry, terminatorGeometry, } from '@opengeometry/floorkit'; ``` ## dimensionGeometry Resolve a linear dimension to vector lines + a label (paper mm). ```typescript theme={null} function dimensionGeometry(dimension: DimensionInput, style?: DimensionStyle): AnnotationGeometry; ``` ## measureDimension Build a viewport-linked dimension between two saved model points (metres). ```typescript theme={null} function measureDimension( from: Point2, to: Point2, viewportId: string, options: MeasureOptions, ): MeasureAnnotation; ``` ## resolveMeasure Resolve a MeasureAnnotation to vector geometry at render time, using the SAME `viewportTransform` the viewport's drawing uses — so witness lines use the same transform as the drawing. ```typescript theme={null} function resolveMeasure( annotation: MeasureAnnotation, viewport: Viewport, drawing: VectorDrawing, system?: UnitSystem, ): AnnotationGeometry; ``` ## resolveAngular Resolve an AngularMeasureAnnotation at render time. ```typescript theme={null} function resolveAngular( annotation: AngularMeasureAnnotation, viewport: Viewport, drawing: VectorDrawing, ): AnnotationGeometry; ``` ## resolveRadial Resolve a RadialMeasureAnnotation at render time. ```typescript theme={null} function resolveRadial( annotation: RadialMeasureAnnotation, viewport: Viewport, drawing: VectorDrawing, system?: UnitSystem, ): AnnotationGeometry; ``` ## leaderGeometry Resolve a leader (polyline + arrowhead + text) to vector lines + a label (paper mm). ```typescript theme={null} function leaderGeometry(leader: LeaderInput, style?: LeaderStyle): AnnotationGeometry; ``` ## terminatorGeometry One terminator at `at`, opening back along `inward`. ```typescript theme={null} function terminatorGeometry( kind: TerminatorKind, at: Point2, inward: Point2, sizeMm: number, ): TerminatorGeometry; ``` ## Types ### DimensionInput The package does not export this type by name. ```typescript theme={null} interface DimensionInput { start: Point2; end: Point2; offset: number; label?: string; fontSize: number; } ``` Perpendicular offset of the dimension line from the start→end segment. Override text; defaults to the measured paper distance. ### DimensionStyle The package does not export this type by name. ```typescript theme={null} interface DimensionStyle extends StrokeStyle { tickLength?: number; terminator?: TerminatorKind; } ``` Terminator half-length in paper mm. Defaults to `fontSize * 0.5`. Which mark to draw at each end. Defaults to "slash" — the architectural convention this package has always drawn, and the only one it could. ### AnnotationGeometry The vector primitives an annotation resolves to (paper mm). The package does not export this type by name. ```typescript theme={null} interface AnnotationGeometry { lines: VectorLine[]; label: VectorText; fills?: DrawingLoop[]; } ``` Solid areas — a filled arrowhead, a dot terminator. Absent for annotations drawn entirely in stroke, which is every one this package emitted before terminators became configurable. ### Point2 2D point in the extractor's output. ```typescript theme={null} type Point2 = PlanVectorPoint; ``` ### UnitSystem Drawing scales: the named ratios a technical drawing is actually plotted at, and the one formatter that turns a ratio into the label printed on the sheet. ```typescript theme={null} type UnitSystem = 'metric' | 'imperial'; ``` ### AngularMeasureAnnotation A viewport-linked angular dimension: `vertex`/`from`/`to` are saved points in the viewport's **model** space (metres). The package does not export this type by name. ```typescript theme={null} interface AngularMeasureAnnotation { id: string; kind: 'angular'; styleId?: string; viewportId: string; vertex: Point2; from: Point2; to: Point2; radius: number; fontSize: number; precision?: number; label?: string; } ``` Named style in `PaperDocument.dimensionStyles`. Absent uses the default. Angle vertex (model metres). A point along the first ray (model metres). A point along the second ray (model metres). Arc radius in paper mm (annotation scale, zoom/scale-independent). Decimal places on the degree value. Default 0. Optional label override; default = measured angle in degrees. ### RadialMeasureAnnotation A viewport-linked radial dimension: `center`/`edge` are saved points in a viewport's **model** space (metres), typically an arc/circle centre and edge. The package does not export this type by name. ```typescript theme={null} interface RadialMeasureAnnotation { id: string; kind: 'radial'; styleId?: string; viewportId: string; center: Point2; edge: Point2; mode: 'radius' | 'diameter'; fontSize: number; precision?: number; label?: string; } ``` Named style in `PaperDocument.dimensionStyles`. Absent uses the default. Arc/circle centre (model metres). A point on the arc/circle (model metres); `|edge − center|` is the radius. `radius` → "R…"; `diameter` → "⌀…" (line spans through the centre). Decimal places on the measured mm value. Default 0. Optional label override; default = measured radius/diameter in mm. ### LeaderInput The package does not export this type by name. ```typescript theme={null} interface LeaderInput { target: Point2; points: Point2[]; text: string; fontSize: number; } ``` Arrow tip — points at the thing being annotated. Knee points from the target to the text anchor (last point). ### LeaderStyle The package does not export this type by name. ```typescript theme={null} interface LeaderStyle extends StrokeStyle { arrowLength?: number; arrowAngleDeg?: number; } ``` Arrowhead barb length in paper mm. Defaults to `fontSize`. Half-angle of the arrowhead in degrees. Defaults to 20. ### TerminatorKind ```typescript theme={null} type TerminatorKind = 'slash' | 'arrow' | 'open-arrow' | 'dot' | 'none'; ``` ### TerminatorGeometry type A terminator's ink: stroked lines plus any filled loops. The package does not export this type by name. ```typescript theme={null} interface TerminatorGeometry { lines: VectorLine[]; fills: DrawingLoop[]; } ``` Solid areas — a filled arrowhead or a dot. Empty for stroked kinds. ### StrokeStyle Shared stroke styling for annotation lines (paper mm). The package does not export this type by name. ```typescript theme={null} interface StrokeStyle { strokeColor?: PlanVectorColor; strokeWidth?: number; } ``` ### PlanVectorPoint ```typescript theme={null} interface PlanVectorPoint { x: number; y: number; } ``` ### PlanVectorColor ```typescript theme={null} type PlanVectorColor = [number, number, number]; ``` ## Related * [`DrawingLoop`](/floorkit/api/drawings/draw-project-view#drawingloop) * [`MeasureAnnotation`](/floorkit/api/annotations/dimension-strings#measureannotation) * [`MeasureOptions`](/floorkit/api/annotations/dimension-strings#measureoptions) * [`VectorDrawing`](/floorkit/api/drawings/draw-project-view#vectordrawing) * [`VectorLine`](/floorkit/api/drawings/draw-project-view#vectorline) * [`VectorText`](/floorkit/api/drawings/draw-project-view#vectortext) * [`Viewport`](/floorkit/api/drawings/sheet-documents#viewport) # Dimension strings Source: https://docs.ploterr.com/floorkit/api/annotations/dimension-strings Make overall, chain and facade dimension strings for a drawing. These functions make the dimension annotations of a drawing. `overallDimensions` gives the full width and the full height. The other functions give a string of dimensions along an edge or a facade. ## Import ```typescript theme={null} import { overallDimensions, chainDimensions, facadeDimensions, facadeDimensionExtent, facadeRuns, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { overallDimensions, type VectorDrawing, type Viewport, type Sheet, } from '@opengeometry/floorkit'; export function addOverallDimensions( drawing: VectorDrawing, viewport: Viewport, sheet: Sheet, ): void { sheet.annotations.push( ...overallDimensions(drawing, viewport, { offset: 8, fontSize: 2.5, precision: 0, }), ); } ``` ## Notes * Model points are in metres. Offsets and text sizes are in paper millimetres. * The dimensions keep the points that the drawing had at the time of the call. Make them again after the model changes. * Replace the old annotations when you make new ones. This prevents duplicate ids. ## overallDimensions Overall width + height measures from the drawing's **real model extents** (its bounds). ```typescript theme={null} function overallDimensions( drawing: VectorDrawing, viewport: Viewport, options: MeasureOptions, ): MeasureAnnotation[]; ``` ## chainDimensions An **exterior dimension chain** along one edge of the drawing — the running string of measurements an issued plan carries outside its façade, breaking at each wall junction and each opening jamb. ```typescript theme={null} function chainDimensions( drawing: VectorDrawing, viewport: Viewport, options: ChainOptions, ): MeasureAnnotation[]; ``` ## facadeDimensions One dimension string per exterior run, offset to the OUTSIDE of the plan. ```typescript theme={null} function facadeDimensions( storey: DimensionableStorey, viewport: Viewport, options: FacadeDimensionOptions, ): MeasureAnnotation[]; ``` ## facadeDimensionExtent The outermost paper offset `facadeDimensions` will use, so a caller can place the overall dimension beyond it. ```typescript theme={null} function facadeDimensionExtent( storey: DimensionableStorey, viewport: Viewport, options: FacadeDimensionOptions, ): number; ``` ## facadeRuns The straight runs of exterior wall, each with the stations a dimension string breaks at. ```typescript theme={null} function facadeRuns( storey: DimensionableStorey, options?: { minRun?: number; exteriorPset?: [string, string]; checkRooms?: boolean; onDropped?: (dropped: FacadeDropped) => void; }, ): FacadeRun[]; ``` ## Types ### MeasureOptions The package does not export this type by name. ```typescript theme={null} interface MeasureOptions { offset: number; fontSize: number; precision?: number; idPrefix?: string; } ``` Perpendicular offset of the dimension line, in paper mm. Label height, in paper mm. Decimal places on the millimetre value. Default 0. ### MeasureAnnotation A viewport-linked dimension: `from`/`to` are saved points in the viewport's **model** space (metres). ```typescript theme={null} interface MeasureAnnotation { id: string; kind: 'measure'; styleId?: string; viewportId: string; from: Point2; to: Point2; offset: number; fontSize: number; precision?: number; label?: string; } ``` Named style in `PaperDocument.dimensionStyles`. Absent uses the default. Viewport whose drawing + transform resolve this dimension. Perpendicular offset of the dimension line, in paper mm. Label height, in paper mm. Decimal places on the measured mm value. Default 0. Optional label override; default = measured model distance in mm. ### ChainOptions The package does not export this type by name. ```typescript theme={null} interface ChainOptions extends MeasureOptions { along: ChainEdge; band?: number; tolerance?: number; minSpan?: number; } ``` The edge the chain runs along. Depth searched inward from that edge, in metres. Only geometry reaching into this band contributes a breakpoint, so a chain measures the façade it sits against rather than every wall on the floor. Default 1.0 — one band comfortably deeper than the thickest wall in the Swiss Dwellings sample (0.91 m). Breakpoints closer together than this (metres) are one breakpoint. Default 0.02. A link shorter than this (metres) is dropped, folding into its neighbour: a 2 cm jamb return is a construction detail, not a dimension. Default 0.05 — deliberately BELOW the kernel's 0.10 m minimum wall, so every wall the model can express still earns its own dimension. Raise it past 0.10 and a chain silently swallows every partition, which is how a dimensioned drawing loses its partitions. ### DimensionableStorey The package does not export this type by name. ```typescript theme={null} interface DimensionableStorey { walls?: DimensionableWall[]; spaces?: DimensionableSpace[]; } ``` Rooms, used to confirm a wall really is on the façade — see `outerSideIsOpen`. ### FacadeDimensionOptions The package does not export this type by name. ```typescript theme={null} interface FacadeDimensionOptions extends MeasureOptions { minRun?: number; onDropped?: (dropped: FacadeDropped) => void; exteriorPset?: [string, string]; } ``` Ignore runs shorter than this, in metres. Default 0.6 — a doorway. A 1.5 m floor dropped `s24485-w6`, a 1225 mm façade WITH a window in it. Called for every façade left undimensioned, so a caller can report it rather than let it vanish. Which pset flags an exterior wall. ### FacadeDropped Why a façade was not dimensioned. The package does not export this type by name. ```typescript theme={null} interface FacadeDropped { wallId?: string; reason: 'too-short' | 'room-behind' | 'not-tagged' | 'illegible'; detail: string; } ``` ### FacadeRun The package does not export this type by name. ```typescript theme={null} interface FacadeRun { wallId?: string; start: Point2; end: Point2; length: number; stations: number[]; openings: number; thickness: number; } ``` The wall this run belongs to. Run ends in drawing space — `BmWall` points are world XZ and the drawing maps `{x, y: z}`. True length along the run, in metres. Ascending stations along the run, always beginning at 0 and ending at `length`. How many openings put jambs on this run. The wall's thickness, so a caller can clear the dimension line off the FACE rather than the centreline — otherwise clearance shrinks as walls get thicker. ### ChainEdge Which edge of the drawing a dimension chain runs along. The package does not export this type by name. ```typescript theme={null} type ChainEdge = 'top' | 'bottom' | 'left' | 'right'; ``` ### DimensionableWall The package does not export this type by name. ```typescript theme={null} interface DimensionableWall { id?: string; points: Array<[number, number]> | Point2[]; closed?: boolean; thickness: number; openings?: DimensionableOpening[]; psets?: Record>; } ``` A closed wall's last segment runs from the final point back to the first. ### DimensionableSpace The package does not export this type by name. ```typescript theme={null} interface DimensionableSpace { profile?: { outerRing?: Array<[number, number]> | Point2[]; }; type?: string; psets?: Record>; } ``` Room type, if known. An OUTDOOR type behind a wall does not make it internal. ### DimensionableOpening The subset of `BmStorey` this needs. The package does not export this type by name. ```typescript theme={null} interface DimensionableOpening { offset: number; width: number; frameWidth?: number; } ``` CLEAR opening, between the frames — not the hole in the wall. Frame on each side. The structural opening is `width + 2 * frameWidth`. ABSENT IS NOT ZERO: the kernel cuts the void at `frameWidth ?? DEFAULT_FRAME_WIDTH` (0.05), so treating an absent frame as 0 reproduces exactly the 100 mm error this module was fixed for — just on documents that do not spell the frame out. ## Related * [`Point2`](/floorkit/api/annotations/dimension-geometry#point2) * [`VectorDrawing`](/floorkit/api/drawings/draw-project-view#vectordrawing) * [`Viewport`](/floorkit/api/drawings/sheet-documents#viewport) # Dimension styles Source: https://docs.ploterr.com/floorkit/api/annotations/dimension-styles The text heights, font and terminators that a dimension uses. ## Import ```typescript theme={null} import { resolveDimensionStyle, DEFAULT_DIMENSION_STYLE, DEFAULT_FONT_FAMILY, TEXT_HEIGHTS_MM, } from '@opengeometry/floorkit'; ``` ## resolveDimensionStyle The style an annotation resolves to. ```typescript theme={null} function resolveDimensionStyle( styleId: string | undefined, registry: DimensionStyleRegistry | undefined, ): DimensionStyleSpec; ``` ## DEFAULT\_DIMENSION\_STYLE What a dimension gets when its document names no style. ```typescript theme={null} const DEFAULT_DIMENSION_STYLE: DimensionStyleSpec; ``` ## DEFAULT\_FONT\_FAMILY The font stack for drawing text. ```typescript theme={null} const DEFAULT_FONT_FAMILY = '"Paper Mono", "Courier New", Courier, monospace'; ``` ## TEXT\_HEIGHTS\_MM ISO 3098 text heights, in millimetres. ```typescript theme={null} const TEXT_HEIGHTS_MM: readonly [1.8, 2.5, 3.5, 5, 7, 10, 14, 20]; ``` ## Types ### DimensionStyleRegistry Named styles a document carries, keyed by the id an annotation points at. ```typescript theme={null} type DimensionStyleRegistry = Record; ``` ### DimensionStyleSpec How dimensions and leaders are drawn on one document. ```typescript theme={null} interface DimensionStyleSpec extends StrokeStyle { terminator?: TerminatorKind; terminatorSizeMm?: number; textHeightMm?: number; precision?: number; fontFamily?: string; } ``` Mark at each end of the dimension line. Default "slash". Terminator size in paper mm. Defaults to the label height. Text height in paper mm. Decimal places (metric) or fraction denominator (imperial). ## Related * [`StrokeStyle`](/floorkit/api/annotations/dimension-geometry#strokestyle) * [`TerminatorKind`](/floorkit/api/annotations/dimension-geometry#terminatorkind) # Dimensions Source: https://docs.ploterr.com/floorkit/api/annotations/dimensions Creates model dimensions and finds them by id. DimensionTool is the shared instance. ## Import ```typescript theme={null} import { Dimensions, DimensionTool } from '@opengeometry/floorkit'; ``` ## Constructor ```typescript theme={null} constructor(scene?: THREE.Scene); ``` ## Properties ## Methods ### getInstance ```typescript theme={null} static getInstance(): Dimensions; ``` ### set ```typescript theme={null} set(key: string, value: any): void; ``` ### getDimensionsById ```typescript theme={null} getDimensionsById(key: string): any; ``` ### createDimension ```typescript theme={null} createDimension(type: DimensionType): any; ``` ### createDimensionIn ```typescript theme={null} createDimensionIn(scene: THREE.Scene, type: DimensionType): any; ``` Scene-injected creation: adds the dimension to the given scene without reading the instance/global scene, so it never throws "Scene not initialized". ## DimensionTool ```typescript theme={null} const DimensionTool: Dimensions; ``` ## Types ### DimensionType ```typescript theme={null} type DimensionType = 'length' | 'angle' | 'area' | 'radius' | 'diameter' | 'volume' | 'custom'; ``` # LineDimension Source: https://docs.ploterr.com/floorkit/api/annotations/line-dimension A dimension that shows the distance between two points in the model. ## Import ```typescript theme={null} import { LineDimension } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends Line implements IPrimitive` ## Constructor ```typescript theme={null} constructor(lineDimensionConfig?: LengthDimensionOptions); ``` ## Properties ## Methods ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): LengthDimensionOptions; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Draggable handles: the two measured endpoints (free on their plane) and an `offset` grip that slides perpendicular to set `spacing`. ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### createWitnessLines ```typescript theme={null} createWitnessLines(): void; ``` ### createLabel ```typescript theme={null} createLabel(position: { x: number; y: number; z: number; }, angleY: number): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ## Types ### LengthDimensionOptions The package does not export this type by name. ```typescript theme={null} interface LengthDimensionOptions { ogid?: string; startPoint: Array; endPoint: Array; color: number; spacing: number; label: string; fontSize: number; fontColor: number; font?: string; lineWidth: number; } ``` ## Related * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`IPrimitive`](/floorkit/api/primitives/line-primitive#iprimitive) # Number formats Source: https://docs.ploterr.com/floorkit/api/annotations/number-formats Write a scale or a length as text. `formatLength` writes a length in metres as dimension text. `formatScale` writes a drawing scale as a label such as `1:50`. ## Import ```typescript theme={null} import { formatScale, formatLength, defaultLengthFormat } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { formatLength } from '@opengeometry/floorkit'; console.log(formatLength(10.3, { system: 'metric' })); // 10300 console.log( formatLength(1.25, { system: 'metric', display: 'm', precision: 2, showUnit: true, }), ); // 1.25 m console.log(formatLength(0.3048, { system: 'imperial' })); // 1' ``` ```typescript theme={null} import { formatScale } from '@opengeometry/floorkit'; console.log(formatScale(1 / 50)); // 1:50 console.log(formatScale(2)); // 2:1 console.log(formatScale(0)); // NTS console.log(formatScale(1 / 48, 'imperial')); ``` ## Notes * The metric default is millimetres with no decimals. The imperial default is feet and inches, to 1/16 inch. * A viewport at 1:50 has `scale: 1 / 50`. * A scale that is zero, negative or not a number gives `NTS`. * A length that is not a finite number gives an empty string. ## formatScale The scale label for a sheet or a viewport. ```typescript theme={null} function formatScale(ratio: number, system?: UnitSystem, scaleId?: string): string; ``` ## formatLength Format a model length for a dimension label. ```typescript theme={null} function formatLength(meters: number, format: LengthFormat): string; ``` ## defaultLengthFormat The default format for a unit system — what a dimension gets when unstyled. ```typescript theme={null} function defaultLengthFormat( system: UnitSystem, ): Required>; ``` ## Types ### LengthFormat ```typescript theme={null} interface LengthFormat { system: UnitSystem; display?: LengthDisplay; precision?: number; showUnit?: boolean; prefix?: string; suffix?: string; } ``` Defaults to `mm` for metric and `ft-in` for imperial. Metric: decimal places. Imperial `ft-in`/`in`: the fraction denominator to round to (2, 4, 8, 16, 32 or 64). Defaults to 0 metric, 16 imperial. Print the unit after the value ("3000 mm"). Imperial always marks its units. ### LengthDisplay How a length is written. ```typescript theme={null} type LengthDisplay = 'mm' | 'cm' | 'm' | 'ft-in' | 'in'; ``` ## Related * [`UnitSystem`](/floorkit/api/annotations/dimension-geometry#unitsystem) # RadiusDimension Source: https://docs.ploterr.com/floorkit/api/annotations/radius-dimension A dimension that shows the radius of an arc or a circle. ## Import ```typescript theme={null} import { RadiusDimension } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends THREE.Group implements IPrimitive` ## Constructor ```typescript theme={null} constructor(config?: RadiusDimensionOptions); ``` ## Properties ## Methods ### setOPConfig ```typescript theme={null} setOPConfig(config: RadiusDimensionOptions): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): RadiusDimensionOptions; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` A single handle for the `center`, free on its plane. ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### createLabel ```typescript theme={null} createLabel(position: THREE.Vector3, angleY: number, text: string): void; ``` ## Types ### RadiusDimensionOptions The package does not export this type by name. ```typescript theme={null} interface RadiusDimensionOptions { ogid?: string; center: Array; radius: number; angle: number; textOffset?: number; color: number; label?: string; fontSize: number; fontColor: number; font?: string; lineWidth: number; } ``` ## Related * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`IPrimitive`](/floorkit/api/primitives/line-primitive#iprimitive) # Room and opening labels Source: https://docs.ploterr.com/floorkit/api/annotations/room-and-opening-labels Choose which labels a plan shows and keep them apart. ## Import ```typescript theme={null} import { filterRoomLabels, tagOpenings, declutterText, ROOM_LABEL_NAME, ROOM_LABEL_DETAIL, } from '@opengeometry/floorkit'; ``` ## filterRoomLabels Return the primitives with the room label lines this mode does not draw removed. ```typescript theme={null} function filterRoomLabels( primitives: ReadonlyArray, mode?: RoomLabelMode, ): DrawingPrimitive[]; ``` ## tagOpenings Append a tag per door and per window, at the centre of what that opening drew. ```typescript theme={null} function tagOpenings( primitives: DrawingPrimitive[], mode?: OpeningTagMode, { labels, heightM, }?: { labels?: Map; heightM?: number; }, ): DrawingPrimitive[]; ``` ## declutterText Return the primitives with colliding labels removed. ```typescript theme={null} function declutterText(primitives: ReadonlyArray): DrawingPrimitive[]; ``` ## ROOM\_LABEL\_NAME The `style.subId` values `Space.toDrawing` tags its two label lines with. ```typescript theme={null} const ROOM_LABEL_NAME = 'label:name'; ``` ## ROOM\_LABEL\_DETAIL ```typescript theme={null} const ROOM_LABEL_DETAIL = 'label:detail'; ``` ## Types ### RoomLabelMode `full` = name + `number · area`; `name` = name only; `none` = neither. ```typescript theme={null} type RoomLabelMode = 'full' | 'name' | 'none'; ``` ### OpeningTagMode Whether a plan carries door and window tags. ```typescript theme={null} type OpeningTagMode = 'none' | 'auto'; ``` ## Related * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) # Sheet symbols Source: https://docs.ploterr.com/floorkit/api/annotations/sheet-symbols Geometry for the scale bar, the north arrow and the view title. ## Import ```typescript theme={null} import { scaleBarGeometry, northArrowGeometry, viewTitleGeometry } from '@opengeometry/floorkit'; ``` ## scaleBarGeometry A graphic scale bar: alternating filled and open cells, labelled in real model units. ```typescript theme={null} function scaleBarGeometry( origin: Point2, ratio: number, options?: { unitMeters?: number; divisions?: number; system?: UnitSystem; }, ): DrawnSymbol; ``` ## northArrowGeometry A north arrow: a filled point with an open tail, plus an "N". ```typescript theme={null} function northArrowGeometry(center: Point2, radiusMm?: number, angleDeg?: number): DrawnSymbol; ``` ## viewTitleGeometry The title under a viewport: its name over a heavy rule, with the view's own scale beside it. ```typescript theme={null} function viewTitleGeometry( viewport: Viewport, options?: { system?: UnitSystem; number?: string; }, ): DrawnSymbol; ``` ## Types ### DrawnSymbol The package does not export this type by name. ```typescript theme={null} interface DrawnSymbol { lines: VectorLine[]; texts: VectorText[]; } ``` ## Related * [`Point2`](/floorkit/api/annotations/dimension-geometry#point2) * [`UnitSystem`](/floorkit/api/annotations/dimension-geometry#unitsystem) * [`VectorLine`](/floorkit/api/drawings/draw-project-view#vectorline) * [`VectorText`](/floorkit/api/drawings/draw-project-view#vectortext) * [`Viewport`](/floorkit/api/drawings/sheet-documents#viewport) # Beam Source: https://docs.ploterr.com/floorkit/api/architecture/beam Beam is a structural member between two points. Use it for a lintel, a ridge beam or a sloped member. The section can be a rectangle, a circle or your own profile. ## Import ```typescript theme={null} import { Beam } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Beam, BeamShape, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const beam = new Beam({ shape: BeamShape.RECTANGULAR, start: [0, 3, 0], end: [5, 3.5, 0], width: 0.2, depth: 0.35, }); try { beam.setOPConfig({ end: [5, 3.8, 0] }); console.log(beam.toDrawing('top'), beam.getOPConfig()); } finally { beam.dispose(); } } ``` ## Notes * All values are in metres. The axis goes through the centre of the section. * The start point and the end point must be different. * A `CUSTOM` shape needs a `customProfile`. Floorkit uses only the outer ring of that profile. * The class makes geometry. It does not calculate structural strength. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements IShape, IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Axis start point (world). The axis runs through the section centroid. Axis end point (world). May differ in Y from `start` — sloped beams are supported (ridge beams, raking members). Horizontal section dimension for RECTANGULAR shape. Vertical section dimension for RECTANGULAR shape. Radius for CIRCULAR shape. Custom section profile (local \[u, v] coordinates centered on \[0, 0]: u = horizontal across the beam, v = vertical). Only the outer ring is used (swept sections cannot carry holes). Only when shape === CUSTOM. ## Properties Read-only. Axis length (world). Computed, read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface; a host inspector renders this generically. ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` Vector-export roots (RFC §7). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Produce the beam's plan footprint as a standardized region primitive from its authored 2D polygon. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Draggable handles: the two axis endpoints, each free on its own horizontal plane (its Y is preserved, so sloped beams stay sloped). ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): BeamOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### beginEdit ```typescript theme={null} beginEdit(): void; ``` Start one live edit; a second `beginEdit` before `endEdit` keeps the first start. ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` End the live edit; its exact undo and redo, or undefined when no option changed. ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### BeamOptions ```typescript theme={null} interface BeamOptions extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.BEAM; shape: BeamShape; start: Vec3; end: Vec3; width: number; depth: number; radius: number; customProfile?: PolygonProfile; color: number; material: ColumnMaterial; } ``` ### BeamShape ```typescript theme={null} enum BeamShape { RECTANGULAR = 'RECTANGULAR', CIRCULAR = 'CIRCULAR', CUSTOM = 'CUSTOM', } ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`ColumnMaterial`](/floorkit/api/architecture/column#columnmaterial) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # buildFromBuildingModel Source: https://docs.ploterr.com/floorkit/api/architecture/build-from-building-model buildFromBuildingModel makes 3D elements from a building model. Use it to show a building that the user does not edit. For editing, use [`BuildingSession`](/floorkit/api/editor/building-session). ## Import ```typescript theme={null} import { buildFromBuildingModel, disposeBuildResult, BmBuildError } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import * as THREE from 'three'; import { buildFromBuildingModel, disposeBuildResult, type BuildingModel, } from '@opengeometry/floorkit'; export function inspectBuild(model: BuildingModel): void { const build = buildFromBuildingModel(model, new THREE.Group()); try { console.log(build.warnings, build.sourceByLiveId); } finally { disposeBuildResult(build); } } ``` ## Notes * Start the geometry kernel first. * The `root` of the result is already a child of the parent that you give. * `elementsById` gives the element for an id in the model. `sourceByLiveId` gives the model id for a live element. * Read `warnings` after the build. * A door, a window or a hole that does not fit on its wall at its saved `offset` is drawn there without a hole. The build does not change the model. It gives the warning `opening-does-not-fit`: its `openingFit` gives the offsets where the opening fits and the nearest one. Move the opening to such an offset to remove the warning. * Call `disposeBuildResult` when you replace or remove the build. * Give the content of external assets in `assetSources`. The function does not download them. ## Signature ```typescript theme={null} function buildFromBuildingModel( ir: BuildingModel, parent: THREE.Object3D, options?: BuildOptions, ): BuildResult; ``` ## Parameters Default: `{}`. ## Returns ```typescript theme={null} BuildResult; ``` ## disposeBuildResult Release a completed build after replacing it or closing its owner. ```typescript theme={null} function disposeBuildResult(built: BuildResult): void; ``` ## BmBuildError ```typescript theme={null} class BmBuildError extends Error { readonly path: string; readonly code: string; readonly reason?: unknown; constructor(path: string, code: string, message: string, reason?: unknown); } ``` ## Types ### BuildOptions ```typescript theme={null} interface BuildOptions { validated?: boolean; stampMembership?: boolean; assetSources?: Record; massColors?: Readonly>; } ``` Skip revalidation for a document already checked this tick. Stamp `storeyId`/`buildingId` onto every element's propertySet. Default true. `.op` sources for the document's `external-op` assets, keyed by their `uri`, as either a raw buffer or an already-parsed definition. Fetching is the CALLER's pre-step so this function stays synchronous — the same split `.opbase` uses (`OPBaseOptions.assetSources`). An `embedded-op` asset needs nothing here. The colour of a mass part by its use, `#rrggbb` by use name, used for a part when neither it nor its mass has a `color`. Replaces `MASS_COLORS_BY_USE` whole; a use it does not list takes `#a0b8c8`. Pass it where the project is opened (`ProjectManager` `build`) so that the live build, the plan drawings and the sheets take the same colours. ### BuildResult ```typescript theme={null} interface BuildResult { root: THREE.Group; elementsById: Map; modelObjectsById: Map; sourceByLiveId: Map; storeyGroups: Map; wallNetworks: Map; warnings: BmIssue[]; spatialStructure: BuildingModelSpatialStructure; } ``` One group per build, already added to the parent, at identity — elements are in world coordinates. BuildingModel id -> live element, so a caller can edit without re-parsing the document. Saved basic geometry, kept separate from BIM elements and their exporter contract. Live element ID -> authored BuildingModel identity and feature. ### CompiledModelObject Extend this discriminated union alongside BmModelObject. ```typescript theme={null} type CompiledModelObject = | CompiledLineObject | CompiledArcObject | CompiledPolylineObject | CompiledPolygonObject | CompiledRectangleObject | CompiledCuboidObject | CompiledCylinderObject; ``` ### BuildingModelSpatialStructure The non-geometric ownership context returned by a BuildingModel build. ```typescript theme={null} interface BuildingModelSpatialStructure { project: SpatialStructureScope; site?: SpatialStructureScope; buildings: SpatialStructureBuilding[]; } ``` ### CompiledLineObject A compiled line and its authored ownership, kept separate from BIM elements. The package does not export this type by name. ```typescript theme={null} interface CompiledLineObject { kind: 'line'; id: string; buildingId: string; storeyId: string; workingPlaneId: string; worldStart: Vec3; worldEnd: Vec3; color?: string; object: LinePrimitive; } ``` Saved #rrggbb stroke, independent of the live THREE material. ### CompiledArcObject A compiled circular arc and its world-space working-plane frame. ```typescript theme={null} interface CompiledArcObject { kind: 'arc'; id: string; buildingId: string; storeyId: string; workingPlaneId: string; worldCenter: Vec3; worldXAxis: Vec3; worldYAxis: Vec3; radius: number; startAngle: number; sweepAngle: number; color?: string; object: ArcPrimitive; } ``` ### CompiledPolylineObject A compiled open path with vertices in world XYZ coordinates. ```typescript theme={null} interface CompiledPolylineObject { kind: 'polyline'; id: string; buildingId: string; storeyId: string; workingPlaneId: string; worldVertices: Vec3[]; color?: string; object: PolylinePrimitive; } ``` ### CompiledPolygonObject A compiled filled profile with rings in world XYZ coordinates. ```typescript theme={null} interface CompiledPolygonObject { kind: 'polygon'; id: string; buildingId: string; storeyId: string; workingPlaneId: string; worldOuterRing: Vec3[]; worldHoles: Vec3[][]; color?: string; object: PolygonPrimitive; } ``` ### CompiledRectangleObject A compiled rectangle with its saved mode and projected world boundary. ```typescript theme={null} interface CompiledRectangleObject { kind: 'rectangle'; id: string; buildingId: string; storeyId: string; workingPlaneId: string; mode: 'size' | 'freeform'; worldOuterRing: Vec3[]; worldHoles: Vec3[][]; color?: string; object: RectanglePrimitive; } ``` ### CompiledCuboidObject A native upright body and its authored footprint, with explicit saved ownership. ```typescript theme={null} interface CompiledCuboidObject { kind: 'cuboid'; id: string; buildingId: string; storeyId: string; workingPlaneId: string; mode: 'size' | 'freeform'; height: number; worldOuterRing: Vec3[]; worldHoles: Vec3[][]; color?: string; object: AnalyticSolid & { ogType: string; }; } ``` ### CompiledCylinderObject A native circular body with explicit authored ownership and world placement. ```typescript theme={null} interface CompiledCylinderObject { kind: 'cylinder'; id: string; buildingId: string; storeyId: string; workingPlaneId: string; worldCenter: Vec3; radius: number; height: number; color?: string; object: AnalyticSolid & { ogType: string; }; } ``` ### SpatialStructureScope Property-bearing owner in a BuildingModel spatial hierarchy. ```typescript theme={null} interface SpatialStructureScope { id: string; name: string; elevation?: number; propertySets?: PropertySets; } ``` ### SpatialStructureBuilding ```typescript theme={null} interface SpatialStructureBuilding extends SpatialStructureScope { storeys: SpatialStructureScope[]; } ``` ## Related * [`ArcPrimitive`](/floorkit/api/primitives/arc-primitive) * [`LinePrimitive`](/floorkit/api/primitives/line-primitive) * [`OPElement`](/floorkit/api/architecture/op-element) * [`PolygonPrimitive`](/floorkit/api/primitives/polygon-primitive) * [`PolylinePrimitive`](/floorkit/api/primitives/polyline-primitive) * [`RectanglePrimitive`](/floorkit/api/primitives/rectangle-primitive) * [`WallNetwork`](/floorkit/api/architecture/wall-network) * [`BmColor`](/floorkit/api/architecture/validate-building-model#bmcolor) * [`BmIssue`](/floorkit/api/architecture/validate-building-model#bmissue) * [`BuildingModel`](/floorkit/api/architecture/validate-building-model#buildingmodel) * [`BuildingSourceRef`](/floorkit/api/drawings/plan-drawing-extractor#buildingsourceref) * [`OpenElementDefinition`](/floorkit/api/architecture/open-element#openelementdefinition) * [`PropertySets`](/floorkit/api/architecture/op-element#propertysets) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Building Source: https://docs.ploterr.com/floorkit/api/architecture/building The live mass that a project builds for each saved mass. Read it; change the mass with the project commands. ## Import ```typescript theme={null} import { Building } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends OPElement implements IShape, IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config: Partial> & Pick); ``` Colour of the mass: of every part that has no `color` of its own. World Y of the bottom face of the first segment. Programme of the whole mass; a segment or storey may override it. Draw the storey boundaries on the solids. The stack, bottom-up. Each segment extrudes from the top of the one below and carries its storeys, which tile its height. ## Properties Which segment the `profile` / `height` / `extrudeHeight` accessors address. Read-only. The stack, cloned. The saved id of the active segment: the part that the editor grips, a move, a turn and Delete act on. Setting the id of a segment of this mass makes it active; any other id changes nothing. An editor reads it when it builds its grips and sets it again when one of its gestures begins, so two editors on one mass each edit the part whose grips they drag. Read-only. Read-only. Read-only. Height of the ACTIVE segment (the total is `quantities().height`). Read-only. Local Y of the bottom of the active segment: the plane of its footprint and the base of the push/pull grip, which for an upper segment is the top of the segments below it. Push/pull alias for the GUIEditor — the extrude dimension is the active segment's height. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface. ### setSegment ```typescript theme={null} setSegment(index: number, patch: Partial>): void; ``` Patch a segment of the live mass, as an editor does during a drag. ### selectPart ```typescript theme={null} selectPart(object: THREE.Object3D): boolean; ``` Make the part that drew `object` the active segment, so that the editor grips, a move and a turn, and Delete act on that part. ### storeyElevations ```typescript theme={null} storeyElevations(): Array<{ storeyId: string; segmentId: string; elevation: number; height: number; use?: BuildingUse; }>; ``` Every storey with its world floor elevation, bottom-up. ### quantities ```typescript theme={null} quantities(options?: { grade?: number; }): MassQuantities; ``` Quantities of the mass; `options.grade` is the world Y of the site grade (default 0). ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` Draw the stack. ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` One region per segment plus a `"{n}F · {use}"` label at the top segment's centroid. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` Draw again the parts whose colour changed; each colour keeps its own shared materials. ### getOPConfig ```typescript theme={null} getOPConfig(): BuildingOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` Merge a partial config into the live mass. ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### BuildingOptions ```typescript theme={null} interface BuildingOptions extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.BUILDING; profile: BuildingProfile; height: number; segments: MassSegment[]; use: BuildingUse; showStoreys: boolean; color: number; elevation: number; } ``` ### MassSegment ```typescript theme={null} interface MassSegment { id: string; name?: string; footprint: PolygonProfile; height: number; use?: BuildingUse; color?: number; storeys: MassStorey[]; } ``` Footprint in the world XZ plane; holes are courtyards. Extrusion height, metres. Always equals the sum of `storeys[].height`. Colour of this part, `0xrrggbb`; absent draws it in the colour of the mass. Explicit storeys, bottom-up, tiling `height` exactly. ### BuildingUse Programme of a mass, a segment, or a storey. ```typescript theme={null} type BuildingUse = | 'residential' | 'office' | 'retail' | 'hotel' | 'civic' | 'industrial' | 'parking' | 'mixed' | 'other' | `custom:${string}`; ``` ### MassQuantities ```typescript theme={null} interface MassQuantities { footprintArea: number; grossFloorArea: number; grossFloorAreaByUse: Record; grossFloorAreaByKind: Record; grossVolume: number; height: number; numberOfStoreys: number; facadeAreaFlat: number; storeys: StoreyQuantity[]; } ``` `Qto_BuildingBaseQuantities.FootPrintArea` — net area of the ground segment. `Qto_BuildingBaseQuantities.GrossFloorArea` — Σ storey plates. `Qto_BuildingBaseQuantities.GrossVolume` — Σ netArea × segment height. `Qto_BuildingBaseQuantities.Height` — top of the stack minus `elevation`. `Pset_BuildingCommon.NumberOfStoreys`. Σ (outer + hole perimeters) × height per segment — every face is vertical. ### BuildingProfile Polygon profile for a Building — alias of PolygonProfile. ```typescript theme={null} type BuildingProfile = PolygonProfile; ``` ### StoreyQuantity ```typescript theme={null} interface StoreyQuantity { storeyId: string; segmentId: string; elevation: number; height: number; use?: BuildingUse; areaKind: AreaKind; grossFloorArea: number; perimeter: number; } ``` World Y of the storey's floor. Net plate area (footprint minus courtyards), m². Outer footprint perimeter, m. ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`AreaKind`](/floorkit/api/architecture/massing-helpers#areakind) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`MassStorey`](/floorkit/api/architecture/massing-helpers#massstorey) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Ceiling Source: https://docs.ploterr.com/floorkit/api/architecture/ceiling A ceiling plate. You place it by the height of its lower face. ## Import ```typescript theme={null} import { Ceiling } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends OPElement implements IShape, IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Polygon profile in the world XZ plane (same conventions as Slab). World Y of the ceiling's UNDERSIDE (the face seen from the room below). The profile is extruded upward by `thickness`. ogids of openings whose `opening3D` Solids are CSG-subtracted at build time (light fixtures, access hatches). Populated by `addOpening` / `removeOpening`. ## Properties Push/pull alias for the GUIEditor — the extrude dimension is the thickness. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface; a host inspector renders this generically. ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### addOpening ```typescript theme={null} addOpening(openingElement: Opening): void; ``` Attach an opening element whose `opening3D` Solid will be CSG-subtracted from the extruded ceiling (light fixtures, access hatches). ### removeOpening ```typescript theme={null} removeOpening(ogid: string): void; ``` ### getHostedOpenings ```typescript theme={null} getHostedOpenings(): Opening[]; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` Vector-export roots (RFC §7). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Produce the ceiling's plan footprint as a standardized region primitive (outer ring + profile holes), from its authored 2D polygon. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): CeilingOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### CeilingOptions ```typescript theme={null} interface CeilingOptions extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.CEILING; profile: CeilingProfile; thickness: number; color: number; material: CeilingMaterial; heightOffset: number; openings: string[]; } ``` ### CeilingProfile Polygon profile for a Ceiling — alias of PolygonProfile. ```typescript theme={null} type CeilingProfile = PolygonProfile; ``` ### CeilingMaterial ```typescript theme={null} enum CeilingMaterial { GYPSUM = 'GYPSUM', ACT = 'ACT', PLASTER = 'PLASTER', WOOD = 'WOOD', OTHER = 'OTHER', } ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`Opening`](/floorkit/api/architecture/opening) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # CircularWall Source: https://docs.ploterr.com/floorkit/api/architecture/circular-wall CircularWall is one curved wall with optional openings. Use it for a free-standing curved wall. For a curved wall that joins other walls or holds doors and windows, use a `Wall` with a circular axis in a `WallNetwork`. ## Import ```typescript theme={null} import { CircularWall } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { CircularWall, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const wall = new CircularWall({ axis: { kind: 'circularArc', center: [0, 0, 0], radius: 4, startAngle: 0, sweepAngle: Math.PI / 2, }, thickness: 0.25, height: 3, }); try { const openingId = wall.addOpening({ station: 3, width: 1, bottom: 0.8, height: 1.2, }); wall.updateOpening(openingId, { bottom: 1 }); console.log(wall.length, wall.frameAt(3), wall.toDrawing('top')); wall.removeOpening(openingId); } finally { wall.dispose(); } } ``` ## Notes * Lengths are in metres. `startAngle` and `sweepAngle` are in radians. * The position of an opening is its distance along the axis. * An opening must stay inside the wall and must leave material above it. * The radius must be larger than half of the thickness. * A round opening needs an equal width and height. * `volume` is the gross volume. It does not subtract the openings. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(options?: Partial); ``` ## Properties Read-only. Read-only. Read-only. Read-only. Read-only. Read-only. Read-only. ## Methods ### getOPConfig ```typescript theme={null} getOPConfig(): CircularWallOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(update: Partial): void; ``` ### addOpening ```typescript theme={null} addOpening(opening: CircularWallOpeningOptions): string; ``` ### updateOpening ```typescript theme={null} updateOpening(ogid: string, update: Partial>): void; ``` ### removeOpening ```typescript theme={null} removeOpening(ogid: string): void; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### frameAt ```typescript theme={null} frameAt(station: number): StationWallFrame; ``` ### anchorSpace ```typescript theme={null} anchorSpace(): "world"; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingCurvedRegion[]; ``` Produce standardized drawing primitives for a view directly from the element's parametric data (the 0.3.0 contract). ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### CircularWallOptions ```typescript theme={null} interface CircularWallOptions extends HasPropertySets { ogid?: string; labelName: string; axis: Extract< WallAxis, { kind: 'circularArc'; } >; thickness: number; height: number; material: WallMaterial | string; color: number; deflection: number; accuracy?: AnalyticAccuracy; openings: CircularWallOpeningOptions[]; } ``` ### CircularWallOpeningOptions ```typescript theme={null} type CircularWallOpeningOptions = ProfiledWallOpening; ``` ### StationWallFrame ```typescript theme={null} interface StationWallFrame extends WallFrame { station: number; segmentIndex: number; segmentStation: number; } ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DrawingCurvedRegion`](/floorkit/api/drawings/draw-project-view#drawingcurvedregion) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`ProfiledWallOpening`](/floorkit/api/architecture/opening-helpers#profiledwallopening) * [`Vec3`](/floorkit/api/architecture/wall#vec3) * [`WallAxis`](/floorkit/api/architecture/wall-network#wallaxis) * [`WallFrame`](/floorkit/api/architecture/opening#wallframe) * [`WallMaterial`](/floorkit/api/architecture/wall#wallmaterial) # Column Source: https://docs.ploterr.com/floorkit/api/architecture/column Column is a vertical member with a rectangular, circular or custom section. Use it for columns and piers. ## Import ```typescript theme={null} import { Column } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Column, ColumnShape, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const column = new Column({ shape: ColumnShape.CIRCULAR, center: [2, 3], elevation: 0, radius: 0.2, height: 3, }); try { column.setOPConfig({ ...column.getOPConfig(), height: 3.4 }); console.log(column.toDrawing('top'), column.getOPConfig()); } finally { column.dispose(); } } ``` ## Notes * `center` is `[x, z]` in metres. `elevation` is the bottom of the column. `rotation` is in degrees. * A `CUSTOM` shape needs a `customProfile`. * The class makes geometry. It does not calculate structural strength. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements IShape, IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Center insertion point in the world XZ plane: \[x, z]. Y-axis rotation in degrees, applied to the local profile around the center before geometry is built. World Y of the column base. The profile is extruded upward by `height`. Column height (vertical extent from elevation). Width (X-extent) for RECTANGULAR shape. Depth (Z-extent) for RECTANGULAR shape. Radius for CIRCULAR shape. Custom polygon profile (local coordinates, centered on \[0,0]). Only used when shape === CUSTOM. ogids of openings whose `opening3D` Solids should be CSG-subtracted. ## Properties Push/pull alias for the GUIEditor — the extrude dimension is the height. Editable cross-section for the GUIEditor. Reads the current local profile (rectangular/circular/custom); writing switches the column to a CUSTOM profile and rebuilds. Edits align with the editor when `center` is \[0,0] and `rotation` is 0 (the profile is generated in the column's local frame). ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface; a host inspector renders this generically. ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### addOpening ```typescript theme={null} addOpening(openingElement: Opening): void; ``` ### removeOpening ```typescript theme={null} removeOpening(ogid: string): void; ``` ### getHostedOpenings ```typescript theme={null} getHostedOpenings(): Opening[]; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Produce standardized drawing primitives for a view directly from the element's parametric data (the 0.3.0 contract). ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): ColumnOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: ColumnOptions): void; ``` ### beginEdit ```typescript theme={null} beginEdit(): void; ``` Start one live edit; a second `beginEdit` before `endEdit` keeps the first start. ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` End the live edit; its exact undo and redo, or undefined when no option changed. ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Draggable handles: a base grip (move on the XZ plane) and a top grip (raise/lower the height along +Y). ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### ColumnOptions ```typescript theme={null} interface ColumnOptions extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.COLUMN; shape: ColumnShape; center: PointXZ; rotation: number; elevation: number; height: number; width: number; depth: number; radius: number; customProfile?: PolygonProfile; color: number; material: ColumnMaterial; openings: string[]; } ``` ### ColumnShape ```typescript theme={null} enum ColumnShape { RECTANGULAR = 'RECTANGULAR', CIRCULAR = 'CIRCULAR', CUSTOM = 'CUSTOM', } ``` ### ColumnMaterial ```typescript theme={null} enum ColumnMaterial { CONCRETE = 'CONCRETE', STEEL = 'STEEL', TIMBER = 'TIMBER', COMPOSITE = 'COMPOSITE', OTHER = 'OTHER', } ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`Opening`](/floorkit/api/architecture/opening) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PointXZ`](/floorkit/api/primitives/polygon-maths#pointxz) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Door Source: https://docs.ploterr.com/floorkit/api/architecture/door Door is a door with a frame, a panel and a plan swing symbol. Attach it to a wall and the wall gets a real opening. ## Import ```typescript theme={null} import { Door } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Door, Wall, WallNetwork, WallMaterial, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const network = new WallNetwork(); const opening = new Door({ stationLocal: { alongWall: 2.5 }, panelDimensions: { width: 0.9, thickness: 0.04 }, doorHeight: 2.1, baseHeight: 0, doorQuadrant: 1, swingAngleDegrees: 90, }); try { const wall = network.add( new Wall({ labelName: 'Partition', axis: { kind: 'line', start: [0, 0, 0], end: [5, 0, 0] }, thickness: 0.2, height: 3, baseElevation: 0, locationLine: 'centre', locationOffset: 0, material: WallMaterial.CONCRETE, color: 0xcccccc, priority: 0, joins: { start: { allow: true }, end: { allow: true } }, }), ); network.resolve(); wall.attachOpening(opening, { offset: 2.5 }); opening.setOPConfig({ ...opening.getOPConfig(), swingAngleDegrees: 45 }); console.log(network.resolve().diagnostics); wall.commitOpeningCuts(); console.log(opening.getPhysicalParts(), opening.toDrawing('top')); } finally { network.dispose(); opening.dispose(); } } ``` ## Notes * Dimensions are in metres. Swing angles are in degrees. * `doorQuadrant` sets the hinge side and the swing side, relative to the direction of the wall. Values 1 and 3 put the hinge at the near jamb. Values 2 and 4 put it at the far jamb. Change 1 to 3, or 2 to 4, to swing to the other side. * `baseHeight` is the height above the base of the host wall. * After you attach or change a door on a live wall, call `wall.commitOpeningCuts()`. This call cuts the wall. * `setOPConfig` needs the full options. Copy `getOPConfig()`, change one field and pass the copy. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends Opening implements IShape, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(baseDoorConfig?: Partial); ``` `thickness` is the across-wall depth used only when the door is unhosted; when hosted, the lining auto-matches the host wall's thickness. Which way the door swings, as a 2x2 of hinge side x swing face. 0 = closed, 90 = fully open. Applied to both 2D and 3D. THE vertical authority: the bottom of the rough opening, measured above the host wall's base. Never a world Y — the host frame's origin supplies the storey elevation, so `world.y = frame.origin.y + baseHeight`. Without a host or free placement, the identity frame makes `baseHeight` world Y. ## Properties Read-only. Station-based: the authored position along the host wall. Read-only. At runtime Door IS an Opening; this getter is kept for wall attachers that expect an `Opening` handle. After making `Opening` generic over its discriminator, the cast is structural and clean. Read-only. Plan-view hole loop (Vector3\[] at Y=0). The 1 µm across-wall inset (HOLE\_2D\_TOLERANCE) keeps hole edges strictly inside the outer polygon so the WASM triangulator handles them correctly. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### getPhysicalParts ```typescript theme={null} getPhysicalParts(): readonly PhysicalElementPart[]; ``` Borrow actual material parts; descriptors contain no seeds, cutters or symbols. ### anchorSpace ```typescript theme={null} anchorSpace(): "local" | "world"; ``` A world-space station handle. ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ], snapRadius?: number): void; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` A door is positioned by its station, not by a centreline, so the base `Opening.move` (which walks `points`) does not apply. ### getAnalyticCutBounds ```typescript theme={null} getAnalyticCutBounds(): AnalyticOpeningCutBounds; ``` Exact station/elevation rectangle used by analytic wall subtraction. ### getOPConfig ```typescript theme={null} getOPConfig(): DoorOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: DoorOptions): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` Vector-export roots (RFC §7). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Produce the door's plan symbol as standardized primitives: jamb frames and the leaf as regions, and the swing as a true DrawingArc (not a tessellated polyline) — read directly from the authored 2D geometry. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ## Types ### DoorOptions ```typescript theme={null} interface DoorOptions extends OpeningOptions { stationLocal: StationLocal; panelDimensions: { width: number; thickness: number; }; frameDimensions: { width: number; thickness: number; }; doorType: DoorType; doorHeight: number; frameColor: number; panelMaterial: DoorMaterialType; doorColor: number; doorRotation: number; doorQuadrant: number; swingAngleDegrees: number; } ``` ### PhysicalElementPart Borrowed material body. ```typescript theme={null} interface PhysicalElementPart { readonly role: PhysicalPartRole; readonly body: Solid | Sweep; } ``` ### StationLocal Horizontal position of an opening on its host wall. ```typescript theme={null} interface StationLocal { alongWall: number; } ``` ### AnalyticOpeningCutBounds ```typescript theme={null} interface AnalyticOpeningCutBounds { startAlongWall: number; endAlongWall: number; bottom: number; top: number; } ``` ### DoorType ```typescript theme={null} enum DoorType { GLASS = 'GLASS', WOOD = 'WOOD', DOUBLEDOOR = 'DOUBLEDOOR', SLIDING = 'SLIDING', FOLDING = 'FOLDING', DOUBLEACTION = 'DOUBLEACTION', OTHER = 'OTHER', } ``` ### DoorMaterialType ```typescript theme={null} enum DoorMaterialType { WOOD = 'WOOD', GLASS = 'GLASS', METAL = 'METAL', OTHER = 'OTHER', } ``` ### PhysicalPartRole Semantic part identity is recorded when physical geometry is constructed. ```typescript theme={null} type PhysicalPartRole = | { readonly kind: 'step'; readonly flightIndex: number; readonly stepIndex: number; } | { readonly kind: 'landing'; readonly landingIndex: number; } | { readonly kind: 'door-frame'; } | { readonly kind: 'door-leaf'; readonly leafIndex: number; } | { readonly kind: 'window-frame'; } | { readonly kind: 'glazing'; } | { readonly kind: 'rail'; } | { readonly kind: 'baluster'; readonly balusterIndex: number; }; ``` ## Related * [`Opening`](/floorkit/api/architecture/opening) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OpeningOptions`](/floorkit/api/architecture/opening#openingoptions) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Element definitions Source: https://docs.ploterr.com/floorkit/api/architecture/element-definitions Read, check and write the definition of a custom element. ## Import ```typescript theme={null} import { parseOpenElementDefinition, validateOpenElementDefinition, serializeOpenElementDefinition, buildControlSchema, createPlacementMatrix, } from '@opengeometry/floorkit'; ``` ## parseOpenElementDefinition ```typescript theme={null} function parseOpenElementDefinition(buffer: string): OpenElementDefinition; ``` ## validateOpenElementDefinition ```typescript theme={null} function validateOpenElementDefinition(definition: unknown): OpenElementDefinition; ``` ## serializeOpenElementDefinition ```typescript theme={null} function serializeOpenElementDefinition(definition: unknown): string; ``` ## buildControlSchema ```typescript theme={null} function buildControlSchema(definition: OpenElementDefinition): ControlSchema; ``` ## createPlacementMatrix ```typescript theme={null} function createPlacementMatrix(placement?: Placement): THREE.Matrix4; ``` ## Related * [`ControlSchema`](/floorkit/api/architecture/open-element#controlschema) * [`OpenElementDefinition`](/floorkit/api/architecture/open-element#openelementdefinition) * [`Placement`](/floorkit/api/shapes/cuboid-shape#placement) # Floor Source: https://docs.ploterr.com/floorkit/api/architecture/floor Floor is a flat floor region at one elevation. Use it for floor finishes and area plates. For a floor with thickness, use `Slab`. ## Import ```typescript theme={null} import { Floor } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Floor, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const floor = new Floor({ labelName: 'Ground floor', elevation: 0, profile: { outerRing: [ [0, 0], [6, 0], [6, 4], [0, 4], ], holes: [ [ [2, 1], [3, 1], [3, 2], [2, 2], ], ], }, }); try { floor.setOPConfig({ ...floor.getOPConfig(), elevation: 0.1 }); console.log(floor.toDrawing('top')); } finally { floor.dispose(); } } ``` ## Notes * Profile points are `[x, z]` in metres. `elevation` is the world height. * The profile can have holes. A profile that is not valid throws an error. * A floor shows in the plan only. It has no solid. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements IShape, IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Polygon profile in the world XZ plane. `outerRing` is auto-closed and normalized to CW (the convention `opengeometry.Polygon` expects). Holes are auto-closed and normalized to CCW. Elevation from world center ## Properties Always empty — a massing plate has no 3D geometry. Required by `IShape`, and kept so `dispose()` stays uniform with the other elements. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` Vector-export roots (RFC §7). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Produce the floor's plan footprint as a standardized region primitive (outer ring + profile holes), from its authored 2D polygon. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): FloorOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: FloorOptions): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### FloorOptions ```typescript theme={null} interface FloorOptions extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.FLOOR; profile: FloorProfile; color: number; material: FloorMaterial; elevation: number; } ``` ### FloorProfile Polygon profile for a Floor — alias of PolygonProfile. ```typescript theme={null} type FloorProfile = PolygonProfile; ``` ### FloorMaterial ```typescript theme={null} enum FloorMaterial { HARDWOOD = 'HARDWOOD', TILE = 'TILE', CARPET = 'CARPET', LAMINATE = 'LAMINATE', CONCRETE = 'CONCRETE', OTHER = 'OTHER', } ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # LineArcPolyWall Source: https://docs.ploterr.com/floorkit/api/architecture/line-arc-poly-wall One wall that follows a path of straight lines and arcs. ## Import ```typescript theme={null} import { LineArcPolyWall } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends OPElement implements IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(options?: Partial); ``` ## Properties Read-only. Read-only. Read-only. Read-only. Read-only. Read-only. Read-only. ## Methods ### getOPConfig ```typescript theme={null} getOPConfig(): LineArcPolyWallOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(update: Partial): void; ``` ### frameAt ```typescript theme={null} frameAt(station: number): StationWallFrame; ``` ### addOpening ```typescript theme={null} addOpening(opening: LineArcWallOpening): string; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingCurvedRegion[]; ``` Produce standardized drawing primitives for a view directly from the element's parametric data (the 0.3.0 contract). ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### LineArcPolyWallOptions ```typescript theme={null} interface LineArcPolyWallOptions extends HasPropertySets { ogid?: string; labelName: string; axes: WallAxis[]; thickness: number; height: number; material: WallMaterial | string; color: number; deflection: number; accuracy?: AnalyticAccuracy; openings: LineArcWallOpening[]; } ``` ### LineArcWallOpening ```typescript theme={null} type LineArcWallOpening = ProfiledWallOpening; ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DrawingCurvedRegion`](/floorkit/api/drawings/draw-project-view#drawingcurvedregion) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`ProfiledWallOpening`](/floorkit/api/architecture/opening-helpers#profiledwallopening) * [`StationWallFrame`](/floorkit/api/architecture/circular-wall#stationwallframe) * [`Vec3`](/floorkit/api/architecture/wall#vec3) * [`WallAxis`](/floorkit/api/architecture/wall-network#wallaxis) * [`WallMaterial`](/floorkit/api/architecture/wall#wallmaterial) # Massing helpers Source: https://docs.ploterr.com/floorkit/api/architecture/massing-helpers Read the areas of a mass and check its storeys. ## Import ```typescript theme={null} import { storeyTilingError, resolveAreaKind, AREA_KINDS, massQuantities, massBuildingCommand, toBuildingUse, irMassToMassLike, massToShell, BUILDING_USES, MASS_COLORS_BY_USE, massSegmentColorCommand, } from '@opengeometry/floorkit'; ``` ## storeyTilingError Storeys must tile their segment exactly; return the mismatch (0 when they do). ```typescript theme={null} function storeyTilingError(segment: MassSegment): number; ``` ## resolveAreaKind Explicit kind → `parking` use → `parking`; a storey whose top is at or below the site grade (world Y) → `basement`; otherwise `gross`. ```typescript theme={null} function resolveAreaKind( storey: MassStorey, segmentUse: BuildingUse | undefined, storeyElevation: number, grade: number, ): AreaKind; ``` ## AREA\_KINDS What a storey's plate counts as. ```typescript theme={null} const AREA_KINDS: readonly ['gross', 'balcony', 'core', 'basement', 'parking', 'mechanical']; ``` ## massQuantities Quantities of a stacked-prism mass, where `options.grade` is the world Y of the site grade (default 0) and a storey whose top is at or below it is a basement. ```typescript theme={null} function massQuantities( mass: MassLike, options?: { grade?: number; }, ): MassQuantities; ``` ## massBuildingCommand The `add-building` command for a new building with one mass, from an outline drawn in site XZ metres. ```typescript theme={null} function massBuildingCommand( model: Readonly, outline: readonly PointXZ[] | PolygonProfile, options: MassBuildingOptions, ): BuildingCommand; ``` ## toBuildingUse Coerce a BuildingModel use string to a `BuildingUse` (`custom:` prefix for anything unknown). ```typescript theme={null} function toBuildingUse(use: string | undefined): BuildingUse | undefined; ``` ## irMassToMassLike The `MassLike` view of an `BmBuilding` with a `mass`: segments with their storeys resolved from `building.storeys` by id (in `storeyIds` order). ```typescript theme={null} function irMassToMassLike(building: BmBuilding): MassLike; ``` ## massToShell Build the shell of `mass` as an `BmBuilding`. ```typescript theme={null} function massToShell(mass: MassLike, opts?: ShellOptions): BmBuilding; ``` ## BUILDING\_USES ```typescript theme={null} const BUILDING_USES: readonly [ 'residential', 'office', 'retail', 'hotel', 'civic', 'industrial', 'parking', 'mixed', 'other', ]; ``` ## MASS\_COLORS\_BY\_USE The colour of a mass part by its use, when neither the part nor its mass has a `color`. ```typescript theme={null} const MASS_COLORS_BY_USE: Readonly>; ``` ## massSegmentColorCommand The `replace-mass` command that gives one part of a saved mass the colour `color` (`#rrggbb`), or takes its own colour away when `color` is `undefined`; `undefined` when the part already has that colour. ```typescript theme={null} function massSegmentColorCommand( model: Readonly, buildingId: string, segmentId: string, color: BmColor | undefined, ): ReplaceMassCommand | undefined; ``` ## Types ### MassBuildingOptions What `massBuildingCommand` needs besides the outline. ```typescript theme={null} interface MassBuildingOptions { storeyHeight: number; storeyCount: number; use?: string; name?: string; baseElevation?: number; } ``` Floor-to-floor height of every storey, in metres; above 0. How many storeys the mass has; an integer of 1 or more. Use of the segment and of its storeys, for example `office`. Name of the new building. World Y of the bottom of the mass. Defaults to the site grade, or 0 without a site. ### MassStorey ```typescript theme={null} interface MassStorey { id: string; name?: string; height: number; use?: BuildingUse; areaKind?: AreaKind; } ``` Floor-to-floor height, metres. Overrides the owning segment's use for this storey (a retail ground floor under offices). Explicit area classification. Derived when absent — see resolveAreaKind. ### AreaKind What a storey's plate counts as. ```typescript theme={null} type AreaKind = (typeof AREA_KINDS)[number]; ``` ### MassLike The structural shape `massQuantities` needs — a `Building`'s config, an `BmMass`, or a plain object. ```typescript theme={null} interface MassLike { id: string; name?: string; elevation: number; segments: MassSegment[]; } ``` World Y of the bottom face of `segments[0]`. ### ShellOptions ```typescript theme={null} interface ShellOptions { wallThickness?: number; slabThickness?: number; roofThickness?: number; wallMaterial?: WallMaterial | string; color?: BmColor; } ``` Default 0.3 m. Default 0.25 m. Default 0.25 m. ## Related * [`BmBuilding`](/floorkit/api/architecture/validate-building-model#bmbuilding) * [`BmColor`](/floorkit/api/architecture/validate-building-model#bmcolor) * [`BuildingCommand`](/floorkit/api/editor/pure-edits#buildingcommand) * [`BuildingModel`](/floorkit/api/architecture/validate-building-model#buildingmodel) * [`BuildingUse`](/floorkit/api/architecture/building#buildinguse) * [`MassQuantities`](/floorkit/api/architecture/building#massquantities) * [`MassSegment`](/floorkit/api/architecture/building#masssegment) * [`PointXZ`](/floorkit/api/primitives/polygon-maths#pointxz) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`ReplaceMassCommand`](/floorkit/api/editor/pure-edits#replacemasscommand) * [`WallMaterial`](/floorkit/api/architecture/wall#wallmaterial) # OPBaseProject Source: https://docs.ploterr.com/floorkit/api/architecture/op-base-project OPBaseProject places many custom elements as one assembly. Use it for furniture layouts and equipment groups that repeat the same asset. ## Import ```typescript theme={null} import { OPBaseProject, loadOPBaseAssetSources } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { OPBaseProject, ElementType, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const project = new OPBaseProject({ id: 'equipment-layout', name: 'Equipment layout', assets: { equipment: { id: 'equipment', kind: 'embedded-op', definition: { version: 1, id: 'equipment', labelName: 'Equipment', type: ElementType.FURNITURE, dimensions: { width: 1, depth: 0.6, height: 0.9 }, }, }, }, nodes: { first: { id: 'first', assetRef: 'equipment', placement: { position: [1, 0, 2], rotation: [0, 0, 0], scale: [1, 1, 1] }, }, }, roots: ['first'], }); try { project.setNodePlacement('first', { position: [2, 0, 3], rotation: [0, 0, 0], scale: [1, 1, 1], }); console.log(project.getNode('first')?.worldPlacement); console.log(project.getOPBaseBuffer()); } finally { project.dispose(); } } ``` ## Notes * Each node has a parent, a placement and an asset. * `setNodePlacement` moves a node and rebuilds the assembly. Get the elements again after a rebuild. * `getOPBaseBuffer` returns the assembly as text. * Give the definitions of external assets yourself. The class does not download them. * An assembly is not a building. For storeys, walls, views and sheets, use a [building model](/floorkit/concepts/building-model). * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). **Inheritance:** `extends THREE.Group` ## Constructor ```typescript theme={null} constructor(config?: OPBaseInput); ``` ## Properties ## Methods ### parseBuffer ```typescript theme={null} static parseBuffer(buffer: string): OPBaseDocument; ``` ### normalizeDocument ```typescript theme={null} static normalizeDocument(document: unknown): OPBaseDocument; ``` ### getOPBaseBuffer ```typescript theme={null} getOPBaseBuffer(): string; ``` ### getOPBaseConfig ```typescript theme={null} getOPBaseConfig(): OPBaseConfig; ``` ### setOPBaseConfig ```typescript theme={null} setOPBaseConfig(config: OPBaseInput): void; ``` ### setNodePlacement ```typescript theme={null} setNodePlacement(nodeId: string, placement: Placement): void; ``` ### getAsset ```typescript theme={null} getAsset(id: string): OPBaseResolvedAsset | undefined; ``` ### getNode ```typescript theme={null} getNode(id: string): OPBaseResolvedNode | undefined; ``` ### getRelation ```typescript theme={null} getRelation(id: string): OPBaseRelation | undefined; ``` ### getExportElements ```typescript theme={null} getExportElements(): OpenElement[]; ``` ### resolveProject ```typescript theme={null} resolveProject(): OPBaseResolvedDocument; ``` ### generateExportDocument ```typescript theme={null} generateExportDocument(): OPBaseResolvedDocument; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## loadOPBaseAssetSources ```typescript theme={null} function loadOPBaseAssetSources( document: OPBaseDocument, assetBaseUrl?: string, ): Promise>; ``` ## Types ### OPBaseConfig ```typescript theme={null} interface OPBaseConfig extends OPBaseDocument { ogid?: string; } ``` ### OPBaseInput ```typescript theme={null} type OPBaseInput = string | OPBaseDocument | OPBaseOptions; ``` ### OPBaseDocument ```typescript theme={null} interface OPBaseDocument { version: 1; id: string; name: string; units: 'meters'; assets: Record; nodes: Record; roots: string[]; relations?: Record; metadata?: Record; } ``` ### OPBaseResolvedAsset ```typescript theme={null} interface OPBaseResolvedAsset { id: string; kind: OPBaseAsset['kind']; definition: OpenElementDefinition; sourceUri?: string; } ``` ### OPBaseResolvedNode ```typescript theme={null} interface OPBaseResolvedNode { id: string; assetRef: string; parentId?: string; visible: boolean; locked: boolean; localPlacement: Placement; worldPlacement: Placement; childIds: string[]; relationIds: string[]; container: THREE.Group; element: OpenElement; overrides?: OPBaseNodeOverrides; } ``` ### OPBaseRelation ```typescript theme={null} interface OPBaseRelation { id: string; kind: 'attachedTo' | 'hostedBy' | 'insertedInto' | 'dependsOn' | 'groupMember'; from: string; to: string; attachment?: AttachmentSpec; metadata?: Record; } ``` ### OPBaseResolvedDocument ```typescript theme={null} interface OPBaseResolvedDocument { id: string; name: string; units: 'meters'; metadata?: Record; assets: Record; nodes: Record; roots: string[]; relations: Record; elements: OpenElement[]; } ``` ### OPBaseOptions ```typescript theme={null} interface OPBaseOptions extends Partial< Omit > { version?: 1; assets?: Record; nodes?: Record; roots?: string[]; relations?: Record; source?: string | OPBaseDocument; assetBaseUrl?: string; assetSources?: Record; } ``` ### OPBaseAsset ```typescript theme={null} type OPBaseAsset = OPBaseExternalAsset | OPBaseEmbeddedAsset; ``` ### OPBaseNode ```typescript theme={null} interface OPBaseNode { id: string; assetRef: string; parentId?: string; placement: Placement; visible?: boolean; locked?: boolean; overrides?: OPBaseNodeOverrides; } ``` ### OPBaseNodeOverrides ```typescript theme={null} interface OPBaseNodeOverrides { labelName?: string; metadata?: Record; dimensions?: OpenElementDimensions; } ``` ### AttachmentSpec ```typescript theme={null} interface AttachmentSpec { hostAnchorId?: string; childAnchorId?: string; offset?: Placement; alignment?: 'match' | 'flipX' | 'flipY' | 'flipZ'; face?: string; slot?: string; } ``` ### OPBaseExternalAsset ```typescript theme={null} interface OPBaseExternalAsset { id: string; kind: 'external-op'; uri: string; } ``` ### OPBaseEmbeddedAsset ```typescript theme={null} interface OPBaseEmbeddedAsset { id: string; kind: 'embedded-op'; definition: OpenElementDefinition; } ``` ## Related * [`OpenElement`](/floorkit/api/architecture/open-element) * [`OpenElementDefinition`](/floorkit/api/architecture/open-element#openelementdefinition) * [`OpenElementDimensions`](/floorkit/api/architecture/open-element#openelementdimensions) * [`Placement`](/floorkit/api/shapes/cuboid-shape#placement) # OPElement Source: https://docs.ploterr.com/floorkit/api/architecture/op-element The base class of all Floorkit elements. ## Import ```typescript theme={null} import { OPElement } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends THREE.Object3D` ## Constructor ```typescript theme={null} constructor(); ``` ## Properties ## Methods ### add ```typescript theme={null} add(...objects: THREE.Object3D[]): this; ``` Adds another Object3D as child of this Object3D. ## Types ### ElementType ```typescript theme={null} enum ElementType { SITE = 'SITE', PRIMITIVE = 'PRIMITIVE', SHAPE = 'SHAPE', DOOR = 'DOOR', WINDOW = 'WINDOW', WALL = 'WALL', OPENING = 'OPENING', SLAB = 'SLAB', FLOOR = 'FLOOR', BUILDING = 'BUILDING', COLUMN = 'COLUMN', STAIR = 'STAIR', SPACECONTAINER = 'SPACECONTAINER', ROOF = 'ROOF', RAILING = 'RAILING', CEILING = 'CEILING', BEAM = 'BEAM', BOARD = 'BOARD', PAPER = 'PAPER', FIXTURE = 'FIXTURE', FURNITURE = 'FURNITURE', APPLIANCE = 'APPLIANCE', LANDSCAPE = 'LANDSCAPE', MEP_RUN = 'MEP_RUN', MEP_SEGMENT = 'MEP_SEGMENT', MEP_FITTING = 'MEP_FITTING', MEP_EQUIPMENT = 'MEP_EQUIPMENT', MEP_TAP_OFF = 'MEP_TAP_OFF', DC_RACK = 'DC_RACK', DC_CONTAINMENT = 'DC_CONTAINMENT', DC_RAISED_FLOOR = 'DC_RAISED_FLOOR', DC_PERFORATED_TILE = 'DC_PERFORATED_TILE', ZONING_ENVELOPE = 'ZONING_ENVELOPE', DATUM_LEVEL = 'DATUM_LEVEL', DATUM_GRID = 'DATUM_GRID', DATUM_REFERENCE_PLANE = 'DATUM_REFERENCE_PLANE', DATUM_PROJECT_ORIGIN = 'DATUM_PROJECT_ORIGIN', DATUM_SECTION_LINE = 'DATUM_SECTION_LINE', DATUM_ELEVATION_MARKER = 'DATUM_ELEVATION_MARKER', } ``` ### IPlaceable Implemented by every project-anchored element (walls, slabs, openings, datums) to translate and rotate by mutating their point-defining state and re-running setOPGeometry(). ```typescript theme={null} interface IPlaceable { move(delta: Vec3): void; rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; } ``` ### HasPropertySets Mixed into an element's options type wherever interoperable data is accepted. ```typescript theme={null} interface HasPropertySets { propertySets?: PropertySets; } ``` ### OPEditableSchema The full editable surface of an element type. ```typescript theme={null} interface OPEditableSchema { groups: ReadonlyArray; props: ReadonlyArray; } ``` Ordered section names. ### OPEditableProp ```typescript theme={null} interface OPEditableProp { path: string; label: string; group: string; control: OPControl; } ``` Dot-path into the element config (`getOPConfig()`), e.g. `"elevation"` or `"head.diameter"`. Section heading the prop is grouped under, e.g. `"Geometry"`, `"Appearance"`. ### OPControl How a single editable property is rendered + constrained. ```typescript theme={null} type OPControl = | { kind: 'number'; unit?: string; min?: number; max?: number; step?: number; } | { kind: 'text'; } | { kind: 'boolean'; } | { kind: 'color'; } | { kind: 'select'; options: ReadonlyArray<{ value: string; label: string; }>; } | { kind: 'vec2'; unit?: string; } | { kind: 'vec3'; unit?: string; } | { kind: 'propertySets'; } | { kind: 'readonly'; }; ``` ### HasEditableSchema Implemented by every element class as a static, per-type descriptor. ```typescript theme={null} interface HasEditableSchema { editableSchema(): OPEditableSchema; } ``` ### EditAnchor ```typescript theme={null} interface EditAnchor { id: string; role: 'vertex' | 'edge' | 'edge-mid' | 'height' | 'elevation'; position: [number, number, number]; dof: EditAnchorDOF; } ``` Session-stable id, e.g. `"v:outer:2"`. Stable across an in-place drag; refreshed on re-render after a topology change (insert/delete vertex). NOT a persistent per-vertex GUID. Anchor position in the element's LOCAL frame. The host transforms it by the rendered object's world matrix to place the handle. ### EditAnchorDOF Degrees of freedom for a single edit anchor — how the host may move it. ```typescript theme={null} type EditAnchorDOF = | { kind: 'xz'; } | { kind: 'axisY'; baseY: number; } | { kind: 'alongAxis'; origin: [number, number, number]; dir: [number, number, number]; } | { kind: 'scalar'; }; ``` ### HasEditAnchors Implemented by element classes that expose draggable geometry handles. ```typescript theme={null} interface HasEditAnchors { getEditAnchors(): EditAnchor[]; applyEditAnchor(id: string, position: [number, number, number], snapRadius?: number): void; anchorSpace?(): 'local' | 'world'; } ``` ### PropertySets Named property sets owned by an element or spatial container. ```typescript theme={null} type PropertySets = Record; ``` ### PropertySet ```typescript theme={null} type PropertySet = Record; ``` ### PropertyValue ```typescript theme={null} type PropertyValue = string | number | boolean; ``` ## Related * [`Vec3`](/floorkit/api/architecture/wall#vec3) # OpenElement Source: https://docs.ploterr.com/floorkit/api/architecture/open-element OpenElement is a custom element that you define as data. Use it for furniture, equipment and other objects that are not walls, slabs or roofs. One definition gives a 2D plan symbol and a 3D model. ## Import ```typescript theme={null} import { OpenElement } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { OpenElement, ElementType, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const asset = new OpenElement({ version: 1, id: 'equipment', labelName: 'Equipment', type: ElementType.FURNITURE, dimensions: { width: 1, depth: 0.6, height: 0.9 }, profileView: { elements: [ { id: 'footprint', kind: 'polygon', vertices: [ [-0.5, -0.3], [0.5, -0.3], [0.5, 0.3], [-0.5, 0.3], ], }, ], }, modelView: { elements: [ { id: 'body', kind: 'cuboid', center: [0, 0.45, 0], width: 1, depth: 0.6, height: 0.9 }, ], }, }); try { asset.updateNodeProperty('body', 'modelView', 'height', 1.1); console.log(asset.getControlSchema(), asset.getOPBuffer()); } finally { asset.dispose(); } } ``` ## Notes * The definition uses metres. * The input can be text, a definition object, or options with a `source`. * Use `updateNodeProperty` or `setOPConfig` to change the element. An unknown node id throws an error. * `getOPBuffer` returns the definition as text. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). **Inheritance:** `extends Polyline implements IShape, PlanVectorExportable, PortOwner` ## Constructor ```typescript theme={null} constructor(config?: OpenElementInput); ``` ## Properties Read-only. ## Methods ### parseBuffer ```typescript theme={null} static parseBuffer(buffer: string): OpenElementDefinition; ``` ### normalizeDefinition ```typescript theme={null} static normalizeDefinition(definition: unknown): OpenElementDefinition; ``` ### getOPBuffer ```typescript theme={null} getOPBuffer(): string; ``` ### getAnchorDefinition ```typescript theme={null} getAnchorDefinition(anchorId: string): AnchorDefinition | undefined; ``` ### getAnchorMatrix ```typescript theme={null} getAnchorMatrix(anchorId: string): THREE.Matrix4; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Standardized plan primitives from the asset's authored 2D profile view. ### setOPConfig ```typescript theme={null} setOPConfig(config: OpenElementInput): void; ``` ### beginEdit ```typescript theme={null} beginEdit(): void; ``` Start one live edit; a second `beginEdit` before `endEdit` keeps the first start. ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` End the live edit; its exact undo and redo, or undefined when no option changed. ### getOPConfig ```typescript theme={null} getOPConfig(): OpenElementConfig; ``` ### getControlSchema ```typescript theme={null} getControlSchema(): ControlSchema; ``` ### updateNodeProperty ```typescript theme={null} updateNodeProperty(nodeId: string, section: "profileView" | "modelView" | "definitions", property: string, value: number | string): void; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` Apply a previewed handle drag. ### dispose ```typescript theme={null} dispose(): void; ``` ### setConfig ```typescript theme={null} setConfig(options: Parameters[0]): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ## Types ### PortOwner ```typescript theme={null} interface PortOwner { ogid: string; getPorts(): DistributionPort[]; } ``` ### OpenElementConfig ```typescript theme={null} interface OpenElementConfig extends OpenElementDefinition, HasPropertySets { ogid?: string; } ``` ### OpenElementInput ```typescript theme={null} type OpenElementInput = string | OpenElementDefinition | OpenElementOptions; ``` ### OpenElementDefinition ```typescript theme={null} interface OpenElementDefinition { version: 1; id: string; labelName: string; type: ElementType; dimensions: OpenElementDimensions; placement?: Placement; metadata?: Record; anchors?: OpenElementAnchors; ports?: PortDefinition[]; definitions?: OpenElementDefinitions; profileView?: OpenElementProfileView; modelView?: OpenElementModelView; } ``` ### AnchorDefinition ```typescript theme={null} interface AnchorDefinition { placement: Placement; metadata?: Record; } ``` ### ControlSchema ```typescript theme={null} interface ControlSchema { nodes: NodeControlDescriptor[]; } ``` ### DistributionPort ```typescript theme={null} interface DistributionPort { id: string; domain: PortDomain; systemType: string; flowDirection: PortFlowDirection; placement: Placement; profile: PortProfile; internal?: boolean; metadata?: Record; } ``` Free-form but conventional: "supplyAir", "chilledWaterSupply", "power". Local placement on the owning element. Cross-section the connecting run must match. Chain-internal port of a generated run piece. Regenerated wholesale on every path edit, so external connections must never target it — the graph rejects such connects with `INTERNAL_PORT`. ### OpenElementOptions ```typescript theme={null} interface OpenElementOptions extends Partial>, HasPropertySets { version?: 1; dimensions?: Partial; source?: string | OpenElementDefinition; } ``` ### OpenElementDimensions ```typescript theme={null} interface OpenElementDimensions { width: number; depth: number; height: number; } ``` ### OpenElementAnchors ```typescript theme={null} type OpenElementAnchors = Record; ``` ### PortDefinition A distribution port authored on a `.op` asset. ```typescript theme={null} type PortDefinition = Omit; ``` ### OpenElementDefinitions ```typescript theme={null} type OpenElementDefinitions = Record; ``` ### OpenElementProfileView ```typescript theme={null} interface OpenElementProfileView { elements: ProfileViewElement[]; } ``` ### OpenElementModelView ```typescript theme={null} interface OpenElementModelView { elements: ModelViewElement[]; } ``` ### NodeControlDescriptor ```typescript theme={null} interface NodeControlDescriptor { nodeId: string; nodeKind: string; section: 'profileView' | 'modelView' | 'definitions'; properties: PropertyDescriptor[]; } ``` ### PortDomain ```typescript theme={null} type PortDomain = 'hvac' | 'piping' | 'electrical' | 'cableCarrier' | 'conduit'; ``` ### PortFlowDirection ```typescript theme={null} type PortFlowDirection = 'source' | 'sink' | 'bidirectional'; ``` ### PortProfile Cross-section a connecting run must match (or bridge with a transition). ```typescript theme={null} type PortProfile = | { shape: 'round'; diameter: number; } | { shape: 'rectangular'; width: number; height: number; } | { shape: 'tray'; width: number; height: number; } | { shape: 'electrical'; poles: number; voltage?: number; amperage?: number; }; ``` ### DefinitionNode ```typescript theme={null} type DefinitionNode = ProfileNode | Curve3DNode | ModelNode | OperationNode; ``` ### ProfileViewElement ```typescript theme={null} type ProfileViewElement = ProfileNode | RefNode; ``` ### ModelViewElement ```typescript theme={null} type ModelViewElement = ModelNode | RefNode; ``` ### PropertyDescriptor ```typescript theme={null} interface PropertyDescriptor { key: string; type: ControlPropertyType; value: number | string; label?: string; } ``` ### ProfileNode ```typescript theme={null} type ProfileNode = ProfilePolygonNode | ProfilePolylineNode | ProfileArcNode; ``` ### Curve3DNode ```typescript theme={null} type Curve3DNode = Curve3DPolylineNode; ``` ### ModelNode ```typescript theme={null} type ModelNode = ModelCuboidNode | ModelCylinderNode | ModelSweepNode; ``` ### OperationNode ```typescript theme={null} type OperationNode = Offset2DOpNode | Boolean3DOpNode; ``` ### RefNode ```typescript theme={null} interface RefNode extends BaseElementNode { ref: string; } ``` ### ControlPropertyType ```typescript theme={null} type ControlPropertyType = 'number' | 'color'; ``` ### ProfilePolygonNode ```typescript theme={null} interface ProfilePolygonNode extends BaseElementNode { kind: 'polygon'; vertices: Point2[]; } ``` ### ProfilePolylineNode ```typescript theme={null} interface ProfilePolylineNode extends BaseElementNode { kind: 'polyline'; points: Point2[]; } ``` ### ProfileArcNode ```typescript theme={null} interface ProfileArcNode extends BaseElementNode { kind: 'arc'; center: Point2; radius: number; startAngle: number; sweepAngle: number; deflection?: number; } ``` ### Curve3DPolylineNode ```typescript theme={null} interface Curve3DPolylineNode extends BaseElementNode { kind: 'polyline3d'; points: Point3[]; } ``` ### ModelCuboidNode ```typescript theme={null} interface ModelCuboidNode extends BaseElementNode { kind: 'cuboid'; center: Point3; width: number; height: number; depth: number; } ``` ### ModelCylinderNode ```typescript theme={null} interface ModelCylinderNode extends BaseElementNode { kind: 'cylinder'; center: Point3; radius: number; height: number; angle?: number; } ``` ### ModelSweepNode ```typescript theme={null} type ModelSweepNode = ModelSweepDirectNode | ModelSweepRefNode; ``` ### Offset2DOpNode ```typescript theme={null} interface Offset2DOpNode extends BaseElementNode { kind: 'offset2d'; sourceRef: string; distance: number; acuteThresholdDegrees?: number; bevel?: boolean; output: 'polyline' | 'polygon'; } ``` ### Boolean3DOpNode ```typescript theme={null} interface Boolean3DOpNode extends BaseElementNode { kind: 'boolean3d'; operation: 'subtract' | 'union' | 'intersect'; baseRef: string; operandRefs: string[]; } ``` ### BaseElementNode The package does not export this type by name. ```typescript theme={null} interface BaseElementNode { id?: string; color?: ColorValue; placement?: Placement; } ``` ### Point2 The package does not export this type by name. ```typescript theme={null} type Point2 = [number, number]; ``` ### Point3 The package does not export this type by name. ```typescript theme={null} type Point3 = [number, number, number]; ``` ### ModelSweepDirectNode ```typescript theme={null} interface ModelSweepDirectNode extends BaseElementNode { kind: 'sweep'; path: Point3[]; profile: Point3[]; capStart?: boolean; capEnd?: boolean; } ``` ### ModelSweepRefNode ```typescript theme={null} interface ModelSweepRefNode extends BaseElementNode { kind: 'sweep'; pathRef: string; profileRef: string; capStart?: boolean; capEnd?: boolean; } ``` ### ColorValue The package does not export this type by name. ```typescript theme={null} type ColorValue = number | string; ``` ## Related * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`Placement`](/floorkit/api/shapes/cuboid-shape#placement) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) # Opening Source: https://docs.ploterr.com/floorkit/api/architecture/opening Opening is the base class of Door, Window and WallOpening. Use it only when you need a free cutter shape. For a hole in a wall, use one of the three subclasses. ## Import ```typescript theme={null} import { Opening } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Opening, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const opening = new Opening({ points: [ [0, 0], [1.2, 0], ], thickness: 0.3, height: 2.1, baseHeight: 0, }); try { opening.setOPConfig({ ...opening.getOPConfig(), height: 2.2 }); console.log(opening.holeLoop2D, opening.getAnalyticCutBounds()); } finally { opening.dispose(); } } ``` ## Notes * Points are `[alongWall, acrossWall]` in metres. `baseHeight` gives the vertical position. * An opening does not cut the objects near it. The host does the cut. * `Wall.attachOpening` accepts a `Door`, a `Window` or a `WallOpening`. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements IShape, IPlaceable` ## Constructor ```typescript theme={null} constructor(openingConfig?: Partial>); ``` THE vertical authority: the bottom of the rough opening, measured above the host wall's base. Never a world Y — the host frame's origin supplies the storey elevation, so `world.y = frame.origin.y + baseHeight`. Without a host or free placement, the identity frame makes `baseHeight` world Y. ## Properties Read-only. Where this opening currently sits along its host wall — the counterpart of `baseHeight` for the horizontal axis. Read by the walls so that attaching without an explicit `offset` preserves an authored position instead of snapping the opening back to the wall's start. Bottom of the rough opening above the host wall's base. See OpeningOptions. Read-only. Plan-view hole loop (Vector3\[] in the XZ plane at Y=0). The wall uses this as a Polygon hole. The 1 µm across-wall inset keeps hole edges strictly inside the outer polygon so the WASM triangulator does not produce degenerate output when edges coincide with the boundary. Read-only. ## Methods ### bindHostEdit ```typescript theme={null} bindHostEdit(callbacks: { begin: () => void; preview?: (position: Vec3, snapRadius: number) => void; commit: () => { undo: () => void; redo: () => void; } | void; } | null): void; ``` ### beginEdit ```typescript theme={null} beginEdit(): void; ``` ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` ### bindHostCurve ```typescript theme={null} bindHostCurve(curve: OpeningHostCurve | null): void; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this opening by a WORLD-space `delta`, hosted or not — the delta is projected onto the active frame's axes (identity when no frame is bound), so there is one interpretation rather than one per hosting state. ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPConfig ```typescript theme={null} setOPConfig(config: OpeningOptions): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): OpeningOptions; ``` ### anchorSpace ```typescript theme={null} anchorSpace(): "local" | "world"; ``` Two draggable endpoints. ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### bindHostFrame ```typescript theme={null} bindHostFrame(frame: WallFrame | null): void; ``` Bind this opening to its host wall's frame. ### bindFreePlacement ```typescript theme={null} bindFreePlacement(placement: FreeOpeningPlacement): void; ``` Place an unhosted opening at a storey datum without creating a wall host. ### toWorldPoints ```typescript theme={null} toWorldPoints(): Vector3[]; ``` Stored centreline points as world-space Vector3s, at `baseHeight`. ### getAnalyticCutBounds ```typescript theme={null} getAnalyticCutBounds(): AnalyticOpeningCutBounds | null; ``` Exact station/elevation rectangle used by analytic wall subtraction. ### getWorldCutElevationRange ```typescript theme={null} getWorldCutElevationRange(): { bottom: number; top: number; } | null; ``` World Y range of the analytic rough opening, including ancestor placement once. ### dispose ```typescript theme={null} dispose(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ## Types ### OPGeometry Visual primitive stored in an element's subElements2D / subElements3D maps. ```typescript theme={null} interface OPGeometry extends THREE.Object3D { outline?: boolean; } ``` ### OpeningHostCurve ```typescript theme={null} interface OpeningHostCurve { center: [number, number, number]; radius: number; startAngle: number; sweepAngle: number; centreOffset: number; } ``` ### OpeningOptions ```typescript theme={null} interface OpeningOptions extends HasPropertySets { ogid?: string; labelName: string; type: T; hostWallId?: string; thickness?: number; height?: number; baseHeight?: number; length?: number; points?: [number, number][]; } ``` ### WallFrame Wall-local coordinate frame. The package does not export this type by name. ```typescript theme={null} interface WallFrame { origin: Vector3; alongWallAxis: Vector3; acrossWallAxis: Vector3; verticalAxis: Vector3; length: number; thickness?: number; } ``` Host wall thickness. Populated when the frame comes from a wall; undefined for unhosted use. ### FreeOpeningPlacement ```typescript theme={null} interface FreeOpeningPlacement { position: [x: number, z: number]; yawDeg: number; elevation: number; } ``` Centre of the opening in the world XZ plane. Degrees from +X toward +Z. World Y of the owning storey's datum. ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`AnalyticOpeningCutBounds`](/floorkit/api/architecture/door#analyticopeningcutbounds) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`Event`](/floorkit/api/architecture/wall-network#event) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Opening helpers Source: https://docs.ploterr.com/floorkit/api/architecture/opening-helpers Check an opening profile and read the saved configuration of an opening. ## Import ```typescript theme={null} import { openingConfigSnapshot, validateOpeningProfile, roundOpeningCutter, } from '@opengeometry/floorkit'; ``` ## openingConfigSnapshot ```typescript theme={null} function openingConfigSnapshot(opening: HostedOpening): { config: object; restore: () => void; }; ``` ## validateOpeningProfile ```typescript theme={null} function validateOpeningProfile( opening: ProfiledWallOpening, tolerance: number, ): WallOpeningProfile; ``` ## roundOpeningCutter ```typescript theme={null} function roundOpeningCutter( opening: ProfiledWallOpening, frame: StationWallFrame, thickness: number, deflection: number, accuracy: AnalyticAccuracy | undefined, color: number, centerHeight?: number, ): AnalyticSolid; ``` ## Types ### ProfiledWallOpening ```typescript theme={null} interface ProfiledWallOpening { ogid?: string; station: number; width: number; bottom: number; height: number; profile?: WallOpeningProfile; } ``` ### WallOpeningProfile ```typescript theme={null} type WallOpeningProfile = 'rectangular' | 'round' | 'arched'; ``` ## Related * [`HostedOpening`](/floorkit/api/architecture/wall#hostedopening) * [`StationWallFrame`](/floorkit/api/architecture/circular-wall#stationwallframe) # Property rules Source: https://docs.ploterr.com/floorkit/api/architecture/property-rules Find elements that do not have a required property. ## Import ```typescript theme={null} import { evaluateRequiredPropertyRule, evaluateDoorFireRatingRule, DOOR_FIRE_RATING_REQUIRED, } from '@opengeometry/floorkit'; ``` ## evaluateRequiredPropertyRule Run a named-property requirement against its applicable element type. ```typescript theme={null} function evaluateRequiredPropertyRule( elements: Iterable, rule: RequiredPropertyRule, ): RequiredPropertyRuleReport; ``` ## evaluateDoorFireRatingRule Run DOOR\_FIRE\_RATING\_REQUIRED against an element collection. ```typescript theme={null} function evaluateDoorFireRatingRule( elements: Iterable, ): RequiredPropertyRuleReport; ``` ## DOOR\_FIRE\_RATING\_REQUIRED ```typescript theme={null} const DOOR_FIRE_RATING_REQUIRED: Readonly; ``` ## Types ### PropertyRuleElement A minimal element view for property-compliance rules. ```typescript theme={null} interface PropertyRuleElement extends PropertySetCarrier { ogid?: string; ogType?: string; labelName?: string; } ``` Stable result identifier, normally a Floorkit `ogid`. Floorkit element discriminator, for example `DOOR`. ### RequiredPropertyRule A declarative requirement for one named scalar property. ```typescript theme={null} interface RequiredPropertyRule { id: string; name: string; ogType: string; propertySet: string; property: string; } ``` ### RequiredPropertyRuleReport A stable, UI-ready summary of one rule run. ```typescript theme={null} interface RequiredPropertyRuleReport { rule: RequiredPropertyRule; inScope: number; passed: number; failed: number; passRate: number | null; items: RequiredPropertyRuleItem[]; } ``` Percentage in \[0, 100], or null when no elements matched the rule scope. ### RequiredPropertyRuleItem One element evaluated by a required-property rule. ```typescript theme={null} interface RequiredPropertyRuleItem { elementId?: string; elementName?: string; value?: PropertyValue; status: 'pass' | 'fail'; reason?: 'missing-property-set' | 'missing-property' | 'blank-value'; } ``` Present only for failures, so a host application can explain the repair. ## Related * [`PropertySetCarrier`](/floorkit/api/architecture/property-sets#propertysetcarrier) * [`PropertyValue`](/floorkit/api/architecture/op-element#propertyvalue) # Property sets Source: https://docs.ploterr.com/floorkit/api/architecture/property-sets Read and change the property sets of an element. A property set is a named group of values on an element, such as a fire rating or a finish. Floorkit uses the common BIM naming style, for example `Pset_DoorCommon`. These functions read and change the property sets of an element. ## Import ```typescript theme={null} import { getPropertySets, replacePropertySets, setPropertyValue, removePropertyValue, normalizePropertySets, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { getPropertySets, setPropertyValue, type PropertySetOwner } from '@opengeometry/floorkit'; export function markFireRated(door: PropertySetOwner): void { setPropertyValue(door, 'Pset_DoorCommon', 'FireRating', 'EI30'); console.log(getPropertySets(door)); } ``` ## Notes * A value is text, a number or a boolean. * In a building model, write property sets in the `propertySets` field of an element. They are saved with the model. * The functions return a result that tells you what changed. ## getPropertySets Return a deep copy so host UI state cannot mutate the live model by reference. ```typescript theme={null} function getPropertySets(owner: PropertySetOwner): PropertySets | undefined; ``` ## replacePropertySets Replace all named sets after validating and copying the supplied value. ```typescript theme={null} function replacePropertySets( owner: PropertySetOwner, propertySets: unknown, ): PropertySetUpdateResult; ``` ## setPropertyValue Add or update one property in one named property set. ```typescript theme={null} function setPropertyValue( owner: PropertySetOwner, propertySetName: string, propertyName: string, value: unknown, ): PropertySetUpdateResult; ``` ## removePropertyValue Remove one property, removing its set too when it becomes empty. ```typescript theme={null} function removePropertyValue( owner: PropertySetOwner, propertySetName: string, propertyName: string, ): PropertySetUpdateResult; ``` ## normalizePropertySets Validate and deep-copy property sets. ```typescript theme={null} function normalizePropertySets(value: unknown, path?: string): NormalizePropertySetsResult; ``` ## Types ### PropertySetOwner An element carrier or a direct owner such as a BuildingModel spatial scope. ```typescript theme={null} type PropertySetOwner = PropertySetCarrier | HasPropertySets; ``` ### PropertySetUpdateResult ```typescript theme={null} interface PropertySetUpdateResult { warnings: PropertySetWarning[]; } ``` ### NormalizePropertySetsResult ```typescript theme={null} interface NormalizePropertySetsResult { propertySets: PropertySets | undefined; warnings: PropertySetWarning[]; } ``` ### PropertySetCarrier The structural contract used by host-app editing helpers. ```typescript theme={null} interface PropertySetCarrier { propertySet: HasPropertySets; } ``` ### PropertySetWarning ```typescript theme={null} interface PropertySetWarning { path: string; code: | 'invalid-property-sets' | 'invalid-property-set' | 'invalid-property-name' | 'invalid-property-value' | 'reserved-property-set'; message: string; } ``` ## Related * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`PropertySets`](/floorkit/api/architecture/op-element#propertysets) # Railing Source: https://docs.ploterr.com/floorkit/api/architecture/railing Railing is a top rail with balusters along a path. The path can slope, so a railing can follow a stair. ## Import ```typescript theme={null} import { Railing } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Railing, Stair, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const stair = new Stair({ width: 1.2, totalRise: 3, treadDepth: 0.28 }); const railing = new Railing({ height: 1.1, balusterSpacing: 0.1 }); try { stair.attachRailing(railing, { side: 'left', inset: 0.05 }); stair.setOPConfig({ totalRise: 3.2 }); console.log(railing.getOPConfig().path, railing.getPhysicalParts()); } finally { stair.detachRailing(railing); railing.dispose(); stair.dispose(); } } ``` ## Notes * All dimensions are in metres. The rail height is measured above the path. * `Stair.attachRailing` makes the railing follow the stair. Detach the railing before you dispose it. * `balusterSpacing` sets the distance between balusters. The clear gap also depends on the baluster size. * The default sizes are not a safety-code check. * When you replace `topRail`, give its width and its height. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements IShape, IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Rail base path (world coordinates). Segments may slope — a stair-hosted railing follows the flight nosings. The top rail runs `height` above this path; balusters drop from the rail to it. Rail height above the base path (m). Guard code: ≥ 1.0 m typical. Maximum clear spacing between balusters (m). Code: a 100 mm sphere must not pass — the 0.11 m default center spacing with 20 mm balusters keeps the clear gap at 90 mm. Square baluster side (m). ogid of the hosting Stair, when attached via `Stair.attachRailing`. ## Properties ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface; a host inspector renders this generically. ### getPhysicalParts ```typescript theme={null} getPhysicalParts(): readonly PhysicalElementPart[]; ``` Borrow actual material parts; descriptors contain no seeds, cutters or symbols. ### setHostPath ```typescript theme={null} setHostPath(path: Vec3[]): void; ``` Replace the base path from a hosting element (Stair). ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` Vector-export roots (RFC §7). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Base path as standardized line primitives. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Draggable handles: one per base-path point, free on its own horizontal plane (Y preserved, so a sloped/stair-hosted path keeps its slope). ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): RailingOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### beginEdit ```typescript theme={null} beginEdit(): void; ``` Start one live edit; a second `beginEdit` before `endEdit` keeps the first start. ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` End the live edit; its exact undo and redo, or undefined when no option changed. ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### RailingOptions ```typescript theme={null} interface RailingOptions extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.RAILING; path: Vec3[]; height: number; balusterSpacing: number; balusterSize: number; topRail: RailingTopRail; hostStairId?: string; color: number; } ``` ### RailingTopRail ```typescript theme={null} interface RailingTopRail { width: number; height: number; } ``` Horizontal rail width (m). Vertical rail height/depth (m). ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PhysicalElementPart`](/floorkit/api/architecture/door#physicalelementpart) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Roof Source: https://docs.ploterr.com/floorkit/api/architecture/roof Roof makes a flat, gable, hip or shed roof from one footprint. ## Import ```typescript theme={null} import { Roof } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Roof, RoofType, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const roof = new Roof({ roofType: RoofType.GABLE, pitch: 30, overhang: 0.4, thickness: 0.2, elevation: 6, massStyle: false, profile: { outerRing: [ [0, 0], [8, 0], [8, 5], [0, 5], ], }, }); try { roof.setOPConfig({ pitch: 35 }); console.log(roof.toDrawing('top'), roof.getOPConfig()); } finally { roof.dispose(); } } ``` ## Notes * Profile points are `[x, z]` in metres. `pitch` and `ridgeDirection` are in degrees. `elevation` is the height of the eave. * A flat roof can have holes. A pitched roof cannot. * A hip roof needs a convex footprint. * `overhang` moves the roof edge outward. `thickness` is measured at a right angle to the roof plane. * `massStyle` fills the volume below the roof planes. * The `pitch` setter keeps the value between 5 and 75 degrees. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements IShape, IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Wall-line footprint in the world XZ plane (the eave ring is this ring offset outward by `overhang`). Holes are honored by FLAT only; pitched types throw on holes. Slope in degrees from horizontal (GABLE/HIP/SHED). Clamped to 5–75. Horizontal eave overhang beyond the footprint (m). Plate thickness measured perpendicular to the roof plane (m). World Y of the eave (wall plate top). Plan direction of the ridge in degrees (GABLE: edges within 45° of this direction slope; SHED: the high side faces this direction's left). Defaults to the direction of the footprint's longest edge. When true, build the filled "attic mass" under the roof planes instead of a constant-thickness plate — half the boolean work, and exactly what massing shells need. ## Properties ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface; a host inspector renders this generically. ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` Vector-export roots (RFC §7). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Plan footprint region plus the ridge line (gable) / slope arrow (shed), emitted parametrically. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): RoofOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### beginEdit ```typescript theme={null} beginEdit(): void; ``` Start one live edit; a second `beginEdit` before `endEdit` keeps the first start. ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` End the live edit; its exact undo and redo, or undefined when no option changed. ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Draggable handles: one grip per footprint vertex (move on the XZ plane) and an eave-elevation grip at the footprint centroid (raise/lower along +Y). ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### RoofOptions ```typescript theme={null} interface RoofOptions extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.ROOF; profile: RoofProfile; roofType: RoofType; pitch: number; overhang: number; thickness: number; elevation: number; ridgeDirection?: number; massStyle: boolean; color: number; material: RoofMaterial; } ``` ### RoofProfile Polygon profile for a Roof — alias of PolygonProfile. ```typescript theme={null} type RoofProfile = PolygonProfile; ``` ### RoofType ```typescript theme={null} enum RoofType { FLAT = 'FLAT', GABLE = 'GABLE', HIP = 'HIP', SHED = 'SHED', } ``` ### RoofMaterial ```typescript theme={null} enum RoofMaterial { TILE = 'TILE', METAL = 'METAL', MEMBRANE = 'MEMBRANE', SHINGLE = 'SHINGLE', OTHER = 'OTHER', } ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Slab Source: https://docs.ploterr.com/floorkit/api/architecture/slab Slab is a plate with thickness. Use it for floor plates, terraces and landings. ## Import ```typescript theme={null} import { Slab } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Slab, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const slab = new Slab({ labelName: 'First-floor slab', elevation: 3, thickness: 0.25, profile: { outerRing: [ [0, 0], [6, 0], [6, 4], [0, 4], ], holes: [ [ [2, 1], [3, 1], [3, 2], [2, 2], ], ], }, }); try { slab.setOPConfig({ ...slab.getOPConfig(), thickness: 0.3 }); console.log(slab.toDrawing('top'), slab.getHostedOpenings()); } finally { slab.dispose(); } } ``` ## Notes * Profile points are `[x, z]` in metres. `elevation` is the bottom of the slab. The slab extends upward. * For a hole that shows in the plan and in 3D, put it in `profile.holes`. * `addOpening` cuts a hole from an `Opening`. Call `removeOpening` before you dispose that opening. * The `slabThickness` setter keeps the thickness at 0.05 m or more. * `setOPConfig` needs the full options. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements IShape, IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Polygon profile in the world XZ plane. `outerRing` is auto-closed and normalized to CW (the convention `opengeometry.Polygon` expects). Holes are auto-closed and normalized to CCW. World Y of the bottom face of the slab. The profile is extruded upward by `thickness`. ogids of openings whose `opening3D` Solids should be CSG-subtracted from the extruded slab at build time. Populated by `addOpening` / `removeOpening`. ## Properties Push/pull alias for the GUIEditor — the extrude dimension is the thickness. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### addOpening ```typescript theme={null} addOpening(openingElement: Opening): void; ``` Attach an opening element whose `opening3D` Solid will be CSG-subtracted from the extruded slab. ### removeOpening ```typescript theme={null} removeOpening(ogid: string): void; ``` ### getHostedOpenings ```typescript theme={null} getHostedOpenings(): Opening[]; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` Vector-export roots (RFC §7). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Produce the slab's plan footprint as a standardized region primitive (outer ring + profile holes), from its authored 2D polygon. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): SlabOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: SlabOptions): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### SlabOptions ```typescript theme={null} interface SlabOptions extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.SLAB; profile: SlabProfile; thickness: number; color: number; material: SlabMaterial; elevation: number; openings: string[]; } ``` ### SlabProfile Polygon profile for a Slab — alias of PolygonProfile. ```typescript theme={null} type SlabProfile = PolygonProfile; ``` ### SlabMaterial ```typescript theme={null} enum SlabMaterial { CONCRETE = 'CONCRETE', STEEL = 'STEEL', TIMBER = 'TIMBER', COMPOSITE = 'COMPOSITE', OTHER = 'OTHER', } ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`Opening`](/floorkit/api/architecture/opening) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Space Source: https://docs.ploterr.com/floorkit/api/architecture/space Space is a room or a zone. It has a name, a number, an area and a perimeter. Use it for room labels and area lists. ## Import ```typescript theme={null} import { Space } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Space, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const room = new Space({ name: 'Studio', number: '101', elevation: 0, showLabel: false, show3D: true, height: 3, profile: { outerRing: [ [0, 0], [6, 0], [6, 4], [0, 4], ], holes: [ [ [2, 1], [3, 1], [3, 2], [2, 2], ], ], }, }); try { room.setOPConfig({ name: 'Workshop' }); console.log(room.area, room.perimeter, room.toDrawing('top')); } finally { room.dispose(); } } ``` ## Notes * Profile points are `[x, z]` in metres. `area` is in square metres. * Holes are subtracted from the area. `perimeter` measures the outer boundary. * You give the boundary. Floorkit does not find rooms from walls. * For a text label, call `setGlyphScene(scene, camera)` before you create the space. * `show3D` shows the room as a translucent volume. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements IShape, IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Room name shown as the first label line (e.g. "Kitchen"). Room number shown on the second label line (e.g. "101"). Room boundary in the world XZ plane. `outerRing` is auto-closed and normalized to CW (the convention `opengeometry.Polygon` expects). Holes are auto-closed and normalized to CCW and are excluded from `area`. World Y of the room's floor plane. Height of the optional 3D volume (profile × height). Show the translucent 3D volume in model view. Default off — a Space is primarily a plan-view element. Show the centroid name/number label in plan view. Label text height in world meters. ## Properties Push/pull alias for the GUIEditor — the extrude dimension is the height. Read-only. Net floor area in m² — outer ring minus holes. Computed, read-only. Read-only. Outer-boundary length in m. Computed, read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface; a host inspector renders this generically. ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` Vector-export roots (RFC §7). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Plan footprint region plus the name/number·area texts at the centroid. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): SpaceOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### beginEdit ```typescript theme={null} beginEdit(): void; ``` Start one live edit; a second `beginEdit` before `endEdit` keeps the first start. ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` End the live edit; its exact undo and redo, or undefined when no option changed. ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Draggable handles: one grip per boundary vertex (move on the XZ plane) and a height grip at the centroid (raise/lower the 3D volume along +Y). ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### SpaceOptions ```typescript theme={null} interface SpaceOptions extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.SPACECONTAINER; name: string; number: string; profile: SpaceProfile; elevation: number; height: number; show3D: boolean; showLabel: boolean; fontSize: number; color: number; } ``` ### SpaceProfile Polygon profile for a Space — alias of PolygonProfile. ```typescript theme={null} type SpaceProfile = PolygonProfile; ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`Event`](/floorkit/api/architecture/wall-network#event) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Stair Source: https://docs.ploterr.com/floorkit/api/architecture/stair Stair makes a straight, L-shaped or U-shaped stair with equal risers. It also draws the plan symbol. ## Import ```typescript theme={null} import { Stair, computeStairLayout } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Stair, StairLayout, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const stair = new Stair({ layout: StairLayout.L_SHAPE, origin: [0, 0], elevation: 0, width: 1.1, totalRise: 3, treadDepth: 0.28, maxRiserHeight: 0.19, landingDepth: 1.2, turnDirection: 'left', }); try { stair.setOPConfig({ totalRise: 3.1 }); console.log(stair.riserCount, stair.actualRiserHeight, stair.layoutWarnings); console.log(stair.toDrawing('top', { cutElevation: 1.2 })); } finally { stair.dispose(); } } ``` ## Notes * Dimensions are in metres. `origin` is `[x, z]`. `rotation` is in degrees. * Floorkit calculates the number of risers from the rise. Read `actualRiserHeight` for the result. * Layout warnings tell you about steps that are not comfortable. They are not a building-code approval. * `attachRailing` makes a railing follow the stair. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements IShape, IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Plan position of the first flight's walkline start: \[x, z]. Plan rotation in degrees about `origin` (CCW viewed from above). World Y of the stair base. Flight width (m). Level-to-level rise (m). Tread depth / going (m); clamped to ≥ 0.1, warned below 0.25. Optional target riser height (m); the solver auto-counts equal risers at or below `min(riserHeight, maxRiserHeight)`. Hard riser cap (m). Landing depth (m), clamped to ≥ width (L/U layouts). L/U turn direction walking up: "left" or "right". Draw the conventional break line in plan. ## Properties Fires after every geometry rebuild — hosted railings re-derive their paths from it (mirrors `onWallGeometryChanged`/`onOpeningUpdated`). Read-only. Solved riser count. Computed, read-only. Read-only. Solved equal riser height (m). Computed, read-only. Read-only. Total walkline length in plan (m). Computed, read-only. Read-only. Industry-rule warnings from the last layout solve (riser cap, tread minimum, 2R+T comfort). Empty when the stair is code-friendly. Read-only. The solved flight/landing layout in stair-local coordinates. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface; a host inspector renders this generically. ### getPhysicalParts ```typescript theme={null} getPhysicalParts(): readonly PhysicalElementPart[]; ``` Borrow actual material parts; descriptors contain no seeds, cutters or symbols. ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### getRailingPath ```typescript theme={null} getRailingPath(side: "left" | "right", inset?: number): Vec3[]; ``` Rail base path along one side of the stair: flight start → flight top at the step-walking-surface heights, then level AROUND the landing — never diagonally across it. ### attachRailing ```typescript theme={null} attachRailing(railing: Railing, opts?: { side?: "left" | "right"; inset?: number; }): void; ``` Host a railing on this stair: derives its base path from the flight nosings on the chosen side and re-derives it on every stair rebuild (subscribes `onStairUpdated` — the wall `attachDoor` re-bind pattern). ### detachRailing ```typescript theme={null} detachRailing(railing: Railing): void; ``` Unhost a railing: stops syncing its path (the path itself is kept). ### getHostedRailings ```typescript theme={null} getHostedRailings(): Railing[]; ``` Railings currently hosted on this stair. ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` Vector-export roots (RFC §7). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView, options?: StairPlanDrawingOptions): DrawingPrimitive[]; ``` Plan symbol as standardized primitives, emitted parametrically from the solved layout: flight/landing regions, nosing lines, the walkline with its arrowhead, and the break line. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Draggable handles for a parametric stair: the plan `origin` (free on the floor plane), an `elevation` grip (vertical), and `width`, `going` and `landing` grips that slide across or along the first flight. ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): StairOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### beginEdit ```typescript theme={null} beginEdit(): void; ``` Start one live edit; a second `beginEdit` before `endEdit` keeps the first start. ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` End the live edit; its exact undo and redo, or undefined when no option changed. ### dispose ```typescript theme={null} dispose(): void; ``` ## computeStairLayout Solve the riser count and lay out flights/landings for the requested configuration. ```typescript theme={null} function computeStairLayout(input: StairLayoutInput): StairLayoutResult; ``` ## Types ### StairOptions ```typescript theme={null} interface StairOptions extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.STAIR; layout: StairLayout; origin: PointXZ; rotation: number; elevation: number; width: number; totalRise: number; treadDepth: number; riserHeight?: number; maxRiserHeight: number; landingDepth: number; turnDirection: StairTurnDirection; showBreakLine: boolean; color: number; } ``` ### StairLayout Pure stair layout solver — riser math and flight/landing plan rectangles in stair-local coordinates, separated from the element class so the industry rules are unit-testable without the WASM kernel. ```typescript theme={null} enum StairLayout { STRAIGHT = 'STRAIGHT', L_SHAPE = 'L_SHAPE', U_SHAPE = 'U_SHAPE', } ``` ### StairTurnDirection ```typescript theme={null} type StairTurnDirection = 'left' | 'right'; ``` ### StairLayoutResult ```typescript theme={null} interface StairLayoutResult { riserCount: number; actualRiserHeight: number; treadCount: number; runLength: number; landingSide: number; flights: StairFlight[]; landings: StairLanding[]; warnings: string[]; } ``` Total tread rectangles across all flights. Total walkline length in plan (flights + landings). Clamped landing square side, `max(landingDepth, width)`. 0 for STRAIGHT. ### StairPlanDrawingOptions View-only options; never change the saved stair dimensions or solids. ```typescript theme={null} interface StairPlanDrawingOptions { cutElevation?: number; } ``` Finite world Y of the plan cut. ### StairLayoutInput ```typescript theme={null} interface StairLayoutInput { layout: StairLayout; width: number; totalRise: number; treadDepth: number; riserHeight?: number; maxRiserHeight?: number; landingDepth?: number; turnDirection?: StairTurnDirection; } ``` Flight width (m). Level-to-level rise (m). Tread depth / going (m). Target riser height (m); the solver picks the count whose equal risers come closest below this. Defaults to 0.178. Hard riser cap (m). Defaults to 0.19. Landing depth (m), clamped to ≥ width (L/U layouts). L/U turn direction, viewed from above walking up: "left" turns toward −Z, "right" toward +Z. ### StairFlight ```typescript theme={null} interface StairFlight { riserCount: number; baseElevation: number; direction: PointXZ; steps: StairStep[]; walkline: { start: PointXZ; end: PointXZ; }; } ``` Risers this flight climbs (its tread count is `risers − 1` when it ends at a landing — the landing is the arrival surface — and `risers` for the final flight, whose top tread represents the arrival level). Elevation of the flight's first tread's base above the stair base. Unit run direction in local XZ. Walkline along the flight centerline, base → top. ### StairLanding ```typescript theme={null} interface StairLanding { rect: StairRect; topElevation: number; walkline: { start: PointXZ; end: PointXZ; }; } ``` Walkline across the landing connecting the adjoining flights. ### StairStep ```typescript theme={null} interface StairStep { rect: StairRect; topElevation: number; } ``` Plan rectangle of the tread. Elevation of the tread's walking surface above the stair base. ### StairRect Axis-aligned-in-local-space plan rectangle, CCW \[corner, +run, +run+across, +across]. ```typescript theme={null} type StairRect = [PointXZ, PointXZ, PointXZ, PointXZ]; ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`Railing`](/floorkit/api/architecture/railing) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`Event`](/floorkit/api/architecture/wall-network#event) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PhysicalElementPart`](/floorkit/api/architecture/door#physicalelementpart) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PointXZ`](/floorkit/api/primitives/polygon-maths#pointxz) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # validateBuildingModel Source: https://docs.ploterr.com/floorkit/api/architecture/validate-building-model validateBuildingModel checks a building model and returns all problems. It does not need the geometry kernel, so it also runs on a server. Use it for a model that comes from a file, an API or an AI agent. ## Import ```typescript theme={null} import { validateBuildingModel, assertValidBuildingModel, BmValidationError, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { validateBuildingModel } from '@opengeometry/floorkit'; export function reportProblems(input: unknown): boolean { const result = validateBuildingModel(input); for (const issue of result.errors) console.error(issue.path, issue.code, issue.message); for (const issue of result.warnings) console.warn(issue.path, issue.code, issue.message); return result.ok; } ``` ## Notes * Each problem has a `path`, a `code` and a `message`. * `assertValidBuildingModel` returns the typed model, or throws `BmValidationError`. * An opening that overlaps another opening, or whose offset is not on its wall, is an error. Floorkit does not move it. * Whether an opening fits between the joints of its wall is a fact of the build, not of this check: the build reports `opening-does-not-fit` in `warnings`. * A valid model can fail in the build step, because the build also checks the geometry. ## Signature ```typescript theme={null} function validateBuildingModel(document: unknown): BmValidationResult; ``` ## Parameters ## Returns ```typescript theme={null} BmValidationResult; ``` ## assertValidBuildingModel Validate and return the document, throwing `BmValidationError` listing every problem. ```typescript theme={null} function assertValidBuildingModel(document: unknown): BuildingModel; ``` ## BmValidationError ```typescript theme={null} class BmValidationError extends Error { readonly issues: BmIssue[]; constructor(issues: BmIssue[]); } ``` ## Types ### BuildingModel ```typescript theme={null} interface BuildingModel { schema: 'floorkit.building' | 'floorkit.building-model'; version: 1 | 2 | 3 | 4; units: 'meters'; upAxis: 'y'; id: string; name?: string; metadata?: Record; propertySets?: BmPsets; psets?: BmPsets; site?: BmSite; buildings: BmBuilding[]; assets?: Record; conditioning?: BmConditioning; } ``` Discriminator. Identifiers written by earlier versions are accepted on input and replaced by these; anything else is rejected rather than guessed at. An assertion, not a conversion knob. Spelled to match `OPBaseDocument.units`. Y-up, matching the Three.js scene and every element's own convention. v2 interoperable name. `psets` remains readable for v1 documents. Optional site framing. Present so a multi-building document has somewhere to say where it is. Catalog assets, referenced by instances. `.op` / `.opbase` remain the asset format. Conditioning-export metadata; absent means sensible defaults. ### BmArcObject A circular arc authored in a building working plane; angles are radians. ```typescript theme={null} interface BmArcObject { kind: 'arc'; id: string; workingPlaneId: string; center: [number, number]; radius: number; startAngle: number; sweepAngle: number; color?: BmColor; } ``` Signed sweep, with `0 < abs(sweepAngle) <= 2π`. ### BmCuboidObject ```typescript theme={null} type BmCuboidObject = BmCuboidSizeObject | BmCuboidFreeformObject; ``` ### BmCylinderObject An analytic upright cylinder centered in a horizontal building working plane. ```typescript theme={null} interface BmCylinderObject { kind: 'cylinder'; id: string; workingPlaneId: string; center: [number, number]; radius: number; height: number; color?: BmColor; } ``` ### BmCuboidSizeObject An upright solid dimensioned around a horizontal working-plane center. ```typescript theme={null} interface BmCuboidSizeObject extends BmCuboidBase { mode: 'size'; center: [number, number]; width: number; breadth: number; } ``` ### BmCuboidFreeformObject An upright extrusion with an authored footprint and optional through holes. ```typescript theme={null} interface BmCuboidFreeformObject extends BmCuboidBase { mode: 'freeform'; outerRing: [number, number][]; holes?: [number, number][][]; } ``` ### BmLineObject A straight segment authored in the local coordinates of a building working plane. ```typescript theme={null} interface BmLineObject { kind: 'line'; id: string; workingPlaneId: string; start: [number, number]; end: [number, number]; color?: BmColor; } ``` ### BmPolylineObject An open path authored as vertices in a building working plane. ```typescript theme={null} interface BmPolylineObject { kind: 'polyline'; id: string; workingPlaneId: string; vertices: [number, number][]; color?: BmColor; } ``` ### BmPolygonObject A filled profile authored in a building working plane. ```typescript theme={null} interface BmPolygonObject { kind: 'polygon'; id: string; workingPlaneId: string; outerRing: [number, number][]; holes?: [number, number][][]; color?: BmColor; } ``` ### BmRectangleObject ```typescript theme={null} type BmRectangleObject = BmRectangleSizeObject | BmRectangleFreeformObject; ``` ### BmRectangleSizeObject A rectangle dimensioned around a center in plane-local coordinates. ```typescript theme={null} interface BmRectangleSizeObject extends BmRectangleBase { mode: 'size'; center: [number, number]; width: number; breadth: number; } ``` ### BmRectangleFreeformObject An edited rectangle saved as an arbitrary filled plane-local profile. ```typescript theme={null} interface BmRectangleFreeformObject extends BmRectangleBase { mode: 'freeform'; outerRing: [number, number][]; holes?: [number, number][][]; } ``` ### BmModelObject Extend this discriminated union as additional model primitives are supported. ```typescript theme={null} type BmModelObject = | BmLineObject | BmArcObject | BmPolylineObject | BmPolygonObject | BmRectangleObject | BmCuboidObject | BmCylinderObject; ``` ### BmWorkingPlane A named local 2D frame in world XYZ coordinates. ```typescript theme={null} interface BmWorkingPlane { id: string; name?: string; origin: Vec3; xAxis: Vec3; yAxis: Vec3; } ``` ### BmColor A colour as `#rrggbb`. ```typescript theme={null} type BmColor = string; ``` ### BmSceneCategory ```typescript theme={null} type BmSceneCategory = 'interior' | 'exterior' | 'aerial'; ``` ### BmPsetValue ```typescript theme={null} type BmPsetValue = boolean | number | string; ``` ### BmPsets ```typescript theme={null} type BmPsets = PropertySets; ``` ### BmCameraPose A camera pose. ```typescript theme={null} interface BmCameraPose { position: [number, number, number]; target: [number, number, number]; ogid?: string; t?: number; category?: BmSceneCategory; } ``` ### BmSite The parcel. ```typescript theme={null} interface BmSite { id: string; name?: string; boundary?: PolygonProfile; lotLines?: LotLineClass[]; elevation?: number; geoAnchor?: GeoAnchor; trueNorthDeg?: number; zoning?: ZoningRuleSet; propertySets?: BmPsets; psets?: BmPsets; } ``` Parcel in the world XZ plane; holes are excluded islands. One class per outer-ring edge of `boundary` (edge i = `outerRing[i] → [i+1]`). World Y of grade. Default 0. WGS84 anchor for the local origin, if the project is georeferenced. Degrees from project north (−Z) to true north, positive clockwise from above. The zoning rules this document was evaluated against — values are the consumer's; the schema and evaluator are `zoning/`. ### BmConditioning ```typescript theme={null} interface BmConditioning { sceneId?: string; category?: BmSceneCategory; axisTags?: Record; poses?: BmCameraPose[]; } ``` Default poses. `--cameras` and `--sample-cameras` both override these. ### BmBuilding ```typescript theme={null} interface BmBuilding { id: string; name?: string; buildingType?: string; propertySets?: BmPsets; psets?: BmPsets; workingPlanes?: BmWorkingPlane[]; mass?: BmMass; storeys: BmStorey[]; } ``` Free-form programme tag — `residential`, `office`, … Kept open; consumers disagree on the set. Named world-space frames for model objects owned by this building. The massing volume, when the building is (also) a mass. Its segments REFERENCE storeys in `storeys` by id — the storey list stays the single source of elevations and heights, and a storey may carry zero elements while the mass gives it a body. ### BmMassSegment One extruded segment of a mass (podium, tower, setback), bottom-up. ```typescript theme={null} interface BmMassSegment { id: string; name?: string; footprint: PolygonProfile; height: number; use?: string; color?: BmColor; storeyIds: string[]; } ``` Footprint in the world XZ plane; holes are courtyards. Extrusion height. Must equal the sum of the referenced storeys' heights. `BuildingUse` value, or any string (`custom:` is implied). Colour of this part, `#rrggbb`. A part without one takes the `color` of the mass, else the colour of its use in the palette of the build (`MASS_COLORS_BY_USE`, or `BuildOptions.massColors`), else `#a0b8c8`. Ids of the storeys that tile this segment, bottom-up and contiguous. ### BmMass ```typescript theme={null} interface BmMass { id: string; elevation: number; use?: string; color?: BmColor; propertySets?: BmPsets; segments: BmMassSegment[]; } ``` World Y of the bottom face of `segments[0]`. Colour of every part of the mass that has no `color` of its own, `#rrggbb`. Data on the physical mass element; building-scope data stays on `BmBuilding`. ### BmWallSegmentRef Authored identity of one segment, including a closed run's final wrap segment. ```typescript theme={null} interface BmWallSegmentRef { wallId: string; segmentIndex: number; } ``` ### BmDetachedFaceTrim Auxiliary face trim; axes and host/opening ownership stay in authored walls. ```typescript theme={null} interface BmDetachedFaceTrim { wall: BmWallSegmentRef; end: import('../../elements/walls/wall-model').WallEnd; host: BmWallSegmentRef; source?: 'T' | 'X'; } ``` ### BmWallNetworkState V4 reconstruction state; saved nonautomatic joining is not supported yet. ```typescript theme={null} interface BmWallNetworkState { options: import('../../elements/walls/wall-model').WallNetworkOptions & { autoJoin: true; }; detachedFaceTrims: BmDetachedFaceTrim[]; } ``` ### BmStorey One storey. ```typescript theme={null} interface BmStorey { id: string; name?: string; elevation: number; height: number; usage?: string; areaKind?: AreaKind; propertySets?: BmPsets; psets?: BmPsets; walls?: BmWall[]; modelObjects?: BmModelObject[]; unhostedOpenings?: BmUnhostedOpening[]; wallJoins?: import('../../elements/walls/wall-model').WallJoinOverride[]; wallNetworkState?: BmWallNetworkState; slabs?: BmSlab[]; roofs?: BmRoof[]; columns?: BmColumn[]; beams?: BmBeam[]; spaces?: BmSpace[]; stairs?: BmStair[]; railings?: BmRailing[]; instances?: BmAssetInstance[]; } ``` World Y of this storey's structural floor datum. Floor-to-floor height. Also the default wall height for walls that omit one. Programme for this storey — `retail`, `residential`, … What the plate of this storey counts as (basement, core, parking, ...). Derived when absent: a `parking` use gives `parking`, a storey whose top is at or below the site grade gives `basement`, otherwise `gross`. Zoning counts toward the floor area ratio only the kinds in `countsTowardFAR`. Basic geometry saved in this storey, placed by a building working plane. v3 openings placed in the world XZ plane without a host wall. V4 only. Restored before the first network resolution; never a second wall model. ### BmWall ```typescript theme={null} interface BmWall { id: string; labelName?: string; points: PointXZ[]; closed?: boolean; locationLine?: import('../../elements/walls/wall-model').LocationLine; locationOffset?: number; priority?: number; joins?: { start: import('../../elements/walls/wall-model').WallEndJoin; end: import('../../elements/walls/wall-model').WallEndJoin; }; thickness: number; height?: number; material?: WallMaterial | string; color?: BmColor; propertySets?: BmPsets; psets?: BmPsets; openings?: BmOpening[]; } ``` Centreline in the world XZ plane. Network walls require horizontal paths, so there is no Y here to get wrong; the compiler places it at the storey's elevation. Defaults to the owning storey's `height`. Hosted by this wall. The nesting IS the host relationship. ### BmStationAlong Distance along the wall centreline from `points[0]`, accumulated across segments and including the closing segment when `closed`. ```typescript theme={null} type BmStationAlong = number; ``` ### BmOpening ```typescript theme={null} type BmOpening = BmDoor | BmWindow | BmVoid; ``` ### BmUnhostedOpening An opening placed in the world XZ plane, with no wall cutter or host relationship. ```typescript theme={null} type BmUnhostedOpening = BmUnhostedDoor | BmUnhostedWindow; ``` ### BmUnhostedDoor ```typescript theme={null} type BmUnhostedDoor = Omit & BmUnhostedOpeningPose; ``` ### BmUnhostedWindow ```typescript theme={null} type BmUnhostedWindow = Omit & BmUnhostedOpeningPose; ``` ### BmUnhostedOpeningPose ```typescript theme={null} interface BmUnhostedOpeningPose { position: PointXZ; yawDeg: number; frameDepth?: number; offset?: never; hostWallId?: never; hostId?: never; wallId?: never; } ``` World \[X, Z] position in metres. Degrees in the world XZ plane: +X is 0, turning toward +Z is positive. Former host wall depth, or an authored frame depth. Defaults to 0.15 m. ### BmOpeningBase ```typescript theme={null} interface BmOpeningBase { id: string; labelName?: string; offset: BmStationAlong; propertySets?: BmPsets; psets?: BmPsets; } ``` ### BmDoor ```typescript theme={null} interface BmDoor extends BmOpeningBase { kind: 'door'; width: number; height: number; baseHeight?: number; frameWidth?: number; panelThickness?: number; doorType?: DoorType; doorQuadrant?: 1 | 2 | 3 | 4; swingAngleDegrees?: number; color?: BmColor; frameColor?: BmColor; } ``` Threshold height above the wall base. A real 3D position for a door. Default 0. Which way the door swings, as a 2x2 of hinge side x swing face. ### BmWindow ```typescript theme={null} interface BmWindow extends BmOpeningBase { kind: 'window'; width: number; height: number; sillHeight: number; frameWidth?: number; paneThickness?: number; windowType?: WindowType; frameColor?: BmColor; glassColor?: BmColor; } ``` Bottom of the glass above the wall base — the only vertical control for a window. ### BmVoid A plain unfilled hole — `WallOpening`. ```typescript theme={null} interface BmVoid extends BmOpeningBase { kind: 'void'; width: number; height: number; sillHeight?: number; } ``` Sill height above the wall base. Default 0. ### BmSlabRole Which class to build. ```typescript theme={null} type BmSlabRole = 'slab' | 'floor' | 'ceiling'; ``` ### BmSlab ```typescript theme={null} interface BmSlab { id: string; labelName?: string; role?: BmSlabRole; profile: PolygonProfile; thickness: number; baseOffset: number; material?: SlabMaterial | FloorMaterial | CeilingMaterial; color?: BmColor; propertySets?: BmPsets; psets?: BmPsets; } ``` Metres above the storey's `elevation` to the plate's BOTTOM face (its underside, for a ceiling). ### BmRoof ```typescript theme={null} interface BmRoof { id: string; labelName?: string; profile: PolygonProfile; roofType?: RoofType; pitch?: number; overhang?: number; thickness: number; baseOffset: number; ridgeDirection?: number; massStyle?: boolean; material?: RoofMaterial; color?: BmColor; propertySets?: BmPsets; psets?: BmPsets; } ``` Metres above the storey's `elevation` to the EAVE (the wall-plate top). ### BmStair A stair on a storey. ```typescript theme={null} interface BmStair { id: string; labelName?: string; layout?: StairLayout; origin: PointXZ; rotation?: number; baseOffset: number; width?: number; totalRise?: number; treadDepth?: number; riserHeight?: number; maxRiserHeight?: number; landingDepth?: number; turnDirection?: StairTurnDirection; showBreakLine?: boolean; color?: BmColor; propertySets?: BmPsets; psets?: BmPsets; } ``` Defaults to `STRAIGHT`. Plan position of the first flight's walkline start. Plan rotation in degrees about `origin`, CCW from above. Defaults to 0. Metres above the storey's `elevation` to the stair base. Flight width. Defaults to 1 m. Level-to-level rise. Defaults to the storey's `height`, which is what a stair inside a storey almost always climbs. Tread depth / going. Defaults to 0.28 m. Target riser height; the solver fits equal risers at or below this and `maxRiserHeight`. Hard riser cap. Defaults to 0.19 m. Landing depth for L and U layouts, clamped to at least `width`. Defaults to 1 m. Which way an L or U turns walking up. Defaults to `left`. Draw the conventional break line across the first flight in plan. Defaults to true. ### BmRailing A railing, as a path in WORLD coordinates rather than relative to the storey — a stair-hosted rail follows the flight nosings and therefore climbs through the storey, so a single `baseOffset` could not describe it. ```typescript theme={null} interface BmRailing { id: string; labelName?: string; path: [number, number, number][]; height?: number; color?: BmColor; propertySets?: BmPsets; psets?: BmPsets; } ``` Rail base path, world coordinates, `[x, y, z]` per point. Segments may slope. Rail height above the base path. Defaults to 1 m; guard code typically wants at least that. ### BmColumn ```typescript theme={null} interface BmColumn { id: string; labelName?: string; shape?: ColumnShape; center: PointXZ; rotation?: number; baseOffset: number; height?: number; width?: number; depth?: number; radius?: number; customProfile?: PolygonProfile; material?: ColumnMaterial; color?: BmColor; propertySets?: BmPsets; psets?: BmPsets; } ``` Metres above the storey's `elevation` to the column base. Defaults to the storey's `height`. ### BmBeam ```typescript theme={null} interface BmBeam { id: string; labelName?: string; shape?: BeamShape; start: [number, number, number]; end: [number, number, number]; width?: number; depth?: number; radius?: number; customProfile?: PolygonProfile; material?: ColumnMaterial; color?: BmColor; propertySets?: BmPsets; psets?: BmPsets; } ``` `[x, yOffset, z]` — y is STOREY-RELATIVE, unlike `BeamOptions.start` which is world. ### BmSpace ```typescript theme={null} interface BmSpace { id: string; name: string; number?: string; profile: PolygonProfile; baseOffset?: number; height?: number; show3D?: boolean; color?: BmColor; propertySets?: BmPsets; psets?: BmPsets; } ``` ### BmAssetRef ```typescript theme={null} type BmAssetRef = | { kind: 'external-op'; uri: string; } | { kind: 'embedded-op'; definition: unknown; }; ``` ### BmAssetInstance ```typescript theme={null} interface BmAssetInstance { id: string; assetRef: string; position: [number, number, number]; rotationYDeg?: number; scale?: [number, number, number]; labelName?: string; propertySets?: BmPsets; psets?: BmPsets; } ``` `position.y` is STOREY-RELATIVE; the compiler adds the storey elevation. ### BmIssue ```typescript theme={null} interface BmIssue { path: string; code: string; message: string; openingFit?: BmOpeningFit; } ``` JSON path into the document, e.g. `buildings[0].storeys[0].walls[1].openings[2].offset`. Stable machine code, so a caller can branch without parsing prose. One sentence, with the numbers in it. The numbers of an `opening-does-not-fit` issue. ### BmOpeningFit Where a hosted opening fits on its wall, in offsets along the saved centreline of that wall. ```typescript theme={null} interface BmOpeningFit { wallId: string; openingId: string; offset: number; fitRange: { start: number; end: number; } | null; nearestOffset: number | null; reasons: WallOpeningFitReason[]; } ``` The offset that was asked or saved. The range of fitting offsets on its wall segment that holds `nearestOffset`; null when none. The fitting offset nearest to `offset`, or null when the opening fits nowhere there. Why it does not fit; `t-junction` alone means it is over the seam of a T stem. ### BmValidationResult ```typescript theme={null} interface BmValidationResult { ok: boolean; errors: BmIssue[]; warnings: BmIssue[]; } ``` ### BmCuboidBase The package does not export this type by name. ```typescript theme={null} interface BmCuboidBase { kind: 'cuboid'; id: string; workingPlaneId: string; height: number; color?: BmColor; } ``` ### BmRectangleBase The package does not export this type by name. ```typescript theme={null} interface BmRectangleBase { kind: 'rectangle'; id: string; workingPlaneId: string; color?: BmColor; } ``` ## Related * [`AreaKind`](/floorkit/api/architecture/massing-helpers#areakind) * [`BeamShape`](/floorkit/api/architecture/beam#beamshape) * [`CeilingMaterial`](/floorkit/api/architecture/ceiling#ceilingmaterial) * [`ColumnMaterial`](/floorkit/api/architecture/column#columnmaterial) * [`ColumnShape`](/floorkit/api/architecture/column#columnshape) * [`DoorType`](/floorkit/api/architecture/door#doortype) * [`FloorMaterial`](/floorkit/api/architecture/floor#floormaterial) * [`GeoAnchor`](/floorkit/api/world/site#geoanchor) * [`LocationLine`](/floorkit/api/architecture/wall#locationline) * [`LotLineClass`](/floorkit/api/world/site#lotlineclass) * [`PointXZ`](/floorkit/api/primitives/polygon-maths#pointxz) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`PropertySets`](/floorkit/api/architecture/op-element#propertysets) * [`RoofMaterial`](/floorkit/api/architecture/roof#roofmaterial) * [`RoofType`](/floorkit/api/architecture/roof#rooftype) * [`SlabMaterial`](/floorkit/api/architecture/slab#slabmaterial) * [`StairLayout`](/floorkit/api/architecture/stair#stairlayout) * [`StairTurnDirection`](/floorkit/api/architecture/stair#stairturndirection) * [`Vec3`](/floorkit/api/architecture/wall#vec3) * [`WallEnd`](/floorkit/api/architecture/wall-network#wallend) * [`WallEndJoin`](/floorkit/api/architecture/wall#wallendjoin) * [`WallJoinOverride`](/floorkit/api/architecture/wall-network#walljoinoverride) * [`WallMaterial`](/floorkit/api/architecture/wall#wallmaterial) * [`WallNetworkOptions`](/floorkit/api/architecture/wall-network#wallnetworkoptions) * [`WallOpeningFitReason`](/floorkit/api/architecture/wall#wallopeningfitreason) * [`WindowType`](/floorkit/api/architecture/window#windowtype) * [`ZoningRuleSet`](/floorkit/api/world/zoning-rules#zoningruleset) # Wall Source: https://docs.ploterr.com/floorkit/api/architecture/wall Wall is one straight or curved wall. It has an axis, a thickness, a height and a base elevation. To join walls at corners, add them to a `WallNetwork`. ## Import ```typescript theme={null} import { Wall } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Wall, WallMaterial, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const wall = new Wall({ labelName: 'Partition', axis: { kind: 'line', start: [0, 0, 0], end: [5, 0, 0] }, thickness: 0.2, height: 3, baseElevation: 0, locationLine: 'centre', locationOffset: 0, material: WallMaterial.CONCRETE, color: 0xcccccc, priority: 0, joins: { start: { allow: true }, end: { allow: true } }, }); try { wall.setOPConfig({ height: 3.2 }); console.log(wall.getOPConfig(), wall.toDrawing('top')); } finally { wall.dispose(); } } ``` ## Notes * Values are in metres and the Y axis points up. The angles of a circular axis are in radians. * The constructor needs all `WallOptions`. `setOPConfig` accepts some of them. * `locationLine` sets which line of the wall follows the axis: the centre or one face. * To add a door or a window, call `attachDoor` or `attachWindow`, then `commitOpeningCuts()`. * `dispose()` detaches the openings. Dispose the door and window objects separately. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends OPElement implements PlanVectorExportable, IPlaceable` ## Constructor ```typescript theme={null} constructor(options: WallOptions); ``` ## Properties Read-only. Read-only. Read-only. Read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### refreshOutline ```typescript theme={null} refreshOutline(solids?: readonly AnalyticSolid[]): void; ``` ### anchorSpace ```typescript theme={null} anchorSpace(): "world"; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ], snapRadius?: number): void; ``` ### beginOpeningGesture ```typescript theme={null} beginOpeningGesture(): void; ``` ### beginWallDragPreview ```typescript theme={null} beginWallDragPreview(): void; ``` Show moving opening outlines while an edited wall has an uncut preview body. ### endWallDragPreview ```typescript theme={null} endWallDragPreview(): void; ``` ### commitOpeningCuts ```typescript theme={null} commitOpeningCuts(): void; ``` ### prepareOpeningCuts ```typescript theme={null} prepareOpeningCuts(): { commit: () => void; rollback: () => void; }; ``` ### abortOpeningGesture ```typescript theme={null} abortOpeningGesture(): void; ``` ### getHostedOpenings ```typescript theme={null} getHostedOpenings(): HostedOpening[]; ``` ### attachDoor ```typescript theme={null} attachDoor(opening: Door, placement?: WallHostPlacement): void; ``` ### attachWindow ```typescript theme={null} attachWindow(opening: Window, placement?: WallHostPlacement): void; ``` ### attachWallOpening ```typescript theme={null} attachWallOpening(opening: WallOpening, placement?: WallHostPlacement): void; ``` ### openingFit ```typescript theme={null} openingFit(input: WallOpeningFitInput): WallOpeningFit; ``` The one fit rule of a hosted opening: its whole cut in a net run of this resolved wall. ### attachOpening ```typescript theme={null} attachOpening(opening: HostedOpening, placement?: WallHostPlacement): void; ``` ### detachOpening ```typescript theme={null} detachOpening(openingOrId: HostedOpening | string): void; ``` ### moveOpening ```typescript theme={null} moveOpening(openingOrId: HostedOpening | string, placement: WallHostPlacement): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): WallOptions; ``` ### getNetworkConfig ```typescript theme={null} getNetworkConfig(): Readonly; ``` ### restoreConfig ```typescript theme={null} restoreConfig(config: WallOptions): void; ``` ### beginEdit ```typescript theme={null} beginEdit(): void; ``` ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` ### deleteFromNetwork ```typescript theme={null} deleteFromNetwork(): { undo: () => void; redo: () => void; } | undefined; ``` ### setOPConfig ```typescript theme={null} setOPConfig(update: Partial): void; ``` ### bind ```typescript theme={null} bind(network: WallNetwork | undefined): void; ``` ### assignAxis ```typescript theme={null} assignAxis(axis: WallOptions["axis"]): void; ``` ### setResolvedFootprint ```typescript theme={null} setResolvedFootprint(footprint: WallFootprint): boolean; ``` ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Produce standardized drawing primitives for a view directly from the element's parametric data (the 0.3.0 contract). ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): AnalyticSolid[]; ``` ### getOpeningDiagnostics ```typescript theme={null} getOpeningDiagnostics(junctions?: readonly Junction[], reportPendingCuts?: boolean): JunctionDiagnostic[]; ``` Opening diagnostics of this wall; `junctions` are the junctions of this wall when given. ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### WallOptions ```typescript theme={null} interface WallOptions extends HasPropertySets { ogid?: string; labelName: string; axis: WallAxis; thickness: number; height: number; baseElevation: number; locationLine: LocationLine; locationOffset: number; material: WallMaterial | string; color: number; priority: number; joins: { start: WallEndJoin; end: WallEndJoin; }; } ``` ### WallFootprint ```typescript theme={null} interface WallFootprint { rings: FootprintRing[]; } ``` ### HostedOpening The package does not export this type by name. ```typescript theme={null} type HostedOpening = Door | Window | WallOpening; ``` ### WallHostPlacement Where to place an opening on its host wall. The package does not export this type by name. ```typescript theme={null} interface WallHostPlacement { offset?: number; baseHeight?: number; } ``` Arc length from the host wall's start point. Bottom of the rough opening above the wall's base. Never a world Y. ### WallOpeningFitInput A full-width cut asked at one centre station of a wall, in metres from the wall base. ```typescript theme={null} interface WallOpeningFitInput { station: number; width: number; bottom: number; top: number; openingId: string; avoidTSeams?: boolean; } ``` Count the seam of a T stem against the fit, as an added opening does: an opening over a seam does not fit, and the nearest station and fit range keep clear of the seams. ### WallOpeningFit Where a hosted opening fits on one wall: stations are centre distances along its axis. ```typescript theme={null} interface WallOpeningFit { fits: boolean; reasons: WallOpeningFitReason[]; nearestStation: number | null; fitRange: { start: number; end: number; } | null; } ``` The whole cut lies in a net run of the wall, inside its height: the wall cuts it here. Why it does not fit; `t-junction` when the cut lies over the seam of a T stem. The fitting centre station nearest to the asked one, or null when it fits nowhere. The range of fitting centre stations that holds `nearestStation`, or null. ### Junction ```typescript theme={null} interface Junction { key: string; kind: JunctionKind; point: XZ; members: Array<{ wallId: string; at: WallEnd | 'path'; station?: number; }>; order: string[]; type: JoinType; caps: Record; diagnostics: JunctionDiagnostic[]; } ``` ### JunctionDiagnostic ```typescript theme={null} type JunctionDiagnostic = | { code: 'beveled' | 'unresolvable' | 'absorbed' | 'disconnected' | 'detached' | 'clamped'; junctionKey: string; wallIds: string[]; } | { code: 'clash-overlap' | 'self-loop'; wallIds: string[]; } | { code: 'opening-over-junction' | 'opening-boolean-unavailable'; wallId: string; openingId: string; } | { code: 'opening-outside-net-run' | 'opening-rejected-at-crossing'; wallId: string; openingId: string; fit: WallOpeningFit; }; ``` ### Vec3 Strict 3-tuple of numbers. ```typescript theme={null} type Vec3 = [number, number, number]; ``` ### LocationLine ```typescript theme={null} type LocationLine = 'centre' | 'left' | 'right'; ``` ### WallMaterial ```typescript theme={null} enum WallMaterial { CONCRETE = 'CONCRETE', BRICK = 'BRICK', WOOD = 'WOOD', GLASS = 'GLASS', OTHER = 'OTHER', } ``` ### WallEndJoin ```typescript theme={null} interface WallEndJoin { allow: boolean; type?: JoinType; } ``` ### FootprintRing ```typescript theme={null} interface FootprintRing { edges: FootprintEdge[]; holes?: FootprintEdge[][]; } ``` ### WallOpeningFitReason Why a hosted opening does not fit at a station; `t-junction` alone still fits (it is cut). ```typescript theme={null} type WallOpeningFitReason = | 'too-wide' | 'x-crossing' | 'outside-net-run' | 'outside-height' | 't-junction'; ``` ### JunctionKind ```typescript theme={null} type JunctionKind = 'L' | 'continuation' | 'T' | 'X' | 'Y' | 'multi' | 'free'; ``` ### FootprintEdge ```typescript theme={null} type FootprintEdge = | { kind: 'line'; from: XZ; to: XZ; role: EdgeRole; } | { kind: 'arc'; center: XZ; radius: number; from: number; to: number; ccw: boolean; role: EdgeRole; }; ``` ### EdgeRole ```typescript theme={null} type EdgeRole = 'face' | 'cap' | 'seam'; ``` ## Related * [`Door`](/floorkit/api/architecture/door) * [`OPElement`](/floorkit/api/architecture/op-element) * [`WallNetwork`](/floorkit/api/architecture/wall-network) * [`WallOpening`](/floorkit/api/architecture/wall-opening) * [`Window`](/floorkit/api/architecture/window) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`JoinType`](/floorkit/api/architecture/wall-network#jointype) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`Profile`](/floorkit/api/primitives/line-primitive#profile) * [`WallAxis`](/floorkit/api/architecture/wall-network#wallaxis) * [`WallEnd`](/floorkit/api/architecture/wall-network#wallend) * [`XZ`](/floorkit/api/architecture/wall-network#xz) # Wall helpers Source: https://docs.ploterr.com/floorkit/api/architecture/wall-helpers Read the length, frame and footprint of a wall axis. ## Import ```typescript theme={null} import { wallAxisLength, wallAxisFrameAt, wallAxisChainFrameAt, stableWallAngle, freeFootprint, validateWallFootprint, } from '@opengeometry/floorkit'; ``` ## wallAxisLength ```typescript theme={null} function wallAxisLength(axis: WallAxis): number; ``` ## wallAxisFrameAt Angles increase from +X toward +Z; station is centerline arc length in metres. ```typescript theme={null} function wallAxisFrameAt(axis: WallAxis, station: number, thickness?: number): StationWallFrame; ``` ## wallAxisChainFrameAt ```typescript theme={null} function wallAxisChainFrameAt( axes: readonly WallAxis[], station: number, thickness?: number, ): StationWallFrame; ``` ## stableWallAngle ```typescript theme={null} function stableWallAngle(value: number): number; ``` ## freeFootprint ```typescript theme={null} function freeFootprint(options: WallOptions): WallFootprint; ``` ## validateWallFootprint ```typescript theme={null} function validateWallFootprint(footprint: WallFootprint, config: WallOptions): void; ``` ## Related * [`StationWallFrame`](/floorkit/api/architecture/circular-wall#stationwallframe) * [`WallAxis`](/floorkit/api/architecture/wall-network#wallaxis) * [`WallFootprint`](/floorkit/api/architecture/wall#wallfootprint) * [`WallOptions`](/floorkit/api/architecture/wall#walloptions) # WallNetwork Source: https://docs.ploterr.com/floorkit/api/architecture/wall-network WallNetwork joins walls. Add walls to one network and Floorkit resolves each corner, T-junction and crossing. ## Import ```typescript theme={null} import { WallNetwork } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { WallNetwork, WallMaterial, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const network = new WallNetwork({ defaultJoin: 'miter' }); try { const walls = network.addPolyline( [ [0, 0, 0], [5, 0, 0], [5, 0, 4], ], { labelName: 'Perimeter', thickness: 0.2, height: 3, baseElevation: 0, locationLine: 'centre', locationOffset: 0, material: WallMaterial.CONCRETE, color: 0xcccccc, priority: 0, joins: { start: { allow: true }, end: { allow: true } }, }, ); const report = network.resolve(); console.log(report.junctions, report.diagnostics); console.log(walls.map((wall) => wall.toDrawing('top'))); } finally { network.dispose(); } } ``` ## Notes * `add` registers one wall. Call `resolve()` after you add a group of walls. `addPolyline` makes one wall for each segment and resolves them. * Read `ResolveReport.diagnostics` after `resolve()`. It lists openings that moved and joins that were not possible. * After you change a live opening, call `commitOpeningCuts()` on its wall. * The network is not a scene object. Add each wall to your scene. * `dispose()` disposes the walls of the network. Dispose doors and windows separately. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). ## Constructor ```typescript theme={null} constructor(options?: Partial); ``` ## Properties Read-only. Read-only. Read-only. Read-only. Read-only. Read-only. Metres from every wall beyond which a dragged door or window leaves its wall; `undefined` keeps it on walls. ## Methods ### subscribeInvalidation ```typescript theme={null} subscribeInvalidation(callback: (changed: readonly string[]) => void): () => void; ``` ### syncArcConfigEndpoints ```typescript theme={null} syncArcConfigEndpoints(wallId: string, axis: WallAxis): void; ``` Keep a property-edited arc's free endpoint nodes on the authored circle. ### beginEdit ```typescript theme={null} beginEdit(): void; ``` ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` ### atomicModelEdit ```typescript theme={null} atomicModelEdit(edit: () => T): T; ``` ### invalidateWall ```typescript theme={null} invalidateWall(id: string, diagnostics?: JunctionDiagnostic[], extraChanged?: string[]): void; ``` Tell listeners that a wall changed; without `diagnostics` they get its opening diagnostics. ### snapAxisPoint ```typescript theme={null} snapAxisPoint(cursor: XZ, radius: number, origin?: XZ): { point: XZ; kind: "endpoint" | "midpoint" | "perpendicular"; } | undefined; ``` ### snapEndPoint ```typescript theme={null} snapEndPoint(wallOrId: Wall | string, end: WallEnd, cursor: XZ, radius: number): XZ; ``` ### nearestWall ```typescript theme={null} nearestWall(point: XZ, maxDistance: number): { wall: Wall; station: number; distance: number; } | undefined; ``` ### outlineNeighbors ```typescript theme={null} outlineNeighbors(wall: Wall): Wall[]; ``` ### lastResolveReport ```typescript theme={null} lastResolveReport(): ResolveReport; ``` The junctions and diagnostics of the last resolve, as they are now, without a new resolve. ### junctionsOfWall ```typescript theme={null} junctionsOfWall(wallId: string): readonly Junction[]; ``` The junctions of the last resolve that `wallId` is a member of. ### openingDiagnostics ```typescript theme={null} openingDiagnostics(): Extract[]; ``` The current opening diagnostics of the hosted openings: an opening that does not fit where it is has an entry with its `fit`; one over a T seam has `opening-over-junction`. ### isSplitCrossingWall ```typescript theme={null} isSplitCrossingWall(wallId: string, junctions?: readonly Junction[]): boolean; ``` ### tSeamStations ```typescript theme={null} tSeamStations(host: Wall, junctions?: readonly Junction[]): { start: number; end: number; }[]; ``` The stations along `host` where the seam of a T stem lies on a face of `host`. ### openingJunctionDiagnostics ```typescript theme={null} openingJunctionDiagnostics(host: Wall, openingId: string, bounds: { startAlongWall: number; endAlongWall: number; }, junctions?: readonly Junction[]): JunctionDiagnostic[]; ``` ### registerOpening ```typescript theme={null} registerOpening(opening: Door | Window | WallOpening, point: XZ, maxDistance: number): { undo: () => void; redo: () => void; } | undefined; ``` ### beginHostedOpeningEdit ```typescript theme={null} beginHostedOpeningEdit(opening: HostedOpening): void; ``` ### previewHostedOpeningEdit ```typescript theme={null} previewHostedOpeningEdit(opening: HostedOpening, position: Vec3, snapRadius: number): void; ``` ### commitHostedOpeningEdit ```typescript theme={null} commitHostedOpeningEdit(opening: HostedOpening): { undo: () => void; redo: () => void; } | undefined; ``` ### rehostOpening ```typescript theme={null} rehostOpening(opening: Door | Window | WallOpening, point: XZ, maxDistance: number): { undo: () => void; redo: () => void; } | undefined; ``` ### add ```typescript theme={null} add(wall: Wall): Wall; ``` ### addArc ```typescript theme={null} addArc(axis: Extract, options: Omit, id?: string): Wall; ``` ### addPolyline ```typescript theme={null} addPolyline(points: readonly Vec3[], options: Omit, ids?: readonly string[], resolve?: boolean): Wall[]; ``` ### replaceSavedWalls ```typescript theme={null} replaceSavedWalls(saved: { runs: readonly { points: readonly Vec3[]; options: Omit; ids: readonly string[]; }[]; options: Partial | undefined; }, placed?: (walls: readonly Wall[]) => void): void; ``` Bring the walls and options to the saved runs; no joint, trim or order of before stays, and after a throw the network holds the walls of both states until the next call repairs it. ### setJoin ```typescript theme={null} setJoin(junctionKey: string, type: JoinType, order?: string[]): ResolveReport; ``` ### disallowJoin ```typescript theme={null} disallowJoin(wallOrId: Wall | string, end: WallEnd, disallow?: boolean): void; ``` ### remove ```typescript theme={null} remove(wallOrId: Wall | string): void; ``` ### moveEnd ```typescript theme={null} moveEnd(wallOrId: Wall | string, end: WallEnd, point: XZ): ResolveReport; ``` ### translateWall ```typescript theme={null} translateWall(wallOrId: Wall | string, delta: XZ): ResolveReport; ``` ### resolve ```typescript theme={null} resolve(dirtyWallIds?: ReadonlySet): ResolveReport; ``` ### restoreDetachedFaceTrims ```typescript theme={null} restoreDetachedFaceTrims(trims: readonly WallDetachedFaceTrim[]): void; ``` Replace auxiliary face trims atomically without resolving or replacing walls. **Throws:** \{Error} If the trim payload is invalid or refers to a missing wall. ### toModel ```typescript theme={null} toModel(): WallNetworkModel; ``` ### fromModel ```typescript theme={null} static fromModel(model: WallNetworkModel): WallNetwork; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### WallNode ```typescript theme={null} interface WallNode { id: string; point: [number, number]; ends: WallEndRef[]; } ``` ### WallConstraint ```typescript theme={null} interface WallConstraint extends WallEndRef { hostWallId: string; station: number; } ``` ### Event Simple event handler by [Jason Kleban](https://gist.github.com/JasonKleban/50cee44960c225ac1993c922563aa540). The package does not export this type by name. ```typescript theme={null} class Event { add( handler: T extends void ? { (): void; } : { (data: T): void; }, ): void; remove( handler: T extends void ? { (): void; } : { (data: T): void; }, ): void; trigger: (data?: T) => void; reset(): void; } ``` Triggers all the callbacks assigned to this event. ### ResolveReport ```typescript theme={null} interface ResolveReport { changed: string[]; junctions: Junction[]; diagnostics: JunctionDiagnostic[]; } ``` ### WallNetworkOptions ```typescript theme={null} interface WallNetworkOptions { joinTolerance: number; defaultJoin: JoinType; miterLimit: number; autoJoin: boolean; } ``` ### WallAxis ```typescript theme={null} type WallAxis = | { kind: 'line'; start: Vec3; end: Vec3; } | { kind: 'circularArc'; center: Vec3; radius: number; startAngle: number; sweepAngle: number; }; ``` ### XZ ```typescript theme={null} type XZ = { x: number; z: number; }; ``` ### WallEnd ```typescript theme={null} type WallEnd = 'start' | 'end'; ``` ### HostedOpening The package does not export this type by name. ```typescript theme={null} type HostedOpening = Door | Window | WallOpening; ``` ### JoinType ```typescript theme={null} type JoinType = 'miter' | 'butt' | 'squareOff'; ``` ### WallDetachedFaceTrim A detached wall-end face trim; source retains the native T/X reconstruction policy. ```typescript theme={null} interface WallDetachedFaceTrim extends WallEndRef { hostWallId: string; source?: 'T' | 'X'; } ``` ### WallNetworkModel ```typescript theme={null} interface WallNetworkModel { walls: WallOptions[]; nodes: WallNode[]; constraints: WallConstraint[]; detachedFaceTrims?: WallDetachedFaceTrim[]; joinOverrides: WallJoinOverride[]; options: WallNetworkOptions; } ``` ### WallEndRef ```typescript theme={null} interface WallEndRef { wallId: string; end: WallEnd; } ``` ### WallJoinOverride ```typescript theme={null} interface WallJoinOverride { junctionKey: string; type?: JoinType; order?: string[]; } ``` ## Related * [`Door`](/floorkit/api/architecture/door) * [`Wall`](/floorkit/api/architecture/wall) * [`WallOpening`](/floorkit/api/architecture/wall-opening) * [`Window`](/floorkit/api/architecture/window) * [`Junction`](/floorkit/api/architecture/wall#junction) * [`JunctionDiagnostic`](/floorkit/api/architecture/wall#junctiondiagnostic) * [`Vec3`](/floorkit/api/architecture/wall#vec3) * [`WallOptions`](/floorkit/api/architecture/wall#walloptions) # WallOpening Source: https://docs.ploterr.com/floorkit/api/architecture/wall-opening A plain hole in a wall, with no door or window in it. ## Import ```typescript theme={null} import { WallOpening } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends Opening implements IShape, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Position along the host wall: `alongWall` is the opening's centerline distance from the wall start. The sill is `baseHeight`. Sill height above the host wall's base. See OpeningOptions.baseHeight. Clear opening width along the wall. Clear opening height (sill to head). Shape of the structural void. A round void uses equal width and height. ## Properties Read-only. Station-based: the authored position along the host wall. Read-only. Plan-view hole loop (Vector3\[] at Y=0). ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface; a host inspector renders this generically. ### anchorSpace ```typescript theme={null} anchorSpace(): "local" | "world"; ``` A single station handle sliding the opening along its host wall (world space, `alongAxis` along the wall). ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ], snapRadius?: number): void; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Station-based, so the base `Opening.move` (which walks `points`) does not apply. ### getAnalyticCutBounds ```typescript theme={null} getAnalyticCutBounds(): AnalyticOpeningCutBounds; ``` Sill height above the host wall's base. ### getOPConfig ```typescript theme={null} getOPConfig(): WallOpeningOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` Vector-export roots (RFC §7). ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Plan symbol as standardized primitives: the two jambs as solid lines and the head as a hidden-class line (renderers dash hidden edges). ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ## Types ### WallOpeningOptions ```typescript theme={null} interface WallOpeningOptions extends OpeningOptions { stationLocal: StationLocal; baseHeight: number; width: number; height: number; profile?: 'rectangular' | 'round' | 'arched'; } ``` ## Related * [`Opening`](/floorkit/api/architecture/opening) * [`AnalyticOpeningCutBounds`](/floorkit/api/architecture/door#analyticopeningcutbounds) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OpeningOptions`](/floorkit/api/architecture/opening#openingoptions) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`StationLocal`](/floorkit/api/architecture/door#stationlocal) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Wall stations Source: https://docs.ploterr.com/floorkit/api/architecture/wall-stations Convert a distance along a saved wall into a position on one of its segments. ## Import ```typescript theme={null} import { wallSegments, totalLength, resolveStation, openingHalfExtent, openingVerticalSpan, DEFAULT_FRAME_WIDTH, } from '@opengeometry/floorkit'; ``` ## wallSegments Centreline segments, including the closing one when the path is a loop. ```typescript theme={null} function wallSegments(points: PointXZ[], closed?: boolean): WallSegment[]; ``` ## totalLength ```typescript theme={null} function totalLength(segments: WallSegment[]): number; ``` ## resolveStation Resolve a perimeter offset to (segment, distance along it). ```typescript theme={null} function resolveStation(offset: number, segments: WallSegment[]): ResolvedStation | null; ``` ## openingHalfExtent Half the opening's extent along the wall — what has to fit inside its segment. ```typescript theme={null} function openingHalfExtent(opening: BmOpening): number; ``` ## openingVerticalSpan Vertical span of the cutter, relative to the wall base. ```typescript theme={null} function openingVerticalSpan(opening: BmOpening): { low: number; high: number; }; ``` ## DEFAULT\_FRAME\_WIDTH ```typescript theme={null} const DEFAULT_FRAME_WIDTH = 0.05; ``` ## Types ### WallSegment ```typescript theme={null} interface WallSegment { index: number; length: number; start: number; } ``` Cumulative length at this segment's start. ### ResolvedStation ```typescript theme={null} interface ResolvedStation { segmentIndex: number; localU: number; segmentLength: number; } ``` Distance from this segment's start; used as the hosted wall station. ## Related * [`BmOpening`](/floorkit/api/architecture/validate-building-model#bmopening) * [`PointXZ`](/floorkit/api/primitives/polygon-maths#pointxz) # Window Source: https://docs.ploterr.com/floorkit/api/architecture/window Window is a window with a frame, glass and a plan symbol. Attach it to a wall and the wall gets a real opening. ## Import ```typescript theme={null} import { Window } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Window, Wall, WallNetwork, WallMaterial, Floorkit } from '@opengeometry/floorkit'; async function example(wasmURL: string): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const network = new WallNetwork(); const opening = new Window({ stationLocal: { alongWall: 2.5 }, windowDimensions: { width: 1.2, thickness: 0.04 }, windowHeight: 1.2, sillHeight: 0.9, }); try { const wall = network.add( new Wall({ labelName: 'Partition', axis: { kind: 'line', start: [0, 0, 0], end: [5, 0, 0] }, thickness: 0.2, height: 3, baseElevation: 0, locationLine: 'centre', locationOffset: 0, material: WallMaterial.CONCRETE, color: 0xcccccc, priority: 0, joins: { start: { allow: true }, end: { allow: true } }, }), ); network.resolve(); wall.attachOpening(opening, { offset: 2.5 }); opening.setOPConfig({ ...opening.getOPConfig(), sillHeight: 1.1 }); console.log(network.resolve().diagnostics); wall.commitOpeningCuts(); console.log(opening.getPhysicalParts(), opening.toDrawing('top')); } finally { network.dispose(); opening.dispose(); } } ``` ## Notes * Dimensions are in metres. * `sillHeight` is the bottom of the glass above the base of the host wall. * After you attach or change a window on a live wall, call `wall.commitOpeningCuts()`. This call cuts the wall. * `setOPConfig` needs the full options. Copy `getOPConfig()`, change one field and pass the copy. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). * This object is live geometry. To save a building, write it in a [building model](/floorkit/concepts/building-model). **Inheritance:** `extends Opening implements IShape, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(baseWindowConfig?: Partial); ``` Window centre in the host wall's local frame — horizontal only. The vertical is `sillHeight` and nothing else. Clear height of the glass opening. Height of the sill — the bottom of the glass opening — above the host wall's base. THE vertical authority: the bottom of the rough opening, measured above the host wall's base. Never a world Y — the host frame's origin supplies the storey elevation, so `world.y = frame.origin.y + baseHeight`. Without a host or free placement, the identity frame makes `baseHeight` world Y. ## Properties Bottom of the ROUGH OPENING above the wall base — the unified vertical every opening exposes. A window is authored by its sill, and the opening it needs starts one frame-width lower to seat the lining, so the two differ by exactly `frameDimensions.width`. That relationship is stated here and nowhere else. Read-only. Station-based: the authored position along the host wall. Read-only. Window IS the opening — return self. Kept for compatibility with wall attachers. Read-only. Plan-view hole loop (Vector3\[] at Y=0). The 1 µm across-wall inset (HOLE\_2D\_TOLERANCE) keeps hole edges strictly inside the outer polygon so the WASM triangulator handles them correctly. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### getPhysicalParts ```typescript theme={null} getPhysicalParts(): readonly PhysicalElementPart[]; ``` Borrow actual material parts; descriptors contain no seeds, cutters or symbols. ### anchorSpace ```typescript theme={null} anchorSpace(): "local" | "world"; ``` A single draggable handle that slides the window along its host wall. ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ], snapRadius?: number): void; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` A window is positioned by its station and sill, not by a centreline, so the base `Opening.move` (which walks `points`) does not apply. ### getAnalyticCutBounds ```typescript theme={null} getAnalyticCutBounds(): AnalyticOpeningCutBounds; ``` Exact station/elevation rectangle used by analytic wall subtraction. ### getOPConfig ```typescript theme={null} getOPConfig(): WindowOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: WindowOptions): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` Produce the window's plan symbol as standardized region primitives: the two jamb linings and the two glazing slits, from its authored 2D geometry. ### dispose ```typescript theme={null} dispose(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ## Types ### WindowOptions ```typescript theme={null} interface WindowOptions extends OpeningOptions { stationLocal: WindowStationLocal; windowDimensions: { width: number; thickness: number; }; frameDimensions: { width: number; thickness: number; }; windowType: WindowType; windowHeight: number; sillHeight: number; frameColor: number; glassColor: number; } ``` ### WindowStationLocal ```typescript theme={null} interface WindowStationLocal { alongWall: number; } ``` Distance along the wall from its start point. ### WindowType ```typescript theme={null} enum WindowType { GLASS = 'GLASS', WOOD = 'WOOD', SLIDING = 'SLIDING', CASEMENT = 'CASEMENT', AWNING = 'AWNING', FIXED = 'FIXED', OTHER = 'OTHER', } ``` ## Related * [`Opening`](/floorkit/api/architecture/opening) * [`AnalyticOpeningCutBounds`](/floorkit/api/architecture/door#analyticopeningcutbounds) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OpeningOptions`](/floorkit/api/architecture/opening#openingoptions) * [`PhysicalElementPart`](/floorkit/api/architecture/door#physicalelementpart) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Datum defaults Source: https://docs.ploterr.com/floorkit/api/datums/datum-defaults The default colours and visibility of each datum type. ## Import ```typescript theme={null} import { DATUM_COLORS, DEFAULT_DATUM_VISIBILITY } from '@opengeometry/floorkit'; ``` ## DATUM\_COLORS Palette reserved for datums. ```typescript theme={null} const DATUM_COLORS: { readonly NEUTRAL: 8947848; readonly LEVEL: 4941188; readonly GRID: 3889560; readonly REFERENCE: 10258687; readonly ORIGIN: 13849600; readonly SECTION: 2962486; readonly ELEVATION: 623843; }; ``` ## DEFAULT\_DATUM\_VISIBILITY ```typescript theme={null} const DEFAULT_DATUM_VISIBILITY: Required; ``` ## Types ### DatumType Shared type definitions for datum objects. ```typescript theme={null} enum DatumType { LEVEL = 'LEVEL', GRID = 'GRID', REFERENCE_PLANE = 'REFERENCE_PLANE', PROJECT_ORIGIN = 'PROJECT_ORIGIN', SECTION_LINE = 'SECTION_LINE', ELEVATION_MARKER = 'ELEVATION_MARKER', } ``` ### DatumVisibility ```typescript theme={null} interface DatumVisibility { plan?: boolean; model?: boolean; } ``` # ElevationMarker Source: https://docs.ploterr.com/floorkit/api/datums/elevation-marker A marker in the plan that shows where an elevation looks from. ## Import ```typescript theme={null} import { ElevationMarker } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends OPElement implements IShape, IPlaceable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` World-space placement of the marker on the XZ plane. View direction — either a cardinal preset or an explicit unit vector on the XZ plane. Render four markers 90° apart (one per cardinal) sharing a single centre, matching the standard four-arrow elevation symbol. Head labels shown inside the bubble. Identifier of the elevation view this marker generates. Outer radius of the bubble in world units. Line / symbol colour. Defaulted per subtype when omitted. Line weight for the datum's 2D representation. Whether this datum is shown in plan / model views. ## Properties Read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): ElevationMarkerOptions; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` A single handle for the marker `position`, free on the floor plane. ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ## Types ### ElevationMarkerOptions ```typescript theme={null} interface ElevationMarkerOptions extends DatumOptionsBase { datumType: DatumType.ELEVATION_MARKER; position: [number, number, number]; direction: CardinalDirection | [number, number]; cluster: boolean; head: ElevationHeadLabel; linkedViewId?: string; bubbleRadius: number; } ``` ### CardinalDirection ```typescript theme={null} type CardinalDirection = 'N' | 'S' | 'E' | 'W'; ``` ### ElevationHeadLabel ```typescript theme={null} interface ElevationHeadLabel { drawingNumber: string; sheetNumber: string; } ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DatumOptionsBase`](/floorkit/api/datums/level#datumoptionsbase) * [`DatumType`](/floorkit/api/datums/datum-defaults#datumtype) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`Event`](/floorkit/api/architecture/wall-network#event) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Grid Source: https://docs.ploterr.com/floorkit/api/datums/grid A structural grid with labelled axes in two directions. ## Import ```typescript theme={null} import { Grid } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends OPElement implements IShape, IPlaceable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Characters omitted from auto-generated alpha labels. Elevation (world Y) at which the grid is drawn. Render axis lines as dashed. Default `false` (solid). Line / symbol colour. Defaulted per subtype when omitted. Line weight for the datum's 2D representation. Whether this datum is shown in plan / model views. ## Properties Read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface; a host inspector renders this generically. ### rectangular ```typescript theme={null} static rectangular(opts: { uSpacings: number[]; vSpacings: number[]; origin?: [ number, number ]; elevation?: number; labelName?: string; head?: Partial; }): Partial; ``` Build a rectangular grid from spacing arrays and return the resulting options so callers can pass them straight into the constructor. ### generateLabels ```typescript theme={null} static generateLabels(count: number, mode: GridLabelMode, skip?: string[]): string[]; ``` Sequence of axis labels following ISO 5457 / US NCS conventions: `A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, ...` (I and O skipped). ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(_angleDeg: number, _axis?: Vec3, _anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): GridOptions; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Two handles (start/end) per LINE axis, free on the grid's elevation plane. ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getAxesSnapshot ```typescript theme={null} getAxesSnapshot(): { u: GridAxis[]; v: GridAxis[]; }; ``` ## Types ### GridOptions ```typescript theme={null} interface GridOptions extends DatumOptionsBase { datumType: DatumType.GRID; gridKind: GridKind; uAxes: GridAxis[]; vAxes: GridAxis[]; head: GridHead; labelSkip: string[]; elevation: number; dashedAxes?: boolean; } ``` ### GridHead ```typescript theme={null} interface GridHead { diameter: number; fontSize: number; } ``` Diameter of the bubble on the XZ plane in world units. Text size for the bubble label. ### GridLabelMode ```typescript theme={null} type GridLabelMode = 'alpha' | 'numeric'; ``` ### GridAxis ```typescript theme={null} interface GridAxis { tag: string; curveSpec: GridAxisCurve; head?: { start?: boolean; end?: boolean; }; } ``` Which endpoint(s) carry a bubble. ### GridKind ```typescript theme={null} type GridKind = 'rectangular' | 'radial' | 'irregular'; ``` ### GridAxisCurve ```typescript theme={null} type GridAxisCurve = | { curve: 'line'; start: [number, number]; end: [number, number]; } | { curve: 'arc'; center: [number, number]; radius: number; startAngle: number; endAngle: number; }; ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DatumOptionsBase`](/floorkit/api/datums/level#datumoptionsbase) * [`DatumType`](/floorkit/api/datums/datum-defaults#datumtype) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`Event`](/floorkit/api/architecture/wall-network#event) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # Level Source: https://docs.ploterr.com/floorkit/api/datums/level A horizontal reference plane at a fixed height. ## Import ```typescript theme={null} import { Level } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends OPElement implements IShape, IPlaceable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` World Y elevation. Start point on the X-Z plane: `[x, z]`. Signed length of the line along local +X (before rotation). Plan-view rotation around the origin, degrees. Default 0. Show the label at the start endpoint. Default true. Show the label at the end endpoint. Default true. Font size for the OpenGlyph label, in world units. Default 2.5. Override for label text. Falls back to `${labelName} ${formatElevation(elevation)}`. The storey this datum displays, when it is bound to one (`Building` segment storey / `BmStorey` id). Data only — the level stays a visual datum. Line / symbol colour. Defaulted per subtype when omitted. Line weight for the datum's 2D representation. Whether this datum is shown in plan / model views. ## Properties Read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): LevelOptions; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Draggable handles: the start `origin` (free on the floor plane), the far `end` (slides along the level's own direction → length), and an `elevation` grip at the midpoint (vertical). ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ## Types ### LevelOptions ```typescript theme={null} interface LevelOptions extends DatumOptionsBase { datumType: DatumType.LEVEL; elevation: number; origin: [number, number]; length: number; rotationDeg?: number; showStartLabel?: boolean; showEndLabel?: boolean; fontSize?: number; headLabel?: string; storeyId?: string; } ``` ### DatumOptionsBase Common fields shared by every datum subtype. ```typescript theme={null} interface DatumOptionsBase { ogid?: string; labelName: string; datumType: DatumType; color?: number; lineWidth?: number; visibility?: DatumVisibility; } ``` Line / symbol colour. Defaulted per subtype when omitted. Line weight for the datum's 2D representation. Whether this datum is shown in plan / model views. ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DatumType`](/floorkit/api/datums/datum-defaults#datumtype) * [`DatumVisibility`](/floorkit/api/datums/datum-defaults#datumvisibility) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`Event`](/floorkit/api/architecture/wall-network#event) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # ProjectOrigin Source: https://docs.ploterr.com/floorkit/api/datums/project-origin The origin point of the project, with an optional survey point. ## Import ```typescript theme={null} import { ProjectOrigin } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends OPElement implements IShape, IPlaceable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` World-space location of the project zero. Rotation about world Y (radians) applied to the site axes. Arm length of the tri-axis gizmo. Optional survey fix that this origin maps to. Whether the triangular benchmark symbol is shown. Line / symbol colour. Defaulted per subtype when omitted. Line weight for the datum's 2D representation. Whether this datum is shown in plan / model views. ## Properties Read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, _axis?: Vec3, _anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): ProjectOriginOptions; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` A single handle for the gizmo `position`, free on the floor plane. ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ## Types ### ProjectOriginOptions ```typescript theme={null} interface ProjectOriginOptions extends DatumOptionsBase { datumType: DatumType.PROJECT_ORIGIN; position: [number, number, number]; rotationY: number; gizmoSize: number; surveyPoint?: SurveyPoint; showBenchmark: boolean; } ``` ### SurveyPoint ```typescript theme={null} interface SurveyPoint { latitude: number; longitude: number; elevation: number; description?: string; } ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DatumOptionsBase`](/floorkit/api/datums/level#datumoptionsbase) * [`DatumType`](/floorkit/api/datums/datum-defaults#datumtype) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`Event`](/floorkit/api/architecture/wall-network#event) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # ReferencePlane Source: https://docs.ploterr.com/floorkit/api/datums/reference-plane A guide plane at an angle that you choose. ## Import ```typescript theme={null} import { ReferencePlane } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends OPElement implements IShape, IPlaceable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` World-space origin of the plane. Plane normal (will be normalised). Half-width along the plane's local X and Y axes. Whether the plane body is drawn as a semi-transparent polygon. Line / symbol colour. Defaulted per subtype when omitted. Line weight for the datum's 2D representation. Whether this datum is shown in plan / model views. ## Properties Read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(_angleDeg: number, _axis?: Vec3, _anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): ReferencePlaneOptions; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` A single handle for the plane `origin`, free on the floor plane. ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ## Types ### ReferencePlaneOptions ```typescript theme={null} interface ReferencePlaneOptions extends DatumOptionsBase { datumType: DatumType.REFERENCE_PLANE; origin: [number, number, number]; normal: [number, number, number]; extents: [number, number]; showBody: boolean; } ``` ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DatumOptionsBase`](/floorkit/api/datums/level#datumoptionsbase) * [`DatumType`](/floorkit/api/datums/datum-defaults#datumtype) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`Event`](/floorkit/api/architecture/wall-network#event) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # SectionLine Source: https://docs.ploterr.com/floorkit/api/datums/section-line A line in the plan that shows where a section cuts. ## Import ```typescript theme={null} import { SectionLine } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends OPElement implements IShape, IPlaceable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` Start of the cut line on the XZ plane. End of the cut line on the XZ plane. Near / far view-depth clipping distances from the cut line. Flip the cut direction arrow. Additional interior vertices for stepped / jogged sections. Head labels at either end of the cut. Identifier of the section view this line generates, if any. Line / symbol colour. Defaulted per subtype when omitted. Line weight for the datum's 2D representation. Whether this datum is shown in plan / model views. ## Properties Read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate this element by `angleDeg` around `axis` (default Y-up), pivoted at `anchor` (default the element's own centroid). ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): SectionLineOptions; ``` ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Draggable handles: the cut-line `start` and `end`, plus one per jog waypoint, each free on its own horizontal plane (Y preserved). ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ## Types ### SectionLineOptions ```typescript theme={null} interface SectionLineOptions extends DatumOptionsBase { datumType: DatumType.SECTION_LINE; start: [number, number, number]; end: [number, number, number]; depth: { near: number; far: number; }; flip: boolean; jogs?: [number, number, number][]; headStart: SectionHeadLabel; headEnd: SectionHeadLabel; linkedViewId?: string; } ``` ### SectionHeadLabel ```typescript theme={null} interface SectionHeadLabel { drawingNumber: string; sheetNumber: string; } ``` Drawing number assigned when the linked view is placed on a sheet. Sheet number carrying the linked view. ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DatumOptionsBase`](/floorkit/api/datums/level#datumoptionsbase) * [`DatumType`](/floorkit/api/datums/datum-defaults#datumtype) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`Event`](/floorkit/api/architecture/wall-network#event) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # drawProjectView Source: https://docs.ploterr.com/floorkit/api/drawings/draw-project-view drawProjectView makes the vector drawing of one saved view: a plan, a section or an elevation. The drawing is a list of lines, arcs, regions and text in metres. ## Import ```typescript theme={null} import { drawProjectView, drawProjectViewFromBuild, ProjectDrawingError, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { drawProjectViewFromBuild, type ProjectFile, type BuildingSession, } from '@opengeometry/floorkit'; export function currentDrawing(project: ProjectFile, viewId: string, session: BuildingSession) { const current: ProjectFile = { ...project, buildingModel: session.snapshot() }; return drawProjectViewFromBuild(current, viewId, session.currentBuild); } ``` ## Notes * `drawProjectView` builds a temporary copy of the building. `drawProjectViewFromBuild` uses the build of a session that is open, which is faster. * With `drawProjectViewFromBuild`, put `session.snapshot()` into the project first. * A plan cuts straight walls at the cut height of the view. * If Floorkit cannot draw a view, it throws `ProjectDrawingError` with a `code`. It does not draw a footprint in place of a cut. ## Signature ```typescript theme={null} function drawProjectView( project: ProjectFile, viewId: string, options?: DrawProjectViewOptions, ): VectorDrawing; ``` ## Parameters Default: `{}`. ## Returns ```typescript theme={null} VectorDrawing; ``` ## drawProjectViewFromBuild Draw a saved view from an existing compilation without rebuilding its geometry. ```typescript theme={null} function drawProjectViewFromBuild( project: ProjectFile, viewId: string, built: BuildResult, options?: DrawProjectViewOptions, ): VectorDrawing; ``` ## ProjectDrawingError ```typescript theme={null} class ProjectDrawingError extends Error { readonly code: ProjectDrawingErrorCode; readonly viewId: string; constructor(code: ProjectDrawingErrorCode, viewId: string, message: string); } ``` ## Types ### VectorDrawing Plain-data vector drawing returned by a DrawingExtractor. ```typescript theme={null} interface VectorDrawing { units: 'meters'; bounds: PlanVectorBounds; primitives: DrawingPrimitive[]; texts?: VectorText[]; } ``` The standardized drawing geometry. Optional model-anchored labels (e.g. from the kernel projection path). ### DrawProjectViewOptions ```typescript theme={null} interface DrawProjectViewOptions { assetSources?: BuildOptions['assetSources']; massColors?: BuildOptions['massColors']; } ``` The colours of mass parts by use; see `BuildOptions.massColors`. ### ProjectDrawingErrorCode ```typescript theme={null} type ProjectDrawingErrorCode = | 'inconsistent-build' | 'unknown-view' | 'unknown-storey' | 'unknown-element' | 'unsupported-element' | 'empty-filter' | 'empty-view' | 'unsupported-visible-range' | 'unsupported-view'; ``` ### PlanVectorBounds ```typescript theme={null} interface PlanVectorBounds { min: PlanVectorPoint; max: PlanVectorPoint; width: number; height: number; } ``` ### VectorExtractable Implemented by model elements that can be drawn into a paper viewport — the three-free successor to `PlanVectorExportable.getExportRoots()`. ```typescript theme={null} interface VectorExtractable { getBrepEntities(): BrepEntity[]; } ``` ### DrawingSourceRef A drawing hit may identify a saved feature without granting an edit gesture. ```typescript theme={null} type DrawingSourceRef = BuildingSourceRef & { readonly interaction: 'selectable' | 'inspect-only'; }; ``` ### VectorLine A projected line segment. ```typescript theme={null} type VectorLine = PlanVectorLine; ``` ### DrawingView Views the extractor can project. ```typescript theme={null} type DrawingView = 'top' | 'front' | 'right' | 'left' | 'back' | 'iso'; ``` ### VectorText A model label/tag projected as vector text (paper renders it natively). ```typescript theme={null} interface VectorText { position: Point2; text: string; height: number; paperHeightMm?: number; rotationDeg?: number; } ``` Text height in model units (meters). Paper millimetres, overriding height at placement — see DrawingText.paperHeightMm. ### VectorArc An optional projected arc segment. ```typescript theme={null} interface VectorArc { center: Point2; radius: number; startAngleDeg: number; endAngleDeg: number; stroke_width?: number; stroke_color?: PlanVectorColor; } ``` ### DrawingRequest Describes what a single viewport draws from model space. ```typescript theme={null} interface DrawingRequest { view: DrawingView; cut?: { elevation?: number; sectionId?: string; }; camera?: ProjectionCamera; hlr?: PlanProjectionHlr; entities?: string[] | 'all'; labelMm?: number; } ``` Plan cut-plane elevation, or the section/elevation line id. Required for `view: 'iso'`. Which model entities to include, or `'all'`. Print every text primitive at this height in paper millimetres, regardless of the sheet scale — stamped onto each as DrawingText.paperHeightMm. Without it a consumer has to rewrite each element's `fontSize` before extracting, which mutates the model to change a drawing and reaches only the element types it knows about. ### DrawingExtractor Headless, three-free extraction API. ```typescript theme={null} interface DrawingExtractor { extract(req: DrawingRequest): VectorDrawing; } ``` ### DrawingLoop An ordered, implicitly-closed ring of points. ```typescript theme={null} type DrawingLoop = Point2[]; ``` ### DrawingFill Region fill (poché) descriptor. ```typescript theme={null} interface DrawingFill { color?: PlanVectorColor; pattern?: 'solid' | 'hatch'; } ``` ### DrawingStyle ```typescript theme={null} interface DrawingStyle { role?: 'cut' | 'silhouette' | 'projected' | 'hidden' | 'datum' | 'marker'; ogType?: string; ogid?: string; subId?: string; outlineGroup?: string; stroke?: { color?: PlanVectorColor; width?: number; }; fill?: DrawingFill; outline?: boolean; edgeClass?: string; } ``` Physical drawing meaning; paper/renderers resolve its print appearance. Source element type ("WALL"|"DOOR"|...) → app-side lineweight + fill policy. Source element instance id. Stamped by the extractor so an interactive 2D renderer can map a picked primitive back to its element (`getElementById(ogid)` → `getEditAnchors()`). Unset on the export-only path. Optional sub-feature id within the source element (e.g. "edge:2", "v:outer:3") for finer-grained editing/hit-testing. Producers set this when they emit per-feature primitives; element-level picking only needs `ogid`. The joined wall group that an outline line bounds: the id of its first wall and the index of that wall's cut region. Region fill (poché). Unset unless a producer or fill policy assigns it. `false` draws a region's fill WITHOUT its own outline. Set when the outline is drawn separately: merged wall poché emits the boundary of the joined mass as `line` primitives, so each wall's own ring must not be stroked or every junction seam comes back. Ignored by the other primitive kinds, which are outlines already. Kernel HLR edge class, carried verbatim from projection (e.g. "VisibleOutline" | "Hidden" | "VisibleCrease"). Unset for authored 2D (plan) primitives. Lets the renderer dash hidden edges, etc. ### DrawingPrimitiveSource ```typescript theme={null} interface DrawingPrimitiveSource { sourceRef?: DrawingSourceRef; sourceRefs?: readonly DrawingSourceRef[]; } ``` Authored identity for selection; absent when the drawing cannot attribute a source. Multiple authored owners of merged material. No arbitrary single editable owner. ### DrawingLine ```typescript theme={null} interface DrawingLine extends DrawingPrimitiveSource { kind: 'line'; start: Point2; end: Point2; style?: DrawingStyle; } ``` ### DrawingArc ```typescript theme={null} interface DrawingArc extends DrawingPrimitiveSource { kind: 'arc'; center: Point2; radius: number; startAngleDeg: number; endAngleDeg: number; style?: DrawingStyle; } ``` ### DrawingBoundary ```typescript theme={null} type DrawingBoundary = DrawingLine | DrawingArc; ``` ### DrawingCurvedRegion ```typescript theme={null} interface DrawingCurvedRegion extends DrawingPrimitiveSource { kind: 'curvedRegion'; outer: DrawingBoundary[]; holes: DrawingBoundary[][]; style?: DrawingStyle; } ``` ### DrawingRegion ```typescript theme={null} interface DrawingRegion extends DrawingPrimitiveSource { kind: 'region'; outer: DrawingLoop; holes: DrawingLoop[]; style?: DrawingStyle; } ``` Outer boundary ring. Inner rings (holes) — even-odd voids, e.g. door/window openings in a wall. ### DrawingText ```typescript theme={null} interface DrawingText extends DrawingPrimitiveSource { kind: 'text'; position: Point2; text: string; align?: 'left' | 'center' | 'right'; height: number; paperHeightMm?: number; rotationDeg?: number; style?: DrawingStyle; } ``` Horizontal alignment at position; omitted preserves centered text. Text height in model units (meters). Text height in PAPER millimetres, overriding height at placement. ### DrawingPolyline An open or closed run of straight segments through `points` (back to the first when `closed`). ```typescript theme={null} interface DrawingPolyline extends DrawingPrimitiveSource { kind: 'polyline'; points: Point2[]; closed?: boolean; style?: DrawingStyle; } ``` ### DrawingPrimitive ```typescript theme={null} type DrawingPrimitive = | DrawingLine | DrawingArc | DrawingRegion | DrawingCurvedRegion | DrawingText | DrawingPolyline; ``` ### PlanVectorLine ```typescript theme={null} interface PlanVectorLine { start: PlanVectorPoint; end: PlanVectorPoint; stroke_width?: number; stroke_color?: PlanVectorColor; ogType?: string; } ``` Source element type (e.g. "WALL"), for app-side lineweight policy. ## Related * [`BrepEntity`](/floorkit/api/drawings/kernel-drawing-extractor#brepentity) * [`BuildingSourceRef`](/floorkit/api/drawings/plan-drawing-extractor#buildingsourceref) * [`BuildOptions`](/floorkit/api/architecture/build-from-building-model#buildoptions) * [`BuildResult`](/floorkit/api/architecture/build-from-building-model#buildresult) * [`PlanProjectionHlr`](/floorkit/api/drawings/drawing-helpers#planprojectionhlr) * [`PlanVectorColor`](/floorkit/api/annotations/dimension-geometry#planvectorcolor) * [`PlanVectorPoint`](/floorkit/api/annotations/dimension-geometry#planvectorpoint) * [`Point2`](/floorkit/api/annotations/dimension-geometry#point2) * [`ProjectFile`](/floorkit/api/exports/project-file#projectfile) * [`ProjectionCamera`](/floorkit/api/drawings/sheet-documents#projectioncamera) # Drawing helpers Source: https://docs.ploterr.com/floorkit/api/drawings/drawing-helpers Read, measure and convert drawing primitives. ## Import ```typescript theme={null} import { extractPhysicalWallPlan, projectWorldLinework, drawingFromProfile, toLines, boundsOfPrimitives, boundaryPoint, boundaryBoundsPoints, arcSegmentCount, validateCurvedRegion, flattenCurvedRegion, createLiveSourceResolver, DEFAULT_ISOMETRIC_CAMERA, DEFAULT_ISOMETRIC_HLR, } from '@opengeometry/floorkit'; ``` ## extractPhysicalWallPlan Extract actual horizontal sections of straight planar wall bodies in metres. ```typescript theme={null} function extractPhysicalWallPlan( walls: readonly Wall[], worldCutY: number, options?: PhysicalWallPlanOptions, ): VectorDrawing; ``` ## projectWorldLinework Orthographically project supplied 3D segments into right/down drawing metres. ```typescript theme={null} function projectWorldLinework( frame: ViewFrame, segments: readonly WorldLineSegment[], ): VectorDrawing; ``` ## drawingFromProfile Wrap an element's editable `profile` (a closed cross-section ring + holes, in `[x, z]`) as a single-region VectorDrawing so the 2D engine can render and edit it with the SAME path it uses for plan footprints — i.e. ```typescript theme={null} function drawingFromProfile(profile: Profile, ogType: string, ogid: string): VectorDrawing; ``` ## toLines Tessellate the standardized primitive vocabulary down to flat line segments: line → 1 segment; arc → N segments; region → outer + hole boundary segments; polyline → its segments (plus the closing one when `closed`); text → none. ```typescript theme={null} function toLines( primitives: DrawingPrimitive[], options?: { arcSegments?: number; deflection?: number; }, ): VectorLine[]; ``` ## boundsOfPrimitives Bounds include analytic arc extrema and text anchors, without tessellation. ```typescript theme={null} function boundsOfPrimitives(primitives: DrawingPrimitive[]): PlanVectorBounds; ``` ## boundaryPoint ```typescript theme={null} function boundaryPoint(boundary: DrawingBoundary, end?: boolean): Point2; ``` ## boundaryBoundsPoints Includes all angular extrema within a signed sweep, without sampling. ```typescript theme={null} function boundaryBoundsPoints(boundary: DrawingBoundary): Point2[]; ``` ## arcSegmentCount ```typescript theme={null} function arcSegmentCount(arc: DrawingArc, deflection: number): number; ``` ## validateCurvedRegion ```typescript theme={null} function validateCurvedRegion(region: DrawingCurvedRegion): void; ``` ## flattenCurvedRegion Explicit polygon approximation for consumers that require point rings. ```typescript theme={null} function flattenCurvedRegion(region: DrawingCurvedRegion, deflection: number): DrawingRegion; ``` ## createLiveSourceResolver Build a lookup for the current compilation. ```typescript theme={null} function createLiveSourceResolver( sourceByLiveId: ReadonlyMap, ): (source: DrawingSourceRef) => string | undefined; ``` ## DEFAULT\_ISOMETRIC\_CAMERA ```typescript theme={null} const DEFAULT_ISOMETRIC_CAMERA: PlanProjectionCamera; ``` ## DEFAULT\_ISOMETRIC\_HLR ```typescript theme={null} const DEFAULT_ISOMETRIC_HLR: PlanProjectionHlr; ``` ## Types ### PhysicalWallPlanOptions ```typescript theme={null} interface PhysicalWallPlanOptions { sourceByLiveId?: ReadonlyMap; } ``` Actual compiler registry; only supplied wall identities become selectable. ### ViewFrame Orthographic frame in metres. ```typescript theme={null} interface ViewFrame { origin: Point3D; horizontal: Point3D; downward: Point3D; look: Point3D; } ``` ### WorldLineSegment A caller-supplied world-space segment and its independent drawing metadata. ```typescript theme={null} interface WorldLineSegment { start: Point3D; end: Point3D; sourceRef?: DrawingSourceRef; style?: DrawingStyle; generatedCut?: boolean; } ``` Saved feature identity and interaction policy; never inferred from style. The caller identifies generated cut linework; this helper does not find cuts. ### PlanProjectionCamera ```typescript theme={null} interface PlanProjectionCamera { position: Point3D; target: Point3D; up: Point3D; near: number; projection_mode: 'Orthographic' | 'Perspective'; } ``` ### PlanProjectionHlr ```typescript theme={null} interface PlanProjectionHlr { hide_hidden_edges: boolean; } ``` ## Related * [`Wall`](/floorkit/api/architecture/wall) * [`BuildingSourceRef`](/floorkit/api/drawings/plan-drawing-extractor#buildingsourceref) * [`DrawingArc`](/floorkit/api/drawings/draw-project-view#drawingarc) * [`DrawingBoundary`](/floorkit/api/drawings/draw-project-view#drawingboundary) * [`DrawingCurvedRegion`](/floorkit/api/drawings/draw-project-view#drawingcurvedregion) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`DrawingRegion`](/floorkit/api/drawings/draw-project-view#drawingregion) * [`DrawingSourceRef`](/floorkit/api/drawings/draw-project-view#drawingsourceref) * [`DrawingStyle`](/floorkit/api/drawings/draw-project-view#drawingstyle) * [`PlanVectorBounds`](/floorkit/api/drawings/draw-project-view#planvectorbounds) * [`Point2`](/floorkit/api/annotations/dimension-geometry#point2) * [`Point3D`](/floorkit/api/views/view-frames#point3d) * [`Profile`](/floorkit/api/primitives/line-primitive#profile) * [`VectorDrawing`](/floorkit/api/drawings/draw-project-view#vectordrawing) * [`VectorLine`](/floorkit/api/drawings/draw-project-view#vectorline) # KernelDrawingExtractor Source: https://docs.ploterr.com/floorkit/api/drawings/kernel-drawing-extractor Makes a vector drawing of live elements with the geometry kernel. ## Import ```typescript theme={null} import { KernelDrawingExtractor } from '@opengeometry/floorkit'; ``` **Inheritance:** `implements DrawingExtractor` ## Constructor ```typescript theme={null} constructor(entities: BrepEntity[]); ``` ## Methods ### extract ```typescript theme={null} extract(req: DrawingRequest): VectorDrawing; ``` ## Types ### BrepEntity A single BRep entity to project headlessly, plus any vector labels it carries. ```typescript theme={null} interface BrepEntity { id: string; kind: string; brepSerialized: string; texts?: VectorText[]; } ``` ## Related * [`DrawingExtractor`](/floorkit/api/drawings/draw-project-view#drawingextractor) * [`DrawingRequest`](/floorkit/api/drawings/draw-project-view#drawingrequest) * [`VectorDrawing`](/floorkit/api/drawings/draw-project-view#vectordrawing) * [`VectorText`](/floorkit/api/drawings/draw-project-view#vectortext) # Page sizes and frames Source: https://docs.ploterr.com/floorkit/api/drawings/page-sizes-and-frames Standard page sizes and the frame that goes around a sheet. `pageSize` gives the width and the height of a standard page in millimetres. The other functions find formats and make the frame of a sheet. ## Import ```typescript theme={null} import { pageSize, formatsIn, sizeMatchesFormat, sheetFrameFor, sheetFrameGeometry, PAGE_SPECS, PAGE_FAMILY, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { pageSize, sizeMatchesFormat } from '@opengeometry/floorkit'; const portrait = pageSize('A4', 'portrait'); // 210 × 297 mm const landscape = pageSize('A3', 'landscape'); // 420 × 297 mm console.log(sizeMatchesFormat('A3', 'landscape', landscape)); // true ``` ## Notes * Landscape exchanges the width and the height. * For a custom size, write the width and the height in the sheet. * `sizeMatchesFormat` checks that a size agrees with its format. ## pageSize Resolve a named format + orientation to width/height in millimetres. ```typescript theme={null} function pageSize(format: Exclude, orientation: PageOrientation): PageSize; ``` ## formatsIn Formats in a family, largest first — the order a size picker lists them. ```typescript theme={null} function formatsIn(family: PageFamily): Exclude[]; ``` ## sizeMatchesFormat Whether a stored size agrees with the format and orientation it claims. ```typescript theme={null} function sizeMatchesFormat( format: PageFormat, orientation: PageOrientation, size: PageSize, toleranceMm?: number, ): boolean; ``` ## sheetFrameFor Resolve a page to its frame. ```typescript theme={null} function sheetFrameFor(page: PageSpec): SheetFrame; ``` ## sheetFrameGeometry The frame as drawable geometry, in absolute paper millimetres. ```typescript theme={null} function sheetFrameGeometry( frame: SheetFrame, page: PageSpec, ): { lines: VectorLine[]; texts: VectorText[]; }; ``` ## PAGE\_SPECS Portrait dimensions in millimetres for each named format. ```typescript theme={null} const PAGE_SPECS: Record, PageSize>; ``` ## PAGE\_FAMILY The standard each format belongs to. ```typescript theme={null} const PAGE_FAMILY: Record, PageFamily>; ``` ## Types ### PageFormat Standard page sizes for paper space, in **millimetres** (portrait, width × height), with a **top-left** origin (x increases right, y increases down). ```typescript theme={null} type PageFormat = | 'A0' | 'A1' | 'A2' | 'A3' | 'A4' | 'A5' | 'A6' | 'B0' | 'B1' | 'B2' | 'B3' | 'B4' | 'B5' | 'ANSI_A' | 'ANSI_B' | 'ANSI_C' | 'ANSI_D' | 'ANSI_E' | 'ARCH_A' | 'ARCH_B' | 'ARCH_C' | 'ARCH_D' | 'ARCH_E' | 'ARCH_E1' | 'Letter' | 'Legal' | 'Tabloid' | 'Custom'; ``` ### PageOrientation ```typescript theme={null} type PageOrientation = 'portrait' | 'landscape'; ``` ### PageSize type ```typescript theme={null} interface PageSize { width: number; height: number; } ``` ### PageFamily Which standard a format belongs to — what a size picker groups by. ```typescript theme={null} type PageFamily = 'ISO-A' | 'ISO-B' | 'ANSI' | 'ARCH' | 'US'; ``` ### SheetFrame What a sheet's frame looks like, resolved to absolute paper millimetres. ```typescript theme={null} interface SheetFrame { margins: SheetMargins; inner: FrameRect; drawBorder: boolean; centringMarks: boolean; zones?: SheetZones; } ``` The rect the frame is stroked on, and that the title block anchors to. ### SheetMargins Inset from each page edge, in paper millimetres. ```typescript theme={null} interface SheetMargins { left: number; top: number; right: number; bottom: number; } ``` ### FrameRect Rectangle in paper millimetres. The package does not export this type by name. ```typescript theme={null} interface FrameRect { x: number; y: number; width: number; height: number; } ``` ### SheetZones The zone reference grid along the border — how a drawing says "see B4". ```typescript theme={null} interface SheetZones { columns: number; rows: number; bandMm: number; } ``` Depth of the labelled band, measured inwards from the page edge. ## Related * [`PageSpec`](/floorkit/api/drawings/sheet-documents#pagespec) * [`VectorLine`](/floorkit/api/drawings/draw-project-view#vectorline) * [`VectorText`](/floorkit/api/drawings/draw-project-view#vectortext) # Paper styles Source: https://docs.ploterr.com/floorkit/api/drawings/paper-styles The built-in drawing styles and the rules that select one. A paper style sets how a drawing looks: line weights, colours, hatches and text. Floorkit has four built-in styles, and you can add your own. `resolvePaperStyle` changes a style id or a partial style into a full style. ## Import ```typescript theme={null} import { resolvePaperStyle, mergeStyle, resolveSheetPolicy, resolveViewportPolicy, documentStyleOf, PAPER_STYLES, DEFAULT_PAPER_STYLE_ID, ISO_128, INK_SKETCH, GRAPHITE, NAPKIN, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { resolvePaperStyle } from '@opengeometry/floorkit'; const resolved = resolvePaperStyle({ extends: 'ink-sketch', ink: '#183047' }); if (resolved) { console.log(resolved.style, resolved.unresolved); } const none = resolvePaperStyle(undefined); // undefined ``` ## Notes * The built-in styles are `ISO_128`, `INK_SKETCH`, `GRAPHITE` and `NAPKIN`. * Your style replaces a built-in style that has the same id. * An unknown id gives the default style, and the id appears in `unresolved`. * With no input, the function returns `undefined`. ## resolvePaperStyle Flatten a style input against a registry. ```typescript theme={null} function resolvePaperStyle( input: PaperStyleInput | undefined, registry?: PaperStyleRegistry, ): ResolvedPaperStyle | undefined; ``` ## mergeStyle Merge one style over another, at the depth each field means: - `classes` / `edgeClasses`: key by key, then field by field on the class, then field by field on each `byKind` entry — so an overlay that says `{ classes: { WALL: { ink } } }` keeps WALL's weight, fill and hatch. ```typescript theme={null} function mergeStyle(base: PaperStyle, over: PaperStyle): PaperStyle; ``` ## resolveSheetPolicy The policy a sheet renders under: `deps.paperStyle` (caller) → the sheet → the document. ```typescript theme={null} function resolveSheetPolicy(sheet: Sheet, deps: PaperRenderDeps): SheetPolicy | undefined; ``` ## resolveViewportPolicy The policy one viewport renders under. ```typescript theme={null} function resolveViewportPolicy( sheet: Sheet, viewport: Viewport, deps: PaperRenderDeps, sheetPolicy: SheetPolicy | undefined, ): SheetPolicy | undefined; ``` ## documentStyleOf A document's style fields, for `PaperRenderDeps.documentStyle`. ```typescript theme={null} function documentStyleOf(doc: DocumentStyleFields): DocumentStyleFields | undefined; ``` ## PAPER\_STYLES The presets that ship, keyed by id. ```typescript theme={null} const PAPER_STYLES: Readonly>; ``` ## DEFAULT\_PAPER\_STYLE\_ID ```typescript theme={null} const DEFAULT_PAPER_STYLE_ID: BuiltinPaperStyleId; ``` ## ISO\_128 ```typescript theme={null} const ISO_128: PaperStyle; ``` ## INK\_SKETCH Freehand ink — a drafted plan in technical pen on vellum, in the register Francis Ching teaches: a compressed four-rung hierarchy, warm near-black ink that lightens as the line thins, solid poché pulled a hair in from its line, crossed corners with a short placed start and a longer lifted finish, a second strike on the heavy lines, and upper-case lettering with a little tracking. ```typescript theme={null} const INK_SKETCH: PaperStyle; ``` ## GRAPHITE Soft graphite on trace — a design-development plan in pencil on canary trace: every weight one rung heavier than ink (a pencil line is wider than a pen's), warm greys that lighten as the line thins, a light warm poché with one sparse pass of scrubbed hatch over it (a tone, not a material), wider re-strike offsets than ink, longer softer tapers, a re-struck door swing, and larger upper-case lettering. ```typescript theme={null} const GRAPHITE: PaperStyle; ``` ## NAPKIN Napkin sketch — the parti drawing: a fineliner on a paper napkin, drawn fast. ```typescript theme={null} const NAPKIN: PaperStyle; ``` ## Types ### PaperStyleInput ```typescript theme={null} type PaperStyleInput = PaperStyleId | PaperStyle; ``` ### ResolvedPaperStyle ```typescript theme={null} interface ResolvedPaperStyle { style: PaperStyle; unresolved: string[]; } ``` Flattened: every `extends` walked and merged, no `extends` left. Every reference that did not resolve, in the order it was met. ### PaperRenderDeps Dependencies the emitters (and, later, the screen renderer) need that aren't part of the serializable document: the kernel-backed drawing extractor that resolves each viewport to vector data, and (optionally) the title-block template registry used to render a sheet's title block. ```typescript theme={null} interface PaperRenderDeps { resolveSavedRasterView?: ( viewId: string, viewport?: import('../model/viewport').Viewport, ) => import('../model/raster-view').RasterDrawing | undefined; extractor: DrawingExtractor; resolveSavedView?: (viewId: string) => VectorDrawing; templates?: TitleblockTemplateRegistry; unitSystem?: UnitSystem; projectName?: string; date?: string; dimensionStyles?: DimensionStyleRegistry; paperStyle?: PaperStyleInput; paperStyles?: PaperStyleRegistry; documentStyle?: DocumentStyleFields; } ``` Resolve prepared named raster content; return undefined for a vector view. Draw a named project view; absent keeps bare paper renderers inline-only. Which conventions to format numbers with. Fills the title block's `projectName` auto-field, which nothing populated. ISO date (YYYY-MM-DD) for the title block's `date` auto-field. Absent means today, which makes a rendered sheet non-reproducible — a batch export or a golden test pins it here. Mirrors `PaperDocument.dimensionStyles`; set from it by the exporter and the renderer, for the same reason `unitSystem` is. A caller's paper style — an id or an inline overlay — for this render only. Narrower than anything stored: it outranks the viewport's, the sheet's and the document's own style. Extra presets a host registers, resolvable by id alongside the builtins. A document's own `paperStyles` outrank these. ### DocumentStyleFields The style-bearing fields of a document, lifted into the render deps. ```typescript theme={null} interface DocumentStyleFields { styleId?: PaperStyleId; styleOverride?: PaperStyle; paperStyles?: PaperStyleRegistry; } ``` ### BuiltinPaperStyleId The presets that ship. ```typescript theme={null} type BuiltinPaperStyleId = 'iso-128' | 'ink-sketch' | 'graphite' | 'napkin'; ``` ## Related * [`DimensionStyleRegistry`](/floorkit/api/annotations/dimension-styles#dimensionstyleregistry) * [`DrawingExtractor`](/floorkit/api/drawings/draw-project-view#drawingextractor) * [`PaperStyle`](/floorkit/api/drawings/sheet-documents#paperstyle) * [`PaperStyleId`](/floorkit/api/drawings/sheet-documents#paperstyleid) * [`PaperStyleRegistry`](/floorkit/api/drawings/sheet-documents#paperstyleregistry) * [`RasterDrawing`](/floorkit/api/views/prepare-project-raster-views#rasterdrawing) * [`Sheet`](/floorkit/api/drawings/sheet-documents#sheet) * [`SheetPolicy`](/floorkit/api/drawings/viewport-placement#sheetpolicy) * [`TitleblockTemplateRegistry`](/floorkit/api/drawings/titleblock-registry#titleblocktemplateregistry) * [`UnitSystem`](/floorkit/api/annotations/dimension-geometry#unitsystem) * [`VectorDrawing`](/floorkit/api/drawings/draw-project-view#vectordrawing) * [`Viewport`](/floorkit/api/drawings/sheet-documents#viewport) # Pens and hatches Source: https://docs.ploterr.com/floorkit/api/drawings/pens-and-hatches Line weights, dash patterns and hatch patterns. ## Import ```typescript theme={null} import { resolvePen, penFor, penContextFor, resolveInk, visibleUnder, lineWeightFor, stepUp, stepDown, hatchLines, PEN_TABLE, PEN_LADDER_MM, EDGE_CLASS_MODIFIER, HATCH_PATTERNS, DASH_HIDDEN, DASH_CENTRE, DASH_PHANTOM, DASH_CUTTING, } from '@opengeometry/floorkit'; ``` ## resolvePen ```typescript theme={null} function resolvePen(style?: DrawingStyle): Pen; ``` ## penFor The ink a primitive is drawn with. ```typescript theme={null} function penFor(primitive: DrawingPrimitive, ctx?: PenContext): Pen; ``` ## penContextFor The per-viewport context: the policy plus an empty memo. ```typescript theme={null} function penContextFor(policy: SheetPolicy): PenContext; ``` ## resolveInk Class resolution, memoised per `${ogType}|${edgeClass}|${kind}`, then the producer's own overlay: an explicit width always wins (a producer that measured something — and `placeDrawing` bakes the resolved width back on, so the common case is a float compare and no allocation); an explicit fill wins only where the style allows it. ```typescript theme={null} function resolveInk( style: DrawingStyle | undefined, kind: PrimitiveKind, ctx: PenContext, ruled?: boolean, ): Pen; ``` ## visibleUnder Whether a style draws this primitive at all (`ClassStyle.visible`). ```typescript theme={null} function visibleUnder(primitive: DrawingPrimitive, ctx: PenContext): boolean; ``` ## lineWeightFor ```typescript theme={null} function lineWeightFor(ogType?: string): number; ``` ## stepUp One rung heavier on PEN\_LADDER\_MM — the sibling of stepDown, with the same rule for weights that are not on the ladder: returned unchanged, never snapped. ```typescript theme={null} function stepUp(weightMm: number): number; ``` ## stepDown One rung lighter on PEN\_LADDER\_MM. ```typescript theme={null} function stepDown(weightMm: number): number; ``` ## hatchLines Hatch lines for a region, in the region's own coordinates (paper mm when the region has already been placed). ```typescript theme={null} function hatchLines(region: DrawingRegion | DrawingCurvedRegion, spec: HatchSpec): VectorLine[]; ``` ## PEN\_TABLE The drafting palette, keyed by `ElementType`. ```typescript theme={null} const PEN_TABLE: Record; ``` ## PEN\_LADDER\_MM ISO 128 line-width ladder, in millimetres. ```typescript theme={null} const PEN_LADDER_MM: readonly [0.13, 0.18, 0.25, 0.35, 0.5, 0.7, 1, 1.4, 2]; ``` ## EDGE\_CLASS\_MODIFIER How an HLR edge class modifies the pen its element already resolved to. ```typescript theme={null} const EDGE_CLASS_MODIFIER: Record Pen>; ``` ## HATCH\_PATTERNS Material hatches, for the drawings that want them. ```typescript theme={null} const HATCH_PATTERNS: Record; ``` ## DASH\_HIDDEN Dashed narrow — hidden edges, things concealed by what is in front. ```typescript theme={null} const DASH_HIDDEN: number[]; ``` ## DASH\_CENTRE Long-dashed dotted narrow — centre lines, axes of symmetry. ```typescript theme={null} const DASH_CENTRE: number[]; ``` ## DASH\_PHANTOM Long-dashed double-dotted narrow — adjacent parts, outlines beyond the cut. ```typescript theme={null} const DASH_PHANTOM: number[]; ``` ## DASH\_CUTTING Long-dashed dotted wide — cutting planes. ```typescript theme={null} const DASH_CUTTING: number[]; ``` ## Types ### Pen How one primitive is inked on paper. ```typescript theme={null} interface Pen { color: string; weightMm: number; dash?: number[]; fill?: string; hatch?: HatchSpec; cap?: 'butt' | 'round'; outline?: false; } ``` Drafting ink, as a CSS colour. Never the element's 3D material colour. Lineweight in paper millimetres — a rung on PEN\_LADDER\_MM. Dash runs in paper mm. Absent or empty means continuous. Solid poché for `region` primitives. Mutually exclusive with `hatch`. Hatched poché for `region` primitives. Mutually exclusive with `fill`. Line cap. Absent is the renderer's default (butt); a hand stroke wants round. Unset on every pen the shipped table resolves. `false` draws a `region` as fill only, with no outline at all — `stroke="none"`, never a zero width, which PDF renders as the thinnest line the device can make. Unset on every pen the shipped table resolves. ### PenContext Built once per viewport in `placeDrawing`; the memo lives here, never at module level, so a render is a pure function of its inputs. ```typescript theme={null} interface PenContext { readonly style: PaperStyle; readonly sheetSalt: number; readonly inkCache: Map; } ``` ### PrimitiveKind ```typescript theme={null} type PrimitiveKind = DrawingPrimitive['kind']; ``` ### HatchSpec A region's hatch. ```typescript theme={null} interface HatchSpec { angleDeg: number; spacingMm: number; weightMm: number; color: string; crossAngleDeg?: number; } ``` A second pass at this angle — how cross-hatched materials are drawn. ## Related * [`DrawingCurvedRegion`](/floorkit/api/drawings/draw-project-view#drawingcurvedregion) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`DrawingRegion`](/floorkit/api/drawings/draw-project-view#drawingregion) * [`DrawingStyle`](/floorkit/api/drawings/draw-project-view#drawingstyle) * [`PaperStyle`](/floorkit/api/drawings/sheet-documents#paperstyle) * [`SheetPolicy`](/floorkit/api/drawings/viewport-placement#sheetpolicy) * [`VectorLine`](/floorkit/api/drawings/draw-project-view#vectorline) # PlanDrawingExtractor Source: https://docs.ploterr.com/floorkit/api/drawings/plan-drawing-extractor PlanDrawingExtractor makes a plan drawing from live elements. The plan shows the footprint of each element. It does not cut the building at a height. ## Import ```typescript theme={null} import { PlanDrawingExtractor, assertPlanDrawingRequest } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { PlanDrawingExtractor, type PlanVectorExportable } from '@opengeometry/floorkit'; export function extractLivePlan(elements: PlanVectorExportable[]) { const extractor = new PlanDrawingExtractor(elements); return extractor.extract({ view: 'top' }); } ``` ## Notes * The output uses metres. * `entities` selects elements by id. Without it, all elements are drawn. * The top view is the supported view. A request for a cut plan or a section throws an error. * For a cut plan, a section or an elevation of a saved building, use [`drawProjectView`](/floorkit/api/drawings/draw-project-view). * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). **Inheritance:** `implements DrawingExtractor` ## Constructor ```typescript theme={null} constructor(elements: PlanVectorExportable[], options?: { stairCutElevation?: number; roomLabels?: RoomLabelMode; openingTags?: OpeningTagMode; sourceByLiveId?: ReadonlyMap; }); ``` ## Methods ### extract ```typescript theme={null} extract(req: DrawingRequest): VectorDrawing; ``` ## assertPlanDrawingRequest Shared validation for extractors that compose authored plan primitives. ```typescript theme={null} function assertPlanDrawingRequest(req: DrawingRequest): void; ``` ## Types ### BuildingSourceRef ```typescript theme={null} type BuildingSourceRef = | ElementSourceRef | WallSegmentSourceRef | OpeningSourceRef | ModelObjectSourceRef; ``` ### ElementSourceRef Authored identity carried independently of a drawing primitive's visual style. The package does not export this type by name. ```typescript theme={null} interface ElementSourceRef { readonly kind: 'element'; readonly modelId: string; readonly buildingId?: string; readonly storeyId?: string; readonly elementId: string; readonly massSegmentId?: string; } ``` A whole saved element: a slab, a room, a mass and the like. The saved model. The building of the element; absent for an element of the site. The storey of the element; absent for an element of no storey, such as a mass. The saved id of the element. The saved id of the part (segment) of the mass `elementId` that a plan region draws. Only the plan drawing of a mass sets it; the part resolves to its mass, and a press on it selects the part. ### WallSegmentSourceRef The package does not export this type by name. ```typescript theme={null} interface WallSegmentSourceRef { readonly kind: 'wall-segment'; readonly modelId: string; readonly buildingId: string; readonly storeyId: string; readonly elementId: string; readonly segmentIndex: number; readonly startPointIndex: number; readonly endPointIndex: number; } ``` The saved wall run, shared by every compiled segment. Zero-based segment index in the saved wall run. ### OpeningSourceRef The package does not export this type by name. ```typescript theme={null} interface OpeningSourceRef { readonly kind: 'opening'; readonly modelId: string; readonly buildingId: string; readonly storeyId: string; readonly elementId: string; readonly hostWallId?: string; } ``` Present only while the saved opening belongs to a wall run. ### ModelObjectSourceRef A saved basic object; its current drawing can be inspected before an edit gesture exists. The package does not export this type by name. ```typescript theme={null} interface ModelObjectSourceRef { readonly kind: 'model-object'; readonly modelId: string; readonly buildingId: string; readonly storeyId: string; readonly elementId: string; } ``` ## Related * [`DrawingExtractor`](/floorkit/api/drawings/draw-project-view#drawingextractor) * [`DrawingRequest`](/floorkit/api/drawings/draw-project-view#drawingrequest) * [`OpeningTagMode`](/floorkit/api/annotations/room-and-opening-labels#openingtagmode) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`RoomLabelMode`](/floorkit/api/annotations/room-and-opening-labels#roomlabelmode) * [`VectorDrawing`](/floorkit/api/drawings/draw-project-view#vectordrawing) # Plan style Source: https://docs.ploterr.com/floorkit/api/drawings/plan-style The colours and line widths of the on-screen plan. ## Import ```typescript theme={null} import { planStyle, PLAN_STYLE, DEFAULT_PLAN_STYLE } from '@opengeometry/floorkit'; ``` ## planStyle ```typescript theme={null} function planStyle(ogType?: string): PlanStyle; ``` ## PLAN\_STYLE Keys are the literal `ElementType` values elements actually report, which is not always the name you would guess: a network `Wall` reports `WALL`, `Space` reports `SPACECONTAINER`, and every opening subclass — `Door`, `Window`, `WallOpening` — reports its own type or `OPENING`. ```typescript theme={null} const PLAN_STYLE: Record; ``` ## DEFAULT\_PLAN\_STYLE ```typescript theme={null} const DEFAULT_PLAN_STYLE: PlanStyle; ``` ## Types ### PlanStyle type Drafting style for one source element type in the plan view. ```typescript theme={null} interface PlanStyle { stroke: string; weight: number; poche?: string; } ``` Ink (stroke) colour — overrides the element's 3D material colour. Screen-constant lineweight in px. Applied with `vector-effect: non-scaling-stroke` so weights stay crisp and zoom-independent — the hallmark of CAD drafting. Poché fill for cut solids (region primitives only); undefined = no fill. # Scales and units Source: https://docs.ploterr.com/floorkit/api/drawings/scales-and-units Standard drawing scales and conversion between millimetres, points and pixels. ## Import ```typescript theme={null} import { scalesFor, scaleById, nearestStandardScale, METRIC_SCALES, IMPERIAL_SCALES, mmToPdfPoints, pdfPointsToMm, mmToScreen, screenToMm, } from '@opengeometry/floorkit'; ``` ## scalesFor Every scale offered for a unit system — the list a scale picker renders. ```typescript theme={null} function scalesFor(system: UnitSystem): NamedScale[]; ``` ## scaleById Look a registered scale up by the id stored in `Viewport.scaleId`. ```typescript theme={null} function scaleById(id: string): NamedScale | undefined; ``` ## nearestStandardScale The registered scale matching a ratio in a given system, if there is one. ```typescript theme={null} function nearestStandardScale(ratio: number, system?: UnitSystem): NamedScale | undefined; ``` ## METRIC\_SCALES ISO 5455 reductions plus the enlargements, and the intermediate architectural scales (1:25, 1:1250, 1:2500) that practice uses alongside them. ```typescript theme={null} const METRIC_SCALES: NamedScale[]; ``` ## IMPERIAL\_SCALES ```typescript theme={null} const IMPERIAL_SCALES: NamedScale[]; ``` ## mmToPdfPoints Millimetres → PDF points (1 pt = 1/72 inch). ```typescript theme={null} function mmToPdfPoints(mm: number): number; ``` ## pdfPointsToMm PDF points → millimetres. ```typescript theme={null} function pdfPointsToMm(points: number): number; ``` ## mmToScreen Millimetres → screen pixels at a given pixels-per-mm zoom. ```typescript theme={null} function mmToScreen(mm: number, pixelsPerMm: number): number; ``` ## screenToMm Screen pixels → millimetres at a given pixels-per-mm zoom. ```typescript theme={null} function screenToMm(pixels: number, pixelsPerMm: number): number; ``` ## Types ### NamedScale A scale a drawing is plotted at, as both a ratio and the way it is written. ```typescript theme={null} interface NamedScale { id: string; label: string; ratio: number; system: UnitSystem; } ``` Stable key stored in `Viewport.scaleId`, e.g. "1:50" or "1/4in=1ft". Written out rather than derived from the label, so it stays readable and cannot collide as the ladder grows. How the scale is written on the sheet. Dimensionless paper ÷ real ratio. 1:50 → 0.02. ## Related * [`UnitSystem`](/floorkit/api/annotations/dimension-geometry#unitsystem) # Sheet documents Source: https://docs.ploterr.com/floorkit/api/drawings/sheet-documents Make and check the document that holds your sheets. A paper document holds your sheets. `createEmptyDocument` makes a new one. `validateDocument` checks one that you read from storage. ## Import ```typescript theme={null} import { createEmptyDocument, validateDocument, findSavedView, UnresolvedSavedViewError, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { createEmptyDocument, pageSize } from '@opengeometry/floorkit'; const document = createEmptyDocument(); document.unitSystem = 'metric'; document.sheets.push({ id: 'ground-plan', name: 'Ground floor', page: { format: 'A3', orientation: 'landscape', size: pageSize('A3', 'landscape'), margin: 10, }, viewports: [], annotations: [], }); const json = JSON.stringify(document); ``` ## Notes * A new document has no sheets. * Sheet sizes, margins and positions are in millimetres. The origin is the top-left corner. * The document is plain JSON data. Your app owns it. ## createEmptyDocument A blank document with no sheets. ```typescript theme={null} function createEmptyDocument(): PaperDocument; ``` ## validateDocument Structural guard for a deserialized document (validate at the storage boundary). ```typescript theme={null} function validateDocument(doc: unknown): doc is PaperDocument; ``` ## findSavedView Look up one saved drawing definition without resolving or drawing it. ```typescript theme={null} function findSavedView( viewId: string, views: readonly SavedDrawingView[], ): SavedDrawingView | undefined; ``` ## UnresolvedSavedViewError A named view needs a project-level resolver before it can be drawn. ```typescript theme={null} class UnresolvedSavedViewError extends Error { readonly viewportId: string; readonly viewId: string; readonly code = 'unresolved-saved-view'; constructor(viewportId: string, viewId: string); } ``` ## Types ### PaperDocument The serializable paper-space document — the source of truth a consumer stores and syncs (.spec/debt.md "Paper / drawing layer"). ```typescript theme={null} interface PaperDocument { version: 1; unitSystem?: UnitSystem; dimensionStyles?: DimensionStyleRegistry; styleId?: PaperStyleId; styleOverride?: PaperStyle; paperStyles?: PaperStyleRegistry; sheets: Sheet[]; } ``` Which conventions the whole drawing set follows: millimetre dimensions and 1:50-style scales, or feet-and-inches and 1/4" = 1'-0". A set is one country's practice, so this lives on the document rather than per sheet. Absent means `"metric"`, so existing documents are unchanged. Named dimension styles, keyed by the id an annotation's `styleId` names. Absent means every dimension uses `DEFAULT_DIMENSION_STYLE`. The paper style the whole set is drawn in — a builtin id or one of this document's own `paperStyles`. An overlay on the style: one class changed without forking the preset. Plain JSON, like everything else here. House styles that travel with the file, resolvable by id. They outrank the builtins and anything a host registers. ### Sheet ```typescript theme={null} interface Sheet { number?: string; id: string; name: string; page: PageSpec; viewports: Viewport[]; annotations: Annotation[]; titleblock?: TitleblockInstance; styleId?: PaperStyleId; styleOverride?: PaperStyle; } ``` Explicit sheet reference number; independent of the display name. This sheet's style, outranking the document's. Layered over the document's overlay, then the viewport's over this. ### Viewport ```typescript theme={null} type Viewport = ViewportBase & ( | { source: ViewSource; viewId?: never; } | { viewId: string; source?: never; } ); ``` ### SavedDrawingView Authored drawing intent. ```typescript theme={null} type SavedDrawingView = | SavedRasterIsometricView | (SavedViewBase & { kind: 'plan'; storeyId: string; cutHeightAboveStorey: number; visibleRange?: { below: number; above: number; }; elementIds?: string[] | 'all'; showMassing?: boolean; }) | (SavedViewBase & PhysicalDrawingScope & { kind: 'section'; line: { start: WorldXZ; end: WorldXZ; }; lookSide: 'left' | 'right'; depth: number; verticalCrop: { minY: number; maxY: number; }; }) | (SavedViewBase & PhysicalDrawingScope & { kind: 'elevation'; origin: WorldXZ; lookDirection: WorldXZ; crop: { left: number; right: number; bottom: number; top: number; }; }) | (SavedViewBase & { kind: 'iso'; camera: ProjectionCamera & { projection_mode: 'Orthographic'; }; elementIds?: string[] | 'all'; }); ``` ### PaperStyleId A builtin autocompletes; any other string is a custom (registered) id. ```typescript theme={null} type PaperStyleId = BuiltinPaperStyleId | (string & {}); ``` ### PaperStyle A style, or the overlay a document/sheet/viewport layers on one. ```typescript theme={null} interface PaperStyle { id?: string; name?: string; extends?: string; paper?: { color?: string; ground?: 'screen' | 'print' | 'both'; }; ink?: string; ladderStep?: number; classes?: Record; edgeClasses?: Record; producerFill?: 'allow' | 'ignore'; typography?: Typography; hand?: HandProfile; } ``` The preset this layers on. Resolved depth-first with a cycle guard. Default stroke colour for every class. Rungs walked on `PEN_LADDER_MM` for every class. Keyed by `ElementType` (`WALL`, `DOOR`, `SPACECONTAINER`, …). Composed on top of the class entry for an HLR edge class (`Hidden`, `VisibleCrease`); the shipped `EDGE_CLASS_MODIFIER` is already applied underneath, so these are deltas. Whether a producer's explicit `style.fill` reaches the paper. `"allow"` is `resolvePen`'s behaviour and the default; presets say `"ignore"`. Hand-drawn linework. Absent means ruled — every CAD preset. ### PaperStyleRegistry ```typescript theme={null} type PaperStyleRegistry = Record; ``` ### PageSpec ```typescript theme={null} interface PageSpec { format: PageFormat; orientation: PageOrientation; size: { width: number; height: number; }; margin: number; frame?: SheetFrameOptions; } ``` Paper units = millimetres, top-left origin (A4 = 210 × 297). Uniform inset from every edge. Still the only inset when `frame` is absent, which is what keeps existing documents rendering exactly as before. The printed drawing frame — border, asymmetric filing margin, centring marks and zone grid (ISO 5457\). Absent means no printed frame, and `margin` alone positions the title block. ### Annotation Sheet annotations, in paper units (mm, top-left origin). ```typescript theme={null} type Annotation = | TextAnnotation | DimensionAnnotation | MeasureAnnotation | AngularMeasureAnnotation | RadialMeasureAnnotation | LeaderAnnotation | ImageAnnotation | ShapeAnnotation; ``` ### TitleblockInstance Placement of a title-block template on a sheet, with values bound to the template's named fields (.spec/debt.md "Paper / drawing layer"). ```typescript theme={null} interface TitleblockInstance { templateId: string; values: Record; } ``` Values bound by name to the template's fields. ### ViewportBase A live reference into model space, placed on a sheet. The package does not export this type by name. ```typescript theme={null} interface ViewportBase { representation?: 'vector' | 'raster'; drawingNumber?: string; id: string; frame: { x: number; y: number; width: number; height: number; }; scale: number; scaleId?: string; rotationDeg: number; clip: boolean; viewCenter?: Point2; title?: { text?: string; showScale?: boolean; }; styleId?: PaperStyleId; styleOverride?: PaperStyle; } ``` Saved sections/elevations share one view definition across both outputs. Explicit drawing number belonging to this sheet placement. Placement rect on the sheet, in paper units (mm, top-left origin). Dimensionless drawing ratio (paper ÷ real), e.g. 1:50 → 0.02. Registered scale name (`NamedScale.id`), e.g. "1:50" or "1/4in-1ft". The model point (metres) placed at the centre of `frame`. Title drawn beneath the viewport. Absent draws nothing. This viewport's style, outranking the sheet's and the document's — how a 1:20 detail hatches differently from the 1:100 plan beside it. The narrowest overlay: layered over the document's and the sheet's. ### ViewSource What the viewport draws — resolved by the core drawing extractor (§7). ```typescript theme={null} type ViewSource = | { kind: 'plan'; levelId: string; cutElevation?: number; hideHidden?: boolean; entities?: string[] | 'all'; } | { kind: 'elevation'; sectionId: string; } | { kind: 'section'; sectionId: string; } | { kind: 'iso'; camera: ProjectionCamera; }; ``` ### SavedRasterIsometricView Saved rendering intent, independent of generated pixels and sheet placement. ```typescript theme={null} interface SavedRasterIsometricView { id: string; name: string; kind: 'raster-iso'; camera: ProjectionCamera & { projection_mode: 'Orthographic'; }; frame: RasterViewFrame; far: number; elementIds?: string[] | 'all'; background: string | null; } ``` Camera-space clipping distance in metres; must exceed camera.near. Null means transparent; an opaque background is a #rrggbb colour. ### SavedViewBase The package does not export this type by name. ```typescript theme={null} interface SavedViewBase { id: string; name: string; } ``` ### PhysicalDrawingScope Omitted scope means the entire model; an explicit list never silently drops unsupported IDs. ```typescript theme={null} interface PhysicalDrawingScope { buildingId?: string; elementIds?: string[] | 'all'; settings?: PhysicalDrawingSettings; } ``` ### WorldXZ World XZ coordinates in metres, written as \[x, z]. The package does not export this type by name. ```typescript theme={null} type WorldXZ = [number, number]; ``` ### ProjectionCamera Projection camera for the extractor. ```typescript theme={null} type ProjectionCamera = PlanProjectionCamera; ``` ### ClassStyle One element class's treatment. ```typescript theme={null} interface ClassStyle { visible?: boolean; weightMm?: number; ladderStep?: number; ink?: string; dash?: number[] | 'none'; fill?: string | 'none'; hatch?: string | HatchSpec | 'none'; cap?: 'butt' | 'round'; byKind?: Partial>>; } ``` `false` drops the class before projection — it costs nothing downstream. An explicit lineweight in paper mm. Prefer ladderStep, which keeps the weight a rung on `PEN_LADDER_MM`. Rungs to walk on `PEN_LADDER_MM`. Steps SUM across the style, the class, its `byKind` entry and the edge class. Stroke colour, as CSS. Dash runs in paper mm; `"none"` makes the line continuous. Poché fill for regions; `"none"` removes it. Poché hatch: a key into `HATCH_PATTERNS`, a literal spec, or `"none"`. Per primitive kind, merged over the class entry — how a door's swing (an arc) is drawn lighter than its leaf (a line) without a second class. ### Typography ```typescript theme={null} interface Typography { fontFamily?: string; letterSpacingEm?: number; textTransform?: 'none' | 'uppercase'; scale?: number; } ``` Always terminate the stack in `Helvetica, Arial, sans-serif`: it is one of the PDF standard 14, which is what the PDF sets in. Tracking, as a fraction of the text height (0.08 = +8 %). Multiplier on text height. √2 (1.414) is one rung of the ISO 3098 ladder. ### HandProfile The character of hand-drawn linework. ```typescript theme={null} interface HandProfile { salt: number; bowAmplitudeMm: number; weightRefMm: number; fieldLatticeMm: number; minLengthMm: number; segmentLenMm: number; capN: number; detail?: { amplitudeMm: number; latticeMm: number; }; overshootStartMm: number; overshootEndMm: number; cornerTurnDeg: number; crossedProbability: number; openGapMinMm: number; openGapMaxMm: number; strikeTiers: Array<{ atOrAboveMm: number; strikes: number; }>; restrikeOffsetMm: number; restrikeWeightFactor: number; restrikeTrimStartMm: number; restrikeTrimEndMm: number; restrikeWanderMm?: number; startBeadMm: number; startBeadWidthFactor: number; endTaperMm: number; endTaperWidthFactor: number; weightVariance: number; arcCentreOffsetMm: number; arcRadiusVar: number; arcSweepOverDeg: number; arcSweepUnderDeg: number; arcRestrikes: number; pocheInsetMm: number; pocheScribble?: boolean; scribbleJitter?: number; hatchAngleJitterDeg: number; hatchRegionDriftDeg: number; hatchSpacingVar: number; hatchEndShortMm: number; hatchEndOverMm: number; textRunRotationDeg: number; textBaselineMm: number; maxPrimitives: number; shedClasses: string[]; reach?: HandReach; } ``` Peak displacement of the field, in mm. 0 draws ruled lines. The weight the amplitude is quoted at; a stroke's interior samples scale by √(weight / weightRefMm), so heavier nibs wander more. Cell size of the field. Two faces of a wall a few mm apart sample nearly the same value, so the wall keeps its thickness; a long stroke crosses several cells and reads as a shallow S. Strokes shorter than this are not subdivided (their ends still follow the field, so they stay attached). Sample spacing along a stroke, and the cap on samples per stroke. A second, finer octave of the field laid over the bow — the nervous wander of a fineliner on a napkin. Positional like the bow, so shared vertices still agree. Absent means the bow alone; `segmentLenMm` must be a fraction of `latticeMm` for the wander to be sampled. Axial overrun past a corner: the start is placed, the finish is a lift. A turn sharper than this is a corner (pen lifted); shallower is one stroke. Share of corners drawn crossed; the rest stop short by an open gap. One decision per corner, shared by every stroke that meets there. Strikes by weight band, e.g. `[{ atOrAboveMm: 0.35, strikes: 2 }]`. Each re-strike wanders on its own by this much (mm) instead of tracking the first pass exactly — the passes cross and the line goes hairy. A short stroke at the start at `startBeadWidthFactor` × weight (a bead when > 1, a ramp when \< 1). 0 draws none. Only on strokes long enough to carry one. A short stroke at the end at `endTaperWidthFactor` × weight. 0 draws none. ± fraction of the nominal weight, one value per emitted stroke. Extra arcs at a radial offset — how a pencil re-strikes a swing. Pull the fill in from the outline by this much: the sliver of paper an inked poché leaves against its line. 0 fills to the line. Draw the hatch as continuous back-and-forth zigzags across each region — a scribbled poché, ONE stroke per run, laid across the region's long axis the way a hand scribbles a wall band — instead of separate parallel lines. Per-vertex jitter of a scribble's pitch and tilt, as a fraction of the spacing — what keeps a scribble from reading as ruled hatching. Per-line angle jitter, and one drift per region, both in degrees. ± fraction of the spacing, one value per region (so lines stay in register within a region). Each hatch line ends between `-short` and `+over` of the boundary. When the predicted output would exceed this, `shedClasses` are dropped BEFORE synthesis — a decision about what is on the sheet, never about where a primitive sits in the array. How far the hand reaches beyond the building. By default only viewport geometry is redrawn: dimensions, the frame and the title block stay ruled, because a drawing that wobbles its dimensions is a presentation, not an issue. A napkin sketch is exactly that presentation, and asks for all three. ### SheetFrameOptions What a document may say about its frame. ```typescript theme={null} interface SheetFrameOptions { border?: boolean; zones?: boolean; centringMarks?: boolean; margins?: Partial; bindingEdge?: 'left' | 'top'; } ``` Draw the border rectangle. Default true once `frame` is present at all. Zone reference grid. Default: on for A3-equivalent and larger. Centring marks at the middle of each edge. Default true. Override any of the four margins. Which edge gets the wide filing margin. Default "left". ### TextAnnotation ```typescript theme={null} interface TextAnnotation { id: string; kind: 'text'; text: string; position: Point2; fontSize: number; fontFamily?: string; color?: string; align?: 'left' | 'center' | 'right'; rotationDeg?: number; } ``` ### DimensionAnnotation The package does not export this type by name. ```typescript theme={null} interface DimensionAnnotation { id: string; kind: 'dimension'; styleId?: string; start: Point2; end: Point2; offset: number; label?: string; fontSize: number; } ``` Named style in `PaperDocument.dimensionStyles`. Absent uses the default. Perpendicular offset of the dimension line from the start→end segment. ### LeaderAnnotation The package does not export this type by name. ```typescript theme={null} interface LeaderAnnotation { id: string; kind: 'leader'; styleId?: string; target: Point2; points: Point2[]; text: string; fontSize: number; } ``` Named style in `PaperDocument.dimensionStyles`. Absent uses the default. Arrow tip — points at the thing being annotated. Knee points from the target to the text anchor (last point). ### ImageAnnotation The package does not export this type by name. ```typescript theme={null} interface ImageAnnotation { id: string; kind: 'image'; src: string; rect: { x: number; y: number; width: number; height: number; }; } ``` Data URL or remote URL; emitters must not require a DOM canvas. ### ShapeAnnotation The package does not export this type by name. ```typescript theme={null} interface ShapeAnnotation { id: string; kind: 'shape'; shape: ShapeKind; points: Point2[]; radius?: number; stroke?: string; strokeWidth?: number; fill?: string; } ``` line/polyline: vertices. rect: \[topLeft, bottomRight]. circle: \[center]. Required for `shape: 'circle'`. ### PhysicalDrawingSettings Shared appearance for technical vectors and raster presentation of one saved view. ```typescript theme={null} interface PhysicalDrawingSettings { context?: 'visible' | 'cut-only'; hiddenLines?: boolean; background?: string | null; levelLabels?: { datum: number; unit: 'm' | 'ft'; }; } ``` Absent means actual cut plus visible context; cut-only applies to sections. Absent means hidden lines are omitted from technical vectors. Raster background. Absent means white; null means transparent. Authored storey elevations, relative to this world-height datum in metres. ### HandReach The sheet furniture a hand profile may redraw, besides the building. ```typescript theme={null} interface HandReach { annotations?: boolean; frame?: boolean; titleblock?: boolean; } ``` Dimension, leader and view-title lines, and un-coloured shape annotations. Labels keep their measured value; authored colours (a redline) stay ruled. The drawing frame: border, zone ticks, centring marks. The title block's boxes and rules (a filled box stays ruled). ### ShapeKind The package does not export this type by name. ```typescript theme={null} type ShapeKind = 'line' | 'polyline' | 'rect' | 'circle'; ``` ## Related * [`AngularMeasureAnnotation`](/floorkit/api/annotations/dimension-geometry#angularmeasureannotation) * [`BuiltinPaperStyleId`](/floorkit/api/drawings/paper-styles#builtinpaperstyleid) * [`DimensionStyleRegistry`](/floorkit/api/annotations/dimension-styles#dimensionstyleregistry) * [`HatchSpec`](/floorkit/api/drawings/pens-and-hatches#hatchspec) * [`MeasureAnnotation`](/floorkit/api/annotations/dimension-strings#measureannotation) * [`PageFormat`](/floorkit/api/drawings/page-sizes-and-frames#pageformat) * [`PageOrientation`](/floorkit/api/drawings/page-sizes-and-frames#pageorientation) * [`PlanProjectionCamera`](/floorkit/api/drawings/drawing-helpers#planprojectioncamera) * [`Point2`](/floorkit/api/annotations/dimension-geometry#point2) * [`PrimitiveKind`](/floorkit/api/drawings/pens-and-hatches#primitivekind) * [`RadialMeasureAnnotation`](/floorkit/api/annotations/dimension-geometry#radialmeasureannotation) * [`RasterViewFrame`](/floorkit/api/views/raster-capture#rasterviewframe) * [`SheetMargins`](/floorkit/api/drawings/page-sizes-and-frames#sheetmargins) * [`UnitSystem`](/floorkit/api/annotations/dimension-geometry#unitsystem) # TitleblockRegistry Source: https://docs.ploterr.com/floorkit/api/drawings/titleblock-registry TitleblockRegistry holds your title block templates. A sheet names a template by its id and gives the field values. ## Import ```typescript theme={null} import { TitleblockRegistry, resolveTitleblock, DEFAULT_TITLEBLOCK_ID, DEFAULT_TITLEBLOCK_TEMPLATE, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { TitleblockRegistry, DEFAULT_TITLEBLOCK_ID, DEFAULT_TITLEBLOCK_TEMPLATE, } from '@opengeometry/floorkit'; const templates = new TitleblockRegistry(); const standard = templates.get(DEFAULT_TITLEBLOCK_ID); const custom = { ...DEFAULT_TITLEBLOCK_TEMPLATE, id: 'office-titleblock' }; templates.register(custom); console.log(standard, templates.get('office-titleblock')); ``` ## Notes * The default template is registered, unless `seedDefault` is `false`. * `register()` replaces a template that has the same id. * `get()` returns `undefined` for an unknown id. * Template geometry is in paper millimetres. * The registry is not saved in the project file. Give it again when you export, through the `templates` option. **Inheritance:** `implements TitleblockTemplateRegistry` ## Constructor ```typescript theme={null} constructor(seedDefault?: boolean); ``` ## Methods ### register ```typescript theme={null} register(template: TitleblockTemplate): void; ``` ### get ```typescript theme={null} get(id: string): TitleblockTemplate | undefined; ``` ## resolveTitleblock Resolve a template + instance + context to absolute paper-mm drawables. ```typescript theme={null} function resolveTitleblock( template: TitleblockTemplate, instance: TitleblockInstance, context: TitleblockContext, ): ResolvedTitleblock; ``` ## DEFAULT\_TITLEBLOCK\_ID ```typescript theme={null} const DEFAULT_TITLEBLOCK_ID = 'default'; ``` ## DEFAULT\_TITLEBLOCK\_TEMPLATE A bottom-right A-series title block that works out of the box: project name, sheet name, an editable sheet number, ISO date, scale, and page x/of y. ```typescript theme={null} const DEFAULT_TITLEBLOCK_TEMPLATE: TitleblockTemplate; ``` ## Types ### TitleblockTemplateRegistry The package does not export this type by name. ```typescript theme={null} interface TitleblockTemplateRegistry { register(template: TitleblockTemplate): void; get(id: string): TitleblockTemplate | undefined; } ``` ### TitleblockTemplate A reusable title-block: static graphics + named fields, authored once and placed via a `TitleblockInstance` (.spec/debt.md "Paper / drawing layer"). ```typescript theme={null} interface TitleblockTemplate { id: string; name: string; size: { width: number; height: number; }; anchor: 'bottom-right' | 'bottom' | 'right' | 'top' | 'free'; origin?: { x: number; y: number; }; elements: TitleblockElement[]; } ``` Block size in paper units (mm). Absolute paper-mm origin, for `anchor: "free"`. Ignored otherwise. ### TitleblockContext Inputs used to compute auto-fields when resolving a title block. The package does not export this type by name. ```typescript theme={null} interface TitleblockContext { page: PageSpec; sheetName: string; projectName?: string; pageNumber?: number; pageCount?: number; scale?: string; date?: string; } ``` Pre-formatted scale label, e.g. "1:50". ISO date (YYYY-MM-DD). Defaults to today. ### ResolvedTitleblock A title block resolved to absolute paper-mm drawables. The package does not export this type by name. ```typescript theme={null} interface ResolvedTitleblock { statics: ResolvedStatic[]; texts: ResolvedText[]; } ``` ### TitleblockElement ```typescript theme={null} type TitleblockElement = TitleblockStatic | TitleblockField; ``` ### ResolvedStatic The package does not export this type by name. ```typescript theme={null} interface ResolvedStatic { kind: 'line' | 'rect' | 'image'; rect: Rect; stroke?: string; fill?: string; src?: string; } ``` ### ResolvedText The package does not export this type by name. ```typescript theme={null} interface ResolvedText { text: string; rect: Rect; fontSize: number; align: 'left' | 'center' | 'right'; } ``` ### TitleblockStatic The package does not export this type by name. ```typescript theme={null} interface TitleblockStatic { kind: 'line' | 'rect' | 'image'; rect: Rect; stroke?: string; fill?: string; src?: string; } ``` Relative to the block origin (top-left). `line` runs corner→corner. ### TitleblockField The package does not export this type by name. ```typescript theme={null} interface TitleblockField { kind: 'field'; name: string; auto?: TitleblockAutoField; rect: Rect; fontSize: number; align?: 'left' | 'center' | 'right'; label?: string; } ``` Bound by name from `TitleblockInstance.values`, unless `auto` is set. Relative to the block origin (top-left). Optional caption drawn before the value. ### Rect Rectangle in paper units (mm). The package does not export this type by name. ```typescript theme={null} interface Rect { x: number; y: number; width: number; height: number; } ``` ### TitleblockAutoField The package does not export this type by name. ```typescript theme={null} type TitleblockAutoField = | 'pageNumber' | 'pageCount' | 'date' | 'scale' | 'sheetName' | 'projectName'; ``` ## Related * [`PageSpec`](/floorkit/api/drawings/sheet-documents#pagespec) * [`TitleblockInstance`](/floorkit/api/drawings/sheet-documents#titleblockinstance) # View markers and level labels Source: https://docs.ploterr.com/floorkit/api/drawings/view-markers-and-level-labels Draw section marks, elevation marks and storey level labels. ## Import ```typescript theme={null} import { resolveProjectViewMarkers, drawProjectViewMarkers, drawPhysicalLevelLabels, } from '@opengeometry/floorkit'; ``` ## resolveProjectViewMarkers Resolve plan markers without mutating saved intent. ```typescript theme={null} function resolveProjectViewMarkers( context: ProjectViewMarkerContext, parentViewId: string, ): readonly ResolvedProjectViewMarker[]; ``` ## drawProjectViewMarkers Draw saved markers in parent plan world XZ, mapped to drawing XY. ```typescript theme={null} function drawProjectViewMarkers( context: ProjectViewMarkerContext, parentViewId: string, options?: ProjectViewMarkerDrawingOptions, ): readonly ProjectViewMarkerDrawingGroup[]; ``` ## drawPhysicalLevelLabels Draw authored storey datums inside the vertical crop, with a paper height of 2.5 mm. ```typescript theme={null} function drawPhysicalLevelLabels( model: BuildingModel, view: PhysicalSavedView, volume: PhysicalViewVolume, ): DrawingPrimitive[]; ``` ## Types ### ProjectViewMarkerContext ```typescript theme={null} interface ProjectViewMarkerContext { views: readonly SavedDrawingView[]; paper: PaperDocument; markers?: readonly ProjectViewMarker[]; } ``` ### ResolvedProjectViewMarker ```typescript theme={null} interface ResolvedProjectViewMarker { marker: ProjectViewMarker; viewId: string; status: | 'resolved' | 'unplaced' | 'placement-required' | 'missing-view' | 'missing-placement' | 'unsupported-view'; viewName?: string; drawingNumber?: string; sheetNumber?: string; geometry?: ProjectViewMarkerGeometry; } ``` ### ProjectViewMarkerDrawingOptions ```typescript theme={null} interface ProjectViewMarkerDrawingOptions { headSizeMetres?: number; paperTextHeightMm?: number; } ``` Arrowhead length in parent plan world metres. Default 0.3. Printed label height in millimetres. Default 2.5. ### ProjectViewMarkerDrawingGroup Authored marker hit target, separate from BIM element ownership. ```typescript theme={null} interface ProjectViewMarkerDrawingGroup { markerId: string; viewId: string; status: ResolvedProjectViewMarker['status']; primitives: DrawingPrimitive[]; } ``` ### PhysicalSavedView The package does not export this type by name. ```typescript theme={null} type PhysicalSavedView = Extract< SavedDrawingView, { kind: 'section' | 'elevation'; } >; ``` ### PhysicalViewVolume A vertical physical view in metres, with crop x right, y down and depth along look. ```typescript theme={null} interface PhysicalViewVolume { readonly kind: 'section' | 'elevation'; readonly frame: ViewFrame; readonly crop: PlanVectorBounds; readonly near: 0; readonly far?: number; } ``` The authored view plane is always the near plane. Positive depth from the view plane; absent for an unbounded elevation. ### ProjectViewMarkerGeometry Geometry in parent plan world \[x, z] metres; positive Z follows plan screen-down. ```typescript theme={null} type ProjectViewMarkerGeometry = | { kind: 'section'; line: { start: [number, number]; end: [number, number]; }; lookDirection: [number, number]; } | { kind: 'elevation'; origin: [number, number]; lookDirection: [number, number]; } | { kind: 'reference'; position: [number, number]; rotationDeg: number; }; ``` ## Related * [`BuildingModel`](/floorkit/api/architecture/validate-building-model#buildingmodel) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`PaperDocument`](/floorkit/api/drawings/sheet-documents#paperdocument) * [`PlanVectorBounds`](/floorkit/api/drawings/draw-project-view#planvectorbounds) * [`ProjectViewMarker`](/floorkit/api/exports/project-file#projectviewmarker) * [`SavedDrawingView`](/floorkit/api/drawings/sheet-documents#saveddrawingview) * [`ViewFrame`](/floorkit/api/drawings/drawing-helpers#viewframe) # Viewport placement Source: https://docs.ploterr.com/floorkit/api/drawings/viewport-placement Fit a drawing into a viewport and convert between model and paper. ## Import ```typescript theme={null} import { placeDrawing, viewportTransform, inverseViewportTransform, fitTransform, fitViewportScale, viewCenterOf, viewSourceToDrawingRequest, modelToPaper, } from '@opengeometry/floorkit'; ``` ## placeDrawing Place a viewport's `VectorDrawing` (model metres) into its `frame` (paper mm). ```typescript theme={null} function placeDrawing( drawing: VectorDrawing, viewport: Viewport, policy?: SheetPolicy, ): PlacedDrawing; ``` ## viewportTransform The model→paper point transform for a viewport: model metres centred in the `frame` at `viewport.scale`, then rotated by `viewport.rotationDeg`. ```typescript theme={null} function viewportTransform(drawing: VectorDrawing, viewport: Viewport): (point: Point2) => Point2; ``` ## inverseViewportTransform Inverse of viewportTransform: map a paper-mm point back to model metres for the same viewport. ```typescript theme={null} function inverseViewportTransform( drawing: VectorDrawing, viewport: Viewport, ): (paper: Point2) => Point2; ``` ## fitTransform Compute the stage transform that fits a page into a viewport, centred. ```typescript theme={null} function fitTransform( viewport: FitViewport, pageMm: { width: number; height: number; }, { pixelsPerMm, padding, minScale, maxScale }: FitOptions, ): FitTransform | null; ``` ## fitViewportScale The scale and aim that fit a drawing inside a frame, snapped to a real scale. ```typescript theme={null} function fitViewportScale( drawing: VectorDrawing, frame: Viewport['frame'], options?: { paddingMm?: number; system?: UnitSystem; }, ): { scale: number; scaleId?: string; viewCenter: Point2; }; ``` ## viewCenterOf The model point that lands at the centre of the viewport's frame. ```typescript theme={null} function viewCenterOf(drawing: VectorDrawing, viewport: Viewport): Point2; ``` ## viewSourceToDrawingRequest Map a viewport's `ViewSource` to a core DrawingRequest. ```typescript theme={null} function viewSourceToDrawingRequest( source: ViewSource, options?: { cutElevation?: number; hideHidden?: boolean; }, ): DrawingRequest; ``` ## modelToPaper Model length (metres) → paper length (millimetres) at a drawing scale. ```typescript theme={null} function modelToPaper(modelMeters: number, scale: number): number; ``` ## Types ### SheetPolicy Resolved once per sheet (and again per viewport that names its own style). ```typescript theme={null} interface SheetPolicy { style: PaperStyle; paperColor: string; ground: 'screen' | 'print' | 'both'; sheetSalt: number; viewportTextInk: string; unresolved: string[]; } ``` The flattened style: no `extends` left, every layer merged. `hashString(sheet.id)` — the salt every hand stroke on this sheet shares. Ink for model-anchored `drawing.texts`, which never pass through `penFor`. Style ids, `extends`, hatch keys and colours that could not be resolved. Warned once per sheet; the render falls back rather than failing. ### PlacedDrawing A viewport's drawing mapped into paper space (mm), ready to emit. The package does not export this type by name. ```typescript theme={null} interface PlacedDrawing { primitives: DrawingPrimitive[]; readonly lines: VectorLine[]; texts: VectorText[]; clip?: { x: number; y: number; width: number; height: number; }; pen?: PenContext; } ``` Standardized primitives in paper mm, with lineweight resolved. Clip rectangle (the viewport frame) when `viewport.clip` is set. The style context the primitives were resolved under. ### FitViewport Viewport dimensions in screen pixels. ```typescript theme={null} interface FitViewport { width: number; height: number; } ``` ### FitOptions ```typescript theme={null} interface FitOptions { pixelsPerMm: number; padding: number; minScale: number; maxScale: number; } ``` Layer scale — how many screen pixels one paper millimetre occupies at 1×. Fraction of the viewport to fill, leaving breathing room. ### FitTransform type The stage transform that frames a page: uniform scale plus a top-left offset. ```typescript theme={null} interface FitTransform { scale: number; x: number; y: number; } ``` ## Related * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`DrawingRequest`](/floorkit/api/drawings/draw-project-view#drawingrequest) * [`PaperStyle`](/floorkit/api/drawings/sheet-documents#paperstyle) * [`PenContext`](/floorkit/api/drawings/pens-and-hatches#pencontext) * [`Point2`](/floorkit/api/annotations/dimension-geometry#point2) * [`UnitSystem`](/floorkit/api/annotations/dimension-geometry#unitsystem) * [`VectorDrawing`](/floorkit/api/drawings/draw-project-view#vectordrawing) * [`VectorLine`](/floorkit/api/drawings/draw-project-view#vectorline) * [`VectorText`](/floorkit/api/drawings/draw-project-view#vectortext) * [`Viewport`](/floorkit/api/drawings/sheet-documents#viewport) * [`ViewSource`](/floorkit/api/drawings/sheet-documents#viewsource) # beginManagedWallCreate Source: https://docs.ploterr.com/floorkit/api/editor/begin-managed-wall-create Start the creation of one wall in a session, for your own wall tool. ## Import ```typescript theme={null} import { beginManagedWallCreate } from '@opengeometry/floorkit'; ``` ## Signature ```typescript theme={null} function beginManagedWallCreate( session: BuildingSession, buildingId: string, storeyId: string, ): ManagedWallCreate | undefined; ``` ## Parameters ## Returns ```typescript theme={null} ManagedWallCreate | undefined; ``` **Throws:** \{BuildingSessionError} When a captured commit is stale or disposed. **Throws:** \{BmEditError} When the wall payload or resulting saved geometry is invalid. ## Types ### ManagedWallCreate A captured saved-storey transaction; an uncommitted drawing draft publishes nothing. ```typescript theme={null} interface ManagedWallCreate { commit(wall: WallCreateCommand['wall']): boolean; } ``` ## Related * [`BuildingSession`](/floorkit/api/editor/building-session) * [`WallCreateCommand`](/floorkit/api/editor/building-session#wallcreatecommand) # bindManagedSceneEditor Source: https://docs.ploterr.com/floorkit/api/editor/bind-managed-scene-editor bindManagedSceneEditor connects a GUIEditor to a BuildingSession. The user then edits the building in the 3D view, and each edit goes into the session. ## Import ```typescript theme={null} import { bindManagedSceneEditor } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { bindManagedSceneEditor, type GUIEditor, type BuildingSession, } from '@opengeometry/floorkit'; export function connectScene(editor: GUIEditor, session: BuildingSession): void { bindManagedSceneEditor(editor, session, { hostedOpeningDetachTolerance: 0.35, onError: (error) => console.error('Scene edit rejected', error), }); } ``` ## Notes * Add `session.root` to your scene one time. * `hostedOpeningDetachTolerance` is a distance in metres. When the user moves a door or a window farther than this from its wall, it leaves the wall. * Set `onError` to show an edit that Floorkit rejected. * Dispose the editor before the session. ## Signature ```typescript theme={null} function bindManagedSceneEditor( editor: GUIEditor, session: BuildingSession, options?: ManagedSceneBindingOptions, ): void; ``` ## Parameters Default: `{}`. ## Returns ```typescript theme={null} void ``` ## Types ### ManagedSceneBindingOptions ```typescript theme={null} interface ManagedSceneBindingOptions { onError?: (error: unknown) => void; hostedOpeningDetachTolerance?: number; history?: ManagedEditHistory; } ``` Metres from every wall beyond which a dragged door or window leaves its wall; default 0.35. Undo and redo through another history, such as a project manager; default is the session. ## Related * [`BuildingSession`](/floorkit/api/editor/building-session) * [`GUIEditor`](/floorkit/api/editor/gui-editor) * [`ManagedEditHistory`](/floorkit/api/editor/managed-plan-editor#managededithistory) # BuildingSession Source: https://docs.ploterr.com/floorkit/api/editor/building-session BuildingSession holds one building, its 3D geometry and its edit history. You send it edit commands. It checks each command, builds the geometry again and keeps undo and redo. ## Import ```typescript theme={null} import { BuildingSession, BuildingSessionError } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { BuildingSessionError, type BuildingSession, type BuildingEditCommand, } from '@opengeometry/floorkit'; export function captureEdit(session: BuildingSession, command: BuildingEditCommand): () => void { const revisionAtGestureStart = session.revision; return () => { try { session.apply(command, revisionAtGestureStart); } catch (error) { if (error instanceof BuildingSessionError && error.code === 'stale-revision') { console.warn('The model changed during this gesture; start again.'); return; } throw error; } }; } ``` ## Notes * Add `session.root` to your scene one time. It stays valid after each edit. * Read `session.revision` when an edit starts, and pass it to `apply`. If a different edit came first, `apply` throws `stale-revision`. * An edit that fails changes nothing. * A new edit clears the redo list. `undo` and `redo` return `null` when there is nothing to do. * `snapshot()` returns a copy of the building model. Save this copy. * After an edit, get the elements again from `currentBuild` by their ids. A changed element can be a new object, and Floorkit then disposes the old object. A changed wall, and a door, a window or a hole on a wall, can also stay the same object with new geometry. * `add-element`, `replace-element` and `remove-element` change one slab, roof, room, stair, column, beam, railing or instance. An `add` rejects an id that the list has. A `replace` or a `remove` rejects an id that the list does not have, and the new element of a `replace` must have the same id. * `add-asset`, `replace-asset` and `remove-asset` change one asset of the model. `remove-asset` rejects an asset that an instance uses. * `add-storey`, `remove-storey` and `replace-storey-settings` change the storeys of a building. `add-storey` rejects a storey id that the building has, and `remove-storey` rejects the last storey. * `replace-mass` sets the mass of a building and the settings of all its storeys in one step. `storeys` is the new storey list, in its order. A storey id that stays keeps its walls and elements, a new id adds an empty storey, and a storey that is left out is removed. It rejects the removal of a storey that holds an element. Without `mass`, the building has no mass. `massSegmentHeightCommand(model, buildingId, segmentId, height)` gives this command for a new height of one segment, snapped to whole storeys. * `add-building` adds a whole building, last in the list: its storeys, their elements and its optional `mass`. Each id in it must be new in the whole model. The smallest valid building has one storey, and for a massing study a `mass` with one segment whose `storeyIds` names that storey and whose `height` is the height of the storey. `remove-building` removes a building and all it holds; it rejects the last building of the model. * `replace-site` replaces the site whole: `boundary`, `lotLines`, `elevation`, `geoAnchor`, `trueNorthDeg` and the `zoning` rules. Without `site`, the model has no site any more. The model check refuses a self-intersecting boundary and a `lotLines` list that does not have one class per boundary edge. Run `evaluateZoning` again after the step; the command does not run it. The session builds again only the storeys and the mass of the added or removed building, or only the site. * A `batch` applies several commands as one step: one revision, one build and one undo step. If one command fails, nothing changes, and the message of the error starts with the place of the command, such as `command 2 of 3:`. After a batch, `lastEditCommand` is the batch. * A session does not know the saved views of a project. Thus a `remove-storey`, a `replace-mass` or a `remove-building` that removes a storey that a plan view names passes in a session. [`ProjectManager.apply`](/floorkit/api/editor/project-manager) rejects it. * The history is not saved in the project file. ## Constructor ```typescript theme={null} constructor(model: BuildingModel, options?: Pick); ``` `.op` sources for the document's `external-op` assets, keyed by their `uri`, as either a raw buffer or an already-parsed definition. Fetching is the CALLER's pre-step so this function stays synchronous — the same split `.opbase` uses (`OPBaseOptions.assetSources`). An `embedded-op` asset needs nothing here. The colour of a mass part by its use, `#rrggbb` by use name, used for a part when neither it nor its mass has a `color`. Replaces `MASS_COLORS_BY_USE` whole; a use it does not list takes `#a0b8c8`. Pass it where the project is opened (`ProjectManager` `build`) so that the live build, the plan drawings and the sheets take the same colours. Stamp `storeyId`/`buildingId` onto every element's propertySet. Default true. ## Properties Read-only. Stable scene node. The app may mount it once and retain it across edits. Read-only. Read-only. Read-only. Read-only. Read-only. Caller-owned identity of the latest successfully published edit or history replay; after a batch it is the batch. Read-only. ## Methods ### snapshot ```typescript theme={null} snapshot(): BuildingModel; ``` A caller-owned copy; live preview edits never change this saved model. ### apply ```typescript theme={null} apply(command: BuildingEditCommand, expectedRevision: number): number; ``` Return the new revision after one complete edit and compilation. ### undo ```typescript theme={null} undo(expectedRevision: number): number | null; ``` Restore the preceding committed model; an empty history is a no-op. ### redo ```typescript theme={null} redo(expectedRevision: number): number | null; ``` Reapply the next committed model; an empty redo history is a no-op. ### subscribe ```typescript theme={null} subscribe(listener: (change: BuildingSessionChange) => void): () => void; ``` Notify after a committed transition; unsubscribe may be called repeatedly. ### dispose ```typescript theme={null} dispose(): void; ``` ## BuildingSessionError ```typescript theme={null} class BuildingSessionError extends Error { readonly code: BuildingSessionErrorCode; constructor(code: BuildingSessionErrorCode, message: string); } ``` ## Types ### BuildingEditCommand A single, serializable edit to an authored building model. ```typescript theme={null} type BuildingEditCommand = | BatchCommand | ModelObjectEditCommand | ElementListCommand | AssetCommand | StoreyCommand | ReplaceMassCommand | BuildingCommand | ReplaceSiteCommand | SlabTransformCommand | SpaceTransformCommand | RoofTransformCommand | StairTransformCommand | ElementReshapeCommand | NetworkWallTranslateCommand | NetworkWallEndMoveCommand | WallCreateCommand | WallDeleteCommand | HostedOpeningRemoveCommand | FreeOpeningRemoveCommand | HostedOpeningCreateCommand | FreeOpeningCreateCommand | HostedOpeningDetachCommand | HostedOpeningRehostCommand | { kind: 'transform-wall'; buildingId: string; storeyId: string; wallId: string; origin: PointXZ; translation: PointXZ; yawDeg: number; } | { kind: 'move-wall-point'; buildingId: string; storeyId: string; wallId: string; pointIndex: number; position: PointXZ; } | { kind: 'move-hosted-opening'; buildingId: string; storeyId: string; wallId: string; openingId: string; offset: number; } | { kind: 'move-free-opening'; buildingId: string; storeyId: string; openingId: string; position: PointXZ; yawDeg: number; } | { kind: 'rehost-free-opening'; buildingId: string; storeyId: string; openingId: string; wallId: string; position: PointXZ; yawDeg: number; }; ``` ### BuildingSessionChange ```typescript theme={null} interface BuildingSessionChange { readonly kind: 'apply' | 'undo' | 'redo' | 'restore'; readonly revision: number; readonly changed?: readonly string[]; } ``` What made the step. `restore` is no saved step: a step failed after it changed built objects, and the session built them again from the saved model. The revision of the session after the step. A `restore` leaves the revision as it was. The live ids (keys of `currentBuild.elementsById` and `modelObjectsById`) that the step built again, added or removed, where a removed id is in the list and not in the build; absent when the whole building was built again. An element that was built again is a new object, or the same object with new geometry. ### BuildingSessionErrorCode ```typescript theme={null} type BuildingSessionErrorCode = 'stale-revision' | 'disposed' | 'reentrant-transition'; ``` ### BatchCommand Several edits as one step, in order: all of them or none. ```typescript theme={null} interface BatchCommand { kind: 'batch'; commands: readonly Exclude[]; } ``` ### ModelObjectEditCommand One complete saved object edit; replacement preserves authored identity and kind. ```typescript theme={null} type ModelObjectEditCommand = | { kind: 'add-model-object'; buildingId: string; storeyId: string; object: BmModelObject; } | { kind: 'replace-model-object'; buildingId: string; storeyId: string; objectId: string; object: BmModelObject; } | { kind: 'remove-model-object'; buildingId: string; storeyId: string; objectId: string; }; ``` ### ElementListCommand One saved edit of a storey list; a replacement keeps the element id and its list. ```typescript theme={null} type ElementListCommand = { [List in keyof ElementLists]: | { kind: 'add-element'; buildingId: string; storeyId: string; list: List; element: ElementLists[List]; } | { kind: 'replace-element'; buildingId: string; storeyId: string; list: List; elementId: string; element: ElementLists[List]; } | { kind: 'remove-element'; buildingId: string; storeyId: string; list: List; elementId: string; }; }[keyof ElementLists]; ``` ### AssetCommand One saved edit of the block definitions of a model; an instance names its definition by id. ```typescript theme={null} type AssetCommand = | { kind: 'add-asset'; assetId: string; asset: BmAssetRef; } | { kind: 'replace-asset'; assetId: string; asset: BmAssetRef; } | { kind: 'remove-asset'; assetId: string; }; ``` ### StoreyCommand One saved edit of the storeys of a building; the settings of a storey are replaced whole. ```typescript theme={null} type StoreyCommand = | { kind: 'add-storey'; buildingId: string; storey: BmStorey; } | { kind: 'remove-storey'; buildingId: string; storeyId: string; } | { kind: 'replace-storey-settings'; buildingId: string; storeyId: string; settings: Pick; }; ``` ### SlabTransformCommand One saved slab transform in world XZ coordinates, retaining its vertical placement. ```typescript theme={null} interface SlabTransformCommand extends ProfileTransform { kind: 'transform-slab'; buildingId: string; storeyId: string; slabId: string; } ``` ### SpaceTransformCommand One saved space transform in world XZ coordinates, retaining its vertical placement. ```typescript theme={null} interface SpaceTransformCommand extends ProfileTransform { kind: 'transform-space'; buildingId: string; storeyId: string; spaceId: string; } ``` ### RoofTransformCommand One saved roof transform in world XZ coordinates, retaining its vertical placement. ```typescript theme={null} interface RoofTransformCommand extends ProfileTransform { kind: 'transform-roof'; buildingId: string; storeyId: string; roofId: string; } ``` ### StairTransformCommand One saved stair transform in world XZ coordinates, retaining its vertical placement. ```typescript theme={null} interface StairTransformCommand extends ProfileTransform { kind: 'transform-stair'; buildingId: string; storeyId: string; stairId: string; } ``` ### ElementReshapeCommand ```typescript theme={null} type ElementReshapeCommand = | SlabReshapeCommand | RoofReshapeCommand | SpaceReshapeCommand | StairReshapeCommand; ``` ### NetworkWallTranslateCommand Translate one compiled segment by a world XZ delta through its storey's WallNetwork. ```typescript theme={null} interface NetworkWallTranslateCommand { kind: 'translate-network-wall'; buildingId: string; storeyId: string; wallId: string; segmentIndex: number; translation: PointXZ; } ``` ### NetworkWallEndMoveCommand Move one end of a compiled segment through its storey's WallNetwork. ```typescript theme={null} interface NetworkWallEndMoveCommand { kind: 'move-network-wall-end'; buildingId: string; storeyId: string; wallId: string; segmentIndex: number; end: WallEnd; position: PointXZ; } ``` ### WallCreateCommand Append one complete authored wall run; openings require their own saved edit. ```typescript theme={null} interface WallCreateCommand { kind: 'create-wall'; buildingId: string; storeyId: string; wall: Omit & { openings?: never; }; } ``` ### WallDeleteCommand Remove the complete authored run, including every segment of a closed wall. ```typescript theme={null} interface WallDeleteCommand { kind: 'delete-wall'; buildingId: string; storeyId: string; wallId: string; } ``` ### HostedOpeningRemoveCommand Remove one authored opening from its exact wall without changing its wall axis. ```typescript theme={null} interface HostedOpeningRemoveCommand { kind: 'remove-hosted-opening'; buildingId: string; storeyId: string; wallId: string; openingId: string; } ``` ### FreeOpeningRemoveCommand Remove one exact storey-owned free door or window without changing any wall. ```typescript theme={null} interface FreeOpeningRemoveCommand { kind: 'remove-free-opening'; buildingId: string; storeyId: string; openingId: string; } ``` ### HostedOpeningCreateCommand Add one complete authored door or window to its explicitly chosen wall. ```typescript theme={null} interface HostedOpeningCreateCommand { kind: 'add-hosted-opening'; buildingId: string; storeyId: string; wallId: string; opening: BmDoor | BmWindow; } ``` ### FreeOpeningCreateCommand Add one complete physical door or window at an explicit free pose in a storey. ```typescript theme={null} interface FreeOpeningCreateCommand { kind: 'add-free-opening'; buildingId: string; storeyId: string; opening: BmUnhostedOpening; } ``` ### HostedOpeningDetachCommand Explicitly remove a door/window from its host and place it in world XZ metres. The package does not export this type by name. ```typescript theme={null} interface HostedOpeningDetachCommand { kind: 'detach-hosted-opening'; buildingId: string; storeyId: string; wallId: string; openingId: string; position: PointXZ; } ``` ### HostedOpeningRehostCommand Move one door, window or void from its wall to a place on another wall of the same storey. ```typescript theme={null} interface HostedOpeningRehostCommand { kind: 'rehost-hosted-opening'; buildingId: string; storeyId: string; wallId: string; openingId: string; toWallId: string; offset: number; } ``` Distance in metres along the saved centreline of the new wall. ### ElementLists The storey lists that the element commands edit, with the element type of each list. ```typescript theme={null} interface ElementLists { slabs: BmSlab; roofs: BmRoof; spaces: BmSpace; stairs: BmStair; columns: BmColumn; beams: BmBeam; railings: BmRailing; instances: BmAssetInstance; } ``` ### ProfileTransform A rigid world XZ transform shared by authored polygon families. The package does not export this type by name. ```typescript theme={null} interface ProfileTransform { origin: PointXZ; translation: PointXZ; yawDeg: number; } ``` World XZ pivot in metres. World XZ translation in metres, applied after rotation. Relative degrees; positive yaw turns +X toward +Z. ### SlabReshapeCommand Replace one slab's outline and thickness. ```typescript theme={null} interface SlabReshapeCommand { kind: 'reshape-slab'; buildingId: string; storeyId: string; slabId: string; profile: PolygonProfile; thickness: number; } ``` Outline and holes in world XZ metres. ### RoofReshapeCommand Replace one roof's outline and its eave height above the storey. ```typescript theme={null} interface RoofReshapeCommand { kind: 'reshape-roof'; buildingId: string; storeyId: string; roofId: string; profile: PolygonProfile; baseOffset: number; } ``` Outline and holes in world XZ metres. ### SpaceReshapeCommand Replace one room's outline and, when given, its height. ```typescript theme={null} interface SpaceReshapeCommand { kind: 'reshape-space'; buildingId: string; storeyId: string; spaceId: string; profile: PolygonProfile; height?: number; } ``` Outline and holes in world XZ metres. ### StairReshapeCommand Put one stair's start point at a new place and height above the storey; a given size replaces its own. ```typescript theme={null} interface StairReshapeCommand { kind: 'reshape-stair'; buildingId: string; storeyId: string; stairId: string; origin: PointXZ; baseOffset: number; width?: number; treadDepth?: number; landingDepth?: number; } ``` ## Related * [`BmAssetInstance`](/floorkit/api/architecture/validate-building-model#bmassetinstance) * [`BmAssetRef`](/floorkit/api/architecture/validate-building-model#bmassetref) * [`BmBeam`](/floorkit/api/architecture/validate-building-model#bmbeam) * [`BmColumn`](/floorkit/api/architecture/validate-building-model#bmcolumn) * [`BmDoor`](/floorkit/api/architecture/validate-building-model#bmdoor) * [`BmModelObject`](/floorkit/api/architecture/validate-building-model#bmmodelobject) * [`BmRailing`](/floorkit/api/architecture/validate-building-model#bmrailing) * [`BmRoof`](/floorkit/api/architecture/validate-building-model#bmroof) * [`BmSlab`](/floorkit/api/architecture/validate-building-model#bmslab) * [`BmSpace`](/floorkit/api/architecture/validate-building-model#bmspace) * [`BmStair`](/floorkit/api/architecture/validate-building-model#bmstair) * [`BmStorey`](/floorkit/api/architecture/validate-building-model#bmstorey) * [`BmUnhostedOpening`](/floorkit/api/architecture/validate-building-model#bmunhostedopening) * [`BmWall`](/floorkit/api/architecture/validate-building-model#bmwall) * [`BmWindow`](/floorkit/api/architecture/validate-building-model#bmwindow) * [`BuildingCommand`](/floorkit/api/editor/pure-edits#buildingcommand) * [`BuildingModel`](/floorkit/api/architecture/validate-building-model#buildingmodel) * [`BuildOptions`](/floorkit/api/architecture/build-from-building-model#buildoptions) * [`BuildResult`](/floorkit/api/architecture/build-from-building-model#buildresult) * [`PointXZ`](/floorkit/api/primitives/polygon-maths#pointxz) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`ReplaceMassCommand`](/floorkit/api/editor/pure-edits#replacemasscommand) * [`ReplaceSiteCommand`](/floorkit/api/editor/pure-edits#replacesitecommand) * [`WallEnd`](/floorkit/api/architecture/wall-network#wallend) # Editable properties Source: https://docs.ploterr.com/floorkit/api/editor/editable-properties Read and write one value in the configuration of an element. ## Import ```typescript theme={null} import { readConfigPath, writeConfigPath, PROPERTY_SETS_EDITABLE_PROP, } from '@opengeometry/floorkit'; ``` ## readConfigPath ```typescript theme={null} function readConfigPath(config: unknown, path: string): unknown; ``` ## writeConfigPath ```typescript theme={null} function writeConfigPath( target: { getOPConfig(): TConfig; setOPConfig(partial: Partial): void; }, path: string, value: unknown, ): void; ``` ## PROPERTY\_SETS\_EDITABLE\_PROP Reusable descriptor for host inspectors. ```typescript theme={null} const PROPERTY_SETS_EDITABLE_PROP: OPEditableProp; ``` ## Related * [`OPEditableProp`](/floorkit/api/architecture/op-element#opeditableprop) # GUIEditor Source: https://docs.ploterr.com/floorkit/api/editor/gui-editor GUIEditor is the 3D editing overlay. It draws handles above your Three.js view and lets the user select, move and reshape elements. ## Import ```typescript theme={null} import { GUIEditor } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import type * as THREE from 'three'; import { GUIEditor, type CameraControlsLike } from '@opengeometry/floorkit'; export function attachEditor( container: HTMLElement, camera: THREE.Camera, root: THREE.Object3D, controls: CameraControlsLike, ): GUIEditor { const editor = new GUIEditor(container, camera, controls); editor.enablePicking(root); editor.onChange = (change) => console.log(change.kind, change); return editor; } ``` ## Notes * The editor does not own your renderer or your camera. * Call `update()` in each frame. * To keep the edits in a building model, connect the editor with [`bindManagedSceneEditor`](/floorkit/api/editor/bind-managed-scene-editor). * `dispose()` removes the overlay and its listeners. ## Constructor ```typescript theme={null} constructor(container: HTMLElement, camera: THREE.Camera, controls?: CameraControlsLike); ``` ## Properties Read-only. Called after every edit reaches the target, for hosts that need to react (mark a document dirty, push an undo entry, re-run a schedule). Optional and unset by default; assign it directly — `editor.onChange = (change) => {…}`. Called with the live target, before anything is saved, so that a host can show live numbers (the floor area of a mass) while a grip is used. It fires on every live write of a profile, push/pull, radius or anchor grip, with the kind `profile`, `extrudeHeight`, `radius` or `anchor`: each pointer move of a drag, and also the typed push/pull value, an edge-midpoint insert and a vertex delete. It fires on each preview frame of the move and turn grips, with the kind `move` or `rotate`, but not on their release. It does not fire for the end and body drags of a saved wall or for the drags of a door or window, nor when a drag ends without a save (Escape, a release next to the press, or a refusal that the error callback reports): the target is put back without a call. The saved edit reports once through `onChange`. Optional; assign it directly: `editor.onDrag = (change) => {…}`. Read-only. Whether the next scene click will attempt a saved free-opening attachment. ## Methods ### addControls ```typescript theme={null} addControls(target: EditableTarget): void; ``` Attach editing handles to a polygon, rectangle, line, cuboid, element, cylinder, or any target exposing the generic `getEditAnchors()` contract. ### clearSelection ```typescript theme={null} clearSelection(): void; ``` ### armFreeOpeningRehost ```typescript theme={null} armFreeOpeningRehost(): boolean; ``` Wait for a deliberate wall mesh click to attach the selected saved free opening. ### setManagedEdits ```typescript theme={null} setManagedEdits(deps: ManagedSceneEditDeps): void; ``` Use saved building commands for scene grips and keyboard history. ### setTool ```typescript theme={null} setTool(tool: WallTool | undefined): void; ``` ### enablePicking ```typescript theme={null} enablePicking(root: THREE.Object3D, resolve?: (hit: THREE.Intersection) => EditableTarget | undefined): void; ``` ### update ```typescript theme={null} update(): void; ``` Public hook for hosts that prefer to drive the overlay from their own loop. ### dispose ```typescript theme={null} dispose(): void; ``` Stop the editor: cancel the loop, remove the overlay and listeners. ## Types ### EditableTarget Structural contract the editor needs from any target: a `THREE.Object3D` that exposes an editable `.profile` ring (the edit channel) and an `ogType` discriminator. ```typescript theme={null} interface EditableTarget extends THREE.Object3D { ogType: string; profile?: Profile; extrudeHeight?: number; readonly extrudeBase?: number; selectPart?(object: THREE.Object3D): boolean; activePart?: string; profileTopology?: ProfileTopology; radius?: number; getEditAnchors?(): EditAnchor[]; applyEditAnchor?(id: string, position: [number, number, number], snapRadius?: number): void; anchorSpace?(): 'local' | 'world'; beginEdit?(): void; endEdit?(): | { undo: () => void; redo: () => void; } | undefined; deleteFromNetwork?(): | { undo: () => void; redo: () => void; } | undefined; } ``` Editable outer ring + holes. Absent for circle targets (cylinder). Local Y of the plane that the profile and the push/pull grip sit on, when it is not the bottom of the object: an upper part of a stacked mass sits on the parts below it. A target made of parts: the id of the part that the grips edit, the one `selectPart` chose. An editor reads it when it builds its grips and sets it again when one of its gestures begins, so that two editors on one target each edit the part whose grips they drag. Present (with no `profile`) => a circle target: push/pull + radius handle only. ### EditorChange Emitted after every write-back, via `GUIEditor.onChange` or `createModelRenderer().onChange`. ```typescript theme={null} interface EditorChange { kind: EditorChangeKind; target: EditableTarget; anchorId?: string; ogid?: string; ogids?: string[]; } ``` The `EditAnchor` id, for `kind: 'anchor'` only. The edited element's id, where the surface knows it — the 2D plan view always does, because it resolved the element from a picked `ogid` in the first place. This is what lets a host write the edit back to its own store without searching for which element changed. `EditableTarget` is a bare `THREE.Object3D` and carries no id of its own. The elements whose built objects the step changed. `ogid` and `target` name the element of the last command of the step whose element changed. ### CameraControlsLike Anything with a toggleable `enabled` flag — both three.js `OrbitControls` and the `camera-controls` library used by Floorkit/`PlanCamera` satisfy this, so the editor stays renderer-agnostic. ```typescript theme={null} interface CameraControlsLike { enabled: boolean; } ``` ### ManagedSceneEditDeps The scene projects saved points; the project owner commits their edits. ```typescript theme={null} interface ManagedSceneEditDeps { modelObjects?: ManagedModelObjectEdits; isActive(): boolean; getElementById?(liveId: string): EditableTarget | undefined; wallPoints(liveId: string): | { points: readonly [number, number][]; elevation: number; } | undefined; hostedOpening?(liveId: string): | { position: readonly [number, number, number]; } | undefined; beginHostedOpening?(liveId: string): | { commit(position: [number, number]): boolean; } | undefined; freeOpening?(liveId: string): | { position: readonly [number, number]; elevation: number; } | undefined; beginFreeOpeningMove?(liveId: string): | { commit(position: [number, number]): boolean; } | undefined; beginFreeOpeningRehost?(liveId: string): ManagedOpeningRehostTransaction | undefined; beginWallEnd( liveId: string, end: 'start' | 'end', ): | { commit(position: [number, number]): string | undefined; } | undefined; beginNetworkWall?(liveId: string): | { commit(translation: [number, number]): boolean; } | undefined; beginReshape?(liveId: string): | { commit(): boolean; } | undefined; transformTarget?(liveId: string): SceneTransformTarget | undefined; beginTransform?( liveId: string, origin: [number, number], ): ManagedTransformTransaction | undefined; deleteWall?(liveId: string): EditorChange | undefined; committedChange(): EditorChange | undefined; undo(): EditorChange | undefined; redo(): EditorChange | undefined; subscribe(callback: () => void): () => void; reportError(error: unknown): void; } ``` ### ProfileTopology ```typescript theme={null} type ProfileTopology = 'open' | 'closed'; ``` ### EditorChangeKind Which channel of the target an edit wrote to. ```typescript theme={null} type EditorChangeKind = | 'profile' | 'extrudeHeight' | 'radius' | 'anchor' | 'move' | 'rotate' | 'create' | 'delete' | 'undo' | 'redo'; ``` ### ManagedModelObjectEdits Editor callbacks; the project owner resolves planes and commits saved transactions. The package does not export this type by name. ```typescript theme={null} interface ManagedModelObjectEdits { view(liveId: string): ManagedModelObjectView | undefined; pointOnPlane(liveId: string, origin: Vec3, direction: Vec3): [number, number] | undefined; begin(liveId: string, handleId: string): ModelObjectPreviewDraft | undefined; } ``` ### ManagedOpeningRehostTransaction The package does not export this type by name. ```typescript theme={null} interface ManagedOpeningRehostTransaction { commit(wallLiveId: string, position: [number, number]): boolean; } ``` ### SceneTransformTarget Saved XZ placement and copied outline on the owning elevation plane. The package does not export this type by name. ```typescript theme={null} interface SceneTransformTarget { pivot: TransformPosition; elevation: number; corners: readonly TransformPosition[]; liveIds?: readonly string[]; } ``` ### ManagedTransformTransaction One saved transform; previewing the overlay does not write an edit. The package does not export this type by name. ```typescript theme={null} interface ManagedTransformTransaction { commit(translation: [number, number], yawDeg: number): boolean; } ``` ### ManagedModelObjectView Copied saved geometry and the actual world frame used to project its handles. The package does not export this type by name. ```typescript theme={null} interface ManagedModelObjectView { presentation: ModelObjectPresentation; plane: BmWorkingPlane; } ``` ### ModelObjectPreviewDraft The package does not export this type by name. ```typescript theme={null} interface ModelObjectPreviewDraft { presentation(): ModelObjectPresentation; update(point: [number, number]): void; commit(): boolean; } ``` ### TransformPosition Pure drawing-plane geometry for the managed transform preview. The package does not export this type by name. ```typescript theme={null} type TransformPosition = readonly [number, number]; ``` ### ModelObjectPresentation The package does not export this type by name. ```typescript theme={null} interface ModelObjectPresentation { id: string; handles: { id: string; point: [number, number]; }[]; paths: { points: [number, number][]; closed?: boolean; }[]; } ``` ## Related * [`WallTool`](/floorkit/api/editor/wall-tool) * [`BmWorkingPlane`](/floorkit/api/architecture/validate-building-model#bmworkingplane) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`Event`](/floorkit/api/architecture/wall-network#event) * [`Profile`](/floorkit/api/primitives/line-primitive#profile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # ManagedPlanEditor Source: https://docs.ploterr.com/floorkit/api/editor/managed-plan-editor ManagedPlanEditor is the 2D plan editor. It shows one saved plan view and lets the user edit walls and openings. Each edit goes through a `BuildingSession`, so undo and redo work. ## Import ```typescript theme={null} import { ManagedPlanEditor } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { ManagedPlanEditor, type ProjectFile, type BuildingSession } from '@opengeometry/floorkit'; export async function mountPlan( project: ProjectFile, viewId: string, container: HTMLElement, session: BuildingSession, ): Promise { const plan = new ManagedPlanEditor(project, viewId, { session }); plan.onError = (error) => console.error('Plan edit rejected', error); try { await plan.mount(container); return plan; } catch (error) { plan.dispose(); throw error; } } ``` ## Notes * The view must be a saved plan in the project. * Give the same session to each plan editor and to the 3D editor. Then all of them show the same building. * `save()` returns the project text with the current building. * Set `onError` to show an edit that Floorkit rejected. * If you give a session, you own it. Dispose the editors first, then the session. If you give none, the editor makes one and disposes it. ## Constructor ```typescript theme={null} constructor(project: ProjectFile, viewId: string, options?: ManagedPlanEditorOptions); ``` Share one saved building session across several plans and a 3D view. Metres from every wall beyond which a dragged door or window leaves its wall; default 0.35. Undo and redo through another history, such as a project manager; default is the session. Refuse a command before a gesture of this plan saves it; throw to refuse. A project manager passes its storey-in-use rule, as it does to a bound 3D editor. Whether a saved view names the storey; Delete of a part of a mass keeps such a storey. ## Properties Read-only. Read-only. Read-only. The renderer of the plan. Assign `renderer.onDrag` for live figures during a grip drag (the same rules as `GUIEditor.onDrag`), and `renderer.onChange` for each saved edit. ## Methods ### mount ```typescript theme={null} mount(container: HTMLElement): Promise; ``` Mount the editable plan once; callers may mount session.root in their 3D scene. ### startWallDrawing ```typescript theme={null} startWallDrawing(wall: WallDrawingTemplate): void; ``` Collect a saved wall axis on this plan. ### startOutlineDrawing ```typescript theme={null} startOutlineDrawing(options?: OutlineDrawingOptions): Promise; ``` Let the user draw a closed outline on this plan and resolve with it, in site XZ metres (the plan's model coordinates), without saving anything; `undefined` when the user presses Escape or the draft ends otherwise (another gesture, a change of the model, `dispose`). ### startMassDrawing ```typescript theme={null} startMassDrawing(options: MassDrawingOptions): Promise; ``` Let the user draw the outline of a new building with one mass, then save it as one step (`add-building` from `massBuildingCommand`; `renderer.onChange` gets a `create`) and resolve with the id of the new mass. ### projectSnapshot ```typescript theme={null} projectSnapshot(): ProjectFile; ``` ### save ```typescript theme={null} save(): string; ``` ### setProject ```typescript theme={null} setProject(project: ProjectFile): void; ``` Replace the saved views, sheets and markers of this plan; a mounted plan draws again. ### undo ```typescript theme={null} undo(): number | null; ``` Undo one step of `history`, or of the session; renderer.onChange gets a changed building. ### redo ```typescript theme={null} redo(): number | null; ``` Redo one step of `history`, or of the session; renderer.onChange gets a changed building. ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### ManagedPlanEditorOptions ```typescript theme={null} interface ManagedPlanEditorOptions { session?: BuildingSession; build?: Pick; hostedOpeningDetachTolerance?: number; history?: ManagedEditHistory; checkCommand?(command: BuildingEditCommand): void; storeyInUse?(storeyId: string): boolean; } ``` ### WallDrawingTemplate The package does not export this type by name. ```typescript theme={null} type WallDrawingTemplate = Omit; ``` ### OutlineDrawingOptions How `ManagedPlanEditor.startOutlineDrawing` collects the outline. ```typescript theme={null} interface OutlineDrawingOptions { mode?: 'free' | 'rectangle'; } ``` `free` (the default): each click adds a corner; a click on the first corner, Enter or a double click closes the outline once it has three corners. `rectangle`: two clicks at opposite corners. ### MassDrawingOptions The new building of `ManagedPlanEditor.startMassDrawing`, and how its outline is drawn. ```typescript theme={null} interface MassDrawingOptions extends MassBuildingOptions, OutlineDrawingOptions {} ``` ### ManagedEditHistory The package does not export this type by name. ```typescript theme={null} interface ManagedEditHistory { undo(): number | null; redo(): number | null; } ``` ## Related * [`BuildingSession`](/floorkit/api/editor/building-session) * [`BuildingEditCommand`](/floorkit/api/editor/building-session#buildingeditcommand) * [`BuildOptions`](/floorkit/api/architecture/build-from-building-model#buildoptions) * [`MassBuildingOptions`](/floorkit/api/architecture/massing-helpers#massbuildingoptions) * [`PointXZ`](/floorkit/api/primitives/polygon-maths#pointxz) * [`ProjectFile`](/floorkit/api/exports/project-file#projectfile) * [`WallCreateCommand`](/floorkit/api/editor/building-session#wallcreatecommand) # ProjectManager Source: https://docs.ploterr.com/floorkit/api/editor/project-manager ProjectManager holds one open project. It keeps the views, the sheets and the markers of the project, and it owns the one `BuildingSession` that builds the building. It mounts the plan editors, connects the 3D editor, saves the project and exports the sheets. Each change is one step in one history, so one undo works for the building and for the sheets. ## Import ```typescript theme={null} import { ProjectManager } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import type * as THREE from 'three'; import { GUIEditor, ProjectManager, type CameraControlsLike, type ManagedPlanEditor, type Sheet, } from '@opengeometry/floorkit'; export async function showProject( text: string, scene: THREE.Scene, planContainer: HTMLElement, sceneContainer: HTMLElement, camera: THREE.Camera, controls: CameraControlsLike, ): Promise<{ manager: ProjectManager; plan: ManagedPlanEditor; sceneEditor: GUIEditor }> { const manager = ProjectManager.open(text); scene.add(manager.root); const plan = await manager.mountPlanEditor(planContainer, 'ground-plan'); const sceneEditor = new GUIEditor(sceneContainer, camera, controls); manager.bindSceneEditor(sceneEditor); return { manager, plan, sceneEditor }; } export async function addSheetAndExport( manager: ProjectManager, newSheet: Sheet, ): Promise<{ saved: string; pdf: Blob }> { manager.apply({ kind: 'add-project-item', list: 'sheets', item: newSheet }); manager.undo(); manager.redo(); const saved = manager.save(); const pdf = await manager.exportPdf(); return { saved, pdf }; } export function closeProject( scene: THREE.Scene, manager: ProjectManager, sceneEditor: GUIEditor, ): void { sceneEditor.dispose(); scene.remove(manager.root); manager.dispose(); } ``` ## Notes ### Project commands `apply` takes an edit command of the building, or one of these commands of the project lists. Each item has an `id`. An edit command of the building can be a `batch`. A batch cannot hold a command of the project lists. | Command | Result | | - | - | | `add-project-item` | Adds one item to `views`, `sheets` or `markers`. The `id` must be new in that list. | | `replace-project-item` | Replaces the item that has the given `id`. The new item must have the same `id`. | | `remove-project-item` | Removes the item that has the given `id`. | | `replace-paper-settings` | Replaces the settings of the paper. The sheets and the paper version stay. | ```typescript theme={null} import type { ProjectCommand } from '@opengeometry/floorkit'; export function renameSheet(manager: ProjectManager, sheet: Sheet, name: string): number { const command: ProjectCommand = { kind: 'replace-project-item', list: 'sheets', id: sheet.id, item: { ...sheet, name }, }; return manager.apply(command); } ``` ### Checks of a command * Floorkit checks a project command as `save()` checks the project. A command that fails throws `ProjectFileError` and changes nothing. * The value `-0`, the value `undefined` and an object that is not plain give the code `invalid-json-value`. * The other codes of a project command are `duplicate-item`, `unknown-item`, `item-id-mismatch` and `invalid-command`. * You cannot remove a view that a mounted plan editor shows, and you cannot make it a view that is not a plan. The code is `view-in-use`. * If a plan editor shows a view that you added in this session, an `undo` that reaches the "add view" step fails with `view-in-use`. Dispose that plan editor first. * Through `manager.apply` you cannot remove a storey while a plan view names it. The manager throws `ProjectFileError` with the code `storey-in-use` and the path of the view. Remove the view first. A `remove-storey` in a `batch` has the same check, also when the batch adds the storey again. * Through `manager.apply` you cannot remove a building while a saved view shows it: a plan view of one of its storeys, a section or elevation view with its `buildingId`, or a view whose `elementIds` lists anything the building holds. The code is `building-in-use`, with the path of the view. A bound 3D editor has the same check. * Through `manager.apply` you cannot remove the site, or give it a new id, while the `elementIds` of a view lists it. The code is `site-in-use`. * An undo or a redo of a step of the project lists is checked as `save()` checks the project. A step that fails throws `ProjectFileError` and changes nothing. ### History * `apply`, `undo` and `redo` return the new revision. `undo` and `redo` return `null` when there is no step. * A drag in an editor is a building step in the same history. The undo key in each editor undoes the last step of the manager. * `subscribe` reports each step with its `revision`, its `part` (`building` or `project`) and its `cause` (`apply`, `undo`, `redo` or `restore`). A `restore` is no new step and the revision stays as it was: a step of the building failed after it changed built elements, and the session built them again from the saved model. * Do not call `apply`, `undo` or `redo` in a listener. The manager throws an error with the code `reentrant-transition`. `dispose()` in a listener during a building step throws the same code. * After a project step, the manager draws each mounted plan again. Thus one project step costs one plan draw for each mounted plan editor. * With a `ProjectManager`, call `manager.apply`, `manager.undo` and `manager.redo`. A direct `manager.session.apply` is counted as a step. A direct `manager.session.undo` or `redo`, and a storey command sent to the session while a view names that storey, can leave a step that the manager cannot undo, or a project that `save()` refuses. While a view names a missing storey, the undo of each project step is refused. To repair it, undo the building step, or remove the view with `manager.apply`. ### Files and errors * `ProjectManager.open` throws `ProjectFileError` for a bad file. A file that is valid, but whose building does not compile, throws `BmBuildError` or `Error`. * A project file from an earlier version opens, and the project commands work on it. `save()` writes the current version. * Add `root` to your scene one time. It stays the same object after each edit. * You make the `GUIEditor` and you dispose it. `dispose()` of the manager removes the plan editors and the session. * After `dispose()`, `exportPdf` gives a promise that rejects. * A deployed site needs a valid hostname key for exports, including a non-commercial demo. See [Licensing](/floorkit/licensing). ## Constructor ```typescript theme={null} constructor(project: ProjectFile, options?: ProjectManagerOptions); ``` Build options of the one building session, for example the sources of external assets. Metres from every wall beyond which a dragged door or window leaves its wall; default 0.35. Called when an editor rejects an edit, or cannot draw a step of the project lists. ## Properties Read-only. The one building session of this project. Read-only. The built building. Add it to the Three.js scene one time. Read-only. Goes up by one for each applied, undone or redone step. ## Methods ### open ```typescript theme={null} static open(text: string, options?: ProjectManagerOptions): ProjectManager; ``` Read project text, check it and build the building. ### snapshot ```typescript theme={null} snapshot(): ProjectFile; ``` The current views, sheets and markers, with the current building. ### save ```typescript theme={null} save(): string; ``` The current project as text. ### apply ```typescript theme={null} apply(command: ProjectCommand): number; ``` Apply one command. ### undo ```typescript theme={null} undo(): number | null; ``` Undo the last step. ### redo ```typescript theme={null} redo(): number | null; ``` Redo the last undone step. ### subscribe ```typescript theme={null} subscribe(listener: (change: ProjectChange) => void): () => void; ``` Call `listener` after each completed step. ### mountPlanEditor ```typescript theme={null} mountPlanEditor(container: HTMLElement, viewId: string): Promise; ``` Mount a 2D plan editor for one saved plan view. ### bindSceneEditor ```typescript theme={null} bindSceneEditor(editor: GUIEditor): void; ``` Connect a 3D editor to this project; its edits pass the same storey checks as `apply`, and Delete of a part of a mass keeps a storey that a saved plan view shows. ### setBuildableEnvelope ```typescript theme={null} setBuildableEnvelope(shown: boolean): void; ``` Show or hide the buildable envelope of the site in the 3D scene (`root`). ### exportPdf ```typescript theme={null} exportPdf(sheetIds?: string[], options?: ProjectExportOptions): Promise; ``` Export the given sheets, or each sheet, of the current project as one PDF. ### dispose ```typescript theme={null} dispose(): void; ``` Remove the plan editors of this manager, then the session. ## Types ### ProjectManagerOptions ```typescript theme={null} interface ProjectManagerOptions { build?: Pick; hostedOpeningDetachTolerance?: number; onError?: (error: unknown) => void; } ``` ### ProjectCommand One command of a project manager: an edit of the building or a change of the project lists. ```typescript theme={null} type ProjectCommand = BuildingEditCommand | ProjectListCommand; ``` ### ProjectListCommand One change of the list of views, sheets or markers. ```typescript theme={null} type ProjectListCommand = | { kind: 'add-project-item'; list: 'views'; item: SavedDrawingView; } | { kind: 'add-project-item'; list: 'sheets'; item: Sheet; } | { kind: 'add-project-item'; list: 'markers'; item: ProjectViewMarker; } | { kind: 'replace-project-item'; list: 'views'; id: string; item: SavedDrawingView; } | { kind: 'replace-project-item'; list: 'sheets'; id: string; item: Sheet; } | { kind: 'replace-project-item'; list: 'markers'; id: string; item: ProjectViewMarker; } | { kind: 'remove-project-item'; list: 'views' | 'sheets' | 'markers'; id: string; } | { kind: 'replace-paper-settings'; settings: Omit; }; ``` ### ProjectChange One completed step of a project manager, as a subscriber gets it. ```typescript theme={null} interface ProjectChange { revision: number; part: 'building' | 'project'; cause: 'apply' | 'undo' | 'redo' | 'restore'; changed?: readonly string[]; } ``` The revision of the manager after the step. A `restore` leaves the revision as it was. `building` is an edit of the session; `project` is a change of the project lists. What made the step. `restore` is no saved step: a step of the building failed after it changed built objects, and the session built them again from the saved model. The live ids of the built elements that the step built again, added or removed, where a removed id is in the list and not in the build; empty for a step of the project lists, and absent when the whole building was built again. An element that was built again is a new object, or the same object with new geometry. ## Related * [`BuildingSession`](/floorkit/api/editor/building-session) * [`GUIEditor`](/floorkit/api/editor/gui-editor) * [`ManagedPlanEditor`](/floorkit/api/editor/managed-plan-editor) * [`BuildingEditCommand`](/floorkit/api/editor/building-session#buildingeditcommand) * [`BuildOptions`](/floorkit/api/architecture/build-from-building-model#buildoptions) * [`PaperDocument`](/floorkit/api/drawings/sheet-documents#paperdocument) * [`ProjectExportOptions`](/floorkit/api/exports/export-project-pdf#projectexportoptions) * [`ProjectFile`](/floorkit/api/exports/project-file#projectfile) * [`ProjectViewMarker`](/floorkit/api/exports/project-file#projectviewmarker) * [`SavedDrawingView`](/floorkit/api/drawings/sheet-documents#saveddrawingview) * [`Sheet`](/floorkit/api/drawings/sheet-documents#sheet) # Pure edits Source: https://docs.ploterr.com/floorkit/api/editor/pure-edits Apply one edit command to a model without a session. A pure edit applies one edit command to a building model and returns a new model. It needs no session, and it does not change the model that you give. Use it on a server, in a test, or when you do not need geometry. ## Import ```typescript theme={null} import { applyBuildingEdit, applyBuildingEditResolved, massSegmentHeightCommand, BmEditError, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { applyBuildingEdit, BmEditError, type BmColumn, type BuildingModel, } from '@opengeometry/floorkit'; export function withColumn(model: BuildingModel, column: BmColumn): BuildingModel | undefined { try { return applyBuildingEdit(model, { kind: 'add-element', buildingId: 'building', storeyId: 'ground', list: 'columns', element: column, }); } catch (error) { if (error instanceof BmEditError) { console.error(error.code, error.message); return undefined; } throw error; } } ``` ## Notes * Both functions take a [`BuildingEditCommand`](/floorkit/api/editor/building-session#buildingeditcommand). `applyBuildingEditResolved` takes each `kind`. `applyBuildingEdit` rejects some wall and opening commands, such as `create-wall` and `add-hosted-opening`, with the code `resolved-edit-required`. * `add-element`, `replace-element` and `remove-element` change one entry of a list of a storey: `slabs`, `roofs`, `spaces`, `stairs`, `columns`, `beams`, `railings` or `instances`. `add-element` rejects an id that the list has. `replace-element` and `remove-element` reject an id that the list does not have. The new element of `replace-element` must have the same id. * `add-asset`, `replace-asset` and `remove-asset` change one asset of the model. `add-asset` rejects an id that exists. `replace-asset` and `remove-asset` reject an id that does not exist. `remove-asset` rejects an asset that an instance uses. * `add-storey`, `remove-storey` and `replace-storey-settings` change the storeys of one building. `add-storey` rejects a storey id that the building has. `remove-storey` rejects the last storey of a building. `replace-storey-settings` replaces the `name`, the `elevation`, the `height`, the `usage` and the `areaKind` together. The `areaKind` is what the plate of the storey counts as (`gross`, `balcony`, `core`, `basement`, `parking` or `mechanical`); a value that is not one of these is refused as `invalid-command`. * `replace-mass` sets the mass of one building and the settings of all its storeys. A storey id that stays keeps its elements, a new id adds an empty storey, and a storey that is left out is removed. It rejects the removal of a storey that holds an element. `massSegmentHeightCommand(model, buildingId, segmentId, height)` gives this command for a new height of one segment. * `add-building` adds a whole building, last in the list: its storeys, their elements and its optional `mass`. Each id in it must be new in the whole model. The smallest valid building has one storey, and for a massing study a `mass` with one segment whose `storeyIds` names that storey and whose `height` is the height of the storey. `remove-building` removes a building and all it holds; it rejects the last building of the model. * `replace-site` replaces the site whole: `boundary`, `lotLines`, `elevation`, `geoAnchor`, `trueNorthDeg` and the `zoning` rules. Without `site`, the model has no site any more. The model check refuses a self-intersecting boundary and a `lotLines` list that does not have one class per boundary edge. Run `evaluateZoning` again after the step; the command does not run it. * A `batch` applies its `commands` in order and returns one model. If one command fails, the error has the code of that command, and its message starts with the place of the command, such as `command 2 of 3:`. A batch cannot be empty and cannot hold a batch. * A rejected command throws `BmEditError`. The model that you gave is not changed. * The functions check the full result. For an edit in an app, use a [`BuildingSession`](/floorkit/api/editor/building-session). It also builds the geometry and keeps undo and redo. ## applyBuildingEdit Apply one saved edit of a building model, or one batch of edits, and return a validated new model. ```typescript theme={null} function applyBuildingEdit(model: BuildingModel, command: BuildingEditCommand): BuildingModel; ``` **Throws:** \{BmEditError} If the command, target, or resulting geometry is invalid. ## applyBuildingEditResolved Apply an immutable edit with exact junction-aware hosted-opening decisions. ```typescript theme={null} function applyBuildingEditResolved( model: BuildingModel, command: BuildingEditCommand, ): BuildingModel; ``` ## massSegmentHeightCommand The `replace-mass` command that gives one segment of a saved mass the height `height`, snapped to whole storeys at the pitch of its top storey; `undefined` when the snapped height is the saved one. ```typescript theme={null} function massSegmentHeightCommand( model: Readonly, buildingId: string, segmentId: string, height: number, ): ReplaceMassCommand | undefined; ``` ## BmEditError A rejected edit; `issues` carries model-validation details when available. ```typescript theme={null} class BmEditError extends Error { readonly code: BmEditErrorCode; readonly issues?: BmIssue[]; constructor(code: BmEditErrorCode, message: string, issues?: BmIssue[]); } ``` ## Types ### BmEditErrorCode Stable category for a rejected building edit. ```typescript theme={null} type BmEditErrorCode = | 'invalid-command' | 'invalid-target' | 'hosted-openings-unsupported' | 'join-overrides-unsupported' | 'resolved-edit-required' | 'kernel-unavailable' | 'invalid-geometry'; ``` ### ReplaceMassCommand Replace the mass of a building and the settings of its whole storey list in one step. ```typescript theme={null} interface ReplaceMassCommand { kind: 'replace-mass'; buildingId: string; mass?: BmMass; storeys: MassStoreySettings[]; } ``` ### MassStoreySettings The settings of one storey in a `replace-mass` command; its elements are not part of it. ```typescript theme={null} interface MassStoreySettings { id: string; name?: string; elevation: number; height: number; usage?: string; areaKind?: AreaKind; } ``` World Y of the floor of the storey, in metres. Floor-to-floor height in metres. What the plate of the storey counts as; see `BmStorey.areaKind`. ### BuildingCommand Add or remove a whole building of a model. ```typescript theme={null} type BuildingCommand = | { kind: 'add-building'; building: BmBuilding; } | { kind: 'remove-building'; buildingId: string; }; ``` ### ReplaceSiteCommand Replace the site of a model whole: boundary, lot lines, grade, geo anchor, true north and zoning rules. ```typescript theme={null} interface ReplaceSiteCommand { kind: 'replace-site'; site?: BmSite; } ``` ## Related * [`AreaKind`](/floorkit/api/architecture/massing-helpers#areakind) * [`BmBuilding`](/floorkit/api/architecture/validate-building-model#bmbuilding) * [`BmIssue`](/floorkit/api/architecture/validate-building-model#bmissue) * [`BmMass`](/floorkit/api/architecture/validate-building-model#bmmass) * [`BmSite`](/floorkit/api/architecture/validate-building-model#bmsite) * [`BuildingEditCommand`](/floorkit/api/editor/building-session#buildingeditcommand) * [`BuildingModel`](/floorkit/api/architecture/validate-building-model#buildingmodel) # resolveViewEditCandidate Source: https://docs.ploterr.com/floorkit/api/editor/resolve-view-edit-candidate Change a drag on an opening in a drawing into an edit command. ## Import ```typescript theme={null} import { resolveViewEditCandidate, ViewEditCandidateError } from '@opengeometry/floorkit'; ``` ## Signature ```typescript theme={null} function resolveViewEditCandidate( model: BuildingModel, frame: ViewFrame, sourceRef: DrawingSourceRef | undefined, drag: ViewDrag, constraint: { kind: 'along-host-wall'; }, built: BuildResult, ): BuildingEditCommand; ``` ## Parameters ## Returns ```typescript theme={null} BuildingEditCommand; ``` ## ViewEditCandidateError ```typescript theme={null} class ViewEditCandidateError extends Error { readonly code: ViewEditCandidateErrorCode; constructor(code: ViewEditCandidateErrorCode, message: string); } ``` ## Types ### ViewDrag Drawing coordinates are right/down metres, independent of screen zoom. ```typescript theme={null} interface ViewDrag { x: number; y: number; } ``` ### ViewEditCandidateErrorCode ```typescript theme={null} type ViewEditCandidateErrorCode = | 'invalid-constraint' | 'invalid-source' | 'inspect-only' | 'stale-source' | 'unhosted-opening' | 'invalid-drag' | 'invalid-build' | 'invalid-view-frame' | 'foreshortened-axis' | 'off-axis-drag' | 'ambiguous-wall-edge'; ``` ## Related * [`BuildingEditCommand`](/floorkit/api/editor/building-session#buildingeditcommand) * [`BuildingModel`](/floorkit/api/architecture/validate-building-model#buildingmodel) * [`BuildResult`](/floorkit/api/architecture/build-from-building-model#buildresult) * [`DrawingSourceRef`](/floorkit/api/drawings/draw-project-view#drawingsourceref) * [`ViewFrame`](/floorkit/api/drawings/drawing-helpers#viewframe) # SnapEngine Source: https://docs.ploterr.com/floorkit/api/editor/snap-engine SnapEngine finds the nearest snap point or grid point. It does the maths only. Your tool converts the pointer position and draws the indicator. ## Import ```typescript theme={null} import { SnapEngine, drawingSnapTargets } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { SnapEngine } from '@opengeometry/floorkit'; const snapper = new SnapEngine(5, 3); const targets = [ { x: 20, y: 15 }, { x: 40, y: 15 }, ]; const drafting = snapper.snap({ x: 21, y: 16 }, targets); const dimensionEndpoint = snapper.snapToTargets({ x: 21, y: 16 }, targets); snapper.setGridSpacing(10); snapper.setSnapRadius(2); console.log(drafting, dimensionEndpoint); ``` ## Notes * Give the points, the grid spacing and the radius in the same unit. Paper tools use millimetres. * `snap()` tries the nearest target first, then the grid. If it finds none, it returns your point with `snapped: false`. * `snapToTargets()` does not use the grid. * Convert screen pixels to your unit before the call. ## Constructor ```typescript theme={null} constructor(gridSpacing?: number, snapRadius?: number); ``` ## Methods ### snap ```typescript theme={null} snap(point: Point2, targets?: Point2[]): SnapResult; ``` Snap a point. ### snapToTargets ```typescript theme={null} snapToTargets(point: Point2, targets: Point2[]): SnapResult; ``` Endpoint-only snap (NO grid): the nearest target within `snapRadius`, else the point unchanged. ### setGridSpacing ```typescript theme={null} setGridSpacing(spacing: number): void; ``` ### setSnapRadius ```typescript theme={null} setSnapRadius(radius: number): void; ``` Set the snap radius (paper mm). ## drawingSnapTargets Snap candidates from a drawing's primitives: line ends, arc centres, region corners, and optionally segment midpoints. ```typescript theme={null} function drawingSnapTargets(drawing: VectorDrawing, options?: SnapTargetOptions): Point2[]; ``` ## Types ### SnapResult ```typescript theme={null} interface SnapResult { point: Point2; snapped: boolean; snapType?: 'grid' | 'endpoint' | 'midpoint'; } ``` ### SnapTargetOptions The points in a drawing that a snap can lock onto — the geometry a dimension or a dragged vertex should land exactly on. ```typescript theme={null} interface SnapTargetOptions { midpoints?: boolean; excludeOgid?: string; } ``` Include the midpoint of every segment as well as its ends. Skip primitives belonging to this element — a vertex must not snap to the very geometry it is dragging. ## Related * [`Point2`](/floorkit/api/annotations/dimension-geometry#point2) * [`VectorDrawing`](/floorkit/api/drawings/draw-project-view#vectordrawing) # VectorNetwork Source: https://docs.ploterr.com/floorkit/api/editor/vector-network VectorNetwork is an editable path or closed profile. Each node has a position and optional curve handles. ## Import ```typescript theme={null} import { VectorNetwork } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { VectorNetwork } from '@opengeometry/floorkit'; const profile = new VectorNetwork( [ [0, 0], [4, 0], [4, 3], [0, 3], ], true, ); profile.bendSegmentTo(0, 2, -0.4); const inserted = profile.insertNode(1, 4, 1.5); profile.moveNode(inserted, 4.2, 1.5); console.log(profile.toRing()); ``` ## Notes * Nodes use `[x, z]` profile coordinates. * `toRing()` changes curved edges into points. * A closed profile needs three nodes or more. * A network is one path. It has no branches, and it does not split where it crosses itself. ## Constructor ```typescript theme={null} constructor(points: Array>, closed: boolean); ``` ## Properties Read-only. Number of edges: a closed loop has one per node; an open path has one fewer. ## Methods ### segmentEndpoints ```typescript theme={null} segmentEndpoints(i: number): [ Point, Point ]; ``` Endpoints of edge `i` as profile points. ### segmentMidpoint ```typescript theme={null} segmentMidpoint(i: number): Point; ``` Midpoint of edge `i` (on the curve if curved), in profile coords. ### moveNode ```typescript theme={null} moveNode(i: number, x: number, z: number): void; ``` ### endIndex ```typescript theme={null} endIndex(i: number): number; ``` The index of the node the edge leaving `i` ends at. ### bendSegmentTo ```typescript theme={null} bendSegmentTo(i: number, px: number, pz: number): void; ``` Bend edge `i` so its curve passes through `(px, pz)` at the midpoint. ### insertNode ```typescript theme={null} insertNode(i: number, px: number, pz: number): number; ``` Insert a vertex on edge `i`. ### deleteNode ```typescript theme={null} deleteNode(i: number): boolean; ``` Delete node `i` and heal the gap: the previous node's outgoing edge now spans to the next node. ### toRing ```typescript theme={null} toRing(): Array>; ``` Compile to a tessellated outer ring of `[x, z]` points for the kernel. ## Types ### VNNode ```typescript theme={null} interface VNNode { x: number; z: number; curved: boolean; c1: Point; c2: Point; } ``` Whether the edge leaving this node (to the next node) is a cubic Bézier. Control point near this node (absolute profile coords). Valid when `curved`. Control point near the next node (absolute profile coords). Valid when `curved`. ### Point Least-squares cubic Bézier fitting — the "delete and heal" half of the profile editor (Schneider, *An Algorithm for Automatically Fitting Digitized Curves*, Graphics Gems, 1990). The package does not export this type by name. ```typescript theme={null} type Point = [number, number]; ``` # WallTool Source: https://docs.ploterr.com/floorkit/api/editor/wall-tool WallTool draws walls point by point into a WallNetwork. Use it for a click-to-draw tool on live walls. ## Import ```typescript theme={null} import { WallTool } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { WallTool, type WallNetwork, type WallOptions } from '@opengeometry/floorkit'; export function drawAxis(network: WallNetwork, options: Omit): void { const tool = new WallTool(network, options); const y = tool.baseElevation; tool.add([0, y, 0]); tool.add([4, y, 0]); tool.move([4, y, 3]); tool.add([4, y, 3]); const result = tool.finish(); if (result) { result.edit.undo(); result.edit.redo(); } } ``` ## Notes * Points are in metres and must be on the base plane of the tool. * `add` sets a point. `move` updates the preview. `finish` makes the walls. `cancel` drops the draft. * `finish()` returns `undefined` when there are too few points. * For walls that must go into a building session, use [`beginManagedWallCreate`](/floorkit/api/editor/begin-managed-wall-create). ## Constructor ```typescript theme={null} constructor(network: WallNetwork, options: Omit, onCommit?: ((result: WallToolCommit) => void) | undefined, mode?: 'line' | 'arc'); ``` ## Properties Read-only. Read-only. Read-only. Read-only. Read-only. Read-only. Read-only. Read-only. Read-only. ## Methods ### move ```typescript theme={null} move(point: Vec3): void; ``` ### add ```typescript theme={null} add(point: Vec3, closeTolerance?: number): WallToolCommit | undefined; ``` ### finish ```typescript theme={null} finish(close?: boolean): WallToolCommit | undefined; ``` ### removeLast ```typescript theme={null} removeLast(): void; ``` ### cancel ```typescript theme={null} cancel(): void; ``` ## Types ### WallToolCommit ```typescript theme={null} interface WallToolCommit { walls: Wall[]; edit: { undo: () => void; redo: () => void; }; } ``` ## Related * [`Wall`](/floorkit/api/architecture/wall) * [`WallNetwork`](/floorkit/api/architecture/wall-network) * [`Vec3`](/floorkit/api/architecture/wall#vec3) * [`WallFootprint`](/floorkit/api/architecture/wall#wallfootprint) * [`WallOptions`](/floorkit/api/architecture/wall#walloptions) * [`XZ`](/floorkit/api/architecture/wall-network#xz) # DXF Source: https://docs.ploterr.com/floorkit/api/exports/dxf DXF export for drawings comes in the next release. DXF export is not released. It comes in the next release. DXF is the exchange format for 2D CAD drawings. With DXF export, you can open a Floorkit plan, section or elevation in a CAD tool as editable linework. ## What you can do today * Export sheets as [PDF](/floorkit/api/exports/export-project-pdf). The PDF contains vector linework. * Read the linework of a view with [`drawProjectView`](/floorkit/api/drawings/draw-project-view). It returns lines, arcs, regions and text in metres. ## Tell us what you need Tell us which CAD tool and which layer standard you use. Your answer helps us to set the order of the work. Ask for early access to DXF export. # exportProjectPdf Source: https://docs.ploterr.com/floorkit/api/exports/export-project-pdf exportProjectPdf makes one PDF from the sheets of a project. Each sheet becomes one page with vector linework. ## Import ```typescript theme={null} import { exportProjectPdf, sheetsToPdf } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { exportProjectPdf, type ProjectFile } from '@opengeometry/floorkit'; export async function downloadSheets(project: ProjectFile, sheetIds?: string[]): Promise { const blob = await exportProjectPdf(project, sheetIds); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'project-sheets.pdf'; link.click(); window.setTimeout(() => URL.revokeObjectURL(url), 1000); } ``` ## Notes * Without `sheetIds`, Floorkit exports all sheets. * The result is a `Blob`. Your app downloads it or uploads it. * If a sheet shows raster views, prepare the images first and pass them in `rasterViews`. * If Floorkit cannot draw a view, the promise rejects. You do not get a partial PDF. * A deployed site needs a valid hostname key, including a non-commercial demo. See [Licensing](/floorkit/licensing). ## Signature ```typescript theme={null} function exportProjectPdf( project: ProjectFile, sheetIds?: string[], options?: ProjectExportOptions, ): Promise; ``` ## Parameters Default: `{}`. ## Returns ```typescript theme={null} Promise; ``` ## sheetsToPdf Every sheet as one multi-page PDF, in document order. ```typescript theme={null} function sheetsToPdf(sheets: Sheet[], deps: PaperRenderDeps): Blob; ``` ## Types ### ProjectExportOptions ```typescript theme={null} interface ProjectExportOptions extends DrawProjectViewOptions { rasterViews?: RasterViewBatch; style?: ExportOptions['style']; projectName?: string; date?: string; templates?: PaperRenderDeps['templates']; paperStyles?: PaperRenderDeps['paperStyles']; } ``` Prepared committed raster content for named isometric, section and elevation views. Per-call paper style override. Title-block auto fields; the project model's name or id supplies the default name. Custom templates and style presets for this export. ### ExportOptions Per-call options for the exporter's methods. ```typescript theme={null} interface ExportOptions { style?: PaperStyleInput; } ``` Draw this export in a paper style — a builtin id (`"ink-sketch"`), a document-registered id, or an inline overlay — for this call only, without touching the document. Outranks the document's, the sheet's and the viewport's own style; absent means "as the document says". ## Related * [`DrawProjectViewOptions`](/floorkit/api/drawings/draw-project-view#drawprojectviewoptions) * [`PaperRenderDeps`](/floorkit/api/drawings/paper-styles#paperrenderdeps) * [`PaperStyleInput`](/floorkit/api/drawings/paper-styles#paperstyleinput) * [`ProjectFile`](/floorkit/api/exports/project-file#projectfile) * [`RasterViewBatch`](/floorkit/api/views/prepare-project-raster-views#rasterviewbatch) * [`Sheet`](/floorkit/api/drawings/sheet-documents#sheet) # GLB Source: https://docs.ploterr.com/floorkit/api/exports/glb Export the 3D model as a GLB file with the Three.js exporter. A GLB file holds the 3D model in one binary file. Game engines, 3D viewers and AR tools open it. Floorkit builds your model as standard Three.js objects. Thus you can export it with `GLTFExporter`, which is a part of Three.js. You do not need a different package. ## Export a session `session.root` holds the full compiled building. Give it to the exporter and ask for binary output. ```typescript theme={null} import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js'; import type { BuildingSession } from '@opengeometry/floorkit'; export async function exportGlb(session: BuildingSession): Promise { session.root.updateMatrixWorld(true); const result = await new GLTFExporter().parseAsync(session.root, { binary: true, onlyVisible: true, }); if (!(result instanceof ArrayBuffer)) throw new Error('The exporter did not return a GLB.'); return new Blob([result], { type: 'model/gltf-binary' }); } ``` ## Download the file ```typescript theme={null} export async function downloadGlb(session: BuildingSession, fileName: string): Promise { const url = URL.createObjectURL(await exportGlb(session)); const link = document.createElement('a'); link.href = url; link.download = fileName; link.click(); URL.revokeObjectURL(url); } ``` ## Export other objects The exporter accepts any Three.js object. To export one storey, pass its group from `session.currentBuild.storeyGroups`. To export elements that you made with classes such as `Wall` or `Slab`, add them to a `THREE.Group` and pass that group. ## Notes * Units are metres and the Y axis points up. These are also the glTF conventions, so no conversion is necessary. * The file contains geometry, colours and object names. It does not contain the building document. Save a [project file](/floorkit/api/exports/project-file) to keep the model editable. * Call the export after the last edit. The exporter reads the model as it is at that moment. * A deployed site needs a valid hostname key, including a non-commercial demo. See [Licensing](/floorkit/licensing). # IFC Source: https://docs.ploterr.com/floorkit/api/exports/ifc IFC export for building models comes in the next release. IFC export is not released. It comes in the next release. IFC is the open standard for building information models. With IFC export, you can send a Floorkit building to BIM tools with its storeys, elements and property sets. ## What you can do today * Export sheets as [PDF](/floorkit/api/exports/export-project-pdf). * Export the 3D model as [GLB](/floorkit/api/exports/glb). * Save the building as a [project file](/floorkit/api/exports/project-file). The file is typed JSON, so your server can read it. ## Tell us what you need Tell us which IFC version and which BIM tool you use. Your answer helps us to set the order of the work. Ask for early access to IFC export. # Project file Source: https://docs.ploterr.com/floorkit/api/exports/project-file Save a project as JSON text and open it again. A project file is JSON text. It holds the building, the saved views and the sheets. `saveProject` writes the text and `openProject` reads it. ## Import ```typescript theme={null} import { saveProject, openProject, ProjectFileError } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { openProject, saveProject, ProjectFileError, type BuildingSession, type ProjectFile, } from '@opengeometry/floorkit'; export function saveCurrent(project: ProjectFile, session: BuildingSession): string { return saveProject({ ...project, buildingModel: session.snapshot() }); } export function readProject(text: string): ProjectFile | undefined { try { return openProject(text); } catch (error) { if (error instanceof ProjectFileError) { console.error(error.code, error.path, error.message); return undefined; } throw error; } } ``` ## Notes * These functions do not use the disk or the network. Your app stores the text. * Put `session.snapshot()` into `buildingModel` before you save. * `openProject` checks the text. A problem throws `ProjectFileError` with a `code` and a `path`. * The file does not hold geometry, images or undo history. Floorkit builds them again after you open the file. * Files from earlier versions open. ## saveProject Validate and serialize one project, upgrading older BuildingModels without changing the input. ```typescript theme={null} function saveProject(project: ProjectFile): string; ``` ## openProject Parse and validate project JSON without reading from disk or compiling the building. ```typescript theme={null} function openProject(jsonText: string): ProjectFile; ``` ## ProjectFileError A storage-boundary error with a stable code and a path into the project JSON. ```typescript theme={null} class ProjectFileError extends Error { readonly code: string; readonly path: string; readonly reason?: unknown; constructor(code: string, path: string, message: string, reason?: unknown); } ``` ## Types ### ProjectFile The durable project envelope. ```typescript theme={null} interface ProjectFile { schema: 'floorkit.project'; version: 1 | 2 | 3; buildingModel: BuildingModel; views: SavedDrawingView[]; paper: PaperDocument; markers?: readonly ProjectViewMarker[]; } ``` Named drawing definitions; each can later be placed on one or more sheets. Plan markers refer to saved views; source geometry remains derived. Schema 3. ### ProjectViewMarker Markers belong to a saved plan. ```typescript theme={null} type ProjectViewMarker = | (ViewMarkerBase & { kind: 'source'; }) | (ViewMarkerBase & { kind: 'reference'; position: [number, number]; rotationDeg: number; }); ``` ### ViewMarkerBase The package does not export this type by name. ```typescript theme={null} interface ViewMarkerBase { id: string; parentViewId: string; target: ViewMarkerTarget; } ``` ### ViewMarkerTarget A durable saved-view reference. ```typescript theme={null} interface ViewMarkerTarget { viewId: string; sheetId?: string; viewportId?: string; } ``` ## Related * [`BuildingModel`](/floorkit/api/architecture/validate-building-model#buildingmodel) * [`PaperDocument`](/floorkit/api/drawings/sheet-documents#paperdocument) * [`SavedDrawingView`](/floorkit/api/drawings/sheet-documents#saveddrawingview) # Speckle Source: https://docs.ploterr.com/floorkit/api/integration/speckle Publish a Floorkit building to a Speckle project. This connector comes soon. The Speckle connector is not released. It comes soon. Integrations connect Floorkit to the platforms that your team already uses. Speckle is the first one. With the Speckle connector, you can publish a building to a Speckle project as a new version. Each element keeps its name, its storey and its property sets. ## Planned integrations | Integration | Status | | - | - | | Speckle | Coming soon | ## Tell us what you need Tell us which platform you want to connect. Your answer helps us to set the order of the work. Ask for an integration. # License key Source: https://docs.ploterr.com/floorkit/api/licensing/license-key Apply a hostname license key and read its state. Every deployed hostname needs a valid key, including public prototypes and non-commercial demos. Local development needs no key. These functions apply the key and tell you its state. ## Import ```typescript theme={null} import { setLicenseKey, getLicenseState, getLicenseInfo, LicenseError, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Floorkit, LicenseError, getLicenseState } from '@opengeometry/floorkit'; export async function start(wasmURL: string, licenseKey: string): Promise { const floorkit = new Floorkit({ licenseKey }); await floorkit.setupOpenGeometry(wasmURL); await floorkit.setLicenseKey(licenseKey); console.info(`Floorkit license: ${getLicenseState()}`); } export function isLicenseRefusal(error: unknown): error is LicenseError { return error instanceof LicenseError; } ``` Talk with our team about a license for your product. ## Notes * Give the key exactly as you received it. Do not add spaces or line breaks. * Apply the key before the first view or the first export. * Without a valid key on a deployed site, exports throw `LicenseError`. `saveProject` always works. * See [Licensing](/floorkit/licensing) to ask about a license. ## setLicenseKey Apply a floorkit license key. ```typescript theme={null} function setLicenseKey(key?: string | null): Promise; ``` ## getLicenseState The license state of this page. ```typescript theme={null} function getLicenseState(): LicenseState; ``` ## getLicenseInfo The verified key's details, or `undefined` when no valid key is applied. ```typescript theme={null} function getLicenseInfo(): LicenseInfo | undefined; ``` ## LicenseError Thrown by an export when the site has no valid license key. ```typescript theme={null} class LicenseError extends Error { readonly code: LicenseBlockReason; readonly operation: string; constructor(code: LicenseBlockReason, operation: string); } ``` ## Types ### LicenseState The license state of this page. ```typescript theme={null} type LicenseState = | 'development' | 'pending' | 'unlicensed' | 'invalid' | 'wrong-domain' | 'expired' | 'trial-expired' | 'trial' | 'internal' | 'licensed'; ``` ### LicenseInfo What a verified license key says. ```typescript theme={null} interface LicenseInfo { readonly id: string; readonly organization: string; readonly type: LicenseType; readonly hosts: readonly string[]; readonly issuedAt: Date; readonly expiresAt: Date; } ``` The key id. The organisation the key was issued to. The kind of key. The host rules the key is valid for. When the key was issued. The end date of the key. ### LicenseType The kind of a floorkit license key. ```typescript theme={null} type LicenseType = 'trial' | 'paid' | 'internal'; ``` ### LicenseBlockReason Why an export was refused. ```typescript theme={null} type LicenseBlockReason = 'no-key' | 'invalid-key' | 'wrong-domain' | 'expired'; ``` # API Reference Source: https://docs.ploterr.com/floorkit/api/overview The Floorkit API in 13 sections, from the first wall to the exported PDF. The reference has 103 pages. Each page shows one class, one function or one small group of related functions. A page gives you the import line, a short example, the full signature and the types that the API uses. Types and enums do not have their own pages. You find each one on the page of the API that uses it. All names come from one package root: `@opengeometry/floorkit`. Lengths are in metres and the Y axis points up. Paper sizes are in millimetres. ## Sections Start the geometry kernel and prepare text labels. Lines, arcs and closed profiles that you draw in 2D. Solid boxes and cylinders for furniture, massing and custom parts. The building document and every building element that it makes. Levels, grids and reference planes that locate the building. Dimensions, labels and sheet symbols. Turn a saved view into vector linework and place it on a sheet. Saved views, cameras and the renderers that show them. The place around the building: the world graph, the site and its zoning. Take your design out as a PDF, a GLB or a project file. Edit the building in the plan and in 3D, with undo and redo. Connectors to other platforms. Apply a license key for a deployed hostname. # ArcPrimitive Source: https://docs.ploterr.com/floorkit/api/primitives/arc-primitive A circular arc that you draw in 2D. ## Import ```typescript theme={null} import { ArcPrimitive } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends Arc implements IPrimitive` ## Constructor ```typescript theme={null} constructor(arcConfig?: ArcOptions); ``` ## Properties ## Methods ### setOPConfig ```typescript theme={null} setOPConfig(config: ArcOptions): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): ArcOptions; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### retessellate ```typescript theme={null} retessellate(deflection: number): ReturnType; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ## Types ### ArcOptions ```typescript theme={null} interface ArcOptions { ogid?: string; center: Array; radius: number; startAngle: number; sweepAngle: number; deflection: number; color: number; } ``` ## Related * [`IPrimitive`](/floorkit/api/primitives/line-primitive#iprimitive) # LinePrimitive Source: https://docs.ploterr.com/floorkit/api/primitives/line-primitive A straight line between two points. ## Import ```typescript theme={null} import { LinePrimitive } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends Line implements IPrimitive` ## Constructor ```typescript theme={null} constructor(lineConfig?: Partial); ``` ## Properties Editable-path view used by the GUI editor, reusing the same `Profile` contract as PolygonPrimitive. A line is an open 2-vertex path: the outer ring is `[[startX, startZ], [endX, endZ]]`. Editing happens on the XZ plane, so each endpoint's Y is preserved through the existing start/end setters (which keep `path.points` in sync and rebuild the kernel geometry). ## Methods ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): LineOptions; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### setConfig ```typescript theme={null} setConfig(options: Parameters[0]): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ## Types ### IPrimitive The package does not export this type by name. ```typescript theme={null} interface IPrimitive { ogType: string; subNodes: Map; selected: boolean; edit: boolean; propertySet: Record; setOPConfig(config: Record): void; getOPConfig(): Record; setOPGeometry(): void; setOPMaterial(): void; } ``` ### LineOptions ```typescript theme={null} interface LineOptions { ogid?: string; startPoint: Array; endPoint: Array; color: number; fatLines: boolean; width: number; path: Path; } ``` ### Path The package does not export this type by name. ```typescript theme={null} interface Path { points: Array>; } ``` ### Profile Profile is a loop of points. ```typescript theme={null} interface Profile { outerRing: Array>; holes?: Array>>; } ``` # Polygon maths Source: https://docs.ploterr.com/floorkit/api/primitives/polygon-maths Measure, test and transform closed rings in the plan. ## Import ```typescript theme={null} import { signedAreaXZ, ringAreaXZ, perimeterXZ, areaCentroidXZ, centroidXZ, pointInPolygonXZ, ringsOverlap, ringStrictlyInside, isConvexXZ, distancePointToSegmentXZ, ringDistanceXZ, offsetRingOutwardXZ, rotatePointsXZ, translatePointsXZ, validateAndNormalizeProfile, clonePolygonProfile, } from '@opengeometry/floorkit'; ``` ## signedAreaXZ Shoelace area on the XZ plane. ```typescript theme={null} function signedAreaXZ(ring: PointXZ[]): number; ``` ## ringAreaXZ Absolute (unsigned) shoelace area of the ring. ```typescript theme={null} function ringAreaXZ(ring: PointXZ[]): number; ``` ## perimeterXZ Sum of edge lengths around the (implicitly closed) ring. ```typescript theme={null} function perimeterXZ(ring: PointXZ[]): number; ``` ## areaCentroidXZ Area-weighted centroid (shoelace formula). ```typescript theme={null} function areaCentroidXZ(ring: PointXZ[]): PointXZ; ``` ## centroidXZ Average of all points. ```typescript theme={null} function centroidXZ(ring: PointXZ[]): PointXZ; ``` ## pointInPolygonXZ Crossing-number point-in-polygon test on the XZ plane. ```typescript theme={null} function pointInPolygonXZ(p: PointXZ, ring: PointXZ[]): boolean; ``` ## ringsOverlap True if any vertex of one ring lies inside the other, OR any pair of edges crosses. ```typescript theme={null} function ringsOverlap(a: PointXZ[], b: PointXZ[]): boolean; ``` ## ringStrictlyInside True if every vertex of `inner` lies inside `outer` AND no edge of `inner` crosses any edge of `outer`. ```typescript theme={null} function ringStrictlyInside(inner: PointXZ[], outer: PointXZ[]): boolean; ``` ## isConvexXZ True when every turn around the ring has the same sign (collinear runs allowed). ```typescript theme={null} function isConvexXZ(ring: PointXZ[]): boolean; ``` ## distancePointToSegmentXZ Distance from `p` to the segment a→b (clamped to the segment). ```typescript theme={null} function distancePointToSegmentXZ(p: PointXZ, a: PointXZ, b: PointXZ): number; ``` ## ringDistanceXZ Minimum distance between two rings' boundaries: 0 when they overlap or touch, else the closest vertex-to-edge approach in either direction. ```typescript theme={null} function ringDistanceXZ(a: PointXZ[], b: PointXZ[]): number; ``` ## offsetRingOutwardXZ Offset every edge of the ring outward (away from the interior) by `distance`, rebuilding vertices as miter intersections of adjacent offset edges. ```typescript theme={null} function offsetRingOutwardXZ(ring: PointXZ[], distance: number): PointXZ[]; ``` ## rotatePointsXZ Rotate every point around `anchor` by `angleRad` (right-handed about +Y, i.e. ```typescript theme={null} function rotatePointsXZ(ring: PointXZ[], angleRad: number, anchor: PointXZ): PointXZ[]; ``` ## translatePointsXZ Translate every point. ```typescript theme={null} function translatePointsXZ(ring: PointXZ[], dx: number, dz: number): PointXZ[]; ``` ## validateAndNormalizeProfile Validate, dedupe/auto-close, and normalize winding of a polygon profile. ```typescript theme={null} function validateAndNormalizeProfile( profile: PolygonProfile, label: string, ): NormalizedPolygonProfile; ``` ## clonePolygonProfile Deep-clone a profile so caller mutations don't bleed into element state. ```typescript theme={null} function clonePolygonProfile(profile: PolygonProfile): PolygonProfile; ``` ## Types ### PolygonProfile Closed polygon profile in the world XZ plane. ```typescript theme={null} interface PolygonProfile { outerRing: PointXZ[]; holes?: PointXZ[][]; } ``` ### NormalizedPolygonProfile ```typescript theme={null} interface NormalizedPolygonProfile { outer: PointXZ[]; holes: PointXZ[][]; } ``` ### Winding ```typescript theme={null} type Winding = 'CW' | 'CCW'; ``` ### PointXZ ```typescript theme={null} type PointXZ = [number, number]; ``` # PolygonPrimitive Source: https://docs.ploterr.com/floorkit/api/primitives/polygon-primitive A closed outline with three points or more. ## Import ```typescript theme={null} import { PolygonPrimitive } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends Polygon implements IPrimitive` ## Constructor ```typescript theme={null} constructor(polygonConfig?: PolygonOptions); ``` ## Properties ## Methods ### setProfile ```typescript theme={null} setProfile(profile: Profile): void; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: PolygonOptions): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): PolygonOptions; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` Dispose the wrapper's render resources and owned OGPolygon handle. ## Types ### PolygonOptions ```typescript theme={null} interface PolygonOptions { ogid?: string; center: Array; profile: Profile; color: number; placement: Placement; } ``` ## Related * [`IPrimitive`](/floorkit/api/primitives/line-primitive#iprimitive) * [`Placement`](/floorkit/api/shapes/cuboid-shape#placement) * [`Profile`](/floorkit/api/primitives/line-primitive#profile) # PolylinePrimitive Source: https://docs.ploterr.com/floorkit/api/primitives/polyline-primitive An open path through a list of points. ## Import ```typescript theme={null} import { PolylinePrimitive } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends Polyline implements IPrimitive` ## Constructor ```typescript theme={null} constructor(polylineConfig?: Partial); ``` ## Properties ## Methods ### setConfig ```typescript theme={null} setConfig(options: Parameters[0]): void; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): PolylineOptions; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### attachPoint ```typescript theme={null} attachPoint(point: Array): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` Release render resources and the kernel path when a compiled object is replaced. ## Types ### PolylineOptions ```typescript theme={null} interface PolylineOptions { ogid?: string; points: Array>; color: number; } ``` ## Related * [`IPrimitive`](/floorkit/api/primitives/line-primitive#iprimitive) # RectanglePrimitive Source: https://docs.ploterr.com/floorkit/api/primitives/rectangle-primitive A rectangle with a centre, a width and a breadth. ## Import ```typescript theme={null} import { RectanglePrimitive } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends Polygon implements IPrimitive` ## Constructor ```typescript theme={null} constructor(rectangleConfig?: RectangleOptions); ``` ## Properties ## Methods ### setOPConfig ```typescript theme={null} setOPConfig(config: RectangleOptions): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): RectangleOptions; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` Dispose render resources and the owned polygon kernel handle once. ## Types ### RectangleOptions ```typescript theme={null} interface RectangleOptions { ogid?: string; isParametric: boolean; center: Array; width?: number; breadth?: number; color?: number; profile?: Profile; placement: Placement; } ``` ### Profile The package does not export this type by name. ```typescript theme={null} interface Profile { outerRing: Array>; holes?: Array>>; } ``` ## Related * [`IPrimitive`](/floorkit/api/primitives/line-primitive#iprimitive) * [`Placement`](/floorkit/api/shapes/cuboid-shape#placement) # Floorkit Source: https://docs.ploterr.com/floorkit/api/runtime/floorkit Floorkit starts the geometry kernel. Create it one time, when your app starts, before you create geometry. ## Import ```typescript theme={null} import { Floorkit } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Floorkit } from '@opengeometry/floorkit'; import wasmURL from 'opengeometry/opengeometry_bg.wasm?url'; async function initialize(): Promise { await new Floorkit().setupOpenGeometry(wasmURL); } ``` ## Notes * Await `setupOpenGeometry()` before you create an element or a session. * Floorkit does not create a scene, a camera or a renderer. Your app owns them. * For text labels on datums and dimensions, call [`setGlyphScene`](/floorkit/api/runtime/set-glyph-scene) after the setup. * A deployed site passes its hostname license key in the constructor. See [Licensing](/floorkit/licensing). ## Constructor ```typescript theme={null} constructor(options?: FloorkitOptions); ``` License key for a deployed site. Leave it out on a developer's machine. Set to `false` to turn off the once-a-day development usage ping. ## Methods ### setLicenseKey ```typescript theme={null} setLicenseKey(key?: string | null): Promise; ``` Apply a floorkit license key; see setLicenseKey. ### setupOpenGeometry ```typescript theme={null} setupOpenGeometry(wasmURL?: string): Promise; ``` ## Types ### FloorkitOptions Options for Floorkit. ```typescript theme={null} interface FloorkitOptions { licenseKey?: string; telemetry?: boolean; } ``` ## Related * [`LicenseState`](/floorkit/api/licensing/license-key#licensestate) # setGlyphScene Source: https://docs.ploterr.com/floorkit/api/runtime/set-glyph-scene Give Floorkit your scene and camera, so that it can draw text labels. ## Import ```typescript theme={null} import { setGlyphScene } from '@opengeometry/floorkit'; ``` ## Signature ```typescript theme={null} function setGlyphScene( scene: THREE.Scene, camera: THREE.PerspectiveCamera, face?: fontType, ): Promise; ``` ## Parameters Default: `'Roboto_Regular'`. ## Returns ```typescript theme={null} Promise; ``` # CuboidShape Source: https://docs.ploterr.com/floorkit/api/shapes/cuboid-shape A solid box. ## Import ```typescript theme={null} import { CuboidShape } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends Polygon implements IShape` ## Constructor ```typescript theme={null} constructor(config?: CuboidOptions); ``` ## Properties Extrusion distance along +Y. Re-extrudes only; the footprint is untouched. Convert the parametric box into an explicit, editable footprint. One-way to match `RectanglePrimitive`: once freeform, width/depth no longer drive the ring, so flipping back would silently discard edits. ## Methods ### setOPConfig ```typescript theme={null} setOPConfig(config: CuboidOptions): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): CuboidOptions; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### setOPPlacement ```typescript theme={null} setOPPlacement(placement: Placement): void; ``` Apply native placement to both the profile and its current extrusion. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ## Types ### IShape The package does not export this type by name. ```typescript theme={null} interface IShape { ogType: string; subElements2D: Map; subElements3D: Map; selected: boolean; edit: boolean; locked: boolean; propertySet: Record; setOPConfig(config: Record): void; getOPConfig(): Record; setOPGeometry(): void; setOPMaterial(): void; } ``` ### CuboidOptions A cuboid authored as a **2D rectangular profile extruded to a height**, rather than a fixed kernel box primitive. ```typescript theme={null} interface CuboidOptions { ogid?: string; center: Array; width?: number; depth?: number; extrudeHeight?: number; color?: number; isParametric?: boolean; profile?: Profile; placement: Placement; } ``` ### Placement Local-frame transform from an asset/instance's definition coordinates to world coordinates. ```typescript theme={null} interface Placement { anchor?: Vec3; position: Vec3; rotation: Vec3; scale: Vec3; } ``` Local-frame anchor for rotation and scale. Defaults to \[0, 0, 0]. Translation (world-space, meters), applied AFTER rotation and scale. Euler angles (degrees), in THREE.js default XYZ order. Per-axis scale factors. ## Related * [`Profile`](/floorkit/api/primitives/line-primitive#profile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # CylinderShape Source: https://docs.ploterr.com/floorkit/api/shapes/cylinder-shape A solid cylinder. ## Import ```typescript theme={null} import { CylinderShape } from '@opengeometry/floorkit'; ``` **Inheritance:** `extends THREE.Group implements IShape, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(options?: Partial); ``` ## Properties Read-only. Read-only. Read-only. Read-only. ## Methods ### setOPConfig ```typescript theme={null} setOPConfig(update: Partial): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): CylinderOptions; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### setOPPlacement ```typescript theme={null} setOPPlacement(placement: Placement): void; ``` ### getBrepSerialized ```typescript theme={null} getBrepSerialized(): string; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingCurvedRegion[]; ``` Produce standardized drawing primitives for a view directly from the element's parametric data (the 0.3.0 contract). ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### PlanVectorExportable ```typescript theme={null} interface PlanVectorExportable { getExportRoots(view: PlanExportView): THREE.Object3D[]; toDrawing?(view: PlanExportView): DrawingPrimitive[]; } ``` ### CylinderOptions ```typescript theme={null} interface CylinderOptions { ogid?: string; center: [number, number, number]; radius: number; height: number; deflection: number; color: number; placement: Placement; } ``` ### PlanExportView ```typescript theme={null} type PlanExportView = 'top' | 'isometric'; ``` ## Related * [`DrawingCurvedRegion`](/floorkit/api/drawings/draw-project-view#drawingcurvedregion) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`IShape`](/floorkit/api/shapes/cuboid-shape#ishape) * [`Placement`](/floorkit/api/shapes/cuboid-shape#placement) # createModelRenderer Source: https://docs.ploterr.com/floorkit/api/views/create-model-renderer Create the interactive 2D plan renderer. `createModelRenderer` makes the on-screen 2D plan view. The user can pan, zoom and edit the plan in it. ## Import ```typescript theme={null} import { createModelRenderer, wheelNavigation, TRACKPAD_NAVIGATION, MOUSE_NAVIGATION, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { MOUSE_NAVIGATION, PanButton, WheelAction, createModelRenderer, type VectorDrawing, } from '@opengeometry/floorkit'; export async function mountPlan(container: HTMLElement, drawing: VectorDrawing, mouse: boolean) { const renderer = await createModelRenderer(); if (mouse) renderer.setNavigation(MOUSE_NAVIGATION); else renderer.setNavigation({ wheel: WheelAction.Pan, panButtons: [PanButton.Middle] }); renderer.mount(container, drawing); return renderer; } ``` ## Notes * The renderer needs a browser. * `setNavigation` sets how the wheel and the mouse buttons move the view. The sheet view from [`createSheetRenderer`](/floorkit/api/views/create-sheet-renderer) has the same method and the same settings. * The default is `TRACKPAD_NAVIGATION`: a two-finger scroll pans, and a pinch zooms. Ctrl or Cmd with the wheel also zooms. * With `MOUSE_NAVIGATION` the wheel zooms at the pointer. * `panButtons` lists the mouse buttons that pan by a drag. `PanButton.Left` pans only from empty space. The other buttons pan from any place. * In version 1.0.0 the wheel always zoomed. Call `setNavigation(MOUSE_NAVIGATION)` to keep that. * Two fingers on a touch screen do not pan or zoom. ## Signature ```typescript theme={null} function createModelRenderer(): Promise; ``` ## Parameters This function takes no arguments. ## Returns ```typescript theme={null} Promise; ``` ## wheelNavigation Resolve one wheel event to a pan in screen pixels or a zoom factor. ```typescript theme={null} function wheelNavigation(wheel: WheelInput, settings: NavigationSettings): WheelNavigation; ``` ## TRACKPAD\_NAVIGATION Two-finger scroll pans, pinch or Ctrl/Cmd with the wheel zooms. ```typescript theme={null} const TRACKPAD_NAVIGATION: NavigationSettings; ``` ## MOUSE\_NAVIGATION The wheel zooms about the cursor; left and middle drag pan. ```typescript theme={null} const MOUSE_NAVIGATION: NavigationSettings; ``` ## Types ### WheelInput The wheel event fields navigation reads. ```typescript theme={null} interface WheelInput { deltaX: number; deltaY: number; deltaMode: number; ctrlKey: boolean; metaKey: boolean; } ``` ### NavigationSettings How pointer input navigates the 2D plan editor and the sheet editor. ```typescript theme={null} interface NavigationSettings { wheel: WheelAction; panButtons: readonly PanButton[]; zoomSpeed: number; } ``` What an unmodified wheel or two-finger scroll does. Buttons that pan by dragging. `PanButton.Left` pans from empty space only; the other buttons pan from anywhere. Multiplier on zoom speed. Must be a finite number above zero. ### WheelNavigation type The result of wheelNavigation: a pan in pixels or a zoom factor. ```typescript theme={null} type WheelNavigation = | { action: NavigationAction.Pan; dx: number; dy: number; } | { action: NavigationAction.Zoom; factor: number; }; ``` ### WheelAction What an unmodified wheel or two-finger scroll does in a 2D view. ```typescript theme={null} enum WheelAction { Pan = 0, Zoom = 1, } ``` ### PanButton A mouse button that pans by dragging. ```typescript theme={null} enum PanButton { Left = 0, Middle = 1, Right = 2, } ``` ### NavigationAction The navigation a wheel event resolves to. ```typescript theme={null} enum NavigationAction { Pan = 0, Zoom = 1, } ``` # createProjectPhysicalView Source: https://docs.ploterr.com/floorkit/api/views/create-project-physical-view Build the 3D scene and the camera of one saved section or elevation. ## Import ```typescript theme={null} import { createProjectPhysicalView } from '@opengeometry/floorkit'; ``` ## Signature ```typescript theme={null} function createProjectPhysicalView( project: ProjectFile, viewId: string, options?: DrawProjectViewOptions, ): ProjectPhysicalView; ``` ## Parameters Default: `{}`. ## Returns ```typescript theme={null} ProjectPhysicalView; ``` ## Types ### ProjectPhysicalView A detached presentation owns its Three resources after native compilation is released. ```typescript theme={null} interface ProjectPhysicalView { scene: THREE.Scene; readonly sourceByMesh: ReadonlyMap; camera: THREE.OrthographicCamera; volume: PhysicalViewVolume; dispose(): void; } ``` Authored inspection references for each scene mesh; cleared on disposal. ## Related * [`DrawingSourceRef`](/floorkit/api/drawings/draw-project-view#drawingsourceref) * [`DrawProjectViewOptions`](/floorkit/api/drawings/draw-project-view#drawprojectviewoptions) * [`PhysicalViewVolume`](/floorkit/api/drawings/view-markers-and-level-labels#physicalviewvolume) * [`ProjectFile`](/floorkit/api/exports/project-file#projectfile) # createSheetRenderer Source: https://docs.ploterr.com/floorkit/api/views/create-sheet-renderer createSheetRenderer makes the on-screen sheet view. It shows a sheet with its viewports, and the user can move and add items. ## Import ```typescript theme={null} import { createSheetRenderer } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { createSheetRenderer, type PaperDocument, type PaperRenderDeps, type PaperEditEvent, } from '@opengeometry/floorkit'; export async function mountSheets( container: HTMLElement, document: PaperDocument, deps: PaperRenderDeps, onEdit: (event: PaperEditEvent) => void, ): Promise<() => void> { const renderer = await createSheetRenderer(); renderer.mount(container, document, deps); renderer.on('move', onEdit); renderer.on('create', onEdit); renderer.setTool('select'); renderer.fitToPage(); return () => { renderer.off('move', onEdit); renderer.off('create', onEdit); renderer.dispose(); }; } ``` ## Notes * The renderer needs a browser. * The renderer shows your document. It does not own it. On an edit event, update your document and call `update(document)`. * When the panel changes size, call `resize(width, height)` in pixels. * Sheet coordinates are in millimetres. * `setNavigation` sets how the wheel and the mouse buttons move the view. See the notes on [`createModelRenderer`](/floorkit/api/views/create-model-renderer#notes). * Call `dispose()` when you remove the view. ## Signature ```typescript theme={null} function createSheetRenderer(): Promise; ``` ## Parameters This function takes no arguments. ## Returns ```typescript theme={null} Promise; ``` ## Types ### PaperScreenRenderer An interactive screen view of a `PaperDocument` (see .spec/debt.md, "Paper / drawing layer"). ```typescript theme={null} interface PaperScreenRenderer { mount(container: HTMLElement, doc: PaperDocument, deps: PaperRenderDeps): void; update(doc: PaperDocument): void; on(event: PaperEditKind, callback: (event: PaperEditEvent) => void): void; off(event: PaperEditKind, callback: (event: PaperEditEvent) => void): void; resize(width: number, height: number): void; fitToPage(): void; setSheet(sheetId: string): void; activeSheetId(): string | undefined; setTool(toolId: PaperToolId): void; readonly navigation: NavigationSettings; setNavigation(settings: Partial): void; setToolData?(data: { imageSrc?: string }): void; setPaperStyle?(style?: PaperStyleInput): void; dispose(): void; } ``` How the wheel and mouse buttons pan and zoom this view. ### PaperEditKind ```typescript theme={null} type PaperEditKind = 'select' | 'move' | 'resize' | 'create'; ``` ### PaperEditEvent ```typescript theme={null} interface PaperEditEvent { kind: PaperEditKind; targetId?: string; frame?: { x: number; y: number; width: number; height: number; }; annotation?: Annotation; } ``` Document id of the affected viewport/annotation; absent on deselect. The target's current transform (paper mm) after a move/resize. The newly created annotation (present when kind === 'create'). ### PaperToolId ```typescript theme={null} type PaperToolId = | 'select' | 'pan' | 'text' | 'dimension' | 'measure' | 'angular' | 'radial' | 'diameter' | 'leader' | 'line' | 'polyline' | 'rect' | 'circle' | 'image'; ``` ## Related * [`Annotation`](/floorkit/api/drawings/sheet-documents#annotation) * [`NavigationSettings`](/floorkit/api/views/create-model-renderer#navigationsettings) * [`PaperDocument`](/floorkit/api/drawings/sheet-documents#paperdocument) * [`PaperRenderDeps`](/floorkit/api/drawings/paper-styles#paperrenderdeps) * [`PaperStyleInput`](/floorkit/api/drawings/paper-styles#paperstyleinput) # OpenThree Source: https://docs.ploterr.com/floorkit/api/views/open-three OpenThree is a ready viewer. It gives you a scene, a camera, a renderer, lights, a grid and a render loop. Use it when you do not have a Three.js scene of your own. ## Import ```typescript theme={null} import { OpenThree } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { OpenThree, CameraMode } from '@opengeometry/floorkit'; export function mountViewer(container: HTMLElement): () => void { const viewer = new OpenThree(container, () => {}); viewer.planCamera.CameraMode = CameraMode.Model; viewer.toggleGrid(false); return () => viewer.dispose(); } ``` ## Notes * The container must have a width and a height. * The constructor starts the render loop. Do not add a second loop. * Start the geometry kernel separately, before you create elements. * `dispose()` stops the loop and removes the canvas. ## Constructor ```typescript theme={null} constructor(container: HTMLElement, callback: any); ``` ## Properties ## Methods ### generateTheme ```typescript theme={null} generateTheme(): void; ``` ### toggleTheme ```typescript theme={null} toggleTheme(name: activeTheme): void; ``` ### setup ```typescript theme={null} setup(): Promise; ``` ### toggleGrid ```typescript theme={null} toggleGrid(show: boolean): void; ``` ### hexToRgb ```typescript theme={null} hexToRgb(hex: number): THREE.Vector3; ``` ### captureView ```typescript theme={null} captureView(options: { width: number; height: number; camera: THREE.Camera; transparent?: boolean; }): string; ``` Render the scene from `camera` to a PNG data-URL, off-screen at an arbitrary resolution. ### enableAnalyticLod ```typescript theme={null} enableAnalyticLod(options: AnalyticWorkerOptions & AnalyticLodOptions): Promise; ``` Configure public SDK worker assets; model registration is independent of camera requests and scene snapshots. ### disableAnalyticLod ```typescript theme={null} disableAnalyticLod(): void; ``` ### syncAnalyticLod ```typescript theme={null} syncAnalyticLod(): Promise; ``` Call after authored tree changes; unchanged bodies never resend their serialized models. ### refineAnalyticView ```typescript theme={null} refineAnalyticView(deflection: number): Promise; ``` Await this before synchronous image capture to require a world-space chord error. ### dispose ```typescript theme={null} dispose(): void; ``` ### animate ```typescript theme={null} animate(): void; ``` ## Types ### ICanvasTheme ```typescript theme={null} interface ICanvasTheme { [key: string]: { background: string; color: string; gridColor: number; }; } ``` ### activeTheme ```typescript theme={null} type activeTheme = 'light' | 'dark' | 'darkBlue'; ``` ## Related * [`PlanCamera`](/floorkit/api/views/plan-camera) # PlanCamera Source: https://docs.ploterr.com/floorkit/api/views/plan-camera PlanCamera changes one camera between a top-down plan view and a 3D model view. ## Import ```typescript theme={null} import { PlanCamera } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import * as THREE from 'three'; import CameraControls from 'camera-controls'; import { PlanCamera, CameraMode } from '@opengeometry/floorkit'; export function configureCamera( camera: THREE.PerspectiveCamera, container: HTMLElement, ): PlanCamera { CameraControls.install({ THREE }); const navigation = new PlanCamera(camera, container); navigation.CameraMode = CameraMode.Plan; return navigation; } ``` ## Notes * Call `update()` in each frame. * A change of mode resets the position and the target of the camera. * In Plan mode, the left mouse button pans. In Model mode, it rotates. * Await `fitToElement` before work that depends on the new camera position. * If you own the camera, update its projection when the size changes, and dispose `navigation.controls` at the end. `OpenThree` does this for its own camera. ## Constructor ```typescript theme={null} constructor(camera: THREE.PerspectiveCamera | THREE.OrthographicCamera, container: HTMLElement); ``` ## Properties Read-only. Read-only. ## Methods ### setupCamera ```typescript theme={null} setupCamera(): void; ``` ### fitToElement ```typescript theme={null} fitToElement(target: THREE.Object3D | THREE.Object3D[]): Promise; ``` ### update ```typescript theme={null} update(): boolean; ``` ## Types ### CameraMode ```typescript theme={null} enum CameraMode { Plan = 0, Model = 1, } ``` # prepareProjectPhysicalRasterViews Source: https://docs.ploterr.com/floorkit/api/views/prepare-project-physical-raster-views Make the images of section and elevation views before an export. ## Import ```typescript theme={null} import { prepareProjectPhysicalRasterViews, createProjectPhysicalRasterViewProvider, createProjectPhysicalRasterViewResolver, isProjectPhysicalRasterViewCurrent, } from '@opengeometry/floorkit'; ``` ## Signature ```typescript theme={null} function prepareProjectPhysicalRasterViews( project: ProjectFile, provider: PhysicalRasterViewProvider, options?: PrepareProjectRasterViewsOptions, ): Promise; ``` ## Parameters Default: `{}`. ## Returns ```typescript theme={null} Promise; ``` ## createProjectPhysicalRasterViewProvider Capture the saved physical frame with the application's renderer and restore renderer state. ```typescript theme={null} function createProjectPhysicalRasterViewProvider( renderer: THREE.WebGLRenderer, ): PhysicalRasterViewProvider; ``` ## createProjectPhysicalRasterViewResolver Resolve raster intent, rejecting missing or stale content at each call. ```typescript theme={null} function createProjectPhysicalRasterViewResolver( project: ProjectFile, batch?: RasterViewBatch, options?: ProjectRasterViewOptions, ): (viewId: string, viewport?: Viewport) => RasterDrawing | undefined; ``` ## isProjectPhysicalRasterViewCurrent Check model, saved view and asset identity; sheet placement does not stale pixels. ```typescript theme={null} function isProjectPhysicalRasterViewCurrent( project: ProjectFile, viewId: string, batch: RasterViewBatch, options?: ProjectRasterViewOptions, ): boolean; ``` ## Types ### PhysicalRasterViewProvider Application supplied raster capture of a saved section or elevation. ```typescript theme={null} type PhysicalRasterViewProvider = ( request: PhysicalRasterViewRequest, ) => RasterViewImage | Promise; ``` ### PhysicalRasterViewRequest Detached committed physical view inputs; providers render this snapshot. ```typescript theme={null} interface PhysicalRasterViewRequest { buildingModel: Readonly; view: Readonly; pixelWidth: number; pixelHeight: number; sourceKey: string; assetSources?: BuildOptions['assetSources']; } ``` ### SavedPhysicalView ```typescript theme={null} type SavedPhysicalView = Extract< SavedDrawingView, { kind: 'section' | 'elevation'; } >; ``` ## Related * [`BuildingModel`](/floorkit/api/architecture/validate-building-model#buildingmodel) * [`BuildOptions`](/floorkit/api/architecture/build-from-building-model#buildoptions) * [`PrepareProjectRasterViewsOptions`](/floorkit/api/views/prepare-project-raster-views#prepareprojectrasterviewsoptions) * [`ProjectFile`](/floorkit/api/exports/project-file#projectfile) * [`ProjectRasterViewOptions`](/floorkit/api/views/prepare-project-raster-views#projectrasterviewoptions) * [`RasterDrawing`](/floorkit/api/views/prepare-project-raster-views#rasterdrawing) * [`RasterViewBatch`](/floorkit/api/views/prepare-project-raster-views#rasterviewbatch) * [`RasterViewImage`](/floorkit/api/views/prepare-project-raster-views#rasterviewimage) * [`SavedDrawingView`](/floorkit/api/drawings/sheet-documents#saveddrawingview) * [`Viewport`](/floorkit/api/drawings/sheet-documents#viewport) # prepareProjectRasterViews Source: https://docs.ploterr.com/floorkit/api/views/prepare-project-raster-views prepareProjectRasterViews makes the images for the raster isometric views of a project. Do this before you export a sheet that shows such a view. ## Import ```typescript theme={null} import { prepareProjectRasterViews, createProjectRasterViewProvider, createProjectRasterViewResolver, isProjectRasterViewCurrent, ProjectRasterError, } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { prepareProjectRasterViews, isProjectRasterViewCurrent, exportProjectPdf, type ProjectFile, type RasterViewProvider, } from '@opengeometry/floorkit'; export async function exportWithImages( project: ProjectFile, viewId: string, provider: RasterViewProvider, ): Promise { const batch = await prepareProjectRasterViews(project, provider, { viewIds: [viewId], dpi: 300 }); if (!isProjectRasterViewCurrent(project, viewId, batch)) { throw new Error('The model changed. Prepare the images again.'); } return exportProjectPdf(project, undefined, { rasterViews: batch }); } ``` ## Notes * Give a provider that renders the image. `createProjectRasterViewProvider` makes one from your renderer. * The image size follows the largest viewport that shows the view, and the DPI that you set. * Prepare the images again after a change of the model, the view or the assets. A change of the sheet layout does not need new images. * The export rejects a missing or old image with `ProjectRasterError`. * For section and elevation images, use [`prepareProjectPhysicalRasterViews`](/floorkit/api/views/prepare-project-physical-raster-views). ## Signature ```typescript theme={null} function prepareProjectRasterViews( project: ProjectFile, provider: RasterViewProvider, options?: PrepareProjectRasterViewsOptions, ): Promise; ``` ## Parameters Default: `{}`. ## Returns ```typescript theme={null} Promise; ``` ## createProjectRasterViewProvider Render detached committed model inputs in a disposable scene using the app's renderer. ```typescript theme={null} function createProjectRasterViewProvider(renderer: THREE.WebGLRenderer): RasterViewProvider; ``` ## createProjectRasterViewResolver Resolve prepared content, rejecting missing or stale raster images at call time. ```typescript theme={null} function createProjectRasterViewResolver( project: ProjectFile, batch?: RasterViewBatch, options?: ProjectRasterViewOptions, ): (viewId: string) => RasterDrawing | undefined; ``` ## isProjectRasterViewCurrent Check current committed model/view/assets each time; placement changes do not stale pixels. ```typescript theme={null} function isProjectRasterViewCurrent( project: ProjectFile, viewId: string, batch: RasterViewBatch, options?: ProjectRasterViewOptions, ): boolean; ``` ## ProjectRasterError Stable failure codes for preparation, rendering and stale sheet content. ```typescript theme={null} class ProjectRasterError extends Error { readonly code: ProjectRasterErrorCode; readonly viewId: string; constructor(code: ProjectRasterErrorCode, viewId: string, message: string); } ``` ## Types ### RasterViewProvider Apps supply browser/WebGL execution; importing Floorkit does not start a browser. ```typescript theme={null} type RasterViewProvider = ( request: RasterViewRequest, ) => RasterViewImage | Promise; ``` ### PrepareProjectRasterViewsOptions ```typescript theme={null} interface PrepareProjectRasterViewsOptions extends ProjectRasterViewOptions { viewIds?: readonly string[]; dpi?: number; pixelWidth?: number; } ``` ### RasterViewBatch Derived images for sheet resolution. ```typescript theme={null} interface RasterViewBatch { views: ReadonlyMap; } ``` ### ProjectRasterViewOptions ```typescript theme={null} interface ProjectRasterViewOptions { assetSources?: BuildOptions['assetSources']; massColors?: BuildOptions['massColors']; } ``` The colours of mass parts by use; see `BuildOptions.massColors`. Part of the identity. ### RasterDrawing Derived PNG content with camera-plane bounds converted to paper's Y-down convention. ```typescript theme={null} interface RasterDrawing { kind: 'raster'; units: 'meters'; src: string; pixelWidth: number; pixelHeight: number; bounds: { min: Point2; max: Point2; }; overlay?: readonly DrawingPrimitive[]; } ``` Derived camera-plane vectors in metres, placed above pixels using the same fixed bounds. ### ProjectRasterErrorCode ```typescript theme={null} type ProjectRasterErrorCode = | 'unknown-raster-view' | 'missing-raster-view' | 'stale-raster-view' | 'invalid-raster-image' | 'invalid-raster-size' | 'unknown-raster-element' | 'empty-raster-view'; ``` ### RasterViewRequest Detached committed inputs. ```typescript theme={null} interface RasterViewRequest { buildingModel: Readonly; view: Readonly; pixelWidth: number; pixelHeight: number; sourceKey: string; assetSources?: BuildOptions['assetSources']; massColors?: BuildOptions['massColors']; } ``` Exact model/view/asset input identity, issued by preparation and echoed by the provider. The colours of mass parts by use; see `BuildOptions.massColors`. ### RasterViewImage Encoded browser rendering. ```typescript theme={null} interface RasterViewImage { src: string; pixelWidth: number; pixelHeight: number; sourceKey: string; } ``` ### PreparedRasterView ```typescript theme={null} interface PreparedRasterView { sourceKey: string; drawing: RasterDrawing; } ``` ## Related * [`BuildingModel`](/floorkit/api/architecture/validate-building-model#buildingmodel) * [`BuildOptions`](/floorkit/api/architecture/build-from-building-model#buildoptions) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`Point2`](/floorkit/api/annotations/dimension-geometry#point2) * [`ProjectFile`](/floorkit/api/exports/project-file#projectfile) * [`SavedRasterIsometricView`](/floorkit/api/drawings/sheet-documents#savedrasterisometricview) # Raster capture Source: https://docs.ploterr.com/floorkit/api/views/raster-capture Make the camera for a saved view and capture it as an image. ## Import ```typescript theme={null} import { captureRasterView, createRasterIsometricCamera, fitRasterIsometricFrame, createPhysicalViewCamera, } from '@opengeometry/floorkit'; ``` ## captureRasterView Capture a PNG without changing the renderer configuration or the supplied camera pose. ```typescript theme={null} function captureRasterView( renderer: THREE.WebGLRenderer, scene: THREE.Scene, options: CaptureRasterViewOptions, ): CapturedRasterView; ``` ## createRasterIsometricCamera Recreate the saved pose and fixed orthographic window without fitting the model. ```typescript theme={null} function createRasterIsometricCamera(view: SavedRasterIsometricView): THREE.OrthographicCamera; ``` ## fitRasterIsometricFrame Explicitly fit a new camera-plane window in metres; never modifies the saved view. ```typescript theme={null} function fitRasterIsometricFrame( view: SavedRasterIsometricView, bounds: THREE.Box3, padding?: number, ): RasterViewFrame; ``` ## createPhysicalViewCamera Derive a camera from a physical view's exact crop and cut plane in metres. ```typescript theme={null} function createPhysicalViewCamera( volume: PhysicalViewVolume, elevationDepth?: number, ): THREE.OrthographicCamera; ``` ## Types ### CaptureRasterViewOptions ```typescript theme={null} interface CaptureRasterViewOptions { camera: THREE.Camera; width: number; height: number; background?: string | null; } ``` ### CapturedRasterView ```typescript theme={null} interface CapturedRasterView { src: string; pixelWidth: number; pixelHeight: number; } ``` ### RasterViewFrame Orthographic camera-plane extents in metres. ```typescript theme={null} interface RasterViewFrame { left: number; right: number; bottom: number; top: number; } ``` ## Related * [`PhysicalViewVolume`](/floorkit/api/drawings/view-markers-and-level-labels#physicalviewvolume) * [`SavedRasterIsometricView`](/floorkit/api/drawings/sheet-documents#savedrasterisometricview) # resolvePhysicalViewVolume Source: https://docs.ploterr.com/floorkit/api/views/resolve-physical-view-volume Get the frame, the crop and the depth of a saved section or elevation. ## Import ```typescript theme={null} import { resolvePhysicalViewVolume } from '@opengeometry/floorkit'; ``` ## Signature ```typescript theme={null} function resolvePhysicalViewVolume( view: Extract< SavedDrawingView, { kind: 'section' | 'elevation'; } >, ): PhysicalViewVolume; ``` ## Parameters ## Returns ```typescript theme={null} PhysicalViewVolume; ``` ## Related * [`PhysicalViewVolume`](/floorkit/api/drawings/view-markers-and-level-labels#physicalviewvolume) * [`SavedDrawingView`](/floorkit/api/drawings/sheet-documents#saveddrawingview) # View Source: https://docs.ploterr.com/floorkit/api/views/view One named view of the model. ## Import ```typescript theme={null} import { View } from '@opengeometry/floorkit'; ``` ## Constructor ```typescript theme={null} constructor(options: IViewOptions); ``` ## Properties Sets the active state of the view. When a view is active, it can be rendered and interacted with. Inactive views are not rendered and do not respond to user interactions. ## Related * [`IViewOptions`](/floorkit/api/views/view-manager#iviewoptions) # View frames Source: https://docs.ploterr.com/floorkit/api/views/view-frames Convert a point between the model and the frame of a plan, section or elevation. ## Import ```typescript theme={null} import { viewFrameForPlan, viewFrameForSection, viewFrameForElevation, viewFrameForCamera, modelToView, viewToModel, } from '@opengeometry/floorkit'; ``` ## viewFrameForPlan A horizontal plan: +X right, +Z down, looking along -Y. ```typescript theme={null} function viewFrameForPlan(storeyElevation: number, cutHeightAboveStorey: number): ViewFrame; ``` ## viewFrameForSection A vertical section. ```typescript theme={null} function viewFrameForSection(line: SectionLine, lookSide: 'left' | 'right'): ViewFrame; ``` ## viewFrameForElevation A vertical elevation; lookDirection points from the view plane into the model. ```typescript theme={null} function viewFrameForElevation(origin: WorldXZ, lookDirection: WorldXZ): ViewFrame; ``` ## viewFrameForCamera An orthographic camera frame with its target as the model-space origin. ```typescript theme={null} function viewFrameForCamera(camera: Camera): ViewFrame; ``` ## modelToView Project one world point into right/down/depth coordinates in metres. ```typescript theme={null} function modelToView(frame: ViewFrame, worldPoint: Point3D): ViewPoint; ``` ## viewToModel Recover a world point from right/down/depth coordinates in metres. ```typescript theme={null} function viewToModel(frame: ViewFrame, viewPoint: ViewPoint): Point3D; ``` ## Types ### SectionLine The package does not export this type by name. ```typescript theme={null} type SectionLine = { start: WorldXZ; end: WorldXZ; }; ``` ### WorldXZ The package does not export this type by name. ```typescript theme={null} type WorldXZ = [number, number]; ``` ### Camera The package does not export this type by name. ```typescript theme={null} type Camera = { position: Point3D; target: Point3D; up: Point3D; }; ``` ### Point3D ```typescript theme={null} interface Point3D { x: number; y: number; z: number; } ``` ### ViewPoint Coordinates in a view frame. ```typescript theme={null} interface ViewPoint { x: number; y: number; depth: number; } ``` ## Related * [`ViewFrame`](/floorkit/api/drawings/drawing-helpers#viewframe) # ViewManager Source: https://docs.ploterr.com/floorkit/api/views/view-manager Creates views and finds them by id. ## Import ```typescript theme={null} import { ViewManager } from '@opengeometry/floorkit'; ``` ## Constructor This class uses its default or inherited constructor. ## Methods ### createView ```typescript theme={null} createView(options: IViewOptions): View; ``` ### getView ```typescript theme={null} getView(ogid: string): View | undefined; ``` ## Types ### IViewOptions ```typescript theme={null} interface IViewOptions { ogid?: string; container?: HTMLElement; bounds: { min: { x: number; y: number; z: number; }; max: { x: number; y: number; z: number; }; }; camera: { position: { x: number; y: number; z: number; }; target: { x: number; y: number; z: number; }; }; lockCamera?: boolean; elementOverides?: { [elementId: string]: { options: any; }; }; } ``` ## Related * [`View`](/floorkit/api/views/view) # Georeference Source: https://docs.ploterr.com/floorkit/api/world/georeference Convert between longitude and latitude and local metres. ## Import ```typescript theme={null} import { lngLatToLocal, localToLngLat } from '@opengeometry/floorkit'; ``` ## lngLatToLocal Project a WGS84 point to site-local metres `[x, z]` (+X east, −Z north). ```typescript theme={null} function lngLatToLocal(anchor: GeoAnchor, lng: number, lat: number, trueNorthDeg?: number): PointXZ; ``` ## localToLngLat Inverse of lngLatToLocal: site-local metres → `[lng, lat]`. ```typescript theme={null} function localToLngLat( anchor: GeoAnchor, x: number, z: number, trueNorthDeg?: number, ): [lng: number, lat: number]; ``` ## Related * [`GeoAnchor`](/floorkit/api/world/site#geoanchor) * [`PointXZ`](/floorkit/api/primitives/polygon-maths#pointxz) # Site Source: https://docs.ploterr.com/floorkit/api/world/site Site is the land parcel. It holds the boundary, the class of each lot line and an optional geographic anchor. ## Import ```typescript theme={null} import { Site, classifyLotLines } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { Floorkit, Site } from '@opengeometry/floorkit'; export async function createParcel() { await new Floorkit().setupOpenGeometry(); const site = new Site({ labelName: 'Parcel', profile: { outerRing: [ [0, 0], [30, 0], [30, 20], [0, 20], ], }, lotLines: ['front', 'side', 'rear', 'side'], elevation: 0, }); console.log(site.area, site.perimeter); return site; } ``` ## Notes * Points are `[x, z]` in metres. `elevation` is the world height of the ground. * `area` is the net area in square metres, with holes subtracted. `perimeter` measures the outer boundary. * The lot-line list must have one entry for each boundary edge. An edge without a class is a `side` line. * `trueNorthDeg` is the clockwise angle from project north (−Z) to true north. * A site in Floorkit is a design aid. It is not a legal survey. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). **Inheritance:** `extends OPElement implements IPlaceable, PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` ## Properties Always empty — the parcel is a surface. Kept so `dispose()` stays uniform. Resolved lot-line classes, one per unique outer-ring edge (`side` when unset). Read-only. Net parcel area in m² — outer ring minus holes. Computed, read-only. Read-only. Outer-boundary length in m. Computed, read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` Self-describing editable surface; a host inspector renders this generically. ### move ```typescript theme={null} move(delta: Vec3): void; ``` Translate this element by `delta` (world-space, meters). ### rotate ```typescript theme={null} rotate(angleDeg: number, axis?: Vec3, anchor?: Vec3): void; ``` Rotate about Y. ### getOPConfig ```typescript theme={null} getOPConfig(): ISite; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` Merge a partial config and rebuild geometry. ### beginEdit ```typescript theme={null} beginEdit(): void; ``` Start one live edit; a second `beginEdit` before `endEdit` keeps the first start. ### endEdit ```typescript theme={null} endEdit(): { undo: () => void; redo: () => void; } | undefined; ``` End the live edit; its exact undo and redo, or undefined when no option changed. ### getEditAnchors ```typescript theme={null} getEditAnchors(): EditAnchor[]; ``` Draggable handles, in the element's local frame (y = elevation): - one `xz` `vertex` anchor per outer-ring point (`v:outer:<i>`), and - one `xz` `edge-mid` anchor per edge midpoint (`m:outer:<i>`, edge `i`→`i+1`) — the insert-vertex affordance (Mapbox/nebula midpoint handles). ### applyEditAnchor ```typescript theme={null} applyEditAnchor(id: string, position: [ number, number, number ]): void; ``` Write back a previewed handle drag (anchors are in local frame, y = elevation): - `v:outer:<i>` moves outer-ring point i to the dropped (x, z), and - `m:outer:<i>` inserts a new point on edge i→i+1 at the dropped (x, z); the two edges it makes inherit edge i's lot-line class. ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` The parcel is a plan polygon and nothing else, so only `top` exports. ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` The parcel boundary (outer ring + excluded holes) as one region primitive. ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### dispose ```typescript theme={null} dispose(): void; ``` ## classifyLotLines Classify each parcel edge as `front` / `rear` / `side` from the designated street-facing front edge. ```typescript theme={null} function classifyLotLines(outerRing: PointXZ[], frontEdgeIndex: number): LotLineClass[]; ``` ## Types ### ISite ```typescript theme={null} interface ISite extends HasPropertySets { ogid?: string; labelName: string; type: ElementType.SITE; profile: PolygonProfile; lotLines?: LotLineClass[]; geoAnchor?: GeoAnchor; trueNorthDeg?: number; color: number; elevation: number; } ``` ### LotLineClass Zoning class of one parcel edge. ```typescript theme={null} type LotLineClass = 'front' | 'side' | 'rear' | 'custom'; ``` ### GeoAnchor WGS84 fix of the local origin. ```typescript theme={null} interface GeoAnchor { latitude: number; longitude: number; elevation?: number; } ``` Metres above sea level, if known. Carried through, never used by the projection. ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`EditAnchor`](/floorkit/api/architecture/op-element#editanchor) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`HasPropertySets`](/floorkit/api/architecture/op-element#haspropertysets) * [`IPlaceable`](/floorkit/api/architecture/op-element#iplaceable) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PointXZ`](/floorkit/api/primitives/polygon-maths#pointxz) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`Vec3`](/floorkit/api/architecture/wall#vec3) # World graph Source: https://docs.ploterr.com/floorkit/api/world/world-graph Register an element in the kernel world graph. ## Import ```typescript theme={null} import { addElementToWorldGraph, addOpenElementToWorldGraph } from '@opengeometry/floorkit'; ``` ## addElementToWorldGraph ```typescript theme={null} function addElementToWorldGraph( graph: WorldGraph, element: WorldGraphElement, placement: WorldGraphNodePlacement, ): ElementWorldGraphReport; ``` ## addOpenElementToWorldGraph ```typescript theme={null} function addOpenElementToWorldGraph( graph: WorldGraph, element: OpenElement, placement: WorldGraphNodePlacement, ): OpenElementWorldGraphReport; ``` ## Types ### WorldGraphElement ```typescript theme={null} type WorldGraphElement = THREE.Object3D & PlanVectorExportable; ``` ### WorldGraphNodePlacement ```typescript theme={null} interface WorldGraphNodePlacement { id: string; parent?: string; } ``` ### ElementWorldGraphReport ```typescript theme={null} interface ElementWorldGraphReport { nodeId: string; partNodeIds: string[]; unsupportedPartIds: string[]; } ``` ### OpenElementWorldGraphReport ```typescript theme={null} type OpenElementWorldGraphReport = ElementWorldGraphReport; ``` ## Related * [`OpenElement`](/floorkit/api/architecture/open-element) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) # ZoningEnvelope Source: https://docs.ploterr.com/floorkit/api/world/zoning-envelope ZoningEnvelope shows the volume in which you can build on a site. It draws the result of `buildableEnvelope()` as translucent solids. ## Import ```typescript theme={null} import { ZoningEnvelope } from '@opengeometry/floorkit'; ``` ## Example ```typescript theme={null} import { buildableEnvelope, ZoningEnvelope, type Site, type ZoningRuleSet, } from '@opengeometry/floorkit'; export function displayEnvelope(site: Site, rules: ZoningRuleSet) { const envelope = buildableEnvelope(site.getOPConfig(), rules); return new ZoningEnvelope({ envelope, opacity: 0.18 }); } ``` ## Notes * Values are in metres. `opacity` is between 0 and 1. * The envelope does not recalculate. After a change of the site or the rules, call `buildableEnvelope()` again and then `setEnvelope()`. * Without a height-cap rule, give `maxHeight` to `buildableEnvelope()`. * The envelope is a design aid. It is not a legal approval. * Start the geometry kernel before you create this object. See [`Floorkit`](/floorkit/api/runtime/floorkit). **Inheritance:** `extends OPElement implements PlanVectorExportable` ## Constructor ```typescript theme={null} constructor(config?: Partial); ``` 0–1. ## Properties Read-only. ## Methods ### editableSchema ```typescript theme={null} static editableSchema(): OPEditableSchema; ``` ### setEnvelope ```typescript theme={null} setEnvelope(envelope: BuildableEnvelope): void; ``` ### getOPConfig ```typescript theme={null} getOPConfig(): ZoningEnvelopeOptions; ``` ### setOPConfig ```typescript theme={null} setOPConfig(config: Partial): void; ``` ### setOPGeometry ```typescript theme={null} setOPGeometry(): void; ``` ### setOPMaterial ```typescript theme={null} setOPMaterial(): void; ``` ### getExportRoots ```typescript theme={null} getExportRoots(view: PlanExportView): THREE.Object3D[]; ``` ### toDrawing ```typescript theme={null} toDrawing(view: PlanExportView): DrawingPrimitive[]; ``` The grade plate region(s), styled `ZONING_ENVELOPE` (phantom dash on paper). ### dispose ```typescript theme={null} dispose(): void; ``` ## Types ### ZoningEnvelopeOptions ```typescript theme={null} interface ZoningEnvelopeOptions { ogid?: string; labelName: string; type: ElementType.ZONING_ENVELOPE; envelope: BuildableEnvelope; color: number; opacity: number; } ``` ### BuildableEnvelope ```typescript theme={null} interface BuildableEnvelope { heightCap?: number; plates: EnvelopePlate[]; } ``` Metres above grade, when a `height-cap` rule exists. ### EnvelopePlate ```typescript theme={null} interface EnvelopePlate { elevation: number; height: number; footprints: PolygonProfile[]; } ``` World Y of the plate's floor. Zero or more disjoint regions (a deep inset can split a waisted lot). ## Related * [`OPElement`](/floorkit/api/architecture/op-element) * [`DrawingPrimitive`](/floorkit/api/drawings/draw-project-view#drawingprimitive) * [`ElementType`](/floorkit/api/architecture/op-element#elementtype) * [`OPEditableSchema`](/floorkit/api/architecture/op-element#opeditableschema) * [`OPGeometry`](/floorkit/api/architecture/opening#opgeometry) * [`PlanExportView`](/floorkit/api/shapes/cylinder-shape#planexportview) * [`PlanVectorExportable`](/floorkit/api/shapes/cylinder-shape#planvectorexportable) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) # Zoning evaluation Source: https://docs.ploterr.com/floorkit/api/world/zoning-evaluation Calculate the buildable volume and test a mass against the rules. ## Import ```typescript theme={null} import { evaluateZoning, buildableFootprint, buildableEnvelope, resolvedLotLines, edgeSetbacks, requiredInset, heightCapOf, planeOffsetAt, } from '@opengeometry/floorkit'; ``` ## evaluateZoning Evaluate `rules` for `masses` on `site`. ```typescript theme={null} function evaluateZoning(site: SiteLike, masses: MassLike[], rules: ZoningRuleSet): ZoningReport; ``` ## buildableFootprint The buildable plate(s) at `atHeight` above grade — the parcel inset per edge by the rules, holes grown by theirs. ```typescript theme={null} function buildableFootprint( site: SiteLike, rules: ZoningRuleSet, atHeight?: number, ): PolygonProfile[]; ``` ## buildableEnvelope Stack buildable plates from grade to the height cap (or `opts.maxHeight`) in `bandHeight` bands; stops at the first band with no buildable region. ```typescript theme={null} function buildableEnvelope( site: SiteLike, rules: ZoningRuleSet, opts?: { bandHeight?: number; maxHeight?: number; }, ): BuildableEnvelope; ``` ## resolvedLotLines Resolved lot-line classes (every edge `side` when the site has none). ```typescript theme={null} function resolvedLotLines(site: SiteLike): LotLineClass[]; ``` ## edgeSetbacks Per-edge insets at `atHeight` above grade: one per outer edge, one list per hole. ```typescript theme={null} function edgeSetbacks( site: SiteLike, rules: ZoningRuleSet, atHeight?: number, ): { outer: number[]; holes: number[][]; }; ``` ## requiredInset Largest inset among the plane rules addressing `edgeClass` at `atHeight`. ```typescript theme={null} function requiredInset(rules: ZoningRuleSet, edgeClass: LotLineClass, atHeight: number): number; ``` ## heightCapOf Height cap above grade, when a `height-cap` rule exists (the smallest wins). ```typescript theme={null} function heightCapOf(rules: ZoningRuleSet): number | undefined; ``` ## planeOffsetAt Inset one plane rule demands at `atHeight` above grade. ```typescript theme={null} function planeOffsetAt(rule: EnvelopePlaneRule, atHeight: number): number; ``` ## Types ### SiteLike The structural shape the envelope needs — a `Site`'s config or a plain object. ```typescript theme={null} interface SiteLike { profile: PolygonProfile; lotLines?: LotLineClass[]; elevation?: number; } ``` ### ZoningReport ```typescript theme={null} interface ZoningReport { compliant: boolean; violations: ZoningViolation[]; failureClass: ZoningViolationType | null; metrics: ZoningMetrics; } ``` ### EnvelopePlaneRule ```typescript theme={null} interface EnvelopePlaneRule { kind: 'envelope-plane'; id: string; edgeClass: LotLineClass | 'all'; offsetAtGrade: number; startHeight?: number; slopeDeg?: number; } ``` Which lot lines the plane rises from. `all` = every outer edge and every hole edge. Horizontal setback from the lot line at grade, metres. Height above grade at which the plane starts leaning inward. Default 0. Plane slope from horizontal, degrees, in (0, 90]. Default 90 (vertical). ### ZoningViolation ```typescript theme={null} interface ZoningViolation { type: ZoningViolationType; message: string; ruleId?: string; massId?: string; masses?: [string, string]; edgeIndex?: number; edgeClass?: LotLineClass; current?: number; limit?: number; } ``` ### ZoningViolationType ```typescript theme={null} type ZoningViolationType = | 'missing-zoning-basis' | 'outside-parcel' | 'building-collision' | 'building-spacing' | 'exceeds-far' | 'exceeds-lot-coverage' | 'exceeds-volume-ratio' | 'exceeds-height' | 'setback-violation' | 'front-setback-violation' | 'envelope-violation'; ``` ### ZoningMetrics ```typescript theme={null} interface ZoningMetrics { siteArea: number; footprintArea: number; grossFloorArea: number; countedFloorArea: number; floorAreaRatio: number; siteCoverage: number; volumeRatio: number; maxHeight: number; perMass: Array< { id: string; } & MassQuantities >; } ``` Σ ground-touching footprints. GFA of the storeys whose area kind counts toward FAR. Tallest point above grade. ## Related * [`BuildableEnvelope`](/floorkit/api/world/zoning-envelope#buildableenvelope) * [`LotLineClass`](/floorkit/api/world/site#lotlineclass) * [`MassLike`](/floorkit/api/architecture/massing-helpers#masslike) * [`MassQuantities`](/floorkit/api/architecture/building#massquantities) * [`PolygonProfile`](/floorkit/api/primitives/polygon-maths#polygonprofile) * [`ZoningRuleSet`](/floorkit/api/world/zoning-rules#zoningruleset) # Zoning rules Source: https://docs.ploterr.com/floorkit/api/world/zoning-rules Write and check the rule set of a site. ## Import ```typescript theme={null} import { basicZoningRules, validateZoningRuleSet, ZONING_PRESETS } from '@opengeometry/floorkit'; ``` ## basicZoningRules ```typescript theme={null} function basicZoningRules(input: BasicZoningInput): ZoningRuleSet; ``` ## validateZoningRuleSet Every problem with a rule set, as `BmIssue`s rooted at `path` (so `validateBuildingModel` can nest it). ```typescript theme={null} function validateZoningRuleSet(rules: ZoningRuleSet, path?: string): BmIssue[]; ``` ## ZONING\_PRESETS ```typescript theme={null} const ZONING_PRESETS: { 'india-dcr-generic': { id: string; name: string; jurisdiction: string; rules: ( | { kind: 'ratio-cap'; id: string; quantity: 'floorAreaRatio'; max: number; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'ratio-cap'; id: string; quantity: 'siteCoverage'; max: number; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'front'; offsetAtGrade: number; quantity?: undefined; max?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'side'; offsetAtGrade: number; startHeight: number; slopeDeg: number; quantity?: undefined; max?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'rear'; offsetAtGrade: number; startHeight: number; slopeDeg: number; quantity?: undefined; max?: undefined; } | { kind: 'height-cap'; id: string; max: number; quantity?: undefined; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } )[]; countsTowardFAR: 'gross'[]; }; 'us-r-generic': { id: string; name: string; jurisdiction: string; rules: ( | { kind: 'ratio-cap'; id: string; quantity: 'floorAreaRatio'; max: number; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'ratio-cap'; id: string; quantity: 'siteCoverage'; max: number; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'front'; offsetAtGrade: number; quantity?: undefined; max?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'side'; offsetAtGrade: number; quantity?: undefined; max?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'rear'; offsetAtGrade: number; quantity?: undefined; max?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'front'; offsetAtGrade: number; startHeight: number; slopeDeg: number; quantity?: undefined; max?: undefined; } | { kind: 'height-cap'; id: string; max: number; quantity?: undefined; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } )[]; countsTowardFAR: 'gross'[]; }; 'de-baunvo-generic': { id: string; name: string; jurisdiction: string; rules: ( | { kind: 'ratio-cap'; id: string; quantity: 'siteCoverage'; max: number; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'ratio-cap'; id: string; quantity: 'floorAreaRatio'; max: number; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'ratio-cap'; id: string; quantity: 'volumeRatio'; max: number; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'all'; offsetAtGrade: number; startHeight: number; slopeDeg: number; quantity?: undefined; max?: undefined; } | { kind: 'height-cap'; id: string; max: number; quantity?: undefined; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } )[]; countsTowardFAR: ('gross' | 'core')[]; }; 'uk-plot-ratio-generic': { id: string; name: string; jurisdiction: string; rules: ( | { kind: 'ratio-cap'; id: string; quantity: 'floorAreaRatio'; max: number; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; min?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'front'; offsetAtGrade: number; quantity?: undefined; max?: undefined; startHeight?: undefined; slopeDeg?: undefined; min?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'side'; offsetAtGrade: number; startHeight: number; slopeDeg: number; quantity?: undefined; max?: undefined; min?: undefined; } | { kind: 'envelope-plane'; id: string; edgeClass: 'rear'; offsetAtGrade: number; startHeight: number; slopeDeg: number; quantity?: undefined; max?: undefined; min?: undefined; } | { kind: 'separation'; id: string; min: number; quantity?: undefined; max?: undefined; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; } | { kind: 'height-cap'; id: string; max: number; quantity?: undefined; edgeClass?: undefined; offsetAtGrade?: undefined; startHeight?: undefined; slopeDeg?: undefined; min?: undefined; } )[]; countsTowardFAR: 'gross'[]; }; }; ``` ## Types ### BasicZoningInput ```typescript theme={null} interface BasicZoningInput { id?: string; maxFAR?: number; maxCoverage?: number; maxHeight?: number; setbacks?: Partial>; minSeparation?: number; countsTowardFAR?: AreaKind[]; } ``` Ground footprint ÷ site area, 0–1. Metres above grade. Per lot-line-class setbacks at grade (vertical planes). ### ZoningRuleSet ```typescript theme={null} interface ZoningRuleSet { id: string; name?: string; jurisdiction?: string; rules: ZoningRule[]; countsTowardFAR?: AreaKind[]; } ``` Free text — "NYC R6", "Bengaluru BBMP 2020" — provenance lives with the consumer. Storey area kinds that count toward `floorAreaRatio`. Default `["gross"]`. ### ZoningRule ```typescript theme={null} type ZoningRule = RatioCapRule | EnvelopePlaneRule | HeightCapRule | SeparationRule; ``` ### RatioCapRule ```typescript theme={null} interface RatioCapRule { kind: 'ratio-cap'; id: string; quantity: 'floorAreaRatio' | 'siteCoverage' | 'volumeRatio'; max: number; } ``` `floorAreaRatio` = counted GFA ÷ site area; `siteCoverage` = ground footprints ÷ site area; `volumeRatio` = gross volume ÷ site area (BMZ). ### HeightCapRule ```typescript theme={null} interface HeightCapRule { kind: 'height-cap'; id: string; max: number; } ``` Metres above grade (the site elevation). ### SeparationRule ```typescript theme={null} interface SeparationRule { kind: 'separation'; id: string; min: number; } ``` Minimum clear distance between two masses whose vertical ranges overlap, metres. ## Related * [`AreaKind`](/floorkit/api/architecture/massing-helpers#areakind) * [`BmIssue`](/floorkit/api/architecture/validate-building-model#bmissue) * [`EnvelopePlaneRule`](/floorkit/api/world/zoning-evaluation#envelopeplanerule) * [`LotLineClass`](/floorkit/api/world/site#lotlineclass) # Changelog Source: https://docs.ploterr.com/floorkit/changelog What changed in each Floorkit release, newest first. Create and edit site massing with one project and one undo history, through commands and the plan and 3D editors. See [Site massing](/floorkit/playbooks/site-massing) for a worked study. ### Check these when you upgrade * **Breaking: create and edit a `Building` through a project.** Use `massBuildingCommand` with `ProjectManager.apply`, then edit through commands or the managed editors. Standalone `new Building({ profile, height })`, segments without storeys, `storeyHeight`, `Building.setStoreys`, `Building.addSegment`, `Building.removeSegment`, `regenerateStoreys`, `storeyHeightFor` and `STOREY_HEIGHT_BY_USE` are removed. See [Model a mass](/floorkit/guides/model-a-mass). * **Files with `showMassing` need 1.2.0.** Floorkit 1.1.0 refuses that new plan-view field. Files without it still open in both versions. Update custom saved-view validators to accept it. * **`BuildingEditCommand` has new members.** Add `replace-mass`, `add-building`, `remove-building` and `replace-site` to exhaustive switches. `ProjectFileError` also adds `building-in-use` and `site-in-use`; `storey-in-use` can now be reported by a mass edit. * **Recheck floor area against the site's grade and area definitions.** A storey entirely at or below grade is a basement. Basement, parking, core and mechanical areas do not count toward FAR unless the rules include them. Set `BmStorey.areaKind` explicitly where needed; pass `{ grade }` to `massQuantities` or `Building.quantities` for a site above or below zero. * **Sections and elevations exclude masses.** A view that selects only a mass gives `empty-view`. Use a massing plan and 3D view, or convert the mass to a shell for physical cuts. * **Masses use colours by use when no colour is set.** Set `color` on the mass or pass `massColors: {}` to keep the previous default. Plan views and sheets can now fill mass parts with their colours; update screenshot or PDF expectations accordingly. * **The plan editor reports deselection after undo.** A select listener must accept `undefined` when undo removes the selected element. A storey-setting edit in a mass building also reports the changed objects. * **Custom editable targets can supply `activePart`.** The editors preserve the selected mass part during gestures and after saving or undoing. Hosts with custom `EditableTarget` implementations can provide its saved part id alongside `selectPart`. ### Added * **Mass, building and site commands.** `replace-mass`, `add-building`, `remove-building` and `replace-site` work through `BuildingSession` and `ProjectManager`, including batches, undo and redo. `massBuildingCommand` creates a mass from an outline; `massSegmentHeightCommand` changes a part's height in whole storeys. * **Plan and 3D mass editing.** Select a part, reshape, move, turn or delete it. Push/pull changes its height in 3D. Each completed gesture saves one command; edits that break a saved view or remove occupied storeys are refused. * **Draw a building in the plan.** `ManagedPlanEditor.startMassDrawing` draws and saves a new mass as one step. `startOutlineDrawing` returns an outline for an app's own creation flow. Escape cancels without saving. * **Site massing on plans and sheets.** `showMassing: true` draws mass parts, the parcel and the at-grade setback line in plan views, sheets and PDF exports. `ProjectManager.setBuildableEnvelope` shows the supplied envelope in 3D; it is a display setting, not part of a saved project. * **Area kinds and colours for mass parts.** Save `BmStorey.areaKind` and optional `BmMassSegment.color`. `GUIEditor.onDrag` exposes live figures during supported gestures before the command is saved. ### Changed * Mass, building and site edits preserve unaffected objects. A storey-setting edit in a mass building rebuilds the affected storey and mass. * A rebuilt mass keeps its active part. Height drags keep materials and unchanged parts across geometry writes. ### Fixed * Setback checks measure clearance edge to edge, including concave parcels. * Basement classification uses site grade when calculating floor area ratios. * Editor keyboard focus and selection grips recover correctly after mass edits and undo. Plan move/turn feedback works when an app loads another copy of Three.js. One object for an open project, commands for each element of a storey, and edits that stay fast in a large building. ### Check these when you upgrade * **The mouse wheel pans the 2D plan and the sheet.** In 1.0.0 it zoomed. Call `setNavigation(MOUSE_NAVIGATION)` on the view to keep the zoom. See [`createModelRenderer`](/floorkit/api/views/create-model-renderer#notes). * **Install `opengeometry@^2.0.18`.** Floorkit needs 2.0.18 or later. * **Selection and hover are blue.** Set `--op-accent: #000000` on the editor container to keep the black look. * **Drawing text uses Paper Mono.** On screen and in SVG, load Paper Mono in your page, or the text shows in Courier. A PDF needs no font. To keep the old look, set `fontFamily` in the typography of the sheet style and in the dimension styles. * **Editor readouts use the system monospace font.** Set `--op-ui-font` on the editor container for a different font. * **TypeScript: two unions are larger.** `BuildingEditCommand` has new kinds, and the `kind` of a change event can be `restore`. A `switch` that must cover each member needs the new cases. * **`onChange` reports are more exact.** A moved block gives `move` and an added element gives `create`. The labels of a door or a window that moves off its wall or to a different wall changed. `ogids` names only the elements that the step changed. * **Objects stay the same after an edit.** An edit builds again only what changed, so the other objects are not new objects. Do not write `position`, `rotation` or `scale` on a Floorkit object below a storey group: use an edit command. Do not write into a drawing that the plan editor made. * **The model check is stricter.** A column, a beam or a railing with a number that the build cannot use is refused when a file opens. A stair with more than 1,000 risers is refused. ### Added * **[`ProjectManager`](/floorkit/api/editor/project-manager).** One object holds an open project: the building, the views, the sheets and the markers. It has one undo history, it mounts a plan editor, it connects a 3D editor, and it exports sheets. * **Commands for the elements of a storey.** `add-element`, `replace-element` and `remove-element` change slabs, roofs, spaces, stairs, columns, beams, railings and blocks. * **Commands for block definitions and for storeys.** `add-asset`, `replace-asset`, `remove-asset`, `add-storey`, `remove-storey` and `replace-storey-settings`. * **A batch command.** `{ kind: 'batch', commands: [...] }` applies several commands as one step with one undo. When one command fails, nothing changes. * **A change event names what changed.** The events of `BuildingSession.subscribe` and `ProjectManager.subscribe` have an optional `changed` list of live ids, so your app can refresh only those elements. * **`setNavigation`.** Set how the wheel and the mouse buttons move the 2D plan and the sheet. * **A block moves and turns in both editors.** ### Changed * **Edits are fast in a large building.** An edit builds again only the elements that changed, and the plan editor draws again only those. In a storey of 160 rooms, a slab move takes about 5 ms (before about 2.2 s), and a door move takes about 46 ms (before about 2.2 s). * **The first plan draw of a large storey is faster.** At 160 rooms it takes about 0.24 s. It took about 64 s. * **More moves of a wall at a corner are accepted.** * **A body up to 1 km from the origin of the model draws** in a plan, a section and an elevation. * **A selected wall in the 2D plan editor shows its outline,** with no bounding box and no rotate dot. ### Fixed * A dragged wall end joins the wall it is released on. * The wall outline stays whole after a wall drag. * A drag that is refused or cancelled puts the element back, and the 2D plan shows the saved plan again. * The undo of a live edit puts the element back exactly. * The plan symbol of a door or a window no longer shows in the 3D view. * A wall cuts its holes again when only its axis moves. * `LineDimension` prints no debug logs. * A door, a window or a wall opening disposes its kernel bodies. Floorkit 1.0 launched. # How Floorkit works Source: https://docs.ploterr.com/floorkit/concepts/architecture One building document goes in. A 3D model, drawings and export files come out. Floorkit keeps your building as one document. All other things are made from that document: the 3D model, the plan, the sheets and the export files. Thus you save one thing, and all views agree. ## The data flow ```mermaid theme={null} flowchart LR A[Building model] --> B[Session] B --> C[3D model in your scene] B --> D[Views: plan, section, elevation] D --> E[Sheets] E --> F[PDF] C --> G[GLB] A --> H[Project file] ``` ## The four parts | Part | What it does | Main API | | - | - | - | | Building model | Holds the building as typed JSON data | [`validateBuildingModel`](/floorkit/api/architecture/validate-building-model) | | Session | Builds the geometry, applies edits, keeps undo and redo | [`BuildingSession`](/floorkit/api/editor/building-session) | | Views and sheets | Make drawings from the model and place them on paper | [`drawProjectView`](/floorkit/api/drawings/draw-project-view) | | Exports | Give you a PDF, a GLB or a project file | [`exportProjectPdf`](/floorkit/api/exports/export-project-pdf) | ## The document is the source The building model stores the design. A project file also keeps your named views and sheets. Floorkit makes the geometry again when you open that file. * A change to the document changes the 3D model and each drawing. * A change to a mesh in your scene changes nothing in the document. Do not edit meshes. * You choose the id of each wall, door and room. Floorkit keeps these ids, so your app can find an element again. ## Two ways to make geometry **With a document.** You write a [building model](/floorkit/concepts/building-model). Use `ProjectManager` for a product with editors, views, sheets, saving, and one history. It owns the `BuildingSession` that checks commands and builds geometry. Use a session directly when your app manages the other project data separately. See [Save and reopen](/floorkit/guides/save-and-reopen). **With element classes.** You create objects such as `new Wall(...)` or `new Slab(...)` and add them to your scene. Use this way for one object or a small test. Your app must keep these objects, and Floorkit does not save them. ## What your app does Floorkit does not make a page, a toolbar or a file dialog. Your app does these things: * It makes the Three.js scene, the camera and the renderer. * It starts the geometry kernel one time. See [Installation](/floorkit/installation). * It stores project text from `manager.save()` or, for the direct-session route, `saveProject`. * It connects its AI provider to validation and supported commands. See [Build with agents](/floorkit/agents/index). ## Next Learn the structure of the document. Learn how a session applies an edit. # Building model Source: https://docs.ploterr.com/floorkit/concepts/building-model The typed JSON document that holds your building. A building model is one JSON document. It describes the building with numbers and ids, not with meshes. Your app, your server and an AI agent can all read it and write it. ## Structure A model contains buildings. A building contains storeys. A storey contains the elements. ```mermaid theme={null} flowchart TD M[Building model] --> B[Building] B --> S[Storey] S --> W[Walls] W --> O[Doors, windows, voids] S --> P[Slabs and roofs] S --> C[Columns] S --> T[Stairs and railings] S --> R[Spaces] ``` | List in a storey | What it holds | | - | - | | `walls` | Walls. Each wall holds its `openings`. | | `slabs` | Floor plates, floors and ceilings | | `roofs` | Flat, gable, hip and shed roofs | | `columns` | Columns and piers | | `stairs`, `railings` | Stairs and their railings | | `spaces` | Rooms with a name and an area | | `instances` | Furniture and equipment from your own assets | | `unhostedOpenings` | Doors and windows that are not in a wall | | `modelObjects` | Basic shapes: lines, arcs, polygons and boxes | A building can also have a `mass`. A mass is the building as a simple volume, for the early stage of a design. See [Model a mass](/floorkit/guides/model-a-mass). ## A small model ```typescript theme={null} import type { BuildingModel } from '@opengeometry/floorkit'; export const model: BuildingModel = { schema: 'floorkit.building-model', version: 3, units: 'meters', upAxis: 'y', id: 'small-house', buildings: [ { id: 'building', storeys: [ { id: 'ground', elevation: 0, height: 3, walls: [ { id: 'south-wall', points: [ [0, 0], [6, 0], ], thickness: 0.2, openings: [{ id: 'entry-door', kind: 'door', offset: 2, width: 0.9, height: 2.1 }], }, ], }, ], }, ], }; ``` ## The header fields | Field | Value | Meaning | | - | - | - | | `schema` | `'floorkit.building-model'` | Tells Floorkit what the document is | | `version` | `3` | Use `3` for a new model | | `units` | `'meters'` | All lengths are in metres | | `upAxis` | `'y'` | The Y axis points up | | `id` | Your text | The id of the model | ## Rules that keep a model valid * Each id is unique. You choose the ids and Floorkit keeps them. * A wall is a line of `[x, z]` points. For a closed room, set `closed: true`. Do not repeat the first point. * A door or a window is an entry in the `openings` of its wall. Its `offset` is the distance from the first wall point, along the wall. * The `elevation` of a storey is its height in the world. The elements of the storey use offsets from that height. ## Check a model `validateBuildingModel` reads a document and returns each problem with a path, a code and a message. It does not need the geometry kernel, so you can also run it on a server. ```typescript theme={null} import { validateBuildingModel } from '@opengeometry/floorkit'; export function check(input: unknown): boolean { const result = validateBuildingModel(input); for (const issue of result.errors) console.error(issue.path, issue.code, issue.message); return result.ok; } ``` This check makes the model a good target for an AI agent. The agent writes the document. If the check finds a problem, give the path and the message back to the agent and let it correct the document. ## What is not in the model * Meshes and other geometry. Floorkit builds them from the model. * Views and sheets. They are in the [project file](/floorkit/guides/save-and-reopen), next to the model. * Undo history. It belongs to the session. ## Agent proposals Use the [agent building workflow](/floorkit/agents/agent-building-workflow) to turn unknown JSON into an editable project. Preserve IDs and return validation issues to the agent. A valid document can still fail during geometry construction; keep the existing project until its replacement builds. The TypeScript contract is not a downloadable JSON Schema or an LLM tool definition. Your app supplies its AI provider and tool inputs. [Build with agents](/floorkit/agents/index) explains the documentation and application boundaries. ## Next Learn where each number is measured from. See each field of the document. # Coordinates and units Source: https://docs.ploterr.com/floorkit/concepts/coordinates-and-units Metres for the model, millimetres for paper, pixels only for the screen. Floorkit uses three kinds of numbers. Keep them apart and your geometry is always in the correct place. | Space | Coordinates | Unit | | - | - | - | | Model | `[x, y, z]`, the Y axis is up | Metres | | Plan | `[x, z]`, seen from above | Metres | | Paper | `{ x, y }` from the top-left corner of the sheet | Millimetres | | Screen | Pointer and canvas positions | Pixels | ## Heights come from the storey A storey has an `elevation`. This is the only height in the model that is measured in the world. Each element in the storey gives its height as an offset from that elevation. | Field | Measured from | | - | - | | Storey `elevation` | The world zero | | Slab `baseOffset` | The storey elevation, to the bottom of the slab | | Roof `baseOffset` | The storey elevation, to the eave | | Column `baseOffset` | The storey elevation, to the base | | Door `baseHeight` | The base of its wall | | Window `sillHeight` | The base of its wall, to the bottom of the glass | | Railing `path` | The world zero. Each point is `[x, y, z]`. | Example: a storey has `elevation: 3.2`. A slab in it has `baseOffset: 0.1`. The bottom of the slab is at 3.3 metres. ## Positions along a wall The `offset` of a door or a window is a distance along its wall. It starts at the first point of the wall and follows each segment. On a closed wall, it also follows the last segment back to the first point. ## Angles | Field | Unit | Direction | | - | - | - | | `yawDeg` of a free opening | Degrees | Zero points to +X. A positive angle turns to +Z. | | Roof `pitch` | Degrees | The slope of the roof | | Stair `rotation` | Degrees | Counter-clockwise, seen from above | ## Drawing scale A viewport on a sheet has a `scale`. The scale is paper size divided by real size. | Scale | `scale` value | One metre on paper | | - | - | - | | 1:50 | `0.02` | 20 mm | | 1:100 | `0.01` | 10 mm | | 1:200 | `0.005` | 5 mm | ## Pointer positions A pointer position is in pixels. Change it to model metres before you make an edit command. The [plan editor](/floorkit/guides/add-a-plan-editor) and the [3D editor](/floorkit/guides/add-a-3d-editor) do this for you. ## Next Learn how an edit changes the model. Learn how the model becomes a drawing. # Views and sheets Source: https://docs.ploterr.com/floorkit/concepts/drawings A view says what to draw. A sheet says where it goes on paper. Floorkit makes drawings from the building model. You do not draw lines by hand. When the model changes, each drawing changes with it. ## Views A view is a small piece of saved data. It says what to draw, and it does not hold lines. | View | You give | You get | | - | - | - | | Plan | A storey and a cut height | A floor plan with walls cut at that height | | Section | A cut line, a look side and a depth | A vertical cut through the building | | Elevation | A point, a look direction and a crop | An outside face of the building | | Isometric | A camera | A 3D image that you place on a sheet | Plans, sections and elevations are vector drawings: lines, arcs, regions and text in metres. An isometric view is an image. ```typescript theme={null} import type { SavedDrawingView } from '@opengeometry/floorkit'; export const groundPlan: SavedDrawingView = { id: 'ground-plan', name: 'Ground floor plan', kind: 'plan', storeyId: 'ground', cutHeightAboveStorey: 1.2, }; ``` ## Sheets A sheet is a page with a size in millimetres. A viewport puts one view on the sheet at a scale. A title block shows the sheet number and the project name. ```mermaid theme={null} flowchart LR V[View: ground plan] --> P1[Viewport at 1:50 on sheet A-101] V --> P2[Viewport at 1:100 on sheet A-001] ``` One view can be on many sheets. Each viewport has its own frame and its own scale. ## The project A project holds the three things together: the building model, the views and the sheets. You save and open the project as one file. | Field | Holds | | - | - | | `buildingModel` | The building | | `views` | The saved views | | `paper.sheets` | The sheets and their viewports | ## Get the linework [`drawProjectView`](/floorkit/api/drawings/draw-project-view) gives you the vector drawing of one view. Use it to show a plan in your own canvas or to measure a drawing. ## What Floorkit draws today * Plans cut straight walls at the cut height. Doors, windows and stairs show with their plan symbols. * Sections use a straight cut line. Elevations look at the building from outside. * A plan and an elevation draw a body up to 1 km from the origin of the model. A section draws a body up to about 980 m from the origin. When Floorkit cannot draw a body, it gives an error. * If Floorkit cannot draw a view, it gives an error with a code. It does not draw something different in its place. ## Next Save a plan and get its linework. Put a view on an A3 page at 1:50. # Editing and history Source: https://docs.ploterr.com/floorkit/concepts/editing-and-history Each edit is a command. A session checks it, builds it and keeps it for undo. In Floorkit, the user does not change a mesh. The user sends a command, such as "move this door to 2.5 metres". A `BuildingSession` applies the command to the building model and builds the geometry again. This gives you three things for free: a model that is always valid, undo and redo, and a 2D view and a 3D view that always agree. ## What a session does with a command The session checks the command and the model that results from it. It builds the new geometry apart from your scene. Walls join again and openings cut again. If the build is good, the session puts the new geometry below `session.root` and adds the edit to the history. If one step fails, the session throws an error and changes nothing. The old model, the old geometry and the history stay as they were. ## Send a command ```typescript theme={null} import type { BuildingSession } from '@opengeometry/floorkit'; export function moveDoor(session: BuildingSession): void { session.apply( { kind: 'move-hosted-opening', buildingId: 'building', storeyId: 'ground', wallId: 'room-walls', openingId: 'entry-door', offset: 2.5, }, session.revision, ); } ``` ## The commands | Command `kind` | What it does | | - | - | | `create-wall`, `delete-wall` | Adds or removes a wall | | `move-wall-point` | Moves one point of a wall | | `move-network-wall-end` | Moves one end of one wall segment off its corner | | `transform-wall`, `translate-network-wall` | Moves or turns a wall | | `add-hosted-opening`, `remove-hosted-opening` | Adds or removes a door or a window in a wall | | `move-hosted-opening` | Moves a door or a window along its wall | | `rehost-hosted-opening` | Moves a door or a window to a different wall | | `detach-hosted-opening`, `rehost-free-opening` | Takes an opening out of a wall, or puts it in | | `add-free-opening`, `move-free-opening`, `remove-free-opening` | Works with a door or a window that is not in a wall | | `transform-slab`, `transform-roof`, `transform-stair`, `transform-space` | Moves or turns one element | | `reshape-slab`, `reshape-roof`, `reshape-stair`, `reshape-space` | Changes the shape of one element | | `add-element`, `replace-element`, `remove-element` | Adds, replaces or removes one element of a storey | | `add-asset`, `replace-asset`, `remove-asset` | Adds, replaces or removes one asset | | `add-storey`, `remove-storey`, `replace-storey-settings` | Adds or removes a storey, or sets its settings | | `replace-mass` | Sets the mass of a building and all its storeys | | `add-building`, `remove-building` | Adds or removes a whole building | | `replace-site` | Sets or removes the site and its zoning rules | | `add-model-object`, `replace-model-object`, `remove-model-object` | Adds, replaces or removes one basic shape | | `batch` | Applies several commands as one step | See each field in the [`BuildingEditCommand`](/floorkit/api/editor/building-session#buildingeditcommand) type. ## Add, replace and remove `add-element`, `replace-element` and `remove-element` name a `list` of the storey: `slabs`, `roofs`, `spaces`, `stairs`, `columns`, `beams`, `railings` or `instances`. An `add` gives the new `element`. A `replace` gives the `elementId` and the full new `element`. A `remove` gives only the `elementId`. ```typescript theme={null} import type { BmColumn } from '@opengeometry/floorkit'; export function replaceColumn(session: BuildingSession, column: BmColumn): void { session.apply( { kind: 'replace-element', buildingId: 'building', storeyId: 'ground', list: 'columns', elementId: column.id, element: column, }, session.revision, ); } ``` An asset is the definition that an instance shows. The asset commands have an `assetId`, and `add-asset` and `replace-asset` have the `asset`. `add-storey` has the full `storey`. `remove-storey` has its `storeyId`. `replace-storey-settings` has the `storeyId` and the `settings`: the `name`, the `elevation`, the `height` and the `usage` together. `replace-mass` has the `mass`, or no `mass` to remove it, and the settings of each storey of the building in the new order. A storey id that stays keeps its elements. `add-building` has the full `building`; `remove-building` has its `buildingId`. `replace-site` has the full `site`, or no `site` to remove it. The session rejects these commands in these conditions: * An `add` with an id that exists: an element in its list, an asset in the model, a storey in its building. * A `replace` or a `remove` with an id that does not exist. * A `replace-element` whose new element has a different id. * A `remove-asset` of an asset that an instance uses. * A `remove-storey` of the last storey of a building. * A `replace-mass` that removes a storey that holds an element. * An `add-building` with an id that any object of the model has, and a `remove-building` of the last building. * A `replace-site` with no `site` when the model has none. A session does not know the views of your project. A [`ProjectManager`](/floorkit/api/editor/project-manager) does, and it also rejects a `remove-storey` or a `replace-mass` that removes a storey that a plan view names (`storey-in-use`), a `remove-building` of a building that a saved view shows (`building-in-use`), and a `replace-site` that removes a site that a view lists (`site-in-use`). ## Doors and windows in walls A door, a window or a hole fits on its wall when its whole cut lies between the joints of that wall. Then the wall has a hole for it. Floorkit uses this one fit rule in the build, in the commands and in the drags. `add-hosted-opening` puts a door or a window at an `offset` along a wall. `move-hosted-opening` and `rehost-hosted-opening` move a door, a window or a hole to an `offset`. The opening must fit at that offset, or the session rejects the command. The issue `opening-does-not-fit` of the error gives the offsets where the opening fits and the nearest one. `add-hosted-opening` and `rehost-free-opening` also reject an opening over the seam of a T stem; the offsets in the issue then keep clear of the seams. These commands need the geometry kernel: `applyBuildingEdit` rejects them with `resolved-edit-required`. It also rejects a `move-wall-point` of any wall in a storey with hosted openings, because the move can make a door of another wall stop fitting. A saved model can have an opening that does not fit at its offset. Opening the model does not change it. The build draws the opening at its saved offset without a hole, and gives the warning `opening-does-not-fit` in `session.currentBuild.warnings`, with the same numbers. To repair it, move the opening to an offset where it fits. An edit does not change an opening that it does not carry. The session rejects an edit that makes an opening that fits stop fitting, or that puts the seam of a T stem under it. An opening that does not fit never blocks an edit. An edit of the wall of an opening carries the opening at the same distance from the start of its segment. A drag on a door in a drawing becomes a `move-hosted-opening` with [`resolveViewEditCandidate`](/floorkit/api/editor/resolve-view-edit-candidate). Give it the current build. The drag starts at the saved offset, and the door stops at the nearest place where it fits, as in the live editor. A drag that cannot move the door gives the error `invalid-drag`. ```typescript theme={null} import { resolveViewEditCandidate, type DrawingSourceRef, type ViewDrag, type ViewFrame, } from '@opengeometry/floorkit'; export function dragDoor( session: BuildingSession, frame: ViewFrame, hit: DrawingSourceRef, drag: ViewDrag, ): void { const revision = session.revision; const command = resolveViewEditCandidate( session.snapshot(), frame, hit, drag, { kind: 'along-host-wall' }, session.currentBuild, ); session.apply(command, revision); } ``` ## A batch of commands One change of the user can need several commands, such as a paste of several elements. Put them in one `batch`. The session applies the commands in order to one model, builds one time and adds one step to the history. One undo takes all of them back. ```typescript theme={null} export function addColumns(session: BuildingSession, columns: BmColumn[]): void { session.apply( { kind: 'batch', commands: columns.map((column) => ({ kind: 'add-element', buildingId: 'building', storeyId: 'ground', list: 'columns', element: column, })), }, session.revision, ); } ``` * A batch is all or nothing. If one command fails, `apply` throws the error of that command and the session changes nothing. The message starts with the place of the command, such as `command 3 of 6:`. * If one command has a wrong field or an unknown `kind`, the code is `invalid-command`, and the message starts with the place of the command too. * Each command works on the result of the commands before it. Thus a batch can add a storey and then add an element to that storey. * The model must be valid after each command, not only after the last one. For example, in a building with a mass, two commands change the heights of two storeys, and the storeys fit the mass only after the second command. The session rejects this batch at the first command. * The rules of each command stay the same in a batch. For example, a `remove-asset` of an asset that an instance uses fails. * A batch must have one command or more, and it cannot hold a batch. In TypeScript, a batch in a batch does not compile. A list with the type `BuildingEditCommand[]` does not go into a batch, because that type permits a batch. Give the list the type of its commands, such as `ElementListCommand[]`, or the type `BatchCommand['commands']`. * `session.lastEditCommand` gives the batch. ## The revision number A session has a `revision`. It goes up by one after each edit, undo or redo. `apply` takes the revision that the edit started from. If a different edit came first, the number does not agree and `apply` throws `stale-revision`. This stops an old drag from overwriting a newer change. Read the revision when the user starts the action. Do not read it again at the end. ## Undo and redo ```typescript theme={null} export function undoLast(session: BuildingSession): void { if (session.canUndo) session.undo(session.revision); } export function redoLast(session: BuildingSession): void { if (session.canRedo) session.redo(session.revision); } ``` A new edit clears the redo list. The history lives in the session and is not saved in the project file. ## Keep an edit fast A host app sends many edits. These rules keep each edit short. * Send one command through `apply` for one change of the user. If the change needs several commands, send one [`batch`](#a-batch-of-commands). After a command for one slab, roof, room, stair, column, beam, railing, block instance, loose door, loose window or basic shape, Floorkit builds again only that element. The other elements of the storey keep their geometry. After a command for a wall, or for a door or window in a wall, Floorkit joins the walls of the storey again and builds again only the walls, doors and windows that changed. A command that adds a storey or changes its settings builds that storey. A command that changes an asset, or the settings of a storey in a building with a mass, builds the whole building again. A batch builds again what its commands build, one time. * Do not call `session.snapshot()`, `manager.snapshot()`, `manager.save()`, `saveProject`, `new BuildingSession(model)`, `drawProjectView` or an export at each edit or at each frame of a drag. Each of them copies, writes or builds the whole model. Save on an action of the user or on a timer. * Do not open a new session for a change that a command covers, as `replaceBuilding` in [Start a building](/floorkit/guides/start-a-building#change-the-document) does. A new session builds the whole building and has an empty undo history. Open one to load or replace a whole building. * Read the new revision from what `apply` returns, and read the built element from `session.currentBuild.elementsById`. Do not keep an element object across a change: read the element again by its id after each change. A changed slab, roof, stair, column, beam, railing, space, basic shape, block or loose door or window is a new object, and Floorkit disposes the old object. A changed wall, and a door, a window or a hole on a wall, can stay the same object with new geometry, or can be a new object. Do not call `snapshot()` to see the result. `snapshot()` copies the whole model. * After a change, refresh only what the event names. The event of `session.subscribe` and of `manager.subscribe` has `changed`: the ids of the built elements that the step built again, added or removed. An element that the list does not name keeps its object. Read each id from `session.currentBuild.elementsById`, or from `session.currentBuild.modelObjectsById` for a basic shape. An id that is in neither map was removed. If `changed` is absent, the whole building was built again: refresh all. * When a step fails, the model and the revision stay as they were. If the failed step already changed built elements, Floorkit builds them again from the saved model and sends one event with the kind `restore` (the cause `restore` from the manager) and with `changed`. Refresh these elements as after a step. ## Doors and windows follow their wall When a wall moves, its doors and windows move with it and the wall is cut again. If the new wall is too short for an opening, Floorkit rejects the edit. It does not remove the opening without telling you. ## Interactive editors You do not have to write commands by hand for pointer actions. Floorkit has two editors that make the commands for you. Give the same session to both, and the plan and the 3D view show the same building. Edit walls and openings in a 2D plan. Edit the same building in your Three.js view. # Exports Source: https://docs.ploterr.com/floorkit/concepts/exports Take your design out of Floorkit in open formats. Your designs are not locked in. Floorkit gives you files in open formats, and each one is for a different reader. | Format | Status | Use it for | | - | - | - | | PDF | Available | Sheets that people read, print and sign | | GLB | Available | The 3D model in viewers, game engines and AR | | Project file | Available | Saving the design and opening it again | | IFC | Comes in the next release | BIM tools | | DXF | Comes in the next release | 2D CAD tools | ## PDF A PDF contains your sheets as vector linework, one page for each sheet. See [Export a PDF](/floorkit/guides/export-a-pdf). ## GLB A GLB contains the 3D model in one binary file. Floorkit makes standard Three.js objects, so you export them with the Three.js exporter. See [Export a GLB](/floorkit/guides/export-a-glb). ## Project file The project file is JSON text. It holds the building model, the views and the sheets. It is the only format that Floorkit opens again for editing. See [Save and reopen](/floorkit/guides/save-and-reopen). Because the file is typed JSON, your server can read it too. You can make a price list, an area report or a search index from it without a browser. ## IFC and DXF IFC export for building models and DXF export for drawings come in the next release. See [IFC](/floorkit/api/exports/ifc) and [DXF](/floorkit/api/exports/dxf). ## Export the saved state An export uses the building model that you give it. Before an export, put the current model of your session into the project: ```typescript theme={null} import type { BuildingSession, ProjectFile } from '@opengeometry/floorkit'; export function currentProject(project: ProjectFile, session: BuildingSession): ProjectFile { return { ...project, buildingModel: session.snapshot() }; } ``` An exported file is a design document. Check it in your own workflow before you use it for a permit or for construction. # Add a 3D editor Source: https://docs.ploterr.com/floorkit/guides/add-a-3d-editor Let your users select and move elements in your Three.js view. The 3D editor draws handles on top of your Three.js view. When you connect it to a project manager, each drag becomes one step in the history of the manager. The plan editors and the 3D editor of one manager always show the same building. ## Before you start * You have a project. See [Create a plan view](/floorkit/guides/create-a-plan-view). * You have a scene, a camera and camera controls, such as `OrbitControls`. ## Open the project Open the project in a `ProjectManager` and add `manager.root` to your scene one time. ```typescript theme={null} import type * as THREE from 'three'; import { GUIEditor, ProjectManager, type CameraControlsLike, type ProjectFile, } from '@opengeometry/floorkit'; export function openSceneProject(scene: THREE.Scene, project: ProjectFile): ProjectManager { const manager = new ProjectManager(project, { onError: (error) => console.error('Floorkit rejected the edit', error), }); scene.add(manager.root); return manager; } ``` ## Mount the editor `GUIEditor` needs the element that holds your canvas, your camera and your controls. It stops the controls while the user drags a handle. `bindSceneEditor` connects it to the project. ```typescript theme={null} export function mountSceneEditor( container: HTMLElement, camera: THREE.Camera, controls: CameraControlsLike, manager: ProjectManager, ): GUIEditor { const editor = new GUIEditor(container, camera, controls); manager.bindSceneEditor(editor); return editor; } ``` ## Update it in each frame Call `update()` before you render. The handles then follow the camera. ```typescript theme={null} export function renderFrame( editor: GUIEditor, renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, ): void { editor.update(); renderer.render(scene, camera); } ``` ## Edit a wall When the user selects a wall, the editor shows a line along the wall and these handles: | The user drags | Result | | - | - | | A dot at an end | Only that end moves. The other wall at the corner stays where it is. | | The dot at the middle | The wall moves sideways. The walls at its ends become longer or shorter. | | The dot at the centre | All segments of the wall move, with their doors and windows. | | The dot beside the centre dot | All segments of the wall turn about their centre. | An end that leaves a corner is free. An end released near the end of another wall joins that end, and the two walls make a corner. An end released near the body of another wall joins that wall. When an end leaves a corner inside one saved wall, Floorkit cuts the saved wall there and gives the new part its own id. The walls, their doors and their windows move while the user drags. When the user releases the pointer, Floorkit saves one edit command. Thus one drag is one step for undo. ## Edit a door or a window When the user selects a door or a window, the editor shows one dot on it. | The user drags the dot | Result | | - | - | | Along its wall | The door moves along the wall. | | Near a different wall | The door moves into that wall. | | More than 0.35 m from every wall | The door leaves its wall and becomes a loose door. | | On a loose door | The loose door moves. It does not go into a wall. | The door moves while the user drags, and the cut in the wall moves when the user releases the pointer. One drag is one edit command. ## Edit a slab, a roof, a room or a stair When the user selects one of these, the editor shows its handles. | Element | Handles | | - | - | | Slab | A dot at each corner moves that corner. A dot at the middle of an edge adds a corner. | | Roof | A dot at each corner moves that corner. | | Room | A dot at each corner moves that corner. | | Stair | A dot at the start point moves the stair. Three smaller dots set its width, the depth of its treads and the depth of its landing. | The 3D editor also has a handle for the height: the thickness of a slab, the eave of a roof, the height of a room and the base of a stair. The element changes while the user drags. One drag is one `reshape` command. ## Add undo and redo The manager has one history for the building, the views and the sheets. The undo key in the editor uses the same history. ```typescript theme={null} export function sceneActions(manager: ProjectManager) { return { undo: () => manager.undo(), redo: () => manager.redo(), }; } ``` `undo()` and `redo()` return the new revision, or `null` when there is no step. ## Remove the editor You made the editor, so you dispose it. Then remove the project. ```typescript theme={null} export function closeSceneEditor( scene: THREE.Scene, manager: ProjectManager, editor: GUIEditor, ): void { editor.dispose(); scene.remove(manager.root); manager.dispose(); } ``` ## Rules * The container must be the element that the canvas fills. The editor uses its size to place the handles. * An object with an `enabled` property is sufficient for `controls`. `OrbitControls` has one. * The plan editors and the 3D editor of one manager use the same `hostedOpeningDetachTolerance`. Set it in the options of the manager. * The manager does not dispose the `GUIEditor`. Dispose the editor before you dispose the manager. ## Without the manager If your app makes the `BuildingSession` itself, connect the editor with `bindManagedSceneEditor`. See the [bindManagedSceneEditor API](/floorkit/api/editor/bind-managed-scene-editor). ## Next See each member of the 3D editor. See each member of the manager and the project commands. # Add a column Source: https://docs.ploterr.com/floorkit/guides/add-a-column Put a column at a point on a storey. A column is a vertical member at one point. You give its position and its section. Its height is the storey height. ## Before you start You have a session from [Start a building](/floorkit/guides/start-a-building). ## Write the column This column is at `[0, 0]` and has a section of 0.3 by 0.3 metres. ```typescript theme={null} import { ColumnShape, type BmColumn, type BuildingSession, type ElementListCommand, } from '@opengeometry/floorkit'; export const cornerColumn: BmColumn = { id: 'column-a1', shape: ColumnShape.RECTANGULAR, center: [0, 0], baseOffset: 0, width: 0.3, depth: 0.3, }; ``` ## Add it to the storey A column is an entry in the `columns` list of its storey. ```typescript theme={null} export function addColumn(session: BuildingSession): void { session.apply( { kind: 'add-element', buildingId: 'building', storeyId: 'ground', list: 'columns', element: cornerColumn, }, session.revision, ); } ``` The `add-element` command adds the column to the list, and the session builds it. You keep the session and its undo history. The command rejects an id that the list has. ## Sections | `shape` | You give | | - | - | | `ColumnShape.RECTANGULAR` | `width` and `depth` | | `ColumnShape.CIRCULAR` | `radius` | | `ColumnShape.CUSTOM` | `customProfile`, your own outline | In a JSON file, write the shape as text: `"RECTANGULAR"`. ## Rules * `center` is `[x, z]` in metres. `rotation` turns the section, in degrees. * `baseOffset` is the distance from the storey elevation to the base of the column. * The height is the storey height. Set `height` to change it. * A column is geometry. Floorkit does not calculate loads. ## A grid of columns Make one entry for each position. Give each column its own id. ```typescript theme={null} export function columnGrid(columns: number, rows: number, spacing: number): BmColumn[] { return Array.from({ length: columns * rows }, (_, index) => ({ ...cornerColumn, id: `column-${index + 1}`, center: [(index % columns) * spacing, Math.floor(index / columns) * spacing], })); } ``` A grid is a change of many elements at one time. Send it as one `batch`. The session applies the commands of a batch in order and builds one time. This batch removes the corner column and adds the six columns of the grid. ```typescript theme={null} export function addColumnGrid(session: BuildingSession): void { const grid: ElementListCommand[] = columnGrid(3, 2, 3).map((column) => ({ kind: 'add-element', buildingId: 'building', storeyId: 'ground', list: 'columns', element: column, })); session.apply( { kind: 'batch', commands: [ { kind: 'remove-element', buildingId: 'building', storeyId: 'ground', list: 'columns', elementId: cornerColumn.id, }, ...grid, ], }, session.revision, ); } ``` One batch is one step: one undo takes the grid away. If one command of the batch fails, the session changes nothing. See [A batch of commands](/floorkit/concepts/editing-and-history#a-batch-of-commands). ## Next Give a name and an area to a space. See each option of a column. # Add a door Source: https://docs.ploterr.com/floorkit/guides/add-a-door Put a door in a wall. The wall gets a real opening. A door belongs to a wall. You give its position along the wall and its size. Floorkit cuts the opening and adds the frame, the panel and the plan symbol. ## Before you start Your building has the room `room-walls` from [Draw a wall](/floorkit/guides/draw-a-wall). ## Add the door The centre of this door is 2 metres from the first point of the wall, on the first side of the room. ```typescript theme={null} import type { BuildingSession } from '@opengeometry/floorkit'; export function addDoor(session: BuildingSession): void { session.apply( { kind: 'add-hosted-opening', buildingId: 'building', storeyId: 'ground', wallId: 'room-walls', opening: { id: 'entry-door', kind: 'door', offset: 2, width: 0.9, height: 2.1, doorQuadrant: 1, }, }, session.revision, ); } ``` ## Set the swing `doorQuadrant` sets the hinge side and the swing side. Stand at the first point of the wall and look along the wall. | `doorQuadrant` | Hinge | Swings to your | | - | - | - | | `1` | Near jamb, at the smaller `offset` | Left | | `2` | Far jamb, at the larger `offset` | Left | | `3` | Near jamb | Right | | `4` | Far jamb | Right | `swingAngleDegrees` sets the angle of the open door panel. ## Rules * `offset`, `width` and `height` are in metres. * The full door, with its frame, must fit in the wall. It must not overlap a different opening. * `baseHeight` lifts the threshold above the base of the wall. The default is 0. * If the door does not fit, `apply` throws an error and the building does not change. ## Move or remove the door ```typescript theme={null} export function moveDoor(session: BuildingSession): void { session.apply( { kind: 'move-hosted-opening', buildingId: 'building', storeyId: 'ground', wallId: 'room-walls', openingId: 'entry-door', offset: 3, }, session.revision, ); } export function removeDoor(session: BuildingSession): void { session.apply( { kind: 'remove-hosted-opening', buildingId: 'building', storeyId: 'ground', wallId: 'room-walls', openingId: 'entry-door', }, session.revision, ); } ``` ## Move the door to another wall `rehost-hosted-opening` takes the door out of its wall and puts it in a different wall of the same storey. `offset` is the distance along the new wall. ```typescript theme={null} export function moveDoorToWall(session: BuildingSession): void { session.apply( { kind: 'rehost-hosted-opening', buildingId: 'building', storeyId: 'ground', wallId: 'room-walls', openingId: 'entry-door', toWallId: 'garden-wall', offset: 3, }, session.revision, ); } ``` The door keeps its size and its data. The old wall has no cut there after the edit, and the new wall has one. If the door does not fit in the new wall, `apply` throws an error and the building does not change. ## Next Put a window in the same wall. See each option of a door. # Add a floor slab Source: https://docs.ploterr.com/floorkit/guides/add-a-floor-slab Put a plate with thickness below your walls. A slab is a flat plate. You give its outline and its thickness. Use a slab for a floor plate, a terrace or a ceiling. ## Before you start You have a session from [Start a building](/floorkit/guides/start-a-building). ## Write the slab This slab goes below the room from [Draw a wall](/floorkit/guides/draw-a-wall). The path of a wall is its centre line, and those walls are 0.2 metres thick. Thus the slab outline is 0.1 metres outside the wall path, at the outer face of the walls. The slab is 0.2 metres thick. Its top is at the storey elevation, so its bottom is 0.2 metres below. ```typescript theme={null} import type { BmSlab, BuildingSession } from '@opengeometry/floorkit'; export const groundFloor: BmSlab = { id: 'ground-floor', role: 'slab', profile: { outerRing: [ [-0.1, -0.1], [6.1, -0.1], [6.1, 4.1], [-0.1, 4.1], ], }, thickness: 0.2, baseOffset: -0.2, }; ``` ## Add it to the storey A slab is an entry in the `slabs` list of its storey. ```typescript theme={null} export function addFloorSlab(session: BuildingSession): void { session.apply( { kind: 'add-element', buildingId: 'building', storeyId: 'ground', list: 'slabs', element: groundFloor, }, session.revision, ); } ``` The `add-element` command adds the slab to the list, and the session builds it. You keep the session and its undo history. The command rejects an id that the list has. ## Rules * `outerRing` is a list of `[x, z]` points in metres. Do not repeat the first point. * A wall path is the centre line of the wall. To put the edge of a slab at the outer face of a wall, move the outline half of the wall thickness out from the path. * `baseOffset` is the distance from the storey elevation to the bottom of the slab. * `role` can be `'slab'`, `'floor'` or `'ceiling'`. The role names the element. It does not change the shape. * For a hole, such as a stair opening, add a ring to `profile.holes`. ## Move the slab A slab that is in the model has a move command: ```typescript theme={null} export function moveSlab(session: BuildingSession): void { session.apply( { kind: 'transform-slab', buildingId: 'building', storeyId: 'ground', slabId: 'ground-floor', origin: [0, 0], translation: [1, 0], yawDeg: 0, }, session.revision, ); } ``` ## Next Close the building at the top. See each option of a slab. # Add a plan editor Source: https://docs.ploterr.com/floorkit/guides/add-a-plan-editor Give your users a 2D plan that they edit with the pointer. The plan editor shows one plan view of your project. The user selects, drags and draws in it. Each action becomes one step in the history of the project manager, so undo, redo and the 3D view work with no more code. ## Before you start Your project has a plan view with the id `ground-plan`. See [Create a plan view](/floorkit/guides/create-a-plan-view). ## Mount the editor Open the project in a `ProjectManager`. The manager builds the building and gives each editor the same session. ```typescript theme={null} import { ProjectManager, type ManagedPlanEditor, type ProjectFile } from '@opengeometry/floorkit'; export function openPlanProject(project: ProjectFile): ProjectManager { return new ProjectManager(project, { onError: (error) => console.error('Floorkit rejected the edit', error), }); } ``` Then give the manager an element that has a size, and the id of the view. ```typescript theme={null} export function mountPlanEditor( manager: ProjectManager, container: HTMLElement, ): Promise { return manager.mountPlanEditor(container, 'ground-plan'); } ``` ## Edit a wall When the pointer is on an element, the editor shows it in light blue. When the user selects a wall, the editor shows a blue outline around the wall, a line along the wall and these handles: | The user drags | Result | | - | - | | A dot at an end | Only that end moves. The other wall at the corner stays where it is. | | The dot at the middle | The wall moves sideways. The walls at its ends become longer or shorter. | | The wall itself | All segments of the wall move, with their doors and windows. | An end that leaves a corner is free. An end released near the end of another wall joins that end, and the two walls make a corner. An end released near the body of another wall joins that wall. The green square shows that the end will join. When an end leaves a corner inside one saved wall, Floorkit cuts the saved wall there and gives the new part its own id. The walls, their doors and their windows move while the user drags. When the user releases the pointer, Floorkit saves one edit command. Thus one drag is one step for undo. ## Edit a door or a window When the user selects a door or a window, the editor shows one dot on it. | The user drags the dot | Result | | - | - | | Along its wall | The door moves along the wall. | | Near a different wall | The door moves into that wall. | | More than 0.35 m from every wall | The door leaves its wall and becomes a loose door. | | On a loose door | The loose door moves. It does not go into a wall. | The door moves while the user drags, and the cut in the wall moves when the user releases the pointer. One drag is one edit command. ## Edit a slab, a roof, a room or a stair When the user selects one of these, the editor shows its handles. | Element | Handles | | - | - | | Slab | A dot at each corner moves that corner. A dot at the middle of an edge adds a corner. | | Roof | A dot at each corner moves that corner. | | Room | A dot at each corner moves that corner. | | Stair | A dot at the start point moves the stair. Three smaller dots set its width, the depth of its treads and the depth of its landing. | The 3D editor also has a handle for the height: the thickness of a slab, the eave of a roof, the height of a room and the base of a stair. The element changes while the user drags. One drag is one `reshape` command. ## Edit a mass A plan shows the masses of the site, the parcel and the dashed setback line of its zoning rules when its view has `showMassing: true`. The parcel and the setbacks are drawn only: a click passes through them, and a `replace-site` command draws them again. The switch is off by default. Turn it on in the saved view, or with a command, which is one undo step: ```typescript theme={null} export function showMassing(manager: ProjectManager): void { const view = manager.snapshot().views.find((item) => item.id === 'ground-plan'); if (view?.kind !== 'plan') return; manager.apply({ kind: 'replace-project-item', list: 'views', id: view.id, item: { ...view, showMassing: true }, }); } ``` The plan then draws the outline of each part of the mass of every building, under the storey. A mass works like a slab: | The user | What happens | | - | - | | Clicks a part | That part (the tower or the podium) is the one the handles edit. | | Drags a dot at a corner | That corner of the footprint of the part moves. | | Drags a dot at the middle of an edge | A corner is added. | | Drags the part | The part moves. The other parts stay. | | Drags the round grip | The part turns about the centre of its footprint. | | Presses Delete | The part goes. If it is the last part, the mass goes. | Each drag and each Delete is one `replace-mass` command, as in the 3D editor. To show live numbers, such as the floor area, while the user drags, set `editor.renderer.onDrag`; it is called on each frame of a drag, before anything is saved. The plan has no handle for the height: change it in the 3D editor or with a command. The manager refuses a gesture that would remove a storey that a plan view shows, and Delete keeps such a storey as a plain storey. ## Let the user draw a wall `startWallDrawing` starts the wall tool. The user clicks each point. The Enter key finishes the wall and the Escape key cancels it. ```typescript theme={null} export function startWallTool(editor: ManagedPlanEditor): void { editor.startWallDrawing({ id: `wall-${Date.now()}`, thickness: 0.2 }); } ``` ## Set how the user pans and zooms A two-finger scroll pans the plan and a pinch zooms it. Ctrl or Cmd with the wheel also zooms. For a user with a mouse, make the wheel zoom. ```typescript theme={null} import { MOUSE_NAVIGATION } from '@opengeometry/floorkit'; export function useMouseNavigation(editor: ManagedPlanEditor): void { editor.renderer?.setNavigation(MOUSE_NAVIGATION); } ``` `setNavigation` also takes your own settings. See [`createModelRenderer`](/floorkit/api/views/create-model-renderer#notes). ## Add undo, redo and save The manager has one history for the building, the views and the sheets. The undo key in the editor uses the same history. ```typescript theme={null} export function planActions(manager: ProjectManager) { return { undo: () => manager.undo(), redo: () => manager.redo(), save: () => manager.save(), }; } ``` `undo()` and `redo()` return `null` when there is no step. `save()` returns the project text with the current building. Store this text. See [Save and reopen](/floorkit/guides/save-and-reopen). ## Remove the editor `dispose()` of the manager removes each plan editor and then the session. ```typescript theme={null} export function closePlanEditor(manager: ProjectManager): void { manager.dispose(); } ``` To remove one plan editor and keep the project open, call `dispose()` of that editor. ## Rules * You can mount more than one plan editor on one manager, for example one for each storey. * A door or a window leaves its wall when the user drags it more than 0.35 m from every wall. The option `hostedOpeningDetachTolerance` of the manager sets that distance in metres. * While a plan editor shows a view, you cannot remove that view or make it a view that is not a plan. The manager throws `ProjectFileError` with the code `view-in-use`. * If the editor shows a view that you added in this session, an undo that reaches the "add view" step fails with the same code. Dispose that editor first. * After a change of the views, the sheets or the markers, the manager draws each mounted plan again. * A mass is drawn under the elements of the storey. Where a slab or a room covers it, a click selects the slab or the room. ## Without the manager If your app makes the `BuildingSession` itself, make the editor with `ManagedPlanEditor`. See the [ManagedPlanEditor API](/floorkit/api/editor/managed-plan-editor). ## Next Edit the same building in 3D. See each member of the manager and the project commands. # Add a roof Source: https://docs.ploterr.com/floorkit/guides/add-a-roof Make a flat, gable, hip or shed roof from one outline. A roof starts from the outline of the walls below it. You select the roof type and the pitch, and Floorkit makes the roof planes. ## Before you start You have a session from [Start a building](/floorkit/guides/start-a-building). ## Write the roof This gable roof sits on the room from [Draw a wall](/floorkit/guides/draw-a-wall). Its outline is the outer face of the walls, 0.1 metres outside the wall path. Its eave is 3 metres above the storey elevation, at the top of the walls. ```typescript theme={null} import { RoofType, type BmRoof, type BuildingSession } from '@opengeometry/floorkit'; export const mainRoof: BmRoof = { id: 'main-roof', profile: { outerRing: [ [-0.1, -0.1], [6.1, -0.1], [6.1, 4.1], [-0.1, 4.1], ], }, roofType: RoofType.GABLE, pitch: 30, overhang: 0.3, thickness: 0.2, baseOffset: 3, }; ``` ## Add it to the storey A roof is an entry in the `roofs` list of its storey. ```typescript theme={null} export function addRoof(session: BuildingSession): void { session.apply( { kind: 'add-element', buildingId: 'building', storeyId: 'ground', list: 'roofs', element: mainRoof, }, session.revision, ); } ``` The `add-element` command adds the roof to the list, and the session builds it. You keep the session and its undo history. The command rejects an id that the list has. ## Roof types | `roofType` | Shape | Note | | - | - | - | | `RoofType.FLAT` | One level plate | The outline can have holes | | `RoofType.GABLE` | Two planes that meet at a ridge | Set `ridgeDirection` to turn the ridge | | `RoofType.HIP` | A plane on each side | The outline must be convex | | `RoofType.SHED` | One sloped plane | | In a JSON file, write the type as text: `"GABLE"`. ## Rules * `pitch` is in degrees. `overhang` and `thickness` are in metres. * `baseOffset` is the distance from the storey elevation to the eave. * `overhang` moves the roof edge outward from the outline. * A pitched roof cannot have holes. ## Next Connect two storeys. See each option of a roof. # Add a stair Source: https://docs.ploterr.com/floorkit/guides/add-a-stair Connect two levels with a stair that has equal risers. You give the start point, the direction and the width of a stair. Floorkit calculates the number of steps from the storey height and makes each step the same. ## Before you start You have a session from [Start a building](/floorkit/guides/start-a-building). ## Write the stair This straight stair starts at `[1, 1]` and is 1 metre wide. It climbs the full storey height. ```typescript theme={null} import { StairLayout, type BmStair, type BuildingSession } from '@opengeometry/floorkit'; export const mainStair: BmStair = { id: 'main-stair', layout: StairLayout.STRAIGHT, origin: [1, 1], rotation: 0, baseOffset: 0, width: 1, treadDepth: 0.28, }; ``` ## Add it to the storey A stair is an entry in the `stairs` list of the storey where it starts. ```typescript theme={null} export function addStair(session: BuildingSession): void { session.apply( { kind: 'add-element', buildingId: 'building', storeyId: 'ground', list: 'stairs', element: mainStair, }, session.revision, ); } ``` The `add-element` command adds the stair to the list, and the session builds it. You keep the session and its undo history. The command rejects an id that the list has. ## Layouts | `layout` | Shape | | - | - | | `StairLayout.STRAIGHT` | One flight | | `StairLayout.L_SHAPE` | Two flights with a quarter turn | | `StairLayout.U_SHAPE` | Two flights with a half turn | For an L or a U layout, `turnDirection` sets the turn (`'left'` or `'right'`) and `landingDepth` sets the depth of the landing. ## Rules * `origin` is `[x, z]` in metres. It is the start of the walking line of the first flight. * `rotation` is in degrees, counter-clockwise when you look from above. * `totalRise` is the height that the stair climbs. The default is the storey height. * `treadDepth` is the depth of one step. The default is 0.28 metres. * Floorkit keeps each riser at `maxRiserHeight` or less. The default is 0.19 metres. Floorkit makes the geometry of a stair. Check the stair against your local building code. ## Next Add the structure. See each option of a stair. # Add a storey Source: https://docs.ploterr.com/floorkit/guides/add-a-storey Put a second floor on top of the first one. A storey is one level of the building. It has an elevation, a height and its own elements. To make a building with two floors, add a second storey at the height where the first one ends. ## Before you start You have a session from [Start a building](/floorkit/guides/start-a-building). The ground storey is 3 metres high. ## Write the storey The new storey starts at 3 metres. It has its own walls and its own floor slab. ```typescript theme={null} import type { BmStorey, BuildingSession } from '@opengeometry/floorkit'; export const firstFloor: BmStorey = { id: 'first', name: 'First floor', elevation: 3, height: 3, walls: [ { id: 'first-walls', points: [ [0, 0], [6, 0], [6, 4], [0, 4], ], closed: true, thickness: 0.2, }, ], slabs: [ { id: 'first-floor-slab', profile: { outerRing: [ [-0.1, -0.1], [6.1, -0.1], [6.1, 4.1], [-0.1, 4.1], ], }, thickness: 0.2, baseOffset: -0.2, }, ], }; ``` ## Add it to the building ```typescript theme={null} export function addStorey(session: BuildingSession): void { session.apply( { kind: 'add-storey', buildingId: 'building', storey: firstFloor }, session.revision, ); } ``` The `add-storey` command adds the storey with its elements, and the session builds it. You keep the session and its undo history. The command rejects a storey id that the building has. `replace-storey-settings` sets the name, the elevation, the height and the usage of a storey, and `remove-storey` removes a storey. See [Editing and history](/floorkit/concepts/editing-and-history#add-replace-and-remove). ## Rules * `elevation` is the height of the storey floor in the world. It is the only world height in a storey. * The elements in a storey use offsets from that elevation. You do not add the elevation to them. * Each storey has its own walls. Walls on different storeys do not join. * Ids are unique in the full model, not only in one storey. ## Use the storey in commands Each edit command names its storey. To add a door to the new walls, use `storeyId: 'first'` and `wallId: 'first-walls'`. ## Next Change the elements that you made. Make a plan of one storey. # Add a window Source: https://docs.ploterr.com/floorkit/guides/add-a-window Put a window in a wall at a sill height. A window belongs to a wall, as a door does. You give its position along the wall, its size and the height of its sill. ## Before you start Your building has the room `room-walls` from [Draw a wall](/floorkit/guides/draw-a-wall). ## Add the window The centre of this window is 4.5 metres from the first point of the wall. The bottom of the glass is 0.9 metres above the base of the wall. ```typescript theme={null} import type { BuildingSession } from '@opengeometry/floorkit'; export function addWindow(session: BuildingSession): void { session.apply( { kind: 'add-hosted-opening', buildingId: 'building', storeyId: 'ground', wallId: 'room-walls', opening: { id: 'south-window', kind: 'window', offset: 4.5, width: 1.2, height: 1.2, sillHeight: 0.9, }, }, session.revision, ); } ``` ## Rules * `offset`, `width`, `height` and `sillHeight` are in metres. * `sillHeight` is the only vertical control of a window. A window has no `baseHeight`. * The top of the window, with its frame, must be below the top of the wall. * The window must not overlap a different opening in the same wall. ## Move or remove the window A window uses the same commands as a door: `move-hosted-opening` and `remove-hosted-opening`. See [Add a door](/floorkit/guides/add-a-door#move-or-remove-the-door). ## A hole with no window To cut a plain hole, write a void in the model. A void is an entry in the `openings` of the wall, with `kind: 'void'`, an `offset`, a `width` and a `height`. See [Change the document](/floorkit/guides/start-a-building#change-the-document). ## Next Put a floor below the walls. See each option of a window. # Compose a sheet Source: https://docs.ploterr.com/floorkit/guides/compose-a-sheet Put views on an A3 page at a scale, add a dimension and a note, and let your users arrange the page. A sheet is a page. A viewport puts one saved view on the page at a scale. You can put many viewports on one sheet, and one view on many sheets. A sheet also holds dimensions, notes and a title block. ## Before you start You have a project with the views `ground-plan`, `section-a` and `elevation-south`, and the section mark, from [Create a plan view](/floorkit/guides/create-a-plan-view) and [Cut a section](/floorkit/guides/cut-a-section). ## Write the sheet All sizes and positions on a sheet are in millimetres. The origin is the top-left corner of the page. This sheet starts with one viewport, the plan at 1:50. ```typescript theme={null} import type { ProjectFile, Sheet } from '@opengeometry/floorkit'; export const planSheet: Sheet = { id: 'sheet-a101', name: 'House drawings', number: 'A-101', page: { format: 'A3', orientation: 'landscape', size: { width: 420, height: 297 }, margin: 10, }, viewports: [ { id: 'plan-viewport', viewId: 'ground-plan', drawingNumber: '1', frame: { x: 20, y: 20, width: 200, height: 120 }, scale: 0.02, rotationDeg: 0, clip: true, title: { text: 'Ground floor plan', showScale: true }, }, ], annotations: [], titleblock: { templateId: 'default', values: { sheetNumber: 'A-101' } }, }; ``` | Field | Meaning | | - | - | | `viewId` | The id of the saved view that the viewport shows | | `drawingNumber` | The number of this drawing on the sheet. The title shows it | | `frame` | The rectangle on the page, in millimetres | | `scale` | Paper size divided by real size. `0.02` is 1:50. `0.01` is 1:100. | | `clip` | `true` hides the linework outside the frame | | `title` | The text below the viewport. `showScale` adds the scale. | ## Add it to the project ```typescript theme={null} export function addSheet(project: ProjectFile): ProjectFile { return { ...project, paper: { ...project.paper, sheets: [...project.paper.sheets, planSheet] } }; } ``` A project is data, so each change makes a new project. This helper changes the sheet and keeps the other parts of the project: ```typescript theme={null} export function changeSheet(project: ProjectFile, change: (sheet: Sheet) => Sheet): ProjectFile { return { ...project, paper: { ...project.paper, sheets: project.paper.sheets.map((sheet) => sheet.id === planSheet.id ? change(sheet) : sheet, ), }, }; } ``` ## Put more views on the sheet Add one viewport for each view. These viewports put the section at 1:50 and the elevation at 1:100. ```typescript theme={null} import type { Viewport } from '@opengeometry/floorkit'; export const sectionViewport: Viewport = { id: 'section-viewport', viewId: 'section-a', drawingNumber: '2', frame: { x: 235, y: 20, width: 165, height: 140 }, scale: 0.02, rotationDeg: 0, clip: true, title: { text: 'Section A', showScale: true }, }; export const elevationViewport: Viewport = { id: 'elevation-viewport', viewId: 'elevation-south', drawingNumber: '3', frame: { x: 20, y: 160, width: 200, height: 85 }, scale: 0.01, rotationDeg: 0, clip: true, title: { text: 'South elevation', showScale: true }, }; export function addViewport(project: ProjectFile, viewport: Viewport): ProjectFile { return changeSheet(project, (sheet) => ({ ...sheet, viewports: [...sheet.viewports, viewport] })); } ``` ## Change the scale The scale is a field of the viewport. The title below the viewport shows the new scale. ```typescript theme={null} export function setScale(project: ProjectFile, viewportId: string, scale: number): ProjectFile { return changeSheet(project, (sheet) => ({ ...sheet, viewports: sheet.viewports.map((viewport) => viewport.id === viewportId ? { ...viewport, scale } : viewport, ), })); } ``` ## Number the section mark The section mark on the plan can show where its section is. Give the target of the mark the sheet and the viewport. The mark then reads `2 / A-101`: drawing 2 on sheet A-101. ```typescript theme={null} export function numberSectionMark(project: ProjectFile): ProjectFile { return { ...project, markers: (project.markers ?? []).map((marker) => marker.id === 'section-a-mark' ? { ...marker, target: { viewId: 'section-a', sheetId: 'sheet-a101', viewportId: 'section-viewport' }, } : marker, ), }; } ``` ## Add a dimension and a note A sheet holds annotations. A `measure` dimension uses two points of the model, in metres, in the view of one viewport. Floorkit calculates the distance, so the text is always the real size. A `text` note uses one point on the page, in millimetres. ```typescript theme={null} import type { Annotation } from '@opengeometry/floorkit'; export const roomWidth: Annotation = { id: 'room-width', kind: 'measure', viewportId: 'plan-viewport', from: { x: 0, y: 4 }, to: { x: 6, y: 4 }, offset: 10, fontSize: 2.5, }; export const siteNote: Annotation = { id: 'site-note', kind: 'text', text: 'Check the door width on site.', position: { x: 240, y: 200 }, fontSize: 4, }; export function addAnnotation(project: ProjectFile, annotation: Annotation): ProjectFile { return changeSheet(project, (sheet) => ({ ...sheet, annotations: [...sheet.annotations, annotation], })); } ``` ## Show the sheet on screen `createSheetRenderer` makes the screen view of a sheet. It needs your sheets and a function that draws a saved view. Here `project` is a function, so the renderer always draws your current project. ```typescript theme={null} import { TitleblockRegistry, createSheetRenderer, drawProjectView, type PaperScreenRenderer, } from '@opengeometry/floorkit'; export async function showSheet( container: HTMLElement, project: () => ProjectFile, ): Promise { const renderer = await createSheetRenderer(); renderer.mount(container, project().paper, { resolveSavedView: (viewId) => drawProjectView(project(), viewId), extractor: { extract: () => drawProjectView(project(), 'ground-plan') }, templates: new TitleblockRegistry(), projectName: 'Garden house', }); return renderer; } ``` `resolveSavedView` draws a viewport that has a `viewId`. `extractor` draws a viewport that has its own `source`. This project has no such viewport, so the extractor gives the plan. After you change the project, call `renderer.update(project.paper)`. ## Let the user arrange the page The user can select a viewport or an annotation and move it. The renderer does not change your project. It sends an event, and you save the change. ```typescript theme={null} import type { PaperEditEvent } from '@opengeometry/floorkit'; export function applyMove(project: ProjectFile, event: PaperEditEvent): ProjectFile { return changeSheet(project, (sheet) => ({ ...sheet, viewports: sheet.viewports.map((viewport) => viewport.id === event.targetId && event.frame ? { ...viewport, frame: event.frame } : viewport, ), annotations: sheet.annotations.map((annotation) => annotation.id === event.targetId && event.annotation ? event.annotation : annotation, ), })); } export function followEdits( renderer: PaperScreenRenderer, read: () => ProjectFile, write: (project: ProjectFile) => void, ): void { renderer.on('move', (event) => { write(applyMove(read(), event)); renderer.update(read().paper); }); renderer.on('create', (event) => { if (!event.annotation) return; write(addAnnotation(read(), event.annotation)); renderer.update(read().paper); }); } ``` The `create` event comes from a drawing tool. `renderer.setTool('text')` starts the text tool: the user clicks the page and gets a note. Other tools are `'dimension'`, `'leader'`, `'line'` and `'rect'`. `renderer.setTool('select')` goes back to selection. ## Change the paper style A paper style changes the pens, the fills and the lettering of the sheet. It does not change your project. ```typescript theme={null} export function setSheetStyle( renderer: PaperScreenRenderer, style: 'iso-128' | 'ink-sketch' | 'graphite' | 'napkin' | undefined, ): void { renderer.setPaperStyle?.(style); } ``` `undefined` goes back to the style of the document. To export with a style, pass `{ style: 'ink-sketch' }` as the options of [`exportProjectPdf`](/floorkit/guides/export-a-pdf). ## Rules * The `size` of the page must agree with its `format` and `orientation`. [`pageSize`](/floorkit/api/drawings/page-sizes-and-frames) gives you the correct values. * A viewport shows the full view, centred in its frame. Set `viewCenter` to keep one model point in the centre. * To show the same view at a different scale, add a second viewport with the same `viewId`. * The points of a `measure` dimension do not follow the model. If you move the wall, set the points again. * The renderer shows your sheets. It does not own them. Save each change in your project, then call `update`. ## Next Get this sheet as a file. See each field of a sheet, a viewport and an annotation. See each member of the screen view. Make your own style. # Create a plan view Source: https://docs.ploterr.com/floorkit/guides/create-a-plan-view Save a floor plan of one storey and get its linework. A plan view is a horizontal cut through one storey. You save the storey and the cut height. Floorkit makes the linework from the model each time that you ask for it. ## Before you start You have a building model with a storey `ground` that has walls. See [Draw a wall](/floorkit/guides/draw-a-wall). ## Save the view in a project A project holds the building model, the views and the sheets. This project has one plan view and no sheets. ```typescript theme={null} import { drawProjectView, type BuildingModel, type ProjectFile, type SavedDrawingView, } from '@opengeometry/floorkit'; export const groundPlan: SavedDrawingView = { id: 'ground-plan', name: 'Ground floor plan', kind: 'plan', storeyId: 'ground', cutHeightAboveStorey: 1.2, }; export function createProject(buildingModel: BuildingModel): ProjectFile { return { schema: 'floorkit.project', version: 3, buildingModel, views: [groundPlan], paper: { version: 1, unitSystem: 'metric', sheets: [] }, }; } ``` ## Get the linework `drawProjectView` returns the drawing as data: lines, arcs, regions and text in metres. Use it to show the plan in your own canvas. ```typescript theme={null} export function planLinework(project: ProjectFile) { const drawing = drawProjectView(project, 'ground-plan'); console.log(`${drawing.primitives.length} primitives`, drawing.bounds); return drawing; } ``` ## Rules * `cutHeightAboveStorey` is in metres above the storey elevation. A cut at 1.2 metres goes through doors and most windows. * The view holds no lines. It is drawn from the model, so it is always up to date. * `showMassing: true` also draws the outline of each part of the mass of every building on the site, on the sheet and in the plan editor. Under the masses it draws the parcel and the dashed setback line: the edge of what the setback check of `evaluateZoning` leaves buildable under the site's zoning rules. Both are drawn only, never selected. It is off by default. A file with this field does not open in Floorkit 1.1.0. * Give the project the current model of your session before you draw: `{ ...project, buildingModel: session.snapshot() }`. * To show the plan and let the user edit it, use the [plan editor](/floorkit/guides/add-a-plan-editor). ## Next Make a vertical cut through the building. Put this plan on paper. # Cut a section Source: https://docs.ploterr.com/floorkit/guides/cut-a-section Save a vertical cut through the building, or an outside elevation. A section is a vertical cut along a line. An elevation is a view of the building from outside. You save where to cut and where to look. Floorkit makes the linework from the model. ## Before you start You have a project from [Create a plan view](/floorkit/guides/create-a-plan-view). ## Save a section This section cuts the building along the line from `[4.5, -1]` to `[4.5, 5]`. The line goes through the window and across the roof ridge. The view looks to the right of that line, to the door, and shows 5 metres of depth. ```typescript theme={null} import type { ProjectFile, SavedDrawingView } from '@opengeometry/floorkit'; export const sectionA: SavedDrawingView = { id: 'section-a', name: 'Section A', kind: 'section', line: { start: [4.5, -1], end: [4.5, 5] }, lookSide: 'right', depth: 5, verticalCrop: { minY: -0.5, maxY: 6 }, settings: { levelLabels: { datum: 0, unit: 'm' } }, }; ``` | Field | Meaning | | - | - | | `line` | The cut line, as two `[x, z]` points | | `lookSide` | The side that you look to, when you go from start to end | | `depth` | How far the view shows behind the cut, in metres | | `verticalCrop` | The lowest and the highest world height that the view shows | | `settings.levelLabels` | Adds the name and the height of each storey. `datum` is the height that is zero | ## Save an elevation This elevation looks at the south face. The `origin` is 2 metres in front of the building, and `lookDirection` points to the building. ```typescript theme={null} export const southElevation: SavedDrawingView = { id: 'elevation-south', name: 'South elevation', kind: 'elevation', origin: [3, -2], lookDirection: [0, 1], crop: { left: -4, right: 4, bottom: -6, top: 0.5 }, }; ``` The crop is in the frame of the view, in metres from the `origin`. In that frame the vertical axis points down. Thus `bottom: -6` keeps 6 metres above the zero height, and `top: 0.5` keeps 0.5 metres below it. ## Add the views to the project ```typescript theme={null} export function addSectionViews(project: ProjectFile): ProjectFile { return { ...project, version: 3, views: [...project.views, sectionA, southElevation] }; } ``` Then draw a view with `drawProjectView(project, 'section-a')`, or put it on a sheet. ## Show the cut on the plan A mark on the plan shows where the section cuts. You save which plan gets the mark and which view it points to. Floorkit takes the line from the section, so the mark moves when you change the section. ```typescript theme={null} import type { ProjectViewMarker } from '@opengeometry/floorkit'; export const sectionMark: ProjectViewMarker = { id: 'section-a-mark', kind: 'source', parentViewId: 'ground-plan', target: { viewId: 'section-a' }, }; export function addSectionMark(project: ProjectFile): ProjectFile { return { ...project, version: 3, markers: [...(project.markers ?? []), sectionMark] }; } ``` The text of the mark is `[unplaced] Section A` until the section is on a sheet. [Compose a sheet](/floorkit/guides/compose-a-sheet#number-the-section-mark) gives it a drawing number. ## Rules * Draw the cut line fully across the building. Start and end it outside the walls. * Keep the cut line and the far limit of the view away from wall faces, from the edges of openings and from the roof ridge. Floorkit rejects a view when one of its limits only touches a face or lies along an edge. Move the line, or change `depth`, by some centimetres. * An open door reaches one door width out of its wall. Include that distance when you set `depth`. * A level label is at the left of the view, outside the crop. Give the view some empty width on that side when you put it on a sheet. * A section uses a straight line. An elevation looks from outside the building. * If Floorkit cannot draw the view, `drawProjectView` throws `ProjectDrawingError` with a `code`. ## Next Put the section next to the plan. See each field of a mark. # Define a room Source: https://docs.ploterr.com/floorkit/guides/define-a-room Give a name, a number and an area to a space. A room is an outline with a name. Floorkit calculates its area and its perimeter. Use rooms for labels on a plan and for area lists. ## Before you start You have a session from [Start a building](/floorkit/guides/start-a-building). ## Write the room The outline follows the inner faces of the walls. For walls 0.2 metres thick on a 6 by 4 metre outline, the room starts 0.1 metres inside. ```typescript theme={null} import type { BmSpace, BuildingSession } from '@opengeometry/floorkit'; export const livingRoom: BmSpace = { id: 'living-room', name: 'Living room', number: '01', profile: { outerRing: [ [0.1, 0.1], [5.9, 0.1], [5.9, 3.9], [0.1, 3.9], ], }, }; ``` ## Add it to the storey A room is an entry in the `spaces` list of its storey. ```typescript theme={null} export function addRoom(session: BuildingSession): void { session.apply( { kind: 'add-element', buildingId: 'building', storeyId: 'ground', list: 'spaces', element: livingRoom, }, session.revision, ); } ``` The `add-element` command adds the room to the list, and the session builds it. You keep the session and its undo history. The command rejects an id that the list has. ## Read the area After the build, get the room from the session and read its values. ```typescript theme={null} import { Space } from '@opengeometry/floorkit'; export function roomArea(session: BuildingSession): number | undefined { const room = session.currentBuild.elementsById.get('living-room'); return room instanceof Space ? room.area : undefined; } ``` ## Rules * You give the outline. Floorkit does not find rooms from walls. * `area` is in square metres. Holes in the outline are subtracted. * `show3D: true` shows the room as a translucent volume. ## Next Put a second floor on the building. See each option of a space. # Draw a wall Source: https://docs.ploterr.com/floorkit/guides/draw-a-wall Add one wall to a storey with a command. A wall is a line of points with a thickness. Floorkit makes the solid, and it joins the wall to the walls that it touches. ## Before you start You have a session from [Start a building](/floorkit/guides/start-a-building). ## Add a straight wall This garden wall goes from `[0, 7]` to `[6, 7]`. It is 6 metres long and 0.2 metres thick. ```typescript theme={null} import type { BuildingSession } from '@opengeometry/floorkit'; export function drawWall(session: BuildingSession): void { session.apply( { kind: 'create-wall', buildingId: 'building', storeyId: 'ground', wall: { id: 'garden-wall', points: [ [0, 7], [6, 7], ], thickness: 0.2, }, }, session.revision, ); } ``` ## Add a closed room A wall can have more than two points. Set `closed: true` and Floorkit adds the last segment back to the first point. This room is 6 by 4 metres. The other guides add a door, a window, a floor and a roof to it. ```typescript theme={null} export function drawRoom(session: BuildingSession): void { session.apply( { kind: 'create-wall', buildingId: 'building', storeyId: 'ground', wall: { id: 'room-walls', points: [ [0, 0], [6, 0], [6, 4], [0, 4], ], closed: true, thickness: 0.2, }, }, session.revision, ); } ``` ## Rules * Points are `[x, z]` in metres, seen from above. * Do not repeat the first point to close a wall. Use `closed: true`. * The wall height is the storey height. Set `height` to change it. * The points are on the centre line of the wall. Set `locationLine` to put them on a face. * The `id` must be unique in the model. * Walls on the same storey join where they meet: at corners, at T-junctions and at crossings. ## Remove a wall ```typescript theme={null} export function removeWall(session: BuildingSession): void { session.apply( { kind: 'delete-wall', buildingId: 'building', storeyId: 'ground', wallId: 'garden-wall' }, session.revision, ); } ``` ## Next Cut a door into the room. Move a wall point after you draw the wall. # Export a GLB Source: https://docs.ploterr.com/floorkit/guides/export-a-glb Get the 3D model as one GLB file for viewers, game engines and AR. A GLB file holds the 3D model in one binary file. Floorkit builds your model as standard Three.js objects, so you export it with the exporter that is part of Three.js. ## Before you start You have a session from [Start a building](/floorkit/guides/start-a-building) with some elements in it. ## Export and download `session.root` holds the full building. Give it to `GLTFExporter` and ask for binary output. ```typescript theme={null} import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js'; import type { BuildingSession } from '@opengeometry/floorkit'; export async function downloadGlb(session: BuildingSession): Promise { session.root.updateMatrixWorld(true); const result = await new GLTFExporter().parseAsync(session.root, { binary: true, onlyVisible: true, }); if (!(result instanceof ArrayBuffer)) throw new Error('The exporter did not return a GLB.'); const url = URL.createObjectURL(new Blob([result], { type: 'model/gltf-binary' })); const link = document.createElement('a'); link.href = url; link.download = 'building.glb'; link.click(); URL.revokeObjectURL(url); } ``` ## Export one storey A build keeps one group for each storey. Pass that group in place of the root. ```typescript theme={null} export function storeyGroup(session: BuildingSession, storeyId: string) { return session.currentBuild.storeyGroups.get(storeyId); } ``` ## Rules * The model is in metres and the Y axis points up. glTF uses the same conventions, so no conversion is necessary. * The file holds geometry, colours and object names. It does not hold the building model. To keep the design editable, also [save the project](/floorkit/guides/save-and-reopen). * Export after the last edit. The exporter reads the model as it is at that moment. * A deployed site needs a valid hostname key, including a non-commercial demo. See [Licensing](/floorkit/licensing). ## Next Keep the design editable. See the export function as a reusable module. # Export a PDF Source: https://docs.ploterr.com/floorkit/guides/export-a-pdf Get your sheets as one PDF file with vector linework. `exportPdf` of the project manager makes one PDF from the sheets of your project. Each sheet is one page. The linework is vector, so it stays sharp at each zoom level and in print. ## Before you start You have a project with a sheet from [Compose a sheet](/floorkit/guides/compose-a-sheet). ## Open the project A `ProjectManager` holds the project with its sheets and builds the building. ```typescript theme={null} import { ProjectManager, type ProjectFile } from '@opengeometry/floorkit'; export function openSheets(project: ProjectFile): ProjectManager { return new ProjectManager(project); } ``` ## Export and download ```typescript theme={null} export async function downloadPdf(manager: ProjectManager): Promise { const pdf = await manager.exportPdf(); const url = URL.createObjectURL(pdf); const link = document.createElement('a'); link.href = url; link.download = 'drawings.pdf'; link.click(); URL.revokeObjectURL(url); } ``` ## Export some sheets Give the ids of the sheets that you want. ```typescript theme={null} export function exportOneSheet(manager: ProjectManager): Promise { return manager.exportPdf(['sheet-a101']); } ``` ## Set the title block text ```typescript theme={null} export function exportWithTitle(manager: ProjectManager): Promise { return manager.exportPdf(undefined, { projectName: 'Garden house', date: '2026-10-06', }); } ``` ## Rules * The export uses the current building and the current sheets of the manager. You do not copy the building into the project first. * The result is a `Blob`. You can download it, as shown here, or send it to your server. * If Floorkit cannot draw one of the views, the promise rejects. You do not get a partial file. * After `dispose()` of the manager, the promise rejects. * A deployed site needs a valid hostname key for exports, including a non-commercial demo. See [Licensing](/floorkit/licensing). ## Without the manager If your app makes the `BuildingSession` itself, use `exportProjectPdf` with a project that holds the current building. See the [exportProjectPdf API](/floorkit/api/exports/export-project-pdf). ## Next Get the 3D model as a file. See each member of the manager and the project commands. # Model a mass Source: https://docs.ploterr.com/floorkit/guides/model-a-mass Block out a building as a simple volume, change it with commands, check the zoning rules, then turn it into walls. A mass is the building as a simple volume. It is one or more extruded outlines, stacked. Use a mass at the start of a design, before walls exist, to test the size, the height and the floor area. For a complete site study with multiple buildings, zoning, feasibility estimates and option comparison, see the [Site massing playbook](/floorkit/playbooks/site-massing). ## Before you start You have a session from [Start a building](/floorkit/guides/start-a-building). You use one session for the whole guide. ## Write the storeys and the mass This building has three storeys. The mass has two parts: a podium for the ground storey and a smaller tower for the two storeys above it. ```typescript theme={null} import { Building, irMassToMassLike, massToShell, type BmMass, type BmStorey, type BuildingModel, type BuildingSession, } from '@opengeometry/floorkit'; export const storeys: BmStorey[] = [ { id: 'ground', name: 'Ground floor', elevation: 0, height: 4, usage: 'retail' }, { id: 'first', name: 'First floor', elevation: 4, height: 3, usage: 'residential' }, { id: 'second', name: 'Second floor', elevation: 7, height: 3, usage: 'residential' }, ]; export const mass: BmMass = { id: 'building-mass', elevation: 0, segments: [ { id: 'podium', footprint: { outerRing: [ [0, 0], [12, 0], [12, 8], [0, 8], ], }, height: 4, use: 'retail', storeyIds: ['ground'], }, { id: 'tower', footprint: { outerRing: [ [2, 1], [10, 1], [10, 7], [2, 7], ], }, height: 6, use: 'residential', storeyIds: ['first', 'second'], }, ], }; ``` ## Set the site and the mass Both go into the session as commands. `replace-site` gives the building a lot and its zoning rules. `replace-mass` sets the mass and its storeys in one step. A storey that is not in the list is removed, and a new storey id adds an empty storey. ```typescript theme={null} import { massSegmentHeightCommand, type ZoningRuleSet } from '@opengeometry/floorkit'; export const zoning: ZoningRuleSet = { id: 'guide-rules', rules: [ { kind: 'ratio-cap', id: 'far', quantity: 'floorAreaRatio', max: 0.35 }, { kind: 'envelope-plane', id: 'setback', edgeClass: 'all', offsetAtGrade: 3 }, ], }; export function setSiteAndMass(session: BuildingSession): void { session.apply( { kind: 'replace-site', site: { id: 'lot', boundary: { outerRing: [ [-4, -4], [30, -4], [30, 14], [-4, 14], ], }, zoning, }, }, session.revision, ); session.apply({ kind: 'replace-mass', buildingId: 'building', mass, storeys }, session.revision); } ``` The mass belongs to the building, not to a storey. Each part of the mass names the storeys that it contains. Floorkit builds the two volumes, and one undo takes back each step. ## Read the floor area After the build, get the mass from the session and read its quantities. ```typescript theme={null} export function grossFloorArea(session: BuildingSession): number | undefined { const element = session.currentBuild.elementsById.get('building-mass'); return element instanceof Building ? element.quantities().grossFloorArea : undefined; } ``` For this mass the result is 192 square metres: 96 for the podium and 96 for the two tower storeys. | Quantity | Meaning | This mass | | - | - | - | | `footprintArea` | The area of the lowest part | 96 m² | | `grossFloorArea` | The sum of the areas of all storeys | 192 m² | | `grossFloorAreaByUse` | The floor area for each use | retail 96 m², residential 96 m² | | `grossVolume` | The volume of all parts | 672 m³ | | `height` | The height from the bottom to the top | 10 m | | `numberOfStoreys` | The number of storeys in the mass | 3 | ## Change the mass Each change below is one command and one undo step. **Height.** `massSegmentHeightCommand` gives the `replace-mass` command for a new height of one part. The height snaps to whole storeys, at the pitch of the top storey. It adds storeys with new ids, or removes empty top storeys, and it stacks every storey above again. It returns `undefined` when the height does not change. ```typescript theme={null} export function setTowerHeight(session: BuildingSession, height: number): void { const command = massSegmentHeightCommand(session.snapshot(), 'building', 'tower', height); if (command) session.apply(command, session.revision); } ``` **Area kind.** A storey has an `areaKind`: `gross`, `balcony`, `core`, `basement`, `parking` or `mechanical`. A `parking` storey does not count toward the floor area ratio, unless the zoning rules list that kind in `countsTowardFAR`. `replace-storey-settings` sets it. It replaces all the settings of the storey, so give them all. ```typescript theme={null} export function makeGroundParking(session: BuildingSession): void { session.apply( { kind: 'replace-storey-settings', buildingId: 'building', storeyId: 'ground', settings: { name: 'Ground floor', elevation: 0, height: 4, usage: 'parking', areaKind: 'parking', }, }, session.revision, ); } ``` **Colour.** Each part has a colour. A part takes its own `color` (`#rrggbb`), else the `color` of the mass, else the colour of its use (the `use` of the part, else of the mass, else the `buildingType` of the building), else `#a0b8c8`. The colours by use are in `MASS_COLORS_BY_USE`: they follow the land-use colours of US planning maps (residential yellow, retail red, office a lighter red, civic blue, industrial purple, parking grey). `massSegmentColorCommand` gives the `replace-mass` command that sets the colour of one part, or takes it away with `undefined`. The 3D model and the plan draw the part in that colour. ```typescript theme={null} import { massSegmentColorCommand } from '@opengeometry/floorkit'; export function paintTower(session: BuildingSession, color: string | undefined): void { const command = massSegmentColorCommand(session.snapshot(), 'building', 'tower', color); if (command) session.apply(command, session.revision); } ``` To use other colours by use, give your own table as the build option `massColors` where the project is opened: `ProjectManager.open(text, myColours)`. The 3D model, the plans and the sheets of that project then use it. It replaces the whole table; a use that it does not list takes `#a0b8c8`. ```typescript theme={null} import type { ProjectManagerOptions } from '@opengeometry/floorkit'; export const myColours: ProjectManagerOptions = { build: { massColors: { residential: '#f2d675', office: '#e58f7b', park: '#7fb069' } }, }; ``` **Footprint and storeys.** To change a footprint, or to add and remove storeys, send `replace-mass` again with the new mass and the new storey list, as in `setSiteAndMass`. **A new building.** `massBuildingCommand` makes the `add-building` command from an outline in site coordinates. It checks the outline, stacks the storeys from the site grade and gives ids that are new in the whole model. ```typescript theme={null} import { massBuildingCommand } from '@opengeometry/floorkit'; export function addBuildingFromOutline(session: BuildingSession): void { const command = massBuildingCommand( session.snapshot(), [ [16, 0], [24, 0], [24, 8], [16, 8], ], { storeyHeight: 3, storeyCount: 4, use: 'residential' }, ); session.apply(command, session.revision); } ``` ## Edit the mass in 2D A plan view does not draw the massing unless you ask: the switch `showMassing` is off by default, so existing plans and sheets do not change. Turn it on with `replace-project-item` on the views of a `ProjectManager` (one undo step), then mount the plan editor for that view. ```typescript theme={null} import type { ManagedPlanEditor, ProjectManager } from '@opengeometry/floorkit'; export async function editMassInPlan( manager: ProjectManager, container: HTMLElement, viewId: string, ): Promise { const view = manager.snapshot().views.find((item) => item.id === viewId); if (view?.kind !== 'plan') return undefined; manager.apply({ kind: 'replace-project-item', list: 'views', id: view.id, item: { ...view, showMassing: true }, }); const plan = await manager.mountPlanEditor(container, viewId); if (plan.renderer) plan.renderer.onDrag = (change) => console.log('live', change.kind); return plan; } ``` The plan then draws every building's mass with its parts, the parcel (a continuous line) and the setback line (dashed): a mass inside the dashed line passes the setback check of `evaluateZoning`. The parcel and the setbacks are only drawn; a click passes through them. The user clicks a part, drags a corner or edge dot to reshape it, drags the part to move it, drags the round grip to turn it, and presses Delete to remove it. Each gesture is one `replace-mass` and one undo step, as in 3D. The plan has no height grip: change the height in the 3D editor, or type it with `massSegmentHeightCommand`. `onDrag` is on both editors (`GUIEditor.onDrag` in 3D), so you can show live numbers while a drag is in progress. Sheets and PDFs show what the plan shows. **Draw it in the plan.** `plan.startMassDrawing({ storeyHeight: 3, storeyCount: 4 })` lets the user click the corners of the outline: a click on the first corner or Enter closes it, and Escape cancels. It saves the same `add-building` as one undo step and resolves with the id of the new mass, or with `undefined` if the user cancels. `startOutlineDrawing` returns the outline alone, as site XZ points, and saves nothing; an app can pass that polygon to `massBuildingCommand` or use it for something else. ```typescript theme={null} export async function drawBuilding(plan: ManagedPlanEditor): Promise { return plan.startMassDrawing({ storeyHeight: 3, storeyCount: 4, use: 'residential' }); } ``` See the [`ManagedPlanEditor` API](/floorkit/api/editor/managed-plan-editor) and [Add a plan editor](/floorkit/guides/add-a-plan-editor#edit-a-mass). ## Check the zoning rules `evaluateZoning` is pure. It does not read the session, so you run it again after each step. Give it the lot, the masses and the rules. ```typescript theme={null} import { evaluateZoning, type ZoningReport } from '@opengeometry/floorkit'; export function checkZoning(session: BuildingSession): ZoningReport | undefined { const { site, buildings } = session.snapshot(); if (!site?.boundary || !site.zoning) return undefined; const masses = buildings.flatMap((building) => building.mass ? [irMassToMassLike(building)] : [], ); const lot = { profile: site.boundary, lotLines: site.lotLines, elevation: site.elevation }; return evaluateZoning(lot, masses, site.zoning); } ``` With the rules above the mass is within the limit at first (192 m² on a lot of 612 m² is a ratio of 0.31). When you raise the tower to 9 m, the ratio is 0.39 and the report names `exceeds-far`. When you make the ground storey a parking storey, it counts 144 m² and the ratio is 0.24. To change a rule, send `replace-site` with the new lot. It replaces the boundary, the grade and the zoning together. The podium is 4 m from the lot line, so a setback of 5 m puts it in violation: ```typescript theme={null} export function setSetback(session: BuildingSession, metres: number): void { const site = session.snapshot().site; if (!site?.zoning) throw new Error('The model has no site with zoning rules.'); const rules = site.zoning.rules.map((rule) => rule.kind === 'envelope-plane' ? { ...rule, offsetAtGrade: metres } : rule, ); session.apply( { kind: 'replace-site', site: { ...site, zoning: { ...site.zoning, rules } } }, session.revision, ); } ``` Call `session.undo(session.revision)` to go back one step. See [Undo and redo](/floorkit/guides/undo-and-redo). ## Edit the mass in 3D With a 3D editor bound to the session (see [Add a 3D editor](/floorkit/guides/add-a-3d-editor)), the user edits the mass with the pointer. A click selects a part, the podium or the tower. The push/pull grip changes its height, the move and turn grips move or turn it, and Delete removes it. Each gesture saves one command and one undo step. `GUIEditor.onDrag` gives the live target before the save, so that you can show live numbers such as the floor area. See the [`GUIEditor` API](/floorkit/api/editor/gui-editor) and [`massSegmentHeightCommand`](/floorkit/api/architecture/massing-helpers). ## Turn the mass into walls, slabs and roofs When the volume is correct, `massToShell` makes a building with real elements from it. Each storey gets an outer wall. Each part gets floor slabs and a flat roof. ```typescript theme={null} export function massToElements(model: BuildingModel): void { const building = model.buildings[0]; if (!building) throw new Error('The model has no building.'); model.buildings[0] = massToShell(irMassToMassLike(building), { wallThickness: 0.3 }); } ``` Pass `massToElements` to `replaceBuilding`. This opens a new session, and the undo history ends. The new building has no mass, and its id is the id of the mass plus `-shell`: here `building-mass-shell`. The storeys keep their ids. Use the new building id in your edit commands. ## Rules * A footprint is a list of `[x, z]` points in metres. A hole in a footprint is a courtyard. * The parts stack from the bottom to the top. The first part starts at the `elevation` of the mass. * The `height` of a part must be the sum of the heights of its storeys. If it is not, the model is not valid. * A storey belongs to one part only. * `use` can be `residential`, `office`, `retail`, `hotel`, `civic`, `industrial`, `parking`, `mixed` or `other`. A different word gives a warning. * The storeys of a mass can be empty. They do not need walls. * A colour is a `#rrggbb` string. A command with another colour is refused with `invalid-command`. * A plan view shows the mass when the view has `showMassing: true`; the plan editor then edits it. See [Add a plan editor](/floorkit/guides/add-a-plan-editor#edit-a-mass). * Sections and elevations exclude masses. Turn the mass into a shell when you need physical cuts. ## Next Give names and areas to the spaces in a storey. See each option and each quantity of a mass. # Move and reshape Source: https://docs.ploterr.com/floorkit/guides/move-and-reshape Change a wall after you draw it. Its doors and windows follow. You change a wall with a command. Floorkit moves the wall, joins it again to the walls that it touches, and cuts its openings again. ## Before you start Your building has the room and the garden wall from [Draw a wall](/floorkit/guides/draw-a-wall). ## Move one point of a wall `pointIndex` is the position of the point in the `points` list of the wall. Index `2` is the third point, the corner at `[6, 4]`. ```typescript theme={null} import type { BuildingSession } from '@opengeometry/floorkit'; export function stretchRoom(session: BuildingSession): void { session.apply( { kind: 'move-wall-point', buildingId: 'building', storeyId: 'ground', wallId: 'room-walls', pointIndex: 2, position: [7, 5], }, session.revision, ); } ``` The room is now larger at that corner, and the two walls that meet there have a new direction. A door at `offset: 2` is still 2 metres from the first point. ## Take one end off a corner `move-network-wall-end` moves one end of one segment of a wall. Segment `0` goes from the first point to the second point. `end` is `'start'` or `'end'` of that segment. ```typescript theme={null} export function freeWallEnd(session: BuildingSession): void { session.apply( { kind: 'move-network-wall-end', buildingId: 'building', storeyId: 'ground', wallId: 'room-walls', segmentIndex: 1, end: 'end', position: [7.5, 3], }, session.revision, ); } ``` Only that end moves. The other wall at the corner stays where it is, and the corner is open. If the new position is on a different wall, the end joins that wall. Floorkit cuts the saved wall at the open corner. A closed wall becomes an open wall with the same `id`. An open wall becomes two walls, and the new wall gets the old `id` with a number, such as `room-walls-2`. Each door and window stays in its segment. If the new position is less than 1 mm from the corner, the corner stays closed and moves to that position. ## Move or turn a full wall `transform-wall` turns the wall around `origin` and then moves it by `translation`. ```typescript theme={null} export function shiftWall(session: BuildingSession): void { session.apply( { kind: 'transform-wall', buildingId: 'building', storeyId: 'ground', wallId: 'garden-wall', origin: [0, 7], translation: [0, 1], yawDeg: 0, }, session.revision, ); } ``` ## Other elements Slabs, roofs, stairs and rooms have the same kind of command: `transform-slab`, `transform-roof`, `transform-stair` and `transform-space`. Each one takes an `origin`, a `translation` and a `yawDeg`. To change the shape of one of them, use a `reshape` command. This example makes the slab from [Add a floor slab](/floorkit/guides/add-a-floor-slab) longer and thicker. ```typescript theme={null} export function widenSlab(session: BuildingSession): void { session.apply( { kind: 'reshape-slab', buildingId: 'building', storeyId: 'ground', slabId: 'ground-floor', profile: { outerRing: [ [-0.1, -0.1], [8.1, -0.1], [8.1, 4.1], [-0.1, 4.1], ], }, thickness: 0.25, }, session.revision, ); } ``` | Command | What it sets | | - | - | | `reshape-slab` | `profile` and `thickness` | | `reshape-roof` | `profile` and `baseOffset`, the height of the eave | | `reshape-space` | `profile` and, if you give it, `height` | | `reshape-stair` | `origin` and `baseOffset` and, if you give them, `width`, `treadDepth` and `landingDepth` | A `profile` has an `outerRing` and can have `holes`. Each one is a list of `[x, z]` points in metres. All other data of the element stays the same. ## Replace or remove an element A `reshape` command sets some values of an element. To set all values, send `replace-element` with the full element. The new element must have the same `id`. This example gives the slab a new thickness and puts it lower. ```typescript theme={null} import type { BmSlab } from '@opengeometry/floorkit'; export function replaceSlab(session: BuildingSession, slab: BmSlab): void { session.apply( { kind: 'replace-element', buildingId: 'building', storeyId: 'ground', list: 'slabs', elementId: slab.id, element: { ...slab, thickness: 0.3, baseOffset: -0.3 }, }, session.revision, ); } export function removeSlab(session: BuildingSession, slabId: string): void { session.apply( { kind: 'remove-element', buildingId: 'building', storeyId: 'ground', list: 'slabs', elementId: slabId, }, session.revision, ); } ``` | Command | What you give | What it rejects | | - | - | - | | `add-element` | The `list` and the new `element` | An id that the list has | | `replace-element` | The `list`, the `elementId` and the full new `element` | An id that the list does not have, and a new element with a different id | | `remove-element` | The `list` and the `elementId` | An id that the list does not have | `list` is `slabs`, `roofs`, `spaces`, `stairs`, `columns`, `beams`, `railings` or `instances`. Assets and storeys have the same three kinds of command. | Command | What you give | What it rejects | | - | - | - | | `add-asset` | The `assetId` and the `asset` | An id that exists | | `replace-asset` | The `assetId` and the new `asset` | An id that does not exist | | `remove-asset` | The `assetId` | An id that does not exist, and an asset that an instance uses | | `add-storey` | The `buildingId` and the full `storey` | A storey id that the building has | | `remove-storey` | The `buildingId` and the `storeyId` | The last storey of a building | | `replace-storey-settings` | The `storeyId` and the `settings`: `name`, `elevation`, `height` and `usage` together | A storey id that the building does not have | A [`ProjectManager`](/floorkit/api/editor/project-manager) also rejects a `remove-storey` of a storey that a plan view names. To send several commands as one step, put them in a `batch`. See [A batch of commands](/floorkit/concepts/editing-and-history#a-batch-of-commands). ## Rules * Positions are `[x, z]` in metres. `yawDeg` is in degrees, and a positive angle turns +X to +Z. * Doors and windows stay in their wall and keep their `offset`. * If a wall becomes too short for one of its openings, `apply` throws an error and nothing changes. ## Handle a rejected edit ```typescript theme={null} import { BmEditError, BuildingSessionError } from '@opengeometry/floorkit'; export function tryEdit(run: () => void): string | undefined { try { run(); return undefined; } catch (error) { if (error instanceof BuildingSessionError || error instanceof BmEditError) return error.message; throw error; } } ``` Show the message to the user. The building is the same as before the edit. ## Next Go back one edit. Let the user do these edits with the pointer. # Save and reopen Source: https://docs.ploterr.com/floorkit/guides/save-and-reopen Save the project as text and open it again later. A project is one piece of JSON text. It holds the building model, the views and the sheets. Your app stores the text where it wants: in a database, in a file or in the browser. ## Before you start You have a project. See [Create a plan view](/floorkit/guides/create-a-plan-view). ## Open the project A `ProjectManager` holds one open project. It builds the building, and it keeps the views and the sheets. Add `manager.root` to your scene one time. ```typescript theme={null} import type * as THREE from 'three'; import { ProjectFileError, ProjectManager, type ProjectFile } from '@opengeometry/floorkit'; export function openManager(scene: THREE.Scene, project: ProjectFile): ProjectManager { const manager = new ProjectManager(project); scene.add(manager.root); return manager; } ``` ## Save `save()` returns the project as text, with the current building. ```typescript theme={null} export function save(manager: ProjectManager): string { return manager.save(); } ``` ## Reopen `ProjectManager.open` reads the text, checks it and builds the building. Open the new manager first. Then remove the old one. ```typescript theme={null} export function closeManager(scene: THREE.Scene, manager: ProjectManager): void { scene.remove(manager.root); manager.dispose(); } export function reopen(scene: THREE.Scene, current: ProjectManager, text: string): ProjectManager { const next = ProjectManager.open(text); closeManager(scene, current); scene.add(next.root); return next; } ``` `dispose()` removes the plan editors of the manager and its session. ## Handle a bad file `ProjectManager.open` throws `ProjectFileError` for a bad file. The error has a `code` and a `path`. ```typescript theme={null} export function tryOpen(text: string): ProjectManager | undefined { try { return ProjectManager.open(text); } catch (error) { if (error instanceof ProjectFileError) { console.error(`${error.code} at ${error.path}`); return undefined; } throw error; } } ``` A file can be valid and have a building that Floorkit cannot build. Then `ProjectManager.open` throws `BmBuildError` or `Error`. ## What the file holds | In the file | Not in the file | | - | - | | The building model | Meshes and other geometry | | The saved views | The linework of the views | | The sheets and their viewports | Undo history | Floorkit makes the things in the right column again when you open the file. ## Rules * Do not use `JSON.parse` and a type cast in place of `ProjectManager.open`. Only Floorkit checks the file. * Files from earlier Floorkit versions open. Floorkit writes them in the current format when you save. * Keep the old manager until the new one is ready. If the new project does not open, the user still has the old one. * You do not copy the building into the project before you save. `save()` reads the current building. ## Without the manager If your app makes the `BuildingSession` itself, use `saveProject` and `openProject`. See the [project file API](/floorkit/api/exports/project-file). ## Next Give the sheets to other people. See each member of the manager and the project commands. # Start a building Source: https://docs.ploterr.com/floorkit/guides/start-a-building Make an empty building with one storey and show it in your scene. Each guide in this section adds one thing to a building. This guide makes the building that the other guides use. ## Before you start * You did the [installation](/floorkit/installation) and the geometry kernel is running. * You have a Three.js scene. ## Make the building The model has one building with the id `building` and one storey with the id `ground`. The storey is 3 metres high. ```typescript theme={null} import * as THREE from 'three'; import { BuildingSession, type BuildingModel } from '@opengeometry/floorkit'; export const emptyBuilding: BuildingModel = { schema: 'floorkit.building-model', version: 3, units: 'meters', upAxis: 'y', id: 'my-building', buildings: [ { id: 'building', storeys: [{ id: 'ground', name: 'Ground floor', elevation: 0, height: 3 }], }, ], }; ``` A session builds the geometry and applies your edits. Add its `root` to your scene one time. The root stays the same after each edit. ```typescript theme={null} export function startBuilding(scene: THREE.Scene): BuildingSession { const session = new BuildingSession(emptyBuilding); scene.add(session.root); return session; } ``` The scene is still empty, because the storey has no elements. Now [draw a wall](/floorkit/guides/draw-a-wall). ## Change the document Each element has edit commands. You send a command to the session, and the session changes that element. You keep the session and its undo history. The guides for slabs, roofs, stairs, columns and rooms add their element with the `add-element` command. This function removes one element with `remove-element`: ```typescript theme={null} import type { ElementLists } from '@opengeometry/floorkit'; export function removeElement( session: BuildingSession, list: keyof ElementLists, elementId: string, ): void { session.apply( { kind: 'remove-element', buildingId: 'building', storeyId: 'ground', list, elementId }, session.revision, ); } ``` To load a different building, or to change many elements at one time, change the document and open a new session with it: ```typescript theme={null} export function replaceBuilding( scene: THREE.Scene, current: BuildingSession, change: (model: BuildingModel) => void, ): BuildingSession { const model = current.snapshot(); change(model); const next = new BuildingSession(model); scene.remove(current.root); current.dispose(); scene.add(next.root); return next; } ``` * `snapshot()` gives you a copy of the model. You can change the copy safely. * If the new model is not valid, `new BuildingSession` throws an error. The old session is still in the scene. * A new session starts with an empty undo history. Use a command for a change of one element. Use `replaceBuilding` to load or replace a whole building. [Model a mass](/floorkit/guides/model-a-mass) uses this function. ## Close the building When the user leaves the page, remove the root and release the geometry: ```typescript theme={null} export function closeBuilding(scene: THREE.Scene, session: BuildingSession): void { scene.remove(session.root); session.dispose(); } ``` # Undo and redo Source: https://docs.ploterr.com/floorkit/guides/undo-and-redo Go back one edit, or do it again. A session keeps each edit in a history. Undo and redo are one call each. Floorkit builds the geometry of that step again, so the 3D model and the drawings always agree with the model. ## Before you start You have a session and you applied one edit or more. ## Add the two actions ```typescript theme={null} import type { BuildingSession } from '@opengeometry/floorkit'; export function undo(session: BuildingSession): void { if (session.canUndo) session.undo(session.revision); } export function redo(session: BuildingSession): void { if (session.canRedo) session.redo(session.revision); } ``` ## Keep your buttons up to date `subscribe` calls your function after each edit, undo or redo. It returns a function that stops the calls. ```typescript theme={null} export function watchHistory( session: BuildingSession, undoButton: HTMLButtonElement, redoButton: HTMLButtonElement, ): () => void { const refresh = (): void => { undoButton.disabled = !session.canUndo; redoButton.disabled = !session.canRedo; }; refresh(); return session.subscribe(refresh); } ``` ## Rules * A new edit clears the redo list. * `undo` and `redo` return the new revision number, or `null` when there is nothing to do. * The history is not saved in the project file. A session that you open from a file starts with an empty history. * After an undo, get elements again from `session.currentBuild` by their ids. A changed element can be a new object, and Floorkit then disposes the old object. A changed wall, and a door, a window or a hole on a wall, can also stay the same object with new geometry. ## Next Keep the building between visits. See each member of a session. # Use your Three.js scene Source: https://docs.ploterr.com/floorkit/guides/use-your-threejs-scene Show Floorkit geometry in the scene, camera and renderer that you own. Floorkit does not take over your 3D view. It makes Three.js objects, and you add them to your scene. Your lights, your camera and your render loop stay as they are. For an application with views, sheets, and saving, use [ProjectManager](/floorkit/api/editor/project-manager) as described in [How Floorkit works](/floorkit/concepts/architecture). This guide shows the direct `BuildingSession` route. ## Before you start You did the [installation](/floorkit/installation). ## The sequence Make the scene, the camera, the renderer and the camera controls. Put the canvas in an element that has a width and a height. Await `new Floorkit().setupOpenGeometry(wasmURL)` one time. Do this before you create geometry. Create a `BuildingSession` and add `session.root` to the scene. Render the scene in your loop, as you do for other objects. ```typescript theme={null} import * as THREE from 'three'; import { BuildingSession, Floorkit, type BuildingModel } from '@opengeometry/floorkit'; export async function showBuilding( scene: THREE.Scene, wasmURL: string, model: BuildingModel, ): Promise { await new Floorkit().setupOpenGeometry(wasmURL); const session = new BuildingSession(model); scene.add(session.root); return session; } ``` ## Find an element in the scene After a build, you can get the 3D object of an element by its id in the model. ```typescript theme={null} export function elementObject(session: BuildingSession, id: string): THREE.Object3D | undefined { return session.currentBuild.elementsById.get(id); } ``` Read the object again by its id after an edit. A changed element can be a new object; unchanged elements keep their objects. See [Editing and history](/floorkit/concepts/editing-and-history#keep-an-edit-fast). ## Text labels Datums, dimensions and room labels draw text. Before you create them, give Floorkit your scene and a perspective camera: ```typescript theme={null} import { setGlyphScene } from '@opengeometry/floorkit'; export async function prepareText(scene: THREE.Scene, camera: THREE.PerspectiveCamera) { await setGlyphScene(scene, camera); } ``` ## Clean up When the view closes, release things in this sequence: 1. Stop your render loop and remove your listeners. 2. Dispose the editors, if you use them. 3. Remove `session.root` from the scene and call `session.dispose()`. 4. Dispose your controls and your renderer. ## Rules * Do not move, turn or delete the objects below `session.root`. Use an [edit command](/floorkit/concepts/editing-and-history). * The storey groups belong to the session. An object that you add to a storey group is not kept when the session builds that storey again. * Do not put your own object in place of an entry of `session.currentBuild.elementsById`. The session removes and disposes that object when it replaces the element. * Use one copy of Three.js for your app and for Floorkit. * If you do not have a scene, [`OpenThree`](/floorkit/api/views/open-three) gives you a ready viewer. ## Next Let users edit in this scene. Fix a blank canvas. # Installation Source: https://docs.ploterr.com/floorkit/installation Add Floorkit to a browser app with one npm install. Floorkit is a public package on npm. You do not need a token or a private registry. ## Requirements * A browser app that uses a bundler, such as Vite. * [Three.js](https://threejs.org) `0.168`. Floorkit makes Three.js objects and your app shows them. * TypeScript is optional. The package includes its type declarations. For a project from an empty folder, follow the [quickstart](/floorkit/quickstart). Floorkit is ESM; browser geometry needs WebAssembly. You can validate building JSON outside a browser without starting the kernel. ## Install the package ```bash theme={null} npm install @opengeometry/floorkit three@^0.168.0 camera-controls@^2.9.0 opengeometry@^2.0.18 npm install --save-dev @types/three@^0.168.0 ``` `opengeometry` is the geometry kernel. Floorkit installs it as a dependency. Install it directly too, because the next step imports its WebAssembly file. The kernel is a WebAssembly file. Give its address to Floorkit one time, when your app starts. With Vite, the `?url` import gives you the address. ```typescript theme={null} import { Floorkit } from '@opengeometry/floorkit'; import wasmURL from 'opengeometry/opengeometry_bg.wasm?url'; export async function startFloorkit(): Promise { await new Floorkit().setupOpenGeometry(wasmURL); } ``` Wait for this call to finish before you create geometry. TypeScript must know the `?url` import. A new Vite app knows it already: its `tsconfig.json` has `"types": ["vite/client"]`. If yours does not, put this line in `src/vite-env.d.ts`: ```text theme={null} /// ``` ## Use one copy of Three.js The text package currently depends on a different Three.js version. Tell npm to use your app's version everywhere. Add this field to your app's `package.json`, then run the install commands above: ```json theme={null} { "overrides": { "three": "$three" } } ``` `npm ls three` should show one version. If you installed before adding the override, follow [Troubleshooting](/floorkit/troubleshooting). ## Use a different bundler Floorkit needs only the address of `opengeometry_bg.wasm`. Each bundler that can copy a file and give you its address works. You can also copy the file into your public folder and pass its path as text. ## Import from one place All classes, functions and types come from the package root: ```typescript theme={null} import { BuildingSession, type BuildingModel } from '@opengeometry/floorkit'; ``` ## Next steps Show a room with a door and a window. Fix an install or a start-up problem. # Licensing Source: https://docs.ploterr.com/floorkit/licensing Develop locally without a key. Apply a valid hostname license key before deploying any Floorkit app or demo. Floorkit is proprietary software. You can install the public package and develop locally without a key. Every deployed website needs a valid key for its hostname, including a public prototype or non-commercial demo. ## When you need a key | Where Floorkit runs | Requirement | | - | - | | `localhost`, `*.localhost`, or a loopback address | No key; keep the badge visible | | Tests, builds, and scripts outside a browser | No key; exported files keep the badge | | Any deployed website | A valid key for that hostname | | Teaching or research on a deployed website | Ask about academic terms and a hostname key | A trial key lets you evaluate deployment for 14 days and keeps the **made with floorkit** badge. A paid key removes the badge. Paid keys have a 30-day technical grace period after expiry; trial keys do not. Ask about a trial, academic terms, or a commercial license. Startups and solo businesses receive special pricing. The package's `LICENSE.md` contains the terms. Keep the badge visible wherever the SDK shows it unless your valid paid key removes it. ## Apply your key In a Vite app, put the key in your app's `.env.local`: ```text theme={null} VITE_FLOORKIT_LICENSE_KEY=your-hostname-license-key ``` Restart Vite after changing the file. This key is delivered with your browser app; it is not a server API credential or a signing key. Apply it and await verification before the first view or export: ```typescript theme={null} import { Floorkit, setLicenseKey } from '@opengeometry/floorkit'; import wasmURL from 'opengeometry/opengeometry_bg.wasm?url'; export async function startFloorkit(): Promise { const key = import.meta.env.VITE_FLOORKIT_LICENSE_KEY; const floorkit = new Floorkit({ licenseKey: key }); await setLicenseKey(key); await floorkit.setupOpenGeometry(wasmURL); } ``` * Give the key exactly as issued, without extra spaces or line breaks. * Use HTTPS on a named domain covered by the key. * Apply the key separately in each worker that uses the SDK. * A page on your LAN address is not a localhost page. See [Troubleshooting](/floorkit/troubleshooting). ## Handle a missing or invalid key On a deployed hostname without a valid key, views stop after five seconds and SDK exports throw `LicenseError`. A wrong-domain, invalid, or expired key is also refused. Saving the project remains available, so your app can preserve the user's work. Read `getLicenseState()` to show the cause. See the [license key API](/floorkit/api/licensing/license-key) for the states and errors. ## Usage reporting The SDK sends limited usage reports to `https://license.ploterr.com`. Reports contain no project content or page paths. * Development reporting is an anonymous daily report with a machine identifier and SDK version. Set `telemetry: false` in the `Floorkit` options to disable it. * Deployed reporting includes the hostname, SDK version, license status, and key ID when available. It also runs with a paid key and cannot be disabled. * Non-browser runs send no reports. Failure to reach the reporting service does not change license validity. For a strict content security policy, allow `https://license.ploterr.com` in `connect-src`. License verification itself runs in the browser. ## Next See the functions, states, and error types. Diagnose a hostname, key, badge, or export problem. # Site massing Source: https://docs.ploterr.com/floorkit/playbooks/site-massing Try a 600 m² site study. Change building size, check floor area and zoning limits, and compare design options with Floorkit. **Site massing** tests the size and placement of buildings before designing rooms and walls. Use simple volumes to explore floor area, height and the limits of a site. Try this **600 m² parcel** with a shop, homes above it and a second building. No code is needed to use the example. Floorkit supplies the editable model and checks; your app supplies the site rules. [Try the study](#try-the-worked-study) · [Build with an agent](#build-this-with-a-coding-agent) · [Developer recipes](#build-the-app) ## Try the worked study Use **Controls** to edit buildings, review **Results**, or save and download **Options**. ### Compare two options 1. In **Edit**, choose **Add second building** once. Floor area becomes **384 m²** and FAR **0.64**. 2. In **Options**, name this **Baseline** and choose **Save option**. 3. Return to **Edit**, select the tower in **Building part**, enter **9 m** for **Part height**, and press Enter. Floor area becomes **432 m²**, FAR **0.72**, and the study reports one issue. 4. Save this as **Taller tower** in **Options**. Compare the cards or use **Reopen** to return to either design. To undo instead, close the Controls drawer with **×** if it is open, then choose **Undo**. Switch between **Plan** and **3D** to edit the same design. Turn **Envelope on** in Edit to show the supplied setback and height limits. ## Understand the results The example uses an illustrative FAR limit of **0.7**, coverage limit of **40%** and height limit of **15 m**. On this 600 m² parcel, FAR 0.7 allows **420 m² of counted floor area**. The baseline uses 384 m², leaving 36 m² below that limit. The taller option uses 432 m², exceeding it by 12 m² even though its height is below 15 m. | State | Gross / counted floor area | FAR | Coverage | Maximum height | Supplied checks | | - | - | - | - | - | - | | Podium and tower only | 192 m² | 0.32 | 16% | 10 m | Pass | | After adding the second building | 384 m² | 0.64 | 24% | 12 m | Pass | | Tower part raised to 9 m | 432 m² | 0.72 | 24% | 13 m | `exceeds-far` | | After undoing the height change | 384 m² | 0.64 | 24% | 12 m | Pass | **Pass** means the design meets the checks encoded for this example. It does not establish planning approval, structural support, daylight, access or whether homes and parking spaces physically fit. Use the applicable site rules and record their source before applying the method to a real project. To test another constraint, set **Front setback** to **5 m**. Both buildings are 4 m from the front edge, so both fail that setback check. **Undo** restores the original 3 m rule. ## Common questions Add the second building, then raise the tower part from 6 m to 9 m. Watch floor area and FAR change. Use Undo to return to the previous design. FAR measures counted floor area across all floors relative to the parcel. Coverage measures land covered at ground level. The baseline uses 384 m² on a 600 m² parcel: FAR 0.64 and 24% coverage. Another floor can increase FAR without increasing coverage. The sample FAR limit allows 420 m². The taller option has 432 m², so it exceeds that limit by 12 m². Its 13 m height is still below the 15 m height limit. Results shows the affected rule. No. They are sample inputs for learning. Use the applicable parcel measurements and planning controls for your site. A passing report covers the supplied checks; it is not planning approval. See the [calculation limits](#recheck-zoning-after-each-change) before relying on a result. In Options, save each design with a name. Reopen returns to an option saved in this session. Choose Saved options in Download to keep the designs and assumptions before closing the page. This example has no file-import control; a Floorkit app can reopen a downloaded project. No. The baseline has 288 m² of residential floor area. At 80% efficiency and 60 m² per home, the estimate gives three homes. It does not test room layouts, stairs, daylight or parking fit. Change those assumptions in Options. ## Understand the terms | Term | Meaning in this study | Example | | - | - | - | | Parcel | The piece of land being studied. | 30 × 20 m = 600 m². | | Footprint | The outline of a building part when seen from above. | The ground-level shop is 12 × 8 m = 96 m². | | Storey | One floor or level of a building. | The smaller part above the shop has two storeys. | | Podium and tower | A wider lower part with a smaller part stacked above it. | A shop below two residential floors. | | Gross floor area | The sum of the modelled floor areas across all storeys. | The first building has 192 m² across three storeys. | | FAR (floor area ratio) | The floor area counted by the rules divided by the parcel area. | 384 ÷ 600 = 0.64. | | Site coverage | The share of the parcel covered by the building footprints at ground level. | 144 ÷ 600 = 24%. | | Setback | A required distance between a building and a parcel edge. | A front setback of 3 m. | | Buildable envelope | A volume showing the supplied setback and height constraints. | An outline in Plan and a volume in 3D. | The study counts all gross floor area toward FAR. Actual area definitions and exclusions depend on the rules for your site. ## Build this with a coding agent If you want your own site feasibility app, give a coding agent the brief below. For an AI agent that changes designs inside your app, also follow [Agent building workflow](/floorkit/agents/agent-building-workflow): your app provides the AI model and supported editing tools. The recipes use massing APIs planned for **Floorkit 1.2.0**. Check the installed package and its `index.d.ts` against the [changelog](/floorkit/changelog) before using them. A docs page does not establish that the release is published. Give the agent the [documentation index](https://docs.ploterr.com/llms.txt), [this playbook as Markdown](https://docs.ploterr.com/floorkit/playbooks/site-massing.md) and [documentation MCP setup](/floorkit/agents/use-a-coding-agent#connect-documentation-search-through-mcp). These resources help it read the APIs; the documentation MCP does not edit a design. ```text theme={null} Build a browser-based site massing app with @opengeometry/floorkit. Read https://docs.ploterr.com/llms.txt and https://docs.ploterr.com/floorkit/playbooks/site-massing.md first. Check the installed version and index.d.ts for the APIs used in this playbook. If an API is missing, report it before implementing an alternative. Reproduce the playbook's 30 × 20 m parcel at flat grade Y = 0: a retail podium with a residential tower and a separate four-storey building. Use the exact model, authored IDs, footprints and controls from the recipes. Label the supplied FAR 0.7, coverage 40%, height 15 m, setbacks 3 m and separation 4 m as illustrative. Do not invent rules for a real site. Initialize the geometry kernel once. Keep one ProjectManager for the open study and connect the plan and 3D editors to it. Apply supported commands through the manager; preserve IDs and one undo history. Keep geometry in metres, Y up, with footprint points [x, z]. Validate unknown JSON and check stale proposals before applying agent edits, following Agent building workflow. Show quantities, all named zoning violations and visible edit errors. Verify the two-building baseline: 384 m², FAR 0.64, coverage 24%, height 12 m. Set the tower part to 9 m: 432 m², FAR 0.72, total height 13 m, exceeds-far. Undo must restore the baseline. Front setback 5 m must flag both buildings. Save named options with their editable project, rules and assumptions. Compare equivalent site/rule bases; flag different bases. Reopen an option, restore its assumptions and state that saved files do not retain undo history. Export editable JSON, a site plan PDF, envelope-free GLB and report CSV. Treat estimated unit counts as arithmetic assumptions, not a physical fit test. Run and verify locally. Report the package version, assumptions, unresolved checks and verification results. Follow the hostname licensing requirements before deploying. Do not claim the example establishes planning approval. ``` ## Build the app The following recipes are for developers. They reproduce the interactive example above. For a smaller first step, use [Model a mass](/floorkit/guides/model-a-mass). ### Prepare the app Complete the [installation](/floorkit/installation) and initialize the geometry kernel before constructing a `ProjectManager`. You need a Three.js scene, a container for the plan editor, and a panel for quantities and violations. A deployed app also needs a [hostname license key](/floorkit/licensing). Keep one `ProjectManager` per open option. Send changes through `manager.apply`, and connect both editors to that manager so the model, views, saving and undo share one history. Geometry uses metres, Y is up, and footprint points are `[x, z]`. ### Define the parcel and its controls This worked study uses a 30 × 20 m parcel with a retail podium and residential tower. Its controls are illustrative inputs: FAR 0.7, coverage 40%, height 15 m, setbacks 3 m, and building separation 4 m. Supply the applicable values and keep their source and date in your app; Floorkit's presets contain example numbers. The `lotLines` entries correspond to consecutive outer-ring edges, including the last edge back to the first point. Here the edge from `[0, 0]` to `[30, 0]` is the front. Explicit classifications let the zoning evaluator apply each class's setback directly. The study assumes a surveyed parcel with no holes and **flat grade at world Y = 0**. FAR and coverage use the full net parcel area, 600 m², after subtracting any parcel holes. This is not the area inside the setbacks. If the applicable controls require deductions such as road reservations, establish that area basis in your app before comparing results. Record the rule source, effective date, grade datum and unresolved constraints alongside each study. The example labels its controls as illustrative; access, daylight, terrain and parking fit are not evaluated. ```typescript theme={null} import { ProjectManager, basicZoningRules, evaluateZoning, irMassToMassLike, massBuildingCommand, massSegmentHeightCommand, type BuildingModel, type ManagedPlanEditor, type ProjectFile, type ZoningReport, } from '@opengeometry/floorkit'; export const siteModel: BuildingModel = { schema: 'floorkit.building-model', version: 3, units: 'meters', upAxis: 'y', id: 'site-study', site: { id: 'parcel', boundary: { outerRing: [ [0, 0], [30, 0], [30, 20], [0, 20], ], }, lotLines: ['front', 'side', 'rear', 'side'], elevation: 0, trueNorthDeg: 0, zoning: basicZoningRules({ id: 'study-controls', maxFAR: 0.7, maxCoverage: 0.4, maxHeight: 15, setbacks: { front: 3, side: 3, rear: 3 }, minSeparation: 4, countsTowardFAR: ['gross'], }), }, buildings: [ { id: 'podium-tower', name: 'Podium and tower', storeys: [ { id: 'ground', elevation: 0, height: 4, usage: 'retail' }, { id: 'first', elevation: 4, height: 3, usage: 'residential' }, { id: 'second', elevation: 7, height: 3, usage: 'residential' }, ], mass: { id: 'podium-tower-mass', elevation: 0, segments: [ { id: 'podium', name: 'Podium', footprint: { outerRing: [ [4, 4], [16, 4], [16, 12], [4, 12], ], }, height: 4, use: 'retail', storeyIds: ['ground'], }, { id: 'tower', name: 'Tower', footprint: { outerRing: [ [6, 5], [14, 5], [14, 11], [6, 11], ], }, height: 6, use: 'residential', storeyIds: ['first', 'second'], }, ], }, }, ], }; ``` A building holds one mass. Each mass is a stack of parts; each part names its storeys and has a height equal to their summed heights. Preserve these authored IDs when editing. Courtyards are `holes` in a segment footprint. Two towers on a common podium are not one native branching mass. Use separate buildings and partition the shared podium so its floor area is counted once, or model one podium with separate towers starting at its top. Recheck collisions, separation and aggregate quantities. Never duplicate the full podium under each tower: that doubles its floor area. The worked study uses a single podium/tower stack and a separate building at grade. ### Open a project and show the site Use a saved plan view with `showMassing: true`. It draws every building's mass, the parcel and the dashed setback boundary. The parcel and setbacks are display geometry and do not catch clicks. ```typescript theme={null} export const siteProject: ProjectFile = { schema: 'floorkit.project', version: 3, buildingModel: siteModel, views: [ { id: 'site-plan', name: 'Site massing', kind: 'plan', storeyId: 'ground', cutHeightAboveStorey: 1.2, showMassing: true, }, ], paper: { version: 1, unitSystem: 'metric', sheets: [ { id: 'site-sheet', name: 'Site study', number: 'S-01', page: { format: 'A3', orientation: 'landscape', size: { width: 420, height: 297 }, margin: 10, }, viewports: [ { id: 'site-viewport', viewId: 'site-plan', drawingNumber: '1', frame: { x: 20, y: 20, width: 380, height: 245 }, scale: 0.01, rotationDeg: 0, clip: true, title: { text: 'Site massing', showScale: true }, }, ], annotations: [], titleblock: { templateId: 'default', values: { sheetNumber: 'S-01' } }, }, ], }, }; export function openStudy(onError?: (error: unknown) => void): ProjectManager { const manager = new ProjectManager(siteProject, { onError }); manager.setBuildableEnvelope(true); return manager; } export async function mountSitePlan( manager: ProjectManager, container: HTMLElement, ): Promise { return manager.mountPlanEditor(container, 'site-plan'); } ``` Add `manager.root` to your Three.js scene once. Bind a `GUIEditor` with `manager.bindSceneEditor` as in [Add a 3D editor](/floorkit/guides/add-a-3d-editor). The envelope switch shows the buildable volume in 3D and refreshes after a site edit. It is a display setting; enable it again after reopening. A dashed plan setback shows the boundary at grade, so also evaluate height and inclined envelope planes numerically. Pass a visible error handler to `openStudy` so a refused editor action explains its reason beside the model. When both editors stay mounted, route history shortcuts through the app once and clear the inactive editor's selection; hiding its canvas alone does not disable its keyboard handlers. ### Add and edit buildings For a known footprint, make a command from the current model. This second building has four residential storeys on a 6 × 8 m footprint, 4 m from the podium. ```typescript theme={null} export function addSecondBuilding(manager: ProjectManager): void { const command = massBuildingCommand( manager.session.snapshot(), [ [20, 4], [26, 4], [26, 12], [20, 12], ], { name: 'Second building', storeyHeight: 3, storeyCount: 4, use: 'residential' }, ); manager.apply(command); } export async function drawBuilding(plan: ManagedPlanEditor): Promise { return plan.startMassDrawing({ storeyHeight: 3, storeyCount: 4, use: 'residential' }); } export function raiseTower(manager: ProjectManager, height: number): void { const command = massSegmentHeightCommand( manager.session.snapshot(), 'podium-tower', 'tower', height, ); if (command) manager.apply(command); } ``` Call `addSecondBuilding` once for the worked study. For interactive creation, call `drawBuilding` instead: click to add corners, click the first corner or press Enter to finish, and Escape to cancel. Completion adds a new building as one undo step. `startOutlineDrawing` returns a polygon without saving it when your app needs its own creation flow. | Action | Plan editor | 3D editor | | - | - | - | | Select a part | Click its outline or region | Click the part | | Change the footprint | Drag a corner or edge anchor | Drag a footprint anchor | | Move or turn | Use the selected part's controls | Use the selected part's controls | | Change height | A number field calls `raiseTower` | Push/pull the height anchor | | Delete | Delete removes the selected part | Delete removes the selected part | Move, turn and Delete act on the selected part. Deleting the last part removes the mass; it does not remove the building. Use `remove-building` to remove a whole building. The manager refuses removal while a saved view references its storey; change or remove that view first and dispose any editor mounted on it. At least one building must remain. A gesture saves one command and one undo step. Height changes snap to whole storeys; shrinking cannot drop a storey containing elements or referenced by a view. For a custom footprint or storey edit, send `replace-mass` with both the mass and the complete storey settings. Use `manager.undo()` and `manager.redo()` for the shared history. Read the selected building and part before choosing the edit. Widen a ground-level footprint to increase floor area and coverage; widening only the tower increases floor area while this study's coverage stays unchanged. Add storeys to increase floor area and height without changing coverage. Inspect the affected quantities and named violations, then undo or retain the change. Geometry accepted by the editor does not establish structural support, circulation, daylight, core layout or parking fit. ### Recheck zoning after each change Evaluate all masses together, so the result includes total ratios, collisions and separation. Model validation and zoning are separate: the SDK allows a design outside the parcel or over the limits so the user can inspect and repair it. ```typescript theme={null} export function studyReport(manager: ProjectManager): ZoningReport { const { site, buildings } = manager.session.snapshot(); if (!site?.boundary || !site.zoning) throw new Error('The study needs a parcel and controls.'); const masses = buildings.flatMap((building) => building.mass ? [irMassToMassLike(building)] : [], ); return evaluateZoning( { profile: site.boundary, lotLines: site.lotLines, elevation: site.elevation }, masses, site.zoning, ); } export function watchStudy( manager: ProjectManager, render: (report: ZoningReport) => void, ): () => void { render(studyReport(manager)); return manager.subscribe(() => render(studyReport(manager))); } ``` Display `metrics`, `compliant`, and the full `violations` list. Each violation can name a `ruleId`, `massId`, pair of `masses`, or lot `edgeIndex`, plus `current` and `limit` when available. `failureClass` identifies one priority failure for a repair loop; it does not replace the list. An incomplete rule basis can produce `missing-zoning-basis`. `watchStudy` refreshes after committed edits, undo and redo. It returns an unsubscribe function; call it when closing the study. Do not apply another command synchronously inside a subscriber. During a drag, the editor's `onDrag` exposes the live target before commit: read the live mass's `quantities()` for an area preview, then use `studyReport` for the saved design. The saved snapshot still describes the previous committed state during the gesture. Compare the report with the [expected study results](#understand-the-results). Coverage currently counts a mass's lowest footprint only when its base elevation equals site grade. A mass starting above or below grade can understate coverage. The worked study keeps every mass base at grade; check coverage separately for basements, raised towers and split levels. The grade setback picture also does not establish compliance with an inclined envelope at the top of a building. ### Change the parcel or a rule Build a site form in your app. Managed editors have no site-boundary anchors. `replace-site` replaces the whole site, so keep the fields you intend to retain. ```typescript theme={null} export function setFrontSetback(manager: ProjectManager, metres: number): void { const site = manager.session.snapshot().site; if (!site?.zoning) throw new Error('The study needs zoning controls.'); const rules = site.zoning.rules.map((rule) => rule.kind === 'envelope-plane' && rule.edgeClass === 'front' ? { ...rule, offsetAtGrade: metres } : rule, ); manager.apply({ kind: 'replace-site', site: { ...site, zoning: { ...site.zoning, rules } } }); } ``` Set the front setback to 5 m. Both buildings start at `z = 4`, so the report shows setback violations and the displayed boundary changes. Undo restores the 3 m control. Keep geographic coordinates in `geoAnchor` and local metre coordinates in the boundary; `trueNorthDeg` records north, but does not provide a sun or shadow analysis. ### Estimate feasibility and compare options Use `report.metrics.perMass` for the area by storey, use and area kind. Gross floor area and counted floor area are different: `countsTowardFAR` defaults to `['gross']`. Mark parking, basement, core or mechanical storeys with `areaKind` through `replace-storey-settings`, supplying all their settings. Labels such as `usage: 'parking'` describe use; set the area kind explicitly when the counting assumption matters. A storey's floor area is its segment footprint area after subtracting courtyard holes; gross floor area sums those storey areas. Programme use describes what the area is for, while `areaKind` controls its counting category. An explicit `areaKind` takes priority over inference. Automatic basement classification requires the whole storey's top to be at or below grade, not merely its base. Keep efficiency, average unit size, parking ratios and targets in your app. For example, after adding the second building the residential area is 288 m² (96 m² in the tower and 192 m² in the second building). With an assumed 80% efficiency and 60 m² average unit size, it gives 230.4 m² estimated usable residential area and three whole units. ```typescript theme={null} export interface StudyAssumptions { residentialEfficiency: number; averageUnitArea: number; } export const defaultAssumptions: StudyAssumptions = { residentialEfficiency: 0.8, averageUnitArea: 60, }; export function residentialEstimate(report: ZoningReport, assumptions = defaultAssumptions) { const grossResidentialArea = report.metrics.perMass.reduce( (sum, mass) => sum + (mass.grossFloorAreaByUse.residential ?? 0), 0, ); if ( !Number.isFinite(assumptions.residentialEfficiency) || assumptions.residentialEfficiency <= 0 || assumptions.residentialEfficiency > 1 || !Number.isFinite(assumptions.averageUnitArea) || assumptions.averageUnitArea <= 0 ) throw new Error( 'Efficiency must be above 0 and at most 1; average unit area must be positive.', ); const estimatedUsableArea = grossResidentialArea * assumptions.residentialEfficiency; return { grossResidentialArea, estimatedUsableArea, estimatedUnits: Math.floor(estimatedUsableArea / assumptions.averageUnitArea), }; } ``` Label these as estimates and show the assumptions beside the results. An efficiency multiplier does not measure NIA or prove that units, cores or parking spaces fit. Save each option as a separate project with `manager.save()`. The project format holds one building model; store option names, comparison rows and assumptions in your app rather than adding unknown fields to Floorkit's JSON. Compare the reports under the same site and rule set. Use `ProjectManager.open(text)` to reopen an option, construct the replacement before disposing the old manager, and remount its editors. Undo history is not saved. ```typescript theme={null} export interface StudyOption { name: string; projectText: string; assumptions: StudyAssumptions; siteBasis: string; report: ZoningReport; } export function captureOption( manager: ProjectManager, name: string, assumptions: StudyAssumptions, ): StudyOption { const report = studyReport(manager); residentialEstimate(report, assumptions); return { name, projectText: manager.save(), assumptions: { ...assumptions }, siteBasis: JSON.stringify(manager.session.snapshot().site), report, }; } ``` For a concrete comparison, add the second building and save **Baseline**. Raise the tower to 9 m and save **Taller tower**. Keep efficiency at 80% and average unit area at 60 m² for both: Baseline has 384 m², FAR 0.64 and three estimated units; Taller tower has 432 m², FAR 0.72, four estimated units and an FAR violation. Both have 24% coverage. Show the height, counted area and complete violations beside these figures. If an option's `siteBasis` differs, mark it as a different study basis instead of ranking it as an equivalent alternative. Save the app's option records with their assumptions alongside each editable project. ### Deliver the study Save the editable project as the source of the design. Use a sheet viewport referencing `site-plan` to deliver a site plan PDF; `showMassing` carries through to sheets and PDF. See [Compose a sheet](/floorkit/guides/compose-a-sheet) and [Export a PDF](/floorkit/guides/export-a-pdf). The worked project includes an A3 site sheet at 1:100. `manager.exportSheet('site-sheet', 'pdf')` exports it. Deliver the option records, assumption and rule basis, and comparison report alongside the editable project and drawings. For a GLB, follow [Export a GLB](/floorkit/guides/export-a-glb), passing `manager.root` to Three.js's exporter. Call `manager.setBuildableEnvelope(false)` before exporting the root: a raw GLB includes the visible envelope. GLB keeps geometry and colours; the project JSON keeps the editable design. Your app can export the report and comparison table as CSV. When moving to detailed design, `massToShell` produces walls, slabs and flat roofs. It reuses storey IDs and creates a different building ID. Replace the mass building in a copied project, update views that explicitly name the old building, then open the new project. This starts a new history; keep the massing option separately. See [Turn the mass into walls, slabs and roofs](/floorkit/guides/model-a-mass#turn-the-mass-into-walls-slabs-and-roofs). Sections and elevations exclude masses. Use the massing plan and 3D view for this stage, or create a shell for physical cuts. Terrain, neighbour context, map tiles and sun/shadow analysis require app-side work. ### Check the finished workflow * Open the worked project, add the second building and confirm the pass row above. * Raise the tower to 9 m, show `exceeds-far`, and undo to the previous quantities. * Draw another building; Escape cancels without saving, while completion is one undo step. * Select and reshape the tower in 2D, then change its height in 3D; confirm both views follow the same model. * Change the front setback to 5 m, show the violations, and undo the rule change. * Save and reopen; confirm the site, rules, masses and massing plan persist, then enable the envelope again. * Export the site plan PDF and a GLB with the envelope hidden; retain the editable project alongside them. ## API references * [Site](/floorkit/api/world/site) and [zoning rules](/floorkit/api/world/zoning-rules) * [Zoning evaluation](/floorkit/api/world/zoning-evaluation) and [buildable envelope](/floorkit/api/world/zoning-envelope) * [Massing helpers](/floorkit/api/architecture/massing-helpers) and [Building quantities](/floorkit/api/architecture/building) * [ProjectManager](/floorkit/api/editor/project-manager) and [ManagedPlanEditor](/floorkit/api/editor/managed-plan-editor) # Quickstart Source: https://docs.ploterr.com/floorkit/quickstart Create a Vite app, describe a room as data, show it in 3D, and move a door with one command. In this quickstart you write a small building as a document: four walls, a door, a window and a floor. Floorkit builds the 3D model from it. Then you move the door with one command. Use a modern browser and a Node.js version supported by [Vite](https://vite.dev/guide/): 20.19+ or 22.12+. If you have a Vite TypeScript app already, skip the first step. Create a plain TypeScript app. No UI framework is required. ```bash theme={null} npm create vite@latest floorkit-app -- --template vanilla-ts --no-interactive cd floorkit-app ``` Follow [Installation](/floorkit/installation) in this folder to install Floorkit and its peer packages. The template supplies the `dev` and `build` scripts used below. Put this in `index.html`: ```html theme={null} Floorkit quickstart
```
Put this in `src/main.ts`. The document has one building with one storey. Positions are `[x, z]` in metres. ```typescript theme={null} import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; import { BuildingSession, Floorkit, type BuildingModel } from '@opengeometry/floorkit'; import wasmURL from 'opengeometry/opengeometry_bg.wasm?url'; const model: BuildingModel = { schema: 'floorkit.building-model', version: 3, units: 'meters', upAxis: 'y', id: 'my-first-building', buildings: [ { id: 'building', storeys: [ { id: 'ground', elevation: 0, height: 3, walls: [ { id: 'outer-walls', points: [ [0, 0], [6, 0], [6, 4], [0, 4], ], closed: true, thickness: 0.2, openings: [ { id: 'front-door', kind: 'door', offset: 1.5, width: 0.9, height: 2.1 }, { id: 'front-window', kind: 'window', offset: 4.2, width: 1.2, height: 1.2, sillHeight: 0.9, }, ], }, ], slabs: [ { id: 'ground-floor', profile: { outerRing: [ [-0.1, -0.1], [6.1, -0.1], [6.1, 4.1], [-0.1, 4.1], ], }, thickness: 0.2, baseOffset: -0.2, }, ], }, ], }, ], }; ``` The wall path is the centre line of the walls. The floor outline is 0.1 metres outside that path, so the floor ends at the outer face of the 0.2 metre walls. Add this below the document. A `BuildingSession` builds the geometry. Its `root` is a Three.js group that you add to your scene. ```typescript theme={null} async function main(): Promise { const container = document.getElementById('app'); if (!container) throw new Error('The #app element is missing.'); await new Floorkit().setupOpenGeometry(wasmURL); const scene = new THREE.Scene(); scene.background = new THREE.Color(0xf3f4f6); scene.add(new THREE.AmbientLight(0xffffff, 1)); const sun = new THREE.DirectionalLight(0xffffff, 2); sun.position.set(4, 8, 6); scene.add(sun); const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 100); camera.position.set(9, 6, 9); const renderer = new THREE.WebGLRenderer({ antialias: true }); container.appendChild(renderer.domElement); const controls = new OrbitControls(camera, renderer.domElement); controls.target.set(3, 1.2, 2); const session = new BuildingSession(model); scene.add(session.root); const resize = (): void => { const width = Math.max(1, container.clientWidth); const height = Math.max(1, container.clientHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.setSize(width, height); camera.aspect = width / height; camera.updateProjectionMatrix(); }; new ResizeObserver(resize).observe(container); resize(); renderer.setAnimationLoop(() => { controls.update(); renderer.render(scene, camera); }); } main().catch((error: unknown) => console.error(error)); ``` ```bash theme={null} npm run dev ``` Open the localhost address that Vite prints. You see a room with a door and a window. Drag to turn the view and scroll to zoom. Run `npm run build` to check the TypeScript and produce your app. A deployed hostname needs a valid [license key](/floorkit/licensing). An edit is a command. This command moves the door one metre along its wall. Floorkit cuts the wall again and keeps the edit for undo. ```typescript theme={null} function moveDoor(session: BuildingSession): void { session.apply( { kind: 'move-hosted-opening', buildingId: 'building', storeyId: 'ground', wallId: 'outer-walls', openingId: 'front-door', offset: 2.5, }, session.revision, ); } ``` Call `moveDoor(session)` in `main`, after `scene.add(session.root)`.
## What you did * You wrote the building as a typed document. The door and the window are entries in their wall, and the wall has real openings for them. * You gave the document to a `BuildingSession`. It checked the document and built the geometry. * You changed the building with a command. You did not move a mesh. ## Next steps For generated building JSON, continue with the [agent building workflow](/floorkit/agents/agent-building-workflow). For a saved project with views, sheets, and editors, use [ProjectManager](/floorkit/api/editor/project-manager). Learn the parts that you used. Start the modelling guides. Make a drawing from this room. Put the drawing on a sheet and export it. # Troubleshooting Source: https://docs.ploterr.com/floorkit/troubleshooting Find the cause of a problem with the install, the start-up, the view or an edit. Find the heading that agrees with what you see. Each one gives the cause and the correction. ## The install stops with an authentication error `@opengeometry/floorkit` is a public package and needs no token. This error has one usual cause: an `.npmrc` file sends an old token to the npm registry. The file can be in your app, in your home folder or in your CI. Renew the token or remove it. Then install again. ## The geometry kernel does not start Look at the network panel of your browser and find the request for the `.wasm` file. * **404 or an HTML page:** the address is wrong. Pass the correct address to `setupOpenGeometry`. With Vite, import the file with `?url`. See [Installation](/floorkit/installation). * **The request is correct but geometry fails:** you created geometry before the setup finished. Await `setupOpenGeometry()` first. ## The console says "Multiple instances of Three.js being imported" Your app has two copies of Three.js. The text package that Floorkit uses asks for a newer Three.js than your app has, so npm installs a second copy for it. The app runs, but it is larger, and text is made by a different copy than your scene. Tell npm to use your copy everywhere. Add this to the `package.json` of your app: ```json theme={null} { "overrides": { "three": "$three" } } ``` Then delete `node_modules` and `package-lock.json` and install again. `npm ls three` must show one version. ## An error says "Scene not assigned" You created a datum, a dimension or a room label before Floorkit had a scene for its text. Call `await setGlyphScene(scene, camera)` after the kernel setup and before you create these objects. See [Use your Three.js scene](/floorkit/guides/use-your-threejs-scene#text-labels). ## The canvas is blank Check these things in sequence: 1. The container of the canvas has a width and a height. 2. Your render loop calls `renderer.render(scene, camera)`. 3. `session.root` is in the scene. 4. The camera looks at the building. A building near `[0, 0, 0]` is not visible from inside a wall. 5. The scene has a light. ## A view stops and shows "This site needs a floorkit license" The page is on a deployed site and has no valid license key. Floorkit is free on `localhost`. Each other hostname needs a key, and this includes an address on your local network such as `192.168.1.20`. Read `getLicenseState()` and see [Licensing](/floorkit/licensing). ## A view goes blank and shows no message The "made with floorkit" badge is hidden or covered by your interface. Keep the bottom-right corner of the view free. The view draws again when the badge is visible. ## A door or a window does not cut its wall In a building model, a door must be an entry in the `openings` of its wall. A door in `unhostedOpenings` is a free door and cuts nothing. With element classes, call `wall.attachDoor(door, ...)` and then `wall.commitOpeningCuts()`. A door that you only add to the scene cuts nothing. ## `apply` throws an error | Error | Cause | Correction | | - | - | - | | `BuildingSessionError`, `stale-revision` | A different edit came first | Start the action again from the current state | | `BmEditError`, `invalid-target` | The command names an id that is not in the model | Check the building, storey and element ids | | `BmEditError`, `invalid-geometry` | The result is not possible, such as a door that does not fit | Show the message to the user | After each of these errors, the building is the same as before the edit. ## A change is gone after I reopen the project The change was not in the model that you saved. Two causes are usual: * You changed a mesh or an element object directly. Use an [edit command](/floorkit/concepts/editing-and-history). * You saved an old model. Put `session.snapshot()` into the project before you call `saveProject`. ## A view cannot be drawn `drawProjectView` and `exportProjectPdf` throw `ProjectDrawingError` with a `code`. | `code` | Cause | | - | - | | `unknown-view` | No saved view has this id | | `unknown-storey`, `unknown-element` | The view names a storey or an element that is not in the model | | `empty-view`, `empty-filter` | The view contains nothing to draw | | `unsupported-view`, `unsupported-element` | Floorkit cannot draw this view or this element yet | | `inconsistent-build` | The project and the build that you gave are not the same model | For a section, `unsupported-element` can also mean that the cut line or the far limit of the view touches a wall face or the edge of an opening. Move the line by some centimetres. See [Cut a section](/floorkit/guides/cut-a-section#rules). ## An export fails because of raster views A sheet shows an isometric view, and its image is missing or old. Call [`prepareProjectRasterViews`](/floorkit/api/views/prepare-project-raster-views) after the last model change, and pass the result to the export in `rasterViews`. ## Memory grows when I close and open views Something was not disposed. When a view closes, stop the render loop, dispose the editors, remove `session.root`, call `session.dispose()` and dispose your renderer. See [Clean up](/floorkit/guides/use-your-threejs-scene#clean-up). When you ask for help, send the package version, your browser, the function that fails and its full error message. Our team answers on [Discord](https://discord.gg/zCCbputRYp). # What you can build Source: https://docs.ploterr.com/floorkit/use-cases Choose a Floorkit workflow for AI floor-plan generation, browser CAD, architectural drawings, or editable property plans. Floorkit is a browser-based TypeScript SDK for building design applications. Your app owns its interface and storage. Floorkit supplies the building model, geometry, editing, and drawings. Start with the [quickstart](/floorkit/quickstart), then choose a workflow below. Each path uses the public package and the same saved building data. Turn an agent's proposed JSON into a design that people can edit. Add building authoring to your own Three.js scene. Make plans, sections, and scaled drawing sheets from one building. Edit a floor plan, label supplied room outlines, and read their areas. ## AI floor-plan generation **Input:** a user request and building JSON proposed by your AI model. **Result:** a checked building model, editable geometry, and a saved project file. 1. Define the model contract with [Building model](/floorkit/concepts/building-model) and [Coordinates and units](/floorkit/concepts/coordinates-and-units). 2. Follow the [agent building workflow](/floorkit/agents/agent-building-workflow) to check unknown JSON and return errors for correction. 3. Open the project and mount a [plan editor](/floorkit/guides/add-a-plan-editor) or [3D editor](/floorkit/guides/add-a-3d-editor). 4. Let the agent propose supported commands and let the person review the result. Keep the same project and undo history. 5. [Save and reopen](/floorkit/guides/save-and-reopen) the design. Add sheets when you need a PDF. Floorkit does not supply the AI model or produce a plan from a text prompt by itself. Your app handles inference and design requirements. Model validation and geometry checks do not certify building-code compliance. ## Browser CAD **Input:** your scene, camera, renderer, and typed building data. **Result:** building elements in your scene, with supported 2D and 3D edits. 1. [Use your Three.js scene](/floorkit/guides/use-your-threejs-scene). Await kernel initialization before creating geometry. 2. Use `ProjectManager` for one project with building data, views, sheets, and history. Use `BuildingSession` when your app owns the project structure separately. 3. Mount a [plan editor](/floorkit/guides/add-a-plan-editor) and connect a [3D editor](/floorkit/guides/add-a-3d-editor) to that same project. 4. Use [edit commands](/floorkit/concepts/editing-and-history) for saved changes. Your own Three.js objects can remain in your scene. 5. Dispose the editors and project when the view closes. Your app disposes its own renderer and controls. You supply the toolbar, storage, and application workflow. For general solid modelling without building elements, consider the [OpenGeometry kernel](https://docs.opengeometry.io/OpenGeometry/what-is-opengeometry). ## Architecture and BIM tools For early site feasibility, follow the [Site massing playbook](/floorkit/playbooks/site-massing) to compare building footprints, heights, floor area and supplied zoning controls before modelling walls and rooms. **Input:** storeys, building elements, saved view definitions, and a sheet layout. **Result:** plans, straight sections, exterior elevations, and PDF drawing sheets. 1. [Start a building](/floorkit/guides/start-a-building), add [walls](/floorkit/guides/draw-a-wall), [doors](/floorkit/guides/add-a-door), and [storeys](/floorkit/guides/add-a-storey). 2. [Create a plan view](/floorkit/guides/create-a-plan-view), then [cut a section or create an elevation](/floorkit/guides/cut-a-section). 3. [Compose a sheet](/floorkit/guides/compose-a-sheet) with scales, dimensions, notes, and a title block. 4. [Export a PDF](/floorkit/guides/export-a-pdf) or [GLB model](/floorkit/guides/export-a-glb). 5. Save the JSON project too. A PDF or GLB is not an editable Floorkit project. IFC and DXF exports are coming in the next release. Use the currently available outputs when your app needs to ship now. Sections use straight cuts; other drawing limits are in [Views and sheets](/floorkit/concepts/drawings). Floorkit supplies geometry and drawings, not structural or MEP certification. ## Property apps and space planning **Input:** a building model and room outlines supplied by your app or user. **Result:** an editable property plan, room labels and areas, and a design that can be reopened. 1. Create a project with a [plan view](/floorkit/guides/create-a-plan-view). 2. [Add a plan editor](/floorkit/guides/add-a-plan-editor) so people can move walls, doors, and windows. 3. [Define a room](/floorkit/guides/define-a-room) with its name, number, and outline. Read its area in square metres. 4. Keep the room outline current when the design changes. Floorkit does not detect rooms automatically from walls. 5. [Save the project](/floorkit/guides/save-and-reopen), then [compose a sheet](/floorkit/guides/compose-a-sheet) and export a PDF when the user asks. Your app decides what an area means for its property or planning workflow. Check measurements and drawings against that workflow before sharing them as official records. ## Try the workflows The [live examples](https://floorkit-examples.ploterr.com/floorkit) show modelling, editing, saving, and drawing exports. Each guide with a live frame links to its corresponding example. For a coding agent, start with [Build with agents](/floorkit/agents/index). For a new developer, start with the [quickstart](/floorkit/quickstart). Local development is free; any deployed hostname needs a valid license key. See [Licensing](/floorkit/licensing). # What is Floorkit? Source: https://docs.ploterr.com/floorkit/what-is-floorkit A browser-based TypeScript SDK for editable building models, floor plans, and drawing sheets. A Ploterr product powered by OpenGeometry. Floorkit is a Ploterr product powered by OpenGeometry. It is a browser-based TypeScript SDK for apps that create and edit building models, floor plans, and drawing sheets. People and AI agents can work with the same saved building data. Your app chooses its workflow and AI provider. Your application owns the user interface. Floorkit gives you the geometry, the building model, the editor and the drawings. ## What is in the box Draw walls, place doors and windows, and shape rooms. Edit the same model in 2D and in 3D, with undo and redo. Make plans, sections and elevations from the model. Put them on sheets with a title block. Export sheets as PDF and the 3D model as GLB. IFC and DXF come in the next release. The building is a typed JSON document. An agent proposes it, and Floorkit checks and builds it. ## Built on OpenGeometry Floorkit uses [OpenGeometry](https://docs.opengeometry.io/OpenGeometry/what-is-opengeometry), a CAD kernel that our team maintains. The kernel runs in the browser as WebAssembly. Choose Floorkit for building elements, architectural editors, saved projects, and sheets. Choose the kernel for lower-level geometry such as extrusion and booleans. Each package has its own API and license. Each wall, opening and room is a real building element with data. It is not only a picture of one. A door cuts its wall, and a wall joins the walls that it touches. ## How it fits into your app | Your app owns | Floorkit gives you | | - | - | | The page, the panels and the tools | Building elements with correct geometry | | The Three.js scene and the renderer | 3D objects that you add to that scene | | Storage and user accounts | One JSON project file to save and open | | The workflow of your product | Edit commands, drawings, sheets and export files | ## Where to use it Build AI floor-plan applications, browser CAD, architecture/BIM tools, and property or space-planning apps. [What you can build](/floorkit/use-cases) connects each use case to the required guides and current limits. ## Start here Add the package to your web app. Create your app and show the first room. Learn the four parts and how data moves between them. Find each class and function. Give your coding agent the docs, or connect an agent to your building workflow. Choose a complete path for your application. Local development is free. Every deployed hostname needs a valid key, including public prototypes and non-commercial demos. See [Licensing](/floorkit/licensing).