@chestnutlabs/gcode-dialects 0.14.0 → 0.16.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/cura.js CHANGED
@@ -17,8 +17,8 @@ const TYPE_MAP = {
17
17
  support: FeatureRole.Support,
18
18
  'support-interface': FeatureRole.Support,
19
19
  skirt: FeatureRole.Skirt,
20
- raft: FeatureRole.Brim,
21
- 'prime-tower': FeatureRole.Custom,
20
+ raft: FeatureRole.Raft,
21
+ 'prime-tower': FeatureRole.PrimeTower,
22
22
  bridge: FeatureRole.Bridge
23
23
  };
24
24
  const state = new WeakMap();
@@ -0,0 +1,3 @@
1
+ import type { DialectAdapter } from './contracts.js';
2
+ export declare function ideaMaker(): DialectAdapter;
3
+ //# sourceMappingURL=ideamaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ideamaker.d.ts","sourceRoot":"","sources":["../src/ideamaker.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAkB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAmCrE,wBAAgB,SAAS,IAAI,cAAc,CA6C1C"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * ideaMaker dialect adapter (DD-026 T1, RR-007 §5.6).
3
+ *
4
+ * Annotates `;TYPE:` feature roles (ideaMaker's UPPERCASE vocabulary) and object membership from the
5
+ * `;PRINTING: <name>` + `;PRINTING_ID: <n>` STATE channel — `PRINTING_ID: -1` (with `PRINTING:
6
+ * NON-OBJECT`) is housekeeping, `PRINTING_ID: <n≥0>` is model — → `object` channel + `objects:'known'`.
7
+ * ideaMaker emits Marlin-flavoured motion; the marlin firmware adapter composes for firmware identity.
8
+ */
9
+ import { FeatureRole } from '@chestnutlabs/toolpath-core';
10
+ import { applyMarkerRanges } from './annotate.js';
11
+ import { PrintingObjectTracker } from './object-markers.js';
12
+ /** ideaMaker `;TYPE:` names → DD-001 FeatureRole (unknown names → Custom, honestly generic). */
13
+ const TYPE_MAP = {
14
+ 'wall-outer': FeatureRole.ExternalPerimeter,
15
+ 'wall-inner': FeatureRole.Perimeter,
16
+ fill: FeatureRole.Infill,
17
+ 'solid-fill': FeatureRole.SolidInfill,
18
+ support: FeatureRole.Support,
19
+ skirt: FeatureRole.Skirt,
20
+ raft: FeatureRole.Raft,
21
+ 'wipe-tower': FeatureRole.WipeTower,
22
+ bridge: FeatureRole.Bridge
23
+ };
24
+ const state = new WeakMap();
25
+ function stateFor(sink) {
26
+ let s = state.get(sink);
27
+ if (s === undefined) {
28
+ s = { markers: [], objectTracker: new PrintingObjectTracker() };
29
+ state.set(sink, s);
30
+ }
31
+ return s;
32
+ }
33
+ export function ideaMaker() {
34
+ return {
35
+ id: 'ideamaker',
36
+ displayName: 'ideaMaker',
37
+ kind: 'slicer',
38
+ detect(input) {
39
+ const m = /Sliced by (ideaMaker[^\n\r]*)/.exec(input.headText);
40
+ if (m !== null) {
41
+ return { dialectId: 'ideamaker', kind: 'slicer', confidence: 'known', evidence: `header: ${m[1].trim()}` };
42
+ }
43
+ return null;
44
+ },
45
+ onComment(comment, srcByte, sink) {
46
+ const s = stateFor(sink);
47
+ const trimmed = comment.trim();
48
+ if (trimmed.startsWith('TYPE:')) {
49
+ const role = TYPE_MAP[trimmed.slice(5).trim().toLowerCase()];
50
+ s.markers.push({ srcByte, value: role ?? FeatureRole.Custom });
51
+ return;
52
+ }
53
+ // Object membership STATE channel. `;PRINTING:` names the object (or NON-OBJECT), then
54
+ // `;PRINTING_ID:<n>` opens object n (n≥0) or closes to housekeeping (n<0).
55
+ if (trimmed.startsWith('PRINTING_ID:')) {
56
+ const id = trimmed.slice('PRINTING_ID:'.length).trim();
57
+ if (/^\d+$/.test(id)) {
58
+ s.objectTracker.markStart(id, s.pendingName, srcByte);
59
+ }
60
+ else {
61
+ s.objectTracker.markEnd(srcByte); // `-1` (NON-OBJECT) or any non-numeric → close
62
+ }
63
+ s.pendingName = undefined;
64
+ return;
65
+ }
66
+ if (trimmed.startsWith('PRINTING:')) {
67
+ const name = trimmed.slice('PRINTING:'.length).trim();
68
+ s.pendingName = /^non-object$/i.test(name) ? undefined : name;
69
+ return;
70
+ }
71
+ },
72
+ finalize(ir, sink) {
73
+ const s = stateFor(sink);
74
+ const applied = applyMarkerRanges(ir, s.markers, (a, b, v) => sink.setFeature(a, b, v));
75
+ if (applied > 0)
76
+ sink.upgradeCapability('featureRoles', 'known');
77
+ s.objectTracker.apply(ir, sink);
78
+ }
79
+ };
80
+ }
package/dist/index.d.ts CHANGED
@@ -14,6 +14,8 @@ export { BufferedAnnotationSink } from './sink.js';
14
14
  export { prusaSlicer } from './prusaslicer.js';
15
15
  export { orcaBambu } from './orca-bambu.js';
16
16
  export { cura } from './cura.js';
17
+ export { ideaMaker } from './ideamaker.js';
18
+ export { simplify3d } from './simplify3d.js';
17
19
  export { klipper, marlin, repRap } from './firmware.js';
18
20
  export { grblLaser, grblMill, linuxCnc } from './cnc.js';
19
21
  export type { ValidationTier, MachineClass } from './cnc.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,kBAAkB,EACnB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,kBAAkB,EACnB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,8 @@ export { BufferedAnnotationSink } from './sink.js';
3
3
  export { prusaSlicer } from './prusaslicer.js';
4
4
  export { orcaBambu } from './orca-bambu.js';
5
5
  export { cura } from './cura.js';
6
+ export { ideaMaker } from './ideamaker.js';
7
+ export { simplify3d } from './simplify3d.js';
6
8
  export { klipper, marlin, repRap } from './firmware.js';
7
9
  export { grblLaser, grblMill, linuxCnc } from './cnc.js';
8
10
  export { segAtOrAfterByte, applyMarkerRanges, parseKeyValue, parseAreaPoints, bedFromPoints, decodeBase64, ThumbnailCollector } from './annotate.js';
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Shared parsing for slicer object-membership comments (`; printing object …` / `; stop printing
3
+ * object …`), used by every Prusa/Orca/Bambu-lineage adapter. Grounded in the real marker formats
4
+ * catalogued in RR-007 §5:
5
+ * - Bambu Studio: `start printing object, unique label id: 89` (name may follow as `name: …`)
6
+ * - OrcaSlicer: `printing object <name> id:729618461984033312` (no "start"; id can exceed Uint32)
7
+ * - Anycubic / Prusa-lineage: `printing object "<name>" id:0 copy 0`
8
+ *
9
+ * The `object` channel is a `Uint32Array` (0 = none); raw slicer ids can exceed 2^32 and repeat per
10
+ * layer, so each DISTINCT id is mapped to a sequential 1-based channel value, reused across
11
+ * re-bracketing of the same object.
12
+ */
13
+ import type { ToolpathIR } from '@chestnutlabs/toolpath-core';
14
+ import type { AnnotationSink } from './contracts.js';
15
+ /**
16
+ * Match an object-start comment (the leading `; ` is already stripped) across all lineage formats,
17
+ * returning the raw object id (stable per object) + optional name, or null.
18
+ */
19
+ export declare function matchPrintingObjectStart(trimmed: string): {
20
+ id: string;
21
+ name?: string;
22
+ } | null;
23
+ /**
24
+ * Collects `; printing object` / `; stop printing object` markers during a parse and applies them as
25
+ * object-channel ranges at finalize. One instance per parse (per adapter state).
26
+ */
27
+ export declare class PrintingObjectTracker {
28
+ private readonly markers;
29
+ private readonly idToValue;
30
+ private readonly names;
31
+ private counter;
32
+ /** Try to consume a `; printing object` / `; stop printing object` comment; true if it was one. */
33
+ handle(trimmed: string, srcByte: number): boolean;
34
+ /**
35
+ * Open (or reopen) an object range by its stable slicer id — for adapters whose membership uses a
36
+ * non-comment marker convention (e.g. ideaMaker's `;PRINTING_ID:<n>` state). The id maps to a
37
+ * sequential 1-based channel value, reused if seen before.
38
+ */
39
+ markStart(id: string, name: string | undefined, srcByte: number): void;
40
+ /** Close the current object range (back to "no object"). */
41
+ markEnd(srcByte: number): void;
42
+ /** Apply the collected object ranges + names; upgrades the `objects` capability if any resolved. */
43
+ apply(ir: ToolpathIR, sink: AnnotationSink): number;
44
+ }
45
+ //# sourceMappingURL=object-markers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-markers.d.ts","sourceRoot":"","sources":["../src/object-markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgB9F;AAED;;;GAGG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IACnD,OAAO,CAAC,OAAO,CAAK;IAEpB,mGAAmG;IACnG,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAajD;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAUtE,4DAA4D;IAC5D,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI9B,oGAAoG;IACpG,KAAK,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,GAAG,MAAM;CAQpD"}
@@ -0,0 +1,78 @@
1
+ import { applyMarkerRanges } from './annotate.js';
2
+ /**
3
+ * Match an object-start comment (the leading `; ` is already stripped) across all lineage formats,
4
+ * returning the raw object id (stable per object) + optional name, or null.
5
+ */
6
+ export function matchPrintingObjectStart(trimmed) {
7
+ if (!/^(?:start\s+)?printing object\b/i.test(trimmed))
8
+ return null;
9
+ const idm = /\bid:?\s*(\d+)/i.exec(trimmed);
10
+ if (idm === null)
11
+ return null;
12
+ // Name: prefer an explicit `name:<…>` suffix (Bambu), else a quoted or bare name BEFORE the id
13
+ // (OrcaSlicer / Anycubic), rejecting `unique` / `unique label` filler.
14
+ let name = '';
15
+ const suffix = /\bname:\s*(.+?)\s*$/i.exec(trimmed);
16
+ if (suffix !== null) {
17
+ name = suffix[1].trim();
18
+ }
19
+ else {
20
+ const pre = /printing object[,:]?\s*(?:"([^"]*)"|(.+?))\s+(?:unique\s+(?:label\s+)?)?id:?/i.exec(trimmed);
21
+ name = (pre?.[1] ?? pre?.[2] ?? '').trim();
22
+ if (/^unique(\s+label)?$/i.test(name))
23
+ name = '';
24
+ }
25
+ return { id: idm[1], name: name.length > 0 ? name : undefined };
26
+ }
27
+ /**
28
+ * Collects `; printing object` / `; stop printing object` markers during a parse and applies them as
29
+ * object-channel ranges at finalize. One instance per parse (per adapter state).
30
+ */
31
+ export class PrintingObjectTracker {
32
+ markers = [];
33
+ idToValue = new Map();
34
+ names = new Map();
35
+ counter = 0;
36
+ /** Try to consume a `; printing object` / `; stop printing object` comment; true if it was one. */
37
+ handle(trimmed, srcByte) {
38
+ const start = matchPrintingObjectStart(trimmed);
39
+ if (start !== null) {
40
+ this.markStart(start.id, start.name, srcByte);
41
+ return true;
42
+ }
43
+ if (/^stop printing object/i.test(trimmed)) {
44
+ this.markEnd(srcByte);
45
+ return true;
46
+ }
47
+ return false;
48
+ }
49
+ /**
50
+ * Open (or reopen) an object range by its stable slicer id — for adapters whose membership uses a
51
+ * non-comment marker convention (e.g. ideaMaker's `;PRINTING_ID:<n>` state). The id maps to a
52
+ * sequential 1-based channel value, reused if seen before.
53
+ */
54
+ markStart(id, name, srcByte) {
55
+ let value = this.idToValue.get(id);
56
+ if (value === undefined) {
57
+ value = ++this.counter;
58
+ this.idToValue.set(id, value);
59
+ if (name !== undefined)
60
+ this.names.set(value, name);
61
+ }
62
+ this.markers.push({ srcByte, value });
63
+ }
64
+ /** Close the current object range (back to "no object"). */
65
+ markEnd(srcByte) {
66
+ this.markers.push({ srcByte, value: 0 });
67
+ }
68
+ /** Apply the collected object ranges + names; upgrades the `objects` capability if any resolved. */
69
+ apply(ir, sink) {
70
+ const applied = applyMarkerRanges(ir, this.markers, (a, b, v) => sink.setObject(a, b, v));
71
+ if (applied > 0) {
72
+ for (const [value, name] of this.names)
73
+ sink.defineObject(value, name);
74
+ sink.upgradeCapability('objects', 'known');
75
+ }
76
+ return applied;
77
+ }
78
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"orca-bambu.d.ts","sourceRoot":"","sources":["../src/orca-bambu.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAkB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAmErE,wBAAgB,SAAS,IAAI,cAAc,CAiH1C"}
1
+ {"version":3,"file":"orca-bambu.d.ts","sourceRoot":"","sources":["../src/orca-bambu.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAkB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAoErE,wBAAgB,SAAS,IAAI,cAAc,CA2G1C"}
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * OrcaSlicer / Bambu Studio dialect adapter (DD-005 phase 3, issue #75).
3
3
  *
4
- * Annotates: `; FEATURE:` roles (→ `featureRoles: 'known'`), Bambu object
5
- * start/stop comments (→ `object` channel + `objects: 'known'`), bed geometry
4
+ * Annotates: feature roles from `; FEATURE:` (Bambu Studio) **or** `;TYPE:`
5
+ * (real OrcaSlicer / AnycubicSlicerNext — same vocabulary, different prefix;
6
+ * RR-007 §5.8) → `featureRoles: 'known'`; object start/stop comments across all
7
+ * lineage formats (Bambu `start printing object, unique label id:`, Orca
8
+ * `printing object <name> id:<id>`) → `object` channel + `objects: 'known'`; bed geometry
6
9
  * from `printable_area` config comments (bare .gcode files — inside a
7
10
  * `.gcode.3mf` the container's machine config already provides `known`
8
11
  * geometry and outranks this), and printer identity. Detection also accepts
@@ -10,6 +13,7 @@
10
13
  */
11
14
  import { FeatureRole } from '@chestnutlabs/toolpath-core';
12
15
  import { parseFilamentTotals, parseHmsToSeconds } from './prusaslicer.js';
16
+ import { PrintingObjectTracker } from './object-markers.js';
13
17
  import { ThumbnailCollector, applyMarkerRanges, applyWipeRanges, bedFromPoints, matchWipeComment, parseAreaPoints, parseKeyValue } from './annotate.js';
14
18
  /** Orca/Bambu `; FEATURE:` names → DD-001 FeatureRole. */
15
19
  const FEATURE_MAP = {
@@ -27,8 +31,10 @@ const FEATURE_MAP = {
27
31
  'support transition': FeatureRole.Support,
28
32
  skirt: FeatureRole.Skirt,
29
33
  brim: FeatureRole.Brim,
30
- 'prime tower': FeatureRole.Custom,
34
+ raft: FeatureRole.Raft,
35
+ 'prime tower': FeatureRole.PrimeTower,
31
36
  'gap infill': FeatureRole.Infill,
37
+ 'internal bridge': FeatureRole.Bridge,
32
38
  custom: FeatureRole.Custom
33
39
  };
34
40
  const state = new WeakMap();
@@ -37,9 +43,8 @@ function stateFor(sink) {
37
43
  if (s === undefined) {
38
44
  s = {
39
45
  features: [],
40
- objects: [],
41
46
  wipes: [],
42
- objectNames: new Map(),
47
+ objectTracker: new PrintingObjectTracker(),
43
48
  bedPoints: null,
44
49
  thumbs: new ThumbnailCollector()
45
50
  };
@@ -70,23 +75,21 @@ export function orcaBambu() {
70
75
  onComment(comment, srcByte, sink) {
71
76
  const s = stateFor(sink);
72
77
  const trimmed = comment.trim();
73
- if (trimmed.startsWith('FEATURE:')) {
74
- const role = FEATURE_MAP[trimmed.slice(8).trim().toLowerCase()];
78
+ // Feature roles: Bambu Studio emits `; FEATURE:<vocab>`; real OrcaSlicer / AnycubicSlicerNext
79
+ // emit `;TYPE:<vocab>` — the same Orca vocabulary, different prefix (RR-007 §5.8). Accept both.
80
+ const featureName = trimmed.startsWith('FEATURE:')
81
+ ? trimmed.slice(8)
82
+ : trimmed.startsWith('TYPE:')
83
+ ? trimmed.slice(5)
84
+ : null;
85
+ if (featureName !== null) {
86
+ const role = FEATURE_MAP[featureName.trim().toLowerCase()];
75
87
  s.features.push({ srcByte, value: role ?? FeatureRole.Custom });
76
88
  return;
77
89
  }
78
- const start = /^start printing object,?\s*(?:unique\s+)?id:?\s*(\d+)(?:\s+name:\s*(.+))?/i.exec(trimmed);
79
- if (start !== null) {
80
- const value = parseInt(start[1]) + 1; // object channel is 1-based (0 = none)
81
- s.objects.push({ srcByte, value });
82
- if (start[2] !== undefined)
83
- s.objectNames.set(value, start[2].trim());
90
+ // Object membership across all lineage formats (shared object-marker parser).
91
+ if (s.objectTracker.handle(trimmed, srcByte))
84
92
  return;
85
- }
86
- if (/^stop printing object/i.test(trimmed)) {
87
- s.objects.push({ srcByte, value: 0 }); // range terminator
88
- return;
89
- }
90
93
  const wipe = matchWipeComment(comment);
91
94
  if (wipe !== null) {
92
95
  s.wipes.push({ srcByte, open: wipe === 'start' });
@@ -137,12 +140,7 @@ export function orcaBambu() {
137
140
  const features = applyMarkerRanges(ir, s.features, (a, b, v) => sink.setFeature(a, b, v));
138
141
  if (features > 0)
139
142
  sink.upgradeCapability('featureRoles', 'known');
140
- const objects = applyMarkerRanges(ir, s.objects, (a, b, v) => sink.setObject(a, b, v));
141
- if (objects > 0) {
142
- for (const [value, name] of s.objectNames)
143
- sink.defineObject(value, name);
144
- sink.upgradeCapability('objects', 'known');
145
- }
143
+ s.objectTracker.apply(ir, sink);
146
144
  if (applyWipeRanges(ir, s.wipes, sink) > 0)
147
145
  sink.upgradeCapability('wipeMoves', 'known');
148
146
  if (s.bedPoints !== null) {
@@ -1 +1 @@
1
- {"version":3,"file":"prusaslicer.d.ts","sourceRoot":"","sources":["../src/prusaslicer.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAgCrE,sGAAsG;AACtG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK7D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,cAAc,GACnB,OAAO,CAkBT;AAuBD,wBAAgB,WAAW,IAAI,cAAc,CAmF5C"}
1
+ {"version":3,"file":"prusaslicer.d.ts","sourceRoot":"","sources":["../src/prusaslicer.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAkCrE,sGAAsG;AACtG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK7D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,cAAc,GACnB,OAAO,CAkBT;AA8BD,wBAAgB,WAAW,IAAI,cAAc,CAuF5C"}
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { FeatureRole } from '@chestnutlabs/toolpath-core';
10
10
  import { ThumbnailCollector, applyMarkerRanges, applyWipeRanges, bedFromPoints, matchWipeComment, parseAreaPoints, parseKeyValue } from './annotate.js';
11
+ import { PrintingObjectTracker } from './object-markers.js';
11
12
  /** Prusa `;TYPE:` names → DD-001 FeatureRole (unknown names → Custom, honestly generic). */
12
13
  const TYPE_MAP = {
13
14
  perimeter: FeatureRole.Perimeter,
@@ -23,7 +24,8 @@ const TYPE_MAP = {
23
24
  'skirt/brim': FeatureRole.Skirt,
24
25
  skirt: FeatureRole.Skirt,
25
26
  brim: FeatureRole.Brim,
26
- 'wipe tower': FeatureRole.Custom,
27
+ raft: FeatureRole.Raft,
28
+ 'wipe tower': FeatureRole.WipeTower,
27
29
  custom: FeatureRole.Custom
28
30
  };
29
31
  /** Parse PrusaSlicer/Orca `<h>h <m>m <s>s` (any part optional) → seconds; null when zero/unparsed. */
@@ -62,7 +64,13 @@ const state = new WeakMap();
62
64
  function stateFor(sink) {
63
65
  let s = state.get(sink);
64
66
  if (s === undefined) {
65
- s = { markers: [], wipes: [], bedPoints: null, thumbs: new ThumbnailCollector() };
67
+ s = {
68
+ markers: [],
69
+ wipes: [],
70
+ objectTracker: new PrintingObjectTracker(),
71
+ bedPoints: null,
72
+ thumbs: new ThumbnailCollector()
73
+ };
66
74
  state.set(sink, s);
67
75
  }
68
76
  return s;
@@ -90,6 +98,10 @@ export function prusaSlicer() {
90
98
  s.markers.push({ srcByte, value: role ?? FeatureRole.Custom });
91
99
  return;
92
100
  }
101
+ // Object membership (`; printing object …` / `; stop printing object …`) when Label objects is
102
+ // enabled — RR-007 §5.1 / DD-026 T1. The comment retains a leading space, so trim for the parser.
103
+ if (s.objectTracker.handle(comment.trim(), srcByte))
104
+ return;
93
105
  const wipe = matchWipeComment(comment);
94
106
  if (wipe !== null) {
95
107
  s.wipes.push({ srcByte, open: wipe === 'start' });
@@ -135,6 +147,7 @@ export function prusaSlicer() {
135
147
  const applied = applyMarkerRanges(ir, s.markers, (a, b, v) => sink.setFeature(a, b, v));
136
148
  if (applied > 0)
137
149
  sink.upgradeCapability('featureRoles', 'known');
150
+ s.objectTracker.apply(ir, sink);
138
151
  if (applyWipeRanges(ir, s.wipes, sink) > 0)
139
152
  sink.upgradeCapability('wipeMoves', 'known');
140
153
  if (s.bedPoints !== null) {
@@ -0,0 +1,3 @@
1
+ import type { DialectAdapter } from './contracts.js';
2
+ export declare function simplify3d(): DialectAdapter;
3
+ //# sourceMappingURL=simplify3d.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simplify3d.d.ts","sourceRoot":"","sources":["../src/simplify3d.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAkB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAsCrE,wBAAgB,UAAU,IAAI,cAAc,CA6B3C"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Simplify3D dialect adapter (DD-026 T1, RR-007 §5.5).
3
+ *
4
+ * Annotates `; feature <lowercase token>` roles — Simplify3D's lowercase feature vocabulary. There is
5
+ * NO object-membership channel in Simplify3D output (RR-007 §5.5): the prime line is arbitrary
6
+ * Starting-Script geometry and the prime pillar / ooze shield are feature roles, not object brackets.
7
+ * So `objects` stays honestly `unavailable` and framing falls back to feature-role classification
8
+ * (DD-026 T2), never a fabricated membership. Simplify3D emits Marlin-flavoured motion; the marlin
9
+ * firmware adapter composes for firmware identity.
10
+ */
11
+ import { FeatureRole } from '@chestnutlabs/toolpath-core';
12
+ import { applyMarkerRanges } from './annotate.js';
13
+ /** Simplify3D `; feature <token>` names → DD-001 FeatureRole (unknown names → Custom, honestly generic). */
14
+ const FEATURE_MAP = {
15
+ skirt: FeatureRole.Skirt,
16
+ brim: FeatureRole.Brim,
17
+ raft: FeatureRole.Raft,
18
+ 'outer perimeter': FeatureRole.ExternalPerimeter,
19
+ 'inner perimeter': FeatureRole.Perimeter,
20
+ perimeter: FeatureRole.Perimeter,
21
+ infill: FeatureRole.Infill,
22
+ 'solid layer': FeatureRole.SolidInfill,
23
+ 'gap fill': FeatureRole.SolidInfill,
24
+ support: FeatureRole.Support,
25
+ 'dense support': FeatureRole.Support,
26
+ bridge: FeatureRole.Bridge,
27
+ 'prime pillar': FeatureRole.PrimeTower,
28
+ // Ooze shield is an unmapped housekeeping wall — kept as generic `Custom` (stays in-frame, the safe
29
+ // direction per DD-026 §13) rather than guessed into a tower/purge role.
30
+ 'ooze shield': FeatureRole.Custom
31
+ };
32
+ const state = new WeakMap();
33
+ function stateFor(sink) {
34
+ let s = state.get(sink);
35
+ if (s === undefined) {
36
+ s = { markers: [] };
37
+ state.set(sink, s);
38
+ }
39
+ return s;
40
+ }
41
+ export function simplify3d() {
42
+ return {
43
+ id: 'simplify3d',
44
+ displayName: 'Simplify3D',
45
+ kind: 'slicer',
46
+ detect(input) {
47
+ const m = /generated by (Simplify3D[^\n\r]*)/i.exec(input.headText);
48
+ if (m !== null) {
49
+ return { dialectId: 'simplify3d', kind: 'slicer', confidence: 'known', evidence: `header: ${m[1].trim()}` };
50
+ }
51
+ return null;
52
+ },
53
+ onComment(comment, srcByte, sink) {
54
+ const s = stateFor(sink);
55
+ // Space-prefixed lowercase feature comment: `; feature outer perimeter`. The leading space is
56
+ // already stripped by the parser before onComment, so `comment` begins `feature `.
57
+ const trimmed = comment.trim();
58
+ if (/^feature\b/i.test(trimmed)) {
59
+ const token = trimmed.slice('feature'.length).trim().toLowerCase();
60
+ const role = FEATURE_MAP[token];
61
+ s.markers.push({ srcByte, value: role ?? FeatureRole.Custom });
62
+ }
63
+ },
64
+ finalize(ir, sink) {
65
+ const s = stateFor(sink);
66
+ const applied = applyMarkerRanges(ir, s.markers, (a, b, v) => sink.setFeature(a, b, v));
67
+ if (applied > 0)
68
+ sink.upgradeCapability('featureRoles', 'known');
69
+ }
70
+ };
71
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chestnutlabs/gcode-dialects",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Slicer/firmware dialect adapters for ToolpathIR annotation (DD-005). Adapters annotate metadata and optional channels — they can never alter geometry.",
5
5
  "keywords": [
6
6
  "gcode",
@@ -45,6 +45,6 @@
45
45
  "test": "vitest run"
46
46
  },
47
47
  "dependencies": {
48
- "@chestnutlabs/toolpath-core": "0.14.0"
48
+ "@chestnutlabs/toolpath-core": "0.16.0"
49
49
  }
50
50
  }