@chestnutlabs/toolpath-core 0.1.0 → 0.3.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/README.md +1 -1
- package/dist/builder.d.ts +20 -0
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +38 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/ir.d.ts +41 -0
- package/dist/ir.d.ts.map +1 -1
- package/dist/metadata.d.ts +37 -0
- package/dist/metadata.d.ts.map +1 -1
- package/dist/source-map.d.ts +31 -0
- package/dist/source-map.d.ts.map +1 -0
- package/dist/source-map.js +74 -0
- package/dist/time.d.ts +33 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +41 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Neutral **`ToolpathIR`** and **capability model** for the Chestnut Labs G-code Preview toolpath stack.
|
|
4
4
|
This is the replacement seam for the whole stack: the parser **writes** it, renderers and analyzers **read**
|
|
5
|
-
it. See [`docs/design/DD-001`](
|
|
5
|
+
it. See [`docs/design/DD-001`](https://github.com/ChestnutLabs/gcode-preview/blob/dev/docs/design/DD-001-toolpath-ir-and-capability-model.md).
|
|
6
6
|
|
|
7
7
|
**Design rules**
|
|
8
8
|
|
package/dist/builder.d.ts
CHANGED
|
@@ -59,10 +59,30 @@ export declare class ToolpathIRBuilder {
|
|
|
59
59
|
private readonly srcByte;
|
|
60
60
|
private readonly toolsMap;
|
|
61
61
|
private readonly objectsList;
|
|
62
|
+
private readonly retractionsList;
|
|
63
|
+
private readonly colorChangesList;
|
|
62
64
|
private readonly warnings;
|
|
63
65
|
private readonly capabilities;
|
|
64
66
|
constructor(opts?: BuilderOptions);
|
|
65
67
|
addSegment(s: SegmentInput): void;
|
|
68
|
+
/** Record a retraction/unretraction event (DD-009 D1, #148). Position is absolute; stored origin-relative. */
|
|
69
|
+
addRetraction(r: {
|
|
70
|
+
x: number;
|
|
71
|
+
y: number;
|
|
72
|
+
z: number;
|
|
73
|
+
kind: 'retract' | 'unretract';
|
|
74
|
+
srcByte: number;
|
|
75
|
+
segIndex: number;
|
|
76
|
+
}): void;
|
|
77
|
+
/** Record an M600 color-change boundary (DD-009 D2, #147). Position is absolute; stored origin-relative. */
|
|
78
|
+
addColorChange(c: {
|
|
79
|
+
x: number;
|
|
80
|
+
y: number;
|
|
81
|
+
z: number;
|
|
82
|
+
segIndex: number;
|
|
83
|
+
srcByte: number;
|
|
84
|
+
tool: number;
|
|
85
|
+
}): void;
|
|
66
86
|
addWarning(w: Warning): void;
|
|
67
87
|
setCapability(name: string, confidence: Confidence): void;
|
|
68
88
|
setTool(info: ToolInfo): void;
|
package/dist/builder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,UAAU,EAEf,KAAK,QAAQ,EACb,KAAK,UAAU,EAIf,KAAK,KAAK,EACV,KAAK,IAAI,EACT,KAAK,OAAO,EACb,MAAM,SAAS,CAAC;AAIjB,0GAA0G;AAC1G,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,MAAM,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,oFAAoF;IACpF,YAAY,CAAC,EAAE,IAAI,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiB;IACtC,OAAO,CAAC,YAAY,CAAc;IAElC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAgB;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IAExC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+B;IACxD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IACzD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA0B;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;gBAEnD,IAAI,GAAE,cAAmB;IAKrC,UAAU,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI;IAwBjC,8GAA8G;IAC9G,aAAa,CAAC,CAAC,EAAE;QACf,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,IAAI,EAAE,SAAS,GAAG,WAAW,CAAC;QAC9B,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI;IAYR,4GAA4G;IAC5G,cAAc,CAAC,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAY7G,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAI5B,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI;IAIzD,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAI7B,sFAAsF;IACtF,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAKnC,QAAQ,IAAI,UAAU;CAuDvB"}
|
package/dist/builder.js
CHANGED
|
@@ -28,6 +28,8 @@ export class ToolpathIRBuilder {
|
|
|
28
28
|
srcByte = [];
|
|
29
29
|
toolsMap = new Map();
|
|
30
30
|
objectsList = [];
|
|
31
|
+
retractionsList = [];
|
|
32
|
+
colorChangesList = [];
|
|
31
33
|
warnings = [];
|
|
32
34
|
capabilities = {};
|
|
33
35
|
constructor(opts = {}) {
|
|
@@ -57,6 +59,30 @@ export class ToolpathIRBuilder {
|
|
|
57
59
|
this.toolsMap.set(s.tool, { id: s.tool });
|
|
58
60
|
}
|
|
59
61
|
}
|
|
62
|
+
/** Record a retraction/unretraction event (DD-009 D1, #148). Position is absolute; stored origin-relative. */
|
|
63
|
+
addRetraction(r) {
|
|
64
|
+
const o = this.originOffset ?? { x: 0, y: 0, z: 0 };
|
|
65
|
+
this.retractionsList.push({
|
|
66
|
+
x: r.x - o.x,
|
|
67
|
+
y: r.y - o.y,
|
|
68
|
+
z: r.z - o.z,
|
|
69
|
+
kind: r.kind,
|
|
70
|
+
srcByte: r.srcByte,
|
|
71
|
+
segIndex: r.segIndex
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/** Record an M600 color-change boundary (DD-009 D2, #147). Position is absolute; stored origin-relative. */
|
|
75
|
+
addColorChange(c) {
|
|
76
|
+
const o = this.originOffset ?? { x: 0, y: 0, z: 0 };
|
|
77
|
+
this.colorChangesList.push({
|
|
78
|
+
x: c.x - o.x,
|
|
79
|
+
y: c.y - o.y,
|
|
80
|
+
z: c.z - o.z,
|
|
81
|
+
segIndex: c.segIndex,
|
|
82
|
+
srcByte: c.srcByte,
|
|
83
|
+
tool: c.tool
|
|
84
|
+
});
|
|
85
|
+
}
|
|
60
86
|
addWarning(w) {
|
|
61
87
|
this.warnings.push(w);
|
|
62
88
|
}
|
|
@@ -111,7 +137,18 @@ export class ToolpathIRBuilder {
|
|
|
111
137
|
capabilities: this.capabilities
|
|
112
138
|
};
|
|
113
139
|
const tools = [...this.toolsMap.values()].sort((a, b) => a.id - b.id);
|
|
114
|
-
return {
|
|
140
|
+
return {
|
|
141
|
+
header,
|
|
142
|
+
segments,
|
|
143
|
+
layers,
|
|
144
|
+
tools,
|
|
145
|
+
objects: this.objectsList,
|
|
146
|
+
retractions: this.retractionsList,
|
|
147
|
+
colorChanges: this.colorChangesList,
|
|
148
|
+
bounds,
|
|
149
|
+
boundsWithTravel,
|
|
150
|
+
sourceIndex
|
|
151
|
+
};
|
|
115
152
|
}
|
|
116
153
|
}
|
|
117
154
|
/** Group segments into layers by their `layer` index; layer Z is absolute. Assumes dense layer indices. */
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/ir.d.ts
CHANGED
|
@@ -126,6 +126,43 @@ export interface ObjectInfo {
|
|
|
126
126
|
id: string;
|
|
127
127
|
name?: string;
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* A filament retraction (`retract`) or its reversal (`unretract`) — an E-only move
|
|
131
|
+
* with no XYZ travel, so it has a *position* but no segment in the main stream
|
|
132
|
+
* (DD-009 D1 amendment, #148). Kept in a sparse side channel so segment indices,
|
|
133
|
+
* scrub, and layer ranges are untouched. `segIndex` is the index of the next
|
|
134
|
+
* segment emitted after the event, for layer/scrub-aligned marker clipping.
|
|
135
|
+
*/
|
|
136
|
+
export interface RetractionEvent {
|
|
137
|
+
/** Position (same frame as segments: origin-relative). */
|
|
138
|
+
x: number;
|
|
139
|
+
y: number;
|
|
140
|
+
z: number;
|
|
141
|
+
kind: 'retract' | 'unretract';
|
|
142
|
+
/** Byte offset in the source for scrub/telemetry alignment. */
|
|
143
|
+
srcByte: number;
|
|
144
|
+
/** Segment index at the moment of the event (draw-range clip alignment). */
|
|
145
|
+
segIndex: number;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* A manual filament-swap color boundary (`M600`) — a marker with a *position* but
|
|
149
|
+
* no motion segment (DD-009 D2 amendment, #147). Like {@link RetractionEvent} it is
|
|
150
|
+
* kept in a sparse side channel so segment indices, scrub, and layer ranges are
|
|
151
|
+
* untouched. The renderer's `colorChange` color mode shades segments by *swap slot*
|
|
152
|
+
* (the count of color changes at or before a segment), NOT by the `tool` channel.
|
|
153
|
+
*/
|
|
154
|
+
export interface ColorChangeEvent {
|
|
155
|
+
/** Position (same frame as segments: origin-relative). */
|
|
156
|
+
x: number;
|
|
157
|
+
y: number;
|
|
158
|
+
z: number;
|
|
159
|
+
/** Index of the first segment emitted after the swap (slot boundary + marker clip). */
|
|
160
|
+
segIndex: number;
|
|
161
|
+
/** Byte offset in the source for scrub/telemetry alignment. */
|
|
162
|
+
srcByte: number;
|
|
163
|
+
/** Tool active at the swap — provenance only; not used to pick the swap color. */
|
|
164
|
+
tool: number;
|
|
165
|
+
}
|
|
129
166
|
export interface ToolpathBounds {
|
|
130
167
|
min: Vec3;
|
|
131
168
|
max: Vec3;
|
|
@@ -141,6 +178,10 @@ export interface ToolpathIR {
|
|
|
141
178
|
layers: ToolpathLayer[];
|
|
142
179
|
tools: ToolInfo[];
|
|
143
180
|
objects: ObjectInfo[];
|
|
181
|
+
/** Sparse retraction/unretraction events (DD-009 D1, #148); empty when none seen. */
|
|
182
|
+
retractions: RetractionEvent[];
|
|
183
|
+
/** Sparse M600 filament-swap color-change boundaries (DD-009 D2, #147); empty when none seen. */
|
|
184
|
+
colorChanges: ColorChangeEvent[];
|
|
144
185
|
/** Bounds over extruding moves only. */
|
|
145
186
|
bounds: ToolpathBounds;
|
|
146
187
|
/** Bounds including travel moves. */
|
package/dist/ir.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir.d.ts","sourceRoot":"","sources":["../src/ir.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ir.d.ts","sourceRoot":"","sources":["../src/ir.ts"],"names":[],"mappings":"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"}
|
package/dist/metadata.d.ts
CHANGED
|
@@ -64,6 +64,39 @@ export interface FilamentInfo {
|
|
|
64
64
|
color?: string;
|
|
65
65
|
name?: string;
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Slicer-reported filament consumption (#183) — the "info panel" totals a slicer emits as G-code
|
|
69
|
+
* comments. Absent when the slicer doesn't report it (capability-honest). Aggregate across extruders;
|
|
70
|
+
* fields are independently optional (a slicer may report length but not weight).
|
|
71
|
+
*/
|
|
72
|
+
export interface FilamentUsage {
|
|
73
|
+
/** Total filament length in millimetres. */
|
|
74
|
+
lengthMm?: number;
|
|
75
|
+
/** Total extruded volume in cm³. */
|
|
76
|
+
volumeCm3?: number;
|
|
77
|
+
/** Total filament weight in grams. */
|
|
78
|
+
weightG?: number;
|
|
79
|
+
source: {
|
|
80
|
+
adapterId: string;
|
|
81
|
+
evidence: string;
|
|
82
|
+
srcByte?: number;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The slicer's own print-time estimate (#183) — the trustworthy figure for a time readout / time
|
|
87
|
+
* scrub, versus a kinematic estimate. Absent when the slicer doesn't emit it.
|
|
88
|
+
*/
|
|
89
|
+
export interface PrintEstimate {
|
|
90
|
+
/** Estimated print duration in seconds. */
|
|
91
|
+
seconds: number;
|
|
92
|
+
/** Slicer mode label when given (e.g. PrusaSlicer 'normal' / 'silent'). */
|
|
93
|
+
mode?: string;
|
|
94
|
+
source: {
|
|
95
|
+
adapterId: string;
|
|
96
|
+
evidence: string;
|
|
97
|
+
srcByte?: number;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
67
100
|
export interface ThumbnailData {
|
|
68
101
|
width: number;
|
|
69
102
|
height: number;
|
|
@@ -80,6 +113,10 @@ export interface DialectMetadata {
|
|
|
80
113
|
dialects?: DialectDetection[];
|
|
81
114
|
machine?: MachineGeometry;
|
|
82
115
|
filaments?: FilamentInfo[];
|
|
116
|
+
/** Slicer-reported total filament consumption (#183); absent when not emitted. */
|
|
117
|
+
filamentUsage?: FilamentUsage;
|
|
118
|
+
/** The slicer's own print-time estimate (#183); absent when not emitted. */
|
|
119
|
+
printEstimate?: PrintEstimate;
|
|
83
120
|
thumbnails?: ThumbnailData[];
|
|
84
121
|
/** Whitelisted key/value settings only — bounded, never local paths. */
|
|
85
122
|
raw?: Record<string, string>;
|
package/dist/metadata.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,WAAW,MAAM;IACrB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,gEAAgE;AAChE,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EACC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAC1C;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GACtD;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC1C,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,UAAU,EAAE,UAAU,CAAC;IACvB,oEAAoE;IACpE,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnE;AAED,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IACxC,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,aAAa,EAAE,CAAC;IAC7B,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B"}
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source-line ↔ segment mapping (#184) — the "G-code debugger" surface: resolve a rendered segment
|
|
3
|
+
* to the G-code line that produced it, and a source line back to its segment. Framework-free and
|
|
4
|
+
* built on data the IR already carries (`segments.srcByte`, the byte→segment `sourceIndex`); the only
|
|
5
|
+
* missing piece is byte↔line, which needs the source text (the IR stores byte offsets, not lines).
|
|
6
|
+
*
|
|
7
|
+
* Build a {@link SourceLineIndex} once from the source, then all four lookups are O(log n).
|
|
8
|
+
*/
|
|
9
|
+
import { type SourceIndex } from './ir.js';
|
|
10
|
+
/** Byte offsets of each line start (index 0 = line 1 at byte 0), plus the total length. */
|
|
11
|
+
export interface SourceLineIndex {
|
|
12
|
+
/** `lineStarts[k]` is the byte offset where line `k + 1` begins. */
|
|
13
|
+
lineStarts: Uint32Array;
|
|
14
|
+
/** Total source length in bytes. */
|
|
15
|
+
byteLength: number;
|
|
16
|
+
}
|
|
17
|
+
/** Build the line index from the raw source (string or the parsed bytes). O(bytes), one pass. */
|
|
18
|
+
export declare function buildSourceLineIndex(source: string | Uint8Array): SourceLineIndex;
|
|
19
|
+
/** 1-based line number containing `byteOffset` (binary search). Clamped to [1, lineCount]. */
|
|
20
|
+
export declare function lineAtByte(index: SourceLineIndex, byteOffset: number): number;
|
|
21
|
+
/** Byte range `[start, end)` of a 1-based `line`; null when out of range. `end` excludes the newline-led next line. */
|
|
22
|
+
export declare function byteRangeOfLine(index: SourceLineIndex, line: number): [number, number] | null;
|
|
23
|
+
/** 1-based source line that produced segment `segIndex`; null when the index is out of range. */
|
|
24
|
+
export declare function sourceLineOfSegment(srcByte: Uint32Array, lineIndex: SourceLineIndex, segIndex: number): number | null;
|
|
25
|
+
/**
|
|
26
|
+
* The segment produced by a 1-based source `line`, or -1 when the line produced no segment (a comment,
|
|
27
|
+
* a blank line, or a non-motion command). Resolves to the first segment whose source byte falls within
|
|
28
|
+
* the line's byte range.
|
|
29
|
+
*/
|
|
30
|
+
export declare function segmentAtSourceLine(sourceIndex: SourceIndex, lineIndex: SourceLineIndex, line: number): number;
|
|
31
|
+
//# sourceMappingURL=source-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-map.d.ts","sourceRoot":"","sources":["../src/source-map.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,2FAA2F;AAC3F,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,UAAU,EAAE,WAAW,CAAC;IACxB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,iGAAiG;AACjG,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,eAAe,CAYjF;AAED,8FAA8F;AAC9F,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAe7E;AAED,uHAAuH;AACvH,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAM7F;AAED,iGAAiG;AACjG,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGrH;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAiB9G"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** Build the line index from the raw source (string or the parsed bytes). O(bytes), one pass. */
|
|
2
|
+
export function buildSourceLineIndex(source) {
|
|
3
|
+
// TextEncoder is a runtime global (browser + Node); reach it via globalThis so this DOM-free
|
|
4
|
+
// package (lib: ES2022, types: []) typechecks without pulling the DOM lib.
|
|
5
|
+
const Enc = globalThis.TextEncoder;
|
|
6
|
+
const bytes = typeof source === 'string' ? new Enc().encode(source) : source;
|
|
7
|
+
const starts = [0];
|
|
8
|
+
// A trailing '\n' ends the last line rather than starting an empty new one (editor convention),
|
|
9
|
+
// so only record a start when bytes follow the newline.
|
|
10
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
11
|
+
if (bytes[i] === 0x0a /* \n */ && i + 1 < bytes.length)
|
|
12
|
+
starts.push(i + 1);
|
|
13
|
+
}
|
|
14
|
+
return { lineStarts: Uint32Array.from(starts), byteLength: bytes.length };
|
|
15
|
+
}
|
|
16
|
+
/** 1-based line number containing `byteOffset` (binary search). Clamped to [1, lineCount]. */
|
|
17
|
+
export function lineAtByte(index, byteOffset) {
|
|
18
|
+
const starts = index.lineStarts;
|
|
19
|
+
let lo = 0;
|
|
20
|
+
let hi = starts.length - 1;
|
|
21
|
+
let ans = 0;
|
|
22
|
+
while (lo <= hi) {
|
|
23
|
+
const mid = (lo + hi) >> 1;
|
|
24
|
+
if (starts[mid] <= byteOffset) {
|
|
25
|
+
ans = mid;
|
|
26
|
+
lo = mid + 1;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
hi = mid - 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return ans + 1; // 1-based
|
|
33
|
+
}
|
|
34
|
+
/** Byte range `[start, end)` of a 1-based `line`; null when out of range. `end` excludes the newline-led next line. */
|
|
35
|
+
export function byteRangeOfLine(index, line) {
|
|
36
|
+
const starts = index.lineStarts;
|
|
37
|
+
if (line < 1 || line > starts.length)
|
|
38
|
+
return null;
|
|
39
|
+
const start = starts[line - 1];
|
|
40
|
+
const end = line < starts.length ? starts[line] : index.byteLength;
|
|
41
|
+
return [start, end];
|
|
42
|
+
}
|
|
43
|
+
/** 1-based source line that produced segment `segIndex`; null when the index is out of range. */
|
|
44
|
+
export function sourceLineOfSegment(srcByte, lineIndex, segIndex) {
|
|
45
|
+
if (segIndex < 0 || segIndex >= srcByte.length)
|
|
46
|
+
return null;
|
|
47
|
+
return lineAtByte(lineIndex, srcByte[segIndex]);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The segment produced by a 1-based source `line`, or -1 when the line produced no segment (a comment,
|
|
51
|
+
* a blank line, or a non-motion command). Resolves to the first segment whose source byte falls within
|
|
52
|
+
* the line's byte range.
|
|
53
|
+
*/
|
|
54
|
+
export function segmentAtSourceLine(sourceIndex, lineIndex, line) {
|
|
55
|
+
const range = byteRangeOfLine(lineIndex, line);
|
|
56
|
+
if (range === null)
|
|
57
|
+
return -1;
|
|
58
|
+
// Smallest indexed byte >= range start whose byte is < range end.
|
|
59
|
+
const offsets = sourceIndex.byteOffsets;
|
|
60
|
+
let lo = 0;
|
|
61
|
+
let hi = offsets.length;
|
|
62
|
+
while (lo < hi) {
|
|
63
|
+
const mid = (lo + hi) >>> 1;
|
|
64
|
+
if (offsets[mid] < range[0])
|
|
65
|
+
lo = mid + 1;
|
|
66
|
+
else
|
|
67
|
+
hi = mid;
|
|
68
|
+
}
|
|
69
|
+
if (lo < offsets.length && offsets[lo] >= range[0] && offsets[lo] < range[1]) {
|
|
70
|
+
return sourceIndex.segmentIndices[lo];
|
|
71
|
+
}
|
|
72
|
+
// The line produced no segment (comment, blank, or a non-motion command) — honestly -1.
|
|
73
|
+
return -1;
|
|
74
|
+
}
|
package/dist/time.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kinematic time axis over the IR (#181) — a per-segment duration model for a time readout and
|
|
3
|
+
* time-based scrubbing, derived from geometry the IR already carries.
|
|
4
|
+
*
|
|
5
|
+
* Each segment's duration is its 3D length divided by its feedrate (`feedrate` is mm/min). This is a
|
|
6
|
+
* **kinematic estimate**: constant-velocity per segment, NOT acceleration/jerk-aware, so it slightly
|
|
7
|
+
* *under*estimates real print time. A segment with an unknown feedrate (NaN, before the first `F`)
|
|
8
|
+
* contributes zero and flags the estimate as approximate — never a fabricated duration. When the
|
|
9
|
+
* slicer's own estimate is available (`DialectMetadata.printEstimate`, #183), prefer it; this is the
|
|
10
|
+
* honest fallback and the axis time-scrub always needs.
|
|
11
|
+
*/
|
|
12
|
+
import type { ToolpathIR } from './ir.js';
|
|
13
|
+
export interface ToolpathTime {
|
|
14
|
+
/** Cumulative END time (ms) of each segment; length = `segments.count`, monotonic non-decreasing. */
|
|
15
|
+
cumulativeMs: Float64Array;
|
|
16
|
+
/** Total kinematic duration (ms). */
|
|
17
|
+
totalMs: number;
|
|
18
|
+
/**
|
|
19
|
+
* True if any moving segment had an unknown feedrate (contributed 0). The estimate is then a lower
|
|
20
|
+
* bound / approximate — a consumer should disclose it (and prefer the slicer estimate when present).
|
|
21
|
+
*/
|
|
22
|
+
hasUnknownFeedrate: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** Build the cumulative kinematic time axis for `ir`. O(segments), no allocation beyond the axis. */
|
|
25
|
+
export declare function computeToolpathTime(ir: ToolpathIR): ToolpathTime;
|
|
26
|
+
/**
|
|
27
|
+
* The number of segments completed at time `ms` — i.e. the count of segments whose cumulative END
|
|
28
|
+
* time is ≤ `ms` (binary search). 0 before the first segment finishes; `count` at/after the end. This
|
|
29
|
+
* is exactly a scrub cut position (segments `[0, result)` are done), so it maps straight to a
|
|
30
|
+
* segment-index scrub.
|
|
31
|
+
*/
|
|
32
|
+
export declare function segmentsCompletedAtTime(cumulativeMs: Float64Array, ms: number): number;
|
|
33
|
+
//# sourceMappingURL=time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../src/time.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,WAAW,YAAY;IAC3B,qGAAqG;IACrG,YAAY,EAAE,YAAY,CAAC;IAC3B,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,qGAAqG;AACrG,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,UAAU,GAAG,YAAY,CAoBhE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAStF"}
|
package/dist/time.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Build the cumulative kinematic time axis for `ir`. O(segments), no allocation beyond the axis. */
|
|
2
|
+
export function computeToolpathTime(ir) {
|
|
3
|
+
const seg = ir.segments;
|
|
4
|
+
const n = seg.count;
|
|
5
|
+
const cumulativeMs = new Float64Array(n);
|
|
6
|
+
let t = 0;
|
|
7
|
+
let hasUnknownFeedrate = false;
|
|
8
|
+
for (let i = 0; i < n; i++) {
|
|
9
|
+
const dx = seg.x1[i] - seg.x0[i];
|
|
10
|
+
const dy = seg.y1[i] - seg.y0[i];
|
|
11
|
+
const dz = seg.z1[i] - seg.z0[i];
|
|
12
|
+
const dist = Math.hypot(dx, dy, dz);
|
|
13
|
+
const f = seg.feedrate[i]; // mm/min
|
|
14
|
+
if (Number.isFinite(f) && f > 0) {
|
|
15
|
+
t += dist / (f / 60000); // mm ÷ (mm/ms) = ms
|
|
16
|
+
}
|
|
17
|
+
else if (dist > 0) {
|
|
18
|
+
hasUnknownFeedrate = true;
|
|
19
|
+
}
|
|
20
|
+
cumulativeMs[i] = t;
|
|
21
|
+
}
|
|
22
|
+
return { cumulativeMs, totalMs: t, hasUnknownFeedrate };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The number of segments completed at time `ms` — i.e. the count of segments whose cumulative END
|
|
26
|
+
* time is ≤ `ms` (binary search). 0 before the first segment finishes; `count` at/after the end. This
|
|
27
|
+
* is exactly a scrub cut position (segments `[0, result)` are done), so it maps straight to a
|
|
28
|
+
* segment-index scrub.
|
|
29
|
+
*/
|
|
30
|
+
export function segmentsCompletedAtTime(cumulativeMs, ms) {
|
|
31
|
+
let lo = 0;
|
|
32
|
+
let hi = cumulativeMs.length;
|
|
33
|
+
while (lo < hi) {
|
|
34
|
+
const mid = (lo + hi) >>> 1;
|
|
35
|
+
if (cumulativeMs[mid] <= ms)
|
|
36
|
+
lo = mid + 1;
|
|
37
|
+
else
|
|
38
|
+
hi = mid;
|
|
39
|
+
}
|
|
40
|
+
return lo;
|
|
41
|
+
}
|