> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ploterr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# GUIEditor

> 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

<ResponseField name="onSelect" type="Event<EditableTarget | undefined>">
  Read-only.
</ResponseField>

<ResponseField name="onChange" type="(change: EditorChange) => void">
  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) =&gt; &#123;…&#125;`.
</ResponseField>

<ResponseField name="onDrag" type="(change: EditorChange) => void">
  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) =&gt; &#123;…&#125;`.
</ResponseField>

<ResponseField name="isFreeOpeningRehostArmed" type="boolean">
  Read-only. Whether the next scene click will attempt a saved free-opening attachment.
</ResponseField>

## 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;
}
```

<ResponseField name="ogType" type="string" required />

<ResponseField name="profile" type="Profile">
  Editable outer ring + holes. Absent for circle targets (cylinder).
</ResponseField>

<ResponseField name="extrudeHeight" type="number" />

<ResponseField name="extrudeBase" type="number">
  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.
</ResponseField>

<ResponseField name="activePart" type="string">
  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.
</ResponseField>

<ResponseField name="profileTopology" type="ProfileTopology" />

<ResponseField name="radius" type="number">
  Present (with no `profile`) => a circle target: push/pull + radius handle only.
</ResponseField>

### 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[];
}
```

<ResponseField name="kind" type="EditorChangeKind" required />

<ResponseField name="target" type="EditableTarget" required />

<ResponseField name="anchorId" type="string">
  The `EditAnchor` id, for `kind: 'anchor'` only.
</ResponseField>

<ResponseField name="ogid" type="string">
  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.
</ResponseField>

<ResponseField name="ogids" type="string[]">
  The elements whose built objects the step changed. `ogid` and `target` name the element of the
  last command of the step whose element changed.
</ResponseField>

### 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)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.