# KulmanLab CAD — Full Reference > Complete documentation for KulmanLab: what it is, how it works, and every available command. App: https://app.kulmanlab.com Landing page: https://kulmanlab.com Docs: https://kulmanlab.com/docs/ FAQ: https://kulmanlab.com/faq/ Changelog: https://kulmanlab.com/changelog/ Blog: https://kulmanlab.com/blog/ (English only) About: https://kulmanlab.com/about/ Privacy: https://kulmanlab.com/privacy/ Source: https://github.com/volodymyr4509/kulmanlab German (Deutsch): Landing page (DE): https://kulmanlab.com/de/ Docs (DE): https://kulmanlab.com/de/docs/ Changelog (DE): https://kulmanlab.com/de/changelog/ About (DE): https://kulmanlab.com/de/about/ Privacy (DE): https://kulmanlab.com/de/privacy/ FAQ (DE): https://kulmanlab.com/de/faq/ Spanish (Español): Landing page (ES): https://kulmanlab.com/es/ Docs (ES): https://kulmanlab.com/es/docs/ Changelog (ES): https://kulmanlab.com/es/changelog/ About (ES): https://kulmanlab.com/es/about/ Privacy (ES): https://kulmanlab.com/es/privacy/ FAQ (ES): https://kulmanlab.com/es/faq/ French (Français): Landing page (FR): https://kulmanlab.com/fr/ Docs (FR): https://kulmanlab.com/fr/docs/ Changelog (FR): https://kulmanlab.com/fr/changelog/ About (FR): https://kulmanlab.com/fr/about/ Privacy (FR): https://kulmanlab.com/fr/privacy/ FAQ (FR): https://kulmanlab.com/fr/faq/ Italian (Italiano): Landing page (IT): https://kulmanlab.com/it/ Docs (IT): https://kulmanlab.com/it/docs/ Changelog (IT): https://kulmanlab.com/it/changelog/ About (IT): https://kulmanlab.com/it/about/ Privacy (IT): https://kulmanlab.com/it/privacy/ FAQ (IT): https://kulmanlab.com/it/faq/ Portuguese (Português): Landing page (PT): https://kulmanlab.com/pt/ Docs (PT): https://kulmanlab.com/pt/docs/ Changelog (PT): https://kulmanlab.com/pt/changelog/ About (PT): https://kulmanlab.com/pt/about/ Privacy (PT): https://kulmanlab.com/pt/privacy/ FAQ (PT): https://kulmanlab.com/pt/faq/ Ukrainian (Українська): Landing page (UK): https://kulmanlab.com/uk/ Docs (UK): https://kulmanlab.com/uk/docs/ Changelog (UK): https://kulmanlab.com/uk/changelog/ About (UK): https://kulmanlab.com/uk/about/ Privacy (UK): https://kulmanlab.com/uk/privacy/ FAQ (UK): https://kulmanlab.com/uk/faq/ Turkish (Türkçe): Landing page (TR): https://kulmanlab.com/tr/ Docs (TR): https://kulmanlab.com/tr/docs/ Changelog (TR): https://kulmanlab.com/tr/changelog/ About (TR): https://kulmanlab.com/tr/about/ Privacy (TR): https://kulmanlab.com/tr/privacy/ FAQ (TR): https://kulmanlab.com/tr/faq/ Indonesian (Bahasa Indonesia): Landing page (ID): https://kulmanlab.com/id/ Docs (ID): https://kulmanlab.com/id/docs/ Changelog (ID): https://kulmanlab.com/id/changelog/ About (ID): https://kulmanlab.com/id/about/ Privacy (ID): https://kulmanlab.com/id/privacy/ FAQ (ID): https://kulmanlab.com/id/faq/ Arabic (العربية): Landing page (AR): https://kulmanlab.com/ar/ Docs (AR): https://kulmanlab.com/ar/docs/ Changelog (AR): https://kulmanlab.com/ar/changelog/ About (AR): https://kulmanlab.com/ar/about/ Privacy (AR): https://kulmanlab.com/ar/privacy/ FAQ (AR): https://kulmanlab.com/ar/faq/ Hindi (हिन्दी): Landing page (HI): https://kulmanlab.com/hi/ Docs (HI): https://kulmanlab.com/hi/docs/ Changelog (HI): https://kulmanlab.com/hi/changelog/ About (HI): https://kulmanlab.com/hi/about/ Privacy (HI): https://kulmanlab.com/hi/privacy/ FAQ (HI): https://kulmanlab.com/hi/faq/ Japanese (日本語): Landing page (JA): https://kulmanlab.com/ja/ Docs (JA): https://kulmanlab.com/ja/docs/ Changelog (JA): https://kulmanlab.com/ja/changelog/ About (JA): https://kulmanlab.com/ja/about/ Privacy (JA): https://kulmanlab.com/ja/privacy/ FAQ (JA): https://kulmanlab.com/ja/faq/ Chinese Simplified (中文): Landing page (ZH): https://kulmanlab.com/zh/ Docs (ZH): https://kulmanlab.com/zh/docs/ Changelog (ZH): https://kulmanlab.com/zh/changelog/ About (ZH): https://kulmanlab.com/zh/about/ Privacy (ZH): https://kulmanlab.com/zh/privacy/ FAQ (ZH): https://kulmanlab.com/zh/faq/ Polish (Polski): Landing page (PL): https://kulmanlab.com/pl/ Docs (PL): https://kulmanlab.com/pl/docs/ Changelog (PL): https://kulmanlab.com/pl/changelog/ About (PL): https://kulmanlab.com/pl/about/ Privacy (PL): https://kulmanlab.com/pl/privacy/ FAQ (PL): https://kulmanlab.com/pl/faq/ Korean (한국어): Landing page (KO): https://kulmanlab.com/ko/ Docs (KO): https://kulmanlab.com/ko/docs/ Changelog (KO): https://kulmanlab.com/ko/changelog/ About (KO): https://kulmanlab.com/ko/about/ Privacy (KO): https://kulmanlab.com/ko/privacy/ FAQ (KO): https://kulmanlab.com/ko/faq/ Vietnamese (Tiếng Việt): Landing page (VI): https://kulmanlab.com/vi/ Docs (VI): https://kulmanlab.com/vi/docs/ Changelog (VI): https://kulmanlab.com/vi/changelog/ About (VI): https://kulmanlab.com/vi/about/ Privacy (VI): https://kulmanlab.com/vi/privacy/ FAQ (VI): https://kulmanlab.com/vi/faq/ Bengali (বাংলা): Landing page (BN): https://kulmanlab.com/bn/ Docs (BN): https://kulmanlab.com/bn/docs/ Changelog (BN): https://kulmanlab.com/bn/changelog/ About (BN): https://kulmanlab.com/bn/about/ Privacy (BN): https://kulmanlab.com/bn/privacy/ FAQ (BN): https://kulmanlab.com/bn/faq/ Swahili (Kiswahili): Landing page (SW): https://kulmanlab.com/sw/ Docs (SW): https://kulmanlab.com/sw/docs/ Changelog (SW): https://kulmanlab.com/sw/changelog/ About (SW): https://kulmanlab.com/sw/about/ Privacy (SW): https://kulmanlab.com/sw/privacy/ FAQ (SW): https://kulmanlab.com/sw/faq/ Thai (ภาษาไทย): Landing page (TH): https://kulmanlab.com/th/ Docs (TH): https://kulmanlab.com/th/docs/ Changelog (TH): https://kulmanlab.com/th/changelog/ About (TH): https://kulmanlab.com/th/about/ Privacy (TH): https://kulmanlab.com/th/privacy/ FAQ (TH): https://kulmanlab.com/th/faq/ Malay (Bahasa Melayu): Landing page (MS): https://kulmanlab.com/ms/ Docs (MS): https://kulmanlab.com/ms/docs/ Changelog (MS): https://kulmanlab.com/ms/changelog/ About (MS): https://kulmanlab.com/ms/about/ Privacy (MS): https://kulmanlab.com/ms/privacy/ FAQ (MS): https://kulmanlab.com/ms/faq/ Urdu (اردو): Landing page (UR): https://kulmanlab.com/ur/ Docs (UR): https://kulmanlab.com/ur/docs/ Changelog (UR): https://kulmanlab.com/ur/changelog/ About (UR): https://kulmanlab.com/ur/about/ Privacy (UR): https://kulmanlab.com/ur/privacy/ FAQ (UR): https://kulmanlab.com/ur/faq/ Greek (Ελληνικά): Landing page (EL): https://kulmanlab.com/el/ Docs (EL): https://kulmanlab.com/el/docs/ Changelog (EL): https://kulmanlab.com/el/changelog/ About (EL): https://kulmanlab.com/el/about/ Privacy (EL): https://kulmanlab.com/el/privacy/ FAQ (EL): https://kulmanlab.com/el/faq/ Punjabi (ਪੰਜਾਬੀ): Landing page (PA): https://kulmanlab.com/pa/ Docs (PA): https://kulmanlab.com/pa/docs/ Changelog (PA): https://kulmanlab.com/pa/changelog/ About (PA): https://kulmanlab.com/pa/about/ Privacy (PA): https://kulmanlab.com/pa/privacy/ FAQ (PA): https://kulmanlab.com/pa/faq/ Swedish (Svenska): Landing page (SV): https://kulmanlab.com/sv/ Docs (SV): https://kulmanlab.com/sv/docs/ Changelog (SV): https://kulmanlab.com/sv/changelog/ About (SV): https://kulmanlab.com/sv/about/ Privacy (SV): https://kulmanlab.com/sv/privacy/ FAQ (SV): https://kulmanlab.com/sv/faq/ Filipino: Landing page (TL): https://kulmanlab.com/tl/ Docs (TL): https://kulmanlab.com/tl/docs/ Changelog (TL): https://kulmanlab.com/tl/changelog/ About (TL): https://kulmanlab.com/tl/about/ Privacy (TL): https://kulmanlab.com/tl/privacy/ FAQ (TL): https://kulmanlab.com/tl/faq/ Dutch (Nederlands): Landing page (NL): https://kulmanlab.com/nl/ Docs (NL): https://kulmanlab.com/nl/docs/ Changelog (NL): https://kulmanlab.com/nl/changelog/ About (NL): https://kulmanlab.com/nl/about/ Privacy (NL): https://kulmanlab.com/nl/privacy/ FAQ (NL): https://kulmanlab.com/nl/faq/ --- Hebrew (עברית): Landing page (HE): https://kulmanlab.com/he/ Docs (HE): https://kulmanlab.com/he/docs/ Changelog (HE): https://kulmanlab.com/he/changelog/ About (HE): https://kulmanlab.com/he/about/ Privacy (HE): https://kulmanlab.com/he/privacy/ FAQ (HE): https://kulmanlab.com/he/faq/ --- Hausa: Landing page (HA): https://kulmanlab.com/ha/ Docs (HA): https://kulmanlab.com/ha/docs/ Changelog (HA): https://kulmanlab.com/ha/changelog/ About (HA): https://kulmanlab.com/ha/about/ Privacy (HA): https://kulmanlab.com/ha/privacy/ FAQ (HA): https://kulmanlab.com/ha/faq/ --- # About KulmanLab KulmanLab is a free, browser-based 2D CAD editor. No installation, no account, no data uploaded. Open the URL and start drawing — your files stay in your browser's IndexedDB on your own device. ## Key facts - **Free**: no subscription, no credit card, no hidden plan - **No account**: no login or sign-up required - **Works offline**: cached after first load, no server needed - **Privacy**: drawings never leave your device; Google Analytics used for anonymous usage metrics; the app is funded by ads via Google AdSense, with a region-scoped consent banner (Google Consent Mode v2) for the EEA, UK, and Switzerland — see https://kulmanlab.com/privacy/ - **DXF compatible**: reads and writes AC1032 DXF, compatible with LibreCAD, QCAD, BricsCAD, FreeCAD - **Desktop browsers only**: Chrome, Firefox, Safari, Edge — mobile not currently supported ## What it can do - 50+ drawing commands via toolbar or command terminal (LINE, CIRCLE, ARC, MOVE, TRIM, OFFSET, and more) - Full layer management: create, rename, freeze, lock, isolate, color, linetype, lineweight per layer - Dimensions: linear, aligned, radius, diameter, angular, continue - Grip editing on all entity types — drag endpoints, midpoints, arc ends, dimension text - Snapping: endpoint, midpoint, center, intersection, perpendicular, tangent, nearest, grid - Angle tracking with configurable increments (10°, 15°, 20°, 30°, 45°, 90°) - Vector pins with distance tracking: pin a snap point, lock a direction, then type an exact length — places a point at a precise distance with no dragging, rolled out to nearly every command that asks for a point - Layout / paper space with multiple layouts and viewport editing - Print manager: PDF and PNG/WebP export with DPI control - On-canvas text editing (MTEXT) with Bold, Italic, per-character font and size - Font Manager: browse built-in and Google Fonts, or upload custom .ttf files - DXF export carries geometry, text (MTEXT), dimensions, leaders, and hatches, each with its own per-entity-type checklist - Installable from the browser as a PWA - Undo/redo history - Adaptive CAD grid with snap-to-grid ## What it cannot do - 3D modelling - DWG format (proprietary binary — export DXF from your source app instead) - Blocks / xrefs - Parametric constraints - Real-time collaboration (single-user, local only) ## Frequently asked questions **Can I open .dwg files?** No. KulmanLab supports DXF, not DWG. Most desktop CAD apps can export DXF — export from your source app first. **Where are my drawings stored?** In your browser's IndexedDB on your own device. Nothing is uploaded to a server. **What happens if I clear my browser data?** Saved drawings are lost. Export a DXF copy of anything important before clearing browser storage. **Which export formats are available?** DXF and JSON via Export Manager — both carry every entity type (geometry, text with formatting, dimensions, leaders, hatches, layers, linetypes), each with its own checklist of entity types so either export can leave types out without affecting the other. DXF is for round-tripping with other CAD tools, JSON for full-fidelity saves within KulmanLab. Print separately exports PDF and PNG/WebP for print-ready output. **Are KulmanLab DXF files compatible with other software?** Yes. AC1032 DXF opens correctly in LibreCAD, QCAD, BricsCAD, FreeCAD, and any DXF-capable tool. **Can KulmanLab replace my desktop CAD software?** For 2D drafting — yes. It has a full command terminal, layers, snapping, dimensions, grip editing, and DXF compatibility. It does not support 3D, blocks/xrefs, or parametric constraints. **Is real-time collaboration supported?** Not currently. KulmanLab is a single-user tool. **Does KulmanLab collect drawing data?** No. Drawings stay in the browser. Google Analytics collects anonymous usage metrics, and the app shows ads via Google AdSense — neither ever sees drawing content. Full policy: https://kulmanlab.com/privacy/ ## Comparison with alternatives | Feature | KulmanLab | LibreCAD | QCAD | BricsCAD | Onshape | |---|---|---|---|---|---| | Runs in browser | Yes | No | No | No | Yes | | Free | Yes | Yes | Community | No | Free plan | | No account required | Yes | Yes | Yes | No | No | | Works offline | Yes | Yes | Yes | Yes | No | | Files stay local | Yes | Yes | Yes | Yes | No | | Command terminal | Yes | Yes | Yes | Yes | No | | Layout / paper space | Yes | No | No | Yes | Yes | | DXF import & export | Yes | Yes | Yes | Yes | Yes | --- # Complete Command Reference # Align The `align` command repositions selected entities using one or two pairs of source/destination points. With one pair it behaves exactly like [Move](./move) (translate only). With two pairs it also rotates the selection so the source-to-source direction matches the destination-to-destination direction, and can optionally scale it so the source segment length matches the destination segment length — translate, rotate, and scale in a single operation. ## Two ways to start **Pre-select, then align**: select entities, type `align` (or click the toolbar button), click S1, click D1, click S2 (or Enter/Space for translate-only), click D2, then answer the scale prompt. **Activate, then select**: type `align` with nothing selected, select objects, confirm with Enter/Space, then continue with S1 → D1 → S2 → D2 → scale prompt. The terminal only needs enough letters to be unambiguous — typing `al` and pressing Enter activates Align directly, since no other command name starts with those two letters. ## One-point alignment (translate only) After D1 is placed, press Enter or Space instead of clicking a second source point. The selection is translated by the S1→D1 vector — no rotation or scaling — identical to a Move using S1 as the base point and D1 as the destination. ## Two-point alignment (translate + rotate + optional scale) Once S2 and D2 are both placed, the rotation angle is the difference between the destination direction (D1 → D2) and the source direction (S1 → S2). The scale prompt — `scale objects to alignment points? [Yes/No] ` — defaults to No: - **Y** also scales the selection uniformly around D1 so the S1–S2 distance becomes the D1–D2 distance. - **N** or **Enter** keeps the original size — only translate and rotate are applied. Pressing a key at the scale prompt applies the alignment immediately — there's no separate confirmation step. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Confirm selection and advance to the S1 phase | | `Enter` / `Space` (at the S2 step) | Skip rotation — apply a translate-only alignment using S1 and D1 | | `Y` | Apply the alignment with scaling | | `N` / `Enter` (at the scale prompt) | Apply the alignment without scaling | | `Escape` | While picking points: discard them and return to the selection phase; with nothing selected: cancel the command | ## After aligning The aligned entities remain selected at their new position, and the command ends automatically — run Align again, or switch to Move, Rotate, or Scale without re-selecting. ## Align vs Move | | Align | Move | |---|-------|------| | Point pairs | 1 (translate only) or 2 (translate + rotate + scale) | 1 (translate only) | | Rotation | Yes, with a second point pair | No | | Scaling | Optional, with a second point pair | No | | Best for | Fitting one shape onto another using reference points | Simple repositioning | ## Supported entities Align works on every entity type supported by Move, Rotate, and Scale — the same translate, rotate, and scale operations those commands use are applied in sequence, so nothing is excluded. --- # Angle The `angle` command measures the interior angle at a vertex formed by two line segments through three clicked points. The result — always between 0° and 180° — is displayed in the terminal to 4 decimal places. It is one of three measurement commands — [Distance](./distance) measures straight-line length, and [Area](./area) measures the enclosed area and perimeter of a polygon. ## Anatomy of an angle measurement ``` ● first point (end of first ray) \ \ first ray preview \ ● vertex (step 3) / / second ray preview (to cursor) / ● third point → terminal: "Angle: 45.0000°" ``` - **First point** — one end of the angle (step 2). - **Vertex** — the corner at which the angle is measured (step 3). - **Third point** — the other end of the angle (step 4). ## Measuring an angle 1. Type `angle` in the terminal or click the **Angle** toolbar button. 2. **Click the first point** — one arm end of the angle. 3. **Click the vertex** — the corner where the two arms meet. A preview line shows from the first point to the cursor. 4. **Click the third point** — the second arm end. Both preview lines are visible. Placing this point prints the result. 5. **Click again** (optional) to start a new measurement from that click as the new first point. ## Interior angle convention The command computes the angle using the dot product of the two rays from the vertex: - **Always interior**: the result is the smaller angle, between 0° and 180°. - The order you click the end points does not affect the result — only the vertex position matters. - Collinear points (all three on one line) return 0° or 180°. ## Chaining measurements After the result appears, clicking immediately starts the next measurement — the clicked point becomes the new first point. The command never auto-exits until you press `Escape`. ## Angle vs Distance | | Angle | Distance | |---|-------|---------| | What it measures | Interior angle at a vertex | Straight-line length | | Number of clicks | 3 | 2 | | Result format | `45.0000°` | `12.3456` (units) | | Canvas preview | Two lines from vertex to both ends | Line from first point to cursor | | Best for | Opening angle between two features | Length of a gap or segment | ## Keyboard reference | Key | Action | |-----|--------| | `Escape` | Cancel and reset to step 2 | ## Notes - Results are shown in the **terminal only** — nothing is added to the drawing. - Precision is always 4 decimal places in degrees. --- # Arc The `arc` command draws a circular arc through three points you click. The arc is computed as the unique circumcircle passing through all three points — no need to specify a center or radius directly. The arc runs from the first click to the third click, passing through the second. ## Drawing an arc 1. Type `arc` in the terminal or click the **Arc** toolbar button. 2. **Click the first point** — one end of the arc. 3. **Click the second point** — a point the arc must pass through (controls curvature and direction). 4. **Click the third point** — the other end of the arc. The arc is placed and the command exits. ``` ● (2nd click — midpoint on the curve) / \ / \ ● ● 1st 3rd ``` A line preview connects the first two clicks while you position the third. From the second click onward a live arc preview tracks the cursor. > **Collinear points**: if all three points lie on a straight line the arc cannot be computed and no entity is placed. Move the second point off the line and try again. ## Keyboard reference | Key | Action | |-----|--------| | `Escape` | Discard all placed points and exit | There is no typed input in the arc command — all three points are click-only. ## Grip editing — adjusting endpoints and radius A selected arc exposes three grips: | Grip | Position | What it does | |------|----------|--------------| | **Center** | Geometric center of the circumcircle | Moves the whole arc; radius and angles unchanged | | **Start** | First endpoint on the arc | Drag to move the start along the circumcircle — changes both start angle and radius | | **End** | Last endpoint on the arc | Drag to move the end along the circumcircle — changes both end angle and radius | Dragging a start or end grip repositions it to the drag location and recalculates both the angle and the radius from that new position relative to the center. The opposite endpoint stays fixed. ## Selecting arcs | Method | Behaviour | |--------|-----------| | **Click** | Selects if the click lands near the arc curve (not the chord) | | **Drag right** (strict) | Sample points distributed along the arc must all lie inside the box | | **Drag left** (crossing) | Any sample point on the arc that falls inside the box selects it | ## Supported edit commands | Command | What happens to the arc | |---------|------------------------| | [Move](./move) | Translates the center; radius and angles unchanged | | [Copy](./copy) | Creates an identical arc at a new position | | [Rotate](./rotate) | Rotates the center and shifts start/end angles by the rotation amount | | [Mirror](./mirror) | Reflects the center and inverts start/end angles across the mirror axis | | [Scale](./scale) | Scales the center position and multiplies the radius by the scale factor | | [Offset](./offset) | Creates a concentric arc at a larger or smaller radius, same angle span | | [Delete](./delete) | Removes the arc | ## Properties **General** | Property | Default | Meaning | |----------|---------|---------| | Color | 256 (ByLayer) | ACI color index | | Layer | `0` | Layer assignment | | Linetype | ByLayer | Named linetype pattern | | Linetype Scale | 1 | Scale factor on the linetype pattern | | Thickness | 0 | Extrusion thickness | **Geometry** | Property | Meaning | |----------|---------| | Center X / Center Y | Center of the circumcircle | | Radius | Radius of the circumcircle | | Start Angle | Angle in degrees where the arc begins (measured from the positive X axis) | | End Angle | Angle in degrees where the arc ends | ## Arc vs Circle — when to use which | | Arc | Circle | |---|-----|--------| | Span | Partial — first to third click | Full 360° | | Input method | Three points on the curve | Center + radius (click or type) | | Typed input | None | Radius value | | Resize after placement | Drag start/end grips | Drag any cardinal grip | | Best for | Fillets, rounded corners, arched paths | Full holes, round features | ## DXF — ARC entity Arcs are saved as `ARC` entities in the DXF file, storing center coordinates, radius, start angle, and end angle. All properties — including color, layer, linetype, linetype scale, and thickness — round-trip without loss. Any DXF-compatible application (AutoCAD, LibreCAD, etc.) reads these as standard arcs. --- # Area The `area` command measures the enclosed area and perimeter of a polygon defined by three or more clicked points, and prints both results in the terminal to 4 decimal places. It is the third measurement command, alongside [Distance](./distance) (straight-line length) and [Angle](./angle) (interior angle at a vertex). ## Measuring an area 1. Type `area` in the terminal or click the **Area** toolbar button (bottom row of the Measure panel). 2. **Click the first point**, or type `X,Y` and press **Enter** for an exact coordinate. 3. **Click each additional vertex** in order around the shape. Coordinate entry works at every step. 4. Once at least **3 points** are placed, press **Enter** or **Space** (with no coordinate or distance entry pending) to close the polygon and compute the result. 5. The terminal prints `Area: Perimeter: `, and the closed polygon — fill, outline, and vertex grips — stays highlighted on the canvas. 6. **Click anywhere, press any key, or press `Escape`** to dismiss the result and end the command. ## Angle locking and exact distance After the first vertex is placed, moving toward one of the configured angle-tracking increments (10°, 15°, 20°, 30°, 45°, or 90°) locks the next edge to that direction. Type a length and press **Enter** to place the next vertex at exactly that distance along the locked direction, or click to place it at the cursor's projection onto that direction. ## Closing the polygon Enter or Space only close the shape once 3 or more vertices are placed. The edge from the last vertex back to the first is added automatically and counted in both the area and the perimeter. Points may be placed in any order (clockwise or counterclockwise) — the result is identical either way. ## Area vs Distance vs Angle | | Area | Distance | Angle | |---|------|---------|-------| | What it measures | Enclosed area & perimeter of a polygon | Straight-line length | Interior angle at a vertex | | Number of clicks | 3 or more, closed with Enter | 2 | 3 | | Result format | `12.3456 Perimeter: 45.6789` | `12.3456` (units) | `45.0000°` | | Canvas preview | Filled polygon with dashed closing edge | Line from first point to cursor | Two lines from vertex to both ends | | After result | Dismiss with any input, then command exits | Click to chain a new measurement | Click to chain a new measurement | | Best for | Enclosed regions, room or panel area | Length of a gap or segment | Opening angle between two features | ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.`, `-` | Start X coordinate entry, or edge-length entry while angle-locked | | `,` | Lock X and move to Y entry | | `Backspace` | Delete last typed character | | `Enter` / `Space` | Confirm a typed coordinate or length (Enter only); with 3+ vertices and no pending entry, closes the polygon | | `Escape` | While picking vertices, discard them and restart at the first point; once the result is shown, dismiss it and exit | ## Notes - The area is computed with the shoelace formula and is always reported as a positive value, regardless of click order. - Self-intersecting polygons (edges that cross) still produce a numeric result, but the value may not match the visually enclosed region. - Results are shown in the **terminal and as a temporary canvas highlight only** — nothing is added to the drawing as a permanent entity. - Unlike Distance and Angle, Area does **not** chain into a new measurement automatically — after you dismiss the result, run `area` again to measure another polygon. - Precision is always 4 decimal places for both area and perimeter, in the same units as the drawing coordinates (no unit conversion). --- # Array Grid The `ArrayGrid` command creates a rectangular grid of copies from the selected entities — enter the number of rows, columns, and the spacing between them, all typed at the terminal. The original selection occupies the row-0, column-0 cell; every other cell is a translated copy. ## Two ways to start **Pre-select, then array** 1. Select one or more entities. 2. Type `arraygrid` (as few as `arr` is enough — unambiguous) or click the **Array Grid** toolbar button. 3. Type the number of **rows**, press **Enter**. 4. Type the number of **columns**, press **Enter**. 5. Type the **row spacing**, press **Enter**. 6. Type the **column spacing**, press **Enter** — the grid is created immediately. **Activate, then select** — start with nothing selected, select objects, then **Enter**/**Space** to confirm, and continue with the same rows → columns → row spacing → column spacing sequence. ## Rows, columns, and spacing | Prompt | Accepts | Notes | |--------|---------|-------| | Rows | Positive whole numbers | Digits only | | Columns | Positive whole numbers | Digits only | | Row spacing | Signed decimal | Negative reverses direction | | Column spacing | Signed decimal | Negative reverses direction | With 1 row and 1 column, no copies are created. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Confirm selection and advance to the next prompt | | `0`–`9` | Enter digits | | `.`, `-` | Decimal point / sign for spacing values | | `Backspace` | Delete the last typed character | | `Escape` | Clear typed values and return to the selection phase | ## Array Grid vs Copy | | Array Grid | Copy | |---|-----------|------| | Point-picking | None — rows, columns, spacing are typed | Base point and destination are clicked | | Copies created | Rows × columns − 1 | Exactly 1 | | Best for | Repeating a unit across a regular pattern (holes, tiles, fasteners) | A single duplicate at an arbitrary position | ## Supported entities Works on every entity type — all entities implement `translate(dx, dy)` internally, the same operation [Copy](./copy) and [Move](./move) use. ## Related commands - [Copy](./copy) — a single duplicate at an arbitrary offset - [Move](./move) — relocate the original selection instead of copying it --- # Chamfer The `chamfer` command cuts a straight diagonal corner between two [Line](./line) or [Polyline](./polyline) entities. You specify how far back to cut along each entity (d1 and d2), and the command trims both entities to those points and inserts a connecting line between them. Using equal distances produces a symmetric 45° cut; different distances produce an asymmetric bevel. Chamfer works on **Line and Polyline** entities. ## Using chamfer 1. Type `chamfer` in the terminal or click the **Chamfer** toolbar button. 2. **Type the first chamfer distance** (d1 — distance along the first entity) and press **Enter**. 3. **Type the second chamfer distance** (d2 — distance along the second entity) and press **Enter**. 4. **Click the first entity** — the portion you click determines which side of any intersection is kept. 5. **Hover over the second entity** — a dashed line preview shows the resulting chamfer cut. Move the cursor to the side you want to keep. 6. **Click** to apply. Both entities are trimmed and the chamfer line is inserted. ``` Before (d1=5, d2=8): After: ────────────── ──────────╲ │ ╲──── │ ``` ## Side selection When two lines cross each other, the chamfer is applied on the corner defined by the click positions — the portion of each entity on the **same side as the cursor** is kept. - Click near one end of the first entity to select that half. - Move the cursor to the desired half of the second entity — the dashed preview updates live. For Polylines, the click position determines which **segment** of the polyline participates, and the nearest vertex on the intersection side is the one that gets trimmed. When both picks land on the same polyline, the second pick must be a true neighbor of the first — sharing the corner vertex between them — or it's rejected. An **arc segment** of a polyline is never picked; only straight segments count. ## What the command creates - The first entity's endpoint (or polyline vertex) nearest to the intersection is moved to point **T1**, located d1 along the first entity from the intersection. - The second entity's endpoint (or polyline vertex) nearest to the intersection is moved to point **T2**, located d2 along the second entity from the intersection. - A new Line entity is inserted from **T1** to **T2**. The inserted line inherits the current lineweight, color, layer, and linetype settings. ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the current distance value | | `Backspace` | Delete last typed character | | `Enter` | Confirm the typed distance and advance | | `Escape` | Cancel and reset | ## Supported entities | Entity | Supported | |--------|-----------| | Line | Yes | | Polyline / Rectangle | Yes | | Arc, Circle, Ellipse | No | | Text, Spline, Dimension, Leader | No | ## Chamfer vs Fillet | | Chamfer | Fillet | |---|---------|--------| | Corner type | Straight cut | Rounded arc | | Input | Two distances (d1, d2) | One radius | | Inserted entity | Line | Arc | | Supported entities | Line and Polyline (straight segments only) | Line, Arc, and Polyline (straight or arc segments) | --- # Circle The `circle` command draws a circle defined by a center point and a radius. After the center is clicked you can set the radius either by clicking a second point on the canvas or by typing an exact number — both options are live at the same time. ## Drawing a circle 1. Type `circle` in the terminal or click the **Circle** toolbar button. 2. **Click the center point**. 3. Set the radius — either: - **Click any point** on the canvas — the distance from the center becomes the radius, or - **Type the radius** and press **Enter** for an exact value. The circle is placed immediately and the command exits. ``` center ● \ radius line preview \ ● ← click here, or type a number ``` While in the radius phase, the live preview shows the circle at the current cursor distance and also draws a radius line from center to the current point. ## Typed radius input After the center is placed, typing immediately builds up a radius value: | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the radius value | | `Backspace` | Delete the last typed character | | `Enter` | Place the circle at the typed radius | The accumulated value is shown in the terminal prompt (e.g. `enter radius of circle: 25`). The preview updates to show the typed radius while the cursor controls the direction of the radius line marker. ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.` | Build up radius (center must already be placed) | | `Backspace` | Delete last typed character | | `Enter` | Confirm typed radius | | `Escape` | Cancel and reset | ## Grip editing — resizing the radius A selected circle exposes five grips: | Grip | Position | What it does | |------|----------|--------------| | **Center** | Center point | Moves the whole circle; radius stays unchanged | | **Left** | Leftmost point (center − radius) | Drag to set a new radius = distance to center | | **Right** | Rightmost point (center + radius) | Drag to set a new radius = distance to center | | **Top** | Topmost point | Drag to set a new radius = distance to center | | **Bottom** | Bottommost point | Drag to set a new radius = distance to center | All four cardinal grips behave identically — the new radius equals the distance from the center to the drag position. The center stays fixed. ## Selecting circles | Method | Behaviour | |--------|-----------| | **Click** | Selects if the click lands near the circumference | | **Drag right** (strict) | The entire bounding square (center ± radius) must lie inside the box | | **Drag left** (crossing) | Any part of the circumference that crosses or touches the box boundary selects the circle | ## Supported edit commands | Command | What happens to the circle | |---------|---------------------------| | [Move](./move) | Translates the center; radius unchanged | | [Copy](./copy) | Creates an identical circle at a new center | | [Rotate](./rotate) | Rotates the center around the base point; radius unchanged | | [Mirror](./mirror) | Reflects the center across the mirror axis; radius unchanged | | [Scale](./scale) | Scales the center position and multiplies the radius by the scale factor | | [Offset](./offset) | Creates a concentric circle at a larger or smaller radius | | [Delete](./delete) | Removes the circle | ## Properties **General** | Property | Default | Meaning | |----------|---------|---------| | Color | 256 (ByLayer) | ACI color index | | Layer | `0` | Layer assignment | | Linetype | ByLayer | Named linetype pattern | | Linetype Scale | 1 | Scale factor on the linetype pattern | | Thickness | 0 | Extrusion thickness | **Geometry** | Property | Meaning | |----------|---------| | Center X / Center Y | Coordinates of the center point | | Radius | Circle radius in drawing units | ## Circle vs Arc — when to use which | | Circle | Arc | |---|--------|-----| | Span | Full 360° | Partial — defined by start and end angle | | How to draw | Center + radius | Three points on the curve | | Typed input | Radius value | None — click-only | | Resize grip | 4 cardinal points | Start and end points (angle + radius) | | Dimensioning | Radius: [Dim Radius](./dim-radius) · Diameter: [Dim Diameter](./dim-diameter) | [Dim Radius](./dim-radius) | | Best for | Full holes, bolt circles, round features | Fillets, partial curves, arched paths | ## DXF — CIRCLE entity Circles are saved as `CIRCLE` entities in the DXF file. Center coordinates, radius, color, layer, linetype, linetype scale, and thickness all round-trip without loss. Any DXF-compatible application reads these as standard circles. --- # ClipboardCopy The `ClipboardCopy` command writes the selected entities to your **system clipboard** as JSON text. Because it uses the real clipboard rather than an in-memory buffer, the copied geometry survives outside the drawing: paste it into a different file, a second browser tab, or a window you open later with [ClipboardPaste](./clipboard-paste). This is the difference from [Copy](./copy): Copy duplicates entities inside the current drawing in one gesture, while ClipboardCopy puts them somewhere they can be retrieved from a different drawing entirely. ## Two ways to start **Pre-select, then copy**: select entities, press `Ctrl+C` (`Cmd+C` on macOS) or type `ClipboardCopy` — the entities are written to the clipboard immediately and the command exits. **Activate, then select**: press `Ctrl+C` with an empty selection, the prompt reads "pick objects to copy — Enter or Space to confirm", select objects by click or area drag, then press Enter or Space to copy and exit. Pressing Enter or Space with nothing selected simply ends the command without touching the clipboard. ## What gets copied | Part | Purpose | |------|---------| | Entities | The full serialized form of every selected entity | | Reference point | The lower-left corner of the selection's combined bounds — what ClipboardPaste anchors to the cursor | | Layers | Only the layers the copied entities actually reference, by name | | Linetypes | Only the linetypes the copied entities actually reference, by name | Only referenced table entries travel with the copy, not the whole source drawing's layer and linetype tables. Hatch patterns are not bundled and don't need to be: a drawing's pattern table is the built-in default set, and any uploaded `.pat` files live in a per-user store already shared across tabs, so a pasted hatch resolves its own pattern. ## Confirmation On success the terminal reports the count, e.g. `3 entities copied to clipboard`. If the browser refuses clipboard access, the terminal shows "Copy failed: clipboard access denied" and nothing is written — a browser permission decision, not a drawing error. Click once on the canvas to give the page focus and try again. Because the payload is ordinary JSON text, anything else copied afterwards replaces it. ## Keyboard reference | Key | Action | |-----|--------| | `Ctrl+C` / `Cmd+C` | Activate ClipboardCopy | | `Enter` / `Space` | Copy the current selection, or exit if nothing is selected | | `Escape` | Cancel without copying | ## Supported entities ClipboardCopy works on every entity type, serialized with the same mechanism the native `.json` export uses, so nothing is dropped on the way out. --- # ClipboardPaste The `ClipboardPaste` command reads entities that [ClipboardCopy](./clipboard-copy) wrote to the **system clipboard** and places them in the current drawing at a point you pick. Because the clipboard is the real system one, the source can be a different drawing, a different browser tab, or a session from earlier in the day. ## How to paste 1. Press `Ctrl+V` (`Cmd+V` on macOS), or type `ClipboardPaste` in the terminal. 2. The prompt reads "reading clipboard…" while the browser hands over the clipboard text. 3. Once loaded the prompt changes to "pick insertion point" and a preview follows the cursor. 4. **Click** to place the entities. They are added to the drawing and left selected. The preview is anchored by the copy's reference point — the lower-left corner of the original selection's combined bounds — so the relative arrangement of the copied entities is preserved exactly. ## What happens on paste | Step | Behaviour | |------|-----------| | New identities | Every pasted entity is given a fresh id, so pasting twice yields two independent sets | | Translation | Entities are offset by cursor − reference point | | Layer merge | Any referenced layer the destination drawing lacks is added by name | | Linetype merge | Any referenced linetype the destination drawing lacks is added by name | | Selection | The previous selection is cleared and the pasted entities become the selection | Missing table entries are added; existing ones are left alone. If the clipboard carries a layer named `WALLS` in red and the destination already has a `WALLS` layer in blue, the destination's definition wins and the pasted entities join it. Nothing in the destination drawing is redefined by a paste. ## When the clipboard has nothing to paste ClipboardPaste only accepts payloads that ClipboardCopy produced. Anything else — plain text, a URL, an image, JSON from another application — is rejected with "Clipboard has no copied entities". If the browser refuses clipboard access entirely, the message is "Clipboard access denied". Both end the command without changing the drawing. ## Keyboard reference | Key | Action | |-----|--------| | `Ctrl+V` / `Cmd+V` | Activate ClipboardPaste | | `Escape` | Cancel — the entities are discarded and nothing is added | Escaping during the read phase is safe: if the clipboard resolves after the user has already cancelled or started another command, the late result is discarded rather than interrupting whatever is active by then. ## Copying between tabs Open the source drawing, select geometry, press `Ctrl+C`; switch to the other tab or open a second tab on a different file; press `Ctrl+V` and click an insertion point. Both tabs are the same origin and share the system clipboard, so nothing is uploaded and no server is involved — the payload is JSON text on your own clipboard the entire time. --- # Copy The `copy` command creates translated duplicates of selected entities and places them offset from a base point to a destination — the originals stay exactly where they are. This is the one key difference from [Move](./move): Copy adds new entities to the drawing; Move relocates existing ones. ## Two ways to start **Pre-select, then copy** — select entities first, then activate: 1. Select one or more entities on the canvas. 2. Type `copy` in the terminal or click the **Copy** toolbar button. 3. **Click the base point** — the reference for the displacement. 4. **Click the destination** — duplicates appear at the base→destination offset. **Activate, then select** — start the command with nothing selected: 1. Type `copy` or click the toolbar button. 2. **Select objects** — click to toggle individual entities, or drag to select by area. 3. Press **Enter** or **Space** to confirm the selection. 4. **Click the base point**, then **click the destination**. ``` Before: After: [entity A] [entity A] ← originals untouched [entity B] [entity B] [copy of A] ← new entities [copy of B] ``` A ghost preview of the copies follows the cursor from the base point to the destination. ## Angle locking and exact distance After the base point is set the command snaps to 45° axes (0°, 45°, 90°, 135°, …) when the cursor is far enough and near the axis. While locked, type a distance and press **Enter** to place the copies at exactly that offset. | Key | Action | |-----|--------| | `0`–`9`, `.` | Append to the distance value | | `-` | Negative distance — reverses direction along the axis (first character only) | | `Backspace` | Delete the last typed character | | `Enter` | Place copies at the typed distance | ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Confirm selection and advance to base-point phase | | `0`–`9`, `.`, `-` | Build up distance (angle lock must be active) | | `Backspace` | Delete last typed character | | `Enter` | Apply copy at typed distance | | `Escape` | Cancel and reset | ## Selection during the command | Method | Behaviour | |--------|-----------| | **Click** | Toggles the entity under the cursor in/out of the selection | | **Drag right** (strict) | Adds entities fully inside the box | | **Drag left** (crossing) | Adds entities that intersect the box boundary | | **Enter** / **Space** | Confirms the selection | ## After the copy The **originals remain selected** — the new copies are added to the drawing but the selection is cleared and the command exits. To work with the copies immediately, run Copy again on the selection, or start a new command. ## Copy vs Move | | Copy | Move | |---|------|------| | Originals | Stay in place | Removed from original position | | Result count | Increases by the number of copied entities | Unchanged | | After operation | Originals still selected | Moved entities selected at new position | | Best for | Repeating geometry, symmetric layouts | Repositioning geometry | ## Supported entities Copy works on every entity type. All entities implement `translate(dx, dy)` internally so none are excluded. --- # Delete The `delete` command removes selected entities from the drawing. Deletions are recorded in the [Undo](./undo) history and can be reversed with up to 20 steps. There is no separate "confirm delete" dialog — confirmation is a single key press. ## Two ways to delete **Pre-select, then delete** — the fastest path: 1. Select one or more entities on the canvas. 2. Type `delete` in the terminal, click the **Delete** toolbar button, **or press the `Delete` key** directly. Entities are removed instantly — no additional confirmation step. **Activate, then select**: 1. Type `delete` or click the toolbar button (with nothing selected). 2. **Select objects** — click to toggle, or drag to select by area. 3. Press **Enter**, **Space**, or **Delete** to confirm and remove the selected entities. ## Delete key shortcut The `Delete` key on the keyboard acts as a **global shortcut** — if any entities are currently selected, pressing it deletes them immediately, even without opening the Delete command in the terminal. This is the fastest single-step deletion workflow: ``` Click entity → press Delete key → done ``` ## Selection during the command | Method | Behaviour | |--------|-----------| | **Click** | Toggles the entity under the cursor in/out of the selection | | **Drag right** (strict) | Selects only entities fully inside the box | | **Drag left** (crossing) | Selects entities that intersect the box boundary | | **Enter** / **Space** / **Delete** | Confirms and deletes selected entities | ## Recovering deleted entities Deletions are undoable with the [Undo](./undo) command (type `undo` or use the toolbar button). Up to **20 steps** can be reversed per file, and the history persists across page reloads. If you have exceeded 20 deletions without saving, earlier deletions cannot be recovered. ## Supported entities Delete works on every entity type — Line, Polyline, Rectangle, Circle, Arc, Ellipse, Text, Spline, Dimension, Leader, and all others. --- # Dimension Aligned The `dimaligned` command places a dimension that measures the **true straight-line distance** between two points. The dimension line runs parallel to the line connecting the two points, so it can be at any angle. This is the key difference from [Dimension Linear](./dim-linear), which is restricted to horizontal or vertical. ## Anatomy of an aligned dimension ``` ● p2 /| / | (ext line 2, perpendicular to dim line) / | /←5.00→/ / / ● / (ext line 1, perpendicular to dim line) p1 ``` - **Extension lines** — perpendicular to the dimension line, drawn from each measured point. - **Dimension line** — parallel to p1→p2, offset to one side by cursor position. - **Value** — the true Euclidean distance `|p1 – p2|`. ## Placing an aligned dimension 1. Type `dimaligned` in the terminal or click the **Dimension Aligned** toolbar button. 2. **Click the first extension line origin** (p1). Angle locking snaps to 45° while picking p2. 3. **Click the second extension line origin** (p2). 4. **Move the cursor** to one side to set the perpendicular offset of the dimension line. 5. **Click** to place, or type an offset distance and press **Enter** for precise placement. ## Typed offset distance Type a number during placement to fix the dimension line at an exact perpendicular distance from the p1→p2 line: | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the offset | | `Backspace` | Delete the last typed character | | `Enter` / `Space` | Place at the typed offset | The cursor side determines which side the dimension line appears on. ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.` | Build up offset distance (placement phase) | | `Backspace` | Delete last typed character | | `Enter` / `Space` | Confirm typed offset | | `Escape` | Cancel | ## Dimension Aligned vs Dimension Linear | | Dimension Aligned | Dimension Linear | |---|------------------|-----------------| | Dimension line angle | Parallel to p1→p2 — any angle | Always horizontal or vertical | | Measures | True Euclidean distance | X or Y component only | | H/V orientation lock | No | Yes — `H` and `V` keys | | Best for | Diagonal features, angled cuts | Orthogonal layouts, grid-aligned parts | ## Chaining dimensions To add more dimensions continuing from the second extension line of this one, use [Dimension Continue](./dim-continue) — it locks to the same measurement angle as this aligned dimension. ## DXF — DIMENSION entity (aligned type) Aligned dimensions are saved as `DIMENSION` entities with `dimType = 1` (aligned). Extension line origins, dimension line position, text position, measured value, rotation, arrow style, and all display flags round-trip without loss. --- # Dimension Angular The `DimensionAngular` command places an **angular dimension** arc annotation on the drawing. It measures and labels the angle between two lines, the span of an arc, or a sector of a circle. ## How to activate Click the **Dimension Angular** toolbar button in the Markup panel, or type `DimensionAngular` in the terminal. ## Three input modes The first click determines which mode is used: ### Two lines 1. **Click the first line.** The cursor position determines which side of the line is used. 2. **Click the second line.** The two lines must intersect (the intersection is computed automatically; it does not need to be visible on screen). 3. **Click to place** the dimension arc. Move the cursor to choose the radius and which angular sector is labelled — the annotation follows the cursor to whichever side of the vertex you are on. Parallel lines cannot form an angular dimension; the command ignores the second click if the lines do not intersect. ### Arc 1. **Click an arc.** The dimension is created immediately from the arc's start angle to its end angle, using the arc's center as the vertex. 2. **Click to place** the dimension arc at the desired radius. ### Circle 1. **Click a circle.** The first angle endpoint snaps to the nearest point on the circle. 2. **Click a second point** on the circle to define the second angle endpoint. 3. **Click to place** the dimension arc. ## Keyboard reference | Key | Action | |-----|--------| | `Escape` | Cancel and return to the first pick | ## Behaviour details - The dimension arc is always drawn on the side of the vertex where you place it — move the cursor across the vertex to flip to the supplementary angle. - The measured angle is shown in degrees and updates live as you move the cursor during placement. - The resulting annotation is a full `DimensionAngular` entity stored on the current layer. Its appearance properties (arrow size, text height, extension line length) can be adjusted in the Properties panel. - Angular dimensions are included in JSON export but are not supported by the DXF exporter. ## Related commands - [Dimension Linear](./dim-linear) — horizontal or vertical dimension - [Dimension Aligned](./dim-aligned) — dimension aligned to two points - [Dimension Radius](./dim-radius) — radius dimension for arcs and circles - [Dimension Diameter](./dim-diameter) — diameter dimension for circles --- # Dimension Continue The `dimcontinue` command chains new dimensions from the **second extension line** of an existing dimension. Each new segment is placed along the same measurement axis and at the same dimension-line offset as the base. All style properties — arrow size, text height, extension line lengths — are copied from the base automatically. ## How chained dimensions look ``` |←— 3.00 —→|←— 2.50 —→|←— 4.00 —→| | | | | ● ● ● ● p1 p2 (base p3 p4 ext2 → new start) ``` Each rectangle is a separate `DIMENSION` entity. They share the same dimension-line position and measurement direction. ## Starting a chain 1. Type `dimcontinue` in the terminal or click the **Dimension Continue** toolbar button. 2. **If a dimension was just placed** — the command picks it up automatically as the base (no click needed). 3. **If no recent dimension exists** — click any existing dimension to use it as the base. 4. **Click the next extension line origin** — a preview shows the new dimension as you move the cursor. 5. Keep clicking to extend the chain. Each placed dimension becomes the new base automatically. 6. Press **Enter**, **Space**, or **Escape** to finish the chain. ## What is inherited from the base dimension | Property | Inherited from base | |----------|---------------------| | Measurement direction / angle | Yes — locked for the entire chain | | Dimension line offset (distance from measured points) | Yes | | Arrow size | Yes | | Text height | Yes | | Extension line offset and extension | Yes | | Text alignment | Yes | | Style name | Yes | | Color, Layer | Not inherited — uses current layer | ## Measurement direction locking The chain's measurement direction is **fixed to the base dimension's angle**: - Linear base (H) → all continuations measure horizontal distance (Δ X). - Linear base (V) → all continuations measure vertical distance (Δ Y). - Aligned base at any angle → all continuations measure along that same angle. You cannot change the direction mid-chain. Start a new [Dimension Linear](./dim-linear) or [Dimension Aligned](./dim-aligned) to dimension in a different direction. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Finish the chain | | `Escape` | Finish the chain | ## Dimension Continue vs starting fresh | | Dimension Continue | Dimension Linear / Aligned | |---|-------------------|--------------------------| | Starting point | Fixed at last base's ext2 | Click anywhere | | Angle | Locked to base | Free | | Offset | Inherited from base | Set by cursor or typed | | Style | Inherited from base | Current style | | Best for | Cumulative measurements along a row | First dimension or changing direction | ## DXF — DIMENSION entities Each segment in the chain is stored as an independent `DIMENSION` entity in the DXF file. They are not linked in the file — they share properties because they were created from the same base, but each can be edited individually after placement. --- # Dimension Diameter The `dimdiameter` command places a diameter dimension on an arc or circle. The dimension line spans the full diameter — passing through the center between two diametrically opposite arc points — and is labeled `⌀ `. To annotate just the radius from center to one edge, use [Dimension Radius](./dim-radius). ## Anatomy of a diameter dimension ``` ●──────────── ⌀ 10.00 ────────────● (far arc point) (near arc point / text side) ``` - **Dimension line** — spans the full diameter, with arrowheads at both arc-intersection points. - **Near arc point** — the circumference point on the cursor side (where the text label sits). - **Far arc point** — the diametrically opposite point. - **Label** — `⌀` followed by the diameter value. ## Placing a diameter dimension 1. Type `dimdiameter` in the terminal or click the **Dimension Diameter** toolbar button. 2. **Click an arc or circle** to select it. 3. **Move the cursor** to rotate the dimension line to the desired angle. 4. **Click** to place the dimension. Only **Arc** and **Circle** entities can be selected. ## Keyboard reference | Key | Action | |-----|--------| | `Escape` | Cancel | ## Dimension Diameter vs Dimension Radius | | Dimension Diameter | Dimension Radius | |---|-------------------|-----------------| | Measures | Full diameter (2 × radius) | Radius (center to edge) | | Dimension line | Edge → edge through center | Center → edge | | Label prefix | `⌀` | `R` | | Arrowheads | Two (at both arc points) | One (at arc point) | | Best for | Full circular hole or shaft dimensions | Annotating one side of a curved feature | ## DXF — DIMENSION diameter entity Diameter dimensions are saved as `DIMENSION` entities with diameter-type geometry, storing both arc-point positions and the measured diameter value (2 × radius). All properties round-trip without loss. --- # Dimension Linear The `dimlinear` command places a horizontal or vertical dimension between two extension-line origins. The dimension line always runs either perfectly horizontal or perfectly vertical — it cannot be placed at an arbitrary angle. Use [Dimension Aligned](./dim-aligned) when you need a dimension parallel to a diagonal line. ## Anatomy of a linear dimension ``` |←————— 5.00 —————→| | | ● (ext line 1) ● (ext line 2) p1 p2 ``` - **Extension lines** — drop from each measured point perpendicular to the dimension line. - **Dimension line** — horizontal (measures X distance) or vertical (measures Y distance). - **Value** — the projected distance along the chosen axis, not the true point-to-point distance. ## Placing a linear dimension 1. Type `dimlinear` in the terminal or click the **Dimension Linear** toolbar button. 2. **Click the first extension line origin** (p1). 3. **Click the second extension line origin** (p2). Angle locking snaps to 45° while picking p2. 4. **Move the cursor** to position the dimension line. Orientation is detected automatically from cursor position. 5. **Click** to place, or type an offset distance and press **Enter** for precise placement. ## Automatic orientation detection When no orientation is forced, the command reads the cursor position relative to the two measured points: | Cursor position | Detected orientation | What is measured | |-----------------|---------------------|-----------------| | Above or below the points | Horizontal | Δ X between p1 and p2 | | Left or right of the points | Vertical | Δ Y between p1 and p2 | Press **H** to lock horizontal or **V** to lock vertical at any time during the placement phase. Once locked, the orientation does not change as you move the cursor. ## Typed offset distance Type a number during placement to fix the dimension line at an exact distance from the measured points: | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the offset distance | | `Backspace` | Delete the last typed character | | `Enter` / `Space` | Place at the typed distance | The cursor side (above/below for horizontal, left/right for vertical) determines the sign — the dimension line appears on the side the cursor is currently on. ## Keyboard reference | Key | Action | |-----|--------| | `H` | Lock horizontal orientation (placement phase only) | | `V` | Lock vertical orientation (placement phase only) | | `0`–`9`, `.` | Build up offset distance (placement phase) | | `Backspace` | Delete last typed character | | `Enter` / `Space` | Confirm typed offset | | `Escape` | Cancel | ## Dimension Linear vs Dimension Aligned | | Dimension Linear | Dimension Aligned | |---|-----------------|------------------| | Axis | Always H or V | Parallel to the measured line | | Measures | X or Y component only | True Euclidean distance | | H/V keys | Yes — lock orientation | No — always follows p1→p2 | | Best for | Orthogonal layouts, floor plans | Diagonal features, angled cuts | ## Chaining dimensions To add more dimensions continuing from the last extension line, use [Dimension Continue](./dim-continue) immediately after placing this one. ## DXF — DIMENSION entity Linear dimensions are saved as `DIMENSION` entities with `rotationDeg` set to `0` (horizontal) or `90` (vertical). Extension line origins, dimension line position, text position, measured value, arrow style, text height, and all display flags round-trip without loss. --- # Dimension Radius The `dimradius` command places a radius dimension on an arc or circle. The dimension line runs from the center to a point on the circumference in the cursor's direction, labeled `R `. To dimension a full diameter instead, use [Dimension Diameter](./dim-diameter). ## Anatomy of a radius dimension ``` ● (center) \ \ R 5.00 \ ●────── text (cursor side) (arc point) ``` - **Dimension line** — from center through an arc point toward the cursor, with an arrowhead at the arc. - **Label** — `R` followed by the radius value. ## Placing a radius dimension 1. Type `dimradius` in the terminal or click the **Dimension Radius** toolbar button. 2. **Click an arc or circle** to select it. 3. **Move the cursor** to orient the dimension line — the arc-point tracks the cursor direction from the center. 4. **Click** to place the dimension. Only **Arc** and **Circle** entities can be selected. Clicking any other entity type does nothing. ## Keyboard reference | Key | Action | |-----|--------| | `Escape` | Cancel | ## Dimension Radius vs Dimension Diameter | | Dimension Radius | Dimension Diameter | |---|-----------------|-------------------| | Measures | Radius (center to edge) | Diameter (edge to edge through center) | | Dimension line | Center → arc point | Arc point → arc point (through center) | | Label prefix | `R` | `⌀` | | Arrowheads | One (at arc point) | Two (at both arc points) | | Best for | Annotating one side of a curved feature | Annotating full circular dimensions | ## DXF — DIMENSION radius entity Radius dimensions are saved as `DIMENSION` entities with radius-type geometry, storing the center coordinates, the arc-point position, and the measured radius value. All properties round-trip without loss. --- # Distance The `distance` command measures the straight-line (Euclidean) distance between two clicked points and prints the result in the terminal to 4 decimal places. It is one of three measurement commands — [Angle](./angle) measures the angular opening at a vertex, and [Area](./area) measures the enclosed area and perimeter of a polygon. ## Anatomy of a distance measurement ``` ● first point \ \ preview line (live) \ ● second point → terminal: "Distance: 12.3456" ``` - **First point** — origin of the measurement. - **Second point** — endpoint; placing it prints the result immediately. - **Result** — displayed in the terminal, not placed on the canvas. ## Measuring a distance 1. Type `distance` in the terminal or click the **Distance** toolbar button. 2. **Click the first point**. 3. **Click the second point** — the measured distance appears in the terminal. 4. **Click again** (optional) to start a new measurement using that click as the new first point. The command stays active. Press `Escape` at any time to reset to step 2. ## Chaining measurements After the result is shown, clicking immediately starts the next measurement — the clicked point becomes the new first point. This lets you measure a sequence of distances without reactivating the command. ## Distance vs Angle | | Distance | Angle | |---|---------|-------| | What it measures | Straight-line length | Interior angle at a vertex | | Number of clicks | 2 | 3 | | Result format | `12.3456` (units) | `45.0000°` | | Canvas preview | Line from first point to cursor | Two lines from vertex to cursor | | Best for | Length of a gap or segment | Opening angle between two features | ## Keyboard reference | Key | Action | |-----|--------| | `Escape` | Cancel and reset to step 2 | ## Notes - Results are shown in the **terminal only** — nothing is added to the drawing. - The result is expressed in the same units as the drawing coordinates (no unit conversion). - Precision is always 4 decimal places. --- # Ellipse The `ellipse` command draws an ellipse using three clicks: a center point, the endpoint of the first (major) semi-axis at any angle, and the length of the second (minor) semi-axis. The two axes are always perpendicular to each other — the second axis direction is derived automatically from the first. ## Drawing an ellipse 1. Type `ellipse` in the terminal or click the **Ellipse** toolbar button. 2. **Click the center point**. 3. **Click the first axis endpoint** — sets both the direction and the length of the first semi-axis. The line from center to cursor can point in any direction. 4. **Set the second axis length** — move the cursor perpendicular to the first axis, then click or type a length. ``` ● ← first axis endpoint (step 3) / center ● / ← first axis (any angle) | ● ← cursor here sets second axis length (step 4) ``` The ellipse is placed after step 4 and the command exits. ## Axis input — click or type **First axis (step 3):** angle locking snaps to 45° increments (0°, 45°, 90°, 135°, …) once the cursor is far enough from center. While locked, type a length and press **Enter** to set an exact semi-axis distance. **Second axis (step 4):** typed length is always available — no angle lock required. The direction is already fixed perpendicular to the first axis; typing sets only the length. | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the axis length | | `Backspace` | Delete the last typed character | | `Enter` | Place the axis endpoint at the typed length | ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.` | Build up axis length (first axis: angle lock must be active; second axis: always available) | | `Backspace` | Delete last typed character | | `Enter` | Confirm typed length | | `Escape` | Cancel and reset | ## Grip editing — independent axis resizing A selected ellipse exposes five grips: | Grip | Count | What it does | |------|-------|--------------| | **Center** | 1 | Moves the whole ellipse; both axes unchanged | | **Major axis endpoints** | 2 (opposite ends of the longer axis) | Drag to resize the major semi-axis length; minor axis absolute size stays constant | | **Minor axis endpoints** | 2 (opposite ends of the shorter axis) | Drag to resize the minor semi-axis length; major axis unchanged | Major and minor axis grips are independent — you can reshape the ellipse without re-running the command. ## Selecting ellipses | Method | Behaviour | |--------|-----------| | **Click** | Selects if the click lands near the ellipse outline | | **Drag right** (strict) | The axis-aligned bounding box of the ellipse must fit entirely inside the selection box | | **Drag left** (crossing) | Any part of the ellipse outline that crosses the selection box boundary selects it | ## Supported edit commands | Command | What happens to the ellipse | |---------|-----------------------------| | [Move](./move) | Translates the center; both axes unchanged | | [Copy](./copy) | Creates an identical ellipse at a new center | | [Rotate](./rotate) | Rotates the center position and the major axis vector by the same angle | | [Mirror](./mirror) | Reflects the center and recalculates the major axis direction across the mirror axis | | [Scale](./scale) | Scales the center position and multiplies both semi-axis lengths by the factor | | [Offset](./offset) | Creates a concentric ellipse offset outward or inward by a fixed distance | | [Delete](./delete) | Removes the ellipse | ## Properties **General** | Property | Default | Meaning | |----------|---------|---------| | Color | 256 (ByLayer) | ACI color index | | Layer | `0` | Layer assignment | | Linetype | ByLayer | Named linetype pattern | | Linetype Scale | 1 | Scale factor on the linetype pattern | | Thickness | 0 | Extrusion thickness | **Geometry** | Property | Meaning | |----------|---------| | Center X / Center Y | Center of the ellipse | | Major Axis X / Major Axis Y | Vector from center to the major axis endpoint (encodes both direction and length) | | Axis Ratio | Ratio of minor semi-axis to major semi-axis (0 < ratio ≤ 1) | | Start Angle / End Angle | Parametric angles in degrees; both are 0°/360° for a full ellipse | ## Ellipse vs Circle — when to use which | | Ellipse | Circle | |---|---------|--------| | Axes | Two independent semi-axes at any angle | One radius, symmetric | | Rotation | Can be placed at any angle | No rotation | | Typed input | Per-axis length | Radius only | | Grip resize | Major and minor independently | All four cardinal points equally | | Best for | Oblique views, oval features, perspective holes | Symmetric round features | ## DXF — ELLIPSE entity Ellipses are saved as `ELLIPSE` entities in the DXF file. The format stores the center point, the full major axis vector (direction + length), and the axis ratio. Rotation, shape, and all style properties round-trip without loss. A circle is **not** stored as a degenerate ellipse — the two entity types remain distinct in the DXF model. --- # Explode The `explode` command breaks a [Polyline](./polyline) into its separate [Line](./line) and [Arc](./arc) entities — one per segment, exactly where the polyline's own vertices were. The pieces take the polyline's place and keep its lineweight, color, layer, and linetype. Explode works on **Polyline** entities only. ## Using explode Two ways to run it, the same pattern as [Delete](./delete): **Select first, then explode** — the fastest path: 1. Select one or more polylines on the canvas. 2. Type `explode` in the terminal, or click the **Explode** button in the Edit panel. The selected polylines explode immediately — no separate confirmation step, since something is already selected. **Activate, then select**: 1. Type `explode` or click the toolbar button with nothing selected. 2. **Select polylines** — click to toggle, or drag to select by area. 3. Press **Enter** or **Space** to confirm and explode the selected polylines. Only polylines are picked during selection — clicking a Line, Circle, or any other entity does nothing, and an area drag ignores everything except polylines inside or crossing its boundary. ## What you get Each segment of the polyline becomes its own entity: - A **straight segment** becomes a **Line**. - An **arc segment** (from Polyline's [Arc option](./polyline)) becomes an **Arc**, matching the original curve's center, radius, and sweep exactly. Each resulting Line and Arc inherits the source polyline's **lineweight, color, layer, linetype, and linetype scale** — nothing about the geometry's appearance changes, it's just now several independent entities instead of one connected polyline. Explode can be undone in one step with [Undo](./undo), like any other edit. ## Selection during the command | Method | Behaviour | |--------|-----------| | **Click** | Toggles the polyline under the cursor in/out of the selection; clicking a non-polyline entity does nothing | | **Drag right** (strict) | Selects only polylines fully inside the box | | **Drag left** (crossing) | Selects polylines that cross the box boundary | | **Enter** / **Space** | Confirms and explodes the selected polylines | ## Supported entities | Entity | Supported | |--------|-----------| | Polyline / Rectangle | Yes | | Line, Arc, Circle, Ellipse | No — nothing to explode | | Text, Spline, Dimension, Leader, Hatch | No | --- # Export The **Export** command downloads the current drawing to your file system. Two formats sit side by side: **DXF** for compatibility with other CAD tools and **JSON** for full-fidelity saves within KulmanLab CAD. Each has its own checklist of what to put in the file. ## How to export 1. Click the **Export** toolbar button (download icon) in the File panel. 2. The **Export Manager** popup opens with two columns, **JSON** and **DXF**, each listing the drawing's entity types with a checkbox and a count. 3. Untick anything you want left out. Everything starts ticked. 4. Click **Export JSON** or **Export DXF**. The file downloads to your default downloads folder and the popup closes. Press `Escape` to close the popup without exporting. ## Choosing what to export Both columns list the same entity types, each with a count of how many are in the drawing: Lines, Circles, Arcs, Ellipses, Polylines, Splines, Text, Radius Dimensions, Diameter Dimensions, Angular Dimensions, Linear Dimensions, Leaders, Hatches Everything is ticked when the popup opens, so exporting straight away gives you the whole drawing. Untick a type to leave it out of that one file. - **The two columns are independent.** Unticking Hatches under DXF has no effect on what **Export JSON** produces — each format keeps its own selection. - **A type you don't have is greyed out.** A row whose count is `0` cannot be ticked, so the list doubles as an inventory of the drawing. - **The counts are a snapshot.** They are taken when the popup opens and do not update if the drawing changes behind it. Close and reopen to refresh. - **Nothing is deleted.** Unticking shapes the exported file only; the drawing itself is untouched. **Linear Dimensions** covers linear, aligned, and continued dimensions — one entity type created by three different commands. Radius, diameter, and angular dimensions each get their own row. To produce a cut file for laser cutting or CNC, untick Text, the four dimension rows, Leaders, and Hatches, then **Export DXF**. ## Choosing a format | Format | Extension | Best for | Limitations | |--------|-----------|----------|-------------| | **JSON** *(native)* | `.json` | Saving work to reopen in KulmanLab CAD | Not compatible with other CAD tools | | **DXF** | `.dxf` | Sharing with AutoCAD, FreeCAD, LibreCAD, etc. | How much survives depends on the receiving application | **When to use JSON:** anytime you want to save a complete copy of your work. JSON is KulmanLab's native format and preserves every entity exactly — including dimensions, leaders, hatches, and all layer data. **When to use DXF:** when you need to hand off the drawing to someone using another CAD application. The exported file uses AC1032 DXF format and can be opened in most DXF-compatible tools. ## What is exported per format ### JSON export All entity types are included: - Lines, circles, arcs, ellipses, polylines, splines, text - Dimensions (linear, aligned, continued, radius, diameter) - Multileaders - [Hatch](./hatch), with pattern, scale, angle, and origin - Layer definitions and linetype tables ### DXF export All entity types are included: - Lines, circles, arcs, ellipses, polylines (exported as `LWPOLYLINE`), splines - Text, written as `MTEXT` with its per-run formatting — font, height, bold, italic, underline, strikethrough - Dimensions (linear, aligned, continued, radius, diameter, angular), as standard `DIMENSION` entities - Multileaders, as `MULTILEADER` - [Hatch](./hatch), with pattern, scale, angle, and origin - Layer definitions and linetype tables A drawing exported from KulmanLab therefore arrives with its annotation intact rather than as bare geometry. What each receiving application makes of it still varies — DXF support differs between tools, and an older one may ignore entities a newer one reads. If a drawing has to look identical everywhere, use [Print](./print) to capture it as a PDF or image instead. ## Exported file name The downloaded file is named after the current drawing file (e.g. `myplan.json`). The extension changes to match the chosen format. ## Difference between Export and Print | Feature | Export | Print | |---------|--------|-------| | Output | Vector source file (.dxf / .json) | Raster image (.png / .jpeg / .webp / .pdf) | | Editable in other tools | Yes (DXF) | No | | Preserves layers & linetypes | Yes | No (rendered flat) | | Captures dimensions & leaders | JSON only | Yes | Use **Export** when you need an editable file. Use [Print](./print) when you need a visual snapshot. ## Related commands - [Import](./import) — open a DXF or JSON file - [Print](./print) — export the canvas as a PNG, JPEG, WebP, or PDF image - [File Manager](./file-manager) — browse drawings saved in browser storage --- # Extend The `extend` command stretches the nearest endpoint of a hovered [Line](./line), [Arc](./arc), [Ellipse](./ellipse), or open [Polyline](./polyline) to the closest intersection it would form with another entity in the drawing. Hover near the endpoint you want to extend — a preview shows the extended entity — then click to apply. Only entities with an actual endpoint can be extended. A [Circle](./circle) and a full (360°) Ellipse are always closed shapes with no endpoint, so they can never be extended — same for a closed Polyline or Rectangle. A partial Ellipse (an elliptical arc) and an Arc do have endpoints and extend the same way a Line does. ## Extending an entity 1. Type `extend` in the terminal or click the **Extend** toolbar button. 2. **Hover near one end** of the entity you want to extend — the preview shows it extended to the nearest boundary in that direction. 3. **Click** to apply the extension. The command stays active after each extension, so you can continue hovering and clicking to extend more entities. Press **Enter**, **Space**, or **Escape** to exit. ``` Before: After: ────── | ──────────────| (short line) (boundary) (extended to boundary) ``` ## How the endpoint is chosen The command looks at which end the cursor is closer to: - **Line and open Polyline** — cursor nearer the end point extends the end forward; cursor nearer the start point extends the start backward. - **Arc and partial Ellipse** — cursor nearer one angular end grows the arc in that direction, sweeping around the same center and radius (or the same ellipse shape) until it reaches the next boundary. A ray — or, for Arc and Ellipse, the entity's own underlying circle or curve — is cast from the chosen end, and the **closest intersection** with any other entity (excluding the entity itself and the ignored types) becomes the new endpoint. If no intersection is found in that direction, no preview appears and clicking does nothing. ## Boundary exclusions The following entity types are ignored as boundaries — an entity does not extend to meet them: - Text / Mtext - Multileader - Spline All other types (Line, Arc, Circle, Ellipse, Polyline, Dimension) serve as valid boundaries. If a [Polyline](./polyline)'s first or last segment is itself an arc (drawn with the Arc toggle), extending it grows the arc along its own circle — the same way extending a standalone [Arc](./arc) does — rather than treating it as straight. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Exit extend mode | | `Escape` | Exit extend mode | ## Supported entities | Entity | Can be extended? | |--------|----------------| | Line | Yes | | Arc | Yes | | Ellipse | Yes — only if it's already a partial arc; a full ellipse has no endpoint | | Circle | No — always a closed shape with no endpoint | | Polyline (open) | Yes | | Polyline (closed) / Rectangle | No — always a closed shape with no endpoint | | Text, Spline, Dimension, Leader | No | ## Extend vs Trim | | Extend | Trim | |---|--------|------| | What it does | Stretches an entity's endpoint to a boundary | Removes a segment of an entity | | Trigger | Hover near the endpoint to stretch | Hover over the segment to cut | | Result | Endpoint moves outward | Entity splits or shortens | | Supported entities | Line, Arc, Ellipse, Polyline | Line, Arc, Circle, Ellipse, Polyline | --- # File Manager The `FileManager` command opens a **thumbnail grid** of every drawing that has been saved to your browser's local storage, ordered by when each was last saved. Use it to reopen a previous drawing, rename it, or delete it. ## Opening the File Manager Type `FileManager` in the terminal, or click the **File Manager** toolbar button (history icon) in the File panel at the top of the screen. The panel opens on the left side of the canvas and closes automatically as soon as you start another command or [import](./import) a file, so it never lingers over a drawing it doesn't list yet. ## The thumbnail grid Each saved drawing is a card showing a live-rendered thumbnail, its name, and when it was last updated. Thumbnails are generated fresh each time the panel opens. | Action | How | |--------|-----| | **Open** a drawing | Click its thumbnail — replaces the current canvas content | | **Rename** | Click the pencil icon, or double-click the name | | **Delete** | Click the trash icon, then confirm | If no files have been saved yet, the panel shows "No files saved". The card for whichever file is currently open in the editor is marked with an accent-colored ring and has no delete button — renaming it is still available. ## Deleting a file Clicking the trash icon does not delete immediately — it arms a confirmation overlay on that card ("Delete this file?") since deletion is permanent and cannot be undone. ## Renaming a file Click the pencil icon (or double-click the file name) to edit it in place, then **Enter** to confirm or **Escape** to cancel. A rename is rejected if the new name is empty, longer than 100 characters, already used by another saved file (case-insensitive), ends in a dot, or is a Windows-reserved device name (`CON`, `PRN`, `AUX`, `NUL`, `COM1`–`COM9`, `LPT1`–`LPT9`). Characters not valid in a file name (`\ / : * ? " < > |`) are stripped automatically as you type. ## Back up your work — browser storage is not permanent KulmanLab saves drawings to **IndexedDB**, a database built into your browser: files are stored **locally on your device only**, and each browser/device has its own independent storage. This storage can be cleared without warning — clearing site data, low disk space, private/incognito mode, reinstalling the browser, or switching devices. **The only reliable way to keep a drawing safe is to [export](./export) it** — `.json` for full fidelity, `.dxf` for compatibility with other CAD tools. ## Automatic file loading on startup When you open KulmanLab CAD, the app automatically loads the **most recently modified file** from storage. You do not need to manually open it from the File Manager each time. ## Managing storage There is no fixed limit on the number of drawings you can save, but browser storage is finite. If you notice storage warnings, delete older files from the File Manager — or export them first so nothing is lost. To remove all saved drawings at once, use the [WipeStorage](./wipestorage) command. ## File names New and imported files get a **plain name — no timestamp is baked in**. If that name is already taken, a Finder/Explorer-style suffix is appended automatically (`plan (2)`, `plan (3)`, …) so nothing gets overwritten. ## Related commands - [Import](./import) — load a drawing from your file system into browser storage - [Export](./export) — download a drawing to your file system - [New File](./new-file) — start a blank drawing (also saved automatically) - [WipeStorage](./wipestorage) — clear all saved files from browser storage --- # Fillet The `fillet` command rounds the corner between two [Line](./line), [Arc](./arc), or [Polyline](./polyline) segments by inserting a tangent arc of a given radius, and trims (or merges) the selected entities to that point. Fillet works on **Line, Arc, and Polyline** entities — including a polyline's own straight or arc segments. ## Using fillet 1. Type `fillet` in the terminal or click the **Fillet** toolbar button. 2. **Type the fillet radius** and press **Enter**. 3. **Click the first line, arc, or polyline segment** — the portion you click determines which side of any intersection is kept. 4. **Hover over the second entity** — a dashed arc preview shows the resulting fillet. Move the cursor to the side you want to keep. 5. **Click** to apply. ``` Before: After fillet (radius r): ────────────── ──────────╮ │ ╰──── │ ``` ## Side selection for intersecting entities When two entities cross each other, the fillet is applied on the corner defined by the click positions — the portion of each entity on the **same side as the cursor** is kept. - Click near one end of the first entity to select that half. - Move the cursor to the desired half of the second entity — the dashed preview updates live. ## What the command creates The result depends on what you selected: - **Two independent Line/Arc entities**, or any pair that doesn't include an open polyline: both are trimmed to the tangent points **T1**/**T2**, and a new Arc entity is inserted between them. - **Two segments of the same polyline that share a corner vertex**: no new entity — the fillet becomes part of the polyline itself. The corner vertex is replaced by two tangent points, and the arc between them is stored as that edge's bulge value — exactly how a rounded polyline corner round-trips through DXF. - **Every other case involving an open polyline** — two different open polylines, or an open polyline and an independent Line/Arc: both are merged into **one new polyline**, with each side kept to its tangent point and the fillet arc added as an extra bulge segment, replacing the original entities. The inserted or extended arc inherits the current lineweight, color, layer, and linetype settings (or, when it merges into a polyline, the polyline's own settings). ## Corners with no real angle to round If the two selected segments already meet tangentially at a shared vertex — a straight polyline corner, or a line that smoothly becomes a tangent-continuation arc segment — there is no real corner for any circle to round. Fillet detects this and refuses rather than producing a degenerate loop, showing `cannot fillet: no tangent circle fits there`. ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the radius value | | `Backspace` | Delete last typed character | | `Enter` / `Space` | Confirm the typed radius and move to entity selection | | `Escape` | Cancel and reset | ## Supported entities | Entity | Supported | |--------|-----------| | Line | Yes | | Arc | Yes | | Polyline (straight or arc segment) | Yes | | Circle, Ellipse | No | | Text, Spline, Dimension, Leader | No | ## Fillet vs Chamfer | | Fillet | Chamfer | |---|--------|---------| | Corner type | Rounded arc | Straight cut | | Input | One radius | Two distances (d1, d2) | | Inserted entity | Arc | Line | | Supported entities | Line, Arc, and Polyline (straight or arc segments) | Line and Polyline (straight segments only) | --- # Fit The `fit` command calculates the bounding box of all entities in the drawing and adjusts both zoom level and pan position so every entity is visible with a small margin. It is the fastest way to recover a lost view or orient yourself after importing a DXF file. ## Fitting the view Click the **Fit** toolbar button or type `fit` in the terminal. The view adjusts immediately and the command exits — no interaction required. **Double-click the middle mouse button** triggers the same Fit operation at any time without activating any command — the fastest shortcut to reset a lost view mid-drawing. ## How the bounding box fit works 1. Fit finds the axis-aligned bounding box enclosing all entities (min X, max X, min Y, max Y). 2. The zoom level is set so the taller or wider dimension fills the canvas with a margin. 3. The view is centered on the bounding box midpoint. | Drawing state | Result | |--------------|--------| | Wider than tall | Zoom constrained by width | | Taller than wide | Zoom constrained by height | | Single entity | Fits around just that entity | | Empty drawing | View does not change | ## Fit vs manual zoom controls | | Fit | Zoom In / Zoom Out | Scroll wheel | |---|-----|--------------------|-------------| | Centers on | All entities | Viewport midpoint | Cursor | | Step size | Automatic (one shot) | 1.5× per step | ~1.1× per tick | | Best for | Recovering lost view, post-import orient | Stepping in/out from center | Precise cursor-targeted zoom | ## Keyboard reference There is no keyboard shortcut for this command. Use the **middle mouse button double-click** shortcut instead. ## Related view commands | Command | What it does | |---------|-------------| | [Pan](./pan) | Shifts the viewport without zooming | | [Zoom In](./zoom-in) | Multiplies zoom by 1.5× per step | | [Zoom Out](./zoom-out) | Divides zoom by 1.5× per step | --- # Font Manager The `FontManager` command opens a dialog for browsing and selecting fonts, and for uploading your own `.ttf` files for use in Text and Multileader entities. ## Opening the Font Manager - Type `FontManager` in the terminal, **or** - Click the **Font Manager** button in the text editor toolbar. ## Font groups | Group | Contents | |-------|----------| | **Default** | The built-in sans-serif font — always available | | **User** | Your own uploaded `.ttf` fonts (only shown once you've added one) | | **Free** | 15 bundled Google Fonts (EB Garamond, Fira Code, Inter, Lato, Merriweather, Montserrat, Nunito, Open Sans, Oswald, Playfair Display, Poppins, Raleway, Roboto, Roboto Condensed, Source Code Pro) | | **System** | Common OS fonts (Courier New, Georgia, Helvetica, Impact, Lucida Console, Tahoma, Times New Roman, Trebuchet MS, Verdana) | Click any font in the list to preview it on the right — name, alphabet sample, a pangram, and digits. ## Uploading a custom font 1. Click **Add Font** in the dialog footer (or type `FontAdd` in the terminal to open the file picker directly). 2. Choose a `.ttf` file. Only TrueType fonts are supported — `.otf` and `.woff`/`.woff2` are not. 3. The file name (without the extension) becomes the font's name in the **User** group. For example, uploading `MyFont.ttf` adds a font named `MyFont`. Uploaded fonts are saved permanently in the browser (IndexedDB) and reload automatically the next time you open KulmanLab CAD. ## Removing a custom font Hover a font in the **User** group and click the **×** button next to it. Built-in fonts (Default, Free, System) cannot be removed. ## Keyboard reference | Key | Action | |-----|--------| | `↑` / `↓` | Move the selection up or down the font list | | `Escape` | Close the Font Manager | ## DXF compatibility The font name is embedded in exported **MTEXT** entities as an inline formatting code, so a DXF round-tripped through KulmanLab CAD keeps its font assignment. Custom font *files* are not embedded in the DXF — only the font *name* is. If you re-import a drawing that references a custom font you haven't uploaded on this device, text renders in the default font until you upload a font with that same name. ## Related commands | Command | What it does | |---------|-------------| | [Text](./text) | Places the text labels that font choices apply to | | [Match Properties](./match-properties) | Copies text height, but not font, between entities | --- # Hatch The `hatch` command fills the region enclosing a picked point with a pattern. The boundary isn't drawn first — it comes from whatever is already on the canvas, so entities meeting end to end enclose a region exactly as a closed [Polyline](./polyline) does, and any closed shape sitting inside becomes an island the fill leaves alone. ## Filling an area 1. Type `hatch` or click the **Hatch** toolbar button (the swatch icon). 2. Click a point inside the region to fill. 3. The command stays active — keep clicking to fill more areas, each pick creating its own `Hatch` entity. 4. Press **Enter**, **Space**, or **Escape** when done. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Finish the Hatch command | | `Escape` | Finish the Hatch command (same as Enter/Space) | ## What can bound a region Any mix of these, connected end to end with no gap: [Line](./line), [Arc](./arc), [Circle](./circle), [Ellipse](./ellipse), [Polyline](./polyline) / [Rectangle](./rectangle), Spline ([Spline CV](./spline-cv) / [Spline Fit](./spline-fit)). Text, Multileader, and Dimension entities are never treated as boundaries. ## Islands Anything fully closed inside the picked region — a circle, a closed polyline, another hatch's boundary — becomes an island: the fill stops at its edge and leaves it empty. Nesting a closed shape inside another alternates fill/hole at each level. ## When a pick fails | Message | Meaning | |---------|---------| | "no boundary found" | Nothing was hit in any direction from the picked point | | "point is not enclosed" | A boundary exists nearby, but doesn't contain the picked point | | "boundary is open" | The nearest boundary has a gap somewhere | | "boundary too complex" | The boundary loop couldn't be closed within the traversal limit | The command stays active after a failed pick. ## Choosing a pattern Every new hatch starts filled with `ANSI31` (or whichever pattern the last hatch you edited used) — there's no pattern picker before you draw. Select an existing hatch and open its **Pattern** field in the properties panel to open the pattern picker and change it; that choice becomes the default for the next hatch you create, the same way picking a layer or color carries forward. See [Hatch Manager](./hatch-manager) for uploading custom `.pat` files and browsing the full library. `SOLID` is a plain entry in the pattern list, not a separate checkbox or mode. ## Properties | Property | Meaning | |----------|---------| | Pattern | The pattern name, from the shared pattern vocabulary | | Pattern Scale | Scales the pattern's line spacing | | Pattern Angle | Rotates the pattern independently of the boundary | | Origin X / Origin Y | Where the pattern's own repeat is anchored | Moving, rotating, mirroring, or scaling a hatch carries its pattern placement along, so the fill stays aligned with the boundary. ## Grip editing the boundary A selected hatch grips its boundary the way a Polyline grips its vertices — one grip at every corner, one at every edge midpoint. Dragging a corner moves it (a line follows exactly; an arc refits through both neighbors; an ellipse/spline edge snaps to the nearest point on its own curve). Dragging a line/ellipse/spline edge midpoint slides the whole edge. Dragging an arc edge midpoint **bows** the arc through the cursor instead, leaving both ends fixed. The center grip activates [Move](./move) for the whole hatch. ## DXF — HATCH entity Hatches **import** from `HATCH` entities: KulmanLab reads the boundary geometry plus the pattern name, scale, and angle (group codes 70/41/52) — not the pattern's own inline line definitions that AutoCAD writes into the file. The name is looked up in KulmanLab's own pattern library (built-ins plus anything uploaded in [Hatch Manager](./hatch-manager)); a name that isn't in the library falls back to `ANSI31` so the drawing still reads as hatched. Spline-bounded loops (DXF boundary edge type 4) are not yet read. Hatches do **not** currently **export** to DXF — use [Export](./export)'s `.json` format to keep a hatch when saving a drawing that includes one; the `.dxf` format leaves it out. ## Related commands - [Hatch Manager](./hatch-manager) — browse the pattern library and upload `.pat` files - [Move](./move), [Copy](./copy), [Rotate](./rotate), [Mirror](./mirror), [Scale](./scale) — all carry the hatch's pattern placement along - [Delete](./delete) — removes the hatch without affecting the entities that bounded it --- # Hatch Manager The `HatchManager` command opens a dialog for browsing hatch patterns with a live swatch preview, and for uploading your own `.pat` pattern files for use with [Hatch](./hatch). ## Opening the Hatch Manager Type `HatchManager` in the terminal. This is separate from the pattern picker that opens from a hatch's **Pattern** chip in the properties panel — the picker chooses a pattern for one hatch, the Hatch Manager is where you add or remove `.pat` files. ## Pattern groups | Group | Contents | |-------|----------| | **User** | Patterns from your own uploaded `.pat` files, sub-grouped by which file each came from (shown once you've uploaded one) | | **Standard** | `SOLID` plus this drawing's own pattern table — every new drawing starts with the same built-in library, the way its layers and linetypes do | Click any pattern (or use `↑`/`↓`) to preview it — a swatch drawn by the same code the canvas fills with, plus the pattern's name, description, and line count. ## Uploading a custom pattern file 1. Click **Add .pat File** in the dialog footer. 2. Choose a `.pat` file — the standard AutoCAD hatch pattern format. A single file commonly defines many named patterns at once; all appear as separate entries grouped under that file's name. 3. Uploaded files are saved permanently in the browser (IndexedDB), sorted most-recently-added first, and reload automatically the next time you open KulmanLab CAD. Uploading a file that defines a pattern with the same name as a built-in one **shadows** the default — the supported way to get Autodesk's authoritative pattern definitions: upload a real `acad.pat` and its version of ANSI31 (and the other standard names) takes over from KulmanLab's own approximations. If a drawing references a pattern name that isn't in your library — for example, imported from a DXF that used a pattern from an `acad.pat` you haven't uploaded — the hatch still renders using `ANSI31` as a stand-in, rather than falling back to a flat, patternless fill. ## Removing a pattern file Click the **×** next to a file name in the **User** group to remove it and every pattern it defined. Any hatch already using one of those patterns falls back to `ANSI31` immediately. Built-in **Standard** patterns can't be removed. ## Keyboard reference | Key | Action | |-----|--------| | `↑` / `↓` | Move the selection up or down the pattern list | | `Escape` | Close the Hatch Manager | ## Related commands - [Hatch](./hatch) — fills a picked area using the pattern currently selected - [Font Manager](./font-manager) — the same upload/browse pattern, for custom fonts instead of hatch patterns --- # Help Opens the in-app help panel with a summary of all keyboard shortcuts, navigation tips, and general usage. ## How to use Click the **Help** toolbar button or type `help` in the terminal. The help panel opens immediately and the command exits. ## What's in the help panel ### Navigation | Shortcut | Action | |----------|--------| | `Scroll wheel` | Zoom in / out | | `Middle click + drag` | Pan the view | | `Double middle click` | Fit all entities to screen | ### Commands | Shortcut | Action | |----------|--------| | Type | Start typing a command name — suggestions appear on the canvas | | `Tab` | Cycle through the suggestion list | | `Delete` | Delete all selected entities | | `Enter` / `Space` | Confirm command or input | | `Enter` / `Space` (idle) | Repeat the last used command | | `Escape` | Cancel the current command or clear selection | Command suggestions match letters anywhere in a command's name, not just the start (typing `mp` suggests **M**atch **P**roperties), ranked first by match quality and then by how often you've actually used each command. The list is clickable with the mouse, scrolls to keep the selected suggestion in view while cycling with Tab, is capped at around 10 rows, and highlights just the matched letters in place within each suggestion rather than overlaying a full ghost-text completion. ### Selection | Shortcut | Action | |----------|--------| | `Left click` | Select an entity | | `Drag →` | Select only entities **fully inside** the drag area | | `Drag ←` | Select any entity that **touches** the drag area | | `Left click grip` | Edit a vertex or endpoint directly | Selecting many entities at once shows a filter icon in the property panel header — see [Selection Filter](./selection-filter) for narrowing a large selection by Type, Layer, Color, Lineweight, or Linetype before bulk-editing. ### Drawing tips - **Snap points** — the cursor snaps to nearby entity endpoints automatically. - **Sticky angle** — move the cursor far enough in one direction to lock to 45° increments. - **Exact length** — while the angle is locked, type a number and press `Enter` to set an exact distance. --- ## Version The current app version is shown in the top-left corner of the help panel (e.g. `KULMAN 2026.04.15`). --- ## Contact Questions, bug reports, or feature requests? 📧 **kulmanlab@gmail.com** We read every message. --- # Import The **Import** command loads an existing drawing from your local file system into KulmanLab CAD. Both the standard AutoCAD **DXF** format and KulmanLab's own **JSON** format are supported. ## How to import a file 1. Click the **Import** toolbar button (folder icon) in the File panel at the top of the screen. 2. Your browser's file picker opens. Navigate to your drawing file and select it. 3. The drawing loads onto the canvas immediately. The viewport fits all entities automatically. Alternatively, you can drag and drop a file directly onto the canvas. ## Supported file formats | Format | Extension | When to use | |--------|-----------|-------------| | **DXF** | `.dxf` | Drawings from AutoCAD, FreeCAD, LibreCAD, or other CAD tools | | **JSON** *(native)* | `.json` | Drawings previously saved from KulmanLab CAD — full fidelity | ## What gets imported from DXF KulmanLab parses the following DXF entity types: | Entity type | DXF code | Notes | |-------------|----------|-------| | Line | `LINE` | | | Circle | `CIRCLE` | | | Arc | `ARC` | | | Ellipse | `ELLIPSE` | | | Polyline | `LWPOLYLINE` | | | Spline | `SPLINE` | | | Text | `TEXT`, `MTEXT` | | | Dimension | `DIMENSION` | | | Multileader | `MULTILEADER` | | | Hatch | `HATCH` | Boundary geometry plus pattern name/scale/angle only — not the pattern's own inline line definitions; an unrecognized pattern name falls back to `ANSI31` | Layer definitions and linetype tables are also imported from the DXF file when present. Entities that use unsupported DXF types (e.g. `INSERT` block references) are silently skipped — the rest of the drawing still loads. ## File naming and storage Imported files get a **plain name — no timestamp is baked in**. If the name is already taken (for example, re-importing the same source file), a Finder/Explorer-style suffix is appended automatically (`plan (2)`, `plan (3)`, …) so nothing is overwritten. The drawing is automatically saved to browser storage (IndexedDB) after import, so it appears in the [File Manager](./file-manager) and survives page reloads. ## What happens to the current drawing Importing replaces the current canvas. There is no merge or append. If you have unsaved changes, [export](./export) the current drawing first. ## On startup KulmanLab automatically reopens the most recently edited file when the page loads. If no saved files exist, a default sample drawing is loaded. ## Troubleshooting | Problem | Likely cause | Fix | |---------|-------------|-----| | Canvas is empty after import | DXF entities use unsupported types (e.g. `INSERT` block references) | The entities were skipped — check for the "no entities found" message in the terminal | | Import button does nothing | Browser blocked the file picker | Click the button once more; some browsers require a fresh user gesture | | Dimensions look wrong | DXF from a tool that writes non-standard dimension geometry | Re-export from the source app using AutoCAD 2010 or later DXF | ## Related commands - [Export](./export) — download the current drawing as DXF or JSON - [File Manager](./file-manager) — browse and restore drawings saved in the browser - [New File](./new-file) — start a blank drawing --- # LayerIsolate The `LayerIsolate` command freezes every layer **except** those belonging to the selected objects. Use it to quickly focus on specific geometry without hiding or deleting anything permanently — unfreeze with [LayerUnfreezeAll](./layer-unfreeze-all) when done. ## Two ways to start **Pre-select, then isolate** — select entities first, then activate: 1. Select one or more entities on the canvas. 2. Type `LayerIsolate` in the terminal or click the **Layer Isolate** toolbar button. 3. The layers of the selected entities remain visible; all others are frozen immediately. **Activate, then select**: 1. Type `LayerIsolate` or click the toolbar button. 2. **Pick objects** — click individual entities or drag to select by area. 3. Press **Enter** or **Space** to confirm — isolation is applied. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Confirm selection and apply isolation | | `Escape` | Cancel and clear selection | ## Behaviour details - All layers that are **not** represented in the selection are set to frozen. - Layers that **are** represented remain unfrozen, even if they were frozen before. - The selection is cleared after isolation is applied. - The command finishes automatically after applying. ## Undoing isolation Run [LayerUnfreezeAll](./layer-unfreeze-all) to restore all layers to visible in one step. --- # LayerManager The `LayerManager` command opens a table listing every layer in the drawing, with its Freeze, Lock, Plot, Color, Lineweight, and Linetype settings editable directly in the row. It is the central place to add layers, delete unused ones, and adjust how existing ones behave — the other layer commands (LayerMakeCurrent, LayerMatch, LayerIsolate, LayerUnfreezeAll) each do one focused thing without opening it. ## Opening the Layer Manager Type `LayerManager` in the terminal, or click the Layer Manager button in the layer panel. The dialog opens as a floating panel; nothing needs to be selected first. ## The layer table | Column | What it controls | |--------|-------------------| | Name | The layer's name, shown read-only in the table (set once, at creation) | | Freeze | Hides the layer's entities and excludes them from selection until unfrozen | | Lock | Prevents entities on the layer from being edited, without hiding them | | Plot | Whether the layer's entities are included when printing or exporting to PDF | | Color | The layer's ACI color — click the swatch to open the color picker | | Lineweight | The layer's line thickness — click the chip to open the lineweight picker | | Linetype | The layer's dash pattern — click the chip to open the linetype picker | | ✕ | Deletes the layer, when nothing is using it | Toggling Freeze, Lock, or Plot takes effect immediately — there is no separate save step. Entities set to ByLayer for color, lineweight, or linetype (the default) pick up whatever you set here; entities with an explicit override of their own are unaffected. ## Adding a layer Click "+ Add Layer" at the bottom of the table, type a name, and press Enter to confirm or Escape to cancel. Layer names may contain letters, numbers, spaces, and `_`, `-`, `$`. A name that is empty, already in use, or contains any other character is rejected with an inline error, and the row stays open for another try. New layers start unfrozen, unlocked, plottable, with color 7 (white/black), lineweight Default, and linetype Continuous. ## Deleting a layer Each row ends with a ✕ button that removes the layer from the drawing. Deletion is immediate — there is no confirmation step — but it is only offered for layers nothing depends on: | Situation | Button state | |-----------|--------------| | Layer is empty | Enabled — "Delete layer" | | Layer is assigned to at least one entity | Disabled — "Cannot delete: assigned to at least one entity" | | Layer `0` | No button at all | "In use" spans the whole drawing, not just what you are looking at. An entity sitting on a layout (paper space) counts exactly as much as one in model space, so a layer can look empty on screen and still refuse to delete. Frozen layers are no different: freezing hides entities but does not unassign them, so a frozen layer holding entities stays undeletable. Layer `0` can never be deleted. It is the fallback every drawing is guaranteed to have, so the button is not rendered for it at all rather than being shown disabled. Occasionally the ✕ looks available but the click is refused with a banner reading `"WALLS" is now in use and can't be deleted`. Working out which layers are in use means walking every entity in the drawing, so the result is cached and only rebuilt when the entity count changes — cheap at hundreds of entities, not at hundreds of thousands. Moving an existing entity onto a layer does not change the count, so the row's disabled state can be a moment out of date. Clicking re-checks from scratch before deleting anything, which is why the refusal happens at click time rather than the layer disappearing while something still references it. ## What you can't do here There is no indicator in the table for which layer is current; that is set from the layer panel's dropdown or by LayerMakeCurrent. Layer names are fixed at creation — a layer can be deleted and recreated, but not renamed. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` | Confirm a new layer's name (while adding) | | `Escape` | Cancel adding a layer, or close the dialog | --- # LayerMakeCurrent The `LayerMakeCurrent` command sets the **current drawing layer** to whichever layer the clicked entity belongs to. New entities will then be drawn on that layer automatically. ## Usage 1. Type `LayerMakeCurrent` in the terminal or click the **Make Current** toolbar button (eyedropper icon). 2. **Click any entity** on the canvas. 3. The current layer is updated to match that entity's layer. The command finishes immediately. ## Behaviour details - If you click on empty canvas (no entity hit), the terminal shows `no object found` and the command stays active so you can try again. - Only the current layer setting is changed — no entities are modified. - The updated layer is reflected in the layer selector in the toolbar. --- # LayerMatch The `LayerMatch` command reassigns the layer of selected entities to match the layer of a source entity you click. It is the fastest way to move a group of objects onto the correct layer without opening the Layer Manager. ## Workflow **Pre-select, then match**: 1. Select the entities whose layer you want to change. 2. Type `LayerMatch` or click the **Layer Match** toolbar button (paint icon). 3. **Click the source object** — the one whose layer you want to copy. 4. All selected entities move to the source object's layer immediately. **Activate, then select**: 1. Type `LayerMatch` or click the toolbar button with nothing selected. 2. **Pick target objects** — click to toggle individual entities or drag to select by area. 3. Press **Enter** or **Space** to confirm the selection. 4. **Click the source object** — its layer is applied to all targets. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Confirm target selection and advance to source-pick phase | | `Escape` | Reset — return to target selection or cancel entirely | ## Behaviour details - Only the `layer` property is changed — color, linetype, lineweight, and geometry are untouched. - The source object itself is not modified. - The command finishes after the source is clicked. - Clicking on empty canvas during the source-pick phase does nothing. --- # LayerUnfreezeAll The `LayerUnfreezeAll` command clears the frozen flag on **every layer** in the drawing instantly. No selection or confirmation needed — it runs and finishes in one step. ## Usage Type `LayerUnfreezeAll` in the terminal or click the **Unfreeze All** toolbar button (sun icon). All frozen layers become visible immediately. ## When to use Typically used after [LayerIsolate](./layer-isolate) to restore all layers to their normal visible state. ## Behaviour details - Applies to all layers regardless of their current state. - Does not affect locked or plot flags — only the frozen flag is changed. - The command finishes immediately with no prompts. --- # LeaderAdd The `LeaderAdd` command adds a new arrowhead arm to an existing multileader. The new arm points from the leader's existing dogleg to a new arrowhead tip you click. All styling — dogleg position, text, arrowhead type, and size — is inherited from the selected leader. ## Adding an arm 1. Type `LeaderAdd` in the terminal. 2. **Click an existing multileader** to select it. 3. **Click the new arrowhead tip** — a preview line shows from the cursor to the leader's dogleg. Click to place. The command finishes automatically after placing the arm. ``` Before: After: ◄── arm 1 ◄── arm 1 \ \ ●──── dogleg ──── text ●──── dogleg ──── text / arm 2 ──► (new tip you clicked) ``` ## Keyboard reference | Key | Action | |-----|--------| | `Escape` | Cancel and reset | ## Notes - Only **Multileader** entities can be selected — clicking any other entity type does nothing. - The new arm originates at the existing dogleg; you only choose where the arrowhead tip goes. - There is no limit on the number of arms a multileader can have. ## Related commands | Command | What it does | |---------|-------------| | [Leader](./leader) | Create a brand-new multileader from scratch | | [LeaderRemove](./leader-remove) | Remove an arm from a multileader that has two or more | --- # LeaderRemove The `LeaderRemove` command removes one arrowhead arm from an existing multileader. The text label, dogleg, and all remaining arms are preserved — only the selected arm is deleted. A multileader with only one arm cannot have its arm removed. ## Removing an arm 1. Type `LeaderRemove` in the terminal. 2. **Click a multileader** that has two or more arms. If the clicked leader has only one arm, the terminal shows an error and waits for a valid selection. 3. **Move the cursor near the arm** you want to remove — the closest arm is highlighted with a marker. 4. **Click** to remove that arm. ``` Before: After: ◄── arm 1 ◄── arm 1 \ \ ●──── dogleg ──── text ●──── dogleg ──── text / arm 2 ──► ← this arm removed ``` ## How the closest arm is determined The command measures the perpendicular distance from the cursor to each arm's line segments (including the segment from the last arm point to the dogleg). The arm with the smallest distance is highlighted and will be removed on click. ## Keyboard reference | Key | Action | |-----|--------| | `Escape` | Cancel and reset | ## Notes - A leader with **only one arm** is protected — you must add an arm first before removing one. - The dogleg position and text content are always preserved regardless of which arm is removed. ## Related commands | Command | What it does | |---------|-------------| | [Leader](./leader) | Create a new multileader from scratch | | [LeaderAdd](./leader-add) | Add an arm to an existing multileader | --- # Leader The `leader` command draws a multileader annotation in four steps: an arrowhead that touches a feature, a leader line that bends at a dogleg, a text anchor, and a typed label. Of all annotation commands, Leader is the only one that includes an interactive text-input phase with a blinking cursor preview. ## Anatomy of a multileader ``` ◄── arrowhead tip (step 2 — touches the feature) \ \ leader line \ ●──── dogleg (step 3) ──── text anchor (step 4) Label text (step 5) ``` - **Arrowhead tip** — the pointed end placed at the feature being annotated. - **Dogleg** — the elbow where the leader line bends toward the text. - **Text anchor** — where the label is positioned. Text aligns left or right automatically. ## Drawing a leader 1. Type `leader` in the terminal or click the **Leader** toolbar button. 2. **Click the arrowhead tip** — the exact point the arrow touches. 3. **Click the dogleg** — the bend in the leader. Angle locks to 45° increments; type a length and press **Enter** for precise placement. 4. **Click the text position** — where the label anchors. Angle locking applies here too. 5. **Type the label text** — the canvas preview updates live with a blinking cursor. Press **Enter** to place. ## Angle locking (steps 3 and 4) After each placed point, the command snaps to 45° axes when the cursor is far enough away. While locked: - The preview snaps to the axis. - Type a length and press **Enter** to place the next point at that exact distance. ## Automatic text direction The text alignment adjusts to the cursor position relative to the dogleg: | Cursor position | Text direction | |-----------------|---------------| | To the **right** of the dogleg | Left-to-right from the text anchor | | To the **left** of the dogleg | Right-to-left (anchored on the right side) | No manual adjustment needed — move the cursor to the side where you want the label and it aligns correctly. ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.`, `-` | Build up segment length (angle lock active; `-` first character only) | | `Backspace` | Delete last typed character | | `Enter` | Confirm typed length (steps 3–4) or place the leader (step 5) | | `Escape` | Cancel and reset to step 2 | ## Adding and removing arms - To add an extra arrowhead arm to an existing leader: [LeaderAdd](./leader-add) - To remove an arm from a leader that has two or more: [LeaderRemove](./leader-remove) ## DXF — import only **Leaders are import-only.** `MLEADER` entities from DXF files are read and displayed correctly, but leaders drawn in the editor are **not written** when saving a DXF file. Use leaders for visual annotation; avoid relying on them for round-trip workflows. --- # Line The `line` command draws individual straight-line segments stored as separate `LINE` entities in the DXF model. After each segment the command stays active and re-uses the endpoint as a new start point, so you can build connected paths one segment at a time. Unlike a [Polyline](./polyline), chained lines remain independent entities — each one can be trimmed, extended, or deleted without affecting its neighbours. ## Drawing lines 1. Type `line` in the terminal or click the **Line** toolbar button. 2. **Click the start point**. 3. **Click the end point** — the segment is placed instantly and the endpoint becomes the next start point. 4. Keep clicking to chain more segments. 5. Press **Enter** or **Escape** to stop. ``` ●──────────●──────────●──────────● start 2nd click 3rd click Enter to finish (auto-becomes next start) ``` Only need a single segment? Press **Enter** or **Escape** right after step 3. ## Angle locking and exact length input As you move the cursor after placing a point, the command watches for a 45° snap axis (0°, 45°, 90°, 135°, …). The angle **locks** when: - the cursor is at least **5 × grip size** from the anchor, **and** - it is within **1 grip size** of perpendicular distance from the nearest axis. When locked the preview snaps to the axis and you can enter an exact length: | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the length value | | `-` | Negative length — reverses direction along the axis (first character only) | | `Backspace` | Delete the last typed character | | `Enter` | Place the endpoint at the typed distance | The accumulated value is shown live in the terminal (e.g. `click end point or enter length: 12.5`). Click while locked and the click is projected onto the axis, so the endpoint always lies exactly on it. Moving back close to the anchor point disengages the lock. ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.`, `-` | Build up length (angle lock must be active) | | `Backspace` | Delete last typed character | | `Enter` | Confirm typed length, or finish the chain if no length is typed | | `Escape` | Finish the chain and exit | ## Grip editing — stretching endpoints A selected line shows three grips: | Grip | Where | What it does | |------|-------|--------------| | **Start** | First endpoint | Drag to reposition — the end stays fixed | | **Midpoint** | Centre of the line | Activates **Move** for the whole line | | **End** | Second endpoint | Drag to reposition — the start stays fixed | Stretching one endpoint never affects the other. This differs from [Polyline](./polyline) grip editing, where moving a vertex reshapes the entire path. ## Selecting lines | Method | Behaviour | |--------|-----------| | **Click** | Selects the line if the click is within hit-test distance of the segment | | **Drag right** (strict) | Line is selected only if both endpoints fall inside the box | | **Drag left** (crossing) | Line is selected if any part of the segment crosses the box boundary | ## Supported edit commands Lines are the **only** entity that [Trim](./trim) and [Extend](./extend) operate on. All standard transformation commands also apply: | Command | What happens to a line | |---------|------------------------| | [Move](./move) | Translates both endpoints by the same displacement | | [Copy](./copy) | Creates an identical line at a new position | | [Rotate](./rotate) | Rotates both endpoints around the chosen base point | | [Mirror](./mirror) | Reflects both endpoints across the mirror axis | | [Scale](./scale) | Scales both endpoints uniformly from the base point | | [Offset](./offset) | Creates a parallel line at a fixed perpendicular distance | | [Trim](./trim) | Cuts the line at intersections — **lines only** | | [Extend](./extend) | Stretches the nearest endpoint to reach a boundary — **lines only** | | [Delete](./delete) | Removes the line from the drawing | ## Properties When a line is selected the properties panel shows every field that the DXF `LINE` record carries: **General** | Property | Default | Meaning | |----------|---------|---------| | Color | 256 (ByLayer) | ACI color index | | Layer | `0` | Layer assignment | | Linetype | ByLayer | Named linetype pattern | | Linetype Scale | 1 | Scale factor on the linetype pattern | | Thickness | 0 | Extrusion thickness | **Geometry** | Property | Meaning | |----------|---------| | Start X / Start Y | First endpoint coordinates | | End X / End Y | Second endpoint coordinates | All fields are editable directly in the panel without re-running the command. ## Line vs Polyline — when to use which | | Line | Polyline | |---|------|---------| | Entity count | One `LINE` per segment | One `LWPOLYLINE` for the whole path | | Trim / Extend | Yes — segment by segment | No | | Closed shape | No | Yes (close flag) | | Grip editing | Stretch individual endpoints | Move any vertex along the path | | Best for | Construction lines, single segments, geometry you'll trim | Contours, outlines, shapes you keep whole | ## DXF — LINE entity Lines are saved as `LINE` entities in the DXF file. Every property — start/end coordinates, color, layer, linetype, linetype scale, and thickness — round-trips without loss. When you open a DXF that contains `LINE` entities they become fully editable `Line` objects in the editor. Lines drawn in the editor are also written as `LINE` entities on save, so they are readable by AutoCAD, LibreCAD, and any other DXF-compatible application. --- # Match Properties The `MatchProperties` command copies **visual and layer properties** from a source entity to one or more target entities. Only properties that are shared between the source and target entity types are transferred — geometry is never changed. ## How to activate Click the **Match Properties** toolbar button (paint roller icon) in the Style panel, or type `MatchProperties` in the terminal. ## Workflow **Activate first, then pick source:** 1. Type `MatchProperties` or click the toolbar button with nothing pre-selected. 2. **Click the source entity** — the one whose properties you want to copy. 3. **Click each target entity** to apply the source properties. You can click multiple entities one by one. 4. To apply to a group at once, **drag a selection box** over the targets. 5. Press **Enter** or **Escape** to finish. **Pre-select the source, then activate:** 1. Click a single entity to select it. 2. Activate `MatchProperties`. The selected entity is used as the source automatically. 3. Click target entities or drag-select, then **Enter** or **Escape** to finish. ## What properties are copied MatchProperties copies properties that belong to a shared base class between the source and target. At minimum, **all entity types** share these properties: | Property | Description | |----------|-------------| | **Color** | The entity's color index (includes "By Layer" / "By Block") | | **Layer** | The layer the entity belongs to | When the source and target are the same entity type (e.g. both are dimensions), additional type-specific properties are also copied — for example text height, arrow size, extension line settings. Geometry (coordinates, radius, length, etc.) is never affected. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Confirm area selection or finish the command | | `Escape` | Finish applying (if source is set) or cancel | ## Behaviour details - The source entity itself is never modified. - Each click or drag-select applies the source properties immediately — there is no confirmation step. - Area selection follows the standard rules: drag **right** for strict selection (fully enclosed), drag **left** for crossing selection (any intersection). - Clicking the source entity as a target is ignored. ## Related commands - [LayerMatch](./layer-match) — move selected entities onto the same layer as a source (layer property only) - [LayerMakeCurrent](./layer-make-current) — set the current drawing layer from a clicked entity --- # Mirror The `mirror` command creates mirrored copies of selected entities reflected across a two-point axis. The originals are **always kept** — unlike [Move](./move) or [Rotate](./rotate), Mirror never modifies existing entities; it only adds new ones. ## Two ways to start **Pre-select, then mirror** — select entities first, then activate: 1. Select one or more entities on the canvas. 2. Type `mirror` in the terminal or click the **Mirror** toolbar button. 3. **Click the first point** of the mirror axis. 4. **Click the second point** of the mirror axis — mirrored copies are placed and the command exits. **Activate, then select** — start the command with nothing selected: 1. Type `mirror` or click the toolbar button. 2. **Select objects** — click to toggle, or drag to select by area. 3. Press **Enter** or **Space** to confirm the selection. 4. **Click the first point**, then **click the second point** of the mirror axis. ``` Original: Mirror axis: Result: | [entity A] → | → [entity A] + [mirrored A] | ``` A live preview of the mirrored copies follows the cursor while you position the second axis point. ## Mirror axis The axis is an infinite line through the two clicked points. It can be at any angle: - Move the cursor close to a **45° snap axis** (0°, 45°, 90°, 135°, …) and the axis locks to that angle — useful for clean horizontal, vertical, or diagonal reflections. - Click away from the snap zone for a free-angle axis. There is no typed input for the mirror axis — both points are click-only. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Confirm selection | | `Escape` | Cancel and reset | ## Selection during the command | Method | Behaviour | |--------|-----------| | **Click** | Toggles the entity under the cursor | | **Drag right** (strict) | Adds entities fully inside the box | | **Drag left** (crossing) | Adds entities that intersect the box | | **Enter** / **Space** | Confirms the selection | ## What gets mirrored Every entity type is supported. Geometry is reflected across the axis mathematically: | Entity | What changes | |--------|-------------| | Line | Both endpoints reflected | | Circle | Center reflected; radius unchanged | | Arc | Center reflected; start and end angles recalculated across the axis | | Ellipse | Center reflected; major axis direction flipped across the axis | | Polyline / Rectangle | Every vertex reflected | | Text | Anchor point reflected; text string is **not** reversed | | Spline | All control vertices / fit points reflected | ## Mirror vs Copy | | Mirror | Copy | |---|--------|------| | Originals | Always kept | Always kept | | New entity position | Reflected across an axis | Offset by a displacement vector | | Best for | Symmetric designs, bilateral features | Repeating geometry in any direction | --- # Move The `move` command translates selected entities from a base point to a destination point. The displacement applied to every selected entity is the vector from base to destination. After the move all entities remain selected at their new position, ready for further edits. ## Two ways to start **Pre-select, then move** — select entities first, then activate: 1. Select one or more entities on the canvas. 2. Type `move` in the terminal or click the **Move** toolbar button. 3. **Click the base point** — the reference for the displacement. 4. **Click the destination** — all selected entities shift by the base→destination vector. **Activate, then select** — start the command with nothing selected: 1. Type `move` or click the toolbar button. 2. **Select objects** — click to toggle individual entities, or drag to select by area. 3. Press **Enter** or **Space** to confirm the selection. 4. **Click the base point**, then **click the destination**. ``` Before: After: ● base → ● destination [entity A] [entity A moved] [entity B] [entity B moved] ``` A ghost preview of all selected entities follows the cursor from the base point to the destination, showing the result before you click. ## Angle locking and exact distance After the base point is set the command watches for a 45° snap axis (0°, 45°, 90°, 135°, …). The direction **locks** when the cursor is far enough from the base and within one grip width of the axis. While locked: - The ghost preview snaps to the axis. - Type a distance and press **Enter** to move exactly that far along the locked direction. - Clicking projects onto the axis, so the destination always lies exactly on it. | Key | Action | |-----|--------| | `0`–`9`, `.` | Append to the distance value | | `-` | Negative distance — reverses direction along the axis (first character only) | | `Backspace` | Delete the last typed character | | `Enter` | Apply the move at the typed distance | ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Confirm selection and advance to base-point phase | | `0`–`9`, `.`, `-` | Build up distance (angle lock must be active) | | `Backspace` | Delete last typed character | | `Enter` | Apply move at typed distance | | `Escape` | Cancel and reset | ## Activating Move from a grip Clicking the **midpoint grip** of a selected [Line](./line) launches Move automatically, with the midpoint already set as the base point and the move phase active. This is the fastest way to reposition a single line without going through the selection step. ## Selection during the command When the command starts in selection phase: | Method | Behaviour | |--------|-----------| | **Click** | Toggles the entity under the cursor in/out of the selection | | **Drag right** (strict) | Adds entities fully inside the box | | **Drag left** (crossing) | Adds entities that intersect the box boundary | | **Enter** / **Space** | Confirms the selection and moves to the base-point phase | ## After the move The moved entities remain selected at their new position. This means you can immediately: - Run **Move** again to nudge them further. - Run [Copy](./copy), [Rotate](./rotate), or [Scale](./scale) without re-selecting. - Press **Delete** to remove them. ## Move vs Copy | | Move | Copy | |---|------|------| | Original position | Vacated — entities are no longer there | Kept — originals stay in place | | Result count | Same number of entities | One additional set per operation | | Selection after | Moved entities selected at new position | Copied entities selected at new position | | Best for | Repositioning geometry | Duplicating geometry | ## Supported entities Move works on every entity type: Line, Polyline, Rectangle, Circle, Arc, Ellipse, Text, Spline, Dimension, Leader, and all others. All entities implement `translate(dx, dy)` so none are excluded. --- # New File The **New File** command clears the canvas and starts a fresh blank drawing. A plain, unique file name is generated automatically. ## How to create a new file Click the **New File** toolbar button (new-page icon) in the File panel. The canvas clears immediately — no prompts or confirmation dialogs. ## What the new file contains A freshly created file starts with: - **No entities** on the canvas. - **One default layer** named `0` with color white and linetype `Continuous`. - A **generated file name** of `kulman` — no timestamp is baked in; if that name is already taken, a Finder/Explorer-style suffix is appended (`kulman (2)`, `kulman (3)`, …). The file is saved to browser storage automatically and appears in [File Manager](./file-manager). ## Warning — unsaved work is discarded Clicking **New File** discards all entities on the current canvas without warning. If you want to keep the current drawing, [export](./export) it first. ## When to use New File vs Import | Situation | Recommended action | |-----------|-------------------| | Starting a drawing from scratch | **New File** | | Opening an existing DXF or JSON file | [Import](./import) | | Copying a drawing to work on a variant | [Export](./export) the current file, then [Import](./import) the copy | ## Related commands - [Import](./import) — open an existing DXF or JSON drawing - [Export](./export) — download the drawing before starting fresh - [File Manager](./file-manager) — restore a previous drawing from browser storage --- # Offset The `offset` command creates a parallel copy of an entity at a fixed perpendicular distance. You type the distance once, then click entities and pick a side — the command stays ready at the same distance so you can offset multiple objects in one session. Supported entity types: **Line, Circle, Arc, Ellipse, Polyline** (including Rectangles). ## Using offset 1. Type `offset` in the terminal or click the **Offset** toolbar button. 2. **Type the offset distance** and press **Enter** or **Space**. 3. **Click an entity** to offset — if the entity is not a supported type, an error message appears and you can click a different entity. 4. **Move the cursor** to the side where the copy should appear — a live preview follows. 5. **Click** to place the offset copy. After each placement the command returns to step 3 at the **same distance**, ready for the next offset. Press **Enter** or **Space** while waiting to pick the next entity to finish the command, or **Escape** to reset back to the distance-entry step. ``` Distance: 10 ───────────────── ← original line ───────────────── ← offset copy (10 units below) ``` ## Per-entity offset behaviour | Entity | How the offset is computed | |--------|---------------------------| | **Line** | Parallel line shifted perpendicular to the original direction | | **Circle** | Concentric circle; click outside → larger radius, inside → smaller radius | | **Arc** | Concentric arc at a new radius; same angular span preserved | | **Ellipse** | Both semi-axes increased or decreased by the same distance | | **Polyline** | Each segment offset independently; adjacent offset segments are mitered at corners | For **Circle**, **Arc**, and **Ellipse**: if the inward offset would reduce any radius or semi-axis to zero or below, the offset is not applied. ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the distance value | | `Backspace` | Delete last typed character | | `Enter` / `Space` (while typing a distance) | Confirm the typed distance and advance to entity selection | | `Enter` / `Space` (while idle, waiting to pick the next entity) | Finish the Offset command | | `Escape` | Reset to distance-entry step | ## Workflow note The distance stays set until you press **Escape**. This makes it efficient to offset many entities at the same spacing — type the distance once, then click and pick side for each entity in turn. ## Offset vs Copy | | Offset | Copy | |---|--------|------| | Displacement | Perpendicular to the entity's geometry | Arbitrary vector (base → destination) | | Supported entities | Line, Circle, Arc, Ellipse, Polyline | All entity types | | Distance input | Typed before selecting the entity | Typed or clicked after selecting | | Best for | Parallel lines, concentric circles, inset/outset paths | Placing duplicates at arbitrary positions | --- # PageManager The `PageManager` command opens the page settings dialog for the active layout, letting you change the paper format, orientation, and the scale between drawing units and millimetres. ## Opening the Page Manager - Type `PageManager` in the terminal, **or** - Right-click a layout tab at the bottom and choose **Page Manager**. ## Settings | Setting | Description | |---------|-------------| | **Paper format** | Standard sheet size (A4, A3, A2, Letter, etc.) | | **Orientation** | Portrait or Landscape | | **Scale** | Drawing units per mm — controls how model coordinates map to paper | Changing the paper size or scale rescales all existing viewports in the layout proportionally so their positions remain correct. ## Notes - PageManager only applies to paper layouts. It is not available in model space. - To add a new layout, click the **+** button in the tab bar at the bottom. - To rename or delete a layout, right-click its tab. --- # Pan The `pan` command enters a persistent drag-to-scroll mode — click and drag anywhere on the canvas to shift the view. Zoom level is unchanged. Pan mode stays active until you press `Escape`, so you can drag multiple times in one activation. ## Panning the view 1. Type `pan` in the terminal or click the **Pan** toolbar button. 2. **Click and drag** anywhere on the canvas to shift the view. 3. Release and drag again as many times as needed. 4. Press `Escape` to exit pan mode. ## Middle-click drag — pan without activating the command The fastest way to pan: **hold the middle mouse button and drag** at any time, even while another command is active. No command activation needed. Releasing the middle button returns to whatever state you were in. ## Pan vs zoom controls | Action | Effect | Requires Pan mode? | |--------|--------|-------------------| | Left-click drag (Pan mode) | Shifts viewport | Yes | | Middle-click drag | Shifts viewport | No — works always | | Scroll wheel | Zooms toward cursor | No — works always | | Double-click middle button | Fit all entities | No — works always | | [Zoom In](./zoom-in) / [Zoom Out](./zoom-out) | Steps zoom 1.5× | No | | [Fit](./fit) | Fits all entities in view | No | ## Keyboard reference | Key | Action | |-----|--------| | `Escape` | Exit pan mode | ## Tips - Use the scroll wheel to zoom toward a target area, then pan to fine-tune position. - **Double-click the middle mouse button** triggers [Fit](./fit) instantly — the fastest way to reset a lost view. - If you are mid-command (e.g., placing a line endpoint), middle-click drag to pan without cancelling the command. --- # Polyline The `polyline` command draws a connected path of any number of straight or arc segments, all stored as a single `LWPOLYLINE` entity. Because the entire path is one object, selecting it selects every segment at once — move, rotate, or scale the whole shape in a single operation. This is the key distinction from chained [Lines](./line), where each segment is an independent entity. Polylines can also be **closed**: the [Rectangle](./rectangle) command uses the same `LWPOLYLINE` entity with a close flag set. ## Drawing a polyline 1. Type `polyline` in the terminal or click the **Polyline** toolbar button. 2. **Click the first point**. 3. **Click each subsequent point** — each click adds a segment from the last placed point. 4. Press **Enter** or **Space** to finish (requires at least 2 points placed). ``` ●──────● 1st 2nd \ \ segment 3 (in progress — cursor here) ● ← click to add, Enter/Space to finish ``` Pressing **Escape** at any time discards all placed points and exits the command. ## Drawing an arc segment Press **A** at any point after the first vertex to toggle Arc mode — the same inline-option pattern AutoCAD's PLINE command uses, mirroring [Rotate](./rotate)'s `Copy` option. The prompt shows the current state as `[Arc=true]` / `[Arc=false]`; pressing **A** again flips it back, so straight and arc segments can be freely mixed in one polyline. While Arc mode is on, each new segment is a **tangent-continuation arc** — AutoCAD's default arc behavior with no center/radius/direction sub-options. It starts tangent to whatever came right before it (the previous line's direction, or the previous arc's end tangent); the very first segment of a polyline defaults to heading due east, having nothing to be tangent to. ## Angle locking and exact segment length The same 45° snap logic as the [Line](./line#angle-locking-and-exact-length-input) command applies between any two consecutive points. When locked to an axis: | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the segment length | | `-` | Negative length — reverses direction along the axis (first character only) | | `Backspace` | Delete the last typed character | | `Enter` | Place the next point at the typed distance | The current accumulated length appears in the terminal prompt in real time. Clicking while locked projects onto the axis so the new vertex lands exactly on it. ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.`, `-` | Build up segment length (angle lock must be active) | | `A` | Toggle Arc mode for the next segment (after the first vertex, no input in progress) | | `Backspace` | Delete last typed character | | `Enter` | Confirm typed length, or finish the polyline if no length is typed and ≥ 2 points exist | | `Space` | Finish the polyline (same as Enter when no length is typed) | | `Escape` | Discard all points and exit | ## Grip editing — vertices and segment midpoints A selected polyline shows two types of grips: | Grip | Position | What it does | |------|----------|--------------| | **Vertex** | At each placed point | Drag to reposition that vertex; all connected segments stretch to follow | | **Segment midpoint** | Centre of each segment | Drag to translate **both** endpoints of that segment together, keeping the segment length and angle intact | The segment-midpoint grip is unique to polylines — it lets you slide an individual segment sideways without changing its length. On a [Line](./line), the midpoint grip instead activates the Move command for the whole entity. An **arc segment** grips the same way as a straight one — dragging either endpoint or the segment-midpoint grip reshapes it, holding the bulge (included angle) constant and refitting through the new position, the same behavior an [Arc](./arc) entity's own grips use. There is no single "move the whole polyline" grip. To move the entire path use the [Move](./move) command. ## Selecting polylines | Method | Behaviour | |--------|-----------| | **Click** | Selects the polyline if the click lands within hit-test distance of any segment | | **Drag right** (strict) | All vertices must fall inside the box | | **Drag left** (crossing) | Any segment that crosses the box boundary selects the whole polyline | Because a polyline is one entity, a crossing selection that touches any segment selects all segments. ## Supported edit commands Polylines support every general transformation, plus offset, trim, extend, chamfer, and fillet — arc segments are fully supported by all of these except Chamfer, which only ever picks a **straight** segment: | Command | What happens to the polyline | |---------|------------------------------| | [Move](./move) | Translates all vertices by the same displacement | | [Copy](./copy) | Creates an identical polyline at a new position | | [Rotate](./rotate) | Rotates all vertices around the chosen base point | | [Mirror](./mirror) | Reflects all vertices across the mirror axis | | [Scale](./scale) | Scales all vertices uniformly from the base point | | [Offset](./offset) | Creates a parallel polyline at a fixed perpendicular distance — arc segments offset to a new radius | | [Trim](./trim) | Removes the portion between two intersection points, straight or arc segments alike | | [Extend](./extend) | Stretches the first or last segment to the next boundary — an arc terminal segment grows along its own circle | | [Chamfer](./chamfer) | Bevels a corner between two adjacent **straight** segments; an arc segment at that corner is skipped when picking | | [Fillet](./fillet) | Rounds the corner between two adjacent segments (straight or arc) of the same polyline as a tangent-arc bulge; filleting across another open polyline or an independent Line/Arc merges both into a new polyline | | [Explode](./explode) | Breaks the polyline into independent Line and Arc entities, one per segment | | [Delete](./delete) | Removes the polyline from the drawing | ## Properties When a polyline is selected the properties panel shows: **General** | Property | Default | Meaning | |----------|---------|---------| | Color | 256 (ByLayer) | ACI color index | | Layer | `0` | Layer assignment | | Linetype | ByLayer | Named linetype pattern | | Linetype Scale | 1 | Scale factor on the linetype pattern | | Thickness | 0 | Extrusion thickness | **Geometry** | Property | Meaning | |----------|---------| | Closed | Whether the last vertex connects back to the first | | Vertex Count | Total number of vertices | | Vertices | Coordinate list of all vertices | ## Polyline vs Line — when to use which | | Polyline | Line | |---|---------|------| | Entity count | One `LWPOLYLINE` for the whole path | One `LINE` per segment | | Closed shape | Yes (close flag) | No | | Arc segments | Yes, per-segment via the `Arc` toggle | No — needs a separate [Arc](./arc) entity | | Trim / Extend | Yes | Yes — segment by segment | | Segment-midpoint grip | Translates the whole segment | Activates Move for the entity | | Best for | Outlines, contours, shapes you keep whole | Construction lines, geometry you'll trim | ## DXF — LWPOLYLINE entity Polylines are saved as `LWPOLYLINE` entities in the DXF file. All properties — vertex coordinates, closed flag, color, layer, linetype, linetype scale, and thickness — round-trip without loss. Rectangles drawn with the [Rectangle](./rectangle) command also save as `LWPOLYLINE` (closed, four vertices) and are indistinguishable at the DXF level. Each vertex also carries a **bulge** (DXF group code 42) — 0 for a straight segment, or the signed tangent-of-quarter-angle value AutoCAD uses for a curved one. Bulges round-trip losslessly, so arc segments imported from another CAD application render, select, grip-edit, trim, extend, and hatch exactly like ones drawn here with the Arc option. `LWPOLYLINE` entities from any DXF-compatible application (AutoCAD, LibreCAD, etc.) are read back as fully editable polylines in the editor. --- # Print Manager The `PrintManager` command opens the **Print Manager** — a dedicated export window with a live preview canvas, format selector (PNG / JPEG / WebP / PDF), monochrome toggle, and optional area crop. Nothing is sent to a physical printer; the output is downloaded as a file. ## Opening the Print Manager Click the **Print** toolbar button or type `PrintManager` in the terminal. The Print Manager opens immediately showing a preview of the current viewport. The preview renders through the exact same code path, at the exact same pixel resolution, as the file you eventually export — changing Quality, Monochrome, or the export area all re-render the preview immediately, so what you see is what downloads, not an approximation of it. ## Print Manager layout The window has two panels: - **Left sidebar** — all export controls. - **Right panel** — live preview canvas that updates as you change settings. ### Sidebar controls | Control | Description | |---------|-------------| | **Change Area** | Crop to a custom rectangle on the canvas (see below) — actually crops the exported image, including on a layout with paper space, not just the on-screen preview | | **Quality** dropdown | Export DPI: Draft 72, Normal 150 (default), Presentation 300, Max 600. Line weights scale with it, so a line keeps the same physical thickness on paper at any Quality; a hairline (lineweight 0) stays a fixed 1px regardless | | **Monochrome** toggle | Forces every entity's color to solid black ink at render time — on by default for clean print output. Rendered as a single pass over the finished image rather than a per-entity effect, so it stays fast even on large plots — a 90,000-entity drawing renders in around 70ms | | **Format** dropdown | PNG, JPEG, WebP, or PDF | | **Export** button | Generate and download the file | ## Selecting a custom export area By default the preview shows the bounding box of every entity in Model space — the same extent Fit zooms to — or the full sheet on a layout. To export a specific region: 1. Click **Change Area** — the Print Manager hides and the canvas becomes interactive. 2. **Click the first corner** of the export rectangle. 3. **Click the opposite corner** — the Print Manager reopens with the selected area in the preview. Press `Escape` during area selection to cancel and restore the previous area. The preview canvas resizes dynamically to match the **exact aspect ratio** of the selected area, so the preview is pixel-accurate. ## Export formats | Format | Best for | Notes | |--------|----------|-------| | **PNG** | Lossless, sharp lines | White background, no transparency | | **JPEG** | Smaller file for sharing | 95% quality, slight compression | | **WebP** | Smallest file for web | Same 95% quality, better compression than JPEG | | **PDF** | Print-ready documents | Image embedded inside a PDF container at the selected Quality's DPI, sized so the page prints at true physical scale | The exported file is named `kulman-.` and downloads automatically. ## Export resolution and background - **Model space / viewport export**: capped at 2000 × 2000 px at the default Normal (150 DPI) Quality, scaled proportionally to the selected area; the cap itself scales with Quality (down at Draft, up to 8000 × 8000 at Max/600 DPI). - **Layout (paper space) export**: sized directly from the layout's paper dimensions at the selected DPI (e.g. A4 at Normal ≈ 1240 × 1754 px) — not subject to the 2000 px viewport cap. - Background is always **white**. - Layers marked as **non-plotting** are excluded from the export. ## Keyboard reference | Key | Action | |-----|--------| | `Escape` (during area selection) | Cancel area selection, restore previous area | | `Escape` (in Print Manager) | Close the Print Manager | --- --- # ChangePrintArea The `ChangePrintArea` command sets the rectangular region that Print Manager exports. It runs on the bare canvas with the Print Manager hidden, and takes two opposite corners — the same two picks as Rectangle, so typed coordinates and snapping behave exactly as they do there. ## Selecting an area 1. Type `ChangePrintArea` in the terminal, or click **Change Area** in the Print Manager sidebar. The Print Manager hides and the canvas becomes interactive. 2. **Click the first corner**, or type `X,Y` and press **Enter** for an exact coordinate. 3. **Click the opposite corner**, or type `X,Y` again. Print Manager reopens with the new area in the preview, which resizes to that area's exact aspect ratio. Corners snap to grips and intersections like any other point pick, so you can crop to drawn geometry instead of by eye. The two corners can be given in any order — opposite corners define the same rectangle whichever you pick first. Press `Escape` to cancel. Nothing is written, so Print Manager reopens with whatever area it already had. ## Where the area is remembered The selection is stored per context rather than globally: | Context | Slot | |---------|------| | Model space | One shared slot | | Each layout | Its own slot, kept separately | Reopening Print Manager on the same layout — or on Model — restores that context's own last crop instead of resetting, and switching between layouts leaves each one's area intact. This is held in memory only. Reloading the page clears every stored area, and Print Manager falls back to the defaults below. ## Default area With nothing stored for the current context, Print Manager opens on: | Context | Default | |---------|---------| | Model space | The bounding box of all entities — the same extent Fit zooms to | | A layout | The full sheet | ## Related commands | Command | What it does | |---------|-------------| | Print Manager | The export window this area applies to | | Rectangle | The same two-corner pick, but draws a polyline | | Fit | Zooms to the extent Model space defaults to | # Rectangle The `rectangle` command draws an axis-aligned rectangle defined by two opposite corner clicks. The result is stored as a **closed `LWPOLYLINE`** with four vertices — one at each corner. There is no dedicated rectangle entity type: after creation the shape behaves exactly like any other [Polyline](./polyline) and every polyline edit applies to it. ## Drawing a rectangle 1. Type `rectangle` in the terminal or click the **Rectangle** toolbar button. 2. **Click the first corner**. 3. **Click the opposite corner** — the rectangle is placed instantly and the command exits. See [Dimension entry](#dimension-entry) below to type an exact width/height instead. ``` ● (first click)────────────┐ | | | live preview follows | | cursor after step 2 | └──────────────────────────● (second click) ``` The two clicks can be any pair of diagonally opposite corners — top-left + bottom-right, or bottom-left + top-right, etc. The order does not matter. Press **Escape** after the first click to cancel. There is nothing to cancel before the first click is placed. ## Dimension entry At any point after the first corner is placed, press **D** to type the rectangle's exact width and height instead of freely picking the opposite corner: 1. **Type the width** and press **Enter**. 2. **Type the height** and press **Enter**. 3. Move the cursor — the rectangle is now pinned to the first corner at the typed size, with only the direction it extends toward the cursor's quadrant changing. 4. **Click** to place the rectangle in that direction. Pressing **D** again at the direction-pick step brings the typed width/height back pre-filled and editable, so values can be corrected before picking a direction. The typed width and height are **remembered** and appear pre-filled the next time Dimension entry is used. ## Keyboard reference | Key | Action | |-----|--------| | `D` | Enter Dimension entry mode (after the first corner is placed), or re-edit the typed values at the direction-pick step | | `Escape` | Cancel after first corner is placed | No angle locking — the sides are always horizontal and vertical by definition. ## Grip editing — reshaping after creation A selected rectangle shows grips at every vertex and at the midpoint of each side: | Grip | Position | What it does | |------|----------|--------------| | **Corner** | Each of the 4 vertices | Drag to move that vertex; the two adjacent sides stretch to follow — the opposite corner stays fixed | | **Side midpoint** | Centre of each of the 4 sides | Drag to translate both endpoints of that side together, keeping the side's length and angle | Dragging a corner grip turns the rectangle into a non-rectangular quadrilateral. If you only need a rectangle that is a different size, drag a corner while keeping the sides roughly orthogonal, or delete it and draw a new one. ## Selecting rectangles Because the rectangle is a polyline, selection works the same way: | Method | Behaviour | |--------|-----------| | **Click** | Selects if the click lands on any of the four sides | | **Drag right** (strict) | All four vertices must lie inside the selection box | | **Drag left** (crossing) | Any side that crosses the box boundary selects the whole rectangle | ## Supported edit commands All polyline edit commands apply. Trim and Extend are [Line](./line)-only and do not work on rectangles: | Command | What happens to the rectangle | |---------|-------------------------------| | [Move](./move) | Translates all four vertices by the same displacement | | [Copy](./copy) | Creates an identical rectangle at a new position | | [Rotate](./rotate) | Rotates all four vertices around the chosen base point | | [Mirror](./mirror) | Reflects all four vertices across the mirror axis | | [Scale](./scale) | Scales all four vertices uniformly from the base point | | [Offset](./offset) | Creates a parallel (inset or outset) rectangle at a fixed distance | | [Delete](./delete) | Removes the rectangle from the drawing | ## Properties When a rectangle is selected the properties panel shows the same fields as any polyline: **General** | Property | Default | Meaning | |----------|---------|---------| | Color | 256 (ByLayer) | ACI color index | | Layer | `0` | Layer assignment | | Linetype | ByLayer | Named linetype pattern | | Linetype Scale | 1 | Scale factor on the linetype pattern | | Thickness | 0 | Extrusion thickness | **Geometry** | Property | Meaning | |----------|---------| | Closed | Always `true` for a rectangle | | Vertex Count | Always `4` for an unmodified rectangle | | Vertices | Coordinates of all four corners | ## Rectangle vs Polyline vs Line | | Rectangle | Polyline | Line | |---|-----------|---------|------| | How to draw | 2 clicks (corners) | Click each vertex | Click each endpoint | | Entity type | Closed `LWPOLYLINE` | Open or closed `LWPOLYLINE` | `LINE` per segment | | Sides always orthogonal | Yes (at creation) | No | No | | Trim / Extend | No | No | Yes | | Best for | Boxes, frames, rectangular areas | Arbitrary outlines and paths | Individual segments, construction lines | ## DXF — LWPOLYLINE entity Rectangles are saved as closed `LWPOLYLINE` entities with four vertices. All properties — vertex coordinates, color, layer, linetype, linetype scale, and thickness — round-trip without loss. There is no dedicated `RECTANGLE` type in DXF. When a file is reopened, the shape appears as a closed four-vertex polyline rather than a rectangle. Any DXF viewer or editor that supports `LWPOLYLINE` (AutoCAD, LibreCAD, etc.) will display it correctly. --- # Redo The `redo` command moves forward through the undo history, reapplying actions that were reversed by [Undo](./undo). Redo is only available when you have stepped back with Undo and have not yet made a new change. ## How to redo - Type `redo` in the terminal, or - Click the **Redo** toolbar button. Each invocation reapplies one previously undone action. Invoke it repeatedly to step forward through all available redo entries. ## Redo stack behaviour | Detail | Behaviour | |--------|-----------| | Available after | One or more [Undo](./undo) steps | | Cleared by | **Any new drawing action** — adding, editing, or deleting an entity | | Storage | Browser, per file — survives page reload (as long as no new action was made before reload) | | Maximum depth | Up to 20 entries (same pool as Undo) | Once a new entity is drawn, deleted, or modified, the redo stack is cleared and those entries cannot be recovered. Only undone actions that have not been superseded by new work can be redone. ## Redo vs Undo | | Redo | Undo | |---|------|------| | Direction | Steps **forward** through undone entries | Steps **backward** through history | | Available when | After at least one Undo, with no new action taken | At least one recorded action exists | | Cleared by | Any new drawing action | Nothing | The toolbar Redo button is greyed out when there are no entries to redo. Use [Undo](./undo) first to create redo entries. --- # Rotate The `rotate` command rotates selected entities around a base point. You specify the rotation angle either by typing a number in degrees or by clicking — the angle is computed from the direction between the base point and the click position. ## Two ways to start **Pre-select, then rotate** — select entities first, then activate: 1. Select one or more entities on the canvas. 2. Type `rotate` in the terminal or click the **Rotate** toolbar button. 3. **Click the base point** — the center of rotation. 4. **Type an angle and press Enter**, or **click** to set the angle from the cursor direction. **Activate, then select** — start the command with nothing selected: 1. Type `rotate` or click the toolbar button. 2. **Select objects** — click to toggle, or drag to select by area. 3. Press **Enter** or **Space** to confirm the selection. 4. **Click the base point**, then set the angle. ``` Before: After (90° rotation around ●): ╔══╗ ● [entity] → ● ║ ║ ╚══╝ ``` A live ghost preview of the rotated entities follows the cursor angle after the base point is set. ## Setting the angle **Typed angle** — type a number (in degrees) at any time after the base point is placed. The preview snaps to the typed angle while you continue to adjust before pressing Enter. **Click angle** — if no typed value is present, clicking sets the angle equal to `atan2(cursorY − baseY, cursorX − baseX)` — the direction from base point to click, in degrees. | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the angle value | | `-` | Negative angle (first character only) | | `C` | Toggle Copy mode (before typing any digits) | | `Backspace` | Delete the last typed character | | `Enter` | Apply rotation at the typed angle | ## Rotating a copy Press **C** at the angle prompt — before typing any digits — to toggle **Copy** mode, the same inline-option pattern AutoCAD's ROTATE command uses. The prompt shows the current state as `[Copy=true]` / `[Copy=false]`; pressing **C** again flips it back. With Copy on, applying the rotation leaves the original selection untouched in place and adds new, rotated copies of each selected entity instead. With Copy off (the default), the selection rotates in place as usual. ## Angle direction Angles follow the **DXF convention**: - **Positive** values rotate **counter-clockwise** in drawing coordinates (Y-up). - On screen, where the Y-axis is inverted (Y-down), positive angles appear **clockwise**. Common values: `90` = quarter turn, `180` = half turn, `-90` = opposite quarter turn. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Confirm selection | | `0`–`9`, `.`, `-` | Build up angle value | | `C` | Toggle Copy mode (angle phase, before typing any digits) | | `Backspace` | Delete last typed character | | `Enter` | Apply rotation | | `Escape` | Cancel and reset | ## Selection during the command | Method | Behaviour | |--------|-----------| | **Click** | Toggles the entity under the cursor | | **Drag right** (strict) | Adds entities fully inside the box | | **Drag left** (crossing) | Adds entities that intersect the box | | **Enter** / **Space** | Confirms the selection | ## Supported entities Rotate works on every entity type. Each entity's geometry is rotated around the base point — for example, a Circle moves its center while the radius stays the same; an Arc moves its center and shifts its start and end angles by the rotation amount; a Text entity moves its anchor point and adds the angle to its Rotation Degree property. --- # Scale The `scale` command resizes selected entities uniformly around a base point. All distances from the base point are multiplied by the scale factor — a factor of `2` doubles all dimensions, `0.5` halves them. The factor is always entered by typing; there is no click-to-set-scale. ## Two ways to start **Pre-select, then scale** — select entities first, then activate: 1. Select one or more entities on the canvas. 2. Type `scale` in the terminal or click the **Scale** toolbar button. 3. **Click the base point** — the fixed point that does not move during scaling. 4. **Type the scale factor** and press **Enter**. **Activate, then select** — start the command with nothing selected: 1. Type `scale` or click the toolbar button. 2. **Select objects** — click to toggle, or drag to select by area. 3. Press **Enter** or **Space** to confirm the selection. 4. **Click the base point**, then type the factor. ``` Base ● Base ● [entity] → [larger entity] factor = 2 → distances from ● are doubled ``` ## Typing the scale factor After the base point is placed the terminal shows `enter scale factor:` and waits for keyboard input: | Key | Action | |-----|--------| | `0`–`9`, `.` | Append digit to the factor | | `-` | Negative factor (first character only — inverts then scales) | | `Backspace` | Delete the last typed character | | `Enter` | Apply the scale at the typed factor | The factor must be non-zero. Common values: | Factor | Effect | |--------|--------| | `2` | Doubles all dimensions | | `0.5` | Halves all dimensions | | `1.5` | Increases by 50% | | `-1` | Mirrors across the base point (equal to a 180° rotation) | There is no live preview while typing — the scaled result appears only after pressing **Enter**. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Confirm selection | | `0`–`9`, `.`, `-` | Build up scale factor | | `Backspace` | Delete last typed character | | `Enter` | Apply scale | | `Escape` | Cancel and reset | ## Selection during the command | Method | Behaviour | |--------|-----------| | **Click** | Toggles the entity under the cursor | | **Drag right** (strict) | Adds entities fully inside the box | | **Drag left** (crossing) | Adds entities that intersect the box | | **Enter** / **Space** | Confirms the selection | ## What gets scaled All entity types are supported. Each entity scales its geometry relative to the base point: | Entity | What changes | |--------|-------------| | Line | Both endpoints scaled away from the base point | | Circle | Center scaled from the base point; radius multiplied by the factor | | Arc | Center scaled; radius multiplied by the factor; angles unchanged | | Ellipse | Center scaled; both semi-axis lengths multiplied by the factor | | Polyline / Rectangle | Every vertex scaled from the base point | | Text | Anchor point scaled; height multiplied by the factor | | Spline | All control vertices / fit points scaled | --- # Spline CV The `splinecv` command draws a **cubic B-spline** by placing control vertices (CVs). The resulting curve is attracted toward each CV but does not pass through them — except at the very first and last vertex, where **clamped knots** anchor the curve exactly. This gives you intuitive shape control: pull a vertex to push the curve toward it without forcing it to touch every point. ## Drawing a spline by control vertices 1. Type `splinecv` in the terminal or click the **Spline CV** toolbar button. 2. **Click to place control vertices** — each click adds a vertex. The terminal shows the current count. 3. Press **Enter** or **Space** to finish (at least 2 vertices required). ``` CV ● ● CV \ / \ / ← curve pulled toward CVs \ / but not passing through them CV ● ● ● CV (start/end: curve touches here) ``` The live preview updates as you move the cursor after each vertex, showing how the spline will look with the next point at the cursor position. Press **Escape** to discard all placed vertices and exit. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Finish and commit the spline (≥ 2 vertices required) | | `Escape` | Discard all vertices and exit | There is no typed length input — all vertices are click-only. ## Grip editing — reshaping via control vertices A selected CV spline exposes one grip per control vertex: | Grip | Position | What it does | |------|----------|--------------| | **Control vertex** | At each CV position | Drag to move that CV — the curve reshapes toward the new position | There is no "move whole spline" grip. To translate the entire spline, use the [Move](./move) command. ## Selecting CV splines | Method | Behaviour | |--------|-----------| | **Click** | Selects if the click lands near any point on the curve | | **Drag right** (strict) | All sample points along the curve must lie inside the selection box | | **Drag left** (crossing) | Any part of the curve that crosses the selection box boundary selects it | ## Supported edit commands | Command | What happens to the spline | |---------|---------------------------| | [Move](./move) | Translates all control vertices by the same displacement | | [Copy](./copy) | Creates an identical spline at a new position | | [Rotate](./rotate) | Rotates all CVs around the chosen base point | | [Mirror](./mirror) | Reflects all CVs across the mirror axis | | [Scale](./scale) | Scales all CVs uniformly from the base point | | [Delete](./delete) | Removes the spline | Splines do not support **Offset**, **Trim**, or **Extend**. ## Properties **General** | Property | Default | Meaning | |----------|---------|---------| | Color | 256 (ByLayer) | ACI color index | | Layer | `0` | Layer assignment | | Linetype | ByLayer | Named linetype pattern | | Linetype Scale | 1 | Scale factor on the linetype pattern | | Thickness | 0 | Extrusion thickness | **Geometry** | Property | Meaning | |----------|---------| | Degree | Polynomial degree — always 3 (cubic) | | Control Vertices | Coordinates of all CVs | | Fit Points | Empty for CV splines; populated only for fit-point splines | ## Spline CV vs Spline Fit — which to use | | Spline CV | Spline Fit | |---|-----------|------------| | Curve passes through points | First and last only (clamped) | Every clicked point exactly | | Shape control | Pull CVs toward a region | Move fit points the curve must touch | | Grip edit effect | CV moves → curve attracted | Fit point moves → curve re-interpolated | | Best for | Smooth aesthetic curves, free-form paths | Curves that must hit specific coordinates | ## DXF — SPLINE entity (control-vertex form) CV splines are saved as `SPLINE` entities in the DXF file, storing the degree, the knot vector, and all control vertex coordinates. All properties — color, layer, linetype, linetype scale, and thickness — round-trip without loss. The `splineFlag` is set to `9` (CV spline) so the form is preserved on reload. Any DXF application that supports `SPLINE` entities with CV data reads these correctly. --- # Spline Fit The `splinefit` command draws a cubic spline that passes through every point you click — an interpolating curve. Unlike [Spline CV](./spline-cv), where the curve is only attracted toward control vertices, here the curve is forced to hit each clicked coordinate exactly. Internally the editor fits control vertices to achieve this, and those CVs are stored alongside the fit points in the DXF file. ## Drawing a spline through fit points 1. Type `splinefit` in the terminal or click the **Spline Fit** toolbar button. 2. **Click to place fit points** — the curve will pass through each one. The terminal shows the current count. 3. Press **Enter** or **Space** to finish (at least 2 points required). ``` ●──────●──────●──────● ← curve passes exactly through each click p1 p2 p3 p4 ``` The live preview shows the current interpolated curve as you move the cursor, including the prospective next point at the cursor position. Press **Escape** to discard all placed points and exit. ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Finish and commit the spline (≥ 2 points required) | | `Escape` | Discard all points and exit | There is no typed length input — all fit points are click-only. ## Grip editing — reshaping via fit points A selected fit spline exposes one grip per fit point: | Grip | Position | What it does | |------|----------|--------------| | **Fit point** | At each clicked position | Drag to move that fit point — the entire curve re-interpolates to pass through the new position | Dragging one grip re-fits the whole curve, not just the adjacent segments. This differs from polyline grip editing, where moving a vertex only reshapes the two adjacent segments. There is no "move whole spline" grip. To translate the entire spline, use the [Move](./move) command. ## Selecting fit splines | Method | Behaviour | |--------|-----------| | **Click** | Selects if the click lands near any point on the curve | | **Drag right** (strict) | All sample points along the curve must lie inside the selection box | | **Drag left** (crossing) | Any part of the curve that crosses the selection box boundary selects it | ## Supported edit commands | Command | What happens to the spline | |---------|---------------------------| | [Move](./move) | Translates all fit points and recomputed CVs by the same displacement | | [Copy](./copy) | Creates an identical spline at a new position | | [Rotate](./rotate) | Rotates all fit points around the chosen base point | | [Mirror](./mirror) | Reflects all fit points across the mirror axis | | [Scale](./scale) | Scales all fit points uniformly from the base point | | [Delete](./delete) | Removes the spline | Splines do not support **Offset**, **Trim**, or **Extend**. ## Properties **General** | Property | Default | Meaning | |----------|---------|---------| | Color | 256 (ByLayer) | ACI color index | | Layer | `0` | Layer assignment | | Linetype | ByLayer | Named linetype pattern | | Linetype Scale | 1 | Scale factor on the linetype pattern | | Thickness | 0 | Extrusion thickness | **Geometry** | Property | Meaning | |----------|---------| | Degree | Polynomial degree — always 3 (cubic) | | Fit Points | Coordinates of all clicked pass-through points | | Control Vertices | Internally computed CVs used to render the curve | ## Spline Fit vs Spline CV — which to use | | Spline Fit | Spline CV | |---|------------|-----------| | Curve passes through points | Every clicked point exactly | First and last only (clamped) | | Grip edit effect | Fit point moves → whole curve re-interpolated | CV moves → curve attracted toward new position | | Shape predictability | High — curve follows clicks | Lower — curve lags behind CVs | | Best for | Curves that must hit specific coordinates | Smooth aesthetic curves, free-form paths | ## DXF — SPLINE entity (fit-point form) Fit splines are saved as `SPLINE` entities in the DXF file, storing both the fit point coordinates and the computed control vertices. The `splineFlag` is set to `8` (fit-point spline) so a reloading application knows which set of points to display as editable grips. All properties — color, layer, linetype, linetype scale, and thickness — round-trip without loss. DXF applications that support fit-point splines (AutoCAD, LibreCAD) will display the fit points as the primary editable data. --- # Text The `text` command places a multi-line text label. After you click a position on the canvas, a popup editor opens where you can type content, apply bold/italic/strikethrough, change fonts and heights, and insert line breaks. Press **Escape** to commit and close the editor. Double-click any existing label to reopen the editor. See the [Text Editor](#text-editor) section for the full editor reference. ## Placing a text label 1. Type `text` in the terminal or click the **Text** toolbar button. 2. **Click the anchor position** on the canvas. Or type `X,Y` and press **Enter** for an exact coordinate. 3. The **text editor popup** opens above the new label. Type your content. 4. Press **Escape** to commit and close the editor. The default height is **12 drawing units**. ## Editing an existing label **Double-click** any text label on the canvas to reopen the editor for that label. ## Keyboard reference **Anchor phase** | Key | Action | |-----|--------| | `0`–`9`, `.`, `-` | Start X coordinate entry | | `,` | Lock X and move to Y entry | | `Backspace` | Delete last typed character | | `Enter` | Confirm typed coordinate | **Text editor phase** | Key | Action | |-----|--------| | Any printable character | Insert at cursor | | `Backspace` / `Delete` | Delete adjacent character or selection | | `Enter` | Insert a line break | | `←` / `→` | Move caret | | `Home` / `End` | Jump to start / end of hard line | | `Escape` | Commit and close the editor | ## Grip editing — repositioning A selected text label exposes one grip at the anchor point: | Grip | Position | What it does | |------|----------|--------------| | **Anchor** | Bottom-left of the text | Drag to reposition the label | ## Selecting text | Method | Behaviour | |--------|-----------| | **Click** | Selects if the click lands within the rotated bounding box of the text | | **Drag right** (strict) | All four corners of the bounding box must lie inside the selection area | | **Drag left** (crossing) | Any overlap between the text bounding box and the selection area selects it | ## Supported edit commands | Command | What happens to the text | |---------|--------------------------| | [Move](./move) | Moves the anchor point | | [Copy](./copy) | Creates an identical label at a new position | | [Rotate](./rotate) | Rotates the anchor position and adds the angle to Rotation Degree | | [Mirror](./mirror) | Reflects the anchor point across the mirror axis (text string is not flipped) | | [Scale](./scale) | Scales the anchor position and multiplies the height by the scale factor | | [Delete](./delete) | Removes the label | Text does not support **Offset**, **Trim**, or **Extend**. ## Properties When a text label is selected the properties panel shows: **General** | Property | Default | Meaning | |----------|---------|---------| | Color | 256 (ByLayer) | ACI color index | | Layer | `0` | Layer assignment | **Geometry** | Property | Meaning | |----------|---------| | Position X / Position Y | Anchor point coordinates | | Height | Base text height in drawing units (default: **12**) | | Rotation Degree | Counter-clockwise rotation in degrees | **Properties** | Property | Meaning | |----------|---------| | Content | The text string (MTEXT inline codes preserved) | | Attachment Point | Alignment code (1 = top-left … 9 = bottom-right) | Text does not have Linetype, Linetype Scale, or Thickness properties. ## DXF — MTEXT entity Text labels are stored as **MTEXT** entities in the DXF file. Bold and italic are encoded using `\L`, `\K`, `\O`, and inline font switches (`\f`). Per-character height is encoded as `\H`. All formatting survives a full DXF round-trip and is readable by AutoCAD, LibreCAD, and other DXF-compatible applications. --- # Trim The `trim` command removes the portion of a [Line](./line), [Arc](./arc), [Circle](./circle), [Ellipse](./ellipse), or [Polyline](./polyline) that lies between two adjacent intersection points, splitting the entity into one or more remaining pieces. The segment to cut is determined by the cursor position — hover over the part you want removed and click to trim it. ## Trimming an entity 1. Type `trim` in the terminal or click the **Trim** toolbar button. 2. **Hover over the segment** you want to remove — a preview highlights exactly the portion that will be cut. 3. **Click** to remove that segment. The command stays active after each trim, so you can continue hovering and clicking to cut more segments — on the same entity or a different one. Press **Enter**, **Space**, or **Escape** to exit. ``` Before: After trimming middle segment: ──────●──────●────── ──────● ●────── intersect intersect (left part) (right part) (middle segment removed) ``` ## How the trim segment is determined The command projects the cursor position onto the hovered entity and finds all intersection points it has with other entities. These intersections divide the entity into segments — for a Line, Arc, or open Polyline, the entity's own endpoints act as additional fixed boundaries. A full Circle or Ellipse, or a closed Polyline (including a Rectangle), has no endpoints of its own, so at least two intersection points are needed before it can be trimmed at all. The segment whose interval contains the cursor's projection is highlighted and will be removed on click. - **Line, Arc, and open Polyline** — the removed segment can be the leading portion, a middle portion (splitting the entity into two pieces), or the trailing portion. - **Circle, Ellipse, and closed Polyline/Rectangle** — since there is no fixed start or end, only the arc between two *intersection points* can be removed. With fewer than two intersections, no preview appears and clicking does nothing. ## What trimming produces | Entity | Result after trimming | |--------|------------------------| | Line | Up to two shorter Line entities | | Arc | Up to two shorter Arc entities | | Circle | One [Arc](./arc) entity — the circle's closed shape is gone | | Ellipse | One Ellipse entity with a start and end angle | | Polyline (open) | Up to two shorter Polyline entities | | Polyline (closed) / Rectangle | One open Polyline entity | ## Keyboard reference | Key | Action | |-----|--------| | `Enter` / `Space` | Exit trim mode | | `Escape` | Exit trim mode | ## Supported entities | Entity | Can be trimmed? | |--------|----------------| | Line | Yes | | Arc | Yes | | Circle | Yes — requires 2 or more intersection points | | Ellipse | Yes — requires 2 or more intersection points | | Polyline (open) | Yes | | Polyline (closed) / Rectangle | Yes — requires 2 or more intersection points | | Text, Spline, Dimension, Leader | No | The entities used as **cutting boundaries** can be a Line, Arc, Circle, Ellipse, or Polyline. A [Polyline](./polyline)'s arc segments (drawn with the Arc toggle, or imported) trim exactly like its straight segments — hover the arc portion between two intersections and click. The trimmed edge keeps its curvature; only its length changes. ## Trim vs Extend | | Trim | Extend | |---|------|--------| | What it does | Removes a segment of an entity | Stretches a line endpoint to a boundary | | Trigger | Hover over the segment to cut | Hover near the endpoint to extend | | Result | Entity splits or shortens | Line endpoint moves to the boundary | | Supported entities | Line, Arc, Circle, Ellipse, Polyline | Line, Arc, Ellipse, Polyline | --- # Undo The `undo` command reverses the last change to the drawing — one step per invocation. Each addition, deletion, or edit of entities is recorded as a separate history entry. Undo steps back through these entries in reverse order. ## How to undo - Type `undo` in the terminal, or - Click the **Undo** toolbar button. Each invocation reverses one recorded action. Invoke it repeatedly to step further back. ## History behaviour | Detail | Value | |--------|-------| | Steps per file | Up to **20** | | Storage | Browser (IndexedDB / localStorage), per file name | | Survives page reload | Yes — history is restored when you reopen the file | | New action after undo | Clears all redo entries ahead of the current position | | Oldest entry when full | Dropped to make room for the newest change | Every entity mutation is recorded: drawing new entities, deleting entities, grip-editing endpoints, applying Move, Rotate, Scale, Mirror, Trim, Extend, and Offset all create history entries. ## Undo vs Redo | | Undo | Redo | |---|------|------| | Direction | Steps **backward** through history | Steps **forward** through undone entries | | Available when | At least one recorded action exists | At least one Undo has been performed and no new action has been taken | | Cleared by | Nothing — history accumulates until the 20-step limit | Any new drawing action | Use [Redo](./redo) to reapply an undone action. The toolbar buttons are greyed out when the respective direction is unavailable. --- # ViewportCopy The `ViewportCopy` command copies a viewport to a new position, preserving its scale and model center. Only available in layout space. ## Copying a viewport 1. Switch to a paper layout tab. 2. Optionally click a viewport to pre-select it. 3. Type `ViewportCopy` in the terminal or click the **Viewport Copy** toolbar button. 4. If no viewport was pre-selected, **click the viewport** to copy. 5. **Click the base point** — the reference point for the move. 6. **Click the destination** — the viewport is placed at the offset between base and destination. ## Notes - ViewportCopy is only available when a paper layout tab is active. - The copied viewport inherits the same scale, model center, locked state, and dimensions as the original. - To create a new viewport from scratch, use [ViewportRectangle](./viewport-rectangle). --- # ViewportRectangle The `ViewportRectangle` command creates a new viewport in the active paper layout by picking two opposite corners. Only available in layout space. ## Creating a viewport 1. Switch to a paper layout using the tab at the bottom of the screen. 2. Type `ViewportRectangle` in the terminal or click the **Viewport Rectangle** toolbar button. 3. **Click the first corner** of the viewport area. 4. **Click the opposite corner** — the viewport is placed immediately. The new viewport shows the full model at the layout's default scale. Use the scroll wheel inside the viewport to zoom, or middle-drag to pan the model view. ## Editing a viewport After placing a viewport, click it to select it: - **Drag the edges or corners** to resize. - **Drag the center grip** to move it. - Use the **scale selector** in the control bar to set an exact scale (e.g. 1:50). To enter a scale not in the list, type it in the input field at the bottom of the dropdown — accepts ratio format (`1:200`, `5:1`) or a plain decimal (`0.005`), then press **Enter**. - Right-click a viewport and use **Lock** to prevent accidental changes. ## Notes - ViewportRectangle is only available when a paper layout tab is active. Running it in model space shows an error message and exits. - To copy an existing viewport, use [ViewportCopy](./viewport-copy). --- # Wipe Storage The `wipestorage` command permanently deletes **all data stored in the browser** for KulmanLab CAD — every saved file, layer and linetype table, and undo history. The page reloads automatically afterwards. :::danger Irreversible This action cannot be undone. All files stored in the browser are deleted. Export any drawings you want to keep as `.json` or `.dxf` files before running this command. ::: ## When to use it - The browser storage is corrupted and the app fails to load or save files. - You want to fully reset the app to a clean state. - You are switching browsers or devices and no longer need the local copy. ## How to run it 1. Type `wipestorage` in the terminal and press **Enter**. 2. The terminal prompts: *Wipe all browser local storage? Type YES to confirm* 3. Type `YES` (any capitalisation) and press **Enter**. The app deletes the database and reloads the page. If you type anything other than `YES` and press **Enter**, or press **Escape**, the command is cancelled and nothing is deleted. ## What gets deleted | Data | Deleted | |------|---------| | All files saved in the browser | Yes | | Layer and linetype tables for each file | Yes | | Undo / redo history for each file | Yes | Only data stored locally in **this browser** is affected. Files you have already exported as `.json` or `.dxf` are not touched. --- # Recovery (?reset URL parameter) If KulmanLab CAD crashes on every startup — for example, after working with extremely large coordinates that cause the graphics compositor to fail — you can clear all locally stored data by appending `?reset` to the app URL: ``` https://kulmanlab.com/?resetKulmanLocalStorage ``` ## What it does 1. Deletes all data from the browser's IndexedDB database: every saved file, layer table, linetype table, and undo history. 2. Removes the `?reset` parameter from the URL so the page does not re-clear on the next load. 3. Loads a fresh blank drawing, identical to opening the app for the first time. ## When to use it Use `?reset` when the app crashes before it finishes loading, making it impossible to reach the terminal and run the [WipeStorage](./wipestorage) command interactively. Common triggers: - A drawing was saved with coordinates beyond ±10¹⁵ (e.g. entered experimentally or imported from a broken DXF), causing the camera fit operation to generate canvas transform values that crash the GPU compositor. - The IndexedDB database became corrupt due to a browser crash mid-write. ## Comparison with WipeStorage | | `?reset` URL parameter | `wipestorage` command | |---|---|---| | Requires the app to load | No | Yes | | Requires terminal access | No | Yes | | Needs confirmation step | No | Yes — type `YES` | | Effect | Identical — clears all browser data | Identical — clears all browser data | Both approaches delete the same data. `?reset` is the emergency fallback for when the app cannot start at all. --- # Zoom In The `zoomin` command multiplies the current zoom level by **1.5×** and exits immediately, centered on the viewport midpoint. It is the toolbar equivalent of one scroll-wheel tick zoomed toward screen center rather than cursor. ## Zooming in Click the **Zoom In** toolbar button or type `zoomin` in the terminal. The zoom is applied instantly and the command exits — no click on the canvas required. ## How the 1.5× step works | Current zoom | After one Zoom In | |-------------|------------------| | 1.00× | 1.50× | | 1.50× | 2.25× | | 10.00× | 15.00× | | 6,667× | 10,000× (capped) | The zoom level is always shown in the **bottom-right corner** of the canvas next to the `zoom` label. The upper limit is **10,000×**; further steps do nothing. ## Toolbar zoom-in vs scroll wheel | | Zoom In button | Scroll wheel | |---|--------------|-------------| | Zoom center | Viewport midpoint | Cursor position | | Step size | 1.5× per click | ~1.1× per tick | | Activation required | No | No — works always | | Best for | Coarse navigation | Precise, cursor-targeted zoom | ## Keyboard reference There are no keyboard shortcuts for this command. Use the scroll wheel instead — it works at any time without activating any command. ## Related view commands | Command | What it does | |---------|-------------| | [Zoom Out](./zoom-out) | Divides zoom by 1.5× per step | | [Fit](./fit) | Resets zoom to show all entities | | [Pan](./pan) | Shifts the viewport without zooming | --- # Zoom Out The `zoomout` command divides the current zoom level by **1.5×** (equivalent to multiplying by ~0.667) and exits immediately, centered on the viewport midpoint. It is the inverse of [Zoom In](./zoom-in). ## Zooming out Click the **Zoom Out** toolbar button or type `zoomout` in the terminal. The zoom is applied instantly and the command exits — no click on the canvas required. ## How the 1.5× step works | Current zoom | After one Zoom Out | |-------------|-------------------| | 1.50× | 1.00× | | 2.25× | 1.50× | | 10.00× | 6.67× | | 0.015× | 0.01× (floored) | The zoom level is always shown in the **bottom-right corner** of the canvas. The lower limit is **0.01×**; further steps do nothing. ## Toolbar zoom-out vs scroll wheel | | Zoom Out button | Scroll wheel | |---|----------------|-------------| | Zoom center | Viewport midpoint | Cursor position | | Step size | 1.5× per click | ~1.1× per tick | | Activation required | No | No — works always | | Best for | Stepping back to see more context | Smooth, cursor-anchored zoom-out | ## Keyboard reference There are no keyboard shortcuts for this command. Use the scroll wheel instead — it works at any time without activating any command. ## Related view commands | Command | What it does | |---------|-------------| | [Zoom In](./zoom-in) | Multiplies zoom by 1.5× per step | | [Fit](./fit) | Resets zoom to show all entities | | [Pan](./pan) | Shifts the viewport without zooming | --- # Coordinate Entry At any point prompt in a drawing or editing command, you can type an exact position instead of clicking. Type the X value (digits, `.`, `-`), press `,`, type the Y value, press **Enter**. The terminal shows the X value locked while you type Y: `[X], [Y{cursor}]`. Works in all commands that ask for a point: Line, Polyline, Rectangle, Circle, Arc, Ellipse, Spline, Move, Copy, Rotate, Mirror, Scale, Leader, all Dimension commands, ViewportRectangle, ViewportCopy, Distance, Angle. Clicking and typing can be freely mixed within the same command — e.g. click a snap point for the start, then type an exact end coordinate. Press Backspace to correct a digit; Escape to cancel the entry and return to click mode. Coordinates are in model space (world units). In layout (paper) space they refer to the paper coordinate system. --- # Grid & Snap Two toggle buttons in the control bar let you overlay a reference grid and lock the cursor to its intersections while drawing. | Button | What it does | |--------|-------------| | **Grid** | Shows a visual dot or line grid on the canvas | | **Snap** | Locks the cursor to the nearest grid point when no geometry snap is closer | The two toggles are independent — you can show the grid without snapping, snap without showing the grid, or use both together. ## Enabling grid and snap Click **Grid** or **Snap** in the control bar toolbar. Active state is highlighted. Settings are persisted across sessions. When **Snap** is enabled, the grid automatically switches its display from lines to **dots** — the dots mark the exact points the cursor will snap to. ## Adaptive grid spacing The grid spacing adjusts automatically as you zoom so that grid lines are always a comfortable distance apart on screen (~40 px). The step is always a "nice" number — a multiple of 1, 2, or 5 at any power of ten: | Example zoom / model scale | Grid step | |---------------------------|-----------| | Zoomed out (large area) | 100, 500, 1000 … | | Medium zoom | 10, 20, 50 … | | Zoomed in (fine detail) | 1, 2, 5 … | | Very close | 0.1, 0.2, 0.5 … | This means every snap point lands on a round coordinate in model space — no floating-point offsets accumulate. ## Snap priority **Endpoint and intersection snaps always take priority over the grid.** The cursor snaps to a grid point only when it is not near any geometry snap candidate (endpoint, midpoint, center, or intersection). This means you can draw with snap-to-grid enabled and still snap precisely to existing geometry when the cursor passes close enough to it. The grid is a fallback, not an override. ## Layout mode - **Model space** — dots or lines fill the entire visible canvas area. - **Layout (paper) space** — dots are clipped to the paper rectangle and do not extend outside it. - **Inside a viewport** — the grid follows the model coordinate system at the viewport's scale, so dots align with the same model units regardless of the viewport magnification. ## Typical workflow 1. Turn on **Grid** and **Snap** before starting a drawing that requires regular spacing. 2. Zoom to the level where the grid step matches your desired increment (e.g. zoom until dots are 10 units apart). 3. Draw — the cursor snaps to grid points automatically. Existing geometry still snaps normally when you are close to it. 4. Turn **Snap** off when you need free cursor movement or want to snap only to geometry. --- # Linetype The **linetype** chip in the toolbar controls the dash pattern assigned to every new entity you draw. Click it to open the picker dropdown. ## Options | Value | Meaning | |-------|---------| | **From Layer** | Entity inherits the linetype defined on its layer. Shown as `ByLayer` in DXF. | | **ByBlock** | Entity inherits the linetype of the block it belongs to. Has no visible effect outside a block. | | **Continuous** | Solid unbroken line — no dash pattern. | | **Named linetypes** | Any linetype loaded from the current DXF file (e.g. `DASHED`, `CENTER`, `HIDDEN`, `PHANTOM`, …). The dropdown shows a live preview of each pattern and its definition string. | ## How it applies The selected linetype is applied to every entity created after the change. It does not retroactively affect existing entities. To change the linetype of existing entities, select them and edit the **Linetype** field in the properties panel, or use MatchProperties to copy it from another entity. ## Linetype scale Each entity also has a **Linetype Scale** property (default `1`). The dash pattern is multiplied by this factor. A value of `2` makes the dashes twice as long; `0.5` makes them half as long. Edit it in the properties panel after selecting an entity. ## Available linetypes The dropdown lists only linetypes present in the currently loaded DXF file. A freshly created file contains only `ByLayer`, `ByBlock`, and `Continuous`. When you import a DXF, all linetypes defined in the file's `$LTYPE` table become available. If you need a specific linetype (e.g. `DASHED2`) that is not in the list, import a DXF file that contains it — the linetype will then appear in the picker for the current session. ## DXF compatibility Linetype names are stored as strings in entity records. `ByLayer` and `ByBlock` are the standard DXF sentinel values. All named linetypes and their dash patterns are preserved exactly on export and round-trip without loss in AutoCAD, LibreCAD, and other DXF-compatible applications. --- # Lineweight The **lineweight** chip in the toolbar controls the stroke width assigned to every new entity you draw. Click it to open the picker dropdown. ## Options | Value | Meaning | |-------|---------| | **From Layer** | Entity inherits the lineweight defined on its layer. The actual width shown depends on the layer setting. | | **Default** | Uses the application default width — rendered as a thin line (1 px). Does not override the layer setting in DXF. | | **0.00 mm – 2.11 mm** | An explicit fixed width. The entity carries this value regardless of its layer's lineweight. | Standard DXF lineweight values are available: 0.00, 0.05, 0.09, 0.13, 0.15, 0.18, 0.20, 0.25, 0.30, 0.35, 0.40, 0.50, 0.53, 0.60, 0.70, 0.80, 0.90, 1.00, 1.06, 1.20, 1.40, 1.58, 2.00, and 2.11 mm. ## How it applies The selected lineweight is applied to every entity created after the change. It does not retroactively affect existing entities. To change the lineweight of existing entities, select them and edit the **Lineweight** field in the properties panel, or use MatchProperties to copy it from another entity. ## Rendering Lineweights are rendered at a scale of **3.78 px per mm** (96 dpi). A 0.25 mm line is approximately 1 px wide on screen; a 1.00 mm line is approximately 4 px. Very thin values (0.00 mm and negatives) are always rendered as at least 0.5 px so they remain visible at any zoom level. ## DXF compatibility Lineweight values are stored as integers in hundredths of a millimetre (e.g. 25 = 0.25 mm) in the DXF `LWPOLYLINE`, `LINE`, `CIRCLE`, and other entity records. **From Layer** is stored as `-1` and **Default** as `-3`, matching the AutoCAD DXF specification. Files round-trip without loss in any DXF-compatible application. --- # Text Editor The text editor opens whenever you place a new text label with the `text` command or double-click an existing text entity. It supports multi-line content, per-character formatting, and word-wrap. ## Opening the editor | Action | Result | |--------|--------| | `text` command → click position | Creates a new text entity and opens the editor | | Double-click an existing text entity | Re-opens the editor for that entity | | `Escape` inside the editor | Closes the editor and keeps all changes | ## Toolbar The toolbar floats above the text's bounding box and stays anchored to the entity as you pan or zoom. ### Bold · Italic · Strikethrough | Button | What it does | |--------|--------------| | **B** | Toggle bold | | *I* | Toggle italic | | ~~S~~ | Toggle strikethrough | **How toggling applies:** - **With a text selection** — the style is applied to exactly the selected characters only. - **No selection, cursor in existing text** — toggles the style on the whole entity (all segments). - **Empty text or new entity** — the style is stored on the empty segment and applied to every character you type from that point on. The button appears highlighted (active) when every character in the current selection — or the character immediately to the left of the cursor — has that style set. ### Font The dropdown groups available typefaces into **Default** (the built-in sans-serif), **User** (your own uploaded fonts, if any), **Free** (a set of bundled Google Fonts), and **System** (common OS fonts such as Helvetica, Times New Roman, Georgia, Courier New, Verdana, Tahoma, Trebuchet MS, Lucida Console, and Impact). - **With a selection** — overrides the font for selected characters only. - **No selection** — applies the font to the whole entity. Not limited to the built-in list — click the **Font Manager** button in the toolbar to upload your own `.ttf` file and add it to the **User** group. See [Font Manager](./font-manager) for details. ### Height The number field sets the **cap height** (height of a capital letter) in drawing units. - **With a selection** — overrides the height for selected characters, independently of the entity's base height. - **No selection** — changes the entity's base height (applies to all characters that do not have an individual height override). ## Cursor and navigation | Key | Action | |-----|--------| | `←` / `→` | Move caret one character left or right | | `Home` | Jump to the start of the current hard line | | `End` | Jump to the end of the current hard line | | `Shift` + `←` / `→` | Extend or shrink the selection | | `Backspace` | Delete the character to the left (or the selection) | | `Delete` | Delete the character to the right (or the selection) | | `Enter` | Insert a line break | | `Escape` | Close the editor | The cursor height automatically matches the cap height of the adjacent character, including the smaller size used for subscript and superscript. ## Word wrap When a text entity has a **reference width** set, long lines are soft-wrapped at word boundaries to fit within that width. To set or change the reference width while the entity is selected, drag the **resize grips** — the thin rectangles on the left and right edges of the dashed bounding box. The content reflows in real time as you drag. Setting the reference width to zero removes word wrap and lets lines grow freely. ## Multi-line text Press `Enter` to insert a hard line break. Each hard line is independent — `Home` and `End` navigate within the current hard line only. ## DXF compatibility Text entities are stored as **MTEXT** in DXF files. Bold and italic are encoded as `\L`, `\K`, `\O`, and inline font switches (`\f`). Per-character height is encoded as `\H`. All formatting is preserved on export and readable by AutoCAD, LibreCAD, and other DXF-compatible applications. --- # Vector Pins Vector Pins are a drawing aid that lets you align new geometry with existing points without drawing construction lines — KulmanLab CAD's equivalent of object snap tracking. The feature is controlled by the **Pins** toggle in the control bar (next to Grid, Snap, and ANGL). It is on by default and the setting persists across sessions. ## Pinning a point Hover a snap point (endpoint, midpoint, or center marker) for **500 ms** during a command — the marker turns into a filled accent square and the point is pinned. Hovering a grip of a selected entity outside a command pins it the same way. Pin as many points as needed. ## Tracking along reference lines Every pinned point projects two invisible reference lines — one horizontal and one vertical — through its exact coordinates: - Within 12 px of a pin's vertical line the cursor snaps onto it: a dashed accent line is drawn through the pin across the whole view, with an X marker at the snapped position. The X coordinate is exactly the pin's X. - The same applies to the horizontal line for the pin's Y coordinate. - Near one line of each orientation — even from two different pins — the cursor snaps to their intersection, placing a point exactly at (X of pin A, Y of pin B). Snapped coordinates are taken directly from the pin, so alignment is exact. ## Snap priority Regular geometry snaps (endpoint, midpoint, center, intersection) take precedence over pin reference lines — if a point snap is closer, it wins. Pin tracking fills the gaps between geometry, it never blocks snapping to the geometry itself. ## Combining with angle lock When a command has locked the cursor to an angle-tracking ray (the ANGL toggle), pin snapping switches to targeting the intersections of the locked ray with the pin reference lines (only ahead of the ray origin). Ray snapping works in every command with angle locking: Line, Polyline, Arc, Circle, Move, Copy, Area, Leader, and ViewportCopy. ## Pin lifecycle All pins are cleared when a new command fires, when Escape is pressed, when the Pins toggle is switched off, or when switching between model and paper space. Within a single command you can pin, draw, pin again — pins survive every click of a multi-point command such as Polyline. --- # Selection Filter Selecting many entities opens the property panel in its multi-selection view ("Selection (N)"). A filter icon next to the close button opens a popup that narrows the selection down by property before bulk-editing. ## Opening the filter Select several entities (drag a box, Shift-click, or Ctrl+A), then click the filter icon (funnel) in the property panel header. A popup opens below it with a checklist for each property that actually varies across the selection. ## Facets Up to five facets are shown, each built live from the current selection: **Type** (entity type name), **Layer** (with a color swatch), **Color** (ACI index), **Lineweight**, and **Linetype**. A facet only appears if the selection contains more than one distinct value for it — ten lines all on the same layer show no Layer facet, since checking it couldn't narrow anything. Entities that don't carry a given property at all (Hatch and Text have no lineweight or linetype) aren't counted toward that facet and are never excluded by it. ## Narrowing the selection Checking values in a facet narrows the selection to entities matching all checked facets — an entity must match at least one checked value in every facet you've touched. Each facet's own options and counts reflect what the *other* checked facets have already narrowed to, so a facet never hides its own checked values. The result count updates live, and the **canvas selection itself is narrowed to match** — filtering actually deselects non-matching entities rather than just changing what's displayed, so the filtered result is ready for bulk editing immediately. ## Related - [Match Properties](./match-properties) — copy properties from one entity to others, once narrowed down - [LayerIsolate](./layer-isolate) — a layer-wide alternative when isolating by layer alone, independent of the current selection --- # Distance Tracking Distance Tracking lets you place a point by typing an exact length instead of clicking. It is controlled by the **Dist** toggle in the control bar, next to Pins and ANGL, and is on by default with the setting persisting across sessions. What it adds is narrow but useful: it lets the most recent vector pin act as the anchor that angle tracking measures from. Without it a command can only measure from a point it has already collected itself, which means the first point of a shape has nothing to measure from at all. ## The three toggles work together | Toggle | Role | |--------|------| | Pins | Supplies the reference point — hover a snap point for 500 ms to pin it | | ANGL | Supplies the angle; distance entry only becomes available once the cursor is angle-locked, so ANGL must be set to a step (10°, 20°, 30°, 45°, 90°) rather than Off | | Dist | Allows the pin to be used as the anchor rather than only the command's own point | With Pins and Dist on but ANGL set to Off, nothing happens: there is no locked direction to measure a length along. ## How Pins and Dist are coupled Distance tracking is meaningless with pins switched off, so the two toggles are kept in step: turning Pins on turns Dist on; turning Pins off turns Dist off; turning Dist on turns Pins on if it wasn't already; turning Dist off leaves Pins on. Dist can never be active while Pins is inactive, but pin tracking can be kept for alignment while distance tracking is switched off. ## Placing a point at an exact distance 1. Turn on Pins and Dist, and set ANGL to an angle step. 2. Start a command that asks for a point. 3. Pin a reference point: hover an existing snap point until the marker turns into a filled square. 4. Move the cursor away from the pin at roughly the angle you want; when it comes close to one of the ANGL steps the direction locks and a tracking indicator appears from the pin. 5. Type the length and press Enter or Space. The point is placed exactly that far from the pin, along the locked angle. While locked the terminal prompt reads `pick start point or enter length: [ ]` and the typed value appears in the brackets. ## Why the first point matters To start a line exactly 250 units right of an existing corner: start Line, pin the corner, move right until the direction locks at 0°, type `250`, press Enter. Without Dist the Line command has collected no points yet, so there is nothing for a typed length to be measured from — you could only click approximately or draw a construction line and delete it afterwards. For the second and later points the command already has its own anchor (the previous point) and that is used first; the pin is consulted as an alternative only when your own anchor is not locked, so pinning something does not hijack a lock you already have. ## Typing freezes the lock Once digits start arriving the anchor stops changing. Whichever point was locked when the first digit landed stays the anchor until the value is committed or cleared — moving the mouse mid-entry will not silently swap the measurement to a different pin or to the command's own point. ## Keyboard reference | Key | Action | |-----|--------| | `0`–`9`, `.` | Append to the length | | `-` | Negative length — reverses direction along the locked angle (first character only) | | `Backspace` | Delete the last character | | `Enter` / `Space` | Place the point at the typed length | | `Escape` | Cancel the command; the lock and typed value are cleared | Typing a length is optional — with the direction locked you can still click, and the point is projected onto the locked angle. ## Where it works Every command that asks you to pick points: Line, Polyline, Arc, Circle, Ellipse, Rectangle, SplineCv, SplineFit, Leader, Area, Move, Copy and ViewportCopy. ## Related - [Vector Pins](./vector-pins) — pinning points and tracking along their reference lines - [Grid & Snap](./grid-snap) — the other precision aids in the control bar - [Distance](./distance) — measuring an existing distance rather than typing a new one