Sign In

@aspicio/core

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@aspicio/core - npm Package Compare versions

Comparing version
0.12.0
to
0.13.0
+46
dist/layers-D5Qs4TFr.mjs
//#region src/layers.ts
/**
* A layer with no rendered entities — the LAYER-table entries (the default
* "0", "Defpoints", …) that no drawn geometry references. `entityCount` is
* scoped to one space (see {@link countEntitiesByLayer}), so a layer counts as
* empty when the *active* space draws nothing on it — a sheet-only layer is
* empty while model space is shown, and vice versa.
*
* The single definition lives here so every presentation surface — the demo
* sidebar and the in-chat viewer widget — classifies layers the same way and
* can't drift.
*/
function isEmptyLayer(layer) {
return layer.entityCount === 0;
}
/** Split layers into those with rendered geometry and the empty ones (see
* {@link isEmptyLayer}), preserving the original order within each group. */
function partitionLayers(layers) {
const rendered = [];
const empty = [];
for (const layer of layers) (isEmptyLayer(layer) ? empty : rendered).push(layer);
return {
rendered,
empty
};
}
/**
* Top-level entities per layer, for one space.
*
* The single definition of what an entity count *is*: every entity carries
* exactly one layer, so each contributes 1 to exactly one bucket and the
* buckets sum to `entities.length`. That is what keeps a layer panel's rows
* and the total beside them two views of one number — they used to be
* accumulated independently, and disagreed on any drawing with more than one
* space.
*
* Block contents are not counted: an INSERT is one entity on its own layer,
* however much geometry it expands into.
*/
function countEntitiesByLayer(entities) {
const counts = /* @__PURE__ */ new Map();
for (const entity of entities) counts.set(entity.layer, (counts.get(entity.layer) ?? 0) + 1);
return counts;
}
//#endregion
export { isEmptyLayer as n, partitionLayers as r, countEntitiesByLayer as t };
//#region src/model/types.d.ts
/** Normalized document model. Decoupled from the parser's output shape. */
interface Point2 {
x: number;
y: number;
}
interface Point3 {
x: number;
y: number;
z: number;
}
/** 2D affine transform: [a, b, c, d, tx, ty] mapping (x,y) → (a·x+c·y+tx, b·x+d·y+ty). */
type Affine2D = [number, number, number, number, number, number];
interface Bounds {
minX: number;
minY: number;
maxX: number;
maxY: number;
}
interface LayerInfo {
name: string;
/** Layer-table color, 24-bit RGB. May differ from what is drawn. */
color: number;
/**
* Colors actually drawn on this layer, dominant first (populated after
* tessellation). Entity-styled files override the table color per entity,
* so UI should prefer `effectiveColors[0]` over `color`.
*/
effectiveColors?: number[];
visible: boolean;
frozen: boolean;
/** Number of top-level entities on this layer. */
entityCount: number;
/** Layer's default linetype name (resolved against the document map). */
lineType?: string;
/**
* Layer's default lineweight in 1/100 mm (DXF group 370). Negative codes
* (-3 default, -2 ByBlock, -1 ByLayer) are dropped to `undefined`.
*/
lineWeight?: number;
}
interface EntityBase {
layer: string;
/** Resolved 24-bit RGB, or null for ByLayer/ByBlock. */
color: number | null;
/**
* OCS extrusion normal (codes 210/220/230) for entity types whose
* coordinates are OCS-relative (ARC, POLYLINE, INSERT). Undefined means
* the default +Z (world coordinates). (0,0,-1) marks mirrored entities.
*/
extrusion?: Point3;
/**
* Linetype name, or "BYLAYER"/undefined to inherit the layer's. Resolved
* against the document's `lineTypes` map to a dash pattern at render time.
*/
lineType?: string;
/**
* Lineweight in 1/100 mm (DXF group 370), or undefined to inherit the
* layer's. Negative "ByLayer/ByBlock/default" codes are dropped to
* undefined so the layer default applies.
*/
lineWeight?: number;
}
/**
* Linetype dash pattern: alternating drawn/gap lengths in drawing units.
* Positive = dash (pen down), negative = gap (pen up), 0 = dot.
*/
interface LineTypeDef {
name: string;
pattern: number[];
/** Sum of |pattern|; 0 for a continuous line. */
patternLength: number;
}
interface LineEntity extends EntityBase {
type: "LINE";
start: Point2;
end: Point2;
}
interface PolylineEntity extends EntityBase {
type: "POLYLINE";
points: Point2[];
/** Bulge per segment starting at points[i]; same length as points. */
bulges: number[];
closed: boolean;
}
interface CircleEntity extends EntityBase {
type: "CIRCLE";
center: Point2;
radius: number;
}
interface ArcEntity extends EntityBase {
type: "ARC";
center: Point2;
radius: number;
/** Radians, CCW from +X. */
startAngle: number;
endAngle: number;
}
interface EllipseEntity extends EntityBase {
type: "ELLIPSE";
center: Point2;
/** Major axis endpoint relative to center. */
majorAxis: Point2;
/** Minor/major ratio. */
axisRatio: number;
/** Parametric range in radians. */
startParam: number;
endParam: number;
}
interface InsertEntity extends EntityBase {
type: "INSERT";
blockName: string;
position: Point2;
scale: Point2;
/** Radians. */
rotation: number;
}
type TextHAlign = "left" | "center" | "right";
type TextVAlign = "baseline" | "bottom" | "middle" | "top";
/** Normalized TEXT and MTEXT. MTEXT format codes are collapsed to plain text. */
interface TextEntity extends EntityBase {
type: "TEXT";
/** Insertion/alignment point. */
position: Point2;
/** Content; may contain newlines (from MTEXT paragraphs). */
text: string;
/** Cap height in drawing units. */
height: number;
/** Radians, CCW. */
rotation: number;
/** Horizontal scale (DXF xScale). */
widthFactor: number;
hAlign: TextHAlign;
vAlign: TextVAlign;
}
interface SplineEntity extends EntityBase {
type: "SPLINE";
controlPoints: Point2[];
/** Knot vector; empty means "generate a clamped uniform vector". */
knots: number[];
degree: number;
closed: boolean;
}
/** Filled triangle/quad: SOLID, TRACE, or a projected 3DFACE. */
interface SolidEntity extends EntityBase {
type: "SOLID";
/** 3 or 4 corners, already reordered to a simple (non-crossing) ring. */
points: Point2[];
}
/** A POINT — rendered as a small crosshair marker. */
interface PointEntity extends EntityBase {
type: "POINT";
position: Point2;
}
/** DIMENSION — rendered by drawing its anonymous geometry block. */
interface DimensionEntity extends EntityBase {
type: "DIMENSION";
/** Name of the anonymous "*D…" block holding the lines, arrows, and text. */
block: string;
/** Block insertion point (usually the origin). */
position: Point2;
}
/** HATCH — filled region(s). Boundaries are pre-sampled to polyline loops. */
interface HatchEntity extends EntityBase {
type: "HATCH";
/** Boundary loops in drawing coordinates (outer + holes, unspecified order). */
loops: Point2[][];
/** Solid fill vs. a line pattern (patterns render as boundary outlines). */
solid: boolean;
}
/** Decoded raster pixels carried by an IMAGE entity. */
interface RasterImage {
/** Pixel dimensions. */
width: number;
height: number;
/**
* RGBA bytes, 4 per pixel, rows top-to-bottom (row 0 is the image's top
* edge), pixels left-to-right. Alpha is straight, not premultiplied.
*/
rgba: Uint8ClampedArray;
}
/**
* A placed raster image (PDF image XObjects; DXF IMAGE is future work).
*
* Placement follows PDF's convention: the image occupies the unit square in
* its own space — (0,0) bottom-left, (1,1) top-right, so the top pixel row
* lies along v=1 — and `transform` maps that square into drawing space.
*/
interface ImageEntity extends EntityBase {
type: "IMAGE";
/** Unit square → drawing space. */
transform: Affine2D;
image: RasterImage;
}
type Entity = LineEntity | PolylineEntity | CircleEntity | ArcEntity | EllipseEntity | InsertEntity | TextEntity | SplineEntity | SolidEntity | PointEntity | DimensionEntity | HatchEntity | ImageEntity;
type EntityType = Entity["type"];
interface BlockDef {
name: string;
basePoint: Point2;
entities: Entity[];
}
/** A paper-space viewport: a window that frames model space at a fixed scale. */
interface Viewport {
/** Center of the window on the paper (paper coords). */
center: Point2;
/** Window size on the paper. */
width: number;
height: number;
/** Model point shown at the window center. */
viewCenter: Point2;
/** Model-space height visible through the window (drives the scale). */
viewHeight: number;
/** View twist, radians (CCW). */
twist: number;
}
/**
* The printable geometry of a *bounded* space — today, a PDF page.
*
* DXF model space is unbounded and has none of this, which is the whole
* distinction: a space either declares where the paper is or it does not,
* and everything downstream (backdrop, fit, guides) keys off that rather
* than off the document's format.
*/
interface PageGeometry {
/**
* The sheet. From PDF's CropBox intersected with its MediaBox — the box
* Acrobat and Preview display, not the larger media the file was imposed
* on — already carried through the page's `/Rotate` transform, so it is
* axis-aligned in the same coordinates as the entities beside it.
*/
sheet: Bounds;
/** Finished size after cutting, when the file declares a TrimBox. */
trim?: Bounds;
/** How far artwork must run past the trim, when a BleedBox is declared. */
bleed?: Bounds;
}
/** A paper-space layout: a printable sheet with its own geometry and viewports. */
interface Layout {
name: string;
/** Drawable paper-space geometry (titleblock, borders, text). */
entities: Entity[];
/** Windows into model space. */
viewports: Viewport[];
/**
* Page geometry, for layouts that are bounded pages (PDF). DXF layouts
* omit it — their sheet is drawn as ordinary entities by the producer.
*/
page?: PageGeometry;
}
interface DrawingDocument {
layers: Map<string, LayerInfo>;
entities: Entity[];
blocks: Map<string, BlockDef>;
/** Linetype definitions from the LTYPE table, keyed by name. */
lineTypes: Map<string, LineTypeDef>;
/** Counts of raw DXF entity types that were skipped by the parser stage. */
unsupported: Record<string, number>;
/**
* Short drawing-unit label from the header's `$INSUNITS` (e.g. "mm", "in"),
* or "" when the drawing is unitless or the code is unknown. `parseDxf`
* always sets it; hand-built documents may omit it (treated as "").
*/
units?: string;
/**
* Paper-space layouts, if any. `entities` holds model space; each layout
* carries its own paper geometry and viewports. `parseDxf` sets it (possibly
* empty); hand-built documents may omit it (treated as no layouts).
*/
layouts?: Layout[];
/**
* Which format produced this document ("dxf", "pdf") — so every surface can
* report it without re-sniffing the bytes (PARSE-13). Parsers always set it;
* hand-built documents may omit it.
*/
format?: string;
/**
* Page geometry for model space, when model space is a bounded page. A PDF
* loads page 1 into `entities` (PDF-5), so the first page's box lives here
* rather than on a layout. Absent for DXF, whose model space is unbounded.
*/
page?: PageGeometry;
}
//#endregion
//#region src/parse/registry.d.ts
/** One file format's contribution: a name, a byte sniff, and a parse. */
interface DrawingParser {
/** Short lowercase format name, e.g. "dxf". Surfaces report it verbatim. */
format: string;
/**
* True when this parser claims the bytes. Sniffs see the whole buffer but
* should only look at the head — they run on every load, for every parser.
*/
sniff(bytes: Uint8Array): boolean;
/** Parse claimed bytes, or throw a `DrawingParseError` carrying `format`. */
parse(bytes: Uint8Array): DrawingDocument | Promise<DrawingDocument>;
}
/** Everything a drawing can be loaded from (PARSE-1). */
type DrawingSource = string | ArrayBuffer | Uint8Array | Blob;
/** Normalize any accepted source to bytes, so sniffs see one shape (PARSE-1). */
declare function toBytes(source: DrawingSource): Promise<Uint8Array>;
/**
* Parse `source` with the first parser whose sniff claims it (PARSE-13).
*
* Sniffs run in the order given, so a caller controls precedence by ordering
* its list. No parser claiming the bytes is a clean, honest failure, not a
* fallback attempt at every parser in turn (PARSE-12).
*/
declare function parseWith(parsers: readonly DrawingParser[], source: DrawingSource): Promise<DrawingDocument>;
//#endregion
export { TextEntity as A, Point2 as C, RasterImage as D, PolylineEntity as E, TextVAlign as M, Viewport as N, SolidEntity as O, PageGeometry as S, PointEntity as T, InsertEntity as _, Affine2D as a, LineEntity as b, Bounds as c, DrawingDocument as d, EllipseEntity as f, ImageEntity as g, HatchEntity as h, toBytes as i, TextHAlign as j, SplineEntity as k, CircleEntity as l, EntityType as m, DrawingSource as n, ArcEntity as o, Entity as p, parseWith as r, BlockDef as s, DrawingParser as t, DimensionEntity as u, LayerInfo as v, Point3 as w, LineTypeDef as x, Layout as y };
+1
-1

