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.
cutremoves the area from everything it covers; the cut-away area becomes empty space.differenceremoves 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.