three-slicer 0.3.3 → 0.3.5

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/engine/PARAMS.md CHANGED
@@ -8,7 +8,7 @@ cannot drift from what `slice()` actually honours. Regenerate with `node types/g
8
8
 
9
9
  <!-- KERNEL-PARAMS:BEGIN — generated by types/gen_kernel_params.mjs, do not edit by hand -->
10
10
 
11
- The kernel accepts **189 parameters**. `deriveKernelParams()` produces 160 of them from the
11
+ The kernel accepts **238 parameters**. `deriveKernelParams()` produces 209 of them from the
12
12
  OrcaSlicer settings map; the remaining 29 have no schema key behind them and can only be set by writing
13
13
  the params object directly — those rows show `—` in the *From setting* column.
14
14
 
@@ -46,21 +46,24 @@ same unit — `first_layer_height` comes from `initial_layer_print_height`, and
46
46
  | `bottom_shell_layers` | `number` | `3` | `bottom_shell_layers` | top_shell_layers/bottom_shell_layers |
47
47
  | `infill_angle` | `number` | `45.0` | `infill_direction` | infill_direction |
48
48
  | `infill_density` | `number` | `0.15` | `sparse_infill_density` | |
49
+ | `infill_jerk` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `infill_jerk`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
49
50
  | `infill_wall_overlap` | `number` | `15.0` | `infill_wall_overlap` | infill_wall_overlap (percent) |
50
51
  | `ironing_flow` | `number` | `10.0` | `ironing_flow` | ironing flow (%) |
51
52
  | `ironing_spacing` | `number` | `0.1` | `ironing_spacing` | ironing line spacing (mm) |
52
53
  | `ironing_speed` | `number` | `30.0` | `ironing_speed` | ironing speed (mm/s) |
53
54
  | `ironing_type` | `string` | `'none'` | `ironing_type` | none\|top\|topmost\|solid (the top family = on) |
55
+ | `sparse_infill_acceleration` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `sparse_infill_acceleration`, `time_lapse_gcode` | |
54
56
  | `sparse_infill_filament_id` | `number` | `0` | `sparse_infill_filament_id` | |
55
57
  | `sparse_infill_flow_ratio` | `number` | `1.0` | `sparse_infill_flow_ratio` | |
56
58
  | `sparse_infill_pattern` | `string` | `'rectilinear'` | `sparse_infill_pattern` | rectilinear\|grid\|triangles\|zigzag\|gyroid |
59
+ | `sparse_infill_speed` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `sparse_infill_speed`, `time_lapse_gcode` | |
57
60
  | `top_shell_layers` | `number` | `4` | `top_shell_layers` | top_shell_layers/bottom_shell_layers |
58
61
 
59
62
  ### Speeds, flow and temperature
60
63
 
61
64
  | Kernel param | Type | Default | From setting | Notes |
62
65
  | --- | --- | --- | --- | --- |
63
- | `bed_temp` | `number` | `60` | `hot_plate_temp` | |
66
+ | `bed_temp` | `number` | `60` | `curr_bed_type`, `hot_plate_temp` | |
64
67
  | `bridge_speed` | `number` | `25.0` | `bridge_speed` | bridge_speed[0] (slowdown for unsupported bottoms) |
65
68
  | `close_fan_the_first_x_layers` | `number` | `1` | `close_fan_the_first_x_layers` | close_fan_the_first_x_layers |
66
69
  | `enable_pressure_advance` | `boolean` | `false` | `enable_pressure_advance` | enable_pressure_advance[0] |
@@ -78,6 +81,10 @@ same unit — `first_layer_height` comes from `initial_layer_print_height`, and
78
81
  | `machine_max_accel_e` | `number` | `5000` | `machine_max_acceleration_e` | mm/s² (machine_max_acceleration_*) — per-axis ceiling, distinct from the accel_* feedrates above |
79
82
  | `machine_max_accel_xy` | `number` | `5000` | `machine_max_acceleration_x` | mm/s² (machine_max_acceleration_*) — per-axis ceiling, distinct from the accel_* feedrates above |
80
83
  | `machine_max_accel_z` | `number` | `500` | `machine_max_acceleration_z` | mm/s² (machine_max_acceleration_*) — per-axis ceiling, distinct from the accel_* feedrates above |
84
+ | `machine_max_acceleration_extruding` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_max_acceleration_extruding`, `machine_start_gcode`, `time_lapse_gcode` | |
85
+ | `machine_max_acceleration_travel` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_max_acceleration_travel`, `machine_start_gcode`, `time_lapse_gcode` | |
86
+ | `machine_max_acceleration_y` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_max_acceleration_y`, `machine_start_gcode`, `time_lapse_gcode` | |
87
+ | `machine_max_jerk_y` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_max_jerk_y`, `machine_start_gcode`, `time_lapse_gcode` | |
81
88
  | `machine_max_speed_e` | `number` | `30` | `machine_max_speed_e` | mm/s |
82
89
  | `machine_max_speed_xy` | `number` | `500` | `machine_max_speed_x` | mm/s |
83
90
  | `machine_max_speed_z` | `number` | `12` | `machine_max_speed_z` | mm/s |
@@ -87,6 +94,7 @@ same unit — `first_layer_height` comes from `initial_layer_print_height`, and
87
94
  | `pressure_advance` | `number` | `0.02` | `pressure_advance` | pressure_advance[0] |
88
95
  | `print_speed` | `number` | `60` | `outer_wall_speed` | |
89
96
  | `slow_down_layer_time` | `number` | `8.0` | `slow_down_layer_time` | slow_down_layer_time (s) |
97
+ | `slow_down_layers` | `number` | `0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `slow_down_layers`, `time_lapse_gcode` | layers over which the first layer's speed ramps up to the print's |
90
98
  | `travel_speed` | `number` | `150` | `travel_speed` | |