@@ -1,2 +0,2 @@

import { d as DrawingDocument, t as DrawingParser } from "./registry-CtIhdVSA.mjs";
import { d as DrawingDocument, t as DrawingParser } from "./registry-C4th1Vh7.mjs";

@@ -3,0 +3,0 @@ //#region src/parse/parse.d.ts

@@ -776,5 +776,8 @@ import { i as DrawingParseError, n as sampleBulge, t as sampleArc } from "./arc-CsclX-ZH.mjs";

if (!entity) continue;
ensureLayer(entity.layer).entityCount += 1;
const layer = ensureLayer(entity.layer);
if (raw.inPaperSpace) paperEntities.push(entity);
else entities.push(entity);
else {
entities.push(entity);
layer.entityCount += 1;
}
}

@@ -781,0 +784,0 @@ const blocks = /* @__PURE__ */ new Map();

@@ -1,2 +0,2 @@

import { A as Viewport, C as PointEntity, D as TextEntity, E as SplineEntity, O as TextHAlign, S as Point3, T as SolidEntity, _ as LayerInfo, a as Affine2D, b as LineTypeDef, c as Bounds, d as DrawingDocument, f as EllipseEntity, g as InsertEntity, h as HatchEntity, i as toBytes, k as TextVAlign, l as CircleEntity, m as EntityType, n as DrawingSource, o as ArcEntity, p as Entity, r as parseWith, s as BlockDef, t as DrawingParser, u as DimensionEntity, v as Layout, w as PolylineEntity, x as Point2, y as LineEntity } from "./registry-CtIhdVSA.mjs";
import { A as TextEntity, C as Point2, D as RasterImage, E as PolylineEntity, M as TextVAlign, N as Viewport, O as SolidEntity, S as PageGeometry, T as PointEntity, _ as InsertEntity, a as Affine2D, b as LineEntity, c as Bounds, d as DrawingDocument, f as EllipseEntity, g as ImageEntity, h as HatchEntity, i as toBytes, j as TextHAlign, k as SplineEntity, l as CircleEntity, m as EntityType, n as DrawingSource, o as ArcEntity, p as Entity, r as parseWith, s as BlockDef, t as DrawingParser, u as DimensionEntity, v as LayerInfo, w as Point3, x as LineTypeDef, y as Layout } from "./registry-C4th1Vh7.mjs";
import { t as DrawingParseError } from "./errors-B0HtqeWu.mjs";

