@chanmeng666/archlang 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -243,6 +243,163 @@ interface PlanNode {
243
243
  body: Statement[];
244
244
  }
245
245
 
246
+ /** Pure geometry helpers. All coordinates in millimetres. Deterministic. */
247
+
248
+ interface Bounds {
249
+ minX: number;
250
+ minY: number;
251
+ maxX: number;
252
+ maxY: number;
253
+ }
254
+ interface WallSegment {
255
+ a: Point;
256
+ b: Point;
257
+ thickness: number;
258
+ category: string;
259
+ }
260
+
261
+ /**
262
+ * Backend-neutral **Scene IR** — the keystone drawing intermediate.
263
+ *
264
+ * `resolve()` produces semantic geometry (rooms, walls, openings); this module
265
+ * defines a flat list of *positioned drawing primitives* tagged with a layer and
266
+ * paint. Geometry is computed exactly **once** here (by the elements, lowered via
267
+ * `scene-build.ts`); every backend (SVG, DXF, PDF, …) is then a thin, pure
268
+ * serializer of the same `Scene`. This kills the geometry duplication the
269
+ * string-based `RenderOp` forced (DXF re-deriving door arcs, PDF rasterizing SVG).
270
+ *
271
+ * Prior art: Typst's `Frame`/`FrameItem` (crates/typst-library/src/layout/frame.rs)
272
+ * — a positioned list of drawable items — and D2's `d2target` (a flat, pointer-free
273
+ * render target consumed by independent backends). ArchLang has no nested
274
+ * transforms, so unlike Typst's `Frame` the node list is flat (no sub-frames).
275
+ *
276
+ * Phase v0.7 keeps this deliberately small: line-weight/line-type/named-layer
277
+ * metadata, hatch primitives, and circles are intentionally deferred to Phase v0.9
278
+ * (roadmap §6). Poché stays an SVG `<pattern>` fill string; page chrome (north
279
+ * arrow, scale bar, title block) stays in the backends for now.
280
+ */
281
+
282
+ /**
283
+ * Ordered draw layers. Nodes are bucketed by `layer` and emitted in this order,
284
+ * preserving collection order within a layer — this exactly reproduces the v0.1
285
+ * global draw order (all wall fills, then all wall faces, doors before windows,
286
+ * labels above fills, …). Doubles as the discriminant of {@link SceneNode.layer}.
287
+ */
288
+ declare const RENDER_PASSES: readonly ["floor", "furniture", "wallFill", "wallFace", "doors", "windows", "labels", "dims", "annotations"];
289
+ type RenderPass = (typeof RENDER_PASSES)[number];
290
+ /** Render-derived sizes (in mm), scaled from the drawing's reference dimension. */
291
+ interface RenderSizes {
292
+ refDim: number;
293
+ wallStroke: number;
294
+ thin: number;
295
+ roomFont: number;
296
+ areaFont: number;
297
+ dimFont: number;
298
+ furnFont: number;
299
+ margin: number;
300
+ hatchGap: number;
301
+ }
302
+ /**
303
+ * How a primitive is painted. Strokes/fills carry colours (already theme-resolved
304
+ * and escaped at the serialization boundary); `width`/`dash` carry *raw* numbers
305
+ * that each backend formats. The optional `linecap`/`linejoin`/`fillRule` cover
306
+ * the exact SVG attributes the original element emitters used, so the SVG
307
+ * serializer reproduces today's output byte-for-byte.
308
+ */
309
+ interface Paint {
310
+ /** A colour, `"none"`, or an SVG pattern ref like `"url(#poche)"`. */
311
+ fill?: string;
312
+ stroke?: string;
313
+ /** Raw stroke width in mm (backend applies its own number formatting). */
314
+ width?: number;
315
+ /** `stroke-dasharray` pair in mm (e.g. door swing arc). */
316
+ dash?: [number, number];
317
+ linecap?: "square";
318
+ linejoin?: "miter";
319
+ fillRule?: "nonzero";
320
+ }
321
+ /**
322
+ * A positioned drawing primitive. Coordinates are absolute millimetres in the
323
+ * plan's space (origin top-left, +x right, +y down — SVG convention); backends
324
+ * apply their own transforms (e.g. DXF's Y-flip).
325
+ */
326
+ type ScenePrim =
327
+ /** A filled/stroked closed polygon (room, furniture, column, opening cover, per-segment wall fill). */
328
+ {
329
+ t: "polygon";
330
+ pts: Point[];
331
+ }
332
+ /** A single straight segment (wall face, door leaf, window pane, dimension lines/ticks). */
333
+ | {
334
+ t: "line";
335
+ a: Point;
336
+ b: Point;
337
+ }
338
+ /**
339
+ * A multi-loop closed region drawn as one path (`fill-rule` nonzero), used for
340
+ * unioned orthogonal walls so the poché fills with proper holes and the outline
341
+ * has no internal seams.
342
+ */
343
+ | {
344
+ t: "region";
345
+ loops: Point[][];
346
+ }
347
+ /**
348
+ * A circular arc (door swing). Carries the `center`/`r` a CAD backend needs to
349
+ * emit a native arc, plus the explicit `start`/`end` points + `sweep` flag an
350
+ * SVG `A` command needs — so neither backend re-derives endpoints from trig.
351
+ */
352
+ | {
353
+ t: "arc";
354
+ center: Point;
355
+ r: number;
356
+ start: Point;
357
+ end: Point;
358
+ sweep: 0 | 1;
359
+ }
360
+ /** A text label. `value` is the raw (unescaped) string; backends escape on emit. */
361
+ | {
362
+ t: "text";
363
+ at: Point;
364
+ value: string;
365
+ size: number;
366
+ anchor: "start" | "middle" | "end";
367
+ baseline: "central";
368
+ /** SVG `font-weight` (e.g. 600 for a room name). */
369
+ weight?: number;
370
+ /** Rotation in degrees about `at` (e.g. dimension text along its line). */
371
+ rotate?: number;
372
+ };
373
+ /** One drawable: a primitive on a layer, with paint and an optional source span. */
374
+ interface SceneNode {
375
+ layer: RenderPass;
376
+ prim: ScenePrim;
377
+ paint: Paint;
378
+ span?: Span;
379
+ }
380
+ /**
381
+ * A complete, backend-neutral drawing. `nodes` is the geometry; the remaining
382
+ * fields are the page-level context backends need (viewBox sizing, theme colours
383
+ * for chrome, north/scale/title block, hatch materials in use). Theme is baked
384
+ * into node paint already — it is carried here only for the page chrome.
385
+ */
386
+ interface Scene {
387
+ /** Padded page width/height in mm (drawing extent + annotation margin). */
388
+ width: number;
389
+ height: number;
390
+ /** Tight drawing bounds (before margin), for chrome placement. */
391
+ bounds: Bounds;
392
+ nodes: SceneNode[];
393
+ theme: Theme;
394
+ sizes: RenderSizes;
395
+ north: NorthDir;
396
+ scale?: string;
397
+ title?: TitleNode;
398
+ name: string;
399
+ /** Distinct wall materials in use (stable order), so the SVG backend can emit hatch `<pattern>`s. */
400
+ materials: string[];
401
+ }
402
+
246
403
  interface CompileError {
247
404
  /** Human-readable message. */
248
405
  message: string;
@@ -280,15 +437,12 @@ interface CompileResult {
280
437
  diagnostics: Diagnostic[];
281
438
  /** The validated AST, present whenever parsing succeeded. */
282
439
  ast?: PlanNode;
283
- }
284
-
285
- /** Pure geometry helpers. All coordinates in millimetres. Deterministic. */
286
-
287
- interface WallSegment {
288
- a: Point;
289
- b: Point;
290
- thickness: number;
291
- category: string;
440
+ /**
441
+ * The backend-neutral Scene IR (positioned drawing primitives), present
442
+ * whenever rendering succeeded (i.e. no fatal errors). Feed it to alternate
443
+ * backends: `toDxf(scene)`, `toPdf(scene)`.
444
+ */
445
+ scene?: Scene;
292
446
  }
293
447
 
294
448
  /**
@@ -381,29 +535,61 @@ declare function resolve(ast: PlanNode): {
381
535
  };
382
536
 
383
537
  /**
384
- * DXF export backend consumes the resolved IR and emits ASCII DXF (R12 /
385
- * AC1009, the most broadly importable flavor). Pure, synchronous, zero-dep:
386
- * DXF is plain text, so this needs no external library and is safe to ship in
387
- * the core. It is NOT part of `compile()` — call it on the IR from `resolve()`.
538
+ * Lowers a resolved plan (IR) to the backend-neutral {@link Scene}.
388
539
  *
389
- * DXF's Y axis points up, while ArchLang's Y points down (SVG convention), so
390
- * every coordinate's Y is negated here to keep plans right-side-up in CAD.
540
+ * This is the single place geometry is assembled: each element contributes
541
+ * positioned primitives via its registry `render`, walls are unioned/offset here
542
+ * (the only element needing cross-segment treatment), and the page-level sizing
543
+ * (reference dimension, derived font/stroke sizes, bounds) is computed once and
544
+ * carried on the Scene for the backends. Pure & deterministic — no I/O, no time.
391
545
  */
392
546
 
393
- /** Render a resolved plan as an ASCII DXF document string. */
394
- declare function toDxf(ir: ResolvedPlan): string;
547
+ /**
548
+ * Build the {@link Scene} for a resolved plan. The theme is merged + sanitized
549
+ * once here and baked into node paint; it is also carried on the Scene for the
550
+ * page chrome (north/scale/title). `opts.width` does not affect the Scene (it is
551
+ * an SVG-only attribute) — only `opts.theme` participates.
552
+ */
553
+ declare function toScene(ir: ResolvedPlan, opts?: CompileOptions): Scene;
395
554
 
396
555
  /**
397
- * PDF export backend — consumes the produced SVG and renders it into a PDF via
398
- * pdfkit + svg-to-pdfkit. These are OPTIONAL dependencies, lazy-`import()`ed so
399
- * the zero-dep core never hard-requires them; a clear error is thrown if they
400
- * are absent. This is async and Node-oriented NOT part of `compile()`.
556
+ * DXF export backend — a pure serializer of the {@link Scene}. Emits ASCII DXF
557
+ * (R12 / AC1009, the most broadly importable flavor). Pure, synchronous,
558
+ * zero-dep: DXF is plain text, so this needs no external library and ships in the
559
+ * core. Build a Scene with `toScene(resolve(ast).ir)` (or `compile().scene`).
560
+ *
561
+ * As of v0.7 the geometry is NOT re-derived here: door arcs, window panes, and
562
+ * dimension ticks are the very `ScenePrim`s the elements produced. Each primitive
563
+ * maps generically to a DXF entity; the only element-aware step is mapping a draw
564
+ * layer to a DXF layer name. DXF's Y axis points up while ArchLang's points down,
565
+ * so every Y is negated to keep plans right-side-up in CAD.
401
566
  */
567
+
568
+ /** Render a {@link Scene} as an ASCII DXF document string. */
569
+ declare function toDxf(scene: Scene): string;
570
+
402
571
  /**
403
- * Convert an ArchLang SVG string to a PDF (returned as a Uint8Array).
404
- * Requires the optional `pdfkit` and `svg-to-pdfkit` packages.
572
+ * PDF export backend a **true vector** serializer of the {@link Scene}.
573
+ *
574
+ * Walks the Scene's positioned primitives into pdfkit drawing ops, so strokes
575
+ * are real vector paths and text is selectable (no SVG rasterization round-trip).
576
+ * `pdfkit` is an OPTIONAL dependency, lazy-`import()`ed so the zero-dep core never
577
+ * hard-requires it; a clear error is thrown if it is absent. Async + Node-oriented
578
+ * — NOT part of `compile()`. Build a Scene with `toScene(ir)` or `compile().scene`.
579
+ *
580
+ * Coordinates: ArchLang is mm, top-left origin, +y down — pdfkit's user space is
581
+ * the same orientation, so we map the viewBox by translating by its top-left and
582
+ * treat 1mm as 1pt (as the previous SVG-based exporter did with `assumePt`).
583
+ *
584
+ * Page chrome (north arrow, scale bar, title block) is drawn with PDF-native
585
+ * helpers to keep parity with the SVG output. This duplicates the chrome geometry
586
+ * (also in `backends/svg.ts`) — a deliberate, bounded cost until chrome itself
587
+ * moves into the Scene in a later phase. Hatch patterns are SVG-specific, so
588
+ * poché regions fill with the solid poché base colour in PDF.
405
589
  */
406
- declare function toPdf(svg: string): Promise<Uint8Array>;
590
+
591
+ /** Convert a {@link Scene} to a vector PDF (Uint8Array). Requires optional `pdfkit`. */
592
+ declare function toPdf(scene: Scene): Promise<Uint8Array>;
407
593
 
408
594
  /**
409
595
  * ArchLang — compile declarative floor-plan source to a professional SVG.
@@ -417,4 +603,4 @@ declare function compile(source: string, opts?: CompileOptions): CompileResult;
417
603
  /** Clear the internal compile cache (useful in long-lived processes/tests). */
418
604
  declare function clearCache(): void;
419
605
 
420
- export { type AstElement, type ColumnNode, type CompileError, type CompileOptions, type CompileResult, type CompileWarning, type ComponentDef, type Diagnostic, type DimNode, type DoorNode, type ElementKind, type ExprPoint, type FurnitureNode, type InstanceNode, type LetNode, type NodeBase, type NorthDir, type PlanNode, type Point, type RColumn, type RDim, type RDoor, type RFurniture, type RRoom, type RWall, type RWindow, type ResolvedElement, type ResolvedPlan, type RoomNode, type Severity, type Span, type Statement, type TitleNode, type WallNode, type WindowNode, clearCache, compile, formatDiagnostic, offsetToLineCol, resolve, toDxf, toPdf };
606
+ export { type AstElement, type ColumnNode, type CompileError, type CompileOptions, type CompileResult, type CompileWarning, type ComponentDef, type Diagnostic, type DimNode, type DoorNode, type ElementKind, type ExprPoint, type FurnitureNode, type InstanceNode, type LetNode, type NodeBase, type NorthDir, type Paint, type PlanNode, type Point, type RColumn, type RDim, type RDoor, type RFurniture, type RRoom, type RWall, type RWindow, type RenderPass, type RenderSizes, type ResolvedElement, type ResolvedPlan, type RoomNode, type Scene, type SceneNode, type ScenePrim, type Severity, type Span, type Statement, type TitleNode, type WallNode, type WindowNode, clearCache, compile, formatDiagnostic, offsetToLineCol, resolve, toDxf, toPdf, toScene };
package/dist/index.js CHANGED
@@ -5,8 +5,9 @@ import {
5
5
  offsetToLineCol,
6
6
  resolve,
7
7
  toDxf,
8
- toPdf
9
- } from "./chunk-PABYLU6Z.js";
8
+ toPdf,
9
+ toScene
10
+ } from "./chunk-CPK5CI5Y.js";
10
11
  export {
11
12
  clearCache,
12
13
  compile,
@@ -14,6 +15,7 @@ export {
14
15
  offsetToLineCol,
15
16
  resolve,
16
17
  toDxf,
17
- toPdf
18
+ toPdf,
19
+ toScene
18
20
  };
19
21
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chanmeng666/archlang",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "A small declarative language that compiles to professional SVG floor plans — like Typst/LaTeX, but for architecture.",
5
5
  "keywords": [
6
6
  "architecture",
@@ -66,7 +66,6 @@
66
66
  "vscode-textmate": "^9.3.2"
67
67
  },
68
68
  "optionalDependencies": {
69
- "pdfkit": "^0.15.2",
70
- "svg-to-pdfkit": "^0.1.8"
69
+ "pdfkit": "^0.15.2"
71
70
  }
72
71
  }