Skip to content
MellowCAD

Concepts

On npm

How the engine works

The engine's pieces and how they fit: commands, the drawing, undo, the state, the scene and the ports to the host.

This page is the mental model: what the engine is made of, how a drawing changes, and how a page learns about it. Read it before building a host; the API reference has the details.

The pieces

page ─── calls: runCommand, setActiveTool… ──▶ CadEngine ▲                                                │ └────────── subscribe() + getState() ◀───────────┤                                                  ▼                            runtime: commands, snaps, undo                               │                  │                               ▼                  ▼                            drawing ──────▶     scene                                                  │                                                  ▼                                   presenter ──▶ <canvas>
  • CadEngine is the engine on a page. It owns the canvas, listens to its pointer, wheel and keys, and draws a frame when something changed. It is the only part that knows about the browser.
  • The runtime inside it does the drafting: the commands, their prompts, object snaps, tracking, the selection, undo. It never touches the DOM, which is why the same engine runs headless as CadDriver.
  • The drawing is the model: its objects, layers, styles, blocks and settings.
  • The scene is what to paint, as a flat list of shapes in screen pixels, and a presenter paints it: CanvasScenePresenter on a canvas, SvgScenePresenter as SVG markup.

The page talks to the engine in one direction with calls and hears back in the other through one subscription. There are no events to wire one by one.

Everything is a command

As in AutoCAD, a drawing changes only through commands. LINE, MOVE, TRIM or LAYER each run a sequence of prompts — Specify first point:, Specify next point or [Undo]: — that the user answers with clicks, typed values or keywords. Enter or Space ends a step or repeats the last command; Escape cancels.

A host starts commands the way AutoCAD's own interface does:

  • Typed: runCommand("LINE"), or the user typing on the drawing or in your command line field. A command is found by its name or alias in the command language (L, LINE, LINEA in Spanish), or by _ and its global name in any language (_LINE).
  • From a button: setActiveTool("line"), by the ids of state.tools. It runs the command as a ribbon button's macro would, so the command line echoes _line.
  • From a menu or a key: Delete is deleteSelection() (ERASE), Ctrl+A is selectAll(), and the shortcut menus' entries have methods of their own.

commandCatalog() lists every command with its aliases and a description, and the command reference says what each one asks and does.

The drawing

Objects — lines, arcs, circles, polylines, splines, ellipses, text, dimensions, hatches and block references (EntityType) — are stored in world coordinates. One drawing unit is one unit of the drawing's LengthUnit (millimetres by default); the unit only says how lengths are written, so changing it rescales nothing. Points are Vec2 or Vec3: a flat object keeps its Z beside its points.

Layers hold objects. Layer 0 always exists. An object takes its layer's colour, linetype and lineweight ("ByLayer") unless it has its own. Text and dimension styles are named tables, as in AutoCAD, and blocks are named groups of objects placed by references.

A host never edits the drawing directly. It runs a command, or calls a method that does what a control of AutoCAD's does: setSelectionColor, applyLayerControl, createLayer, setSelectionProperty and the others. That is what keeps undo and the command history right.

CadEngine does not hand out the drawing's objects either. What a host shows is already in the state, worded for the user: the selection's properties as the Properties palette lists them, the layers with how many objects each holds, the blocks. The whole drawing leaves as a file (serializeDrawing(), see Drawings and files). Headless, CadDriver gives the objects themselves (entities()).

Undo

One command, or one gesture, is one undo step: a whole RECTANG, a grip drag, a change from the Properties palette. U (undo()) takes the last step back and writes on the command line what it took back; REDO (redo()) does it again. state.canUndo and state.canRedo say whether there is something to undo or redo.

The host's own calls are steps too: setSelectionColor is one, as AutoCAD's colour drop-down is. Loading a drawing starts an empty history.

The state

getState() returns one immutable snapshot of everything a host may show:

AreaFields (examples)
The promptcommandLine (prompt, typed text, history, suggestions), commandOptions
Dynamic inputdynamicInput: the box at the cursor, its fields and their values
Drafting aidssnapActive, orthoActive, polarActive, gridVisible, snapEnabled…
SelectionselectionCount, properties (the Properties palette), selectionLayerId…
Drawinglayers, currentLayerId, textStyles, dimStyles, blocks, documentUnit…
Menus and dialogsshortcutMenu, snapOverrideMenu, selectionCycling, palette, unitsDialog…
Wordstools (labels and aliases), names (snaps, colours, linetypes, views)

A snapshot is the same object until something changes, and a new one keeps every part that did not change. So a host can compare by identity and redraw only what changed:

let last = engine.getState().layersengine.subscribe(() => {  const { layers } = engine.getState()  if (layers !== last) {    last = layers    renderLayerList(layers)  }})

subscribe calls its listener once per change and returns a function that unsubscribes. A few parts change on every mouse move — coordinates, cursorUcs, dynamicInput — so render them in a small element of their own. @mellowcad/react's useEditorState leaves them out for that reason.

Every word in the state is in the user's language: prompts, tool labels, property rows and the names of snaps and colours. A host shows them and does not keep its own copies.

The scene

The canvas shows a Scene: a background colour and a flat list of ScenePrimitive — segments, arcs, ellipses, paths, text — already projected to screen pixels, with their colours. The engine builds one per frame, and CanvasScenePresenter paints it.

The same scene can become SVG: SvgScenePresenter turns it into markup anywhere, a server included. buildDrawingPreview builds a scene of the objects alone, framed in a box, for thumbnails, and THEME holds the colours the scene uses.

What the engine asks of its host

The engine reaches the outside world through three small interfaces, so it never assumes a browser:

PortWhat it doesOn a page (CadEngine)Headless (CadDriver)
DrawingStoreWhere the drawing is autosavedstore option; none by defaultstore option
FileTransportWhere SAVE writes and OPEN readsA download and a file pickerfiles option
ClipboardWhat COPYCLIP writes, PASTECLIP readsThe system clipboardclipboard option

Each one is a pair of functions, so a host can put a drawing anywhere: see Drawings and files.

Languages

The engine speaks two languages at once, as AutoCAD's language packs do: the interface (tool labels, palette rows, the names of snaps and colours) and the command language (command names, prompts, keywords and what is drawn on the canvas). English is built in; Spanish and Arabic are one import away. See Languages.