@@ -34,2 +34,19 @@

/** Block recursion bound, shared by rendering and text collection. */
/**
* A raster image placed in tessellation coordinates (the shared offset
* already subtracted, like every position array).
*
* `transform` maps the unit square — (0,0) bottom-left, (1,1) top-right,
* the image's top pixel row along v=1 — into recentered drawing space.
* `corners` are the transformed unit-square corners (bl, br, tr, tl),
* precomputed because every consumer (WebGL quad, SVG placement, bounds)
* needs them.
*/
interface PlacedImage {
image: RasterImage;
transform: Affine2D;
corners: [Point2, Point2, Point2, Point2];
/** Top-level entity index (parallels `segmentIds` / `fillIds`). */
entityId: number;
}
/** Batched line-segment (and optional filled-triangle) geometry for one layer. */

@@ -51,2 +68,4 @@ interface LayerGeometry {

fillIds: Int32Array;
/** Raster images on this layer, in draw order (under fills and lines). */
images: PlacedImage[];
}

@@ -60,2 +79,4 @@ interface Tessellation {

segmentCount: number;
/** Total raster images placed. */
imageCount: number;
/**

@@ -67,2 +88,23 @@ * Colors actually drawn per layer (24-bit RGB → segment count). Unlike the

layerColors: Map<string, Map<number, number>>;
/**
* Top-level entities per layer *in this space*, and nothing outside it.
*
* It rides on the tessellation because a tessellation is exactly one space:
* a count read from here cannot have come from a different space than the
* geometry beside it, which is how the panel's rows and the total above them
* stay two views of one number. Summing the values gives the space's total.
*/
entityCounts: Map<string, number>;
/**
* The space's paper, in tessellation coordinates (the shared `offset`
* already subtracted, like every position array) — or null for an
* unbounded space, which is every DXF space.
*
* It rides beside `layers` rather than in them on purpose: the sheet is
* not a layer and must never behave like one. Everything that walks
* `layers` — picking, snapping, the layer panel, per-layer visibility —
* is therefore blind to it by construction rather than by a filter
* somebody has to remember to write (VIEW-4, VIEW-7, VIEW-9).
*/
backdrop: PageGeometry | null;
}

