cut_out
Cut a hole in a floor area shape or clip a wall run by drawing a ring; subtracts the region and reconciles overlaps so quantities stay accurate.
Instructions
Cut a REAL hole in a committed floor_area shape (#206) — the way the canvas cuts one (#137): the same lib/cutout.js boolean subtract, so the two surfaces can never disagree about what a hole holds. The parent keeps its outer ring plus the reconciled hole(s) (verts_norm_holes), its computed nets for real — N cuts compose, overlap between cuts never double-deducts (set subtraction), a hole ADDS perimeter — and the deduct commits carrying cuts_shape_id so the report and legend read the reconciled number, never a second arithmetic pass. This is the verb for a column, a floor drain, an island of casework INSIDE a room; an independent measure_polygon role:"deduct" stays the tool for a deduction that isn't a hole in one parent. Refusal over guessing: the ring must sit FULLY inside the parent's outer ring (an edge-crossing cut is a boundary correction — edit_shape the parent instead), and a cut that would erase the parent or split it in two refuses whole (trace the pieces as rooms). One journal entry — undo_last restores parent and hole together; delete_shape on the deduct later reverts the cut too (a multi-cut parent rebuilds from the chain's pristine snapshot minus the survivors). AN OPEN RUN IS CLIPPED, NOT SUBTRACTED: wall tile (surface_area) and base/transitions (linear) are polylines traced in plan, so the ring removes the stretch it covers, the run keeps its id and takes what survives, and a cut through the MIDDLE leaves the far side as its own shape (same condition, same height) — quantities ride the surviving length, which is exact, since wall SF is LF × height and a border's SF is LF × thickness. No deduct is minted for a run: there is no area for one to sit on, and a deduct's SF counts against the FLOOR total a run never fills. A ring that misses the run, one that swallows it whole (delete_shape it), and a curved run (its verts are control points) all refuse. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| verts | Yes | The ring, image px — fully inside the parent for an area; over the stretch to remove for a run | |
| parent_shape_id | Yes | A committed floor_area shape id, or an open run (surface_area / linear) to clip (list_shapes) |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| note | Yes | ||
| holes | No | Area parent: holes the parent now carries | |
| pieces | No | Run parent: every stretch that survived the cut — more than one when the ring fell in the middle | |
| hole_sf | No | Area parent: what this cut actually removed from the parent's net — 0 when the ring fell entirely inside an existing hole | |
| shape_id | No | Run parent: the run that was clipped — it keeps its id and takes the first surviving stretch | |
| parent_net | No | ||
| removed_lf | No | Run parent: length the cut took out | |
| removed_sf | No | Run parent: the SF that rode on that length | |
| measure_role | No | Run parent: surface_area or linear | |
| deduct_shape_id | No | Area parent: the reconciled deduct — carries cuts_shape_id; totals skip it (the parent nets the hole) | |
| parent_shape_id | No |