# @crystaldesign/planner-2d-core

Browser-free core of the 2D planner: article SVG parsing, docking logic, dock transform math and collision geometry.
No fabric.js, no engine, no catalog API — runs in the browser and in plain Node (backend services, unit tests, agent testbeds).

Extraction concept and phase plan: [docs/2d-planner-rework/CONCEPT.md](../../docs/2d-planner-rework/CONCEPT.md).
The ported logic is a behavior-faithful extraction of `web-planner-logic`'s `DockingPointHandler`, the fabric-free
`SVGUtils` methods and the docking math of `DivaStandardPlanner.updateWorldPos` (phase P1 — web-planner-logic still
carries its own copy until P2 switches it over to this package).

## Modules

| Module | Contents |
| --- | --- |
| `svg/` | `getDockingPointLines`, `getPolygonPoints`, `getOltGeometry`/`getOlfGeometry`, `removeOlf`/`removeMask`, `getMinBorder` (DOM-based, Node-safe), `pathBoundingBox` (full command set M L H V C S Q T A Z incl. Bezier extrema and arcs) |
| `stretch/` | `stretchArticleSvg` — synchronous SAH/SAV stretch engine, behavior-faithful rewrite of the legacy `ccCheckStretchAreas` (OLT-then-OLF double pass, rounding rules, global-scale mode) with axis-abstracted element/path transformers and a `warnings[]` report; deviations: unknown path commands survive, unsupported stretch areas are ignored, viewBox scaling is applied exactly once |
| `docking/` | `DockingPoint`, `DockingPointHandler` (pairing rules, `checkDPsDoMatch` public), narrow `PlannerElement2D` interface |
| `transform/` | `computeDockedTransform` — pure world-transform math for a docked element |
| `collision/` | `segmentsIntersect` (fabric parity), polygon intersection/containment, `outlinesCollide` |
| `testbed/` | `DockingScene` + `SimplePlannerElement` — SVGs in, docking points/pairings/world positions out |

## Node usage

The parser needs a DOM. In the browser nothing is required; in Node a jsdom window is loaded lazily as fallback
(`configureDom(new JSDOM('').window)` only when a custom DOM should be used):

```ts
import { DockingScene } from '@crystaldesign/planner-2d-core';

const scene = new DockingScene();
const sofa = scene.addArticle(sofaSvg, { layer: 20 });
const extension = scene.addArticle(extensionSvg, { layer: 20 });
const result = scene.tryDock(extension, sofa); // -> { docked, dockingType, rotDeg, worldPosCorner }
```

Logging is a no-op by default; inject one via `setPlanner2DLogger` (e.g. a DivaUtils/diva-backend-commons logger).

## Testing

```
ctra test @crystaldesign/planner-2d-core
```

The suites run under `testEnvironment: node` with explicitly configured jsdom — proving the browser-free contract on every run.