@@ -79,2 +121,8 @@ interface TessellationContext {

addBlock(blockName: string, transform: Affine, layer: string, color: number | null): void;
/**
* Place a raster image. `local` maps the unit square — (0,0) bottom-left,
* top pixel row along v=1 — into entity-local coordinates; the walk
* transform is applied on top.
*/
addImage(image: RasterImage, local: Affine2D): void;
readonly curveSegments: number;

@@ -87,2 +135,18 @@ }

curveSegments?: number;
/**
* Keep line work legible against this canvas colour: pen colours are
* darkened, hue intact, until they reach {@link CONTRAST_TARGET} against
* it (VIEW-18). Omit — the default — to draw the colours the file names.
*
* Applies to DXF only. An ACI index is a display attribute, so remapping
* it for a light canvas is the same kind of decision as picking its RGB
* in the first place; PDF ink is not, and passing this never changes it.
*/
legibleOn?: number;
/**
* The colour a pen darkens *towards* when it has no hue to preserve —
* the theme's ink. Only consulted alongside `legibleOn`; defaults to
* black, which is legible but colder than a warm palette's near-black.
*/
ink?: number;
}

@@ -92,2 +156,16 @@ /** Tessellate a document's model space into per-layer batched geometry. */

/**
* Tessellate a space by name — model space when `name` is omitted or
* {@link MODEL_SPACE}, otherwise the layout that carries it.
*
* Every caller that renders or describes goes through here, so "which space"
* is decided once rather than re-decided at each call site. It used to be
* re-decided at ten of them, every one hardcoding model space, which is why no
* agent surface could reach page 2 of a PDF.
*
* @throws UnknownSpaceError for a name the drawing does not have. The viewer
* does not come through here — it ignores unknown names (VIEW-14) — so nothing
* is served by a silent fallback that would quietly render the wrong sheet.
*/
declare function tessellateSpace(doc: DrawingDocument, name?: string, options?: TessellateOptions): Tessellation;
/**
* Tessellate a paper-space layout: its own geometry in paper coordinates,

@@ -146,2 +224,24 @@ * plus each viewport's model content transformed into paper coords and

/**
* Keep DXF line work legible against this canvas colour (VIEW-18). Omit —
* the default — to draw the colours the file names. PDF ink is never
* affected. See `TessellateOptions.legibleOn`.
*/
legibleOn?: number;
/** The ink a hueless pen darkens towards under `legibleOn`. See VIEW-18. */
ink?: number;
/**
* The paper drawn under a space that declares a page box — a PDF page
* (VIEW-17). 24-bit RGB, or null to draw no sheet. Default: white.
*
* White, and not an off-white "paper" tint, because in a PDF the sheet is
* the *unpainted* region: artwork that paints 0/0/0/0 white — knockout
* type, a barcode's quiet zone — would otherwise appear as a visible
* rectangle against the paper it is supposed to match. Substrate
* simulation is a soft-proof feature with a stated white point, not a
* default backdrop. Spaces with no page box are unaffected.
*/
sheet?: number | null;
/** Hairline drawn around the sheet. 24-bit RGB, or null (default) for none. */
sheetEdge?: number | null;
/**
* The formats this viewer accepts, tried in order (PARSE-13, VIEW-15).

@@ -159,3 +259,14 @@ * Core imports no parser of its own — pass `dxfParser` from

interface ViewerStats {
/**
* Top-level entities in the space on screen — the same scope as
* `segmentCount`, and equal to the sum of the layer counts beside it
* (VIEW-16). Switching spaces changes it.
*/
entityCount: number;
/**
* Top-level entities across every space. Only the whole-drawing question
* needs this — chiefly "did anything at all load", which `entityCount`
* cannot answer for a drawing whose first space happens to be blank.
*/
documentEntityCount: number;
segmentCount: number;

@@ -205,2 +316,4 @@ unsupported: Record<string, number>;

private activeSpace;
private legibleOn;
private ink;
private renderQueued;

@@ -224,3 +337,46 @@ private highlightedLayer;