91
99
 
92
100
  ### Retraction and seams
@@ -115,6 +123,7 @@ same unit — `first_layer_height` comes from `initial_layer_print_height`, and
115
123
  | `skirt_distance` | `number` | `2.0` | `skirt_distance` | skirt_distance / brim_width |
116
124
  | `skirt_height` | `number` | `1` | `skirt_height` | skirt_height: how many layers the skirt is drawn on |
117
125
  | `skirt_loops` | `number` | `1` | `skirt_loops` | skirt_loops |
126
+ | `skirt_speed` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `skirt_speed`, `time_lapse_gcode` | 0 = follow the role's speed |
118
127
 
119
128
  ### Support
120
129
 
@@ -137,11 +146,13 @@ same unit — `first_layer_height` comes from `initial_layer_print_height`, and
137
146
  | `support_interface_flow_ratio` | `?` | — | `support_interface_flow_ratio` | undefined |
138
147
  | `support_interface_pattern` | `string` | `'auto'` | `support_interface_pattern` | support_interface_pattern: auto\|rectilinear\|concentric\|rectilinear_interlaced\|grid |
139
148
  | `support_interface_spacing` | `number` | `0.5` | `support_interface_spacing` | support_interface_spacing (mm, 0=solid). Upstream default 0.5 |
149
+ | `support_interface_speed` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `support_interface_speed`, `time_lapse_gcode` | |
140
150
  | `support_interface_top_layers` | `number` | `2` | `support_interface_top_layers` | support_interface_top_layers |
141
151
  | `support_object_first_layer_gap` | `number` | `0.2` | `support_object_first_layer_gap` | support_object_first_layer_gap (mm) |
142
152
  | `support_on_build_plate_only` | `boolean` | `false` | `support_on_build_plate_only` | support_on_build_plate_only: only support reaching the bed |
143
153
  | `support_overhang_min_area` | `number` | `0.0` | — | minimum overhang area (mm², 0 = auto w²). Replaces the morphological opening filter |
144
154
  | `support_remove_small_overhang` | `boolean` | `true` | `support_remove_small_overhang` | support_remove_small_overhang (upstream default true) |
155
+ | `support_speed` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `support_speed`, `time_lapse_gcode` | |
145
156
  | `support_style` | `string` | `'grid'` | `support_style`, `support_type` | grid\|tree_lite |
146
157
  | `support_threshold_angle` | `number` | `30.0` | `support_threshold_angle` | support_threshold_angle |
147
158
  | `support_threshold_overlap` | `number` | `0.5` | `support_threshold_overlap` | support_threshold_overlap: the overlap criterion when θ=0 (as a fraction of extrusion width) |
@@ -183,9 +194,11 @@ same unit — `first_layer_height` comes from `initial_layer_print_height`, and
183
194
 
184
195
  | Kernel param | Type | Default | From setting | Notes |
185
196
  | --- | --- | --- | --- | --- |
186
- | `bed_depth` | `number` | `256.0` | `printable_area` | bed size (offset = bed/2) |
197
+ | `bed_depth` | `number` | `256.0` | `printable_area` | bed size |
187
198
  | `bed_height` | `number` | `0.0` | `printable_height` | printable_height (mm). 0 = no ceiling (backwards compatible) |
188
- | `bed_width` | `number` | `256.0` | `printable_area` | bed size (offset = bed/2) |
199
+ | `bed_origin_x` | `number` | `0.0` | `printable_area` | |
200
+ | `bed_origin_y` | `number` | `0.0` | `printable_area` | |
201
+ | `bed_width` | `number` | `256.0` | `printable_area` | bed size |
189
202
  | `enable_arc_fitting` | `boolean` | `false` | `enable_arc_fitting` | enable_arc_fitting |
190
203
  | `machine_end_gcode` | `string` | — | `machine_end_gcode` | printer profile custom G-code. Empty = the mini-kernel's own preamble/footer only |
191
204
  | `machine_start_gcode` | `string` | — | `machine_start_gcode` | printer profile custom G-code. Empty = the mini-kernel's own preamble/footer only |
