three-slicer 0.1.2 → 0.1.3

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.
@@ -1,4 +1,4 @@
1
- // The single import site for the 4 extracted JSON files.
1
+ // The single import site for the extracted JSON files.
2
2
  //
3
3
  // Why funnel them through one module: JSON requires `with { type: 'json' }` in Node 22+ ESM, but
4
4
  // Vite/esbuild strip that attribute from bundle output (neither build.target nor importAttributesKey
@@ -10,3 +10,9 @@ export { default as schema } from 'three-slicer/data/config-schema.json' with {
10
10
  export { default as uiTree } from 'three-slicer/data/ui-tree.json' with { type: 'json' }
11
11
  export { default as toggleRules } from 'three-slicer/data/toggle-rules.json' with { type: 'json' }
12
12
  export { default as invalidationMap } from 'three-slicer/data/invalidation-map.json' with { type: 'json' }
13
+ export { default as printers } from 'three-slicer/data/printers.json' with { type: 'json' }
14
+ // processes is the largest artifact (~800 KB) and is only needed once a printer is picked, so it stays a dynamic
15
+ // import. It is generated as a JS module rather than JSON precisely because of the attribute problem above, in its
16
+ // other direction: a dynamic JSON import needs the attribute in Node, and that same attribute makes browsers
17
+ // reject the text/javascript response a dev server gives for .json.
18
+ export const loadProcesses = () => import('three-slicer/data/processes.js').then(m => m.default)
@@ -1,7 +1,7 @@
1
1
  // Maps the actual values of the (editable) right-hand settings panel -> kernel slice parameters.
2
2
  // - Settings state is a sparse map (key->value): only edited keys are stored, otherwise the config-schema default.
3
3
  // - For vector types (coFloats/coInts, …) only the first element is used/edited (simplification).
4
- import { schema } from './data.js'
4
+ import { schema, printers, loadProcesses } from './data.js'
5
5
 
6
6
  export function schemaDefault(key) { return schema[key]?.default }
7
7
  export function settingRaw(settings, key) { return (settings && key in settings) ? settings[key] : schemaDefault(key) }
@@ -13,6 +13,82 @@ const KERNEL_PATTERNS = ['rectilinear', 'grid', 'triangles', 'zigzag', 'gyroid',
13
13
  'honeycomb', '3dhoneycomb', 'crosshatch', 'concentric']
14
14
  const KERNEL_SEAMS = ['nearest', 'aligned', 'back', 'random']
15
15
 
16
+ // Machine limits (the "Motion ability" printer page) -> kernel time-estimate parameters.
17
+ // Declarative on purpose: the kernel collapses X/Y into one axis, so the mapping cannot be a plain pass-through,
18
+ // but adding a limit stays a data row instead of code. The fallback is the kernel's own default, so an unedited
19
+ // profile produces exactly the previous estimate. Upstream leaves machine_max_*_x/y/z/e without a schema default
20
+ // (they only ever come from a printer preset), hence the explicit fallbacks here.
21
+ const MACHINE_LIMITS = {
22
+ machine_max_speed_xy: ['machine_max_speed_x', 500],
23
+ machine_max_speed_z: ['machine_max_speed_z', 12],
24
+ machine_max_speed_e: ['machine_max_speed_e', 30],
25
+ machine_max_accel_xy: ['machine_max_acceleration_x', 5000],
26
+ machine_max_accel_z: ['machine_max_acceleration_z', 500],
27
+ machine_max_accel_e: ['machine_max_acceleration_e', 5000],
28
+ machine_jerk_xy: ['machine_max_jerk_x', 9],
29
+ machine_jerk_z: ['machine_max_jerk_z', 0.4],
30
+ machine_jerk_e: ['machine_max_jerk_e', 2.5],
31
+ machine_accel_print: ['default_acceleration', 5000],
32
+ machine_accel_travel: ['travel_acceleration', 5000],
33
+ machine_accel_retract: ['machine_max_acceleration_retracting', 5000],
34
+ }
35
+ // The schema keys the machine limits are read from — for UI that has to show "which printer characteristics are in play"
36
+ // without hardcoding key strings of its own.
37
+ export const machineLimitKeys = Object.values(MACHINE_LIMITS).map(([key]) => key)
38
+
39
+ // ---- Printer profiles -------------------------------------------------------
40
+ // printers.json is stored column-oriented (see its .d.ts). These two hide that layout so no consumer decodes it.
41
+
42
+ /** Every option key a printer profile can set — what to clear before applying a different printer. */
43
+ export const printerKeys = printers.keys
44
+
45
+ /** Vendor -> profile name -> `[nozzle, setIndex, model]`, straight from the data (for building a picker). */
46
+ export const printersByVendor = printers.byVendor
47
+
48
+ // Process (print) presets live in the ~800 KB processes.json, so they load on demand — the first call fetches,
49
+ // later ones reuse the same promise. Returns a small facade so no caller has to know the column layout.
50
+ let processesPromise = null
51
+ export function processPresets() {
52
+ processesPromise ??= loadProcesses().then(data => ({
53
+ /** Every key a process preset can set — clear these before applying a different one */
54
+ keys: data.keys,
55
+ /** Preset names compatible with a printer profile, in upstream order */
56
+ listFor: (printerProfileName) =>
57
+ (data.byPrinter[printerProfileName] ?? []).map(i => data.presets[i][0]),
58
+ /** The settings a preset applies, ready to merge. `null` when unknown. */
59
+ settingsFor: (presetName) => {
60
+ const preset = data.presets.find(([name]) => name === presetName)
61
+ if (!preset) return null
62
+ const row = data.sets[preset[1]], out = {}
63
+ data.keys.forEach((key, i) => { if (row[i] != null) out[key] = row[i] })
64
+ return out
65
+ },
66
+ }))
67
+ return processesPromise
68
+ }
69
+
70
+ function printerEntry(profileName) {
71
+ for (const models of Object.values(printers.byVendor)) {
72
+ const entry = models[profileName]
73
+ if (entry) return entry
74
+ }
75
+ return null
76
+ }
77
+
78
+ /** The settings a printer profile applies, ready to merge into the settings map. `null` when unknown. */
79
+ export function printerSettings(profileName) {
80
+ const entry = printerEntry(profileName)
81
+ if (!entry) return null
82
+ const row = printers.sets[entry[1]]
83
+ const out = {}
84
+ printers.keys.forEach((key, i) => { if (row[i] != null) out[key] = row[i] })
85
+ return out
86
+ }
87
+
88
+ /** The vendor's recommended process preset for a printer, or '' when the profile names none. */
89
+ export function printerDefaultPreset(profileName) { return printerEntry(profileName)?.[3] ?? '' }
90
+
91
+
16
92
  // Right-panel settings values -> kernel parameters (derived from schema keys)
17
93
  export function deriveKernelParams(settings) {
18
94
  const S = k => settingScalar(settings, k)
@@ -49,8 +125,12 @@ export function deriveKernelParams(settings) {
49
125
  'sparse_infill_line_width','internal_solid_infill_line_width','initial_layer_line_width']) {
50
126
  const v = S(k); if (v != null && v !== '') widths[k] = v
51
127
  }
128
+ const machine = {}
129
+ for (const [param, [key, fallback]] of Object.entries(MACHINE_LIMITS)) machine[param] = num(key, fallback)
130
+
52
131
  return {
53
132
  ...widths,
133
+ ...machine,
54
134
  layer_height: num('layer_height', 0.2),
55
135
  first_layer_height: num('initial_layer_print_height', 0.2),
56
136
  line_width,
@@ -80,6 +160,11 @@ export function deriveKernelParams(settings) {
80
160
  nozzle_temp: num('nozzle_temperature', 200),
81
161
  bed_temp: num('hot_plate_temp', 45), // no bed_temperature key -> hot_plate_temp
82
162
  bed_width, bed_depth,
163
+ bed_height: num('printable_height', 0), // 0 = profile states no ceiling -> kernel skips the check
164
+ // Explicitly-set only: the schema default here is upstream's generic "G28 / G1 Z5" preamble, and falling back
165
+ // to it would rewrite the emitted G-code for every caller. The kernel keeps its own preamble when these are empty.
166
+ machine_start_gcode: String(settings?.machine_start_gcode ?? ''),
167
+ machine_end_gcode: String(settings?.machine_end_gcode ?? ''),
83
168
  enable_support: bool('enable_support', false),
84
169
  support_threshold_angle: num('support_threshold_angle', 30),
85
170
  support_top_z_distance: num('support_top_z_distance', 0.2),
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-slicer",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Browser/WASM 3D-printing slicer reverse-engineered from OrcaSlicer: slicing engine (Arachne walls, tree/grid supports, multi-material), React viewer with GPU toolpath preview, and schema-driven settings UI.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "type": "module",
@@ -78,6 +78,8 @@
78
78
  "data/ui-tree.json",
79
79
  "data/toggle-rules.json",
80
80
  "data/invalidation-map.json",
81
+ "data/printers.json",
82
+ "data/processes.js",
81
83
  "README.md",
82
84
  "LICENSE.txt"
83
85
  ],
@@ -86,7 +88,7 @@
86
88
  "url": "git+https://github.com/kimgh06/Web_Three_Slicer.git",
87
89
  "directory": "packages"
88
90
  },
89
- "homepage": "https://github.com/kimgh06/Web_Three_Slicer#readme",
91
+ "homepage": "https://slicer.kimgh06.com",
90
92
  "keywords": [
91
93
  "3d-printing",
92
94
  "slicer",
@@ -0,0 +1,23 @@
1
+ // printers.json — per-printer settings extracted from OrcaSlicer's resources/profiles/<Vendor>/machine/.
2
+ // Column layout: `keys` names the columns once and each entry in `sets` is a positional row, because
3
+ // repeating the key names per printer costs more than all the values together. Rows are deduplicated —
4
+ // a printer's nozzle variants usually share one row — so a printer holds an index rather than its own copy.
5
+ // Prefer `printerSettings()` from three-slicer/settings over decoding this by hand.
6
+
7
+ /** A positional row aligned to `keys`; `null` where the profile chain never set that option. */
8
+ export type PrinterRow = (unknown | null)[]
9
+
10
+ /** `[nozzle diameter, index into sets, model name without the nozzle suffix]` */
11
+ export type PrinterEntry = [string, number, string]
12
+
13
+ export interface PrinterData {
14
+ /** Option keys, in column order — the same keys the settings panel edits */
15
+ keys: string[]
16
+ /** Deduplicated value rows */
17
+ sets: PrinterRow[]
18
+ /** Vendor -> printer profile name -> entry */
19
+ byVendor: Record<string, Record<string, PrinterEntry>>
20
+ }
21
+
22
+ declare const printers: PrinterData
23
+ export default printers
@@ -0,0 +1,21 @@
1
+ // processes.js — print (process) presets extracted from OrcaSlicer's resources/profiles/<Vendor>/process/.
2
+ // Emitted as a JS module rather than JSON because it is loaded dynamically: see engine/src/data.js for why.
3
+ // Column layout, same as printers.json — `keys` names the columns once and each row in `sets` is positional.
4
+ // Prefer `processPresets()` from three-slicer/settings over decoding this by hand.
5
+
6
+ /** A positional row aligned to `keys`; `null` where the profile chain never set that option. */
7
+ export type ProcessRow = (unknown | null)[]
8
+
9
+ export interface ProcessData {
10
+ /** Option keys, in column order — the keys the kernel consumes, read out of engine/src/settings.js */
11
+ keys: string[]
12
+ /** Deduplicated value rows */
13
+ sets: ProcessRow[]
14
+ /** `[preset name, index into sets]`, indexed by the numbers in `byPrinter` */
15
+ presets: [string, number][]
16
+ /** Printer profile name -> indices into `presets` */
17
+ byPrinter: Record<string, number[]>
18
+ }
19
+
20
+ declare const processes: ProcessData
21
+ export default processes
package/types/data.d.ts CHANGED
@@ -1,11 +1,12 @@
1
- // three-slicer/data — the 4 extracted JSON files. Prefer this over importing the raw JSON:
1
+ // three-slicer/data — the extracted JSON files. Prefer this over importing the raw JSON:
2
2
  // the import attribute (`with { type: 'json' }`) is already applied, so it works in Node and bundlers alike.
3
3
  import type { ConfigOption } from './data/config-schema.json.d.ts'
4
4
  import type { UIPage } from './data/ui-tree.json.d.ts'
5
5
  import type { ToggleGroup } from './data/toggle-rules.json.d.ts'
6
6
  import type { InvalidationRule } from './data/invalidation-map.json.d.ts'
7
+ import type { PrinterData, PrinterRow, PrinterEntry } from './data/printers.json.d.ts'
7
8
 
8
- export type { ConfigOption, UIPage, ToggleGroup, InvalidationRule }
9
+ export type { ConfigOption, UIPage, ToggleGroup, InvalidationRule, PrinterData, PrinterRow, PrinterEntry }
9
10
 
10
11
  /** The 907 option definitions */
11
12
  export const schema: Record<string, ConfigOption>
@@ -15,3 +16,5 @@ export const uiTree: Record<string, UIPage[]>
15
16
  export const toggleRules: Record<string, ToggleGroup>
16
17
  /** Setting key change -> invalidated steps */
17
18
  export const invalidationMap: Record<string, InvalidationRule[]>
19
+ /** Machine limits per printer, from the upstream vendor profiles */
20
+ export const printers: PrinterData
@@ -1,5 +1,5 @@
1
1
  // AUTO-GENERATED by types/gen_settings_types.mjs — DO NOT EDIT.
2
- // Source: data/config-schema.json (907 keys)
2
+ // Source: data/config-schema.json (906 keys)
3
3
 
4
4
  /** [x, y] — coPoint family */
5
5
  export type Point = [number, number]
@@ -831,6 +831,7 @@ export interface SlicerSettings {
831
831
  machine_hotend_change_time?: number
832
832
  /** Filament load time — (s) — Time to load new filament when switch filament. It's usually applicable for single-extruder multi-material machines. For tool changers or multi-tool machines, it's typically 0. For statistics only. */
833
833
  machine_load_filament_time?: number
834
+ /** (mm/s²) — Maximum acceleration of the E axis */
834
835
  machine_max_acceleration_e?: number[] | number
835
836
  /** (mm/s²) — Maximum acceleration for extruding (M204 P) */
836
837
  machine_max_acceleration_extruding?: number[] | number
@@ -838,22 +839,33 @@ export interface SlicerSettings {
838
839
  machine_max_acceleration_retracting?: number[] | number
839
840
  /** (mm/s²) — Maximum acceleration for travel (M204 T), it only applies to Marlin 2. */
840
841
  machine_max_acceleration_travel?: number[] | number
842
+ /** (mm/s²) — Maximum acceleration of the X axis */
841
843
  machine_max_acceleration_x?: number[] | number
844
+ /** (mm/s²) — Maximum acceleration of the Y axis */
842
845
  machine_max_acceleration_y?: number[] | number
846
+ /** (mm/s²) — Maximum acceleration of the Z axis */
843
847
  machine_max_acceleration_z?: number[] | number
844
848
  /** (N) — The allowed maximum output force of Y axis */
845
849
  machine_max_force_Y?: number
850
+ /** (mm/s) — Maximum jerk of the E axis */
846
851
  machine_max_jerk_e?: number[] | number
852
+ /** (mm/s) — Maximum jerk of the X axis */
847
853
  machine_max_jerk_x?: number[] | number
854
+ /** (mm/s) — Maximum jerk of the Y axis */
848
855
  machine_max_jerk_y?: number[] | number
856
+ /** (mm/s) — Maximum jerk of the Z axis */
849
857
  machine_max_jerk_z?: number[] | number
850
858
  /** (mm) — Maximum junction deviation (M205 J, only apply if JD > 0 for Marlin Firmware If your Marlin 2 printer uses Classic Jerk set this value to 0.) */
851
859
  machine_max_junction_deviation?: number[] | number
852
860
  /** (g) — The allowed max printed mass on a plate */
853
861
  machine_max_printed_mass?: number
862
+ /** (mm/s) — Maximum speed of E axis */
854
863
  machine_max_speed_e?: number[] | number
864
+ /** (mm/s) — Maximum speed of X axis */
855
865
  machine_max_speed_x?: number[] | number
866
+ /** (mm/s) — Maximum speed of Y axis */
856
867
  machine_max_speed_y?: number[] | number
868
+ /** (mm/s) — Maximum speed of Z axis */
857
869
  machine_max_speed_z?: number[] | number
858
870
  /** (mm/s) — Minimum speed for extruding (M205 S) */
859
871
  machine_min_extruding_rate?: number[] | number
@@ -1056,8 +1068,6 @@ export interface SlicerSettings {
1056
1068
  physical_printer_preset?: string
1057
1069
  /** Send progress to pipe — Send progress to pipe. */
1058
1070
  pipe?: string
1059
- /** Capabilities — Configuration for the plugin capabilities this preset uses, overriding the global Capabilities configuration. Stored as a raw JSON array and edited through the dialog behind the button, never typed in directly. */
1060
- plugin_config_overrides?: string
1061
1071
  /** Plugins Used — Plugin capabilities referenced by this preset, stored as name;uuid;capability. */
1062
1072
  plugins?: string[] | string
1063
1073
  /** Position — Position of the extruder at the beginning of the custom G-code block. If the custom G-code travels somewhere else, it should write to this variable so OrcaSlicer knows where it travels from when it gets control back. */
@@ -1074,6 +1084,7 @@ export interface SlicerSettings {
1074
1084
  preheat_steps?: number
1075
1085
  /** Preheat time — (s) — To reduce the waiting time after tool change, Orca can preheat the next tool while the current tool is still in use. This setting specifies the time in seconds to preheat the next tool. Orca will insert a M104 command to preheat the tool in advance. */
1076
1086
  preheat_time?: number
1087
+ /** Capabilities — Configuration for the plugin capabilities this preset uses, overriding the global Capabilities configuration. Stored as a raw JSON array and edited through the dialog behind the button, never typed in directly. */
1077
1088
  preset_name?: string
1078
1089
  /** Printer preset names — Names of presets related to the physical printer. */
1079
1090
  preset_names?: string[] | string
package/viewer/styles.css CHANGED
@@ -147,6 +147,27 @@
147
147
  text-shadow: 0 1px 2px #000; pointer-events: none; }
148
148
  /* ---- Slice status display (viewport overlay) ---- */
149
149
  .slice-warn { color: #f0c060; margin: 4px 0; }
150
+ /* Basis line under the print-time estimate — the machine limits the kernel reported using */
151
+ .sc-sub { font-size: 10.5px; opacity: .62; margin: 1px 0 3px; }
152
+ /* Collapsible card section holding an injected settings page (printer motion ability) */
153
+ .sc-fold > summary { display: flex; justify-content: space-between; align-items: center; font-size: 12px;
154
+ color: #5a6069; padding: 2px 0; cursor: pointer; list-style: none; }
155
+ .sc-fold > summary::-webkit-details-marker { display: none; }
156
+ .sc-fold-lbl::before { content: '▸'; display: inline-block; width: 10px; font-size: 9px; opacity: .5; }
157
+ .sc-fold[open] .sc-fold-lbl::before { content: '▾'; }
158
+ .sc-fold > summary b { color: #24282e; }
159
+ .sc-fold[open] > summary { border-bottom: 1px solid #e3e6ea; margin-bottom: 4px; }
160
+ /* The injected page is a full settings form — keep it from stretching the card unboundedly */
161
+ .sc-fold-body { max-height: 320px; overflow-y: auto; }
162
+ .sc-model { max-width: 62%; font-size: 11.5px; padding: 1px 2px; }
163
+ /* Support helpers grouped under the "Generate support" toggle */
164
+ .support-tools { display: flex; align-items: center; justify-content: space-between; gap: 8px;
165
+ margin: 2px 0 2px 16px; padding-left: 6px; border-left: 2px solid #e3e6ea; }
166
+ .sup-style { margin-left: 16px; padding-left: 6px; border-left: 2px solid #e3e6ea; }
167
+ .sup-paint { font-size: 11px; padding: 2px 8px; border: 1px solid #c9ced6; border-radius: 5px;
168
+ background: #fff; color: #24282e; cursor: pointer; white-space: nowrap; }
169
+ .sup-paint.on { background: #2b6cff; border-color: #2b6cff; color: #fff; }
170
+ .sup-paint:disabled { opacity: .45; cursor: not-allowed; }
150
171
  .slice-btn { width: 100%; margin-top: 6px; padding: 7px; border: 0; border-radius: 7px;
151
172
  background: #00ae42; color: #fff; font-weight: 700; cursor: pointer; }
152
173
  .slice-btn:disabled { background: #3a4650; color: #8b98a2; cursor: not-allowed; }