get activeSpaceName(): string;
private tessellateOptions;
/**
* Re-tessellate the space on screen without reloading the drawing.
*
* Needed because pen colours are baked into vertex buffers: unlike the
* paper, which is one uniform, changing the canvas a drawing is judged
* against changes every vertex. The camera is deliberately left alone —
* the geometry has not moved, and refitting under the user would read as
* the viewer losing their place.
*/
private retessellate;
/**
* Re-colour the canvas for a theme switch (VIEW-17, VIEW-18).
*
* One call rather than two setters because the two halves have to agree:
* the paper and the pen colours judged against it are the same decision,
* and applying one without the other puts dark ink on a dark canvas.
*/
setCanvasColors(colors: {
sheet?: number | null;
sheetEdge?: number | null;
legibleOn?: number;
ink?: number;
select?: number;
selectOnSheet?: number;
}): void;
/**
* Repaint the paper, e.g. when the host switches theme (VIEW-17).
*
* Kept separate from the constructor options so a theme switch does not
* have to recreate the viewer: rebuilding it would drop the WebGL context,
* re-parse the drawing and reset the camera, all to change one colour.
*/
setSheetColors(sheet: number | null, edge?: number | null): void;
/**
* The active space's paper, or null when the space is unbounded (VIEW-17).
*
* Hosts need this to style the canvas *around* the sheet: a bounded page
* gets a plain surround, an unbounded space keeps whatever infinite-space
* treatment the host draws. Core states the fact and styles nothing
* itself (INV-1).
*/
get activePage(): PageGeometry | null;
/**
* Switch the displayed space to model space or a paper-space layout by name.

@@ -352,2 +508,10 @@ * Re-tessellates, re-fits, and re-renders. Unknown names are ignored.

background?: string;
/**
* Paper drawn under a bounded space (e.g. "#ffffff"), matching the canvas
* (VIEW-12, VIEW-17). Ignored when the space declares no page box, so a
* DXF export is unaffected. Omit to export page content with no sheet.
*/
sheet?: string;
/** Draw the page's trim and bleed guides, when it declares them (VIEW-19). */
guides?: boolean;
}

@@ -370,2 +534,30 @@ /**

}
/** One space's own figures, so a multi-page or multi-sheet drawing can be
* navigated without describing it six times over. */
interface SpaceSummary {
/** Space name as {@link DescribeOptions.space} accepts it. */
name: string;
entityCount: number;
segmentCount: number;
bounds: {
minX: number;
minY: number;
maxX: number;
maxY: number;
} | null;
size: {
width: number;
height: number;
} | null;
}
/** How much of a drawing to describe. */
interface DescribeOptions extends TessellateOptions {
/**
* Describe one space instead of the whole drawing — "Model" (page 1 of a
* PDF) or a layout/page name from {@link DrawingSummary.spaces}. The reply
* is then scoped to it throughout, and matches what the viewer shows on that
* tab. Omit for the whole drawing.
*/
space?: string;
}
/**

@@ -380,2 +572,8 @@ * A structured, JSON-friendly summary of a parsed drawing — what an agent or

units: string;
/**
* Which space this summary is scoped to, or null when it covers the whole
* drawing. Geometry cannot be summed across spaces, so `bounds` and `size`
* describe `spaces[0]` in that case — the same space `render` returns.
*/
space: string | null;
/** World-space extents, or null for an empty drawing. */

@@ -395,2 +593,11 @@ bounds: {

segmentCount: number;
/**
* Every space in the drawing, model space (a PDF's page 1) first, listed
* whether or not this summary is scoped to one of them.
*
* These do not sum to `entityCount`: a DXF sheet's viewports re-show model
* geometry, so an entity can be drawn in two spaces while existing once.
*/
spaces: SpaceSummary[];
/** Per-layer counts, on the same scope as `entityCount`; they sum to it. */
layers: LayerSummary[];

@@ -409,8 +616,19 @@ /** Top-level entities per DXF type, e.g. `{ LINE: 12, CIRCLE: 3 }`. */

/**
* Derive a structured {@link DrawingSummary} from a parsed document and its
* tessellation. Pure and framework-free (no DOM/WebGL) — usable in Node and
* Cloudflare Workers. Layer colors reflect what is actually drawn (entity
* overrides included), matching the viewer.
* Derive a structured {@link DrawingSummary} from a parsed document. Pure and
* framework-free (no DOM/WebGL) — usable in Node and Cloudflare Workers. Layer
* colors reflect what is actually drawn (entity overrides included), matching
* the viewer.
*
* Covers the whole drawing by default — a six-page PDF reports six pages'
* worth of entities, layers, and text, not page one's — and one space when
* `options.space` names one, which is then the same view the viewer shows on
* that tab (AGT-1).
*
* It tessellates every space to fill `spaces`, which costs a few percent of a
* parse: on a 6-page prepress PDF, 36 ms of tessellation against 814 ms of
* parsing.
*
* @throws UnknownSpaceError when `options.space` names no space in the drawing.
*/
declare function describeDrawing(doc: DrawingDocument, tessellation: Tessellation): DrawingSummary;
declare function describeDrawing(doc: DrawingDocument, options?: DescribeOptions): DrawingSummary;
//#endregion

@@ -420,5 +638,6 @@ //#region src/layers.d.ts

* A layer with no rendered entities — the LAYER-table entries (the default
* "0", "Defpoints", …) that no drawn geometry references. `entityCount` counts
* only entities that tessellated, so this also covers layers whose entities
* were all skipped (unsupported).
* "0", "Defpoints", …) that no drawn geometry references. `entityCount` is
* scoped to one space (see {@link countEntitiesByLayer}), so a layer counts as
* empty when the *active* space draws nothing on it — a sheet-only layer is
* empty while model space is shown, and vice versa.
*

