Documentation

Pro handbook

Modes

A mode is a tool: it turns pointer and key events into edits. The editor has exactly one active mode at a time (editor.mode, null before the first one is set). Set one through the toolbar or directly:

editor.setMode(new DrawCircleMode({ sides: 64 }));

Modes are classes exported by @vectorstream/editor. A mode instance keeps the state of its current gesture, so create a fresh instance each time you switch to it — the toolbar does that for you. Switching modes discards an unfinished gesture.

Modes change the partition only through transactions. Each finished edit is one undo step and ends in exactly one of the events change, unchanged or rejected, carrying the label listed below. Selecting (with transform or lasso) is not an edit: it emits only selectionchange. A mode only changes the active layer.

Overview#

The 21 entries of defaultToolbarEntries(), in its order (vectorstream Pro adds two: cut-circle and difference-circle):

Name Key Class Does Label(s)
draw P DrawMode Draws a polygon by clicks draw-polygon
draw-rectangle R DrawRectangleMode Draws an axis-aligned rectangle by dragging draw-rectangle
draw-circle C DrawCircleMode Draws a circle (a regular polygon) by dragging draw-circle
draw-line L DrawLineMode Draws a line by clicks draw-line
draw-point N DrawPointMode Places a point (shown as a circle) insert-point
draw-marker M DrawPointMode with form: 'pin' Places a point shown as a pin insert-point
text T TextMode Labels a face or places a text set-label, move-label
edit A EditMode Moves, inserts and removes vertices; moves straight boundary lines move-vertex, insert-vertex, remove-vertex, move-edge
split X SplitMode Splits a face along a chord between two of its vertices split-face
union U UnionMode Merges adjacent faces merge-faces
removal E RemovalMode Removes faces and vertices remove-faces, remove-vertex
transform V TransformMode Selects; moves, rotates, scales the selection move-faces, move-points, move-selection, rotate-faces, scale-faces, resize-circle
simplify I SimplifyMode Removes vertices that barely change a boundary simplify-faces
regularize G RegularizeMode Straightens building outlines to right angles straighten-faces
freehand F FreehandMode Draws a polygon by dragging draw-freehand
stamp-triangle 1 StampMode with DREIECK Places a triangle place-stamp
stamp-star 2 StampMode with STERN Places a five-pointed star place-stamp
cut S CutMode Cuts a drawn area out of everything it covers cut
difference D DifferenceMode Cuts a drawn area out of the selected faces only difference
copy K CopyMode Copies faces by dragging them copy-faces
lasso Q LassoMode Selects the faces a drawn loop encloses — (no edit)

Keys are case-insensitive and only act without Ctrl, Alt and Cmd. See Keyboard for all keys.

Drawing#

draw — polygon#

Click to place vertices; the rubber band shows the next edge. Close by clicking the first vertex again (within the snap radius, once there are at least three) or by pressing Enter. Backspace removes the last vertex (a traced section as a whole), Escape discards the drawing. Each click snaps.

The new outline is inserted into the partition: faces it crosses are split, the parts inside become new faces. An outline that crosses itself is split into its cells. An outline that matches existing geometry exactly reports unchanged. Three points on one line are rejected.

No options.

draw-rectangle — rectangle#

Press at one corner, drag to the opposite corner, release. The rectangle is axis-aligned in map coordinates. Escape during the drag aborts. A drag without width or height is rejected. Both corners snap. No options.

draw-circle — circle#

Press at the center, drag outward, release. The circle is a regular polygon. It is round on the map: the radius is measured with the map's aspect at that latitude. With Shift held, width and height follow the pointer independently (an ellipse). Escape aborts. A click without dragging is rejected.

Option Type Default Meaning
sides number 32 Number of polygon sides. An integer from 3 to 1024; otherwise the constructor throws RangeError.

draw-line — line#

Click to place vertices. A line does not close: clicking the first vertex again adds another vertex. Enter finishes (at least two vertices), Backspace removes the last vertex, Escape discards. Clicks snap and trace like the polygon tool.