@@ -195,45 +208,69 @@ same unit — `first_layer_height` comes from `initial_layer_print_height`, and
195
208
 
196
209
  | Kernel param | Type | Default | From setting | Notes |
197
210
  | --- | --- | --- | --- | --- |
211
+ | `accel_to_decel_enable` | `boolean` | `false` | `accel_to_decel_enable`, `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
212
+ | `accel_to_decel_factor` | `number` | `50.0` | `accel_to_decel_factor`, `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | percent |
198
213
  | `alternate_extra_wall` | `boolean` | `false` | `alternate_extra_wall` | alternate_extra_wall |
199
214
  | `arachne_dump` | `boolean` | `false` | — | temporary diagnostic: dump the PASS1 arachne input polygons to stderr |
200
215
  | `bottom_solid_infill_flow_ratio` | `number` | `1.0` | `bottom_solid_infill_flow_ratio` | |
216
+ | `bridge_acceleration` | `number` | `0.0` | `before_layer_change_gcode`, `bridge_acceleration`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
201
217
  | `bridge_flow` | `number` | `1.0` | `bridge_flow` | |
218
+ | `default_acceleration` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `default_acceleration`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
219
+ | `default_jerk` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `default_jerk`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
202
220
  | `detect_thin_wall` | `boolean` | `false` | `detect_thin_wall` | detect_thin_wall: medial-axis thin walls where one loop does not fit |
221
+ | `disable_m73` | `boolean` | `true` | `before_layer_change_gcode`, `change_filament_gcode`, `disable_m73`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
203
222
  | `economy` | `boolean` | `false` | — | |
204
223
  | `emit_pe_tags` | `boolean` | `false` | — | emit PE tags on extrusion runs (enabled automatically when the real PE is used). Default false (backwards compatible) |
205
224
  | `extruder_count` | `number` | `1` | — | multi-material: how many extruders are used (1\|2) |
206
225
  | `extruder_filament_diameter` | `number[]` | `[]` | `filament_diameter` | |