@@ -436,3 +655,44 @@ * The single definition lives here so every presentation surface — the demo

};
/**
* Top-level entities per layer, for one space.
*
* The single definition of what an entity count *is*: every entity carries
* exactly one layer, so each contributes 1 to exactly one bucket and the
* buckets sum to `entities.length`. That is what keeps a layer panel's rows
* and the total beside them two views of one number — they used to be
* accumulated independently, and disagreed on any drawing with more than one
* space.
*
* Block contents are not counted: an INSERT is one entity on its own layer,
* however much geometry it expands into.
*/
declare function countEntitiesByLayer(entities: readonly Entity[]): Map<string, number>;
//#endregion
//#region src/spaces.d.ts
/** Thrown when a caller names a space the drawing does not have. */
declare class UnknownSpaceError extends Error {
constructor(name: string, available: readonly string[]);
}
/** The implicit space every drawing has; layouts are named alongside it. */
declare const MODEL_SPACE = "Model";
/** Every space this drawing offers, model space first (VIEW-14, PDF-5). */
declare function spaceNames(doc: DrawingDocument): string[];
/** The entities one space draws, or null when the drawing has no such space. */
declare function spaceEntities(doc: DrawingDocument, name?: string): Entity[] | null;
/**
* How many top-level entities the drawing holds, each counted once — the
* length of {@link documentEntities} without building it, for the callers
* that only want the number.
*/
declare function documentEntityCount(doc: DrawingDocument): number;
/**
* Every top-level entity in the drawing, each once.
*
* Not the concatenation of {@link spaceEntities} over {@link spaceNames}:
* spaces do not partition a drawing, because a sheet's viewports re-show model
* geometry that is already counted in model space. This is the "how much is in
* this file" number; a space's own count is the "what is on screen" one.
*/
declare function documentEntities(doc: DrawingDocument): Entity[];
//#endregion
//#region src/camera/camera2d.d.ts

@@ -597,2 +857,2 @@ /**

//#endregion
export { type ArcEntity, type BlockDef, type Bounds, Camera2D, type CircleEntity, type DimensionEntity, type DrawingDocument, DrawingParseError, type DrawingParser, type DrawingSource, type DrawingSummary, DrawingViewer, type DrawingViewerOptions, type EllipseEntity, type Entity, type EntityHandler, type EntityHit, type EntityInfo, type EntityType, type FitViewOptions, type GestureOptions, type HatchEntity, type InsertEntity, type LayerGeometry, type LayerInfo, type LayerSummary, type Layout, type LineEntity, type LineTypeDef, type PickedEntity, type Point2, type Point3, type PointEntity, type PolylineEntity, type ShortcutHandlers, type ShortcutViewer, SnapIndex, type SnapKind, type SnapResult, type SolidEntity, type SplineEntity, type SvgExportOptions, type TessellateOptions, type Tessellation, type TessellationContext, type TextEntity, type TextHAlign, type TextLayoutOptions, type TextVAlign, VERSION, type ViewState, type ViewerEvent, type ViewerStats, type Viewport, attachGestures, attachShortcuts, buildSnapIndex, dashPolyline, decodeTextSpecials, describeDrawing, describeEntity, isEmptyLayer, layoutText, niceLength, parseWith, partitionLayers, pickEntity, pickLayer, registerEntityHandler, sampleSpline, stripMText, tessellate, tessellateLayout, tessellationToSvg, toBytes, triangulate, unitLabel };
export { type ArcEntity, type BlockDef, type Bounds, Camera2D, type CircleEntity, type DescribeOptions, type DimensionEntity, type DrawingDocument, DrawingParseError, type DrawingParser, type DrawingSource, type DrawingSummary, DrawingViewer, type DrawingViewerOptions, type EllipseEntity, type Entity, type EntityHandler, type EntityHit, type EntityInfo, type EntityType, type FitViewOptions, type GestureOptions, type HatchEntity, type ImageEntity, type InsertEntity, type LayerGeometry, type LayerInfo, type LayerSummary, type Layout, type LineEntity, type LineTypeDef, MODEL_SPACE, type PickedEntity, type PlacedImage, type Point2, type Point3, type PointEntity, type PolylineEntity, type RasterImage, type ShortcutHandlers, type ShortcutViewer, SnapIndex, type SnapKind, type SnapResult, type SolidEntity, type SpaceSummary, type SplineEntity, type SvgExportOptions, type TessellateOptions, type Tessellation, type TessellationContext, type TextEntity, type TextHAlign, type TextLayoutOptions, type TextVAlign, UnknownSpaceError, VERSION, type ViewState, type ViewerEvent, type ViewerStats, type Viewport, attachGestures, attachShortcuts, buildSnapIndex, countEntitiesByLayer, dashPolyline, decodeTextSpecials, describeDrawing, describeEntity, documentEntities, documentEntityCount, isEmptyLayer, layoutText, niceLength, parseWith, partitionLayers, pickEntity, pickLayer, registerEntityHandler, sampleSpline, spaceEntities, spaceNames, stripMText, tessellate, tessellateLayout, tessellateSpace, tessellationToSvg, toBytes, triangulate, unitLabel };

@@ -1,2 +0,2 @@

import { d as DrawingDocument, t as DrawingParser } from "./registry-CtIhdVSA.mjs";
import { D as RasterImage, d as DrawingDocument, t as DrawingParser } from "./registry-C4th1Vh7.mjs";

@@ -141,2 +141,7 @@ //#region src/parse/pdf/objects.d.ts

//#endregion
//#region src/parse/pdf/image.d.ts
/** Document-scoped decode cache: one entry per image object (per fill colour
* for stencil masks, whose pixels depend on it). `null` = tried and failed. */
type ImageCache = Map<string, RasterImage | null>;
//#endregion
//#region src/parse/pdf/optional-content.d.ts

@@ -189,2 +194,7 @@ /** One optional-content group, in panel order. */