Lines are inserted exactly: where a line crosses an edge or itself, a node is inserted; a line that runs from boundary to boundary across a face splits that face. Drawing the same line again reports unchanged. No options.

draw-point and draw-marker — points#

One click places a point. A click on an existing vertex reports unchanged (the point is already there); a click on an edge splits the edge there; a click elsewhere creates a free-standing point. There is no gesture to abort.

draw-marker is the same class with form: 'pin': the point is drawn as a pin and carries the attribute punktform: 'pin', which travels through GeoJSON export and import. A pin cannot sit on a boundary: snap targets on edges are ignored for it, and a pin that lands exactly on an edge or on a vertex with edges is rejected.

Option Type Default Meaning
form 'kreis' | 'pin' 'kreis' How the point is drawn: circle or pin.
name string 'draw-point' The mode's name; must match the toolbar entry ('draw-marker' in the default toolbar).

freehand — freehand polygon#

Press, drag around the area, release: the loop is closed and inserted. The collected points are thinned with the snap tolerance, so a long drag does not produce hundreds of vertices. Escape aborts. No options.

stamp-triangle, stamp-star — stamps#

A stamp places a fixed shape. A click places it at its default size; press and drag from the center sets size and rotation at once (the distance to the pointer is the size, the direction the rotation). Escape aborts the drag. The size is applied equally to both coordinate axes in map units; unlike the circle tool, the stamp does not correct for the map's aspect.

Option Type Default Meaning
template readonly Point[] — (required) The shape: at least three points, of unit size around the origin; fewer throw an Error. DREIECK (triangle) and STERN (star) are exported.
defaultSize number 1 Scale factor applied to the template on a click without dragging, in world units (degrees on a longitude/latitude map).
name string 'stamp' The mode's name; must match the toolbar entry.

text — labels#

A click on a face starts labeling that face; a click elsewhere starts a text at that point. While the text is being typed, nothing is written; on completion, the label is written in one transaction (for a new text: the point and its label together). An empty text on an existing label removes it; an empty text on a new spot writes nothing. A free-standing label can be dragged to move it (move-label).

The input field is your application's: the mode reports where and how the text is edited (bearbeitung()), takes the typed text (tippe(text)), and writes or discards (abschliessen(), verwerfen()). See Labels and attributes for a working field.

