From e8ba6aa492ae2407db40c17070e8df91011e2338 Mon Sep 17 00:00:00 2001 From: bakedpotatolord Date: Mon, 21 Jul 2025 21:25:31 -0600 Subject: [PATCH 01/47] fix multi-widget bounding box accuracy --- src/kiri/core/selection.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/kiri/core/selection.js b/src/kiri/core/selection.js index 8349cd26..8f74f751 100644 --- a/src/kiri/core/selection.js +++ b/src/kiri/core/selection.js @@ -49,8 +49,13 @@ function update_info() { const ui = api.ui; let bounds = new THREE.Box3(), track; for_meshes(mesh => { - bounds = bounds.union(mesh.getBoundingBox()); + let box = mesh.getBoundingBox().clone(); track = mesh.widget.track; + let {pos} = track + box.min.add(pos); + box.max.add(pos); + console.log(structuredClone({pos})) + bounds = bounds.union(box); }); if (bounds.min.x === Infinity) { if (selectedMeshes.length === 0) { From bf7022f4f5fd393e71987ff7193a6a9f1cc3365d Mon Sep 17 00:00:00 2001 From: bakedpotatolord Date: Mon, 21 Jul 2025 21:27:11 -0600 Subject: [PATCH 02/47] remove console log --- src/kiri/core/selection.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/kiri/core/selection.js b/src/kiri/core/selection.js index 8f74f751..f5fb2a91 100644 --- a/src/kiri/core/selection.js +++ b/src/kiri/core/selection.js @@ -54,7 +54,6 @@ function update_info() { let {pos} = track box.min.add(pos); box.max.add(pos); - console.log(structuredClone({pos})) bounds = bounds.union(box); }); if (bounds.min.x === Infinity) { From 0b0f24fb16e8d4ac6878b1d1426c22650c369caf Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 4 Aug 2025 19:32:23 -0400 Subject: [PATCH 03/47] fix newSlopeFromAngle in cnc/slice --- src/kiri/mode/cam/slice.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/kiri/mode/cam/slice.js b/src/kiri/mode/cam/slice.js index 3afce49a..6a7dfd71 100644 --- a/src/kiri/mode/cam/slice.js +++ b/src/kiri/mode/cam/slice.js @@ -4,6 +4,7 @@ import { base, util } from '../../../geo/base.js'; import { newLine } from '../../../geo/line.js'; import { newPoint } from '../../../geo/point.js'; import { newPolygon } from '../../../geo/polygon.js'; +import { newSlopeFromAngle } from '../../../geo/slope.js'; import { polygons as POLY } from '../../../geo/polygons.js'; import { setSliceTracker } from '../../core/slice.js'; import { ops as OPS } from './ops.js'; @@ -383,11 +384,11 @@ export function addDogbones(poly, dist, reverse) { let bdiff = ((adiff < 0 ? (180 - adiff) : (180 + adiff)) / 2) + 180; if (!open || (i > 1 && i < length)) { if (isCW && adiff > 45) { - let newa = base.newSlopeFromAngle(lastsl.angle + bdiff); + let newa = newSlopeFromAngle(lastsl.angle + bdiff); newpts.push(lastpt.projectOnSlope(newa, dist)); newpts.push(lastpt.clone()); } else if (!isCW && adiff < -45) { - let newa = base.newSlopeFromAngle(lastsl.angle - bdiff); + let newa = newSlopeFromAngle(lastsl.angle - bdiff); newpts.push(lastpt.projectOnSlope(newa, dist)); newpts.push(lastpt.clone()); } From 01b13491270cd190b7093ed4d9d467de167048ca Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 10 Aug 2025 23:48:10 -0400 Subject: [PATCH 04/47] add cam inner first routing option. add engine sample persistence --- src/kiri/core/conf.js | 1 + src/kiri/mode/cam/init-menu.js | 1 + src/kiri/mode/cam/prepare.js | 6 +++++- src/kiri/mode/cam/slice.js | 2 ++ src/kiri/run/engine.js | 5 +++++ web/kiri/engine.html | 9 +++++++-- web/kiri/lang/en.js | 2 ++ 7 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/kiri/core/conf.js b/src/kiri/core/conf.js index 2e9aa772..23ae1477 100644 --- a/src/kiri/core/conf.js +++ b/src/kiri/core/conf.js @@ -556,6 +556,7 @@ export const conf = { camTabsDepth: 5, camTabsMidline: false, camDepthFirst: false, + camInnerFirst: false, camEaseDown: false, camEaseAngle: 10, camOriginTop: true, diff --git a/src/kiri/mode/cam/init-menu.js b/src/kiri/mode/cam/init-menu.js index 69becf60..9cc96073 100644 --- a/src/kiri/mode/cam/init-menu.js +++ b/src/kiri/mode/cam/init-menu.js @@ -97,6 +97,7 @@ export function menu() { camConventional: newBoolean(LANG.ou_conv_s, onBooleanClick, {title:LANG.ou_conv_l}), camEaseDown: newBoolean(LANG.cr_ease_s, onBooleanClick, {title:LANG.cr_ease_l}), camDepthFirst: newBoolean(LANG.ou_depf_s, onBooleanClick, {title:LANG.ou_depf_l}), + camInnerFirst: newBoolean(LANG.ou_inrf_s, onBooleanClick, {title:LANG.ou_inrf_l}), camToolInit: newBoolean(LANG.ou_toin_s, onBooleanClick, {title:LANG.ou_toin_l}), separator: newBlank({ class:"set-sep", driven }), camFirstZMax: newBoolean(LANG.ou_z1st_s, onBooleanClick, {title:LANG.ou_z1st_l}), diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index d57bf66e..df6ec5f3 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -85,6 +85,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { easeDown = process.camEaseDown, easeAngle = process.camEaseAngle, depthFirst = process.camDepthFirst, + innerFirst = process.camInnerFirst, engageFactor = process.camFullEngage, arcTolerance = process.camArcTolerance, arcRes = toRadians(process.camArcResolution), @@ -719,7 +720,10 @@ export function prepEach(widget, settings, print, firstPoint, update) { setTool(tool, rate, plunge); let traceTool = new Tool(settings, tool); let traceToolDiam = traceTool.fluteDiameter(); - printPoint = poly2polyEmit(slice.camLines, printPoint, polyEmit, { swapdir: false }); + printPoint = poly2polyEmit(slice.camLines, printPoint, polyEmit, { + swapdir: false, + weight: innerFirst + }); newLayer(); } diff --git a/src/kiri/mode/cam/slice.js b/src/kiri/mode/cam/slice.js index 6a7dfd71..ba06f5d6 100644 --- a/src/kiri/mode/cam/slice.js +++ b/src/kiri/mode/cam/slice.js @@ -87,6 +87,8 @@ export async function cam_slice(settings, widget, onupdate, ondone) { bottom_z, bottom_cut }, 3); + console.log({ track, bounds, stock, workarea }); + return structuredClone(workarea); }; diff --git a/src/kiri/run/engine.js b/src/kiri/run/engine.js index abd69b2a..00824cbe 100644 --- a/src/kiri/run/engine.js +++ b/src/kiri/run/engine.js @@ -126,6 +126,10 @@ class Engine { return this; } + setTopOffset(offset = 0) { + this.topOffset = offset; + } + setOrigin(x, y, z) { this.settings.origin = { x, y, z }; return this; @@ -152,6 +156,7 @@ class Engine { } slice() { + this.widget.setTopZ(this.settings.stock.z - (this.topOffset || 0)); return new Promise((accept, reject) => { client.clear(); client.sync([this.widget]); diff --git a/web/kiri/engine.html b/web/kiri/engine.html index da417539..40b62df6 100644 --- a/web/kiri/engine.html +++ b/web/kiri/engine.html @@ -99,7 +99,11 @@ }); edit.session.setTabSize(4); edit.session.setUseSoftTabs(true); - let FDMExample = loadExample(demoFDM) + if (localStorage.engineCode) { + edit.session.setValue(localStorage.engineCode); + } else { + loadExample(demoFDM); + } } function display_message(msg) { $('gfoot').innerText = msg ? Object.keys(msg).join(' - ') : ''; @@ -110,7 +114,7 @@ $('dfoot').style.display = 'block'; $('gfoot').style.display = ''; } - function loadExample(func){ + function loadExample(func) { let fnstr = func.toString().split("\n"); edit.session.setValue( fnstr @@ -124,6 +128,7 @@ $('gfoot').style.display = 'block'; let code = edit.session.getValue(); eval(`(function(){${code}})()`); + localStorage.engineCode = code; } function demoFDM() { new Engine() diff --git a/web/kiri/lang/en.js b/web/kiri/lang/en.js index bc713531..fdaa35b3 100644 --- a/web/kiri/lang/en.js +++ b/web/kiri/lang/en.js @@ -790,6 +790,8 @@ self.lang['en-us'] = { ou_conv_l: ["milling direction","uncheck for 'climb'"], ou_depf_s: "depth first", ou_depf_l: ["optimize pocket cuts","with depth priority"], + ou_inrf_s: "inner first", + ou_inrf_l: ["always cut inner to outer"], ou_toin_s: "tool init", ou_toin_l: "add tool change with first operation. when disabled, the gcode assumes the first tool is loaded and offset probed", ou_forz_s: "force z max", From e459d8e765280a2d88157985e8b5546b653b77c5 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 18 Aug 2025 19:50:26 -0400 Subject: [PATCH 05/47] update manifold dep, rm knife params from laser / wjet --- package.json | 2 +- src/kiri/core/conf.js | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/package.json b/package.json index 112e16e8..5e9bdcc9 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "fast-glob": "^3.3.3", "i": "^0.3.7", "jszip": "^3.7.1", - "manifold-3d": "^3.1.1", + "manifold-3d": "^3.2.1", "moment": "^2.29.4", "npm": "^11.4.2", "prettier": "^3.5.3", diff --git a/src/kiri/core/conf.js b/src/kiri/core/conf.js index 23ae1477..d8f38f30 100644 --- a/src/kiri/core/conf.js +++ b/src/kiri/core/conf.js @@ -640,9 +640,6 @@ export const conf = { ctOriginBounds: false, outputInvertX: false, outputInvertY: false, - ctOutKnifeDepth: 1, - ctOutKnifePasses: 1, - ctOutKnifeTip: 2, ctOutInches: false, ctOutShaper: false } @@ -729,9 +726,6 @@ export const conf = { ctOriginBounds: false, outputInvertX: false, outputInvertY: false, - ctOutKnifeDepth: 1, - ctOutKnifePasses: 1, - ctOutKnifeTip: 2, ctOutStack: false, }, }, From 9b809382eca4ea3f55e15a455d5a5d7795f7c0d8 Mon Sep 17 00:00:00 2001 From: bakedpotatolord Date: Tue, 19 Aug 2025 15:58:15 -0600 Subject: [PATCH 06/47] port bugfix from 4.2 --- src/kiri/mode/cam/op-rough.js | 2 +- src/kiri/mode/cam/prepare.js | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/kiri/mode/cam/op-rough.js b/src/kiri/mode/cam/op-rough.js index 7145bb73..e778ca0c 100644 --- a/src/kiri/mode/cam/op-rough.js +++ b/src/kiri/mode/cam/op-rough.js @@ -302,7 +302,7 @@ class OpRough extends CamOp { // set winding specified in output POLY.setWinding(level, cutdir, false); poly2polyEmit(level, printPoint, (poly, index, count) => { - printPoint = polyEmit(poly, index, count, printPoint); + printPoint = polyEmit(poly, index, count, printPoint, {cutFromLast: true}); }); newLayer(); } diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index d57bf66e..969f23c8 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -736,10 +736,15 @@ export function prepEach(widget, settings, print, firstPoint, update) { * @param {number} index - unused * @param {number} count - 1 to set engage factor * @param {Point} fromPoint - the point to rapid move from - * @param {Object} camOutOpts - optional parameters to pass to camOut + * @param {boolean} ops.cutFromLast - whether to emit a 1 when moving from last point. Defaults to false * @returns {Point} - the last point of the polygon */ - function polyEmit(poly, index, count, fromPoint) { + function polyEmit(poly, index, count, fromPoint,ops) { + let { + cutFromLast, + } = ops ?? { + cutFromLast: false + }; let arcQ = [], arcMax = Infinity, // no max arc radius lineTolerance = 0.001, // do not consider points under 0.001mm for arcs @@ -775,7 +780,8 @@ export function prepEach(widget, settings, print, firstPoint, update) { // if(offset == 0) console.log("forEachPoint",point,pidx,points) // console.log({pointA, pointB, indexA, indexB,startIndex}) if (indexA == startIndex) { - camOut(pointA.clone(), 0, { factor: engageFactor }); + //if cutFromLast is true, emit a 1 for a cutting move + camOut(pointA.clone(), cutFromLast ? 1 : 0, { factor: engageFactor }); // if first point, move to and call export function if (arcEnabled) arcQ.push(pointA); } From 2e407a4005c537f480abd7af501403ad82f4a519 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 21 Aug 2025 22:33:46 -0400 Subject: [PATCH 07/47] fix missing md5 import --- src/kiri/core/export.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/kiri/core/export.js b/src/kiri/core/export.js index ae3c50cf..b7440ab1 100644 --- a/src/kiri/core/export.js +++ b/src/kiri/core/export.js @@ -8,6 +8,7 @@ import { util } from '../../geo/base.js'; import { MODES } from './consts.js'; import { LASER as laser_driver } from '../mode/laser/driver.js'; import { SLA as sla_client } from '../mode/sla/client.js'; +import { hash } from '../../ext/md5.js'; let printSeq = parseInt(local['kiri-print-seq'] || local['print-seq'] || "0") + 1; @@ -654,7 +655,7 @@ function exportGCodeDialog(gcode, sections, info, names) { data: gcode },{ name: `Metadata/plate_1.gcode.md5`, - data: ext.md5.hash(gcode) + data: hash(gcode) },{ name: `Metadata/plate_1_small.png`, data: api.view.bambu.s128.png From f2e5dee582d01d2e45d7a40b9d5b8892a174043d Mon Sep 17 00:00:00 2001 From: bakedpotatolord Date: Mon, 25 Aug 2025 13:07:19 -0600 Subject: [PATCH 08/47] add new profiles from forums --- src/kiri/dev/fdm/flashforge.5m.pro.json | 219 ++++++++++++++++++++++++ src/kiri/dev/fdm/taz.lulzbot.747.json | 207 ++++++++++++++++++++++ 2 files changed, 426 insertions(+) create mode 100644 src/kiri/dev/fdm/flashforge.5m.pro.json create mode 100644 src/kiri/dev/fdm/taz.lulzbot.747.json diff --git a/src/kiri/dev/fdm/flashforge.5m.pro.json b/src/kiri/dev/fdm/flashforge.5m.pro.json new file mode 100644 index 00000000..154f51d9 --- /dev/null +++ b/src/kiri/dev/fdm/flashforge.5m.pro.json @@ -0,0 +1,219 @@ +{ + "version": "4.2.3", + "device": "Flashforge 5M Pro", + "process": { + "processName": "Flashforge 5m Pro .2 Height", + "sliceHeight": 0.2, + "sliceShells": 3, + "sliceShellOrder": "in-out", + "sliceLayerStart": "last", + "sliceFillAngle": 45, + "sliceFillOverlap": 0.6, + "sliceFillSparse": 0.08, + "sliceFillRate": 300, + "sliceFillType": "hex", + "sliceSupportDensity": 0.2, + "sliceSupportOffset": 1, + "sliceSupportGap": 1, + "sliceSupportSize": 5, + "sliceSupportArea": 10, + "sliceSupportExtra": 3, + "sliceSupportAngle": 50, + "sliceSupportNozzle": "0", + "sliceSupportEnable": false, + "sliceSolidMinArea": 1.2, + "sliceBottomLayers": 2, + "sliceTopLayers": 2, + "firstSliceHeight": 0.2, + "firstLayerRate": 30, + "firstLayerFillRate": 40, + "firstLayerPrintMult": 1, + "firstLayerLineMult": 1, + "firstLayerYOffset": 0, + "firstLayerNozzleTemp": 205, + "firstLayerBedTemp": 56, + "firstLayerBrim": 0, + "firstLayerBrimTrig": 0, + "firstLayerBeltLead": 3, + "outputRaft": false, + "outputRaftSpacing": 0.2, + "outputTemp": 205, + "outputBedTemp": 56, + "outputFeedrate": 500, + "outputFinishrate": 250, + "outputSeekrate": 500, + "outputShellMult": 1.25, + "outputFillMult": 1.25, + "outputSparseMult": 1.3, + "outputRetractDist": 4, + "outputRetractSpeed": 40, + "outputRetractDwell": 20, + "outputBrimCount": 2, + "outputBrimOffset": 4, + "outputShortPoly": 100, + "outputMinSpeed": 10, + "outputCoastDist": 0, + "outputLayerRetract": false, + "outputInvertX": false, + "outputInvertY": false, + "sliceMinHeight": 0, + "sliceAdaptive": false, + "zHopDistance": 0.2, + "antiBacklash": 1, + "sliceFillWidth": 1, + "firstLayerFanSpeed": 0, + "outputFanSpeed": 255, + "outputRetractWipe": 4, + "outputPurgeTower": 0, + "arcTolerance": 0.15, + "ranges": [], + "sliceLineWidth": 0, + "sliceFillRepeat": 1, + "sliceSupportSpan": 5, + "sliceSupportOutline": false, + "firstLayerBrimIn": 0, + "firstLayerBrimGap": 0, + "firstLayerBeltBump": 0, + "firstLayerFlatten": 0, + "outputDraftShield": false, + "outputBeltFirst": false, + "outputAvoidGaps": true, + "outputLoops": 0, + "sliceFillGrow": 0, + "sliceSolidRate": 200, + "outputAlternating": false, + "sliceDetectThin": "off", + "sliceLayerStartX": 0, + "sliceLayerStartY": 0, + "sliceSupportGrow": 3, + "sliceAngle": 45, + "outputFanLayer": 1, + "sliceZInterleave": false + }, + "code": { + "mode": "FDM", + "internal": 0, + "bedHeight": 2.5, + "bedWidth": 225, + "bedDepth": 225, + "bedRound": false, + "bedBelt": false, + "deviceZMax": 0, + "gcodeTime": 1, + "maxHeight": 225, + "originCenter": true, + "gcodeFan": [ + "M106 S{fan_speed}" + ], + "gcodeFeature": [], + "gcodeTrack": [], + "gcodeLayer": [ + ";AFTER_LAYER_CHANGE", + ";[layer_z]" + ], + "gcodePre": [ + "; EXECUTABLE_BLOCK_START", + "M73 P0 R80", + "M106 S0", + "M106 P2 S0", + ";TYPE:Custom", + "M190 S55", + "M104 S210", + "G90", + "M83", + "G1 Z5 F6000", + "G1 E-0.2 F800", + "G1 X110 Y-110 F6000", + "G1 E2 F800", + "G1 Y-110 X55 Z0.25 F4800", + "G1 X-55 E8 F2400", + "G1 Y-109.6 F2400", + "G1 X55 E5 F2400", + "G1 Y-110 X55 Z0.45 F4800", + "G1 X-55 E8 F2400", + "G1 Y-109.6 F2400", + "G1 X55 E5 F2400", + "G92 E0", + "G90", + "G21", + "M83 ; use relative distances for extrusion", + "; filament start gcode", + ";right_extruder_material: PLA", + "SET_PRESSURE_ADVANCE ADVANCE=0; Override pressure advance value", + "M106 S0", + "M106 P2 S0", + ";LAYER_CHANGE", + ";Z:0.2", + ";HEIGHT:0.2", + ";BEFORE_LAYER_CHANGE", + ";0.2", + "G1 E-.8 F2100", + ";AFTER_LAYER_CHANGE", + ";0.2", + ";_SET_FAN_SPEED_CHANGING_LAYER", + "SET_VELOCITY_LIMIT ACCEL=500", + "G1 X-25.945 Y-17.607 F30000", + "G1 Z.4", + "G1 Z.2", + "G1 E.8 F2100" + ], + "gcodePost": [ + "M106 S0", + "M106 P2 S0", + ";TYPE:Custom", + "; filament end gcode", + "G1 E-3 F3600", + "G0 X50 Y50 F30000", + "M104 S0 ; turn off temperature", + "M73 P100 R0" + ], + "gcodeProc": "", + "gcodeFExt": "gcode", + "extruders": [ + { + "extFilament": 1.75, + "extNozzle": 0.4, + "extSelect": [ + "T0" + ], + "extDeselect": [], + "extOffsetX": 0, + "extOffsetY": 0 + }, + { + "extFilament": 1.75, + "extNozzle": 0.4, + "extSelect": [ + "T1" + ], + "extDeselect": [], + "extOffsetX": 30, + "extOffsetY": 0 + } + ], + "new": false, + "deviceName": "Flashforge 5M Pro", + "imageURL": "", + "fwRetract": true, + "filamentSource": "direct", + "profiles": [], + "extras": { + "palette": { + "printer": "6806c3eabbdb", + "ping": null, + "feed": null, + "push": null, + "offset": null, + "heat": null, + "cool": null, + "press": null + } + }, + "gcodeChange": [ + "T{tool}" + ] + }, + "time": 1756051792656, + "mode": "FDM", + "name": "Flashforge 5m Pro .2 Height" +} \ No newline at end of file diff --git a/src/kiri/dev/fdm/taz.lulzbot.747.json b/src/kiri/dev/fdm/taz.lulzbot.747.json new file mode 100644 index 00000000..f1f225fa --- /dev/null +++ b/src/kiri/dev/fdm/taz.lulzbot.747.json @@ -0,0 +1,207 @@ +{ + "version": "4.2.3", + "device": "Taz LulzBot 747", + "process": { + "processName": "747 .3 LH", + "sliceHeight": 0.3, + "sliceShells": 3, + "sliceShellOrder": "in-out", + "sliceLayerStart": "last", + "sliceFillAngle": 45, + "sliceFillOverlap": 0.3, + "sliceFillSparse": 0.07, + "sliceFillRate": 70, + "sliceFillType": "hex", + "sliceSupportDensity": 0.1, + "sliceSupportOffset": 1, + "sliceSupportGap": 1, + "sliceSupportSize": 25, + "sliceSupportArea": 0.1, + "sliceSupportExtra": 0, + "sliceSupportAngle": 50, + "sliceSupportNozzle": 0, + "sliceSupportEnable": false, + "sliceSolidMinArea": 1, + "sliceBottomLayers": 2, + "sliceTopLayers": 3, + "firstSliceHeight": 0.3, + "firstLayerRate": 15, + "firstLayerFillRate": 15, + "firstLayerPrintMult": 1, + "firstLayerLineMult": 1, + "firstLayerYOffset": 0, + "firstLayerNozzleTemp": 210, + "firstLayerBedTemp": 56, + "firstLayerBrim": 0, + "firstLayerBrimTrig": 0, + "firstLayerBeltLead": 3, + "outputRaft": false, + "outputRaftSpacing": 0.2, + "outputTemp": 210, + "outputBedTemp": 56, + "outputFeedrate": 80, + "outputFinishrate": 40, + "outputSeekrate": 120, + "outputShellMult": 1.15, + "outputFillMult": 1.15, + "outputSparseMult": 1.15, + "outputRetractDist": 0.9, + "outputRetractSpeed": 25, + "outputRetractDwell": 20, + "outputBrimCount": 2, + "outputBrimOffset": 4, + "outputShortPoly": 100, + "outputMinSpeed": 10, + "outputCoastDist": 0, + "outputLayerRetract": true, + "outputInvertX": false, + "outputInvertY": false, + "sliceMinHeight": 0, + "sliceAdaptive": false, + "zHopDistance": 0.6, + "antiBacklash": 1, + "sliceFillWidth": 1, + "firstLayerFanSpeed": 200, + "outputFanSpeed": 255, + "outputRetractWipe": 0, + "outputPurgeTower": 0, + "arcTolerance": 0, + "ranges": [], + "sliceLineWidth": 0, + "sliceFillRepeat": 1, + "sliceSupportSpan": 5, + "sliceSupportOutline": false, + "firstLayerBrimIn": 0, + "firstLayerBrimGap": 0, + "firstLayerBeltBump": 0, + "firstLayerFlatten": 0, + "outputDraftShield": false, + "outputBeltFirst": false, + "outputAvoidGaps": true, + "outputLoops": 0, + "sliceFillGrow": 0, + "sliceSolidRate": 40, + "outputAlternating": false, + "sliceDetectThin": "off", + "sliceLayerStartX": 0, + "sliceLayerStartY": 0, + "sliceSupportGrow": 0, + "sliceAngle": 45, + "outputFanLayer": 1, + "sliceZInterleave": false + }, + "code": { + "mode": "FDM", + "internal": 0, + "imageURL": "", + "bedHeight": 2.5, + "bedWidth": 231, + "bedDepth": 231, + "bedRound": false, + "bedBelt": false, + "deviceZMax": 0, + "gcodeTime": 1, + "maxHeight": 248, + "originCenter": false, + "gcodeFan": [ + "M106 S{fan_speed}" + ], + "gcodeFeature": [], + "gcodeTrack": [], + "gcodeLayer": [], + "gcodePre": [ + ";Generated by Cura LulzBot Edition GCodeWriter Version: 3.6.37", + ";FLAVOR:Marlin", + ";TIME:7538", + ";Filament used: 5.35322m", + ";Layer height: 0.18", + ";Generated with Cura_SteamEngine 3.6.37-win10", + "M82 ;absolute extrusion mode", + ";This G-Code has been generated specifically for the LulzBot Sidekick with a Universal Tool Head", + ";", + ";The following lines can be uncommented for printer specific fine tuning", + ";More information can be found at https://marlinfw.org/meta/gcode/ (Note: current values are based on SE Tool Head)", + ";", + "M92 E420 ;Set Axis Steps-per-unit", + "M301 P21.0 I1.78 D61.93 ;Set Hotend PID", + "M906 E960 ;TMC Motor Current", + ";", + "G90 ; absolute coordinate", + "M82 ; set extruder to absolute mode", + "G92 E0 ; set extruder position to 0", + "M117 Heating... ; progress indicator message on LCD", + "M140 S50.0 ; start bed heating up", + "M109 R140 ; soften filament before homing Z", + "G28 ; Home all axis", + "G1 E-15 F100 ; retract filament", + "M104 S140 ; start extruder heating to probe temp", + "M190 S57.0 ; wait for bed to reach printing temp", + "G29 ; start auto leveling", + "G0 X0 Y0 F5000", + "M109 R210 ; wait for extruder to reach initial printing temp", + "M117 SideKick Printing... ; progress indicator message on LCD", + "G1 Z2 E0 F75 ; prime tiny bit of filament into the nozzle", + "M221 S100 T0", + "G92 E0", + "G1 F600 E-1.75" + ], + "gcodePost": [ + ";This G-Code has been generated specifically for the LulzBot Sidekick with a Universal Tool Head", + "M400 ; wait for moves to finish", + "M140 S45.0 ; start bed cooling", + "M104 S0 ; disable hotend", + "M107 ; disable fans", + "G92 E5 ; set extruder to 5mm for retract on print end", + "M117 Cooling please wait ; progress indicator message on LCD", + "G1 Y-10 Z251.3 E0 F3000 ; move to cooling position", + "G1 E5 ; re-prime extruder", + "M190 R45.0 ; wait for bed to cool down to removal temp", + "G27 ; present finished print", + "M140 S0; keep temperature or cool down", + "M77\t\t\t\t\t ; End GLCD Print Timer", + "G90 ; absolute positioning", + "M18 X E\t\t\t\t \t ; turn off x and e axis", + "M117 Print Complete. ; print complete message", + "M82 ;absolute extrusion mode", + "M104 S0", + ";End of Gcode" + ], + "gcodeProc": "", + "gcodeFExt": "gcode", + "extruders": [ + { + "extFilament": 1.75, + "extNozzle": 0.5, + "extSelect": [ + "T0" + ], + "extDeselect": [], + "extOffsetX": 0, + "extOffsetY": 0 + } + ], + "new": false, + "deviceName": "Taz LulzBot 747", + "fwRetract": false, + "filamentSource": "direct", + "profiles": [], + "extras": { + "palette": { + "printer": "6806c3eabbdb", + "ping": null, + "feed": null, + "push": null, + "offset": null, + "heat": null, + "cool": null, + "press": null + } + }, + "gcodeChange": [ + "T{tool}" + ] + }, + "time": 1756051830810, + "mode": "FDM", + "name": "747 .3 LH" +} \ No newline at end of file From ae2a669c4cfc021db42fbf00fea304cebb4bbef6 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 31 Aug 2025 18:06:53 -0400 Subject: [PATCH 09/47] fix tab cuts into rough clear top --- src/kiri/mode/cam/op-rough.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/kiri/mode/cam/op-rough.js b/src/kiri/mode/cam/op-rough.js index e778ca0c..ad034435 100644 --- a/src/kiri/mode/cam/op-rough.js +++ b/src/kiri/mode/cam/op-rough.js @@ -55,7 +55,14 @@ class OpRough extends CamOp { for (let z = zstart; zsteps > 0; zsteps--) { let slice = newSlice(); slice.z = z; - slice.camLines = POLY.setZ(facing.clone(true), slice.z + roughLeaveZ); + let polys = POLY.setZ(facing.clone(true), slice.z + roughLeaveZ); + if (tabs) { + tabs.forEach(tab => { + tab.off = POLY.expand([tab.poly], toolDiam / 2).flat(); + }); + polys = cutTabs(tabs, polys, slice.z); + } + slice.camLines = polys; slice.output() .setLayer("face", {face: color, line: color}) .addPolys(slice.camLines); From 35660d39cfb1d11d9a91b580f14a4d64ae44b7ff Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 31 Aug 2025 22:13:42 -0400 Subject: [PATCH 10/47] relax drill circularity for lower poly. add ; bc no ts syntax --- mods/bambu/bambu.js | 2 +- src/geo/polygon.js | 2 +- src/kiri/mode/cam/slice.js | 50 ++++++++++++++++++++------------------ 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/mods/bambu/bambu.js b/mods/bambu/bambu.js index 2a74ebe7..2903cfbd 100644 --- a/mods/bambu/bambu.js +++ b/mods/bambu/bambu.js @@ -1115,7 +1115,7 @@ self.kiri.load(api => { ]), h.div({ class: "var-row f-grow" }, [ h.label('active'), - h.input({ id: "bbl_file_active", class: "t-left", readonly }) + h.input({ id: "bbl_file_active", class: "t-left", style: "width: 20ch; max-width: 20ch", readonly }) ]), h.div({ class: "pop-sep" }), h.div({ class: "f-row gap3 f-grow" }, [ diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 11a5732d..38957f46 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -652,7 +652,7 @@ export class Polygon { td += d; if (ap.length === 1 && td >= pe1) { ap.push(np); - } else if (ap.length === 2 && td >= pe2) { + } else if (ap.length === 2 && (td >= pe2 || i >= l)) { ap.push(np); break; } diff --git a/src/kiri/mode/cam/slice.js b/src/kiri/mode/cam/slice.js index ba06f5d6..cf77b042 100644 --- a/src/kiri/mode/cam/slice.js +++ b/src/kiri/mode/cam/slice.js @@ -87,7 +87,7 @@ export async function cam_slice(settings, widget, onupdate, ondone) { bottom_z, bottom_cut }, 3); - console.log({ track, bounds, stock, workarea }); + // console.log({ track, bounds, stock, workarea }); return structuredClone(workarea); }; @@ -660,9 +660,9 @@ export async function holes(settings, widget, individual, rec, onProgress) { // keeps the z bottom relative to the part when z align changes zBottom = isIndexed ? camZBottom : camZBottom - zbOff; - let slicerOpts = { flatoff: 0.001 } + let slicerOpts = { flatoff: 0.001 }; let slicer = new cam_slicer(widget, slicerOpts); - let zFlats = Object.keys(slicer.zFlat).map(Number).map(z => [z, z - 0.002]).flat() + let zFlats = Object.keys(slicer.zFlat).map(Number).map(z => [z, z - 0.002]).flat(); precision = Math.max(0, precision) let intervals = (precision == 0) ? [] : slicer.interval( @@ -672,11 +672,11 @@ export async function holes(settings, widget, individual, rec, onProgress) { } ) - let zees = [...zFlats, ...intervals] + let zees = [...zFlats, ...intervals]; let indices = [...new Set(zees .map(kv => parseFloat(kv).round(5)) .filter(z => z !== null) - )] + )]; let centerDiff = diam * 0.1, area = (diam / 2) * (diam / 2) * Math.PI, circles = [], @@ -692,19 +692,20 @@ export async function holes(settings, widget, individual, rec, onProgress) { for (let [i, slice] of slices.entries()) { for (let top of slice.tops) { // console.log("slicing",slice.z,top) - slice.shadow = computeShadowAt(widget, slice.z, 0) + slice.shadow = computeShadowAt(widget, slice.z, 0); let inner = top.inner; if (!inner) { //no holes continue; } for (let poly of inner) { if (poly.points.length < 7) continue; + let center = poly.calcCircleCenter(); center.area = poly.area(); - center.overlapping = [center] + center.overlapping = [center]; center.depth = 0; // console.log("center",center) - if (poly.circularity() < 0.985) { + if (poly.circularity() < 0.98) { // if not circular, don't add to holes continue; } @@ -715,7 +716,7 @@ export async function holes(settings, widget, individual, rec, onProgress) { } let overlap = false; for (let [i, circle] of circles.entries()) { - let dist = circle.distTo2D(center) + let dist = circle.distTo2D(center); // //if on the same xy point, if (dist <= centerDiff) { @@ -729,19 +730,19 @@ export async function holes(settings, widget, individual, rec, onProgress) { if (!overlap) circles.push(center); } } - onProgress(0.5 + (i / slices.length * 0.25), "recognize circles") + onProgress(0.5 + (i / slices.length * 0.25), "recognize circles"); } - let drills = [] + let drills = []; for (let [i, c] of circles.entries()) { - let overlapping = c.overlapping + let overlapping = c.overlapping; let last = overlapping.shift(); while (overlapping.length) { let circ = overlapping.shift(); let aveArea = (circ.area + last.area) / 2; - let areaDelta = Math.abs(circ.area - last.area) + let areaDelta = Math.abs(circ.area - last.area); if (areaDelta < aveArea * 0.05) { // if area delta less than 5% of average area //keep top circle selected last.depth = last.z - circ.z; @@ -751,24 +752,25 @@ export async function holes(settings, widget, individual, rec, onProgress) { last = circ; } } - if (last.depth != 0) drills.push(last) //add last circle - onProgress(0.75 + (i / circles.length * 0.25), "assemble holes") + if (last.depth != 0) drills.push(last); // add last circle + onProgress(0.75 + (i / circles.length * 0.25), "assemble holes"); } drills.forEach(h => { if (rec.fromTop) { // set z top if selected - h.depth += wztop - h.z - h.z = wztop + h.depth += wztop - h.z; + h.z = wztop; } - delete h.overlapping //for encoding - h.diam = toolDiam // for mesh generation - h.selected = (!individual && Math.abs(h.area - area) <= area * 0.05); //for same size selection + delete h.overlapping; //for encoding + h.diam = toolDiam; // for mesh generation + h.selected = (!individual && Math.abs(h.area - area) <= area * 0.05); // for same size selection }) - // console.log("unfiltered circles",circles) - // console.log("drills",drills) - drills = drills.filter(drill => drill.depth > 0) - widget.shadowedDrills = shadowedDrills + console.log("unfiltered circles",circles); + console.log("drills",drills); + + drills = drills.filter(drill => drill.depth > 0); + widget.shadowedDrills = shadowedDrills; widget.drills = drills; return drills; } From d7f13754c84bbb2436f4590bd44fde53eb0d4927 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 1 Sep 2025 16:56:43 -0400 Subject: [PATCH 11/47] fix widget replace --- src/kiri/core/widget.js | 2 +- src/kiri/core/widgets.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kiri/core/widget.js b/src/kiri/core/widget.js index 7e0cd9cf..3e340433 100644 --- a/src/kiri/core/widget.js +++ b/src/kiri/core/widget.js @@ -155,7 +155,7 @@ class Widget { * @returns {Widget} */ loadVertices(data, options = { index: false }) { - // console.log({ loadVertices: this.id, worker: this.inWorker, data }); + // console.trace({ loadVertices: this.id, worker: this.inWorker, data }); let vertices, autoscale = false; if (ArrayBuffer.isView(data) || typeof(data) != 'object') { diff --git a/src/kiri/core/widgets.js b/src/kiri/core/widgets.js index 56c1e9af..3cfe2d62 100644 --- a/src/kiri/core/widgets.js +++ b/src/kiri/core/widgets.js @@ -63,7 +63,7 @@ function replace(vertices, sel) { } else { // dialog $('load-file').onchange = function(event) { - load.File.load(event.target.files[0]) + load(event.target.files[0]) .then(data => onload(data[0].mesh)) .catch(error => console.log({error})); }; From f593f57c60865e84df04214862d5c60a7dbb56c6 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 1 Sep 2025 17:11:43 -0400 Subject: [PATCH 12/47] fix op register import --- src/kiri/mode/cam/op-register.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kiri/mode/cam/op-register.js b/src/kiri/mode/cam/op-register.js index 99cb4052..9c01da50 100644 --- a/src/kiri/mode/cam/op-register.js +++ b/src/kiri/mode/cam/op-register.js @@ -5,7 +5,7 @@ import { Tool } from './tool.js'; import { newPolygon } from '../../../geo/polygon.js'; import { newSlice } from '../../core/slice.js'; import { newPoint } from '../../../geo/point.js'; -import { util as base_util } from '../../../geo/base.js'; +import { util } from '../../../geo/base.js'; class OpRegister extends CamOp { constructor(state, op) { @@ -88,7 +88,7 @@ class OpRegister extends CamOp { step = -step; } } - for (let z of base.util.lerp(startZ, endZ, op.down)) { + for (let z of util.lerp(startZ, endZ, op.down)) { let slice = newSlice(z); addSlices(slice); sliceOut.push(slice); From 61d2e30e07c33fc007a18c16add4a7695763d151 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Tue, 2 Sep 2025 08:48:50 -0400 Subject: [PATCH 13/47] fix tools dependency for face down and camera selection --- src/kiri/core/init.js | 1 + src/kiri/core/tools.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/kiri/core/init.js b/src/kiri/core/init.js index 65703282..36a68da0 100644 --- a/src/kiri/core/init.js +++ b/src/kiri/core/init.js @@ -20,6 +20,7 @@ import { menu as menuCAM } from '../mode/cam/init-menu.js'; import { menu as menuFDM } from '../mode/fdm/init-menu.js'; import { menu as menuLaser } from '../mode/laser/init-menu.js'; import { menu as menuSLA } from '../mode/sla/init-menu.js'; +import './tools.js'; let { CAM, SLA, FDM, LASER, DRAG, WJET, WEDM } = MODES, { client, catalog, platform, selection, stats } = api, diff --git a/src/kiri/core/tools.js b/src/kiri/core/tools.js index 9ab3a079..e4738c22 100644 --- a/src/kiri/core/tools.js +++ b/src/kiri/core/tools.js @@ -2,9 +2,9 @@ import { api } from './api.js'; import { THREE } from '../../ext/three.js'; +import { space } from '../../moto/space.js'; const { Vector3, Quaternion } = THREE; -const { kiri, moto } = root; const XAXIS = new THREE.Vector3(1,0,0); const ZAXIS = new THREE.Vector3(0,0,1); @@ -106,7 +106,7 @@ api.event.on('tool.mesh.lay-flat', () => { }); function scale() { - let cam = moto.space.internals().camera; + let cam = space.internals().camera; let dist = cam.position.distanceTo(pmesh.position); let scale = opName === 'camera focus' ? Math.min(0.25, 100 / dist) : dist / 100; From de6747606a359404f46bebadb53a8fb692ba02a7 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Tue, 2 Sep 2025 14:01:16 -0400 Subject: [PATCH 14/47] fix tools/device import/export --- src/kiri/core/devices.js | 2 +- src/kiri/core/main.js | 1 + src/kiri/core/settings.js | 10 ++++++---- src/kiri/mode/cam/tools.js | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/kiri/core/devices.js b/src/kiri/core/devices.js index 0c508851..b4d7c701 100644 --- a/src/kiri/core/devices.js +++ b/src/kiri/core/devices.js @@ -331,7 +331,7 @@ function renderDevices(devices) { }; ui.deviceExport.onclick = function(event) { const record = { - version: kiri.version, + version: api.version, device: selected, process: api.process.code(), profiles: event.altKey ? settings.prof() : undefined, diff --git a/src/kiri/core/main.js b/src/kiri/core/main.js index 1cb4d968..51a00689 100644 --- a/src/kiri/core/main.js +++ b/src/kiri/core/main.js @@ -1,6 +1,7 @@ /** Copyright Stewart Allen -- All Rights Reserved */ import './frame.js'; +import '../../ext/base64.js'; import lang from './lang.js'; import STACKS from './stacks.js'; diff --git a/src/kiri/core/settings.js b/src/kiri/core/settings.js index 1831126e..1a5b65b0 100644 --- a/src/kiri/core/settings.js +++ b/src/kiri/core/settings.js @@ -544,12 +544,14 @@ function settingsImport(data, ask) { return; } } + if (api.const.LOCAL) console.log('import', data); - let isSettings = (data.settings && data.version && data.time); - let isProcess = (data.process && data.version && data.time && data.mode && data.name); - let isDevice = (data.device && data.version && data.time); - let isTools = (data.tools && data.version && data.time) + let isSettings = (data.settings && data.time); + let isProcess = (data.process && data.time && data.mode && data.name); + let isDevice = (data.device && data.time); + let isTools = (data.tools && data.time) let isWork = (data.work); + if (!isSettings && !isDevice && !isProcess && !isTools) { uc.alert('invalid settings or device format'); console.log('data',data); diff --git a/src/kiri/mode/cam/tools.js b/src/kiri/mode/cam/tools.js index 7d8007d7..9552fccb 100644 --- a/src/kiri/mode/cam/tools.js +++ b/src/kiri/mode/cam/tools.js @@ -318,7 +318,7 @@ function _showTools() { return; } const record = { - version: kiri.version, + version: api.version, tools: api.conf.get().tools, time: Date.now() }; From 4930aa82f25ce2be2ce5ec98d41fb4252c78e2cc Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Tue, 2 Sep 2025 22:50:18 -0400 Subject: [PATCH 15/47] fix trace follow winding direction --- src/kiri/mode/cam/op-trace.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/kiri/mode/cam/op-trace.js b/src/kiri/mode/cam/op-trace.js index 6ec46dba..f8a937bf 100644 --- a/src/kiri/mode/cam/op-trace.js +++ b/src/kiri/mode/cam/op-trace.js @@ -89,6 +89,7 @@ class OpTrace extends CamOp { if (reContour) { state.contourPolys(widget, slice.camLines); } + POLY.setWinding(slice.camLines, cutdir, false); slice.output() .setLayer("trace follow", {line: color}, false) .addPolys(slice.camLines) From f272ce86f060d0d0368f4fcc70353ca4a36ea541 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Wed, 3 Sep 2025 13:09:18 -0400 Subject: [PATCH 16/47] fix missing import, add inner first to outline and rough --- src/kiri/mode/cam/cl-flip.js | 2 +- src/kiri/mode/cam/client.js | 2 +- src/kiri/mode/cam/op-outline.js | 6 ++++-- src/kiri/mode/cam/op-rough.js | 2 ++ src/kiri/mode/cam/prepare.js | 5 ++++- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/kiri/mode/cam/cl-flip.js b/src/kiri/mode/cam/cl-flip.js index 427ae9db..166d8b4a 100644 --- a/src/kiri/mode/cam/cl-flip.js +++ b/src/kiri/mode/cam/cl-flip.js @@ -1,7 +1,7 @@ /** Copyright Stewart Allen -- All Rights Reserved */ import { api } from '../../core/api.js'; -import { env, opRender } from './client.js'; +import { env, opAdd, opRender } from './client.js'; import { CAM } from './driver-fe.js'; import { clearTabs, restoreTabs } from './cl-tab.js'; import { updateStock } from './cl-stock.js'; diff --git a/src/kiri/mode/cam/client.js b/src/kiri/mode/cam/client.js index 59fa9e10..bd509dbf 100644 --- a/src/kiri/mode/cam/client.js +++ b/src/kiri/mode/cam/client.js @@ -226,7 +226,7 @@ const traceClear = () => { api.conf.save(); }; -const opAdd = (rec) => { +export const opAdd = (rec) => { if (!env.isCamMode) return; clearPops(); let oplist = env.current.process.ops; diff --git a/src/kiri/mode/cam/op-outline.js b/src/kiri/mode/cam/op-outline.js index 67558e4b..c5fa2b7e 100644 --- a/src/kiri/mode/cam/op-outline.js +++ b/src/kiri/mode/cam/op-outline.js @@ -269,9 +269,11 @@ class OpOutline extends CamOp { let polyLast; // console.log({order, orderPolys}); printPoint = poly2polyEmit(orderPolys, printPoint, function(poly, index, count) { - polyLast = polyEmit(poly, index, count, polyLast); - }, { swapdir: false }); + }, { + swapdir: false, + weight: process.camInnerFirst + }); }) newLayer(); } diff --git a/src/kiri/mode/cam/op-rough.js b/src/kiri/mode/cam/op-rough.js index ad034435..ddc8140c 100644 --- a/src/kiri/mode/cam/op-rough.js +++ b/src/kiri/mode/cam/op-rough.js @@ -310,6 +310,8 @@ class OpRough extends CamOp { POLY.setWinding(level, cutdir, false); poly2polyEmit(level, printPoint, (poly, index, count) => { printPoint = polyEmit(poly, index, count, printPoint, {cutFromLast: true}); + }, { + weight: process.camInnerFirst }); newLayer(); } diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index a51d1a49..7ad44092 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -1072,7 +1072,10 @@ export function prepEach(widget, settings, print, firstPoint, update) { fromPoint = depthOutlinePath(fromPoint, depth + 1, levels, radius, emitter, dir, ease); fromPoint = depthOutlinePath(fromPoint, depth + 1, levels, radius, emitter, !dir, ease); return fromPoint; - }, { weight: false, swapdir: false }); + }, { + weight: innerFirst, + swapdir: false + }); return start; } From d5b50f5d2569023a6b10745bfb9fa5bc4e774c3d Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Wed, 3 Sep 2025 16:50:35 -0400 Subject: [PATCH 17/47] fix detected rapid plunge --- src/kiri/core/print.js | 2 +- src/kiri/mode/cam/export.js | 4 ++-- src/kiri/mode/cam/prepare.js | 18 +++++++++++++----- src/mesh/api.js | 13 +++++++------ 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/kiri/core/print.js b/src/kiri/core/print.js index 6be1665d..848229f3 100644 --- a/src/kiri/core/print.js +++ b/src/kiri/core/print.js @@ -88,7 +88,7 @@ class Print { } output.appendAll(input); } - + /** * Prints a polygon to a given output array, possibly with a given extrude factor, * and starting from a given point. The last point is returned. diff --git a/src/kiri/mode/cam/export.js b/src/kiri/mode/cam/export.js index 04a8bdb3..a8354161 100644 --- a/src/kiri/mode/cam/export.js +++ b/src/kiri/mode/cam/export.js @@ -234,7 +234,7 @@ export function cam_export(print, online) { // speed: Infinity, tool: out.tool, point: { x: newpos.x, y: newpos.y, z: newpos.z, a: pos.a }, - emit: 1, + emit: out.emit, }, { dx: 1, dy: 1, dz: 0, time: 0 }); @@ -251,7 +251,7 @@ export function cam_export(print, online) { // speed: Infinity, tool: out.tool, point: { x: newpos.x, y: newpos.y, z: newpos.z, a: newpos.a }, - emit: 1 + emit: out.emit }, { dx: 0, dy: 0, dz: 1, time: 0 }); diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index 7ad44092..7faa3e21 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -393,8 +393,19 @@ export function prepEach(widget, settings, print, firstPoint, update) { let deltaXY = lastPoint.distTo2D(point), deltaZ = point.z - lastPoint.z, absDeltaZ = Math.abs(deltaZ), + hasDelta = deltaXY >= 0.001 && absDeltaZ >= 0, isMove = emit == 0; + // when rapid pluge could cut thru stock, rapid to just above stock + // then continue plunge as a plunge cut + if (deltaZ < 0 && lastPoint.z > stockz && point.z < stockz && emit === 0) { + // console.log('detected plunge cut as rapid move', lastPoint.z, point.z); + let above = point.clone().setZ(stockz + 0.1); + layerPush(above, 0, 0, tool); + // change to cutting move for remainder of plunge + emit = 1; + } + // drop points too close together if (!isLathe && !isArc && deltaXY < 0.001 && point.z === lastPoint.z) { // console.trace(["drop dup",lastPoint,point]); @@ -402,7 +413,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { } // convert short planar moves to cuts in some cases - if (!isRough && !isArc && isMove && deltaXY <= moveLen && deltaZ <= 0 && !lasering) { + if (hasDelta && !isRough && !isArc && isMove && deltaXY <= moveLen && deltaZ <= 0 && !lasering) { let iscontour = tolerance > 0; let isflat = absDeltaZ < 0.001; // restrict this to contouring @@ -718,8 +729,6 @@ export function prepEach(widget, settings, print, firstPoint, update) { function emitTrace(slice) { let { tool, rate, plunge } = slice.camTrace; setTool(tool, rate, plunge); - let traceTool = new Tool(settings, tool); - let traceToolDiam = traceTool.fluteDiameter(); printPoint = poly2polyEmit(slice.camLines, printPoint, polyEmit, { swapdir: false, weight: innerFirst @@ -743,7 +752,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { * @param {boolean} ops.cutFromLast - whether to emit a 1 when moving from last point. Defaults to false * @returns {Point} - the last point of the polygon */ - function polyEmit(poly, index, count, fromPoint,ops) { + function polyEmit(poly, index, count, fromPoint, ops) { let { cutFromLast, } = ops ?? { @@ -855,7 +864,6 @@ export function prepEach(widget, settings, print, firstPoint, update) { radFault = Math.abs(angle) > arcRes if (radFault) { // if so, remove first point - console.log("secondary radfault,",structuredClone(arcQ),{angle,arcRes,a,b}) camOut(arcQ.shift(), 1) } diff --git a/src/mesh/api.js b/src/mesh/api.js index 75bc7381..baf7fe78 100644 --- a/src/mesh/api.js +++ b/src/mesh/api.js @@ -1392,14 +1392,15 @@ const prefs = { scale_group_Z: true }, space: { + bounds: false, + center: false, + dark: false, + floor: false, + grid: true, + select: [], snap: 1, snapon: false, - center: false, - floor: false, - wire: false, - grid: true, - dark: false, - select: [] + wire: false }, sketch: { open_close: false, From 0c50236dc17bc479aad4a27e88cdeb0d8a514f30 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Wed, 3 Sep 2025 20:08:45 -0400 Subject: [PATCH 18/47] fix drill NaN emit, up/over re-plunge speeds --- src/kiri/mode/cam/export.js | 3 +++ src/kiri/mode/cam/prepare.js | 14 +++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/kiri/mode/cam/export.js b/src/kiri/mode/cam/export.js index a8354161..a0dfc09b 100644 --- a/src/kiri/mode/cam/export.js +++ b/src/kiri/mode/cam/export.js @@ -274,6 +274,9 @@ export function cam_export(print, online) { dist = Math.sqrt(dx * dx + dy * dy + dz * dz), newFeed = feed && feed !== pos.f; + // catch NaN in debugging + // if (nl.length === 0 || nl[0] === undefined) + // console.log(out, nl.length, ...nl); // drop dup points (all deltas are 0) if (!arc && !(dx || dy || dz || da)) { diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index 7faa3e21..128e4b06 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -244,7 +244,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { break; } } - camOut(point.clone().setZ(zmax_outer)); + camOut(point.clone().setZ(zmax_outer), 0); points.forEach(function (point, index) { camOut(point, 1); if (index > 0 && index < points.length - 1) { @@ -332,7 +332,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { * @param {number} opts.moveLen typically = tool diameter used to trigger terrain detection * @param {number} opts.factor speed scale factor */ - function camOut(point, emit, opts) { + function camOut(point, emit = 1, opts) { // console.trace({point, emit, opts}) let { center = {}, @@ -399,9 +399,8 @@ export function prepEach(widget, settings, print, firstPoint, update) { // when rapid pluge could cut thru stock, rapid to just above stock // then continue plunge as a plunge cut if (deltaZ < 0 && lastPoint.z > stockz && point.z < stockz && emit === 0) { - // console.log('detected plunge cut as rapid move', lastPoint.z, point.z); - let above = point.clone().setZ(stockz + 0.1); - layerPush(above, 0, 0, tool); + // console.log('detected plunge cut as rapid move', lastPoint.z, stockz, point.z); + layerPush(point.clone().setZ(stockz + 1), 0, 0, tool); // change to cutting move for remainder of plunge emit = 1; } @@ -467,6 +466,11 @@ export function prepEach(widget, settings, print, firstPoint, update) { layerPush(point.clone().setZ(zClearance), 0, 0, tool); // new pos for plunge calc deltaXY = 0; + // if plunge goes below stock, convert to cut + if (emit === 0 && point.z < stockz) { + lastPoint = layerPush(point.clone().setZ(stockz + 1), 0, 0, tool); + emit = 1; + } } } else if (isRough && deltaZ < 0) { layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool); From 2c1e7220bec3935b12dd9ebaa583c378bd0f3a43 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Wed, 3 Sep 2025 23:08:39 -0400 Subject: [PATCH 19/47] only compute line endpoints once --- src/kiri/mode/cam/prepare.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index 128e4b06..fac80971 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -1123,10 +1123,10 @@ function getZClearPath(terrain, x1, y1, x2, y2, z, zadd, off, over) { } } check.reverse(); + let p1 = newPoint(x1, y1); + let p2 = newPoint(x2, y2); for (let i = 0; i < check.length; i++) { let data = check[i]; - let p1 = newPoint(x1, y1); - let p2 = newPoint(x2, y2); let int = data.tops.map(p => p.intersections(p1, p2, true)).flat(); if (int.length) { maxz = Math.max(maxz, data.z + zadd + over); From 852c275f54bdb5731fd640f51a9203ded1a8815b Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Wed, 3 Sep 2025 23:23:08 -0400 Subject: [PATCH 20/47] fix trace outside cut direction --- src/kiri/mode/cam/op-trace.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kiri/mode/cam/op-trace.js b/src/kiri/mode/cam/op-trace.js index f8a937bf..7b759316 100644 --- a/src/kiri/mode/cam/op-trace.js +++ b/src/kiri/mode/cam/op-trace.js @@ -89,7 +89,7 @@ class OpTrace extends CamOp { if (reContour) { state.contourPolys(widget, slice.camLines); } - POLY.setWinding(slice.camLines, cutdir, false); + POLY.setWinding(slice.camLines, offset === "outside" ? !cutdir : cutdir, false); slice.output() .setLayer("trace follow", {line: color}, false) .addPolys(slice.camLines) @@ -145,7 +145,7 @@ class OpTrace extends CamOp { } else { slice.camLines = POLY.flatten(slice.camLines, null, true); } - POLY.setWinding(slice.camLines, cutdir, false); + POLY.setWinding(slice.camLines, offset === "outside" ? !cutdir : cutdir, false); if (debug && shadow) slice.output() .setLayer("trace shadow", {line: 0xff8811}, false) .addPolys(shadow) From 47ea617185f3a5e28a62a5d14f6d6ace6c6b2d84 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Wed, 3 Sep 2025 23:42:55 -0400 Subject: [PATCH 21/47] fix cut dir for outline --- src/kiri/mode/cam/op-outline.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/kiri/mode/cam/op-outline.js b/src/kiri/mode/cam/op-outline.js index c5fa2b7e..270f4c64 100644 --- a/src/kiri/mode/cam/op-outline.js +++ b/src/kiri/mode/cam/op-outline.js @@ -225,7 +225,7 @@ class OpOutline extends CamOp { let easeDown = process.camEaseDown; let toolDiam = this.toolDiam; - let cutdir = op.ov_conv;//process.camConventional; + let cutdir = op.ov_conv; let depthFirst = process.camDepthFirst; let depthData = []; @@ -251,9 +251,9 @@ class OpOutline extends CamOp { }); // set cut direction on outer polys - POLY.setWinding(t, cutdir); + POLY.setWinding(t, !cutdir); // set cut direction on inner polys - POLY.setWinding(c, !cutdir); + POLY.setWinding(c, cutdir); if (depthFirst) { depthData.push(polys); From 06459f18b8f0e48d13b1e8be472c9adef0c42665 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 4 Sep 2025 20:56:35 -0400 Subject: [PATCH 22/47] compensate for cam origin offset on export / import --- src/kiri/mode/cam/export.js | 9 ++++----- src/kiri/run/worker.js | 7 ++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/kiri/mode/cam/export.js b/src/kiri/mode/cam/export.js index a0dfc09b..84afee08 100644 --- a/src/kiri/mode/cam/export.js +++ b/src/kiri/mode/cam/export.js @@ -53,8 +53,6 @@ export function cam_export(print, online) { lasering = false, laserOp, stock = settings.stock || {}, - ztOff = stock.z - widget.track.top, - bounds = widget.getBoundingBox(), zmax = stock.z, runbox = { max: { x: -Infinity, y: -Infinity, z: -Infinity }, @@ -411,7 +409,7 @@ export function cam_export(print, online) { // collect tool info to add to header let toolz = {}, ctool; - const isOffset = offset && (offset.x || offset.y); + const isOffset = offset && (offset.x || offset.y || offset.z); // remap points as necessary for origins, offsets, inversions print.output.forEach(layer => { if (!Array.isArray(layer)) { @@ -430,8 +428,9 @@ export function cam_export(print, online) { // ensure not point is modified twice point.mod = 1; if (isOffset) { - point.x += offset.x; - point.y += offset.y; + point.x += offset.x ?? 0; + point.y += offset.y ?? 0; + point.z -= offset.z ?? 0; } if (scale) { point.x *= scale.x; diff --git a/src/kiri/run/worker.js b/src/kiri/run/worker.js index 97f22e17..cb6b659b 100644 --- a/src/kiri/run/worker.js +++ b/src/kiri/run/worker.js @@ -546,11 +546,12 @@ const dispatch = { parse(args, send) { const { settings, code, type } = args; + const { process } = settings; const origin = settings.origin; const offset = { - x: origin.x, - y: -origin.y, - z: origin.z + x: origin.x - (process.camOriginOffX ?? 0), + y: -origin.y - (process.camOriginOffY ?? 0), + z: origin.z - (process.camOriginOffZ ?? 0) }; const device = settings.device; const print = current.print = newPrint(settings, Object.values(wcache)); From a4ff70e0541fbf75998f1537ce61d5955804805d Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Fri, 5 Sep 2025 15:03:03 -0400 Subject: [PATCH 23/47] relabel roughing 'over' from 'facing' to 'roughing' --- src/kiri/mode/cam/op-rough.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kiri/mode/cam/op-rough.js b/src/kiri/mode/cam/op-rough.js index ddc8140c..341f56d8 100644 --- a/src/kiri/mode/cam/op-rough.js +++ b/src/kiri/mode/cam/op-rough.js @@ -64,7 +64,7 @@ class OpRough extends CamOp { } slice.camLines = polys; slice.output() - .setLayer("face", {face: color, line: color}) + .setLayer("roughing", {face: color, line: color}) .addPolys(slice.camLines); addSlices(slice); camFaces.push(slice); From 46724db7bf783b7f866c4789f7fdd265c75ef4c7 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sat, 6 Sep 2025 22:53:11 -0400 Subject: [PATCH 24/47] fix trace recontour by making it a negative down option --- src/kiri/mode/cam/op-trace.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/kiri/mode/cam/op-trace.js b/src/kiri/mode/cam/op-trace.js index 7b759316..52481169 100644 --- a/src/kiri/mode/cam/op-trace.js +++ b/src/kiri/mode/cam/op-trace.js @@ -38,7 +38,7 @@ class OpTrace extends CamOp { let cutdir = ov_conv; let polys = []; let reContour = false; - let canRecontour = offset !== 'none' && down === 0; + let canRecontour = offset !== 'none' && down < 0; let stockRect = stock.center && stock.x && stock.y ? newPolygon().centerRectangle({x:0,y:0}, stock.x, stock.y) : undefined; updateToolDiams(toolDiam); @@ -57,6 +57,7 @@ class OpTrace extends CamOp { let max = Math.max(...zs); if (max - min > 0.0001 && canRecontour) { reContour = true; + down = 0; } } if (false) newSliceOut(0).output() From 183c2bc79103a76c3b5960e28c1977b955863aac Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 8 Sep 2025 17:23:40 -0400 Subject: [PATCH 25/47] fix missing sliceAll from fdm driver --- src/kiri/mode/fdm/driver-be.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/kiri/mode/fdm/driver-be.js b/src/kiri/mode/fdm/driver-be.js index 397670e2..f1164acc 100644 --- a/src/kiri/mode/fdm/driver-be.js +++ b/src/kiri/mode/fdm/driver-be.js @@ -1,7 +1,7 @@ /** Copyright Stewart Allen -- All Rights Reserved */ import { util } from '../../../geo/base.js'; -import { fdm_slice } from './slice.js'; +import { fdm_slice, sliceAll } from './slice.js'; import { fdm_prepare } from './prepare.js'; import { fdm_export } from './export.js'; @@ -39,6 +39,7 @@ export const FDM = { extrudePerMM, extrudeMM, slice: fdm_slice, + sliceAll: sliceAll, prepare: fdm_prepare, export: fdm_export }; From b1c73b5e4645e2cef90b491d786725909351a325 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 8 Sep 2025 17:40:33 -0400 Subject: [PATCH 26/47] fix import of taper tool dependencies --- src/kiri/mode/cam/tools.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/kiri/mode/cam/tools.js b/src/kiri/mode/cam/tools.js index 9552fccb..8c6b43d2 100644 --- a/src/kiri/mode/cam/tools.js +++ b/src/kiri/mode/cam/tools.js @@ -4,6 +4,7 @@ import { $ } from '../../../moto/webui.js'; import { api } from '../../core/api.js'; import { consts } from '../../core/consts.js'; import { settings as setconf } from '../../core/settings.js'; +import { calcTaperLength, calcTaperAngle } from './tool.js'; const DEG2RAD = Math.PI / 180; @@ -44,7 +45,7 @@ function selectTool(tool) { ui.toolMetric.checked = tool.metric; ui.toolType.selectedIndex = toolNames.indexOf(tool.type); if (tool === 'tapermill') { - ui.toolTaperAngle.value = kiri.driver.CAM.calcTaperAngle( + ui.toolTaperAngle.value = calcTaperAngle( (tool.flute_diam - tool.taper_tip) / 2, tool.flute_len ).round(1); } else if(tool === 'drill'){ @@ -208,16 +209,15 @@ export function updateTool(ev) { selectedTool.metric = ui.toolMetric.checked; selectedTool.type = toolNames[ui.toolType.selectedIndex]; if (selectedTool.type === 'tapermill') { - const CAM = kiri.driver.CAM; const rad = (selectedTool.flute_diam - selectedTool.taper_tip) / 2; if (ev && ev.target === ui.toolTaperAngle) { const angle = parseFloat(ev.target.value); - const len = CAM.calcTaperLength(rad, angle * DEG2RAD); + const len = calcTaperLength(rad, angle * DEG2RAD); selectedTool.flute_len = len; ui.toolTaperAngle.value = angle.round(1); ui.toolFluteLen.value = selectedTool.flute_len.round(4); } else { - ui.toolTaperAngle.value = CAM.calcTaperAngle(rad, selectedTool.flute_len).round(1); + ui.toolTaperAngle.value = calcTaperAngle(rad, selectedTool.flute_len).round(1); } } else { ui.toolTaperAngle.value = 0; From d0bcaa28da34fe195cf4cb36f1979d790309f31f Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 8 Sep 2025 18:33:48 -0400 Subject: [PATCH 27/47] fix gcode import: remove unused mov obj --- src/kiri/core/print.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/kiri/core/print.js b/src/kiri/core/print.js index 848229f3..d0487b52 100644 --- a/src/kiri/core/print.js +++ b/src/kiri/core/print.js @@ -362,14 +362,11 @@ class Print { // if( axis == 'I' || axis == "J") return if (abs) { pos[axis] = val; - if (axis == "X") point.x = factor * pos.X + xoff.x + off.x else if (axis == "Y") point.y = factor * pos.Y + xoff.y + off.y else if (axis == "Z") point.z = factor * pos.Z + xoff.z + off.z + dz - - } else { - mov[axis] = val; + // mov[axis] = val; pos[axis] += val; } // console.log("position updated",structuredClone(pos)) @@ -393,7 +390,6 @@ class Print { center = center.add(prevPoint); center.setZ((prevPoint.z+point.z)/2+dz); } - return { center, point, @@ -402,10 +398,8 @@ class Print { } function outputPoint(point,lastP,emit,{center,arcPoints,retract}) { - // non-move in a new plane means burp out // the old sequence and start a new one - if (newlayer || (autolayer && seq.z != point.z)) { newlayer = false; let dz = point.z - seq.z; From 96beba67e8f276cbbfc26f5634260764a8902451 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Tue, 9 Sep 2025 00:22:24 -0400 Subject: [PATCH 28/47] add rad points to shell inset arrays --- src/kiri/mode/fdm/post.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index bbc088fc..a2c95565 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -167,6 +167,7 @@ function thin_type_3(params) { top.thin_fill = []; top.fill_sparse = []; + // produce trace from outside of poly inward no more than max inset let { noodle, remain } = top.poly.noodle(offsetN * count); top.shells = noodle; top.gaps = last = remain; @@ -180,6 +181,7 @@ function thin_type_3(params) { let pointMap = new Map(); let lineMap = new Map(); + // convert point to deduplicated / unique point record function pointToRec(point) { let key = `${(point.x*100)|0}|${(point.y*100)|0}`; let rec = pointMap.get(key); @@ -196,6 +198,7 @@ function thin_type_3(params) { return rec; } + // convert line to unique line record. matches reversed and duplicate lines function pointsToLine(p0, p1) { p0 = pointToRec(p0); p1 = pointToRec(p1); @@ -294,6 +297,7 @@ function thin_type_3(params) { let showCross = false; let showRad = true; + // divide a segment/redius into 1 or more equal subsegments function div(cr) { if (cr <= maxR) { return [ cr ]; @@ -315,6 +319,8 @@ function thin_type_3(params) { if (showRad) { top.shells = []; } + // ranked points by inset distance + let inset = []; for (let chain of chains.filter(c => c.total >= minR)) { // chain reduction by merging short segments let end = chain[0]; @@ -364,18 +370,26 @@ function thin_type_3(params) { let sx = cx + yo; let sy = cy - xo; let m = 1; + let indx = 0; for (let r of pop) { + indx = Math.min(indx, pop.length - 1 - indx); + let inset_arr = inset[indx]; + if (!inset_arr) { + inset_arr = inset[indx] = []; + } sx -= (r/cr) * yo * m; sy += (r/cr) * xo * m; circ.push(newPolygon().centerCircle({ x:sx, y:sy }, r, 10)); m = 2; + inset_arr.push({ x:sx, y:sy, r }); + indx++; } - // console.log(pop); } } } } + // console.log({ inset }); return { last, gaps }; } From 57ed55ca77dbb8270a3e0f392a10d8aca4f9128c Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Tue, 9 Sep 2025 16:31:48 -0400 Subject: [PATCH 29/47] fixes #423 --- src/kiri/run/engine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kiri/run/engine.js b/src/kiri/run/engine.js index 00824cbe..5b9d7008 100644 --- a/src/kiri/run/engine.js +++ b/src/kiri/run/engine.js @@ -156,7 +156,7 @@ class Engine { } slice() { - this.widget.setTopZ(this.settings.stock.z - (this.topOffset || 0)); + this.widget.setTopZ((this.settings?.stock?.z || 0) - (this.topOffset || 0)); return new Promise((accept, reject) => { client.clear(); client.sync([this.widget]); From eb63ebb1d08a6b6032f08683f0d4c1122353904f Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Tue, 9 Sep 2025 22:55:30 -0400 Subject: [PATCH 30/47] form chains from radii points --- src/kiri/mode/fdm/post.js | 86 ++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 20 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index a2c95565..66c015a7 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -2,7 +2,7 @@ import '../../../ext/jspoly.js'; import { base } from '../../../geo/base.js'; -import { newPolygon } from '../../../geo/polygon.js'; +import { newPolygon, Polygon } from '../../../geo/polygon.js'; import { polygons as POLY, fillArea } from '../../../geo/polygons.js'; import { getRangeParameters } from './driver.js'; import { slicer } from '../../../geo/slicer.js'; @@ -169,7 +169,7 @@ function thin_type_3(params) { // produce trace from outside of poly inward no more than max inset let { noodle, remain } = top.poly.noodle(offsetN * count); - top.shells = noodle; + // top.shells = noodle; top.gaps = last = remain; let thin = top.thin_fill; @@ -293,9 +293,16 @@ function thin_type_3(params) { } } - let showMid = false; - let showCross = false; - let showRad = true; + let showNoodle = false; + let showMid = false; + let showCross = false; + let showRad = true; + let showShells = true; + + // render inset "noodle" + if (showNoodle) { + top.shells.appendAll(noodle); + } // divide a segment/redius into 1 or more equal subsegments function div(cr) { @@ -315,12 +322,8 @@ function thin_type_3(params) { } } - // add chains to thinwall output for visualization - if (showRad) { - top.shells = []; - } - // ranked points by inset distance - let inset = []; + // gather point offsets into shells + let shells = { count: 0 }; for (let chain of chains.filter(c => c.total >= minR)) { // chain reduction by merging short segments let end = chain[0]; @@ -364,32 +367,75 @@ function thin_type_3(params) { thin.push(new Point(cx + yo, cy - xo)); thin.push(new Point(cx - yo, cy + xo)); } - if (showRad) { + if (showRad || showShells) { let circ = top.shells; let pop = div(cr); let sx = cx + yo; let sy = cy - xo; let m = 1; - let indx = 0; + let index = 0; + let plen = pop.length; + let mid = plen / 2; for (let r of pop) { - indx = Math.min(indx, pop.length - 1 - indx); - let inset_arr = inset[indx]; + let shell = index < mid ? index + 1 : -(pop.length - index); + let inset_arr = shells[shell]; if (!inset_arr) { - inset_arr = inset[indx] = []; + inset_arr = shells[shell] = []; } sx -= (r/cr) * yo * m; sy += (r/cr) * xo * m; - circ.push(newPolygon().centerCircle({ x:sx, y:sy }, r, 10)); m = 2; - inset_arr.push({ x:sx, y:sy, r }); - indx++; + if (showRad) { + circ.push(newPolygon().centerCircle({ x:sx, y:sy }, r, 10)); + } + inset_arr.push(new Point(sx, sy, r)); + index++; + shells.count = Math.max(shells.count, Math.abs(shell)); } } } } } - // console.log({ inset }); + // follow shells and output lines + console.log({ shells }); + if (showShells) + for (let shell = 1; shell <= 1; shell++) { + let poly = new Polygon().setOpen(); + let arr = shells[shell]; + let lp = arr[0]; + poly.push(lp); + let find; + for (;;) { + find = { dist: Infinity }; + // find next closest point + for (let i=1; i 1) { + top.shells.push(poly); + poly = new Polygon().setOpen(); + } + arr[find.pi] = undefined; + poly.push(lp = find.pt); + } else { + break; + } + } + console.log({ poly }); + if (poly.length > 1) { + top.shells.push(poly); + } + } + return { last, gaps }; } From a5c3c3019081e361a6287f609b48c27b059a08e1 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Wed, 10 Sep 2025 14:22:09 -0400 Subject: [PATCH 31/47] append base64js for production build --- bin/esbuild.config.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/esbuild.config.mjs b/bin/esbuild.config.mjs index 4881ab00..0cb973bb 100644 --- a/bin/esbuild.config.mjs +++ b/bin/esbuild.config.mjs @@ -12,7 +12,9 @@ const MESH_OUTFILE = 'src/pack/mesh-main.js'; const MESH_EXTRAS = [ ]; const KIRI_OUTFILE = 'src/pack/kiri-main.js'; -const KIRI_EXTRAS = [ ]; +const KIRI_EXTRAS = [ + 'src/ext/base64.js' +]; async function appendExtraModules(extras, outfile, minify = false) { const files = await glob(extras); From 23eb8d275870301ab4926592cb8fe55c9294477d Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Wed, 10 Sep 2025 20:50:48 -0400 Subject: [PATCH 32/47] updated notes w fdm todo --- notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/notes.md b/notes.md index a7036e77..5efbac1a 100644 --- a/notes.md +++ b/notes.md @@ -31,6 +31,10 @@ * `B` multi-extruder rendering of raft fails to offset the rest of the print * `B` multi-extruder purge blocks fail to generate properly for rafts +* `F` min layer time +* `F` separate bridging speed / fan control +* `F` elephant foot compensation +* `F` inner / outer perimeter compensation * `F` better thin-wall (like arachne) * `F` add scarf seams * `F` add 'random' and 'seam' start point options From 3991daadd2dca8119c0b4c09d137433286799114 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 11 Sep 2025 09:47:10 -0400 Subject: [PATCH 33/47] thin3 notes --- src/kiri/mode/fdm/post.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index 66c015a7..f818aef0 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -293,11 +293,15 @@ function thin_type_3(params) { } } + // todo: merge chains at next of 3 or more preferencing + // merging longer chains. then shorten remaining nexus chain + // intersections by minR + let showNoodle = false; - let showMid = false; + let showMid = true; let showCross = false; let showRad = true; - let showShells = true; + let showShells = false; // render inset "noodle" if (showNoodle) { @@ -323,7 +327,9 @@ function thin_type_3(params) { } // gather point offsets into shells + // todo: order outer shell innersection to inner let shells = { count: 0 }; + // filter chains that are too short from consideration for (let chain of chains.filter(c => c.total >= minR)) { // chain reduction by merging short segments let end = chain[0]; @@ -373,11 +379,10 @@ function thin_type_3(params) { let sx = cx + yo; let sy = cy - xo; let m = 1; - let index = 0; + let index = 1; let plen = pop.length; - let mid = plen / 2; for (let r of pop) { - let shell = index < mid ? index + 1 : -(pop.length - index); + let shell = Math.min(index, plen - index + 1); let inset_arr = shells[shell]; if (!inset_arr) { inset_arr = shells[shell] = []; @@ -389,8 +394,8 @@ function thin_type_3(params) { circ.push(newPolygon().centerCircle({ x:sx, y:sy }, r, 10)); } inset_arr.push(new Point(sx, sy, r)); + shells.count = Math.max(shells.count, shell); index++; - shells.count = Math.max(shells.count, Math.abs(shell)); } } } From 76d028bd151cd508e01c660f5770ec783bdcbae2 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 14 Sep 2025 12:48:20 -0400 Subject: [PATCH 34/47] separate chain filter logic --- src/kiri/mode/fdm/post.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index f818aef0..8abceaf6 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -326,11 +326,8 @@ function thin_type_3(params) { } } - // gather point offsets into shells - // todo: order outer shell innersection to inner - let shells = { count: 0 }; // filter chains that are too short from consideration - for (let chain of chains.filter(c => c.total >= minR)) { + chains = chains.filter(c => c.total >= minR).map(chain => { // chain reduction by merging short segments let end = chain[0]; let nuchain = [ end ]; @@ -342,7 +339,17 @@ function thin_type_3(params) { end.len += point.len; } } - chain = nuchain; + return nuchain; + }); + + // connect chains end to end following nexus branches that result + // in the longest final merged chain (open or closed) + console.log(chains); + + // gather point offsets into shells + // todo: order outer shell innersection to inner + let shells = { count: 0 }; + for (let chain of chains) { // emit chain with subdivision for long segments for (let i=0; i Date: Sun, 14 Sep 2025 14:29:23 -0400 Subject: [PATCH 35/47] checkpoint thin3 work --- src/geo/polygon.js | 3 +- src/kiri/mode/fdm/post.js | 245 +++++++++++++++++++++++--------------- 2 files changed, 152 insertions(+), 96 deletions(-) diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 38957f46..e0226946 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -259,8 +259,9 @@ export class Polygon { noodle(width) { let clone = this.clone(true); let ins = clone.offset(width); + let remain = ins.clone(true); let nood = POLY.nest(POLY.flatten([ clone, ...ins ], [], true)); - return { noodle: nood, remain: ins }; + return { noodle: nood, remain }; } // generate center crossing point cloud diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index 8abceaf6..00b85f98 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -173,17 +173,23 @@ function thin_type_3(params) { top.gaps = last = remain; let thin = top.thin_fill; - let scale = 5000; + let scale = 1000; let minR = offsetN / 2; let maxR = minR * 1.5; let minSpur = 0; // offsetN * 3; + let mergeClosePoints = true; + let interpolateShortSpur = true; let pointMap = new Map(); let lineMap = new Map(); + function point2key(point) { + return `${(point.x*100)|0}|${(point.y*100)|0}`; + } + // convert point to deduplicated / unique point record function pointToRec(point) { - let key = `${(point.x*100)|0}|${(point.y*100)|0}`; + let key = point2key(point); let rec = pointMap.get(key); if (!rec) { rec = { @@ -222,6 +228,9 @@ function thin_type_3(params) { let chains = []; + // for each standalone noodle, construct medial axis + // map medial axis points into de-duplicated segments + // which will be re-connected into chains for (let poly of noodle) { // scale the polygon (including inners) so the medial axis code works properly poly = poly.clone(true).scale({ x:scale, y:scale, z:scale }); @@ -234,63 +243,75 @@ function thin_type_3(params) { for (let { point0, point1 } of ma) { pointsToLine(point0, point1); } - for (let seg of lineMap.values()) { - let { p0, p1 } = seg; - let len = seg.len = Math.hypot(p0.x-p1.x, p0.y-p1.y); - // filter out lines where both points' radii are under the - // minR threshold. shorten and fixup lines where minR falls - // somewhere along the gradient between ends. - if (p0.r < minR || p1.r < minR) { - if (p0.r > minR && len > minSpur) { - // move p1 toward p0 until minR met - let pct = (minR - p1.r) / (p0.r - p1.r); - let dx = p1.x - p0.x; - let dy = p1.y - p0.y; - p1.x -= dx * pct; - p1.y -= dy * pct; - p1.r = minR; - pointMap.delete(p1.key); - } else if (p1.r > minR && len > minSpur) { - // move p0 toward p1 until minR met - let pct = (minR - p0.r) / (p1.r - p0.r); - let dx = p0.x - p1.x; - let dy = p0.y - p1.y; - p0.x -= dx * pct; - p0.y -= dy * pct; - p0.r = minR; - pointMap.delete(p0.key); - } else { - lineMap.delete(seg.key); - continue; - } + } + + // connect segments into chains + // console.log({ seg: [...lineMap.values()].sort((s1,s2) => s2.len - s1.len) }); + for (let seg of lineMap.values()) { + let { p0, p1 } = seg; + let len = seg.len = Math.hypot(p0.x-p1.x, p0.y-p1.y); + // filter out lines where both points' radii are under the + // minR threshold. shorten and fixup lines where minR falls + // somewhere along the gradient between ends. + if (interpolateShortSpur) + if (p0.r < minR || p1.r < minR) { + if (p0.r > minR && len > minSpur) { + // move p1 toward p0 until minR met + let pct = (minR - p1.r) / (p0.r - p1.r); + let dx = p1.x - p0.x; + let dy = p1.y - p0.y; + p1.x -= dx * pct; + p1.y -= dy * pct; + p1.r = minR; + pointMap.delete(p1.key); + } else if (p1.r > minR && len > minSpur) { + // move p0 toward p1 until minR met + let pct = (minR - p0.r) / (p1.r - p0.r); + let dx = p0.x - p1.x; + let dy = p0.y - p1.y; + p0.x -= dx * pct; + p0.y -= dy * pct; + p0.r = minR; + pointMap.delete(p0.key); + } else { + // both points eliminated + pointMap.delete(p0.key); + pointMap.delete(p1.key); + lineMap.delete(seg.key); + continue; } - // for each line segment either add it to a chain or start a new chain - let add; - for (let chain of chains) { - if (p0.count === 2 && p0.key === chain[0].key) { - chain.splice(0,0,{...p1, len}); - } else if (p0.count === 2 && p0.key === chain.peek().key) { - chain.peek().len = len; - chain.push({...p1}); - add = true; - } else if (p1.count === 2 && p1.key === chain[0].key) { - chain.splice(0,0,{...p0, len}); - add = true; - } else if (p1.count === 2 && p1.key === chain.peek().key) { - chain.peek().len = len; - chain.push({...p0}); - add = true; - } - if (add) { - chain.total += len; - break; - } + } + // for each line segment either add it to a chain or start a new chain + let add; + for (let chain of chains) { + if (p0.count === 2 && p0.key === chain[0].key) { + // prefix chain with p1 + chain.splice(0,0,{...p1, len}); + add = true; + } else if (p0.count === 2 && p0.key === chain.peek().key) { + // append chain with p1 + chain.peek().len = len; + chain.push({...p1}); + add = true; + } else if (p1.count === 2 && p1.key === chain[0].key) { + // prefix chain with p0 + chain.splice(0,0,{...p0, len}); + add = true; + } else if (p1.count === 2 && p1.key === chain.peek().key) { + // append chain with p0 + chain.peek().len = len; + chain.push({...p0}); + add = true; } - if (!add) { - chains.push([ {...p0 ,len}, {...p1} ]); - chains.peek().total = len; + if (add) { + chain.total += len; + break; } } + if (!add) { + chains.push([ {...p0 ,len}, {...p1} ]); + chains.peek().total = len; + } } // todo: merge chains at next of 3 or more preferencing @@ -300,7 +321,7 @@ function thin_type_3(params) { let showNoodle = false; let showMid = true; let showCross = false; - let showRad = true; + let showRad = false; let showShells = false; // render inset "noodle" @@ -327,10 +348,16 @@ function thin_type_3(params) { } // filter chains that are too short from consideration - chains = chains.filter(c => c.total >= minR).map(chain => { + chains = chains.filter(c => c.total >= minR); + chains.sort((c1,c2) => c2.total - c1.total); + + // merge chain points that are too close + if (mergeClosePoints) + chains = chains.map(chain => { // chain reduction by merging short segments let end = chain[0]; let nuchain = [ end ]; + nuchain.total = chain.total; for (let point of chain.slice(1)) { if (point.len === undefined || point.len + end.len >= minR) { nuchain.push(point); @@ -342,9 +369,37 @@ function thin_type_3(params) { return nuchain; }); + // map chains to nexus via endpoints + let nexus = {}; + function addEp(point, chain) { + let key = point.key; + let rec = nexus[key]; + if (!rec) rec = nexus[key] = []; + if (rec.indexOf(chain) < 0) rec.push(chain); + } + for (let chain of chains) { + addEp(chain[0], chain); + addEp(chain.peek(), chain); + } + + // mark spurs + for (let [key,val] of Object.entries(nexus)) { + if (val.length === 1) { + val[0].spur = true; + delete nexus[key]; + } + } + + // detach spurs from nexus and shorten by ? + for (let [key,val] of Object.entries(nexus)) { + nexus[key] = val.filter(c => !c.spur); + } + + console.log({ chains, nexus }); + // connect chains end to end following nexus branches that result // in the longest final merged chain (open or closed) - console.log(chains); + // map chain endpoints to nexus points // gather point offsets into shells // todo: order outer shell innersection to inner @@ -410,43 +465,43 @@ function thin_type_3(params) { } // follow shells and output lines - console.log({ shells }); - if (showShells) - for (let shell = 1; shell <= 1; shell++) { - let poly = new Polygon().setOpen(); - let arr = shells[shell]; - let lp = arr[0]; - poly.push(lp); - let find; - for (;;) { - find = { dist: Infinity }; - // find next closest point - for (let i=1; i 1) { - top.shells.push(poly); - poly = new Polygon().setOpen(); - } - arr[find.pi] = undefined; - poly.push(lp = find.pt); - } else { - break; - } - } - console.log({ poly }); - if (poly.length > 1) { - top.shells.push(poly); - } - } + // console.log({ shells }); + // if (showShells) + // for (let shell = 1; shell <= 1; shell++) { + // let poly = new Polygon().setOpen(); + // let arr = shells[shell]; + // let lp = arr[0]; + // poly.push(lp); + // let find; + // for (;;) { + // find = { dist: Infinity }; + // // find next closest point + // for (let i=1; i 1) { + // top.shells.push(poly); + // poly = new Polygon().setOpen(); + // } + // arr[find.pi] = undefined; + // poly.push(lp = find.pt); + // } else { + // break; + // } + // } + // console.log({ poly }); + // if (poly.length > 1) { + // top.shells.push(poly); + // } + // } return { last, gaps }; } From 5fe02937c8f4e9f23a36798a87b3a415024455d4 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 14 Sep 2025 19:08:13 -0400 Subject: [PATCH 36/47] fix thin3 cross interp --- src/kiri/mode/fdm/post.js | 134 +++++++++++--------------------------- 1 file changed, 37 insertions(+), 97 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index 00b85f98..40c5a15d 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -163,22 +163,29 @@ function thin_type_2(params) { } function thin_type_3(params) { + // offsetN usually = extrusion width (nozzle diameter) let { z, top, count, offsetN, last, gaps } = params; - top.thin_fill = []; - top.fill_sparse = []; // produce trace from outside of poly inward no more than max inset let { noodle, remain } = top.poly.noodle(offsetN * count); + // top.shells = noodle; top.gaps = last = remain; - let thin = top.thin_fill; + let thin = top.thin_fill = []; + let sparse = top.fill_sparse = []; let scale = 1000; let minR = offsetN / 2; let maxR = minR * 1.5; - let minSpur = 0; // offsetN * 3; - let mergeClosePoints = true; + let minSpur = offsetN * 3; + let interpolateShortSpur = true; + let showChainInterpPoints = true; + let showChainRawPoints = false; + let showNoodle = false; + let showMid = true; + let showCross = true; + let showRad = true; let pointMap = new Map(); let lineMap = new Map(); @@ -246,7 +253,6 @@ function thin_type_3(params) { } // connect segments into chains - // console.log({ seg: [...lineMap.values()].sort((s1,s2) => s2.len - s1.len) }); for (let seg of lineMap.values()) { let { p0, p1 } = seg; let len = seg.len = Math.hypot(p0.x-p1.x, p0.y-p1.y); @@ -318,12 +324,6 @@ function thin_type_3(params) { // merging longer chains. then shorten remaining nexus chain // intersections by minR - let showNoodle = false; - let showMid = true; - let showCross = false; - let showRad = false; - let showShells = false; - // render inset "noodle" if (showNoodle) { top.shells.appendAll(noodle); @@ -351,24 +351,6 @@ function thin_type_3(params) { chains = chains.filter(c => c.total >= minR); chains.sort((c1,c2) => c2.total - c1.total); - // merge chain points that are too close - if (mergeClosePoints) - chains = chains.map(chain => { - // chain reduction by merging short segments - let end = chain[0]; - let nuchain = [ end ]; - nuchain.total = chain.total; - for (let point of chain.slice(1)) { - if (point.len === undefined || point.len + end.len >= minR) { - nuchain.push(point); - end = point; - } else { - end.len += point.len; - } - } - return nuchain; - }); - // map chains to nexus via endpoints let nexus = {}; function addEp(point, chain) { @@ -403,8 +385,11 @@ function thin_type_3(params) { // gather point offsets into shells // todo: order outer shell innersection to inner - let shells = { count: 0 }; for (let chain of chains) { + if (showChainRawPoints) + for (let pt of chain) { + top.shells.push(newPolygon().centerCircle(pt, pt.r ?? 0.1, 10)); + } // emit chain with subdivision for long segments for (let i=0; i 1) { - // top.shells.push(poly); - // poly = new Polygon().setOpen(); - // } - // arr[find.pi] = undefined; - // poly.push(lp = find.pt); - // } else { - // break; - // } - // } - // console.log({ poly }); - // if (poly.length > 1) { - // top.shells.push(poly); - // } - // } + POLY.setZ([...thin, ...top.shells], z); return { last, gaps }; } From ad6d1139ed947eba290365e0b6ffd77fea2c1be3 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 14 Sep 2025 23:27:58 -0400 Subject: [PATCH 37/47] add optional chain merge --- src/kiri/mode/fdm/post.js | 71 ++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index 40c5a15d..0bbb3185 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -179,13 +179,14 @@ function thin_type_3(params) { let maxR = minR * 1.5; let minSpur = offsetN * 3; + let mergeChains = false; let interpolateShortSpur = true; let showChainInterpPoints = true; let showChainRawPoints = false; let showNoodle = false; let showMid = true; let showCross = true; - let showRad = true; + let showRad = false; let pointMap = new Map(); let lineMap = new Map(); @@ -356,32 +357,77 @@ function thin_type_3(params) { function addEp(point, chain) { let key = point.key; let rec = nexus[key]; - if (!rec) rec = nexus[key] = []; - if (rec.indexOf(chain) < 0) rec.push(chain); + if (!rec) rec = nexus[key] = { point, chains: [] }; + if (rec.chains.indexOf(chain) < 0) rec.chains.push(chain); } + // since chains are sorted longest to shortest, + // they will appear in the nexus record longest to shortest for (let chain of chains) { addEp(chain[0], chain); addEp(chain.peek(), chain); } // mark spurs - for (let [key,val] of Object.entries(nexus)) { - if (val.length === 1) { - val[0].spur = true; + for (let [key, rec] of Object.entries(nexus)) { + if (rec.chains.length === 1) { + rec.chains[0].spur = true; delete nexus[key]; + // console.log({ delete_nexus: key }); } } // detach spurs from nexus and shorten by ? - for (let [key,val] of Object.entries(nexus)) { - nexus[key] = val.filter(c => !c.spur); - } + // for (let [key,val] of Object.entries(nexus)) { + // nexus[key].chains = val.chains.filter(c => !c.spur); + // } console.log({ chains, nexus }); + function merge1chain() { + let clen = chains.length; + for (let i=0; i chain); + console.log({ chains }); // gather point offsets into shells // todo: order outer shell innersection to inner @@ -400,12 +446,13 @@ function thin_type_3(params) { // compute medial axis segment cross section let p0 = chain[i]; let p1 = chain[i+1]; + let len = Math.hypot(p0.x-p1.x, p0.y-p1.y); let dr = (p1.r - p0.r); let dx = (p1.x - p0.x); let dy = (p1.y - p0.y); - let ndx = dx / p0.len; - let ndy = dy / p0.len; - let offs = p0.len <= offsetN ? [ 0.5 ] : base.util.lerp(0, p0.len, offsetN, true); + let ndx = dx / len; + let ndy = dy / len; + let offs = len <= offsetN ? [ 0.5 ] : base.util.lerp(0, len, offsetN, true); let indx = 0; let step = 1 / offs.length; // interpolate across the length of the chain segment From edb60e66df526216ec5f477acd7c73ab826bdea6 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 14 Sep 2025 23:57:42 -0400 Subject: [PATCH 38/47] use objects instead of keys for comparison --- src/kiri/mode/fdm/post.js | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index 0bbb3185..951ff447 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -179,7 +179,7 @@ function thin_type_3(params) { let maxR = minR * 1.5; let minSpur = offsetN * 3; - let mergeChains = false; + let mergeChains = true; let interpolateShortSpur = true; let showChainInterpPoints = true; let showChainRawPoints = false; @@ -291,23 +291,21 @@ function thin_type_3(params) { // for each line segment either add it to a chain or start a new chain let add; for (let chain of chains) { - if (p0.count === 2 && p0.key === chain[0].key) { + if (p0.count === 2 && p0 === chain[0]) { // prefix chain with p1 - chain.splice(0,0,{...p1, len}); + chain.splice(0,0,p1); add = true; - } else if (p0.count === 2 && p0.key === chain.peek().key) { + } else if (p0.count === 2 && p0 === chain.peek()) { // append chain with p1 - chain.peek().len = len; - chain.push({...p1}); + chain.push(p1); add = true; - } else if (p1.count === 2 && p1.key === chain[0].key) { + } else if (p1.count === 2 && p1 === chain[0]) { // prefix chain with p0 - chain.splice(0,0,{...p0, len}); + chain.splice(0,0,p0); add = true; - } else if (p1.count === 2 && p1.key === chain.peek().key) { + } else if (p1.count === 2 && p1 === chain.peek()) { // append chain with p0 - chain.peek().len = len; - chain.push({...p0}); + chain.push(p0); add = true; } if (add) { @@ -316,7 +314,7 @@ function thin_type_3(params) { } } if (!add) { - chains.push([ {...p0 ,len}, {...p1} ]); + chains.push([ p0, p1 ]); chains.peek().total = len; } } @@ -391,25 +389,25 @@ function thin_type_3(params) { for (let j=i+1; j Date: Mon, 15 Sep 2025 00:38:16 -0400 Subject: [PATCH 39/47] mark closed chains --- src/kiri/mode/fdm/post.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index 951ff447..568b793b 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -191,6 +191,10 @@ function thin_type_3(params) { let pointMap = new Map(); let lineMap = new Map(); + function pointDist(p0, p1) { + return Math.hypot(p0.x-p1.x, p0.y-p1.y); + } + function point2key(point) { return `${(point.x*100)|0}|${(point.y*100)|0}`; } @@ -256,7 +260,7 @@ function thin_type_3(params) { // connect segments into chains for (let seg of lineMap.values()) { let { p0, p1 } = seg; - let len = seg.len = Math.hypot(p0.x-p1.x, p0.y-p1.y); + let len = seg.len = pointDist(p0, p1); // filter out lines where both points' radii are under the // minR threshold. shorten and fixup lines where minR falls // somewhere along the gradient between ends. @@ -427,6 +431,11 @@ function thin_type_3(params) { chains = chains.filter(chain => chain); console.log({ chains }); + // mark closed chains + for (let chain of chains) { + chain.closed = pointDist(chain[0], chain.peek()) <= offsetN; + } + // gather point offsets into shells // todo: order outer shell innersection to inner for (let chain of chains) { @@ -444,7 +453,7 @@ function thin_type_3(params) { // compute medial axis segment cross section let p0 = chain[i]; let p1 = chain[i+1]; - let len = Math.hypot(p0.x-p1.x, p0.y-p1.y); + let len = pointDist(p0, p1); let dr = (p1.r - p0.r); let dx = (p1.x - p0.x); let dy = (p1.y - p0.y); From 32d4b7e62a37782c6543b74711dc8fb629c34cbb Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 15 Sep 2025 17:11:56 -0400 Subject: [PATCH 40/47] fix thin3 chain marking and shortening --- src/geo/polygon.js | 2 +- src/kiri/mode/fdm/post.js | 123 ++++++++++++++++++++++++++------------ 2 files changed, 86 insertions(+), 39 deletions(-) diff --git a/src/geo/polygon.js b/src/geo/polygon.js index e0226946..846515f9 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -258,7 +258,7 @@ export class Polygon { // of the polygon with the noodle removed (for the next pass) noodle(width) { let clone = this.clone(true); - let ins = clone.offset(width); + let ins = clone.offset(width) ?? []; let remain = ins.clone(true); let nood = POLY.nest(POLY.flatten([ clone, ...ins ], [], true)); return { noodle: nood, remain }; diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index 568b793b..b9818762 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -182,11 +182,13 @@ function thin_type_3(params) { let mergeChains = true; let interpolateShortSpur = true; let showChainInterpPoints = true; + let showCross = false; + let showPoint = true; let showChainRawPoints = false; let showNoodle = false; let showMid = true; - let showCross = true; let showRad = false; + let showNexus = false; let pointMap = new Map(); let lineMap = new Map(); @@ -354,37 +356,6 @@ function thin_type_3(params) { chains = chains.filter(c => c.total >= minR); chains.sort((c1,c2) => c2.total - c1.total); - // map chains to nexus via endpoints - let nexus = {}; - function addEp(point, chain) { - let key = point.key; - let rec = nexus[key]; - if (!rec) rec = nexus[key] = { point, chains: [] }; - if (rec.chains.indexOf(chain) < 0) rec.chains.push(chain); - } - // since chains are sorted longest to shortest, - // they will appear in the nexus record longest to shortest - for (let chain of chains) { - addEp(chain[0], chain); - addEp(chain.peek(), chain); - } - - // mark spurs - for (let [key, rec] of Object.entries(nexus)) { - if (rec.chains.length === 1) { - rec.chains[0].spur = true; - delete nexus[key]; - // console.log({ delete_nexus: key }); - } - } - - // detach spurs from nexus and shorten by ? - // for (let [key,val] of Object.entries(nexus)) { - // nexus[key].chains = val.chains.filter(c => !c.spur); - // } - - console.log({ chains, nexus }); - function merge1chain() { let clen = chains.length; for (let i=0; i=1 && rem < minR; i--) { + let c1 = chain[i]; + let c2 = chain[i-1]; + let d = pointDist(c1,c2); + if (rem + d <= minR) { + chain.pop(); + rem += d; + } else { + let diff = minR - rem; + // move c1 toward c2 + let pct = diff / d; + let dx = c1.x - c2.x; + let dy = c1.y - c2.y; + chain[i] = { + x: c1.x - dx * pct, + y: c1.y - dy * pct, + r: c1.r + }; + rem = 0; + break; + } + } + } + + // add chains + + // shorten chains terminating at nexus by midR + for (let rec of Object.values(nexus)) { + let { point } = rec; + rec.shorts = 0; + if (rec.chains.length < 3) continue; + for (let chain of chains.filter(c => !c.closed)) { + if (pointDist(chain[0], point) < minR) { + chain.reverse(); + shorten(chain); + chain.reverse(); + rec.shorts++; + } else if (chain.peek() === point) { + shorten(chain); + rec.shorts++; + } + } + } + + if (showNexus) + for (let rec of Object.values(nexus)) { + let { point, chains } = rec; + // console.log(rec); + top.shells.push(newPolygon().centerCircle(point, 0.2, 6 - rec.shorts)); } // gather point offsets into shells - // todo: order outer shell innersection to inner + let zi = z; for (let chain of chains) { + // zi += 0.1; if (showChainRawPoints) for (let pt of chain) { top.shells.push(newPolygon().centerCircle(pt, pt.r ?? 0.1, 10)); @@ -447,8 +493,8 @@ function thin_type_3(params) { for (let i=0; i Date: Mon, 15 Sep 2025 17:23:29 -0400 Subject: [PATCH 41/47] clarify thin3 test var naming --- src/kiri/mode/fdm/post.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index b9818762..2d286191 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -179,16 +179,16 @@ function thin_type_3(params) { let maxR = minR * 1.5; let minSpur = offsetN * 3; + let showNoodle = false; let mergeChains = true; let interpolateShortSpur = true; - let showChainInterpPoints = true; - let showCross = false; - let showPoint = true; let showChainRawPoints = false; - let showNoodle = false; - let showMid = true; - let showRad = false; - let showNexus = false; + let showChainInterpPoints = true; + let showMidNormals = true; + let showMidPoints = true; + let showMidline = true; + let showMidRadii = false; + let showNexuses = false; let pointMap = new Map(); let lineMap = new Map(); @@ -474,7 +474,7 @@ function thin_type_3(params) { } } - if (showNexus) + if (showNexuses) for (let rec of Object.values(nexus)) { let { point, chains } = rec; // console.log(rec); @@ -492,7 +492,7 @@ function thin_type_3(params) { // emit chain with subdivision for long segments for (let i=0; i Date: Mon, 15 Sep 2025 21:24:14 -0400 Subject: [PATCH 42/47] thin3 adjust chain nexus normals --- src/kiri/mode/fdm/post.js | 87 ++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 16 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index 2d286191..d3a19198 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -184,10 +184,10 @@ function thin_type_3(params) { let interpolateShortSpur = true; let showChainRawPoints = false; let showChainInterpPoints = true; - let showMidNormals = true; - let showMidPoints = true; let showMidline = true; - let showMidRadii = false; + let showMidPoints = true; + let showMidNormals = true; + let showMidRadii = true; let showNexuses = false; let pointMap = new Map(); @@ -489,29 +489,84 @@ function thin_type_3(params) { for (let pt of chain) { top.shells.push(newPolygon().centerCircle(pt, pt.r ?? 0.1, 10)); } - // emit chain with subdivision for long segments - for (let i=0; i Date: Tue, 16 Sep 2025 09:30:56 -0400 Subject: [PATCH 43/47] thin3 add point/normal fn --- src/kiri/mode/fdm/post.js | 67 +++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index d3a19198..1554ad4d 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -183,11 +183,11 @@ function thin_type_3(params) { let mergeChains = true; let interpolateShortSpur = true; let showChainRawPoints = false; - let showChainInterpPoints = true; + let showChainInterpPoints = false; let showMidline = true; let showMidPoints = true; let showMidNormals = true; - let showMidRadii = true; + let showMidRadii = false; let showNexuses = false; let pointMap = new Map(); @@ -481,6 +481,29 @@ function thin_type_3(params) { top.shells.push(newPolygon().centerCircle(point, 0.2, 6 - rec.shorts)); } + function renderPointNormal(pt, ndx, ndy) { + if (showMidNormals) { + let xo = ndx * pt.r; + let yo = ndy * pt.r; + thin.push(new Point(pt.x + yo, pt.y - xo)); + thin.push(new Point(pt.x - yo, pt.y + xo)); + } + if (showMidRadii) { + const Sx = pt.x + ndy * pt.r; + const Sy = pt.y - ndx * pt.r; + const circ = top.shells; + const pop = div(pt.r); + let acc = 0; + for (let r of pop) { + const t = acc + r; + const px = Sx + -ndy * t; + const py = Sy + ndx * t; + circ.push(newPolygon().centerCircle({x:px, y:py}, r, 10)); + acc += r * 2; + } + } +} + // gather point offsets into shells let zi = z; for (let chain of chains) { @@ -510,7 +533,7 @@ function thin_type_3(params) { let dy = (p1.y - p0.y); let ndx = dx / len; let ndy = dy / len; - segs.push({ p0, p1, len, offs, step, dr, dx, dy, ndx, ndy }); + segs.push({ p0, p1, offs, step, dr, dx, dy, ndx, ndy }); } console.log({ segs }); // compute chain subdivisions @@ -518,7 +541,7 @@ function thin_type_3(params) { for (let si=0; si Date: Tue, 16 Sep 2025 10:22:22 -0400 Subject: [PATCH 44/47] thin3 use point vs dist comparison for closed chains --- src/kiri/mode/fdm/post.js | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index 1554ad4d..a2222402 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -177,17 +177,17 @@ function thin_type_3(params) { let scale = 1000; let minR = offsetN / 2; let maxR = minR * 1.5; - let minSpur = offsetN * 3; + let minSpur = offsetN * 2; let showNoodle = false; let mergeChains = true; let interpolateShortSpur = true; let showChainRawPoints = false; - let showChainInterpPoints = false; + let showChainInterpPoints = true; let showMidline = true; - let showMidPoints = true; - let showMidNormals = true; - let showMidRadii = false; + let showMidPoints = false; + let showMidNormals = false; + let showMidRadii = true; let showNexuses = false; let pointMap = new Map(); @@ -365,25 +365,25 @@ function thin_type_3(params) { let cj = chains[j]; if (!cj) continue; if (ci[0] === cj[0]) { - ci.reverse().appendAll(cj.slice(1)) + ci.reverse().appendAll(cj) ci.total += cj.total; cj.merged = true; chains[j] = undefined; return true; } else if (ci.peek() === cj.peek()) { - ci.appendAll(cj.reverse().slice(1)); + ci.appendAll(cj.reverse()); ci.total += cj.total; cj.merged = true; chains[j] = undefined; return true; } else if (ci.peek() === cj[0]) { - ci.appendAll(cj.slice(1)); + ci.appendAll(cj); ci.total += cj.total; cj.merged = true; chains[j] = undefined; return true; } else if (ci[0] === cj.peek()) { - cj.appendAll(ci.slice(1)); + cj.appendAll(ci); cj.total += ci.total; ci.merged = true; chains[i] = undefined; @@ -394,8 +394,6 @@ function thin_type_3(params) { return false; } - // console.log({ chains }); - // map chains to nexus via endpoints let nexus = {}; function addEp(point, chain) { @@ -411,24 +409,27 @@ function thin_type_3(params) { addEp(chain.peek(), chain); } - // console.log({ chains, nexus, np: Object.values(nexus).length }); - // connect chains end to end following nexus branches that result // in the longest final merged chain (open or closed) // map chain endpoints to nexus points while (mergeChains && merge1chain()) ; + // filter out null chains that were merged into other chains chains = chains.filter(chain => chain); - console.log({ chains }); + + // filter dup points from connecting ends + chains = chains.map(chain => chain.filter((v, i) => i === 0 || v !== chain[i - 1])); // mark closed chains // todo use exact point match by eliminating slice above // then later removing repeated points in the resulting chain from merges for (let chain of chains) { - chain.closed = pointDist(chain[0], chain.peek()) <= offsetN; - // console.log(chain.closed); + chain.closed = (chain[0] === chain.peek()); + console.log(chain.closed ? 'closed' : 'open'); + if (chain.closed) chain.pop(); } + // shorten chain length by minR distance by popping or moving points function shorten(chain) { for (let rem=0, i=chain.length-1; i>=1 && rem < minR; i--) { let c1 = chain[i]; @@ -454,8 +455,6 @@ function thin_type_3(params) { } } - // add chains - // shorten chains terminating at nexus by midR for (let rec of Object.values(nexus)) { let { point } = rec; @@ -476,8 +475,7 @@ function thin_type_3(params) { if (showNexuses) for (let rec of Object.values(nexus)) { - let { point, chains } = rec; - // console.log(rec); + let { point } = rec; top.shells.push(newPolygon().centerCircle(point, 0.2, 6 - rec.shorts)); } From 1501d33c700e59d6c750d6f99b60b66345a8f079 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 21 Sep 2025 17:13:57 -0400 Subject: [PATCH 45/47] fixes #427 now scale prevented below threshold --- src/kiri/core/init.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/kiri/core/init.js b/src/kiri/core/init.js index 36a68da0..02dd0a55 100644 --- a/src/kiri/core/init.js +++ b/src/kiri/core/init.js @@ -1323,7 +1323,8 @@ function init_one() { yr = ((tl && yc) || (!tl && yt) ? ra : 1), zr = ((tl && zc) || (!tl && zt) ? ra : 1); // prevent null scale - if (xr * yr * zr === 0) { + if (xv * xr < 0.1 || yv * yr < 0.1 || zv * zr < 0.1) { + api.alerts.show('invalid scale value'); return; } selection.scale(xr,yr,zr); @@ -1350,7 +1351,11 @@ function init_one() { xr = ((tl && xc) || (!tl && xt) ? ra : 1), yr = ((tl && yc) || (!tl && yt) ? ra : 1), zr = ((tl && zc) || (!tl && zt) ? ra : 1); - + // prevent null scale + if (xv * xr < 0.1 || yv * yr < 0.1 || zv * zr < 0.1) { + api.alerts.show('invalid scale value'); + return; + } selection.scale(xr,yr,zr); ui.scaleX.was = ui.scaleX.value = xv * xr; ui.scaleY.was = ui.scaleY.value = yv * yr; @@ -1362,6 +1367,7 @@ function init_one() { e.target.value = val; } + // on enter but not on blur space.event.onEnterKey([ ui.scaleX, selectionScale, ui.scaleY, selectionScale, @@ -1369,6 +1375,9 @@ function init_one() { ui.sizeX, selectionSize, ui.sizeY, selectionSize, ui.sizeZ, selectionSize, + ]); + // on enter and blur + space.event.onEnterKey([ ui.toolName, updateTool, ui.toolNum, updateTool, ui.toolFluteDiam, updateTool, From fc9b5b668b0ecf5ea097faddcf591217e991be6c Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 21 Sep 2025 17:23:04 -0400 Subject: [PATCH 46/47] fixes #426 by applying proper camZBottom value --- src/kiri/mode/cam/cl-tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kiri/mode/cam/cl-tab.js b/src/kiri/mode/cam/cl-tab.js index 8237065e..0cda4f12 100644 --- a/src/kiri/mode/cam/cl-tab.js +++ b/src/kiri/mode/cam/cl-tab.js @@ -61,7 +61,7 @@ export function createTabBox(iw, ic, n) { const { camZBottom, camStockIndexed } = process; const sz = stock.z || bounds.max.z; const zto = sz - iw.track.top; - const zp = (env.camZBottom || camStockIndexed ? env.camZBottom : sz - track.box.d - zto) + (camTabsMidline ? 0 : camTabsHeight / 2); + const zp = (camZBottom || camStockIndexed ? camZBottom : sz - track.box.d - zto) + (camTabsMidline ? 0 : camTabsHeight / 2); ic.x += n.x * camTabsDepth / 2; // offset from part ic.z -= n.y * camTabsDepth / 2; // offset swap z,y ic.y = zp; // offset swap in world space y,z From 65c2978f43f092d5199a14bb82e4abac2080199c Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 21 Sep 2025 17:35:44 -0400 Subject: [PATCH 47/47] fixes #421 by removing all non-extended-thru cuts when step down is greater than full cut depth of part --- src/kiri/mode/cam/op-outline.js | 5 +++++ src/kiri/mode/cam/op-rough.js | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/kiri/mode/cam/op-outline.js b/src/kiri/mode/cam/op-outline.js index 270f4c64..c9b7e275 100644 --- a/src/kiri/mode/cam/op-outline.js +++ b/src/kiri/mode/cam/op-outline.js @@ -100,6 +100,11 @@ class OpOutline extends CamOp { // extend cut thru (only when z bottom is 0) if (workarea.bottom_z < 0) { let last = slices[slices.length-1]; + // when step down > full cut depth, clear slices and + // leave only the cut-thru pass(es) + if (op.down > workarea.top_stock - workarea.bottom_part) { + slices.length = 0; + } for (let zneg of base_util.lerp(0, -workarea.bottom_cut, op.down)) { if (!last) continue; let add = last.clone(true); diff --git a/src/kiri/mode/cam/op-rough.js b/src/kiri/mode/cam/op-rough.js index 341f56d8..beb24827 100644 --- a/src/kiri/mode/cam/op-rough.js +++ b/src/kiri/mode/cam/op-rough.js @@ -32,11 +32,12 @@ class OpRough extends CamOp { let roughStock = op.all && isIndexed; let toolDiam = new Tool(settings, op.tool).fluteDiameter(); let trueShadow = process.camTrueShadow === true; + let cutThruBypass = op.down > workarea.top_stock - workarea.bottom_part; updateToolDiams(toolDiam); // clear the stock above the area to be roughed out - if (workarea.top_z > workarea.top_part) { + if (workarea.top_z > workarea.top_part && !cutThruBypass) { let shadow = state.shadow.base.clone(); let step = toolDiam * op.step; let inset = roughStock ? @@ -258,6 +259,12 @@ class OpRough extends CamOp { let last = slices[slices.length-1]; + // when step down > full cut depth, clear slices and + // leave only the cut-thru pass(es) + if (cutThruBypass) { + slices.length = 0; + } + // add cut thru passes if (workarea.bottom_z < 0) for (let zneg of base_util.lerp(0, -workarea.bottom_cut, op.down)) { if (!last) continue;