optionalContent?: OptionalContent;
/**
* Shared image-decode cache. Pass one per document: an image XObject
* referenced from several pages decodes once (PDF-9).
*/
imageCache?: ImageCache;
}

@@ -191,0 +201,0 @@ //#endregion

{
"name": "@aspicio/core",
"version": "0.12.0",
"version": "0.13.0",
"description": "Aspicio — a TypeScript DXF and vector-PDF viewer library (WebGL, mobile-first).",

@@ -38,8 +38,8 @@ "homepage": "https://github.com/frontsail-ai/aspicio/tree/master/packages/core#readme",

"devDependencies": {
"@types/node": "^26.1.1",
"@types/three": "^0.185.1",
"@types/node": "^26.2.0",
"@types/three": "^0.185.4",
"@typescript/native-preview": "^7.0.0-dev.20260707.2",
"@vitest/coverage-v8": "4.1.10",
"bumpp": "^11.1.0",
"happy-dom": "^20.10.6",
"bumpp": "^12.2.0",
"happy-dom": "^20.11.2",
"three": "^0.185.1",

@@ -46,0 +46,0 @@ "typescript": "^7.0.2",

@@ -91,5 +91,12 @@ # @aspicio/core

transformed to the sheet scale and clipped to the window — all baked into
one paper-space tessellation (`tessellateLayout`). Entity picking is limited
to model space. `document.layouts` holds the parsed `Layout[]`.
one paper-space tessellation (`tessellateLayout`, or `tessellateSpace(doc,
name)` to pick a space by name). Entity picking is limited to model space.
`document.layouts` holds the parsed `Layout[]`.
`stats.entityCount`, `stats.segmentCount`, and every layer's `entityCount`
describe the space on screen and change when it does (VIEW-16); a sheet counts
the model layers its viewports draw, so nothing reads zero while it is visible.
`stats.documentEntityCount` is the whole drawing, for "did anything load at
all".
### Camera

@@ -96,0 +103,0 @@

//#region src/model/types.d.ts
/** Normalized document model. Decoupled from the parser's output shape. */
interface Point2 {
x: number;
y: number;
}
interface Point3 {
x: number;
y: number;
z: number;
}
/** 2D affine transform: [a, b, c, d, tx, ty] mapping (x,y) → (a·x+c·y+tx, b·x+d·y+ty). */
type Affine2D = [number, number, number, number, number, number];
interface Bounds {
minX: number;
minY: number;
maxX: number;
maxY: number;
}
interface LayerInfo {
name: string;
/** Layer-table color, 24-bit RGB. May differ from what is drawn. */
color: number;
/**
* Colors actually drawn on this layer, dominant first (populated after
* tessellation). Entity-styled files override the table color per entity,
* so UI should prefer `effectiveColors[0]` over `color`.
*/
effectiveColors?: number[];
visible: boolean;
frozen: boolean;
/** Number of top-level entities on this layer. */
entityCount: number;
/** Layer's default linetype name (resolved against the document map). */
lineType?: string;
/**
* Layer's default lineweight in 1/100 mm (DXF group 370). Negative codes
* (-3 default, -2 ByBlock, -1 ByLayer) are dropped to `undefined`.
*/
lineWeight?: number;
}
interface EntityBase {
layer: string;
/** Resolved 24-bit RGB, or null for ByLayer/ByBlock. */
color: number | null;
/**
* OCS extrusion normal (codes 210/220/230) for entity types whose
* coordinates are OCS-relative (ARC, POLYLINE, INSERT). Undefined means
* the default +Z (world coordinates). (0,0,-1) marks mirrored entities.
*/
extrusion?: Point3;
/**
* Linetype name, or "BYLAYER"/undefined to inherit the layer's. Resolved
* against the document's `lineTypes` map to a dash pattern at render time.
*/
lineType?: string;
/**
* Lineweight in 1/100 mm (DXF group 370), or undefined to inherit the
* layer's. Negative "ByLayer/ByBlock/default" codes are dropped to
* undefined so the layer default applies.
*/
lineWeight?: number;
}
/**
* Linetype dash pattern: alternating drawn/gap lengths in drawing units.
* Positive = dash (pen down), negative = gap (pen up), 0 = dot.
*/
interface LineTypeDef {
name: string;
pattern: number[];
/** Sum of |pattern|; 0 for a continuous line. */
patternLength: number;
}
interface LineEntity extends EntityBase {
type: "LINE";
start: Point2;
end: Point2;
}
interface PolylineEntity extends EntityBase {
type: "POLYLINE";
points: Point2[];
/** Bulge per segment starting at points[i]; same length as points. */
bulges: number[];
closed: boolean;
}
interface CircleEntity extends EntityBase {
type: "CIRCLE";
center: Point2;
radius: number;
}
interface ArcEntity extends EntityBase {
type: "ARC";
center: Point2;
radius: number;
/** Radians, CCW from +X. */
startAngle: number;
endAngle: number;
}
interface EllipseEntity extends EntityBase {
type: "ELLIPSE";
center: Point2;
/** Major axis endpoint relative to center. */
majorAxis: Point2;
/** Minor/major ratio. */
axisRatio: number;
/** Parametric range in radians. */
startParam: number;
endParam: number;
}
interface InsertEntity extends EntityBase {
type: "INSERT";
blockName: string;
position: Point2;
scale: Point2;
/** Radians. */
rotation: number;
}
type TextHAlign = "left" | "center" | "right";
type TextVAlign = "baseline" | "bottom" | "middle" | "top";
/** Normalized TEXT and MTEXT. MTEXT format codes are collapsed to plain text. */
interface TextEntity extends EntityBase {
type: "TEXT";
/** Insertion/alignment point. */
position: Point2;
/** Content; may contain newlines (from MTEXT paragraphs). */
text: string;
/** Cap height in drawing units. */
height: number;
/** Radians, CCW. */
rotation: number;
/** Horizontal scale (DXF xScale). */
widthFactor: number;
hAlign: TextHAlign;
vAlign: TextVAlign;
}
interface SplineEntity extends EntityBase {
type: "SPLINE";
controlPoints: Point2[];
/** Knot vector; empty means "generate a clamped uniform vector". */
knots: number[];
degree: number;
closed: boolean;
}
/** Filled triangle/quad: SOLID, TRACE, or a projected 3DFACE. */
interface SolidEntity extends EntityBase {
type: "SOLID";
/** 3 or 4 corners, already reordered to a simple (non-crossing) ring. */
points: Point2[];
}
/** A POINT — rendered as a small crosshair marker. */
interface PointEntity extends EntityBase {
type: "POINT";
position: Point2;
}
/** DIMENSION — rendered by drawing its anonymous geometry block. */
interface DimensionEntity extends EntityBase {
type: "DIMENSION";
/** Name of the anonymous "*D…" block holding the lines, arrows, and text. */
block: string;
/** Block insertion point (usually the origin). */
position: Point2;
}
/** HATCH — filled region(s). Boundaries are pre-sampled to polyline loops. */
interface HatchEntity extends EntityBase {
type: "HATCH";
/** Boundary loops in drawing coordinates (outer + holes, unspecified order). */
loops: Point2[][];
/** Solid fill vs. a line pattern (patterns render as boundary outlines). */
solid: boolean;
}
type Entity = LineEntity | PolylineEntity | CircleEntity | ArcEntity | EllipseEntity | InsertEntity | TextEntity | SplineEntity | SolidEntity | PointEntity | DimensionEntity | HatchEntity;
type EntityType = Entity["type"];
interface BlockDef {
name: string;
basePoint: Point2;
entities: Entity[];
}
/** A paper-space viewport: a window that frames model space at a fixed scale. */
interface Viewport {
/** Center of the window on the paper (paper coords). */
center: Point2;
/** Window size on the paper. */
width: number;
height: number;
/** Model point shown at the window center. */
viewCenter: Point2;
/** Model-space height visible through the window (drives the scale). */
viewHeight: number;
/** View twist, radians (CCW). */
twist: number;
}
/** A paper-space layout: a printable sheet with its own geometry and viewports. */
interface Layout {
name: string;
/** Drawable paper-space geometry (titleblock, borders, text). */
entities: Entity[];
/** Windows into model space. */
viewports: Viewport[];
}
interface DrawingDocument {
layers: Map<string, LayerInfo>;
entities: Entity[];
blocks: Map<string, BlockDef>;
/** Linetype definitions from the LTYPE table, keyed by name. */
lineTypes: Map<string, LineTypeDef>;
/** Counts of raw DXF entity types that were skipped by the parser stage. */
unsupported: Record<string, number>;
/**
* Short drawing-unit label from the header's `$INSUNITS` (e.g. "mm", "in"),
* or "" when the drawing is unitless or the code is unknown. `parseDxf`
* always sets it; hand-built documents may omit it (treated as "").
*/
units?: string;
/**
* Paper-space layouts, if any. `entities` holds model space; each layout
* carries its own paper geometry and viewports. `parseDxf` sets it (possibly
* empty); hand-built documents may omit it (treated as no layouts).
*/
layouts?: Layout[];
/**
* Which format produced this document ("dxf", "pdf") — so every surface can
* report it without re-sniffing the bytes (PARSE-13). Parsers always set it;
* hand-built documents may omit it.
*/
format?: string;
}
//#endregion
//#region src/parse/registry.d.ts
/** One file format's contribution: a name, a byte sniff, and a parse. */
interface DrawingParser {
/** Short lowercase format name, e.g. "dxf". Surfaces report it verbatim. */
format: string;
/**
* True when this parser claims the bytes. Sniffs see the whole buffer but
* should only look at the head — they run on every load, for every parser.
*/
sniff(bytes: Uint8Array): boolean;
/** Parse claimed bytes, or throw a `DrawingParseError` carrying `format`. */
parse(bytes: Uint8Array): DrawingDocument | Promise<DrawingDocument>;
}
/** Everything a drawing can be loaded from (PARSE-1). */
type DrawingSource = string | ArrayBuffer | Uint8Array | Blob;
/** Normalize any accepted source to bytes, so sniffs see one shape (PARSE-1). */
declare function toBytes(source: DrawingSource): Promise<Uint8Array>;
/**
* Parse `source` with the first parser whose sniff claims it (PARSE-13).
*
* Sniffs run in the order given, so a caller controls precedence by ordering
* its list. No parser claiming the bytes is a clean, honest failure, not a
* fallback attempt at every parser in turn (PARSE-12).
*/
declare function parseWith(parsers: readonly DrawingParser[], source: DrawingSource): Promise<DrawingDocument>;
//#endregion
export { Viewport as A, PointEntity as C, TextEntity as D, SplineEntity as E, TextHAlign as O, Point3 as S, SolidEntity as T, LayerInfo as _, Affine2D as a, LineTypeDef as b, Bounds as c, DrawingDocument as d, EllipseEntity as f, InsertEntity as g, HatchEntity as h, toBytes as i, TextVAlign as k, CircleEntity as l, EntityType as m, DrawingSource as n, ArcEntity as o, Entity as p, parseWith as r, BlockDef as s, DrawingParser as t, DimensionEntity as u, Layout as v, PolylineEntity as w, Point2 as x, LineEntity as y };

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display