TextMode has no constructor options. Three public fields set the style of the next new label: groesse ('klein' | 'mittel' | 'gross' | 'sehr-gross', default 'mittel'), farbe (a CSS color, default '' — the renderer's choice) and fett (default false). gestalte({ groesse, farbe, fett }) changes the style of the text being edited.

Editing#

edit — vertices and boundary lines#

This tool shows a handle at every vertex and at the middle of every edge.

  • Drag a vertex to move it (move-vertex).
  • Drag an edge's middle handle to insert a new vertex there (insert-vertex; a click without dragging inserts it in the middle).
  • Alt+click a vertex to remove it (remove-vertex). A vertex where three or more edges meet cannot be removed.
  • Drag an edge (not its middle handle) to move the whole straight line it belongs to, perpendicular to itself (move-edge). With Shift, the line follows the pointer freely; with Alt, only the grabbed edge moves.
  • Escape aborts a drag.

Shared vertices and edges move for all faces that use them. Clicking an object on another layer makes that layer active first (unless it is locked). No options.

transform — select, move, rotate, scale#

The resting tool, like the arrow in a drawing program.

  • Click a face to select it; Shift+click adds it or removes it again; a click on empty space clears the selection. Selecting is not an edit.
  • The selection gets a frame with handles: the middle handle moves, the corner handles scale, the handle above rotates. Scaling keeps the aspect ratio; with Shift held, width and height scale independently. For a selected circle, a radius handle resizes it (resize-circle).
  • A free-standing point or label is moved by dragging it directly.
  • Escape aborts a drag. The partition only changes on release.

Clicking an object on another layer makes that layer active first (unless it is locked).

Option / field Type Default Meaning
mode (constructor option) 'propagate' | 'detach' — Fixes the behavior for every drag, overriding nachbarnFolgen.
nachbarnFolgen (public field) boolean false false (detach): the selection is cut loose and reinserted; neighbors keep their shape. true (propagate): shared boundaries move along and neighbors deform. Read on every drag, so a UI switch can change it.
setzeDrehpunkt(point | null) (method) (point: Point | null) => void — Fixes the center the next rotation drag turns around, instead of the selection's bounding-box center — e.g. the last point the application snapped to. There is no draggable handle for it; the existing rotation handle is grabbed exactly as before. The pivot is consumed the moment that rotation grab starts (completed or not), so the one after it uses the center again unless this is called once more; null clears an armed pivot before it gets used. An armed pivot is also discarded when the selection changes or the tool is left (exit, including through Editor.destroy()), when Escape is pressed without a drag under way, or when a drag on any other handle is aborted — it is aimed at one selection for one upcoming rotation and does not outlive either.

split — split a face#

Click one vertex of a face, then another vertex of the same face: a straight chord between them splits the face (split-face). If the two vertices lie on different boundaries of the face (say, the outline and a hole), the chord connects them without creating a new face. A click elsewhere, or Escape, abandons the first click. No options.

union — merge faces#

Click a face, then an adjacent face: they merge (merge-faces). Faces that only touch at a point are not adjacent and are rejected. If several faces are selected, a click on one of them merges all of them in one step, in an order that keeps each step adjacent. Escape abandons the first click and clears the selection. No options.

removal — remove#

  • Click a face to remove it; if it is part of the selection, the whole selection is removed (remove-faces).
  • Click a vertex to remove it (remove-vertex).
  • Delete or Backspace removes the selected faces.
  • A click on empty space, or Escape, clears the selection.

A removed face is merged into a neighbor: into the empty outside if it borders it, otherwise into the neighboring face with the smallest ID. In a closed grid of parcels, removing an inner parcel therefore enlarges a neighbor. No options.

simplify — simplify boundaries#

Click a face to remove the vertices of its boundary that lie closer to the line between their two neighbors than the tolerance — least significant first, and only vertices where exactly two edges meet. If the face is selected, the whole selection is simplified in one step. Enter simplifies the selection without a click. Where a dropped vertex lies on a boundary shared with a neighbor, the neighbor is simplified along with it.

Option Type Default Meaning
tolerance number the editor's snap tolerance Distance threshold in world units (degrees on a longitude/latitude map).

regularize — straighten outlines#

For building outlines. Hovering over a face shows the result as a preview (the real computation, rolled back). A click straightens the face, or the whole selection if the face is selected; Enter straightens the selection. Edges within the angle tolerance of the main or cross direction become exactly parallel or perpendicular; clearly oblique edges stay. Small jags disappear; two nearly aligned neighboring edges become one straight wall if no vertex moves farther than the kink threshold. A result that would overlap a neighbor is rejected. Angles and distances are measured in meters on the ground, not in degrees.

Option Type Default Meaning
winkelGrad number 15 Angle tolerance in degrees around the main and cross direction.
zackenMeter number 0.5 Jag tolerance in meters.
knickMeter number 2 Kink threshold in meters.

All three are also public, writable properties of the mode; changing one recomputes the preview. The mode does not limit the values (the sliders of the application in app/ offer 1–30° for the angle).

cut and difference — cut away an area#

Click to place vertices as when drawing a polygon; Enter closes the ring and cuts (at least three vertices). Backspace removes the last vertex, Escape discards. Tracing along existing boundaries happens only while Shift is held (on the click, or on Enter for the closing segment). Map box zoom is switched off while these tools are active.

  • cut removes the area from everything it covers; the cut-away area becomes empty space.
  • difference removes it only from the selected faces. What the ring covers beyond them stays as it was. Without a selection, the gesture is rejected.

A piece can only be cut away if it touches the outside of the data (the outer face): cutting a hole into the middle of a parcel would leave the piece with nowhere to go, and is rejected. No options.

copy — copy faces#

Press on a face, drag, release: a copy of its outline is inserted at the offset (copy-faces). If the face is part of the selection, all selected faces are copied. A click without dragging does nothing. A face with holes or with points inside it cannot be copied: the drag shows no preview, and releasing rejects with a message (copyHoles) — nothing changes. Escape aborts. No options.

lasso — select by loop#

Press and drag a loop around faces; on release, the faces lying completely inside the loop are selected. A loop that encloses nothing clears the selection. This tool never edits. Escape aborts. No options.

Pasting#

Not in the toolbar: editor.kopieren() copies the selected faces (outlines and attributes) to the editor's clipboard — the same path behind Cmd+C and the context menu's "Duplicate"; editor.einfuegen() inserts them offset next to the original, and editor.einfuegenMitZeiger() lets the copy hang from the pointer until a click places it (paste), then returns to the previous mode. editor.massKopieren() and editor.massEinfuegen() copy only width, height and angle of the selection's minimal bounding rectangle and apply them to another selection (paste-dimensions).

A face with a hole or an enclosed point is rejected, the same boundary as the copy tool (see copy): kopieren() returns false and reports exactly one rejected event, labeled copy-shape, with editor.copyHoles; nothing is written to the clipboard, not even the plain faces of the same selection. Until this was fixed, it silently copied only the outer ring, with the hole's content and the enclosed point gone from the copy.

Configuring modes#

Options go into the constructor, and a toolbar entry's create builds a fresh instance with them. This example is a complete custom tool list:

import {
  DrawCircleMode,
  DrawMode,
  EditMode,
  RegularizeMode,
  SimplifyMode,
  StampMode,
  TransformMode,
  type ToolbarEntry,
} from '@vectorstream/editor';

/** A house outline of unit size around the origin, for the stamp. */
const HOUSE = [
  { x: -1, y: -1 },
  { x: 1, y: -1 },
  { x: 1, y: 0.4 },
  { x: 0, y: 1 },
  { x: -1, y: 0.4 },
];

/** Your own tool list: your labels, your keys, your options. `create` must return a fresh mode. */
export function myToolbarEntries(): ToolbarEntry[] {
  return [
    { name: 'draw', label: 'Polygon', shortcut: 'p', create: () => new DrawMode() },
    { name: 'draw-circle', label: 'Circle', shortcut: 'c', create: () => new DrawCircleMode({ sides: 64 }) },
    { name: 'edit', label: 'Vertices', shortcut: 'a', create: () => new EditMode() },
    {
      name: 'transform',
      label: 'Move, rotate, scale',
      shortcut: 'v',
      create: () => {
        const mode = new TransformMode();
        mode.nachbarnFolgen = true; // neighbors follow: shared boundaries move along
        return mode;
      },
    },
    { name: 'simplify', label: 'Simplify', create: () => new SimplifyMode({ tolerance: 0.00005 }) },
    {
      name: 'regularize',
      label: 'Straighten',
      shortcut: 'g',
      create: () => new RegularizeMode({ winkelGrad: 10, zackenMeter: 0.25, knickMeter: 1 }),
    },
    {
      name: 'stamp-house',
      label: 'House',
      shortcut: '1',
      // A click without dragging scales the unit template by `defaultSize` (world units — here
      // degrees); dragging sets size and rotation instead.
      create: () => new StampMode({ template: HOUSE, defaultSize: 0.0002, name: 'stamp-house' }),
    },
  ];
}

Its test switches through all entries and checks the options: a 64-sided circle, the transform switch, the three regularize tolerances, and the stamp's size on a click. In addition, docs/handbuch/beispiele/test/modes.test.ts holds a copy of each option table's keys and checks them against the option types at compile time: if an option type gains, loses or renames a key, the typecheck fails. It does not compare against this page, so a table edited here without editing that test is not caught.

Writing your own mode#

A mode is any object implementing the Mode interface: name, enter(ctx), exit(), the three pointer handlers, onKey, and overlay() for what it draws on top. It changes the partition only through ctx.transaction(label, fn), using the intents of @vectorstream/core. See Mode and ModeContext.

See it on a map.