three-slicer-viewer 0.3.4 → 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/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/settings/settings_core.js +110 -53
package/dist/index.js
CHANGED
|
@@ -4864,7 +4864,7 @@ function Dp({ error: e }) {
|
|
|
4864
4864
|
return /* @__PURE__ */ i.createElement("div", { className: "slice-err side-warn", "data-testid": "slice-err" }, t, r && /* @__PURE__ */ i.createElement("pre", { className: "slice-err-detail" }, r));
|
|
4865
4865
|
}
|
|
4866
4866
|
function Gp() {
|
|
4867
|
-
return "0.3.
|
|
4867
|
+
return "0.3.5";
|
|
4868
4868
|
}
|
|
4869
4869
|
const Br = Gp();
|
|
4870
4870
|
function Up({ on: e = !0 }) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "three-slicer-viewer",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "The three-slicer viewer without the slicer — model loading, G-code parsing, GPU toolpath rendering and the settings transforms, under MIT. Plug a slicer in through the `slicer` prop.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,10 @@ import { leanSchema as schema } from './data.js'
|
|
|
14
14
|
import { FFF_FALLBACKS, AUTO_LINE_WIDTH, SLA_FALLBACKS, MACHINE_LIMITS, BED_FALLBACK } from './kernel_defaults.js'
|
|
15
15
|
|
|
16
16
|
export function schemaDefault(key) { return schema[key]?.default }
|
|
17
|
-
export function settingRaw(settings, key) {
|
|
17
|
+
export function settingRaw(settings, key) {
|
|
18
|
+
if (settings && key in settings) return settings[key]
|
|
19
|
+
return schemaDefault(key)
|
|
20
|
+
}
|
|
18
21
|
// Normalize to a scalar ([0] for vectors)
|
|
19
22
|
// A per-extruder column reduced to the single value the kernel's scalar reader wants. The FIRST SET entry, not
|
|
20
23
|
// literally index 0: the filament card writes one column per extruder and leaves a null wherever that extruder has
|
|
@@ -143,7 +146,8 @@ function coerceScalar(type, value) {
|
|
|
143
146
|
return asNumber(value.trim().replace(/%$/, ''))
|
|
144
147
|
case 'coFloatOrPercent': case 'coFloatsOrPercents':
|
|
145
148
|
// "50%" must stay a string — the percent IS the value, and Number('50%') is NaN anyway.
|
|
146
|
-
|
|
149
|
+
if (value.trim().endsWith('%')) return value
|
|
150
|
+
return asNumber(value)
|
|
147
151
|
case 'coPoint': case 'coPoints':
|
|
148
152
|
return parsePoint(value) ?? value
|
|
149
153
|
case 'coPointsGroups':
|
|
@@ -161,25 +165,34 @@ function coerceScalar(type, value) {
|
|
|
161
165
|
// so there the comma cannot also separate a point's own coordinates — hence two parsers rather than one regex.
|
|
162
166
|
function parsePoint(text) {
|
|
163
167
|
const split = String(text).split('x')
|
|
164
|
-
|
|
168
|
+
let pair = split
|
|
169
|
+
if (split.length !== 2) pair = String(text).split(',')
|
|
165
170
|
if (pair.length !== 2) return null
|
|
166
171
|
const x = Number(pair[0]), y = Number(pair[1])
|
|
167
|
-
|
|
172
|
+
if (Number.isFinite(x) && Number.isFinite(y)) return [x, y]
|
|
173
|
+
return null
|
|
168
174
|
}
|
|
169
175
|
function parsePointGroup(text) {
|
|
170
176
|
const points = String(text).split(',').map(entry => {
|
|
171
177
|
const [a, b] = entry.split('x')
|
|
172
178
|
const x = Number(a), y = Number(b)
|
|
173
|
-
|
|
179
|
+
if (Number.isFinite(x) && Number.isFinite(y)) return [x, y]
|
|
180
|
+
return null
|
|
174
181
|
})
|
|
175
|
-
|
|
182
|
+
if (points.length && points.every(Boolean)) return points
|
|
183
|
+
return null
|
|
176
184
|
}
|
|
177
185
|
// Number('') is 0 and Number(' ') is 0, neither of which means "the option was set to zero" — an unparsable
|
|
178
186
|
// value is handed back untouched so the schema default keeps applying instead of a fabricated 0.
|
|
179
187
|
function asNumber(value) {
|
|
180
188
|
if (value.trim() === '') return value
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
return finiteOr(Number(value), value)
|
|
190
|
+
}
|
|
191
|
+
// The number when it is finite, otherwise the fallback. The fallback is evaluated by the caller, so it is only
|
|
192
|
+
// used for values that are already at hand.
|
|
193
|
+
function finiteOr(number, fallback) {
|
|
194
|
+
if (Number.isFinite(number)) return number
|
|
195
|
+
return fallback
|
|
183
196
|
}
|
|
184
197
|
|
|
185
198
|
/**
|
|
@@ -193,7 +206,8 @@ export function normalizeProjectSettings(raw) {
|
|
|
193
206
|
for (const [key, value] of Object.entries(raw || {})) {
|
|
194
207
|
const type = schema[key]?.type
|
|
195
208
|
if (!type) { skipped.push(key); continue }
|
|
196
|
-
|
|
209
|
+
if (Array.isArray(value)) settings[key] = value.map(entry => coerceScalar(type, entry))
|
|
210
|
+
else settings[key] = coerceScalar(type, value)
|
|
197
211
|
applied++
|
|
198
212
|
}
|
|
199
213
|
return { settings, applied, skipped }
|
|
@@ -204,17 +218,22 @@ export function normalizeProjectSettings(raw) {
|
|
|
204
218
|
// slicer's reader — and normalizeProjectSettings above — expects to coerce FROM. Writing the JS types raw would
|
|
205
219
|
// hand OrcaSlicer a bool it reads with its own string parser and this package a round-trip that only works here.
|
|
206
220
|
// Keys the schema does not define are dropped for the same reason normalize drops them: they could not be typed.
|
|
207
|
-
const pointToText = (point) =>
|
|
221
|
+
const pointToText = (point) => {
|
|
222
|
+
if (Array.isArray(point)) return `${point[0]}x${point[1]}`
|
|
223
|
+
return String(point)
|
|
224
|
+
}
|
|
208
225
|
|
|
209
226
|
function serializeScalar(type, value) {
|
|
210
227
|
if (value == null) return ''
|
|
211
228
|
switch (type) {
|
|
212
229
|
case 'coBool': case 'coBools':
|
|
213
|
-
|
|
230
|
+
if (value) return '1'
|
|
231
|
+
return '0'
|
|
214
232
|
case 'coPoint': case 'coPoints':
|
|
215
233
|
return pointToText(value)
|
|
216
234
|
case 'coPointsGroups':
|
|
217
|
-
|
|
235
|
+
if (Array.isArray(value)) return value.map(pointToText).join(',')
|
|
236
|
+
return String(value)
|
|
218
237
|
default:
|
|
219
238
|
return String(value) // numbers, percents, strings, enums — String() is upstream's own spelling for all of them
|
|
220
239
|
}
|
|
@@ -227,9 +246,8 @@ export function serializeProjectSettings(settings) {
|
|
|
227
246
|
const type = schema[key]?.type
|
|
228
247
|
if (!type) continue
|
|
229
248
|
// A coPoint's value IS an array ([x, y]) — the generic array path would serialize its two numbers separately.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
: serializeScalar(type, value)
|
|
249
|
+
if (type !== 'coPoint' && Array.isArray(value)) out[key] = value.map(entry => serializeScalar(type, entry))
|
|
250
|
+
else out[key] = serializeScalar(type, value)
|
|
233
251
|
}
|
|
234
252
|
return out
|
|
235
253
|
}
|
|
@@ -300,6 +318,22 @@ const BED_TEMP_KEYS = {
|
|
|
300
318
|
'Engineering Plate': 'eng_plate_temp', 'High Temp Plate': 'hot_plate_temp', 'Textured PEI Plate': 'textured_plate_temp',
|
|
301
319
|
}
|
|
302
320
|
|
|
321
|
+
// A per-extruder column's first entry; a scalar is its own first entry.
|
|
322
|
+
function firstEntryOf(value) {
|
|
323
|
+
if (Array.isArray(value)) return value[0]
|
|
324
|
+
return value
|
|
325
|
+
}
|
|
326
|
+
// A per-extruder column as a list; a scalar becomes a one-entry list.
|
|
327
|
+
function asList(value) {
|
|
328
|
+
if (Array.isArray(value)) return value
|
|
329
|
+
return [value]
|
|
330
|
+
}
|
|
331
|
+
// The value when it is one of the accepted ones, otherwise the fallback.
|
|
332
|
+
function allowedOr(value, accepted, fallback) {
|
|
333
|
+
if (accepted.includes(value)) return value
|
|
334
|
+
return fallback
|
|
335
|
+
}
|
|
336
|
+
|
|
303
337
|
// Right-panel settings values -> kernel parameters (derived from schema keys)
|
|
304
338
|
// opts.plate: which plate's entry to take from per-plate array options (wipe_tower_x/y — upstream coFloats,
|
|
305
339
|
// one entry per plate). Defaults to 0, so every existing caller reads exactly what it always did.
|
|
@@ -313,16 +347,23 @@ export function deriveKernelParams(settings, opts) {
|
|
|
313
347
|
const presentOrTemplate = (key) => followsTemplate || (settings != null && key in settings && settings[key] != null && settings[key] !== '')
|
|
314
348
|
const S = k => settingScalar(settings, k)
|
|
315
349
|
const str = k => String(S(k) ?? FFF_FALLBACKS[k])
|
|
316
|
-
const num = (k, d = FFF_FALLBACKS[k]) =>
|
|
317
|
-
const bool = (k, d = FFF_FALLBACKS[k]) => {
|
|
350
|
+
const num = (k, d = FFF_FALLBACKS[k]) => finiteOr(Number(S(k)), d)
|
|
351
|
+
const bool = (k, d = FFF_FALLBACKS[k]) => {
|
|
352
|
+
const v = settingRaw(settings, k)
|
|
353
|
+
if (typeof v === 'boolean') return v
|
|
354
|
+
if (Array.isArray(v)) return !!v[0]
|
|
355
|
+
if (v == null) return d
|
|
356
|
+
return !!v
|
|
357
|
+
}
|
|
318
358
|
// Filament retraction overrides: upstream lets a material override the machine's retraction (TPU wants a
|
|
319
359
|
// different pull-back than PLA on the same printer). These schema keys are nullable and carry no default of
|
|
320
360
|
// their own — "unset" means keep the machine value — so this reads the settings map directly rather than
|
|
321
361
|
// through settingScalar, whose schema fallback would not distinguish the two.
|
|
322
362
|
const override = (filamentKey, machineKey, fallback = FFF_FALLBACKS[machineKey]) => {
|
|
323
363
|
const raw = settings?.[filamentKey]
|
|
324
|
-
const value = Number(
|
|
325
|
-
|
|
364
|
+
const value = Number(firstEntryOf(raw))
|
|
365
|
+
if (Number.isFinite(value)) return value
|
|
366
|
+
return num(machineKey, fallback)
|
|
326
367
|
}
|
|
327
368
|
|
|
328
369
|
let line_width = num('line_width'); if (!line_width) line_width = AUTO_LINE_WIDTH // 0 = auto
|
|
@@ -364,7 +405,7 @@ export function deriveKernelParams(settings, opts) {
|
|
|
364
405
|
}
|
|
365
406
|
|
|
366
407
|
// Stage 21: per-feature widths — passed to the kernel verbatim (including strings like "120%"). Unedited keys are omitted -> the kernel derives auto(=line_width).
|
|
367
|
-
// The 0=auto semantics are handled by the kernel's
|
|
408
|
+
// The 0=auto semantics are handled by the kernel's resolve_line_width (upstream Flow formula). Defaults unchanged (no feature set -> line_width).
|
|
368
409
|
const widths = {}
|
|
369
410
|
for (const k of ['outer_wall_line_width','inner_wall_line_width','top_surface_line_width',
|
|
370
411
|
'sparse_infill_line_width','internal_solid_infill_line_width','initial_layer_line_width']) {
|
|
@@ -402,9 +443,11 @@ export function deriveKernelParams(settings, opts) {
|
|
|
402
443
|
// the plate and disabled the automatic placement entirely — measured as a tower at z=-127.5 on a 200mm bed.
|
|
403
444
|
// Absent from the map means nobody chose one, and the placement beside the model stands.
|
|
404
445
|
const chosen = (key) => { const raw = settings?.[key]
|
|
405
|
-
|
|
446
|
+
let v = raw
|
|
447
|
+
if (Array.isArray(raw)) v = raw[plate]
|
|
406
448
|
const n = Number(v)
|
|
407
|
-
|
|
449
|
+
if (v != null && v !== '' && Number.isFinite(n)) return n
|
|
450
|
+
return null }
|
|
408
451
|
const towerX = chosen('wipe_tower_x'), towerY = chosen('wipe_tower_y')
|
|
409
452
|
if (towerX != null) towerSettings.prime_tower_x = towerX
|
|
410
453
|
if (towerY != null) towerSettings.prime_tower_y = towerY
|
|
@@ -442,20 +485,22 @@ export function deriveKernelParams(settings, opts) {
|
|
|
442
485
|
for (const key of PASSTHROUGH_NUM_VECTOR) {
|
|
443
486
|
if (!present(key)) continue
|
|
444
487
|
const raw = settings[key]
|
|
445
|
-
passthrough[key] = (
|
|
488
|
+
passthrough[key] = asList(raw).map(entry => finiteOr(Number(entry), 0))
|
|
446
489
|
}
|
|
447
490
|
for (const key of PASSTHROUGH_STR_VECTOR) {
|
|
448
491
|
if (!present(key)) continue
|
|
449
492
|
const raw = settings[key]
|
|
450
|
-
passthrough[key] = (
|
|
493
|
+
passthrough[key] = asList(raw).map(entry => String(entry ?? ''))
|
|
451
494
|
}
|
|
452
495
|
// support_line_width is coFloatOrPercent, but the kernel reads it with the plain number reader rather than the
|
|
453
|
-
// percent-aware
|
|
496
|
+
// percent-aware json_width_raw the per-feature widths get. A "120%" would reach strtod as a quoted string and
|
|
454
497
|
// parse to 0 == auto — the setting silently ignored. Resolved here against the nozzle, which is the same
|
|
455
|
-
// thing
|
|
498
|
+
// thing json_width_raw does for the widths that do get the percent-aware reader.
|
|
456
499
|
if (present('support_line_width')) {
|
|
457
500
|
const text = String(settingScalar(settings, 'support_line_width')).trim()
|
|
458
|
-
|
|
501
|
+
let width
|
|
502
|
+
if (text.endsWith('%')) width = (parseFloat(text) / 100) * num('nozzle_diameter')
|
|
503
|
+
else width = Number(text)
|
|
459
504
|
if (Number.isFinite(width) && width > 0) passthrough.support_line_width = width
|
|
460
505
|
}
|
|
461
506
|
}
|
|
@@ -524,8 +569,7 @@ export function deriveKernelParams(settings, opts) {
|
|
|
524
569
|
if (!Array.isArray(raw) || raw.length < 2) continue
|
|
525
570
|
perExtruder[param] = raw.map(v => {
|
|
526
571
|
if (v == null || v === '') return scalar // null survives a JSON round-trip; Number(null) would be 0
|
|
527
|
-
|
|
528
|
-
return Number.isFinite(n) ? n : scalar
|
|
572
|
+
return finiteOr(Number(firstEntryOf(v)), scalar)
|
|
529
573
|
})
|
|
530
574
|
}
|
|
531
575
|
|
|
@@ -595,7 +639,7 @@ export function deriveKernelParams(settings, opts) {
|
|
|
595
639
|
support_expansion: num('support_expansion'),
|
|
596
640
|
support_threshold_overlap: (() => { const v = settingRaw(settings, 'support_threshold_overlap')
|
|
597
641
|
if (typeof v === 'string' && v.trim().endsWith('%')) return parseFloat(v) / 100
|
|
598
|
-
|
|
642
|
+
return finiteOr(Number(v), 0.5) })(), // "50%" -> 0.5 (ratio of extrusion width)
|
|
599
643
|
support_on_build_plate_only: bool('support_on_build_plate_only'),
|
|
600
644
|
support_interface_bottom_layers: num('support_interface_bottom_layers'),
|
|
601
645
|
...supportTools, // present only when a support extruder is mapped (see above)
|
|
@@ -623,7 +667,7 @@ export function deriveKernelParams(settings, opts) {
|
|
|
623
667
|
reduce_crossing_wall: bool('reduce_crossing_wall'),
|
|
624
668
|
max_volumetric_extrusion_rate_slope: num('max_volumetric_extrusion_rate_slope'),
|
|
625
669
|
// Stage 7: wall generator classic|arachne (arachne = the real ported OrcaSlicer WallToolPaths, variable width)
|
|
626
|
-
wall_generator: (str('wall_generator')
|
|
670
|
+
wall_generator: allowedOr(str('wall_generator'), ['arachne'], 'classic'),
|
|
627
671
|
// support_density has no matching schema key, so the kernel default (0.15) is used; scarf_length likewise uses the kernel default (10mm)
|
|
628
672
|
}
|
|
629
673
|
}
|
|
@@ -634,7 +678,8 @@ export function deriveKernelParams(settings, opts) {
|
|
|
634
678
|
// path runs. The schema already carries the key (inherited from the upstream lineage), so an imported preset
|
|
635
679
|
// that names a resin printer routes itself.
|
|
636
680
|
export function printerTechnology(settings) {
|
|
637
|
-
|
|
681
|
+
if (String(settingRaw(settings, 'printer_technology') ?? 'FFF').toUpperCase() === 'SLA') return 'SLA'
|
|
682
|
+
return 'FFF'
|
|
638
683
|
}
|
|
639
684
|
|
|
640
685
|
// SLA settings provenance table. Rows come from the schema's `defined_in` marker so a new resin option cannot
|
|
@@ -649,13 +694,12 @@ const SLA_SETTING_OVERRIDES = {
|
|
|
649
694
|
|
|
650
695
|
const SLA_SCHEMA_ROWS = Object.fromEntries(Object.entries(schema)
|
|
651
696
|
.filter(([key, meta]) => key === 'printer_technology' || String(meta.defined_in ?? '').includes('sla_'))
|
|
652
|
-
.map(([key, meta]) =>
|
|
653
|
-
source: 'config-schema.json',
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
}]))
|
|
697
|
+
.map(([key, meta]) => {
|
|
698
|
+
const row = { source: 'config-schema.json', omission: 'schema-default', default: meta.default }
|
|
699
|
+
if (meta.enum_values) row.enum = [...meta.enum_values]
|
|
700
|
+
row.provenance = `${meta.defined_in}:${meta.line}`
|
|
701
|
+
return [key, row]
|
|
702
|
+
}))
|
|
659
703
|
|
|
660
704
|
export const SLA_SETTING_CONTRACT = Object.freeze(Object.fromEntries(
|
|
661
705
|
Object.entries({ ...SLA_SCHEMA_ROWS, ...SLA_SETTING_OVERRIDES }).map(([key, row]) => [key, Object.freeze({
|
|
@@ -671,27 +715,40 @@ export const slaSettingKeys = Object.freeze(Object.keys(SLA_SETTING_CONTRACT))
|
|
|
671
715
|
// and its empty-map key count is a pinned invariant — this one feeds the JS contour slicer and owes bytes to
|
|
672
716
|
// nobody yet. Keys the schema does not define (the resin display's pixel grid, the per-layer height override)
|
|
673
717
|
// are read from the map directly, so they follow the same omission-friendly rule: absent means the default.
|
|
718
|
+
// The number when it is finite and above zero, otherwise the fallback.
|
|
719
|
+
function positiveOr(number, fallback) {
|
|
720
|
+
if (Number.isFinite(number) && number > 0) return number
|
|
721
|
+
return fallback
|
|
722
|
+
}
|
|
723
|
+
// The number when it is finite and not negative (zero is legal), otherwise the fallback.
|
|
724
|
+
function nonNegativeOr(number, fallback) {
|
|
725
|
+
if (Number.isFinite(number) && number >= 0) return number
|
|
726
|
+
return fallback
|
|
727
|
+
}
|
|
728
|
+
// A flag whose absence means on.
|
|
729
|
+
function trueUnlessSet(value) {
|
|
730
|
+
if (value === undefined) return true
|
|
731
|
+
return !!value
|
|
732
|
+
}
|
|
674
733
|
export function deriveSlaParams(settings) {
|
|
675
734
|
const S = k => settingScalar(settings, k)
|
|
676
|
-
const num = (k, d = SLA_FALLBACKS[k]) =>
|
|
735
|
+
const num = (k, d = SLA_FALLBACKS[k]) => positiveOr(Number(S(k)), d)
|
|
677
736
|
|
|
678
737
|
// Read from the MAP, not through the schema fallback: the key is shared with FFF and its schema default is that
|
|
679
738
|
// technology's opinion (0.2mm) — four resin layers thick. Absent means the resin default here.
|
|
680
739
|
const rawLayerHeight = settings?.layer_height
|
|
681
|
-
const mapLayerHeight = Number(
|
|
682
|
-
const layer_height =
|
|
740
|
+
const mapLayerHeight = Number(firstEntryOf(rawLayerHeight))
|
|
741
|
+
const layer_height = positiveOr(mapLayerHeight, SLA_FALLBACKS.layer_height)
|
|
683
742
|
// Zero is a legal fade count (no fade band), so this one cannot go through the positives-only reader.
|
|
684
743
|
const fadedRaw = Number(S('faded_layers'))
|
|
685
744
|
const requestedSupportTree = String(S('support_tree_type') ?? 'default').toLowerCase()
|
|
686
|
-
const support_tree_type = ['default', 'branching', 'organic']
|
|
687
|
-
? requestedSupportTree
|
|
688
|
-
: 'default'
|
|
745
|
+
const support_tree_type = allowedOr(requestedSupportTree, ['default', 'branching', 'organic'], 'default')
|
|
689
746
|
return {
|
|
690
747
|
layer_height,
|
|
691
748
|
initial_layer_height: num('initial_layer_height', layer_height),
|
|
692
749
|
exposure_time: num('exposure_time'),
|
|
693
750
|
initial_exposure_time: num('initial_exposure_time'),
|
|
694
|
-
faded_layers:
|
|
751
|
+
faded_layers: nonNegativeOr(fadedRaw, 10),
|
|
695
752
|
// The physical display and its pixel grid — what turns millimetres into raster pixels at export time.
|
|
696
753
|
// Fallbacks are the SL1's own panel, the reference machine of the format this exports to.
|
|
697
754
|
display_width: num('display_width'),
|
|
@@ -700,8 +757,8 @@ export function deriveSlaParams(settings) {
|
|
|
700
757
|
display_pixels_y: num('display_pixels_y'),
|
|
701
758
|
// Panel mounting: the whole SL1 family is portrait with X mirrored (projection through the vat floor);
|
|
702
759
|
// the raster writer swaps the image axes for portrait exactly like upstream SL1.cpp create_raster.
|
|
703
|
-
display_orientation: String(S('display_orientation') ?? 'portrait')
|
|
704
|
-
display_mirror_x:
|
|
760
|
+
display_orientation: allowedOr(String(S('display_orientation') ?? 'portrait'), ['landscape'], 'portrait'),
|
|
761
|
+
display_mirror_x: trueUnlessSet(settingRaw(settings, 'display_mirror_x')),
|
|
705
762
|
display_mirror_y: !!settingRaw(settings, 'display_mirror_y'),
|
|
706
763
|
// SL1 archive identity fields (config.ini): written as-is, empty when the host never set a named
|
|
707
764
|
// profile — the same thing upstream produces then. material_print_speed defaults to 'fast', the value
|
|
@@ -742,23 +799,23 @@ export function deriveSlaParams(settings) {
|
|
|
742
799
|
// percent sign is stripped here; widening factor and base safety distance are legally ZERO (zero safety
|
|
743
800
|
// falls back to the kernel's own safety_distance), so neither can go through the positives-only reader.
|
|
744
801
|
support_small_pillar_diameter_percent:
|
|
745
|
-
(
|
|
802
|
+
positiveOr(Number(String(S('support_small_pillar_diameter_percent') ?? '').replace('%', '')), 50),
|
|
746
803
|
support_pillar_widening_factor:
|
|
747
|
-
(
|
|
804
|
+
nonNegativeOr(Number(S('support_pillar_widening_factor')), 0),
|
|
748
805
|
support_base_safety_distance:
|
|
749
|
-
(
|
|
806
|
+
nonNegativeOr(Number(S('support_base_safety_distance')), 1),
|
|
750
807
|
support_max_bridges_on_pillar: num('support_max_bridges_on_pillar'),
|
|
751
808
|
support_max_weight_on_model: num('support_max_weight_on_model'),
|
|
752
809
|
// The generator's input slices are gap-closed with this radius, exactly like upstream slice_mesh_ex.
|
|
753
810
|
slice_closing_radius: num('slice_closing_radius'),
|
|
754
|
-
support_object_elevation: (
|
|
811
|
+
support_object_elevation: nonNegativeOr(Number(S('support_object_elevation')), 5),
|
|
755
812
|
support_pillar_connection_mode: String(S('support_pillar_connection_mode') ?? 'dynamic'),
|
|
756
813
|
// The pad (upstream make_pad_cfg): wall height is legally ZERO (flat pad, no cavity), so it cannot go
|
|
757
814
|
// through the positives-only reader.
|
|
758
815
|
pad_brim_size: num('pad_brim_size'),
|
|
759
816
|
pad_wall_thickness: num('pad_wall_thickness'),
|
|
760
817
|
pad_wall_height:
|
|
761
|
-
(
|
|
818
|
+
nonNegativeOr(Number(S('pad_wall_height')), 0),
|
|
762
819
|
pad_wall_slope: num('pad_wall_slope'),
|
|
763
820
|
pad_max_merge_distance: num('pad_max_merge_distance'),
|
|
764
821
|
pad_around_object: !!settingRaw(settings, 'pad_around_object'),
|