NullCad
NullCad is a 2D CAD application purpose-built for cadastral surveying, built with Rust, egui, and a custom wgpu renderer. It is designed to provide a modern, efficient, and precise alternative to the big name CAD programs.
Most CAD software is designed for engineers with surveying being an afterthought or functionality added through seperate plugins that are never quite able to overcome the inherint limitations of the native DXF/DWG file format.
Nullcad is designed from the ground up for the way Land Surveyors actually work.
This book covers how to use NullCad day to day — navigation, drawing and modify tools, snapping, layers, the survey workflow, drafting and PDF output, import/export formats, and the plugin/automation systems.
Where to start
- New to NullCad? Start with Installation and the Interface Overview.
- Setting up a survey project? See Survey Phases.
- Producing a plan of survey for lodgement? See Page Proxies & Viewports and PDF Export.
- Extending NullCad with scripts or an AI agent? See Lua Plugins and MCP Server.
Highlights
- Phase-based survey workflow with independent camera state and status tracking per phase.
- Full snap tracking for easy and efficient drawing and editing of survey geometry.
- Vector PDF export of drafting sheets, with viewports, sheet templates, and title-block substitution.
- Pure-Rust import/export for Shapefile, GeoJSON, DXF, and CSV, with optional GDAL support for KML, GeoPackage, MapInfo TAB, and FileGDB.
- Lua plugin system and an in-process MCP server for scripting and AI agent integration.
- ZIP-based
.nullcadarchive format with full operation history, undo audit trail, and embedded resources.
License
Copyright 2026. All rights reserved.
Installation
The latest release is available from Nullcad.io
| Platform | Formats |
|---|---|
| Linux | AppImage |
| Windows | .exe installer |
Download the installer for your platform from the latest release and run it.
On windows, you will likely see a smart screen warning. This is normal for new software and can be bypassed by clicking “More info” and then “Run anyway”. The binary has not been signed yet, but will be in the future. If you would like to check the integrity of the installer (recommended), you can verify the SHA256 checksum of the binary against the one displayed on the download page.
The command to verify the checksum in powershell is:
Get-FileHash -Algorithm SHA256 <path-to-installer>
The linux AppImage is a self-contained binary that can be run directly and stored anywhere. You may need to make it executable first:
chmod +x <path-to-appimage>
Requirements
- A GPU with wgpu/WebGPU support (essentially any GPU from the last decade, on Vulkan, Metal, DirectX 12, or OpenGL).
- A modern CPU with SSE2 support (essentially any CPU from the last decade).
- Windows 10 or better, or a modern Linux distribution with a recent kernel and graphics drivers.
- At least 2 GB of RAM, but 4 GB or more is recommended for larger surveys.
Interface Overview
NullCad’s main window is organized around a central canvas viewport, surrounded by panels for layers, properties, tools, and phase status.
- Menu bar — Standard menu bar for file operations, the view menu contains all windows for various features of the app.
- Phase tabs — switch between the four survey phases (Setup/Search, Boundary Model, Reinstatement, Drafting). Each phase has independent camera state and status tracking; see Survey Phases.
- Toolbar — grouped by category: Select, Draw, Modify, Survey, Measure, Surface & Drafting. See Drawing Tools and Modify Tools.
- Sign In — sign in to your Nullstrom account, the current open file path is also displayed here.
- Properties panel — inspect and edit the properties of the current selection.
- Layer panel — create, rename, delete, and organize layers with groups; per-layer color, visibility, lock state, and line type. See Layers.
- Tool Option panel — context-sensitive options for the currently active tool.
- Canvas View menu — Toggle visibility of certain elements on the canvas; plans, images, comparisons, labels
- Canvas viewport — the main drawing surface, rendered with a custom wgpu pipeline for precision at large coordinates (see Precision Handling).
- Status bar — coordinates under the cursor, active tool, snap state.
Theme
NullCad is designed around the Nullstrom Design System shared across Nullstrom products.
The default theme is a dark mode with high contrast and color-coded elements.
A light mode is also available from View -> Settings -> Display.
The canvas background is black regardless of the theme, to maximize contrast with survey data.
Distraction-free mode
Press F11 to hide the side panels and show a floating toolbox — useful when you want the canvas to take up the full window.
Settings
Application settings (point size, selection threshold, colors, line weight, label display, camera sensitivity, autosave interval, and more) are available from the Settings menu, organized into categories such as Settings ▸ Interaction and Settings ▸ Labels.
Recent files
The File menu keeps a list of recently opened projects for quick access.
View bookmarks
Save up to 9 camera positions and recall them instantly:
| Action | Input |
|---|---|
| Recall bookmark | 1–9 |
| Set bookmark | Ctrl+1–9 |
Navigation & Controls
Navigation
| Action | Input |
|---|---|
| Pan | Middle mouse drag or Alt+Drag |
| Zoom | Scroll wheel (zooms toward cursor) |
| Zoom window | Shift+Left Click Drag |
| Reset camera | Reset Camera button |
| Zoom Extents | E |
| Frame selected | F |
| View bookmarks | 1–9 to recall, Ctrl+1–9 to set |
Panning and zooming are per-phase — each of the four survey phases keeps its own independent camera position, so switching phases doesn’t lose your place.
Editing
| Action | Input |
|---|---|
| Undo | Ctrl+Z |
| Redo | Ctrl+Y or Ctrl+Shift+Z |
| Delete selected | Delete |
| Transform gizmo (move/rotate selection) | W |
| Toggle console | F12 |
| Toggle Operation Log | Ctrl+H |
Undo/redo operates on the full operation history — every mutation is recorded and reversible. See Operation System.
A full, searchable list of shortcuts is available from the in-app keyboard shortcuts help dialog, and summarized in Keyboard Shortcuts & Controls.
Selection
Basic selection
| Action | Input |
|---|---|
| Select | Click on geometry |
| Multi-select | Ctrl+Click |
| Rectangle select | Click and drag on empty canvas |
| Select all | Ctrl+A |
| Select all on layer | Shift+A |
When clicking an object where multiple objects exist, a menu will appear to confirm the object you wish to select.
Selection uses an R-tree spatial index for efficient hit testing, so selecting and range-querying stays fast even on large datasets.
Hover highlighting
Geometry under the cursor is highlighted before you click, giving visual feedback about what a click will select.
Selection sets
Ctrl+G Save the current selection as a named set and restore it later with Ctrl+Shift+G — useful for
repeatedly working with the same group of geometry (e.g. all boundary
points, or all geometry on a particular layer).
Selection history
Navigate back Ctrl+[ and forward Ctrl+] through previous selections, similar to browser
history.
Select Similar
Shift+S Filter and select geometry by type, layer, code, name, position, or length —
useful for bulk operations like “select all points with code BNDRY” or “all
lines on this layer.”
Context menus
Right-click on geometry or empty canvas for context-sensitive actions.
Drawing Tools
Geometry primitives
NullCad supports the following geometry primitives, all stored with full f64 precision:
- Point
- Line
- Polyline
- Polygon
- Rectangle
- Circle
- Arc
- Text
Draw toolbar
The Draw tool category provides: Point, Line, Polyline, Leader, Polygon, Text, Callout, Circle, Arc, and Rectangle.
Drawing tools respect the active snap configuration — clicks snap to endpoints, midpoints, intersections, grid points, and other configured snap targets by default.
Every draw operation is undoable
Like all mutations in NullCad, drawing goes through the operation system — every draw action is recorded, undoable, and tagged with the active survey phase and user if signed in.
Tool Details
Point
P
The Point tool creates a single point at the clicked location or coordinates can be entered manually in the tool options. Points can be used as survey points, reference points, or for snapping to other geometry.
Line
L
The Line tool creates a straight line between two points. Click to set the start point, then click again to set the end point. The line can be drawn at any angle, and snapping will help align it with existing geometry.
Polyline
Y
The Polyline tool creates a series of connected line segments. Click to set the first point, then click additional points to create segments. Right click and press Finish to end. Polylines are always open, and vertices can be edited after creation. To create a closed shape, use the Polygon tool.
Polygon
G
The Polygon tool creates a closed shape with three or more vertices. Click to set the first point, then click additional points to create the edges. Right click and press Finish to close the polygon. Polygons can be edited after creation, and snapping will help align edges with existing geometry.
Rectangle
Rectangle have 4 separate creation modes, available from the tool options when the tool is active.
- 2-point — click two opposite corners to define the rectangle.
- Centre-Corner — click the center point, then click a corner to define the rectangle.
- 3-point — click three points to define the rectangle. Points 1 and 2 define one edge the 3rd defines height/width.
- Corner+Dims — click one corner, then enter width and height in the tool options to define the rectangle.
Circle
Circles have 3 separate creation modes, available from the tool options when the tool is active.
- 1-point — click the center point, then click a point on the circumference to define the radius or enter radius.
- 2-point — click two points to define the diameter of the circle.
- 3-point — click three points to define the circle. The circle will pass through all three points.
Arc
Arcs have 2 separate creation modes, available from the tool options when the tool is active.
- 3-point — click three points to define the arc. The arc will pass through all three points.
- Centre+Start+End — click the center point, then click the start and end points to define the arc.
Text
T
Text can be added to the canvas by clicking to set the top left anchor, in the tool options you can then set the content of the text and the size.
Once created, selecting the text will allow changing the properties or adding dynamic variables in the properties panel.
Formatting can be changed by double clicking the text to open it for editing, a toolbar will appear at the top of the canvas where you can change properties. The font dropdown will display Text Styles first and then all other system fonts available on the system.
Modify Tools
Basic modify tools
- Move — translate selected geometry.
- Rotate — rotate selected geometry around a point.
- Scale — scale selected geometry.
- Trim/Extend — trim or extend lines/polylines to a boundary. Any geometry with edges can be the boundary — lines, polylines, polygons, circles, arcs and rectangles — whether picked by hand or gathered by “use all visible geometry”.
- Offset — create a parallel copy of a line or polyline at a fixed distance.
Transform gizmo
Press W to activate the transform gizmo on the current selection: a drag-based move/rotate handle with snapping, angle snap, and a relocatable pivot point. This is the fastest way to reposition or reorient a group of geometry interactively.
Explode
Press B to break compound geometry into simpler components — polygons become polylines, polylines become individual lines.
Vertex editing
Most basic geometry can be edited by pressing Space when the object is selected: add, remove, and move
individual vertices after the geometry is created. Pressing Space again or Esc will end the edit mode.
Undo/redo
Every modify operation is recorded in the operation history and fully reversible with Ctrl+Z / Ctrl+Y. See Operation System.
Snapping
S
Snapping is a feature that allows you to easily align points to existing geometry or a grid. It is particularly useful for precision drawing and editing, ensuring that points are placed accurately relative to other elements in the design. Snapping can be configured at any time by pressing S.
NullCad supports several snap modes, which apply while using drawing and modify tools, all snaps below are passive and are computed constantly on hover if they are on:
- Endpoint — snap to the start/end of a line or polyline segment or to a point object.
- A green circle shows position of snap
- Midpoint — snap to the midpoint of a segment.
- A blue triangle shows the position of the snap
- Nearest — snap to the closest point on a geometry.
- An orange square shows the position of the snap
- Grid — snap to a configurable grid spacing.
- A yellow cross shows the position of the snap
- Intersection — passively snap to the intersection of two line segments. This takes into account all visible
geometry in view.
- A red cross shows the position of the snap
- Perpendicular — snap from a base point perpendicular to a line.
- An unfilled orange square shows the position of the snap
- Extension — snap to the extension of a line segment.
- A purple unfilled diamond with a dashed line showing where the extension originates.
- Angle constraint — constrain the next point to specific angle
increments from the previous point.
- The rubber band is constrained to the nearest angle and the angle is displayed near the end of the line
Active snap modes
At any time you may activate two different active snapping modes,
- Intersection —
IThis manual intersection snap allows you to select the two lines you would like to compute the intersection for. - Average —
VThis snap allows you to select two or more points and compute the average position of those points. PressEnterto accept the computed average.
Configuring snapping
S
Snap settings (which modes are active, grid spacing, tolerance) are available from the snap configuration panel and persist with the document state.
You can change the tolerance for snaps at the bottom of the window.
Layers
Layer management
Nullcad has a robust layer management system. There are 3 types of layers available to the user:
- Standard Layer
- Layer Group
- WMS Layer
Pro
Layer properties
Standard layers have:
- Visibility — Hide or show all geometry on the layer
- Lock state — locked layers can’t be edited or selected
- Label visibility — see Annotations & Labels
- Colour — Set the colour of the layer’s geometry, the full ACI palette is available or pick from a custom RGB value
- Name — You can use any layer name you like, it is not required that layers have unique names, nullcad stores a unique identifier for each layer under the hood.
- Line weight — Set the line weight of all geometry on this layer. Uses the CAD standard millimetre denominations.
- Line type — Set the line type of all the geometry on this layer. Select from built in or custom line types.
- Delete button — Remove the layer. If the layer contains geometry you will be asked if you want to move the geometry or delete it along with the layer.
Phase-based filtering
Geometry is tagged with the survey phase it was created in (see Survey Phases), and the canvas filters what’s shown accordingly — you generally only see geometry relevant to the current phase, plus anything explicitly composed into a Drafting viewport.
WMS Layer
Pro Feature
WMS Layers allow for background maps to be displayed easily behind geometry.
You can manage templates from View -> WMS Templates where you can set the
properties of a WMS template, save and edit existing templates.
To add a template WMS layer to the canvas you must have a CRS set in File -> Document Properties
to allow the system to understand the required transformation to apply to the
fetched tiles.
Transformations are applied on the fly through proj4. It is not necessarily the most robust transformation and so should not be relied upon for survey data.
Fetched tiles are stored in an on-disk cache to speed up viewing and limit
the amount of bandwidth use. If you need to clear the cache from stale data
you can press the Clear Cache button at the bottom of the WMS Templates window.
Resources
Nullcad can import and view multiple formats of media separate from pure geometry, including PDFs and Images (JPG, PNG). These resources are stored directly in the save file rather than referenced externally. This keeps the project fully portable.
Resources can be dropped directly onto the canvas alongside geometry.
The Resource Manager
View -> Resources
This is where you import and view all resources attached to a document. Use the +Add Resources button to import resources from your file manager. It will allow selecting one or multiple images or PDFs to import. Once imported, save the document to write them to the archive.
Imported data files will also show in the resource manager.
The resource manager can be searched and filtered for various types. Drag Resources from the Resource Manager window to the canvas to add them, PDF’s will be added from the first page of the PDF, images show as you might expect, either will be scaled to a percentage of the view. Select a resource in the canvas to resize by dragging the handles and move them by dragging anywhere on the image/pdf.
You can change the page of the PDF in the properties panel and you can drop as many as you like of a single resource to show multiple pages of the same document.
You can copy/paste resources in the canvas. The pasted resource will occupy the same position and scale as the original, if you cant find it, its probably stacked on the original, drag it to a new position.
Codelists
Pro Feature
Codelists map survey codes to layers and layer properties (color, line type, line weight). During import, matching a geometry’s code against a codelist auto-creates and assigns the appropriate layer — useful for bringing in field data that uses a consistent coding scheme (e.g. from a data collector).
The Codelist Window
View -> Codelists
The codelist window allows you to manage codelists in the document. To create a new
codelist, enter a name and press Create. If this is the first codelist it will be
automatically set as the active codelist.
Adding codes to the codelist can be done by entering the code and layer in the
fields above the list and pressing +Add.
Once a code is added to the list you can set the properties of the layer.
Auto-manage layers
When a document has an active codelist, the auto-manage feature becomes available.
It can be accessed by selecting the Auto-Manage Layers button above the layers
in the layer panel.
When this mode is active, changing the code field on any geometry automatically
moves the geometry to the layer defined in the codelist. You can also apply these
mappings to all geometry by clicking the Apply to all geometry button in the layer
panel.
Import/Export Codelists
Codelists are stored on the document or Document Template, if you are working from a fresh document you can import bulk codes from a CSV or from a previously exported codelist. The format required can be seen in the tool-tip of the import/export buttons.
TIN Surfaces
NullCad can build Triangulated Irregular Network (TIN) surfaces from selected points, using a Constrained Delaunay Triangulation, and display them as a triangle wireframe, as elevation contours, or both.
Creating a surface
- Activate the TIN Surface tool (SURVEY toolbar group). The TIN panel opens on the right.
- Select the points you want to triangulate (they must have Z values), then click Add Selected Points in the panel.
- Optionally add breaklines: select exactly two points from the list and click Add Breakline from Selection.
- Click Generate TIN Surface. At least 3 points with Z values are required, and the points must not all be collinear.
The surface is created as a single undoable operation on the active layer.
Display modes
Each surface has its own display settings, edited in the Properties panel and saved with the document:
- Triangles — wireframe of the triangulation edges (default).
- Contours — elevation contour lines derived from the mesh.
- Both — edges and contours together.
Contours are controlled by two per-surface values:
- Contour interval — vertical distance between levels, aligned to absolute multiples of the interval (an interval of 0.5 produces levels at …, 10.0, 10.5, 11.0, …).
- Major every — every Nth level is a major contour and is drawn at twice the line weight.
Contours update live when the surface or its settings change, and they follow the display mode in drafting viewports and PDF export. If the interval is small enough to produce more than 2000 levels, contours are hidden and the Properties panel explains why.
Extracting contours
Extract Contours to Polylines (Properties panel) creates one polyline geometry object per contour line, on the surface’s layer, with the contour elevation stored as the polyline’s Z value. The extraction is a single undoable operation. Extracted polylines are ordinary geometry — they can be edited and exported, but they do not update if the surface changes.
Regenerating
Regenerate from Source Points (Properties panel) rebuilds the triangulation from the current positions and elevations of the points the surface was created from. Deleted source points are dropped (at least 3 must remain), breaklines are remapped, and the display settings are kept. The regeneration is undoable.
Non-destructive
Building a TIN surface never modifies the source points — the surface is a
separate geometry object (TinSurface) that stores its own copy of the
vertex data and references the source point IDs for regeneration.
Breaklines
- Hard breaklines force triangle edges to follow the breakline exactly — used for discontinuities like retaining walls or building edges. Two hard breaklines may share endpoints but must not cross each other.
- Soft breaklines are stored with the surface but do not affect the triangulation; they are reserved for future interpolation features.
Snapping and selection
Endpoint snap targets the TIN’s vertices; midpoint, nearest, perpendicular, and intersection snaps target its triangulation edges. Clicking selects the surface by its displayed linework (edges and/or contours), as does rectangle selection.
Export
- PDF — the surface is drawn following its display mode, with major contours stroked heavier; this includes drafting viewports.
- DXF — the surface exports as one
3DFACEentity per triangle with true per-vertex elevations, readable by other CAD packages. - Extracted contour polylines export like any other polyline (e.g. DXF polylines carrying their elevation).
Annotations & Labels
Text annotations
Add positioned text labels with configurable style and anchoring.
Callout annotations
Text with a leader line and configurable arrowhead — useful for pointing at a specific feature from a label placed nearby.
Point labels
Point geometry can be automatically labeled based on its metadata (code, description, name), following cartographic best practices to avoid label overlap.
Label content
- Default format:
{code} {description} - Supported placeholders:
{code},{description},{name} - Empty labels (no content) are automatically hidden
Label placement
- Labels are positioned at the point location, with collision detection to prevent overlaps.
- Priority system: labels on the active layer are placed first.
- After priority sorting, remaining ties are broken top-to-bottom.
Zoom behavior
Labels maintain a constant screen size as you zoom, rather than scaling with world geometry:
- Target screen height: 12.0 px (configurable 8.0–24.0 px).
- World size is clamped between a minimum (default 0.5) and maximum (default 50.0) so labels stop shrinking/growing past those bounds at extreme zoom.
Performance
- Maximum label count (default 1000).
- Only labels within the visible camera bounds are generated (frustum culling).
- A minimum screen-height threshold prevents rendering labels too small to read.
Visibility controls
- Global toggle: Settings ▸ Labels ▸ Enable Point Labels.
- Per-layer toggle: the 🏷 button in the layer panel, next to the lock button. Labels also respect the layer’s own visibility.
Settings reference (Settings ▸ Labels)
| Setting | Range | Purpose |
|---|---|---|
| Enable Point Labels | on/off | Master toggle |
| Max Labels | 100–5000 | Cap on displayed labels |
| Target Screen Height | 8.0–24.0 px | Desired constant on-screen label height |
| Min World Size | 0.1–10.0 | Smallest label size when zoomed in |
| Max World Size | 10.0–200.0 | Largest label size when zoomed out |
| Min Screen Height | 4.0–20.0 px | Culling threshold for tiny labels |
| Label Format | template string | e.g. {code}: {description} |
Usage
- Create points with a code and description (e.g. via CSV import or manually).
- Enable labels: Settings ▸ Labels ▸ Enable Point Labels.
- Toggle per-layer visibility with the 🏷 button in the layer panel.
- Customize the label format string, e.g.
{code}: {description}.
Text Styles
A text style is a named preset for text objects — it bundles font family, bold/italic, default height, colour, and line spacing under a name like “Road Names” or “Lot Numbers”, as traditional CAD does. Styles are stored in the document and save/load with templates, so an office standard set travels with your drawings.
Every new document starts with two styles matching the built-in fonts:
- Standard — Inter
- Monospace — the bundled Roboto Mono
Styles are presets, not links
Applying a style stamps its properties onto the text object at that moment. Text objects keep no reference back to the style, so:
- Editing a style later does not restyle existing text.
- Deleting a style leaves existing text exactly as it is.
To restyle existing text, re-apply the (edited) style to it.
Managing styles
Open the manager from View -> Text Styles. Each style is a row you can
edit in place:
- Name — what appears in the font dropdown.
- Font — Inter and Monospace first, then every font installed on the machine (see Fonts below).
- B / I — bold and italic toggles.
- Height — the default text height, in world units (metres for a survey drawing) — unlike line type patterns, which are paper millimetres.
- Color — full ACI palette or a custom RGB value.
- Spacing — line spacing multiplier for multi-line text (0.5–3.0, 1.0 = normal).
Create a style by typing a name at the top and clicking Create (it starts as Inter with default properties); delete one with ✕.
Applying a style
Double-click a text object (or create one with the Text tool) to enter text editing. The font dropdown in the text-edit toolbar lists the document’s styles at the top — each with a summary of its font, weight, and height — followed by the raw font families. Picking a style stamps all of its properties onto the text, including the height; picking a bare font family changes only the font.
Fonts
Font families come from three sources, scanned once at launch:
- Bundled — Inter and Roboto Mono ship inside NullCad and render identically on every machine.
- User fonts — any font file placed in the
fontsfolder of NullCad’s configuration directory:- Linux:
~/.config/nullcad/fonts - Windows:
%APPDATA%\nullcad\fonts
- Linux:
- System fonts — everything installed on the machine.
Fonts installed while NullCad is running are picked up on the next launch.
If a document asks for a font that isn’t available on the current machine, the text renders in a fallback font rather than disappearing. For drawings that move between machines, prefer the bundled families or install the same fonts (e.g. via the user fonts folder) everywhere the drawing is opened.
Templates
Templates
Most settings and document properties can be saved to a template including:
- Layers
- CRS
- Scale Factor
- Codelists
- WMS Templates
- Linetypes
Save a template through File -> Save As Template
You can then set a default template in View -> Settings -> Templates. This
template will be used whenever you start a new document.
Line Types
Every piece of geometry is drawn with a line type — either one of the built-in patterns or a custom linetype defined in TOML. Line types can be set in two places:
- Per layer — the Line type dropdown in the layer panel applies to all geometry on the layer (see Layers).
- Per geometry — the Linetype dropdown in the properties panel overrides the layer setting for the selected geometry. Choosing Default in the layer dropdown lets each geometry use its own setting.
Built-in line types
Continuous (the default), Dashed, Dotted, Dash-Dot, Dash-Dot-Dot, Center, Hidden, and Phantom.
Custom line types
Nullcad ships a default library of survey-oriented linetypes (Boundary,
Easement, Traverse, Wire Fence, Water, Gas, Sewer, and more) and lets you
define your own in a TOML file. Custom linetypes can include text and
symbols repeated along the line — e.g. a water main drawn as
———— W ———— W ————.
- Import:
File -> Import -> Linetypes (TOML). If an imported name matches an existing linetype, a conflict dialog asks whether to overwrite the existing definition or skip the incoming one. Overwriting keeps existing geometry pointing at the updated definition. - Export:
File -> Export -> Linetypes (TOML)writes the document’s custom linetypes to a file you can share or re-import elsewhere.
Custom linetypes are stored in the document itself (and in templates), so a drawing always carries the definitions it uses.
The TOML format
[linetypes.myline] # Required namespace; the key must be unique
name = "My Line" # Required; shown in the linetype dropdowns
description = "A test linetype" # Optional, shown alongside the name
pattern = [ # Required: the repeating pattern
{ stroke = 3.0 }, # Visible dash, 3 mm long
{ gap = 2.0 }, # Invisible break, 2 mm long
{ text = "E", font_size = 2.5 },
{ symbol = "M -1 -1 L 1 1", scale = 1.0 },
]
A file can contain any number of [linetypes.<key>] tables. The pattern
list repeats along the whole length of the line.
All dimensions are in millimetres of paper at 1:1, scaled on screen and
in output by the current linetype scale — a 3.0 stroke
prints 3 mm long at 1:1, 3 m ground at 1:1000.
Pattern elements
Each entry in pattern is one of five element types, chosen by which keys
it contains:
| Element | Keys | Notes |
|---|---|---|
| Stroke | stroke | Visible dash; the value is its length in mm. |
| Gap | gap | Invisible break; the value is its length in mm. |
| Dot | dot = true | A point marker occupying 0.5 mm of pattern. |
| Text | text, font_size, rotation | Text along the line. font_size is in mm (default 2.5); rotation is in degrees relative to the line direction (default 0). |
| Symbol | symbol, scale, rotation, keep_stroke | A small vector shape (see below). scale defaults to 1.0, rotation to 0. |
Only the keys shown are recognised — e.g. scale on a text element is
silently ignored; use font_size.
Symbols
The symbol value is a path in a small subset of SVG path syntax —
straight lines only:
M x y— move to (absolute)L x y— line to (absolute)m dx dy— move to (relative)l dx dy— line to (relative)
Numbers are whitespace-separated; commas are not accepted — write
"M -1 -1 L 1 1", not "M -1, -1, L 1, 1". Curves and arcs are not
supported. Coordinates are in symbol units around the line: the path is
multiplied by scale, so at scale = 1.0 a coordinate of 1 is 1 mm from
the line. Some examples:
{ symbol = "M -1 -1 L 1 1" } # single tick /
{ symbol = "M -1 -1 L 1 1 M -1 1 L 1 -1" } # X marker
{ symbol = "M -1 -1 L 1 -1 L 1 1 L -1 1 L -1 -1" } # square
By default a symbol replaces a section of the line: it occupies a slot
2 × scale mm wide, punched out of the pattern like a gap. Set
keep_stroke = true to instead draw the symbol on top of a continuous
line without consuming any pattern length — right for tick marks along an
unbroken line, as in the default Building linetype:
[linetypes.building]
name = "Building"
pattern = [
{ stroke = 3.0 },
{ symbol = "M 0 0 L 1 1", scale = 1.0, keep_stroke = true },
]
Linetype scale
Pattern dimensions are paper units, so how large they appear depends on the
linetype scale, set per survey phase from
the 1:… indicator in the status bar:
- Manual — type a scale denominator (e.g.
1:500). Pick the scale you intend to plot at and the plotted pattern matches its definition in mm. - Link to zoom — the scale is recomputed from the current zoom level and your screen PPI (Settings › Display), so patterns keep a constant on-screen size as you zoom. The status bar shows a link icon while active.
Troubleshooting
- “No linetypes found in file” — the file has no
[linetypes.<key>]tables; check the namespace spelling. - Import fails with a parse error — the console (
View -> Console) reports the offending line. Common causes: a missingpattern, a missingname, or commas inside asymbolpath. - Pattern looks solid or invisible — the linetype scale for the current phase is too small or too large for the geometry; adjust it from the status bar.
Document Properties
The document properties should be the first thing checked and filled when starting a new job. It contains properties and settings that affect the rest of job.
File -> Document Properties
Combined Scale Factor
Nullcad is designed around the idea that incoming data from a controller is in grid coordinates, and final plans and data are required to be coordinated. Conversely, distances are required to be plane distances and entered distances from existing plans are also in plane. Traditionally, data is scaled to plane after import from a controller for calculations and surveyors must rescale data back to grid to attain final coordinates.
Nullcad solves this by keeping your data in grid and using a global CSF to dynamically scale entered distances back to grid and show or label distances on geometry in plane. The scale factor can be entered manually in the General tab of the Document Properties window. You can also calculate a CSF by selecting and existing point in your document, normally a known control point.
Note: when calculating a CSF make sure the point used has a Z/Height.
NullCad uses the following formula for MGA (Map Grid of Australia) / UTM projections.
Formula
CSF = PSF × HSF
Where:
- CSF = Combined Scale Factor
- PSF = Point Scale Factor (accounts for map projection distortion)
- HSF = Height Scale Factor (accounts for elevation above the ellipsoid)
Point Scale Factor (PSF)
PSF = 0.9996 + 1.23 × (E - 500000)² × 10⁻¹⁴
Where:
- E = Easting coordinate (metres)
- 500000 = False easting (central meridian offset for UTM/MGA zones)
- 0.9996 = Central meridian scale factor (k₀) for UTM
PSF equals exactly 0.9996 at the central meridian (E = 500,000m) and increases as you move east or west.
Height Scale Factor (HSF)
HSF = 1 - (H × 0.1571 × 10⁻⁶)
Where:
- H = Ellipsoidal height (metres)
- 0.1571 × 10⁻⁶ ≈ 1 / 6,371,000 (approximation of 1/Earth radius)
HSF reduces distances as elevation increases, since higher points are further from Earth’s centre.
Where to configure it
The active CSF is set in the Document Properties and applied by all tools that take a distance.
Caveat
This formula is a simplified approximation suitable for most surveying work within a single UTM/MGA zone. For high-precision geodetic work spanning zone boundaries or requiring rigorous error bounds, use the full Transverse Mercator equations instead.
Survey Phases
NullCad organizes work into four phases, each with independent camera state and status tracking. Geometry and operations are tagged with the phase they’re created in, so switching phases filters what’s shown and keeps work-in-progress in one phase from cluttering another.
| Phase | Purpose |
|---|---|
| Setup/Search | A virtual desk for laying out plans, research, and annotations before modeling begins. |
| Boundary Model | Building the surveyable model from historical plans. This is the default phase. |
| Reinstatement | Comparing field measurements against the boundary model and adjusting. |
| Drafting | Finalizing the plan for submission — page proxies, viewports, PDF export. |
Switching phases
Use the phase tabs at the top of the window. Each phase remembers its own camera pan/zoom position independently, so returning to a phase puts you back where you left off.
Why phase-based?
Cadastral survey work naturally moves through stages — research, modeling, field verification, and final drafting — each with different working material and different geometry. Tagging geometry by phase lets each stage stay visually uncluttered by the others, while still keeping everything in one document with a single operation history and undo/redo stack.
Phase status tracking
Each phase tracks its own completion status, useful for keeping a survey project’s overall progress visible at a glance.
Related tools
- Traverse Tool — used mainly in Boundary Model, to build the model from bearings and distances.
- Comparison Tool — used in Reinstatement, to compare field measurements against the model.
- Page Proxies & Viewports — used in Drafting, to lay out final sheets.
Traverse Tool
The Traverse tool builds connected survey traverses by entering bearings and distances, rather than clicking points on the canvas — the standard way boundary geometry is specified on a plan of survey.
Basic use
- Select the Traverse tool from the Survey tool category.
- Enter a starting point (or snap to an existing one).
- Enter a bearing and distance for each subsequent leg.
- Each leg is added to a connected polyline as you go.
Bearing format
Bearings are entered and displayed in degrees-minutes-seconds (DMS), rounded to the nearest second — the surveying standard — not decimal degrees.
The default input is HP notation (DDD.MMSS), where the digits after
the decimal point encode minutes and seconds rather than a decimal fraction
of a degree:
| Input | Parsed as |
|---|---|
123.2312 | 123°23’12“ |
45.30 | 45°30’00“ |
90 | 90°00’00“ |
45 30 15 | 45°30’15“ (space-separated DMS also accepted) |
To enter true decimal degrees instead, prefix the value with d (e.g.
d45.5 → 45.5° = 45°30’00“). Append * to reverse a bearing by 180°.
Scale factor support
The Traverse tool supports a Combined Scale Factor (CSF) for grid-to-ground distance conversion, so entered distances can be treated as either ground distances or grid distances depending on how your survey data was recorded. See Combined Scale Factor for the formula and when to apply it.
Where this fits in the workflow
The Traverse tool is most commonly used in the Boundary Model phase, building the surveyable model from bearings and distances taken off historical plans. See Survey Phases.
Comparison Tool
The Comparison tool compares field measurements against the boundary model, using bearing/distance input — the core of the Reinstatement phase (see Survey Phases).
Purpose
After building a boundary model from historical plans, field survey work verifies that model against measurements taken on the ground. The Comparison tool records those field bearing/distance observations against model points and stores the comparison results with the document.
Comparison storage
Comparisons are stored in the document (document/comparison.rs), so
comparison history is preserved with the project and available for review
or reporting alongside the rest of the survey data.
Bearing format
As with the Traverse tool, bearings are entered in DMS/HP
notation (DDD.MMSS), not decimal degrees. The comparison table displays
the survey bearing, measured bearing, and the delta between them each
rounded to the nearest arcsecond (e.g. 045° 30' 00") rather than showing
fractional seconds, keeping the table readable.
Related
- Traverse Tool — used to build the original boundary model.
- Combined Scale Factor — relevant when field measurements need grid/ground conversion before comparison.
Page Proxies & Viewports
The Drafting phase (see Survey Phases) is where a survey project becomes a finished plan for lodgement. Two geometry types drive this: page proxies and viewports.
Page proxies
A page proxy lays out a drafting sheet at a chosen draw scale, on one of the standard paper sizes:
- ISO A-series (A0–A4)
- US (Letter, Legal, Tabloid, …)
- ANSI (A–E)
- ARCH (A–E)
Each page proxy becomes one page in the exported PDF (see PDF Export). A page can be assigned a sheet template (a title block) — see below.
Sheet templates
A sheet template supplies the title-block linework and text placed on top
of a page’s content, with substitutable fields like <PROJECT>, sheet
number, and sheet name.
Sheet templates can be imported directly from a DXF file’s paper-space
layout: NullCad recovers the layout’s true physical paper size from the
DXF’s embedded plot-settings (not just its arbitrary limits/extents), and
rescales non-millimeter content to fit. INSERT block references (common
for real title blocks) are expanded recursively.
Assigning a template to a sheet resizes the page to the smallest standard paper size that fully contains the template, so the template’s linework — laid out in its own physical millimeter coordinates — always ends up inside the visible page rather than centered against a mismatched size.
The Sheet Manager (accessible from the Drafting phase) lists all sheets in the document, lets you assign templates, fill in title-block field values, and reorder/renumber sheets.
Viewports
A viewport is a window placed on a Drafting-phase page that displays content from another phase (most commonly Boundary Model), with its own independent pan, zoom, and rotation — similar to a viewport in traditional CAD software. This is how survey/model geometry actually ends up on a printed sheet: you draft the page and title block in the Drafting phase, then open one or more viewports onto the phase that holds the real geometry.
Draw scale
Each page proxy has its own draw scale, so different sheets in the same document can be laid out at different scales (e.g. an overview sheet at 1:1000 and a detail sheet at 1:200). Linetype patterns (dashes, dots) on the Drafting-phase canvas follow whichever page’s draw scale the camera is currently nearest to, so on-screen dash spacing matches what will actually print on that sheet.
PDF Export
NullCad exports drafting sheets as a multi-page vector PDF — geometry is written as real vector paths (and text as glyph outlines), not a raster image, so the output stays crisp at any zoom and prints cleanly.
What gets exported
Each page proxy in the document becomes one PDF page:
- Page content — geometry sharing the page’s phase and intersecting its bounds is transformed from world meters to paper millimeters at the page’s draw scale, styled per layer (color, line weight, line type), and clipped to the sheet.
- Viewport content — geometry composed through any viewports on the page, from whichever phase each viewport targets, at that viewport’s own pan/zoom/rotation.
- Sheet template — the assigned title block, rendered on top, with its
substitutable fields (
<PROJECT>, sheet number/name, etc.) filled in.
Pages are ordered by (sheet number, name), so legacy documents (where
every sheet number is 0) keep their existing name order.
Exporting
Open the PDF export dialog from the Sheet Manager or File ▸ Export ▸ PDF. You can choose which pages to include and export options such as monochrome output.
Line types in the exported PDF
Custom linetype patterns (dashes, dots, center lines, etc.) are resolved against the project’s linetype library and interpreted as paper millimeters, independent of the page’s draw scale — so a dash pattern defined for a 1:500 sheet looks the same physical size as the same pattern on a 1:100 sheet.
Text
Text is rendered as filled glyph outlines rather than PDF text objects, so the exported PDF doesn’t depend on font availability on the viewer’s system and renders identically everywhere.
Supported Formats
Native (pure Rust — always available)
| Format | Extension | Notes |
|---|---|---|
| ESRI Shapefile | .shp | Industry-standard GIS vector format |
| GeoJSON | .geojson, .json | Web-friendly geographic data format; non-finite coordinates are rejected on import |
| DXF | .dxf | AutoCAD Drawing Exchange Format, with native Circle/Arc support; non-finite coordinates are rejected on import |
| CSV | .csv | Point data with configurable column mapping (import) and column/format selection (export) — see CSV Export |
GDAL (optional — requires the gdal feature)
| Format | Extension | Notes |
|---|---|---|
| KML | .kml | Keyhole Markup Language, for Google Earth |
| GeoPackage | .gpkg | Modern SQLite-based GIS format |
| MapInfo TAB | .tab | MapInfo native format |
| OpenFileGDB | .gdb | ESRI File Geodatabase |
See Building from Source for how to build with GDAL support.
Coordinate reference systems
All formats support CRS handling with automatic transformation options via proj4rs.
DXF import notes
- BYBLOCK colors fall back to black (the source block’s actual color isn’t always recoverable through the parsing crate used).
- When importing DXF sheet templates (paper-space layouts, for use as drafting title blocks), the true physical paper size is recovered from the DXF’s plot-settings where possible; binary DXF files fall back to a layout-limits/content-extents heuristic.
Web viewer bundle (.ncweb)
A separate, purpose-built export format for the companion field/phone viewer — see Web Viewer Bundle.
CSV Export
NullCad supports exporting geometry to CSV with customizable column options, complementing CSV import.
Column selection
| Column | Description |
|---|---|
| Name | Point number or label (auto-generated as PT1, PT2, … if empty) |
| Vertex Index | Index of a vertex within a multi-vertex geometry (lines/polylines) |
| X | Easting or longitude |
| Y | Northing or latitude |
| Z / Height | Elevation or height (defaults to 0 if unset) |
| Code | Feature code for categorization |
| Description | Detailed description or notes |
| Layer | Layer name |
Format options
- Coordinate precision: 0–10 decimal places, defaults to 6.
Geometry handling
By default, only Point geometry is exported.
Enabling export non-point geometries adds:
- Lines — all vertices (start and end), one row each.
- Polylines — all vertices, one row each.
- Polygons — the centroid, as a single row.
- Circles, Arcs, Rectangles — the center point, as a single row.
- Text — the insertion point, as a single row.
Export behavior
- Selection-aware: if geometry is selected, only the selection is exported; otherwise all active (non-deleted) geometry is exported.
- Auto-generated names: points with an empty name get
PT1,PT2, etc. - Default height: Z/Height defaults to
0(rather than being left empty) when unset. - Multiple rows per geometry: exporting a line/polyline with export of non-points enabled produces one row per vertex, all sharing the same metadata (name, code, description, …).
Usage
- Select geometry to export (optional — leave nothing selected to export everything).
- File ▸ Export ▸ CSV…
- Choose which columns to include, and optionally enable “Export non-point geometries.”
- Export, then choose the output file location.
Default configuration
Enabled by default: Name, X, Y, Z/Height, Code, Description, 6 decimal places of precision.
Disabled by default: Vertex Index, Layer, non-point geometry export.
CSV format
Standard RFC 4180: header row, comma-separated fields, fields containing commas are quoted, empty fields are unquoted empty strings.
Example — points only (default)
name,x,y,z,code,description
PT1,500000.123456,4500000.654321,123.450000,CTRL,Control Point
PT2,500100.234567,4500100.765432,124.560000,BNDRY,Boundary Marker
PT3,500200.345678,4500200.876543,0.000000,TOPO,Topographic Point
Example — line vertices with vertex index
name,vertex_index,x,y,code
LINE1,0,100.000000,200.000000,BOUND
LINE1,1,150.000000,250.000000,BOUND
LINE1,2,200.000000,300.000000,BOUND
Example — auto-generated names
name,x,y,z
PT1,100.000,200.000,0.000
PT2,300.000,400.000,0.000
See also
- Supported Formats — CSV import and other export formats.
Web Viewer Bundle
NullCad can export a self-contained .ncweb bundle for a companion
browser/phone viewer — useful for reviewing a survey project in the field
without running the full desktop application.
What’s in the bundle
.ncweb is a ZIP archive containing:
manifest.json— CRS, extent, camera position, layer list.features.geojson— geometry as GeoJSON, with styles resolved (colors, line weights, etc.) at export time so the viewer doesn’t need to replicate NullCad’s full styling logic.resources/— embedded resources (e.g. reference images) needed by the viewer.
Exporting
File ▸ Export ▸ Web Viewer Bundle…, then choose an output location for
the .ncweb file.
The viewer
The companion viewer is a TypeScript Progressive Web App (PWA), designed
for field/phone use — it opens a .ncweb bundle and renders the exported
geometry with the resolved styles, without needing network connectivity
once loaded.
Keeping exporter and viewer in sync
The .ncweb format is produced by the Rust exporter
(src/gis_io/native/web_bundle.rs) and consumed by the separate viewer/
TypeScript project. Because the format is shared between two independently
built codebases, changes to the bundle’s schema need to land on both sides
together.
Lua Plugins
NullCad can be extended with Lua 5.4 plugins. Plugins can add menu commands, interactive canvas tools, panels and dialogs, and automation hooks that react to application events. Every document mutation a script makes goes through the normal operations system, so plugin edits are undoable and appear in the audit trail like hand-drawn geometry.
Installing plugins
Plugins live in the NullCad config directory:
| Platform | Location |
|---|---|
| Linux | ~/.config/nullcad/plugins/ |
| Windows | %APPDATA%\nullcad\plugins\ |
| macOS | ~/Library/Application Support/nullcad/plugins/ |
Two layouts are supported:
- Single file — drop
my_plugin.luadirectly inplugins/. - Directory — a folder containing
plugin.tomland an entry script:
# plugins/survey-helpers/plugin.toml
name = "Survey Helpers"
id = "survey-helpers" # optional, defaults to the folder name
version = "0.1.0"
description = "Point grids, selection reports"
author = "You"
entry = "init.lua" # optional, defaults to init.lua
Plugins load on startup. Use Plugins ▸ Reload Plugins after editing a
script, and Plugins ▸ Plugin Manager… to enable/disable plugins or see
load errors. Plugins ▸ Run Script… executes a one-off .lua file
without installing it (useful for batch automation).
A minimal plugin
-- plugins/hello.lua
nc.register_command{
id = "hello",
title = "Say Hello",
run = function(ctx)
ctx:toast("Hello from Lua!", "success")
end,
}
This adds Plugins ▸ hello ▸ Say Hello to the menu bar.
The nc global
Every plugin runs in its own Lua state with the nc table installed:
| Item | Purpose |
|---|---|
nc.version | NullCad version string |
nc.ctx | Application context (see below) |
nc.register_command{...} | Add a menu command |
nc.register_tool{...} | Add an interactive canvas tool |
nc.register_panel{...} | Add a toggleable panel |
nc.on(event, handler) | Subscribe to an application event |
All callbacks receive ctx (the same object as nc.ctx) as their first
argument.
Commands
nc.register_command{
id = "count", -- required, unique within the plugin
title = "Count Geometry", -- menu label (defaults to id)
run = function(ctx) ... end, -- required
}
Tools
Tools receive snapped canvas clicks in world coordinates. Return true from
on_click to finish the tool (the app returns to selection mode); return
false/nothing to keep receiving clicks. Tool state is just Lua upvalues.
local first = nil
nc.register_tool{
id = "two-point",
name = "Two Point Line",
instructions = "Click start point, then end point",
on_click = function(ctx, x, y)
if not first then
first = {x = x, y = y}
return false
end
ctx:create_line{start_x = first.x, start_y = first.y, end_x = x, end_y = y}
first = nil
return true
end,
}
Registered tools appear in the plugin’s menu; activating one behaves like any built-in tool (Esc cancels, snapping applies).
Panels and dialogs
UI is declarative: a list of widget tables rendered by the app. Button
clicks call your on_event handler with the button id and the current
values of all input widgets (keyed by widget id).
Widget types:
{type = "heading", text = "Section"}
{type = "label", text = "Static text"}
{type = "separator"}
{type = "text", id = "name", label = "Name", value = "default"}
{type = "number", id = "dist", label = "Distance", value = 10.0}
{type = "checkbox", id = "flag", label = "Enabled", value = true}
{type = "select", id = "mode", label = "Mode", options = {"A", "B"}, value = "A"}
{type = "button", id = "go", label = "Go"}
Panels are registered once and toggled from the plugin’s menu (or with
ctx:open_panel(id) / ctx:close_panel(id)); update their contents with
ctx:update_panel(id, widgets).
nc.register_panel{
id = "quick-point",
title = "Quick Point",
widgets = {
{type = "number", id = "x", label = "Easting", value = 0.0},
{type = "number", id = "y", label = "Northing", value = 0.0},
{type = "button", id = "create", label = "Create Point"},
},
on_event = function(ctx, event, values)
if event == "create" then
ctx:create_point{x = values.x, y = values.y}
end
end,
}
Dialogs are opened from any callback with ctx:show_dialog. Return
true from the handler to close the dialog; the window’s ✕ also closes it.
ctx:show_dialog{
title = "Grid Options",
widgets = {
{type = "number", id = "rows", label = "Rows", value = 5},
{type = "number", id = "spacing", label = "Spacing", value = 10.0},
{type = "button", id = "ok", label = "Create"},
{type = "button", id = "cancel", label = "Cancel"},
},
on_event = function(ctx, event, values)
if event == "ok" then make_grid(ctx, values) end
return true -- close on any button
end,
}
Events (automation)
nc.on("document_saved", function(ctx, e)
ctx:log("Saved to " .. e.path)
end)
| Event | Payload |
|---|---|
document_opened | {path} |
document_saved | {path} |
document_new | {} |
operation_applied | {description} |
selection_changed | {ids, count} |
phase_changed | {phase} |
Events are delivered once per frame, after the mutation completes.
Operations performed inside an operation_applied handler do not
re-trigger the event (no feedback loops).
ctx reference
Coordinates are world meters (f64). Geometry and layer ids are UUID strings.
Errors raise Lua errors — wrap calls in pcall if you want to recover.
Reading
| Method | Returns |
|---|---|
ctx:document_info() | {project_name, file_path, geometry_count, layer_count, current_phase, active_layer_id, ...} |
ctx:current_phase() | "SetupSearch" | "BoundaryModel" | "Reinstatement" | "Drafting" |
ctx:list_layers() | {layers = {{id, name, visible, locked, color, order}, ...}} |
ctx:list_geometry{layer_name=, geometry_type=, limit=} | {count, items = {{id, type, name, layer_name, z, summary}, ...}} (all filters optional) |
ctx:get_geometry(id) | Full record incl. geometry table (e.g. {type = "Point", x, y}; Text/Rectangle include rotation_degrees, CCW from east) |
ctx:selection() | Array of selected geometry ids |
Creating geometry
All creators accept optional layer_id/layer_name (defaults to the
default layer), name, description, and (where meaningful) z. They
return the new geometry’s id.
ctx:create_point{x=, y=, z=}
ctx:create_line{start_x=, start_y=, end_x=, end_y=}
ctx:create_polyline{points = {{x=, y=}, ...}} -- ≥ 2 points
ctx:create_polygon{points = {{x=, y=}, ...}} -- ≥ 3 points
ctx:create_circle{center_x=, center_y=, radius=}
ctx:create_arc{center_x=, center_y=, radius=, start_angle=, end_angle=}
ctx:create_rectangle{corner1_x=, corner1_y=, corner2_x=, corner2_y=}
ctx:create_text{x=, y=, content=, height=}
ctx:create_layer{name=, color="#RRGGBB"} -- returns layer id
Modifying
| Method | Effect |
|---|---|
ctx:delete(id) | Soft-delete a geometry object (undoable) |
ctx:move_geometry(ids, dx, dy) | Translate the given ids by a delta |
ctx:rotate_geometry(ids, center_x, center_y, angle_degrees) | Rotate around a center point, clockwise degrees (surveying convention, same as the Rotate tool) |
ctx:set_selection(ids) / ctx:clear_selection() | Change the selection |
Feedback & UI
| Method | Effect |
|---|---|
ctx:toast(message, level?) | Toast notification ("info", "success", "warning", "error") |
ctx:log(message, level?) | Write to the console (F12), attributed to the plugin |
ctx:show_dialog{title=, widgets=, on_event=} | Open a dialog |
ctx:update_panel(id, widgets) | Replace a panel’s widgets (values with matching ids survive) |
ctx:open_panel(id) / ctx:close_panel(id) | Show/hide a registered panel |
Notes & limitations
- Scripts run on the UI thread: a long loop blocks the interface. Keep per-callback work modest.
ctxis only valid while NullCad is calling into your script (load, callbacks). Don’t stash it for use from Lua coroutines/timers — there are none anyway.- Lua’s standard library is available (
math,string,table,io,os), so scripts can read/write files for import/export automation. - Full examples live in
docs/examples/plugins/in the repository:survey-helpers/(commands, tools, panels, events) andalign-text.lua(rotate a text object to match a clicked line direction).
MCP Server
NullCad includes an in-process Model Context Protocol (MCP) server (HTTP+SSE), letting an AI agent read and create geometry in the currently open document.
How it works
The MCP server runs as a background tokio task inside the application. Mutations requested by an agent are funnelled back to the egui main thread via a channel, so they go through the same operation system as any other edit — every agent-driven change is undoable and recorded in the audit trail, exactly like Lua plugin edits or manual drawing.
Enabling the server
The MCP server’s settings (enable/disable, port) are available in the application settings and persist with the document.
Connecting an agent
Once enabled, point an MCP-compatible client at the server’s HTTP+SSE endpoint to give an agent access to:
- Reading document info, layers, and geometry.
- Creating and modifying geometry (points, lines, polylines, polygons, circles, arcs, rectangles, text).
- Reading and changing the current selection.
The tool surface mirrors the Lua plugin API’s ctx methods (see
Lua Plugins) — both are implemented against the
same underlying operation builders (mcp/tools.rs), so behavior (units,
coordinate conventions, rotation direction) is consistent between the two
integration paths.
Why in-process?
Running the MCP server inside the application (rather than as a separate process talking to a file) means an agent is always working against the live, in-memory document state — including unsaved changes — and its edits appear immediately in the UI, just like a human’s.
Keyboard Shortcuts & Controls
A full, searchable list is available in-app via the keyboard shortcuts help dialog. This page summarizes the defaults.
Navigation
| Action | Input |
|---|---|
| Pan | Middle mouse drag |
| Zoom | Scroll wheel (zooms toward cursor) |
| Reset camera | Reset Camera button |
| Distraction-free mode | F11 |
| View bookmarks | 1–9 to recall, Ctrl+1–9 to set |
Selection
| Action | Input |
|---|---|
| Select | Click on geometry |
| Multi-select | Ctrl+Click |
| Rectangle select | Click and drag on empty canvas |
| Select all | Ctrl+A |
Editing
| Action | Input |
|---|---|
| Undo | Ctrl+Z |
| Redo | Ctrl+Y or Ctrl+Shift+Z |
| Delete selected | Delete |
| Explode | B |
| Transform gizmo (move/rotate selection) | W |
| Toggle console | F12 |
Tool categories
| Category | Tools |
|---|---|
| Select | Default selection mode |
| Draw | Point, Line, Polyline, Leader, Polygon, Text, Callout, Circle, Arc, Rectangle |
| Modify | Move, Rotate, Scale, Trim/Extend, Offset, Transform Gizmo |
| Survey | Traverse, Comparison |
| Measure | Distance and bearing measurement (bearing shown in DMS, rounded to the nearest second) |
| Surface & Drafting | TIN Surface, Page Proxy, Viewport |
See also: User Guide for what each tool does.