226
+ | `extruder_first_layer_temp` | `number[]` | `[]` | `nozzle_temperature_initial_layer` | nozzle_temperature_initial_layer per extruder (the first layer's own) |
207
227
  | `extruder_flow_ratio` | `number[]` | `[]` | `filament_flow_ratio` | |
208
228
  | `extruder_nozzle_temp` | `number[]` | `[]` | `nozzle_temperature` | |
209
229
  | `extruder_retract_length` | `?` | `[]` | `filament_retraction_length` | undefined |
210
230
  | `extruder_retract_speed` | `?` | `[]` | `filament_retraction_speed` | undefined |
211
231
  | `extruder_z_hop` | `?` | `[]` | `filament_z_hop` | undefined |
212
232
  | `extrusion_rate_slope_segment_length` | `number` | `1.0` | — | segment split length for the real PE (mm) |
213
- | `filament_max_volumetric_speed` | `number[]` | `[]` | `filament_max_volumetric_speed` | mm³/s, per filament; empty = no cap (GW::capped_feed) |
233
+ | `filament_max_volumetric_speed` | `number[]` | `[]` | `filament_max_volumetric_speed` | mm³/s, per filament; empty = no cap (GcodeWriter::capped_feed) |
214
234
  | `filter_out_gap_fill` | `number` | `0.0` | `filter_out_gap_fill` | filter_out_gap_fill (mm): gap fill shorter than this is dropped |
235
+ | `first_layer_bed_temp` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `hot_plate_temp_initial_layer`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | the plate type's _initial_layer bed option (<0 = none sent) |
215
236
  | `first_layer_flow_ratio` | `?` | — | `first_layer_flow_ratio` | undefined |
237
+ | `first_layer_nozzle_temp` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `nozzle_temperature_initial_layer`, `time_lapse_gcode` | nozzle_temperature_initial_layer (<0 = none sent) |
216
238
  | `flush_into_infill` | `boolean` | `false` | `flush_into_infill` | |
217
239
  | `flush_multiplier` | `number` | `1.0` | `flush_multiplier` | upstream scales the whole table by this |
218
240
  | `flush_volumes_matrix` | `number[]` | `[]` | `flush_volumes_matrix` | |
219
241
  | `gap_fill_flow_ratio` | `?` | — | `gap_fill_flow_ratio` | undefined |
220
242
  | `gap_infill_speed` | `number[]` | `[]` | `gap_infill_speed` | gap_infill_speed (mm/s, per extruder): a first entry of 0 turns gap fill off (upstream has_gap_fill); empty = the schema's 30 |
221
243
  | `gcode_config_block` | `boolean` | `false` | — | ; CONFIG_BLOCK_START … the parameters this slice ran with … END |
244
+ | `gcode_flavor` | `string` | — | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `gcode_flavor`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
222
245
  | `gcode_role_tags` | `boolean` | `false` | — | ;TYPE:<role> on every role change (upstream GCodeProcessor tag) |
223
246
  | `gcode_stats_block` | `boolean` | `false` | — | ; filament used [mm]/[cm3]/[g], cost, total filament change |
224
247
  | `independent_support_layer_height` | `boolean` | `false` | `independent_support_layer_height` | false by default because of the kernel z grid constraint (gap quantized to layers) |
248
+ | `initial_layer_acceleration` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `initial_layer_acceleration`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
249
+ | `initial_layer_infill_speed` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `initial_layer_infill_speed`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
250
+ | `initial_layer_jerk` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `initial_layer_jerk`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
251
+ | `initial_layer_travel_acceleration` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `initial_layer_travel_acceleration`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
252
+ | `initial_layer_travel_jerk` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `initial_layer_travel_jerk`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
253
+ | `initial_layer_travel_speed` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `initial_layer_travel_speed`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | the first layer's travel speed |
254
+ | `inner_wall_acceleration` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `inner_wall_acceleration`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
225
255
  | `inner_wall_flow_ratio` | `number` | `1.0` | `inner_wall_flow_ratio` | |
256
+ | `inner_wall_jerk` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `inner_wall_jerk`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
257
+ | `inner_wall_speed` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `inner_wall_speed`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
258
+ | `internal_solid_infill_acceleration` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `internal_solid_infill_acceleration`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
226
259
  | `internal_solid_infill_flow_ratio` | `?` | — | `internal_solid_infill_flow_ratio` | undefined |
260
+ | `internal_solid_infill_speed` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `internal_solid_infill_speed`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
227
261
  | `keep_stages` | `boolean` | `false` | — | keep the stages cached after slicing (skips the early release — a memory trade-off) |
262
+ | `machine_envelope` | `number[]` | `[]` | `before_layer_change_gcode`, `change_filament_gcode`, `emit_machine_limits_to_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
228
263
  | `mm_group_split` | `number` | `0` | — | triangle group boundary index ([0,split)=T0, [split,N)=T1) |
229
264
  | `mm_group_splits` | `number[]` | `[]` | — | |
230
265
  | `mm_group_tools` | `number[]` | `[]` | — | |
231
266
  | `only_one_wall_first_layer` | `boolean` | `false` | `only_one_wall_first_layer` | only_one_wall_first_layer |
267
+ | `outer_wall_acceleration` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `outer_wall_acceleration`, `time_lapse_gcode` | |
232
268
  | `outer_wall_flow_ratio` | `number` | `1.0` | `outer_wall_flow_ratio` | |
269
+ | `outer_wall_jerk` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `outer_wall_jerk`, `time_lapse_gcode` | |
233
270
  | `pe_external_perimeter_only` | `boolean` | `false` | — | real PE: smooth outer walls only |
234
271
  | `pe_lite` | `boolean` | `true` | — | true = the effective PE-lite (default), false = the ported real PE (tagged g-code) |
235
272
  | `pe_strip_tags` | `boolean` | `true` | — | strip the tags from the final output after real PE post-processing |
236
- | `placeholder_config` | `string` | — | `filament_end_gcode`, `machine_end_gcode`, `machine_start_gcode` | |
273
+ | `placeholder_config` | `string` | — | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode` | |
237
274
  | `precise_outer_wall` | `boolean` | `true` | `precise_outer_wall` | precise_outer_wall |
238
275
  | `prime_tower_ring_size` | `number` | `15.0` | — | side length of the fallback square ring (mm) |
239
276
  | `prime_tower_width` | `number` | `30.0` | `prime_tower_width` | width of the real WipeTower (mm). Separate from the square ring width (15). |
@@ -242,16 +279,28 @@ same unit — `first_layer_height` comes from `initial_layer_print_height`, and
242
279
  | `prime_volume` | `number` | `0.0` | `prime_volume` | extra volume primed on every change, on top of the pair's |
243
280
  | `print_flow_ratio` | `number` | `1.0` | `print_flow_ratio` | |
244
281
  | `reduce_crossing_wall` | `boolean` | `false` | `reduce_crossing_wall` | wall-avoiding travel |
282
+ | `reduce_infill_retraction` | `boolean` | `false` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `reduce_infill_retraction`, `time_lapse_gcode` | no retraction for a travel inside the infill (GCode::needs_retraction); off = the kernel's own rule |
245
283
  | `reuse_stages` | `number` | `0` | — | |
246
284
  | `scarf_joint_flow_ratio` | `?` | — | `scarf_joint_flow_ratio` | undefined |
247
285
  | `set_other_flow_ratios` | `boolean` | `false` | `set_other_flow_ratios` | |
286
+ | `single_extruder_multi_material` | `boolean` | `true` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `single_extruder_multi_material`, `time_lapse_gcode` | one nozzle carries every filament: the switch names no tool |
248
287
  | `single_tool` | `number` | `0` | — | |
288
+ | `small_perimeter_speed` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `small_perimeter_speed`, `time_lapse_gcode` | mm/s for a wall loop within small_perimeter_threshold; 0 = half the outer wall's |
289
+ | `small_perimeter_threshold` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `small_perimeter_threshold`, `time_lapse_gcode` | a RADIUS (mm): loops up to 2*pi*threshold long are small (0 = off) |
249
290
  | `thick_bridges` | `boolean` | `false` | `thick_bridges` | |
250
291
  | `time_engine` | `string` | `'full'` | — | |
251
292
  | `top_bottom_infill_wall_overlap` | `number` | `25.0` | `top_bottom_infill_wall_overlap` | top_bottom_infill_wall_overlap (percent, first and topmost layer) |
252
293
  | `top_solid_infill_flow_ratio` | `number` | `1.0` | `top_solid_infill_flow_ratio` | |
294
+ | `top_surface_acceleration` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode`, `top_surface_acceleration` | |
295
+ | `top_surface_jerk` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode`, `top_surface_jerk` | |
296
+ | `top_surface_speed` | `number` | `-1.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode`, `top_surface_speed` | |
297
+ | `travel_acceleration` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode`, `travel_acceleration` | |
298
+ | `travel_jerk` | `number` | `0.0` | `before_layer_change_gcode`, `change_filament_gcode`, `filament_end_gcode`, `filament_start_gcode`, `file_start_gcode`, `layer_change_gcode`, `machine_end_gcode`, `machine_start_gcode`, `time_lapse_gcode`, `travel_jerk` | |
299
+ | `use_firmware_retraction` | `boolean` | `false` | `use_firmware_retraction` | G10/G11 instead of E moves (upstream GCodeWriter::_retract) |
300
+ | `use_relative_e_distances` | `boolean` | `true` | `use_relative_e_distances` | false = absolute E (M82, G92 E0 after each retraction), upstream's two modes |
253
301
  | `wall_direction` | `string` | `'ccw'` | `wall_direction` | wall_direction (ccw \| cw) |
254
302
  | `wall_sequence` | `string` | `'inner wall/outer wall'` | `wall_sequence` | wall_sequence (inner wall/outer wall \| outer wall/inner wall \| inner-outer-inner wall) |
303
+ | `z_offset` | `number` | `0.0` | `z_offset` | added to every Z the G-code writes (upstream GCodeWriter, z_offset) |
255
304
 
256
305
  <!-- KERNEL-PARAMS:END -->
257
306
 
@@ -3,11 +3,11 @@
3
3
  // between the cut and the union.
4
4
  //
5
5
  // What runs here (WGSL: contour_front_shaders.js), every layer at once:
6
- // cut per triangle, the layers it spans (exact z compares), each crossing in double-float, oriented as tri_plane
6
+ // cut per triangle, the layers it spans (exact z compares), each crossing in double-float, oriented as intersect_triangle_with_plane
7
7
  // cancel segments coinciding exactly in opposite directions removed in pairs (cancel_coincident), by a hash on place
8
- // chain each end continues into the smallest unused segment starting at its 1e-3 mm key (chain_polys' greedy rule)
8
+ // chain each end continues into the smallest unused segment starting at its 1e-3 mm key (chain_polygons' greedy rule)
9
9
  // loops pointer jumping for each loop's head and each segment's rank; tails that lead into a cycle stay out
10
- // layout loops sorted by (layer, head), laid out as chain_polys' n + 1 points, write_polygon's duplicates dropped
10
+ // layout loops sorted by (layer, head), laid out as chain_polygons' n + 1 points, write_polygon's duplicates dropped
11
11
  // tables layer origins and grids, polygon ranges, the cell-list size
12
12
  //
13
13
  // Measured (design report gpu-full-port-trial-2026-10-01.md section 3): 3.0M triangles, 420 layers in 30 ms (the kernel's
@@ -205,10 +205,10 @@ export async function makeContourFront(device) {
205
205
  clear(loopStart, (headCount + 1) * 4)
206
206
  run(LOOPS_SHADER, 'lengths', 'urrrrwwwwwr', bound(uniform([segmentCount, padded, layerCount, headCount])), headCount)
207
207
  scan(loopStart, headCount + 1)
208
- const [slotCount] = await readWords(loopStart, 1, headCount) // points of the closed loops, chain_polys' n + 1 each
208
+ const [slotCount] = await readWords(loopStart, 1, headCount) // points of the closed loops, chain_polygons' n + 1 each
209
209
  run(LOOPS_SHADER, 'place', 'urrrrwwwwwr', bound(uniform([segmentCount, padded, layerCount, headCount])), segmentCount)
210
210
 
211
- // the points write_polygon keeps, the polygons chain_polys keeps, the layer boxes
211
+ // the points write_polygon keeps, the polygons chain_polygons keeps, the layer boxes
212
212
  const kept = buffer('kept', (slotCount + 1) * 4), polygonKept = buffer('polygonKept', (headCount + 1) * 4)
213
213
  const layerBox = buffer('layerBox', layerCount * 32), slotPolygon = buffer('slotPolygon', slotCount * 4)
214
214
  const boxStart = new Int32Array(layerCount * 8)
@@ -1,4 +1,4 @@
1
- // WGSL of the GPU front of PASS1 (contour_front_gpu.js): tri_plane -> chain_polys -> cancel_coincident ->
1
+ // WGSL of the GPU front of PASS1 (contour_front_gpu.js): intersect_triangle_with_plane -> chain_polygons -> cancel_coincident ->
2
2
  // build_gpu_input, every layer at once. Ported from the measured experiment (.omc/plans/gpu-work/fullport/gpu_slice.mjs,
3
3
  // design report gpu-full-port-trial-2026-10-01.md section 3); edited only with a measurement beside it, as
4
4
  // contour_gpu_shaders.js.
@@ -102,12 +102,14 @@ export function filamentPresets() {
102
102
  return filamentsPromise
103
103
  }
104
104
 
105
+ // `parents` holds the abstract machine presets (`instantiation: false`): never offered by the picker, but a user
106
+ // preset file names one as its `inherits`, so printerSettings has to resolve it by name.
105
107
  function printerEntry(profileName) {
106
108
  for (const models of Object.values(printers.byVendor)) {
107
109
  const entry = models[profileName]
108
110
  if (entry) return entry
109
111
  }
110
- return null
112
+ return printers.parents?.[profileName] ?? null
111
113
  }
112
114
 
113
115
  /** The settings a printer profile applies, ready to merge into the settings map. `null` when unknown. */
@@ -5,7 +5,7 @@
5
5
  //
6
6
  // Stage 30 (OOM tolerance): set_layer_sink lets the kernel emit layers as it produces them; each is transferred to main
7
7
  // immediately (Float32Array buffers moved -> the worker copy is released at once) and the kernel frees that layer buffer
8
- // from the heap. The resident result (the full gw.s + the full layersArr) disappears, so the WASM heap peak drops sharply
8
+ // from the heap. The resident result (the full GcodeWriter::gcode + the full layersArray) disappears, so the WASM heap peak drops sharply
9
9
  // -> large models avoid OOM. The final 'done' carries stats only.
10
10
  // In economy mode (params.economy) only G-code chunks are emitted, without toolpaths (finishes without a preview).
11
11
  // The MM / real-PE paths fall back to batch mode.
@@ -27,7 +27,10 @@ import { FILL_TOOLS } from 'three-slicer-viewer/paint'
27
27
  // and createSlicerClient both take an object and stringify it for you. One object, two shapes, and the difference
28
28
  // showed up only at the boundary. The worker now accepts either; the SLA branch always did, which is what made
29
29
  // the split arbitrary rather than principled.
30
- const paramsText = (params) => (typeof params === 'string' ? params : JSON.stringify(params ?? {}))
30
+ const paramsText = (params) => {
31
+ if (typeof params === 'string') return params
32
+ return JSON.stringify(params ?? {})
33
+ }
31
34
 
32
35
  let quiet = false
33
36
  const say = (level, ...args) => { if (!quiet) console[level](...args) }
@@ -120,7 +123,8 @@ const validatedPaintStates = (states) => {
120
123
  // flow (viewer use_slicer.js) reads those two fields and nothing else.
121
124
  const reportedPaintStates = (message, paintedState) => {
122
125
  if (Array.isArray(message.states)) return validatedPaintStates(message.states)
123
- return paintedState === null ? null : [paintedState]
126
+ if (paintedState === null) return null
127
+ return [paintedState]
124
128
  }
125
129
 
126
130
  // The state a paint message asks for: an explicit `state` wins, otherwise null meaning "use the legacy boolean path".
@@ -133,7 +137,10 @@ const explicitPaintState = (message) => {
133
137
 
134
138
  // Brush cursor shape (selector_bridge.h CURSOR_*). Sent as a name because the message is a protocol a host writes
135
139
  // by hand; the integer stays on this side of it. Anything unknown is the sphere the brush has always used.
136
- const cursorShapeOf = (message) => (message.cursor === 'circle' ? 1 : 0)
140
+ const cursorShapeOf = (message) => {
141
+ if (message.cursor === 'circle') return 1
142
+ return 0
143
+ }
137
144
 
138
145
  // The tools that are not the radius brush: upstream's Smart fill and Bucket fill, plus its single-triangle POINTER
139
146
  // cursor — which is literally bucket fill with propagation off, so the three share one dispatch. None of them takes
@@ -150,8 +157,15 @@ const strokeFrom = (message) =>
150
157
  // The fill modes the kernel's fill_preview takes (selector_bridge.h FILL_*), by the tool name the protocol uses.
151
158
  const FILL_PREVIEW_MODE = { smart: 0, bucket: 1, triangle: 2 }
152
159
 
160
+ // A fill's angle limit in degrees, 30 when the message does not carry one. applyFill and the fill preview both
161
+ // read it here, so the preview selects what the fill will.
162
+ const fillAngleOf = (message) => {
163
+ if (Number.isFinite(message.angle)) return message.angle
164
+ return 30
165
+ }
166
+
153
167
  const applyFill = (Module, message, state) => {
154
- const angle = Number.isFinite(message.angle) ? message.angle : 30
168
+ const angle = fillAngleOf(message)
155
169
  if (message.tool === 'smart') {
156
170
  if (state === null) Module.selector_seed_fill_erase(message.facet, message.hx, message.hy, message.hz, angle)
157
171
  else Module.selector_seed_fill(message.facet, message.hx, message.hy, message.hz, angle, state)
@@ -160,7 +174,8 @@ const applyFill = (Module, message, state) => {
160
174
  // Upstream passes -1 as the angle when it is not propagating (GLGizmoPainterBase.cpp ~861): with a single facet
161
175
  // there is no neighbour to measure an angle against, and -1 is how that call says "no angle limit".
162
176
  const propagate = message.tool === 'bucket'
163
- const bucketAngle = propagate ? angle : -1
177
+ let bucketAngle = -1
178
+ if (propagate) bucketAngle = angle
164
179
  if (state === null) Module.selector_bucket_fill_erase(message.facet, message.hx, message.hy, message.hz, bucketAngle, propagate)
165
180
  else Module.selector_bucket_fill(message.facet, message.hx, message.hy, message.hz, bucketAngle, propagate, state)
166
181
  }
@@ -276,7 +291,9 @@ const handleMessage = async (e) => {
276
291
  // `keepPaint` says the mesh is the same model in a new place, so the marks carry over — the reply reports whether
277
292
  // they actually did, since a face-count change makes the kernel fall back to a clean registration.
278
293
  if (d.cmd === 'prepare') {
279
- const kept = d.keepPaint ? Module.selector_reprepare(new Uint8Array(d.stl)) : (Module.selector_prepare(new Uint8Array(d.stl)), false)
294
+ let kept = false
295
+ if (d.keepPaint) kept = Module.selector_reprepare(new Uint8Array(d.stl))
296
+ else Module.selector_prepare(new Uint8Array(d.stl))
280
297
  reply({ type: 'prepared', facets: Module.selector_facet_count(), kept })
281
298
  return
282
299
  }
@@ -321,7 +338,8 @@ const handleMessage = async (e) => {
321
338
  // A kernel built before this binding existed simply has no selector_import_paint; the reply says applied:0
322
339
  // rather than throwing, because losing a 3mf's paint must not also lose its geometry.
323
340
  if (d.cmd === 'importPaint') {
324
- const applied = Module.selector_import_paint ? Module.selector_import_paint(d.facets, d.hex ?? '') : 0
341
+ let applied = 0
342
+ if (Module.selector_import_paint) applied = Module.selector_import_paint(d.facets, d.hex ?? '')
325
343
  const importedReply = paintedReply(Module, d, null)
326
344
  importedReply.applied = applied
327
345
  reply(importedReply); return
@@ -332,7 +350,8 @@ const handleMessage = async (e) => {
332
350
  // which objects were merged in what order is the viewer's knowledge. A kernel built before the binding existed
333
351
  // reports supported:false rather than throwing — the save then falls back to the paint it imported.
334
352
  if (d.cmd === 'exportPaint') {
335
- const exported = Module.selector_export_paint ? Module.selector_export_paint() : null
353
+ let exported = null
354
+ if (Module.selector_export_paint) exported = Module.selector_export_paint()
336
355
  reply({ type: 'paintExport', supported: !!Module.selector_export_paint,
337
356
  facets: exported?.facets ?? [], hex: exported?.hex ?? '' })
338
357
  return
@@ -358,9 +377,11 @@ const handleMessage = async (e) => {
358
377
  // enf/blk stay unconditional for the listener that predates the per-state map. When the request names states,
359
378
  // they are the ONLY ones read, so building the other two would be pure waste on the hot path.
360
379
  const wanted = states && !states.includes(PAINT_STATE_ENFORCER) && !states.includes(PAINT_STATE_BLOCKER)
361
- const overlayReply = { type: 'overlay',
362
- enf: wanted ? EMPTY_OVERLAY : Module.selector_overlay(true),
363
- blk: wanted ? EMPTY_OVERLAY : Module.selector_overlay(false) }
380
+ const overlayReply = { type: 'overlay', enf: EMPTY_OVERLAY, blk: EMPTY_OVERLAY }
381
+ if (!wanted) {
382
+ overlayReply.enf = Module.selector_overlay(true)
383
+ overlayReply.blk = Module.selector_overlay(false)
384
+ }
364
385
  if (states) overlayReply.overlays = Object.fromEntries(states.map(state => [state, Module.selector_overlay_state(state)]))
365
386
  const transfer = [overlayReply.enf, overlayReply.blk, ...Object.values(overlayReply.overlays ?? {})]
366
387
  .filter(a => a?.buffer && a.byteLength > 0).map(a => a.buffer)
@@ -374,13 +395,15 @@ const handleMessage = async (e) => {
374
395
  if (d.cmd === 'fillPreview') {
375
396
  if (!Module.selector_fill_preview) { reply({ type: 'fillPreview', supported: false, tris: EMPTY_OVERLAY }); return }
376
397
  if (d.clear) { Module.selector_fill_preview_clear(); reply({ type: 'fillPreview', supported: true, tris: EMPTY_OVERLAY }); return }
377
- const angle = Number.isFinite(d.angle) ? d.angle : 30
378
398
  const mode = FILL_PREVIEW_MODE[d.tool] ?? FILL_PREVIEW_MODE.smart
379
399
  // Upstream's -1 for the non-propagating case, the same value applyFill sends: one facet has no neighbour to
380
400
  // measure an angle against.
381
- const tris = Module.selector_fill_preview(d.facet, d.hx, d.hy, d.hz, mode === FILL_PREVIEW_MODE.triangle ? -1 : angle, mode)
382
- reply({ type: 'fillPreview', supported: true, tris },
383
- tris?.buffer && tris.byteLength > 0 ? [tris.buffer] : [])
401
+ let previewAngle = fillAngleOf(d)
402
+ if (mode === FILL_PREVIEW_MODE.triangle) previewAngle = -1
403
+ const previewTriangles = Module.selector_fill_preview(d.facet, d.hx, d.hy, d.hz, previewAngle, mode)
404
+ const transfer = []
405
+ if (previewTriangles?.buffer && previewTriangles.byteLength > 0) transfer.push(previewTriangles.buffer)
406
+ reply({ type: 'fillPreview', supported: true, tris: previewTriangles }, transfer)
384
407
  return
385
408
  }
386
409
  // paintMode: the brush-wide options upstream keeps on the gizmo — the overhang restriction and the section
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-slicer",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Browser/WASM 3D-printing slicer reverse-engineered from OrcaSlicer: slicing engine (Arachne walls, tree/grid supports, multi-material), SLA resin slicing with PrusaSlicer's ported support/pad chain and .sl1 export, React viewer with GPU toolpath preview, and schema-driven settings UI.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "type": "module",
@@ -134,6 +134,6 @@
134
134
  "clipping"
135
135
  ],
136
136
  "dependencies": {
137
- "three-slicer-viewer": "0.3.3"
137
+ "three-slicer-viewer": "0.3.5"
138
138
  }
139
139
  }
@@ -24,6 +24,9 @@ export interface PrinterData {
24
24
  text?: unknown[]
25
25
  /** Vendor -> printer profile name -> entry */
26
26
  byVendor: Record<string, Record<string, PrinterEntry>>
27
+ /** The abstract machine presets (`instantiation: false`) by name: not offered for picking, but resolvable as
28
+ * the parent a user preset file `inherits` */
29
+ parents?: Record<string, PrinterEntry>
27
30
  }
28
31
 
29
32
  declare const printers: PrinterData
@@ -17,7 +17,20 @@ export function settingScalar(settings: SlicerSettings | null | undefined, key:
17
17
  * ponytail: 53 returned keys, so Record instead of listing each — kernel params are passed straight to slice(),
18
18
  * not an API consumers read field by field. Expand it when field access is actually needed.
19
19
  */
20
- export function deriveKernelParams(settings: SlicerSettings | null | undefined, opts?: { plate?: number }): Record<string, unknown>
20
+ export function deriveKernelParams(settings: SlicerSettings | null | undefined, opts?: { plate?: number; objectNames?: string[] }): Record<string, unknown>
21
+ /**
22
+ * The printable area's lower-left corner and its centre in printer coordinates, from deriveKernelParams' output.
23
+ * The kernel slices in a plate frame centred on the bed and adds the centre on output, so a stored printer
24
+ * coordinate (wipe_tower_x/y, a 3mf position, G-code) converts to that frame by subtracting `bedCenter`.
25
+ */
26
+ export function bedOrigin(params: Record<string, unknown> | null | undefined): { x: number; y: number }
27
+ /** Code of a printer profile setting the template path reads but does not do yet. */
28
+ export const PARTIAL_SUPPORT_CODE: 'PROFILE_PARTIAL_SUPPORT'
29
+ /** The tool change settings a profile sets that the template path does not apply (empty off the template path or
30
+ * with one tool), each with what happens instead. */
31
+ export function partialSupport(settings: SlicerSettings | null | undefined, opts?: { toolCount?: number }):
32
+ { code: 'PROFILE_PARTIAL_SUPPORT'; key: string; effect: string }[]
33
+ export function bedCenter(params: Record<string, unknown> | null | undefined): { x: number; y: number }
21
34
 
22
35
  /**
23
36
  * Clear-then-merge preset application: deletes `keys` (the catalog's own key set) from `settings`, then