Merge branch 'rel-4.3' of https://github.com/GridSpace/grid-apps into drill-optimization
This commit is contained in:
commit
cd462709d7
37 changed files with 955 additions and 223 deletions
|
|
@ -12,7 +12,9 @@ const MESH_OUTFILE = 'src/pack/mesh-main.js';
|
||||||
const MESH_EXTRAS = [ ];
|
const MESH_EXTRAS = [ ];
|
||||||
|
|
||||||
const KIRI_OUTFILE = 'src/pack/kiri-main.js';
|
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) {
|
async function appendExtraModules(extras, outfile, minify = false) {
|
||||||
const files = await glob(extras);
|
const files = await glob(extras);
|
||||||
|
|
|
||||||
|
|
@ -1115,7 +1115,7 @@ self.kiri.load(api => {
|
||||||
]),
|
]),
|
||||||
h.div({ class: "var-row f-grow" }, [
|
h.div({ class: "var-row f-grow" }, [
|
||||||
h.label('active'),
|
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: "pop-sep" }),
|
||||||
h.div({ class: "f-row gap3 f-grow" }, [
|
h.div({ class: "f-row gap3 f-grow" }, [
|
||||||
|
|
|
||||||
4
notes.md
4
notes.md
|
|
@ -31,6 +31,10 @@
|
||||||
* `B` multi-extruder rendering of raft fails to offset the rest of the print
|
* `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
|
* `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` better thin-wall (like arachne)
|
||||||
* `F` add scarf seams
|
* `F` add scarf seams
|
||||||
* `F` add 'random' and 'seam' start point options
|
* `F` add 'random' and 'seam' start point options
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
"fast-glob": "^3.3.3",
|
"fast-glob": "^3.3.3",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"jszip": "^3.7.1",
|
"jszip": "^3.7.1",
|
||||||
"manifold-3d": "^3.1.1",
|
"manifold-3d": "^3.2.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"npm": "^11.4.2",
|
"npm": "^11.4.2",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
|
|
|
||||||
|
|
@ -258,9 +258,10 @@ export class Polygon {
|
||||||
// of the polygon with the noodle removed (for the next pass)
|
// of the polygon with the noodle removed (for the next pass)
|
||||||
noodle(width) {
|
noodle(width) {
|
||||||
let clone = this.clone(true);
|
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));
|
let nood = POLY.nest(POLY.flatten([ clone, ...ins ], [], true));
|
||||||
return { noodle: nood, remain: ins };
|
return { noodle: nood, remain };
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate center crossing point cloud
|
// generate center crossing point cloud
|
||||||
|
|
@ -652,7 +653,7 @@ export class Polygon {
|
||||||
td += d;
|
td += d;
|
||||||
if (ap.length === 1 && td >= pe1) {
|
if (ap.length === 1 && td >= pe1) {
|
||||||
ap.push(np);
|
ap.push(np);
|
||||||
} else if (ap.length === 2 && td >= pe2) {
|
} else if (ap.length === 2 && (td >= pe2 || i >= l)) {
|
||||||
ap.push(np);
|
ap.push(np);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -556,6 +556,7 @@ export const conf = {
|
||||||
camTabsDepth: 5,
|
camTabsDepth: 5,
|
||||||
camTabsMidline: false,
|
camTabsMidline: false,
|
||||||
camDepthFirst: false,
|
camDepthFirst: false,
|
||||||
|
camInnerFirst: false,
|
||||||
camEaseDown: false,
|
camEaseDown: false,
|
||||||
camEaseAngle: 10,
|
camEaseAngle: 10,
|
||||||
camOriginTop: true,
|
camOriginTop: true,
|
||||||
|
|
@ -639,9 +640,6 @@ export const conf = {
|
||||||
ctOriginBounds: false,
|
ctOriginBounds: false,
|
||||||
outputInvertX: false,
|
outputInvertX: false,
|
||||||
outputInvertY: false,
|
outputInvertY: false,
|
||||||
ctOutKnifeDepth: 1,
|
|
||||||
ctOutKnifePasses: 1,
|
|
||||||
ctOutKnifeTip: 2,
|
|
||||||
ctOutInches: false,
|
ctOutInches: false,
|
||||||
ctOutShaper: false
|
ctOutShaper: false
|
||||||
}
|
}
|
||||||
|
|
@ -728,9 +726,6 @@ export const conf = {
|
||||||
ctOriginBounds: false,
|
ctOriginBounds: false,
|
||||||
outputInvertX: false,
|
outputInvertX: false,
|
||||||
outputInvertY: false,
|
outputInvertY: false,
|
||||||
ctOutKnifeDepth: 1,
|
|
||||||
ctOutKnifePasses: 1,
|
|
||||||
ctOutKnifeTip: 2,
|
|
||||||
ctOutStack: false,
|
ctOutStack: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,7 @@ function renderDevices(devices) {
|
||||||
};
|
};
|
||||||
ui.deviceExport.onclick = function(event) {
|
ui.deviceExport.onclick = function(event) {
|
||||||
const record = {
|
const record = {
|
||||||
version: kiri.version,
|
version: api.version,
|
||||||
device: selected,
|
device: selected,
|
||||||
process: api.process.code(),
|
process: api.process.code(),
|
||||||
profiles: event.altKey ? settings.prof() : undefined,
|
profiles: event.altKey ? settings.prof() : undefined,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { util } from '../../geo/base.js';
|
||||||
import { MODES } from './consts.js';
|
import { MODES } from './consts.js';
|
||||||
import { LASER as laser_driver } from '../mode/laser/driver.js';
|
import { LASER as laser_driver } from '../mode/laser/driver.js';
|
||||||
import { SLA as sla_client } from '../mode/sla/client.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;
|
let printSeq = parseInt(local['kiri-print-seq'] || local['print-seq'] || "0") + 1;
|
||||||
|
|
||||||
|
|
@ -654,7 +655,7 @@ function exportGCodeDialog(gcode, sections, info, names) {
|
||||||
data: gcode
|
data: gcode
|
||||||
},{
|
},{
|
||||||
name: `Metadata/plate_1.gcode.md5`,
|
name: `Metadata/plate_1.gcode.md5`,
|
||||||
data: ext.md5.hash(gcode)
|
data: hash(gcode)
|
||||||
},{
|
},{
|
||||||
name: `Metadata/plate_1_small.png`,
|
name: `Metadata/plate_1_small.png`,
|
||||||
data: api.view.bambu.s128.png
|
data: api.view.bambu.s128.png
|
||||||
|
|
|
||||||
|
|
@ -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 menuFDM } from '../mode/fdm/init-menu.js';
|
||||||
import { menu as menuLaser } from '../mode/laser/init-menu.js';
|
import { menu as menuLaser } from '../mode/laser/init-menu.js';
|
||||||
import { menu as menuSLA } from '../mode/sla/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,
|
let { CAM, SLA, FDM, LASER, DRAG, WJET, WEDM } = MODES,
|
||||||
{ client, catalog, platform, selection, stats } = api,
|
{ client, catalog, platform, selection, stats } = api,
|
||||||
|
|
@ -1322,7 +1323,8 @@ function init_one() {
|
||||||
yr = ((tl && yc) || (!tl && yt) ? ra : 1),
|
yr = ((tl && yc) || (!tl && yt) ? ra : 1),
|
||||||
zr = ((tl && zc) || (!tl && zt) ? ra : 1);
|
zr = ((tl && zc) || (!tl && zt) ? ra : 1);
|
||||||
// prevent null scale
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
selection.scale(xr,yr,zr);
|
selection.scale(xr,yr,zr);
|
||||||
|
|
@ -1349,7 +1351,11 @@ function init_one() {
|
||||||
xr = ((tl && xc) || (!tl && xt) ? ra : 1),
|
xr = ((tl && xc) || (!tl && xt) ? ra : 1),
|
||||||
yr = ((tl && yc) || (!tl && yt) ? ra : 1),
|
yr = ((tl && yc) || (!tl && yt) ? ra : 1),
|
||||||
zr = ((tl && zc) || (!tl && zt) ? 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);
|
selection.scale(xr,yr,zr);
|
||||||
ui.scaleX.was = ui.scaleX.value = xv * xr;
|
ui.scaleX.was = ui.scaleX.value = xv * xr;
|
||||||
ui.scaleY.was = ui.scaleY.value = yv * yr;
|
ui.scaleY.was = ui.scaleY.value = yv * yr;
|
||||||
|
|
@ -1361,6 +1367,7 @@ function init_one() {
|
||||||
e.target.value = val;
|
e.target.value = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// on enter but not on blur
|
||||||
space.event.onEnterKey([
|
space.event.onEnterKey([
|
||||||
ui.scaleX, selectionScale,
|
ui.scaleX, selectionScale,
|
||||||
ui.scaleY, selectionScale,
|
ui.scaleY, selectionScale,
|
||||||
|
|
@ -1368,6 +1375,9 @@ function init_one() {
|
||||||
ui.sizeX, selectionSize,
|
ui.sizeX, selectionSize,
|
||||||
ui.sizeY, selectionSize,
|
ui.sizeY, selectionSize,
|
||||||
ui.sizeZ, selectionSize,
|
ui.sizeZ, selectionSize,
|
||||||
|
]);
|
||||||
|
// on enter and blur
|
||||||
|
space.event.onEnterKey([
|
||||||
ui.toolName, updateTool,
|
ui.toolName, updateTool,
|
||||||
ui.toolNum, updateTool,
|
ui.toolNum, updateTool,
|
||||||
ui.toolFluteDiam, updateTool,
|
ui.toolFluteDiam, updateTool,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
import './frame.js';
|
import './frame.js';
|
||||||
|
import '../../ext/base64.js';
|
||||||
|
|
||||||
import lang from './lang.js';
|
import lang from './lang.js';
|
||||||
import STACKS from './stacks.js';
|
import STACKS from './stacks.js';
|
||||||
|
|
|
||||||
|
|
@ -362,14 +362,11 @@ class Print {
|
||||||
// if( axis == 'I' || axis == "J") return
|
// if( axis == 'I' || axis == "J") return
|
||||||
if (abs) {
|
if (abs) {
|
||||||
pos[axis] = val;
|
pos[axis] = val;
|
||||||
|
|
||||||
if (axis == "X") point.x = factor * pos.X + xoff.x + off.x
|
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 == "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 if (axis == "Z") point.z = factor * pos.Z + xoff.z + off.z + dz
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
mov[axis] = val;
|
// mov[axis] = val;
|
||||||
pos[axis] += val;
|
pos[axis] += val;
|
||||||
}
|
}
|
||||||
// console.log("position updated",structuredClone(pos))
|
// console.log("position updated",structuredClone(pos))
|
||||||
|
|
@ -393,7 +390,6 @@ class Print {
|
||||||
center = center.add(prevPoint);
|
center = center.add(prevPoint);
|
||||||
center.setZ((prevPoint.z+point.z)/2+dz);
|
center.setZ((prevPoint.z+point.z)/2+dz);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
center,
|
center,
|
||||||
point,
|
point,
|
||||||
|
|
@ -402,10 +398,8 @@ class Print {
|
||||||
}
|
}
|
||||||
|
|
||||||
function outputPoint(point,lastP,emit,{center,arcPoints,retract}) {
|
function outputPoint(point,lastP,emit,{center,arcPoints,retract}) {
|
||||||
|
|
||||||
// non-move in a new plane means burp out
|
// non-move in a new plane means burp out
|
||||||
// the old sequence and start a new one
|
// the old sequence and start a new one
|
||||||
|
|
||||||
if (newlayer || (autolayer && seq.z != point.z)) {
|
if (newlayer || (autolayer && seq.z != point.z)) {
|
||||||
newlayer = false;
|
newlayer = false;
|
||||||
let dz = point.z - seq.z;
|
let dz = point.z - seq.z;
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,12 @@ function update_info() {
|
||||||
const ui = api.ui;
|
const ui = api.ui;
|
||||||
let bounds = new THREE.Box3(), track;
|
let bounds = new THREE.Box3(), track;
|
||||||
for_meshes(mesh => {
|
for_meshes(mesh => {
|
||||||
bounds = bounds.union(mesh.getBoundingBox());
|
let box = mesh.getBoundingBox().clone();
|
||||||
track = mesh.widget.track;
|
track = mesh.widget.track;
|
||||||
|
let {pos} = track
|
||||||
|
box.min.add(pos);
|
||||||
|
box.max.add(pos);
|
||||||
|
bounds = bounds.union(box);
|
||||||
});
|
});
|
||||||
if (bounds.min.x === Infinity) {
|
if (bounds.min.x === Infinity) {
|
||||||
if (selectedMeshes.length === 0) {
|
if (selectedMeshes.length === 0) {
|
||||||
|
|
|
||||||
|
|
@ -544,12 +544,14 @@ function settingsImport(data, ask) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (api.const.LOCAL) console.log('import', data);
|
if (api.const.LOCAL) console.log('import', data);
|
||||||
let isSettings = (data.settings && data.version && data.time);
|
let isSettings = (data.settings && data.time);
|
||||||
let isProcess = (data.process && data.version && data.time && data.mode && data.name);
|
let isProcess = (data.process && data.time && data.mode && data.name);
|
||||||
let isDevice = (data.device && data.version && data.time);
|
let isDevice = (data.device && data.time);
|
||||||
let isTools = (data.tools && data.version && data.time)
|
let isTools = (data.tools && data.time)
|
||||||
let isWork = (data.work);
|
let isWork = (data.work);
|
||||||
|
|
||||||
if (!isSettings && !isDevice && !isProcess && !isTools) {
|
if (!isSettings && !isDevice && !isProcess && !isTools) {
|
||||||
uc.alert('invalid settings or device format');
|
uc.alert('invalid settings or device format');
|
||||||
console.log('data',data);
|
console.log('data',data);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
import { api } from './api.js';
|
import { api } from './api.js';
|
||||||
import { THREE } from '../../ext/three.js';
|
import { THREE } from '../../ext/three.js';
|
||||||
|
import { space } from '../../moto/space.js';
|
||||||
|
|
||||||
const { Vector3, Quaternion } = THREE;
|
const { Vector3, Quaternion } = THREE;
|
||||||
const { kiri, moto } = root;
|
|
||||||
|
|
||||||
const XAXIS = new THREE.Vector3(1,0,0);
|
const XAXIS = new THREE.Vector3(1,0,0);
|
||||||
const ZAXIS = new THREE.Vector3(0,0,1);
|
const ZAXIS = new THREE.Vector3(0,0,1);
|
||||||
|
|
@ -106,7 +106,7 @@ api.event.on('tool.mesh.lay-flat', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function scale() {
|
function scale() {
|
||||||
let cam = moto.space.internals().camera;
|
let cam = space.internals().camera;
|
||||||
let dist = cam.position.distanceTo(pmesh.position);
|
let dist = cam.position.distanceTo(pmesh.position);
|
||||||
let scale = opName === 'camera focus' ?
|
let scale = opName === 'camera focus' ?
|
||||||
Math.min(0.25, 100 / dist) : dist / 100;
|
Math.min(0.25, 100 / dist) : dist / 100;
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ class Widget {
|
||||||
* @returns {Widget}
|
* @returns {Widget}
|
||||||
*/
|
*/
|
||||||
loadVertices(data, options = { index: false }) {
|
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,
|
let vertices,
|
||||||
autoscale = false;
|
autoscale = false;
|
||||||
if (ArrayBuffer.isView(data) || typeof(data) != 'object') {
|
if (ArrayBuffer.isView(data) || typeof(data) != 'object') {
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ function replace(vertices, sel) {
|
||||||
} else {
|
} else {
|
||||||
// dialog
|
// dialog
|
||||||
$('load-file').onchange = function(event) {
|
$('load-file').onchange = function(event) {
|
||||||
load.File.load(event.target.files[0])
|
load(event.target.files[0])
|
||||||
.then(data => onload(data[0].mesh))
|
.then(data => onload(data[0].mesh))
|
||||||
.catch(error => console.log({error}));
|
.catch(error => console.log({error}));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
219
src/kiri/dev/fdm/flashforge.5m.pro.json
Normal file
219
src/kiri/dev/fdm/flashforge.5m.pro.json
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
207
src/kiri/dev/fdm/taz.lulzbot.747.json
Normal file
207
src/kiri/dev/fdm/taz.lulzbot.747.json
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
import { api } from '../../core/api.js';
|
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 { CAM } from './driver-fe.js';
|
||||||
import { clearTabs, restoreTabs } from './cl-tab.js';
|
import { clearTabs, restoreTabs } from './cl-tab.js';
|
||||||
import { updateStock } from './cl-stock.js';
|
import { updateStock } from './cl-stock.js';
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ export function createTabBox(iw, ic, n) {
|
||||||
const { camZBottom, camStockIndexed } = process;
|
const { camZBottom, camStockIndexed } = process;
|
||||||
const sz = stock.z || bounds.max.z;
|
const sz = stock.z || bounds.max.z;
|
||||||
const zto = sz - iw.track.top;
|
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.x += n.x * camTabsDepth / 2; // offset from part
|
||||||
ic.z -= n.y * camTabsDepth / 2; // offset swap z,y
|
ic.z -= n.y * camTabsDepth / 2; // offset swap z,y
|
||||||
ic.y = zp; // offset swap in world space y,z
|
ic.y = zp; // offset swap in world space y,z
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ const traceClear = () => {
|
||||||
api.conf.save();
|
api.conf.save();
|
||||||
};
|
};
|
||||||
|
|
||||||
const opAdd = (rec) => {
|
export const opAdd = (rec) => {
|
||||||
if (!env.isCamMode) return;
|
if (!env.isCamMode) return;
|
||||||
clearPops();
|
clearPops();
|
||||||
let oplist = env.current.process.ops;
|
let oplist = env.current.process.ops;
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,6 @@ export function cam_export(print, online) {
|
||||||
lasering = false,
|
lasering = false,
|
||||||
laserOp,
|
laserOp,
|
||||||
stock = settings.stock || {},
|
stock = settings.stock || {},
|
||||||
ztOff = stock.z - widget.track.top,
|
|
||||||
bounds = widget.getBoundingBox(),
|
|
||||||
zmax = stock.z,
|
zmax = stock.z,
|
||||||
runbox = {
|
runbox = {
|
||||||
max: { x: -Infinity, y: -Infinity, z: -Infinity },
|
max: { x: -Infinity, y: -Infinity, z: -Infinity },
|
||||||
|
|
@ -234,7 +232,7 @@ export function cam_export(print, online) {
|
||||||
// speed: Infinity,
|
// speed: Infinity,
|
||||||
tool: out.tool,
|
tool: out.tool,
|
||||||
point: { x: newpos.x, y: newpos.y, z: newpos.z, a: pos.a },
|
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
|
dx: 1, dy: 1, dz: 0, time: 0
|
||||||
});
|
});
|
||||||
|
|
@ -251,7 +249,7 @@ export function cam_export(print, online) {
|
||||||
// speed: Infinity,
|
// speed: Infinity,
|
||||||
tool: out.tool,
|
tool: out.tool,
|
||||||
point: { x: newpos.x, y: newpos.y, z: newpos.z, a: newpos.a },
|
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
|
dx: 0, dy: 0, dz: 1, time: 0
|
||||||
});
|
});
|
||||||
|
|
@ -274,6 +272,9 @@ export function cam_export(print, online) {
|
||||||
dist = Math.sqrt(dx * dx + dy * dy + dz * dz),
|
dist = Math.sqrt(dx * dx + dy * dy + dz * dz),
|
||||||
newFeed = feed && feed !== pos.f;
|
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)
|
// drop dup points (all deltas are 0)
|
||||||
if (!arc && !(dx || dy || dz || da)) {
|
if (!arc && !(dx || dy || dz || da)) {
|
||||||
|
|
@ -408,7 +409,7 @@ export function cam_export(print, online) {
|
||||||
|
|
||||||
// collect tool info to add to header
|
// collect tool info to add to header
|
||||||
let toolz = {}, ctool;
|
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
|
// remap points as necessary for origins, offsets, inversions
|
||||||
print.output.forEach(layer => {
|
print.output.forEach(layer => {
|
||||||
if (!Array.isArray(layer)) {
|
if (!Array.isArray(layer)) {
|
||||||
|
|
@ -427,8 +428,9 @@ export function cam_export(print, online) {
|
||||||
// ensure not point is modified twice
|
// ensure not point is modified twice
|
||||||
point.mod = 1;
|
point.mod = 1;
|
||||||
if (isOffset) {
|
if (isOffset) {
|
||||||
point.x += offset.x;
|
point.x += offset.x ?? 0;
|
||||||
point.y += offset.y;
|
point.y += offset.y ?? 0;
|
||||||
|
point.z -= offset.z ?? 0;
|
||||||
}
|
}
|
||||||
if (scale) {
|
if (scale) {
|
||||||
point.x *= scale.x;
|
point.x *= scale.x;
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ export function menu() {
|
||||||
camConventional: newBoolean(LANG.ou_conv_s, onBooleanClick, {title:LANG.ou_conv_l}),
|
camConventional: newBoolean(LANG.ou_conv_s, onBooleanClick, {title:LANG.ou_conv_l}),
|
||||||
camEaseDown: newBoolean(LANG.cr_ease_s, onBooleanClick, {title:LANG.cr_ease_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}),
|
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}),
|
camToolInit: newBoolean(LANG.ou_toin_s, onBooleanClick, {title:LANG.ou_toin_l}),
|
||||||
separator: newBlank({ class:"set-sep", driven }),
|
separator: newBlank({ class:"set-sep", driven }),
|
||||||
camFirstZMax: newBoolean(LANG.ou_z1st_s, onBooleanClick, {title:LANG.ou_z1st_l}),
|
camFirstZMax: newBoolean(LANG.ou_z1st_s, onBooleanClick, {title:LANG.ou_z1st_l}),
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,11 @@ class OpOutline extends CamOp {
|
||||||
// extend cut thru (only when z bottom is 0)
|
// extend cut thru (only when z bottom is 0)
|
||||||
if (workarea.bottom_z < 0) {
|
if (workarea.bottom_z < 0) {
|
||||||
let last = slices[slices.length-1];
|
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)) {
|
for (let zneg of base_util.lerp(0, -workarea.bottom_cut, op.down)) {
|
||||||
if (!last) continue;
|
if (!last) continue;
|
||||||
let add = last.clone(true);
|
let add = last.clone(true);
|
||||||
|
|
@ -225,7 +230,7 @@ class OpOutline extends CamOp {
|
||||||
|
|
||||||
let easeDown = process.camEaseDown;
|
let easeDown = process.camEaseDown;
|
||||||
let toolDiam = this.toolDiam;
|
let toolDiam = this.toolDiam;
|
||||||
let cutdir = op.ov_conv;//process.camConventional;
|
let cutdir = op.ov_conv;
|
||||||
let depthFirst = process.camDepthFirst;
|
let depthFirst = process.camDepthFirst;
|
||||||
let depthData = [];
|
let depthData = [];
|
||||||
|
|
||||||
|
|
@ -251,9 +256,9 @@ class OpOutline extends CamOp {
|
||||||
});
|
});
|
||||||
|
|
||||||
// set cut direction on outer polys
|
// set cut direction on outer polys
|
||||||
POLY.setWinding(t, cutdir);
|
POLY.setWinding(t, !cutdir);
|
||||||
// set cut direction on inner polys
|
// set cut direction on inner polys
|
||||||
POLY.setWinding(c, !cutdir);
|
POLY.setWinding(c, cutdir);
|
||||||
|
|
||||||
if (depthFirst) {
|
if (depthFirst) {
|
||||||
depthData.push(polys);
|
depthData.push(polys);
|
||||||
|
|
@ -269,9 +274,11 @@ class OpOutline extends CamOp {
|
||||||
let polyLast;
|
let polyLast;
|
||||||
// console.log({order, orderPolys});
|
// console.log({order, orderPolys});
|
||||||
printPoint = poly2polyEmit(orderPolys, printPoint, function(poly, index, count) {
|
printPoint = poly2polyEmit(orderPolys, printPoint, function(poly, index, count) {
|
||||||
|
|
||||||
polyLast = polyEmit(poly, index, count, polyLast);
|
polyLast = polyEmit(poly, index, count, polyLast);
|
||||||
}, { swapdir: false });
|
}, {
|
||||||
|
swapdir: false,
|
||||||
|
weight: process.camInnerFirst
|
||||||
|
});
|
||||||
})
|
})
|
||||||
newLayer();
|
newLayer();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { Tool } from './tool.js';
|
||||||
import { newPolygon } from '../../../geo/polygon.js';
|
import { newPolygon } from '../../../geo/polygon.js';
|
||||||
import { newSlice } from '../../core/slice.js';
|
import { newSlice } from '../../core/slice.js';
|
||||||
import { newPoint } from '../../../geo/point.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 {
|
class OpRegister extends CamOp {
|
||||||
constructor(state, op) {
|
constructor(state, op) {
|
||||||
|
|
@ -88,7 +88,7 @@ class OpRegister extends CamOp {
|
||||||
step = -step;
|
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);
|
let slice = newSlice(z);
|
||||||
addSlices(slice);
|
addSlices(slice);
|
||||||
sliceOut.push(slice);
|
sliceOut.push(slice);
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,12 @@ class OpRough extends CamOp {
|
||||||
let roughStock = op.all && isIndexed;
|
let roughStock = op.all && isIndexed;
|
||||||
let toolDiam = new Tool(settings, op.tool).fluteDiameter();
|
let toolDiam = new Tool(settings, op.tool).fluteDiameter();
|
||||||
let trueShadow = process.camTrueShadow === true;
|
let trueShadow = process.camTrueShadow === true;
|
||||||
|
let cutThruBypass = op.down > workarea.top_stock - workarea.bottom_part;
|
||||||
|
|
||||||
updateToolDiams(toolDiam);
|
updateToolDiams(toolDiam);
|
||||||
|
|
||||||
// clear the stock above the area to be roughed out
|
// 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 shadow = state.shadow.base.clone();
|
||||||
let step = toolDiam * op.step;
|
let step = toolDiam * op.step;
|
||||||
let inset = roughStock ?
|
let inset = roughStock ?
|
||||||
|
|
@ -55,9 +56,16 @@ class OpRough extends CamOp {
|
||||||
for (let z = zstart; zsteps > 0; zsteps--) {
|
for (let z = zstart; zsteps > 0; zsteps--) {
|
||||||
let slice = newSlice();
|
let slice = newSlice();
|
||||||
slice.z = z;
|
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()
|
slice.output()
|
||||||
.setLayer("face", {face: color, line: color})
|
.setLayer("roughing", {face: color, line: color})
|
||||||
.addPolys(slice.camLines);
|
.addPolys(slice.camLines);
|
||||||
addSlices(slice);
|
addSlices(slice);
|
||||||
camFaces.push(slice);
|
camFaces.push(slice);
|
||||||
|
|
@ -251,6 +259,12 @@ class OpRough extends CamOp {
|
||||||
|
|
||||||
let last = slices[slices.length-1];
|
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)
|
if (workarea.bottom_z < 0)
|
||||||
for (let zneg of base_util.lerp(0, -workarea.bottom_cut, op.down)) {
|
for (let zneg of base_util.lerp(0, -workarea.bottom_cut, op.down)) {
|
||||||
if (!last) continue;
|
if (!last) continue;
|
||||||
|
|
@ -302,7 +316,9 @@ class OpRough extends CamOp {
|
||||||
// set winding specified in output
|
// set winding specified in output
|
||||||
POLY.setWinding(level, cutdir, false);
|
POLY.setWinding(level, cutdir, false);
|
||||||
poly2polyEmit(level, printPoint, (poly, index, count) => {
|
poly2polyEmit(level, printPoint, (poly, index, count) => {
|
||||||
printPoint = polyEmit(poly, index, count, printPoint);
|
printPoint = polyEmit(poly, index, count, printPoint, {cutFromLast: true});
|
||||||
|
}, {
|
||||||
|
weight: process.camInnerFirst
|
||||||
});
|
});
|
||||||
newLayer();
|
newLayer();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class OpTrace extends CamOp {
|
||||||
let cutdir = ov_conv;
|
let cutdir = ov_conv;
|
||||||
let polys = [];
|
let polys = [];
|
||||||
let reContour = false;
|
let reContour = false;
|
||||||
let canRecontour = offset !== 'none' && down === 0;
|
let canRecontour = offset !== 'none' && down < 0;
|
||||||
let stockRect = stock.center && stock.x && stock.y ?
|
let stockRect = stock.center && stock.x && stock.y ?
|
||||||
newPolygon().centerRectangle({x:0,y:0}, stock.x, stock.y) : undefined;
|
newPolygon().centerRectangle({x:0,y:0}, stock.x, stock.y) : undefined;
|
||||||
updateToolDiams(toolDiam);
|
updateToolDiams(toolDiam);
|
||||||
|
|
@ -57,6 +57,7 @@ class OpTrace extends CamOp {
|
||||||
let max = Math.max(...zs);
|
let max = Math.max(...zs);
|
||||||
if (max - min > 0.0001 && canRecontour) {
|
if (max - min > 0.0001 && canRecontour) {
|
||||||
reContour = true;
|
reContour = true;
|
||||||
|
down = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (false) newSliceOut(0).output()
|
if (false) newSliceOut(0).output()
|
||||||
|
|
@ -89,6 +90,7 @@ class OpTrace extends CamOp {
|
||||||
if (reContour) {
|
if (reContour) {
|
||||||
state.contourPolys(widget, slice.camLines);
|
state.contourPolys(widget, slice.camLines);
|
||||||
}
|
}
|
||||||
|
POLY.setWinding(slice.camLines, offset === "outside" ? !cutdir : cutdir, false);
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("trace follow", {line: color}, false)
|
.setLayer("trace follow", {line: color}, false)
|
||||||
.addPolys(slice.camLines)
|
.addPolys(slice.camLines)
|
||||||
|
|
@ -144,7 +146,7 @@ class OpTrace extends CamOp {
|
||||||
} else {
|
} else {
|
||||||
slice.camLines = POLY.flatten(slice.camLines, null, true);
|
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()
|
if (debug && shadow) slice.output()
|
||||||
.setLayer("trace shadow", {line: 0xff8811}, false)
|
.setLayer("trace shadow", {line: 0xff8811}, false)
|
||||||
.addPolys(shadow)
|
.addPolys(shadow)
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
easeDown = process.camEaseDown,
|
easeDown = process.camEaseDown,
|
||||||
easeAngle = process.camEaseAngle,
|
easeAngle = process.camEaseAngle,
|
||||||
depthFirst = process.camDepthFirst,
|
depthFirst = process.camDepthFirst,
|
||||||
|
innerFirst = process.camInnerFirst,
|
||||||
engageFactor = process.camFullEngage,
|
engageFactor = process.camFullEngage,
|
||||||
arcTolerance = process.camArcTolerance,
|
arcTolerance = process.camArcTolerance,
|
||||||
arcRes = toRadians(process.camArcResolution),
|
arcRes = toRadians(process.camArcResolution),
|
||||||
|
|
@ -243,7 +244,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
camOut(point.clone().setZ(zmax_outer));
|
camOut(point.clone().setZ(zmax_outer), 0);
|
||||||
points.forEach(function (point, index) {
|
points.forEach(function (point, index) {
|
||||||
camOut(point, 1);
|
camOut(point, 1);
|
||||||
if (index > 0 && index < points.length - 1) {
|
if (index > 0 && index < points.length - 1) {
|
||||||
|
|
@ -331,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.moveLen typically = tool diameter used to trigger terrain detection
|
||||||
* @param {number} opts.factor speed scale factor
|
* @param {number} opts.factor speed scale factor
|
||||||
*/
|
*/
|
||||||
function camOut(point, emit, opts) {
|
function camOut(point, emit = 1, opts) {
|
||||||
// console.trace({point, emit, opts})
|
// console.trace({point, emit, opts})
|
||||||
let {
|
let {
|
||||||
center = {},
|
center = {},
|
||||||
|
|
@ -392,8 +393,18 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
let deltaXY = lastPoint.distTo2D(point),
|
let deltaXY = lastPoint.distTo2D(point),
|
||||||
deltaZ = point.z - lastPoint.z,
|
deltaZ = point.z - lastPoint.z,
|
||||||
absDeltaZ = Math.abs(deltaZ),
|
absDeltaZ = Math.abs(deltaZ),
|
||||||
|
hasDelta = deltaXY >= 0.001 && absDeltaZ >= 0,
|
||||||
isMove = emit == 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, stockz, point.z);
|
||||||
|
layerPush(point.clone().setZ(stockz + 1), 0, 0, tool);
|
||||||
|
// change to cutting move for remainder of plunge
|
||||||
|
emit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// drop points too close together
|
// drop points too close together
|
||||||
if (!isLathe && !isArc && deltaXY < 0.001 && point.z === lastPoint.z) {
|
if (!isLathe && !isArc && deltaXY < 0.001 && point.z === lastPoint.z) {
|
||||||
// console.trace(["drop dup",lastPoint,point]);
|
// console.trace(["drop dup",lastPoint,point]);
|
||||||
|
|
@ -401,7 +412,7 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert short planar moves to cuts in some cases
|
// 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 iscontour = tolerance > 0;
|
||||||
let isflat = absDeltaZ < 0.001;
|
let isflat = absDeltaZ < 0.001;
|
||||||
// restrict this to contouring
|
// restrict this to contouring
|
||||||
|
|
@ -455,6 +466,11 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
layerPush(point.clone().setZ(zClearance), 0, 0, tool);
|
layerPush(point.clone().setZ(zClearance), 0, 0, tool);
|
||||||
// new pos for plunge calc
|
// new pos for plunge calc
|
||||||
deltaXY = 0;
|
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) {
|
} else if (isRough && deltaZ < 0) {
|
||||||
layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool);
|
layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool);
|
||||||
|
|
@ -717,9 +733,10 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
function emitTrace(slice) {
|
function emitTrace(slice) {
|
||||||
let { tool, rate, plunge } = slice.camTrace;
|
let { tool, rate, plunge } = slice.camTrace;
|
||||||
setTool(tool, rate, plunge);
|
setTool(tool, rate, plunge);
|
||||||
let traceTool = new Tool(settings, tool);
|
printPoint = poly2polyEmit(slice.camLines, printPoint, polyEmit, {
|
||||||
let traceToolDiam = traceTool.fluteDiameter();
|
swapdir: false,
|
||||||
printPoint = poly2polyEmit(slice.camLines, printPoint, polyEmit, { swapdir: false });
|
weight: innerFirst
|
||||||
|
});
|
||||||
newLayer();
|
newLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -736,10 +753,15 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
* @param {number} index - unused
|
* @param {number} index - unused
|
||||||
* @param {number} count - 1 to set engage factor
|
* @param {number} count - 1 to set engage factor
|
||||||
* @param {Point} fromPoint - the point to rapid move from
|
* @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
|
* @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 = [],
|
let arcQ = [],
|
||||||
arcMax = Infinity, // no max arc radius
|
arcMax = Infinity, // no max arc radius
|
||||||
lineTolerance = 0.001, // do not consider points under 0.001mm for arcs
|
lineTolerance = 0.001, // do not consider points under 0.001mm for arcs
|
||||||
|
|
@ -775,7 +797,8 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
// if(offset == 0) console.log("forEachPoint",point,pidx,points)
|
// if(offset == 0) console.log("forEachPoint",point,pidx,points)
|
||||||
// console.log({pointA, pointB, indexA, indexB,startIndex})
|
// console.log({pointA, pointB, indexA, indexB,startIndex})
|
||||||
if (indexA == 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 first point, move to and call export function
|
||||||
if (arcEnabled) arcQ.push(pointA);
|
if (arcEnabled) arcQ.push(pointA);
|
||||||
}
|
}
|
||||||
|
|
@ -845,7 +868,6 @@ export function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
radFault = Math.abs(angle) > arcRes
|
radFault = Math.abs(angle) > arcRes
|
||||||
if (radFault) {
|
if (radFault) {
|
||||||
// if so, remove first point
|
// if so, remove first point
|
||||||
|
|
||||||
console.log("secondary radfault,",structuredClone(arcQ),{angle,arcRes,a,b})
|
console.log("secondary radfault,",structuredClone(arcQ),{angle,arcRes,a,b})
|
||||||
camOut(arcQ.shift(), 1)
|
camOut(arcQ.shift(), 1)
|
||||||
}
|
}
|
||||||
|
|
@ -1062,7 +1084,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);
|
||||||
fromPoint = depthOutlinePath(fromPoint, depth + 1, levels, radius, emitter, !dir, ease);
|
fromPoint = depthOutlinePath(fromPoint, depth + 1, levels, radius, emitter, !dir, ease);
|
||||||
return fromPoint;
|
return fromPoint;
|
||||||
}, { weight: false, swapdir: false });
|
}, {
|
||||||
|
weight: innerFirst,
|
||||||
|
swapdir: false
|
||||||
|
});
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1098,10 +1123,10 @@ function getZClearPath(terrain, x1, y1, x2, y2, z, zadd, off, over) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check.reverse();
|
check.reverse();
|
||||||
|
let p1 = newPoint(x1, y1);
|
||||||
|
let p2 = newPoint(x2, y2);
|
||||||
for (let i = 0; i < check.length; i++) {
|
for (let i = 0; i < check.length; i++) {
|
||||||
let data = check[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();
|
let int = data.tops.map(p => p.intersections(p1, p2, true)).flat();
|
||||||
if (int.length) {
|
if (int.length) {
|
||||||
maxz = Math.max(maxz, data.z + zadd + over);
|
maxz = Math.max(maxz, data.z + zadd + over);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { base, util } from '../../../geo/base.js';
|
||||||
import { newLine } from '../../../geo/line.js';
|
import { newLine } from '../../../geo/line.js';
|
||||||
import { newPoint } from '../../../geo/point.js';
|
import { newPoint } from '../../../geo/point.js';
|
||||||
import { newPolygon } from '../../../geo/polygon.js';
|
import { newPolygon } from '../../../geo/polygon.js';
|
||||||
|
import { newSlopeFromAngle } from '../../../geo/slope.js';
|
||||||
import { polygons as POLY } from '../../../geo/polygons.js';
|
import { polygons as POLY } from '../../../geo/polygons.js';
|
||||||
import { setSliceTracker } from '../../core/slice.js';
|
import { setSliceTracker } from '../../core/slice.js';
|
||||||
import { ops as OPS } from './ops.js';
|
import { ops as OPS } from './ops.js';
|
||||||
|
|
@ -86,6 +87,8 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
||||||
bottom_z, bottom_cut
|
bottom_z, bottom_cut
|
||||||
}, 3);
|
}, 3);
|
||||||
|
|
||||||
|
// console.log({ track, bounds, stock, workarea });
|
||||||
|
|
||||||
return structuredClone(workarea);
|
return structuredClone(workarea);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -383,11 +386,11 @@ export function addDogbones(poly, dist, reverse) {
|
||||||
let bdiff = ((adiff < 0 ? (180 - adiff) : (180 + adiff)) / 2) + 180;
|
let bdiff = ((adiff < 0 ? (180 - adiff) : (180 + adiff)) / 2) + 180;
|
||||||
if (!open || (i > 1 && i < length)) {
|
if (!open || (i > 1 && i < length)) {
|
||||||
if (isCW && adiff > 45) {
|
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.projectOnSlope(newa, dist));
|
||||||
newpts.push(lastpt.clone());
|
newpts.push(lastpt.clone());
|
||||||
} else if (!isCW && adiff < -45) {
|
} 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.projectOnSlope(newa, dist));
|
||||||
newpts.push(lastpt.clone());
|
newpts.push(lastpt.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -657,9 +660,9 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
||||||
// keeps the z bottom relative to the part when z align changes
|
// keeps the z bottom relative to the part when z align changes
|
||||||
zBottom = isIndexed ? camZBottom : camZBottom - zbOff;
|
zBottom = isIndexed ? camZBottom : camZBottom - zbOff;
|
||||||
|
|
||||||
let slicerOpts = { flatoff: 0.001 }
|
let slicerOpts = { flatoff: 0.001 };
|
||||||
let slicer = new cam_slicer(widget, slicerOpts);
|
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)
|
precision = Math.max(0, precision)
|
||||||
let intervals = (precision == 0) ? [] : slicer.interval(
|
let intervals = (precision == 0) ? [] : slicer.interval(
|
||||||
|
|
@ -669,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
|
let indices = [...new Set(zees
|
||||||
.map(kv => parseFloat(kv).round(5))
|
.map(kv => parseFloat(kv).round(5))
|
||||||
.filter(z => z !== null)
|
.filter(z => z !== null)
|
||||||
)]
|
)];
|
||||||
let centerDiff = diam * 0.1,
|
let centerDiff = diam * 0.1,
|
||||||
area = (diam / 2) * (diam / 2) * Math.PI,
|
area = (diam / 2) * (diam / 2) * Math.PI,
|
||||||
circles = [],
|
circles = [],
|
||||||
|
|
@ -689,19 +692,20 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
||||||
for (let [i, slice] of slices.entries()) {
|
for (let [i, slice] of slices.entries()) {
|
||||||
for (let top of slice.tops) {
|
for (let top of slice.tops) {
|
||||||
// console.log("slicing",slice.z,top)
|
// console.log("slicing",slice.z,top)
|
||||||
slice.shadow = computeShadowAt(widget, slice.z, 0)
|
slice.shadow = computeShadowAt(widget, slice.z, 0);
|
||||||
let inner = top.inner;
|
let inner = top.inner;
|
||||||
if (!inner) { //no holes
|
if (!inner) { //no holes
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (let poly of inner) {
|
for (let poly of inner) {
|
||||||
if (poly.points.length < 7) continue;
|
if (poly.points.length < 7) continue;
|
||||||
|
|
||||||
let center = poly.calcCircleCenter();
|
let center = poly.calcCircleCenter();
|
||||||
center.area = poly.area();
|
center.area = poly.area();
|
||||||
center.overlapping = [center]
|
center.overlapping = [center];
|
||||||
center.depth = 0;
|
center.depth = 0;
|
||||||
// console.log("center",center)
|
// console.log("center",center)
|
||||||
if (poly.circularity() < 0.985) {
|
if (poly.circularity() < 0.98) {
|
||||||
// if not circular, don't add to holes
|
// if not circular, don't add to holes
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -712,7 +716,7 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
||||||
}
|
}
|
||||||
let overlap = false;
|
let overlap = false;
|
||||||
for (let [i, circle] of circles.entries()) {
|
for (let [i, circle] of circles.entries()) {
|
||||||
let dist = circle.distTo2D(center)
|
let dist = circle.distTo2D(center);
|
||||||
|
|
||||||
// //if on the same xy point,
|
// //if on the same xy point,
|
||||||
if (dist <= centerDiff) {
|
if (dist <= centerDiff) {
|
||||||
|
|
@ -726,19 +730,19 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
||||||
if (!overlap) circles.push(center);
|
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()) {
|
for (let [i, c] of circles.entries()) {
|
||||||
let overlapping = c.overlapping
|
let overlapping = c.overlapping;
|
||||||
|
|
||||||
let last = overlapping.shift();
|
let last = overlapping.shift();
|
||||||
while (overlapping.length) {
|
while (overlapping.length) {
|
||||||
let circ = overlapping.shift();
|
let circ = overlapping.shift();
|
||||||
let aveArea = (circ.area + last.area) / 2;
|
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
|
if (areaDelta < aveArea * 0.05) { // if area delta less than 5% of average area
|
||||||
//keep top circle selected
|
//keep top circle selected
|
||||||
last.depth = last.z - circ.z;
|
last.depth = last.z - circ.z;
|
||||||
|
|
@ -748,24 +752,25 @@ export async function holes(settings, widget, individual, rec, onProgress) {
|
||||||
last = circ;
|
last = circ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (last.depth != 0) drills.push(last) //add last circle
|
if (last.depth != 0) drills.push(last); // add last circle
|
||||||
onProgress(0.75 + (i / circles.length * 0.25), "assemble holes")
|
onProgress(0.75 + (i / circles.length * 0.25), "assemble holes");
|
||||||
}
|
}
|
||||||
drills.forEach(h => {
|
drills.forEach(h => {
|
||||||
if (rec.fromTop) {
|
if (rec.fromTop) {
|
||||||
// set z top if selected
|
// set z top if selected
|
||||||
h.depth += wztop - h.z
|
h.depth += wztop - h.z;
|
||||||
h.z = wztop
|
h.z = wztop;
|
||||||
}
|
}
|
||||||
delete h.overlapping //for encoding
|
delete h.overlapping; //for encoding
|
||||||
h.diam = toolDiam // for mesh generation
|
h.diam = toolDiam; // for mesh generation
|
||||||
h.selected = (!individual && Math.abs(h.area - area) <= area * 0.05); //for same size selection
|
h.selected = (!individual && Math.abs(h.area - area) <= area * 0.05); // for same size selection
|
||||||
})
|
})
|
||||||
|
|
||||||
// console.log("unfiltered circles",circles)
|
console.log("unfiltered circles",circles);
|
||||||
// console.log("drills",drills)
|
console.log("drills",drills);
|
||||||
drills = drills.filter(drill => drill.depth > 0)
|
|
||||||
widget.shadowedDrills = shadowedDrills
|
drills = drills.filter(drill => drill.depth > 0);
|
||||||
|
widget.shadowedDrills = shadowedDrills;
|
||||||
widget.drills = drills;
|
widget.drills = drills;
|
||||||
return drills;
|
return drills;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { $ } from '../../../moto/webui.js';
|
||||||
import { api } from '../../core/api.js';
|
import { api } from '../../core/api.js';
|
||||||
import { consts } from '../../core/consts.js';
|
import { consts } from '../../core/consts.js';
|
||||||
import { settings as setconf } from '../../core/settings.js';
|
import { settings as setconf } from '../../core/settings.js';
|
||||||
|
import { calcTaperLength, calcTaperAngle } from './tool.js';
|
||||||
|
|
||||||
const DEG2RAD = Math.PI / 180;
|
const DEG2RAD = Math.PI / 180;
|
||||||
|
|
||||||
|
|
@ -44,7 +45,7 @@ function selectTool(tool) {
|
||||||
ui.toolMetric.checked = tool.metric;
|
ui.toolMetric.checked = tool.metric;
|
||||||
ui.toolType.selectedIndex = toolNames.indexOf(tool.type);
|
ui.toolType.selectedIndex = toolNames.indexOf(tool.type);
|
||||||
if (tool === 'tapermill') {
|
if (tool === 'tapermill') {
|
||||||
ui.toolTaperAngle.value = kiri.driver.CAM.calcTaperAngle(
|
ui.toolTaperAngle.value = calcTaperAngle(
|
||||||
(tool.flute_diam - tool.taper_tip) / 2, tool.flute_len
|
(tool.flute_diam - tool.taper_tip) / 2, tool.flute_len
|
||||||
).round(1);
|
).round(1);
|
||||||
} else if(tool === 'drill'){
|
} else if(tool === 'drill'){
|
||||||
|
|
@ -208,16 +209,15 @@ export function updateTool(ev) {
|
||||||
selectedTool.metric = ui.toolMetric.checked;
|
selectedTool.metric = ui.toolMetric.checked;
|
||||||
selectedTool.type = toolNames[ui.toolType.selectedIndex];
|
selectedTool.type = toolNames[ui.toolType.selectedIndex];
|
||||||
if (selectedTool.type === 'tapermill') {
|
if (selectedTool.type === 'tapermill') {
|
||||||
const CAM = kiri.driver.CAM;
|
|
||||||
const rad = (selectedTool.flute_diam - selectedTool.taper_tip) / 2;
|
const rad = (selectedTool.flute_diam - selectedTool.taper_tip) / 2;
|
||||||
if (ev && ev.target === ui.toolTaperAngle) {
|
if (ev && ev.target === ui.toolTaperAngle) {
|
||||||
const angle = parseFloat(ev.target.value);
|
const angle = parseFloat(ev.target.value);
|
||||||
const len = CAM.calcTaperLength(rad, angle * DEG2RAD);
|
const len = calcTaperLength(rad, angle * DEG2RAD);
|
||||||
selectedTool.flute_len = len;
|
selectedTool.flute_len = len;
|
||||||
ui.toolTaperAngle.value = angle.round(1);
|
ui.toolTaperAngle.value = angle.round(1);
|
||||||
ui.toolFluteLen.value = selectedTool.flute_len.round(4);
|
ui.toolFluteLen.value = selectedTool.flute_len.round(4);
|
||||||
} else {
|
} else {
|
||||||
ui.toolTaperAngle.value = CAM.calcTaperAngle(rad, selectedTool.flute_len).round(1);
|
ui.toolTaperAngle.value = calcTaperAngle(rad, selectedTool.flute_len).round(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ui.toolTaperAngle.value = 0;
|
ui.toolTaperAngle.value = 0;
|
||||||
|
|
@ -318,7 +318,7 @@ function _showTools() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const record = {
|
const record = {
|
||||||
version: kiri.version,
|
version: api.version,
|
||||||
tools: api.conf.get().tools,
|
tools: api.conf.get().tools,
|
||||||
time: Date.now()
|
time: Date.now()
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
import { util } from '../../../geo/base.js';
|
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_prepare } from './prepare.js';
|
||||||
import { fdm_export } from './export.js';
|
import { fdm_export } from './export.js';
|
||||||
|
|
||||||
|
|
@ -39,6 +39,7 @@ export const FDM = {
|
||||||
extrudePerMM,
|
extrudePerMM,
|
||||||
extrudeMM,
|
extrudeMM,
|
||||||
slice: fdm_slice,
|
slice: fdm_slice,
|
||||||
|
sliceAll: sliceAll,
|
||||||
prepare: fdm_prepare,
|
prepare: fdm_prepare,
|
||||||
export: fdm_export
|
export: fdm_export
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import '../../../ext/jspoly.js';
|
import '../../../ext/jspoly.js';
|
||||||
import { base } from '../../../geo/base.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 { polygons as POLY, fillArea } from '../../../geo/polygons.js';
|
||||||
import { getRangeParameters } from './driver.js';
|
import { getRangeParameters } from './driver.js';
|
||||||
import { slicer } from '../../../geo/slicer.js';
|
import { slicer } from '../../../geo/slicer.js';
|
||||||
|
|
@ -163,25 +163,47 @@ function thin_type_2(params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function thin_type_3(params) {
|
function thin_type_3(params) {
|
||||||
|
// offsetN usually = extrusion width (nozzle diameter)
|
||||||
let { z, top, count, offsetN, last, gaps } = params;
|
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);
|
let { noodle, remain } = top.poly.noodle(offsetN * count);
|
||||||
top.shells = noodle;
|
|
||||||
|
// top.shells = noodle;
|
||||||
top.gaps = last = remain;
|
top.gaps = last = remain;
|
||||||
|
|
||||||
let thin = top.thin_fill;
|
let thin = top.thin_fill = [];
|
||||||
let scale = 5000;
|
let sparse = top.fill_sparse = [];
|
||||||
|
let scale = 1000;
|
||||||
let minR = offsetN / 2;
|
let minR = offsetN / 2;
|
||||||
let maxR = minR * 1.5;
|
let maxR = minR * 1.5;
|
||||||
let minSpur = 0; // offsetN * 3;
|
let minSpur = offsetN * 2;
|
||||||
|
|
||||||
|
let showNoodle = false;
|
||||||
|
let mergeChains = true;
|
||||||
|
let interpolateShortSpur = true;
|
||||||
|
let showChainRawPoints = false;
|
||||||
|
let showChainInterpPoints = true;
|
||||||
|
let showMidline = true;
|
||||||
|
let showMidPoints = false;
|
||||||
|
let showMidNormals = false;
|
||||||
|
let showMidRadii = true;
|
||||||
|
let showNexuses = false;
|
||||||
|
|
||||||
let pointMap = new Map();
|
let pointMap = new Map();
|
||||||
let lineMap = 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}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert point to deduplicated / unique point record
|
||||||
function pointToRec(point) {
|
function pointToRec(point) {
|
||||||
let key = `${(point.x*100)|0}|${(point.y*100)|0}`;
|
let key = point2key(point);
|
||||||
let rec = pointMap.get(key);
|
let rec = pointMap.get(key);
|
||||||
if (!rec) {
|
if (!rec) {
|
||||||
rec = {
|
rec = {
|
||||||
|
|
@ -196,6 +218,7 @@ function thin_type_3(params) {
|
||||||
return rec;
|
return rec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert line to unique line record. matches reversed and duplicate lines
|
||||||
function pointsToLine(p0, p1) {
|
function pointsToLine(p0, p1) {
|
||||||
p0 = pointToRec(p0);
|
p0 = pointToRec(p0);
|
||||||
p1 = pointToRec(p1);
|
p1 = pointToRec(p1);
|
||||||
|
|
@ -219,6 +242,9 @@ function thin_type_3(params) {
|
||||||
|
|
||||||
let chains = [];
|
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) {
|
for (let poly of noodle) {
|
||||||
// scale the polygon (including inners) so the medial axis code works properly
|
// scale the polygon (including inners) so the medial axis code works properly
|
||||||
poly = poly.clone(true).scale({ x:scale, y:scale, z:scale });
|
poly = poly.clone(true).scale({ x:scale, y:scale, z:scale });
|
||||||
|
|
@ -231,69 +257,84 @@ function thin_type_3(params) {
|
||||||
for (let { point0, point1 } of ma) {
|
for (let { point0, point1 } of ma) {
|
||||||
pointsToLine(point0, point1);
|
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);
|
// connect segments into chains
|
||||||
// filter out lines where both points' radii are under the
|
for (let seg of lineMap.values()) {
|
||||||
// minR threshold. shorten and fixup lines where minR falls
|
let { p0, p1 } = seg;
|
||||||
// somewhere along the gradient between ends.
|
let len = seg.len = pointDist(p0, p1);
|
||||||
if (p0.r < minR || p1.r < minR) {
|
// filter out lines where both points' radii are under the
|
||||||
if (p0.r > minR && len > minSpur) {
|
// minR threshold. shorten and fixup lines where minR falls
|
||||||
// move p1 toward p0 until minR met
|
// somewhere along the gradient between ends.
|
||||||
let pct = (minR - p1.r) / (p0.r - p1.r);
|
if (interpolateShortSpur)
|
||||||
let dx = p1.x - p0.x;
|
if (p0.r < minR || p1.r < minR) {
|
||||||
let dy = p1.y - p0.y;
|
if (p0.r > minR && len > minSpur) {
|
||||||
p1.x -= dx * pct;
|
// move p1 toward p0 until minR met
|
||||||
p1.y -= dy * pct;
|
let pct = (minR - p1.r) / (p0.r - p1.r);
|
||||||
p1.r = minR;
|
let dx = p1.x - p0.x;
|
||||||
pointMap.delete(p1.key);
|
let dy = p1.y - p0.y;
|
||||||
} else if (p1.r > minR && len > minSpur) {
|
p1.x -= dx * pct;
|
||||||
// move p0 toward p1 until minR met
|
p1.y -= dy * pct;
|
||||||
let pct = (minR - p0.r) / (p1.r - p0.r);
|
p1.r = minR;
|
||||||
let dx = p0.x - p1.x;
|
pointMap.delete(p1.key);
|
||||||
let dy = p0.y - p1.y;
|
} else if (p1.r > minR && len > minSpur) {
|
||||||
p0.x -= dx * pct;
|
// move p0 toward p1 until minR met
|
||||||
p0.y -= dy * pct;
|
let pct = (minR - p0.r) / (p1.r - p0.r);
|
||||||
p0.r = minR;
|
let dx = p0.x - p1.x;
|
||||||
pointMap.delete(p0.key);
|
let dy = p0.y - p1.y;
|
||||||
} else {
|
p0.x -= dx * pct;
|
||||||
lineMap.delete(seg.key);
|
p0.y -= dy * pct;
|
||||||
continue;
|
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 each line segment either add it to a chain or start a new chain
|
||||||
for (let chain of chains) {
|
let add;
|
||||||
if (p0.count === 2 && p0.key === chain[0].key) {
|
for (let chain of chains) {
|
||||||
chain.splice(0,0,{...p1, len});
|
if (p0.count === 2 && p0 === chain[0]) {
|
||||||
} else if (p0.count === 2 && p0.key === chain.peek().key) {
|
// prefix chain with p1
|
||||||
chain.peek().len = len;
|
chain.splice(0,0,p1);
|
||||||
chain.push({...p1});
|
add = true;
|
||||||
add = true;
|
} else if (p0.count === 2 && p0 === chain.peek()) {
|
||||||
} else if (p1.count === 2 && p1.key === chain[0].key) {
|
// append chain with p1
|
||||||
chain.splice(0,0,{...p0, len});
|
chain.push(p1);
|
||||||
add = true;
|
add = true;
|
||||||
} else if (p1.count === 2 && p1.key === chain.peek().key) {
|
} else if (p1.count === 2 && p1 === chain[0]) {
|
||||||
chain.peek().len = len;
|
// prefix chain with p0
|
||||||
chain.push({...p0});
|
chain.splice(0,0,p0);
|
||||||
add = true;
|
add = true;
|
||||||
}
|
} else if (p1.count === 2 && p1 === chain.peek()) {
|
||||||
if (add) {
|
// append chain with p0
|
||||||
chain.total += len;
|
chain.push(p0);
|
||||||
break;
|
add = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!add) {
|
if (add) {
|
||||||
chains.push([ {...p0 ,len}, {...p1} ]);
|
chain.total += len;
|
||||||
chains.peek().total = len;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!add) {
|
||||||
|
chains.push([ p0, p1 ]);
|
||||||
|
chains.peek().total = len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let showMid = false;
|
// todo: merge chains at next of 3 or more preferencing
|
||||||
let showCross = false;
|
// merging longer chains. then shorten remaining nexus chain
|
||||||
let showRad = true;
|
// intersections by minR
|
||||||
|
|
||||||
|
// render inset "noodle"
|
||||||
|
if (showNoodle) {
|
||||||
|
top.shells.appendAll(noodle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// divide a segment/redius into 1 or more equal subsegments
|
||||||
function div(cr) {
|
function div(cr) {
|
||||||
if (cr <= maxR) {
|
if (cr <= maxR) {
|
||||||
return [ cr ];
|
return [ cr ];
|
||||||
|
|
@ -311,71 +352,249 @@ function thin_type_3(params) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add chains to thinwall output for visualization
|
// filter chains that are too short from consideration
|
||||||
if (showRad) {
|
chains = chains.filter(c => c.total >= minR);
|
||||||
top.shells = [];
|
chains.sort((c1,c2) => c2.total - c1.total);
|
||||||
}
|
|
||||||
for (let chain of chains.filter(c => c.total >= minR)) {
|
function merge1chain() {
|
||||||
// chain reduction by merging short segments
|
let clen = chains.length;
|
||||||
let end = chain[0];
|
for (let i=0; i<clen; i++) {
|
||||||
let nuchain = [ end ];
|
let ci = chains[i];
|
||||||
for (let point of chain.slice(1)) {
|
if (!ci) continue;
|
||||||
if (point.len === undefined || point.len + end.len >= minR) {
|
for (let j=i+1; j<clen; j++) {
|
||||||
nuchain.push(point);
|
let cj = chains[j];
|
||||||
end = point;
|
if (!cj) continue;
|
||||||
} else {
|
if (ci[0] === cj[0]) {
|
||||||
end.len += point.len;
|
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());
|
||||||
|
ci.total += cj.total;
|
||||||
|
cj.merged = true;
|
||||||
|
chains[j] = undefined;
|
||||||
|
return true;
|
||||||
|
} else if (ci.peek() === cj[0]) {
|
||||||
|
ci.appendAll(cj);
|
||||||
|
ci.total += cj.total;
|
||||||
|
cj.merged = true;
|
||||||
|
chains[j] = undefined;
|
||||||
|
return true;
|
||||||
|
} else if (ci[0] === cj.peek()) {
|
||||||
|
cj.appendAll(ci);
|
||||||
|
cj.total += ci.total;
|
||||||
|
ci.merged = true;
|
||||||
|
chains[i] = undefined;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chain = nuchain;
|
return false;
|
||||||
// emit chain with subdivision for long segments
|
}
|
||||||
for (let i=0; i<chain.length-1; i++) {
|
|
||||||
// medial axis segment
|
// map chains to nexus via endpoints
|
||||||
if (showMid) {
|
let nexus = {};
|
||||||
thin.push(new Point(chain[i].x, chain[i].y));
|
function addEp(point, chain) {
|
||||||
thin.push(new Point(chain[i+1].x, chain[i+1].y));
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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 = (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];
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
// compute medial axis segment cross section
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (showNexuses)
|
||||||
|
for (let rec of Object.values(nexus)) {
|
||||||
|
let { point } = rec;
|
||||||
|
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) {
|
||||||
|
// zi += 0.1;
|
||||||
|
if (showChainRawPoints)
|
||||||
|
for (let pt of chain) {
|
||||||
|
top.shells.push(newPolygon().centerCircle(pt, pt.r ?? 0.1, 10));
|
||||||
|
}
|
||||||
|
// draw medial axis chain
|
||||||
|
let { closed, length } = chain;
|
||||||
|
let term = closed ? length : length - 1;
|
||||||
|
let segs = [];
|
||||||
|
for (let i=0; i<term; i++) {
|
||||||
let p0 = chain[i];
|
let p0 = chain[i];
|
||||||
let p1 = chain[i+1];
|
let p1 = chain[(i+1) % length];
|
||||||
|
let len = pointDist(p0, p1);
|
||||||
|
// medial axis segment
|
||||||
|
if (showMidline) {
|
||||||
|
thin.push(new Point(p0.x, p0.y, zi));
|
||||||
|
thin.push(new Point(p1.x, p1.y, zi));
|
||||||
|
}
|
||||||
|
// compute medial axis segment normal
|
||||||
|
let offs = len <= offsetN ? [ 0 ] : base.util.lerp(0, len, offsetN, true);
|
||||||
|
let step = 1 / offs.length;
|
||||||
let dr = (p1.r - p0.r);
|
let dr = (p1.r - p0.r);
|
||||||
let dx = (p1.x - p0.x);
|
let dx = (p1.x - p0.x);
|
||||||
let dy = (p1.y - p0.y);
|
let dy = (p1.y - p0.y);
|
||||||
let ndx = dx / p0.len;
|
let ndx = dx / len;
|
||||||
let ndy = dy / p0.len;
|
let ndy = dy / len;
|
||||||
let offs = p0.len <= offsetN ? [ 0.5 ] : base.util.lerp(0, p0.len, offsetN, true);
|
segs.push({ p0, p1, offs, step, dr, dx, dy, ndx, ndy });
|
||||||
let indx = 0;
|
}
|
||||||
let step = 1 / offs.length;
|
console.log({ segs });
|
||||||
// interpolate across the length of the chain segment
|
// compute chain subdivisions
|
||||||
for (let off of offs) {
|
let slen = segs.length;
|
||||||
let inc = (indx++ * step);
|
for (let si=0; si<slen; si++) {
|
||||||
let cr = p0.r + dr * inc;
|
let seg = segs[si];
|
||||||
let cx = p0.x + dx * inc;
|
let segp = segs[(si - 1 + slen) % slen];
|
||||||
let cy = p0.y + dy * inc;
|
let { p0, p1, offs, step, dr, dx, dy, ndx, ndy } = seg;
|
||||||
let xo = ndx * cr;
|
let first = si === 0;
|
||||||
let yo = ndy * cr;
|
let last = si === slen - 1;
|
||||||
if (showCross) {
|
let mid = !(first || last);
|
||||||
thin.push(new Point(cx + yo, cy - xo));
|
// for length 2, first and last segment are the same
|
||||||
thin.push(new Point(cx - yo, cy + xo));
|
if (showMidPoints) {
|
||||||
|
if (first) {
|
||||||
|
// todo: handle closed
|
||||||
|
top.shells.push(newPolygon().centerCircle(p0, 0.08, 10));
|
||||||
}
|
}
|
||||||
if (showRad) {
|
if (mid) {
|
||||||
let circ = top.shells;
|
// mid segments
|
||||||
let pop = div(cr);
|
top.shells.push(newPolygon().centerCircle(p0, 0.08, 10));
|
||||||
let sx = cx + yo;
|
}
|
||||||
let sy = cy - xo;
|
if (last) {
|
||||||
let m = 1;
|
// todo: handle closed
|
||||||
for (let r of pop) {
|
top.shells.push(newPolygon().centerCircle(p0, 0.08, 10));
|
||||||
sx -= (r/cr) * yo * m;
|
if (closed) {
|
||||||
sy += (r/cr) * xo * m;
|
console.log('closed');
|
||||||
circ.push(newPolygon().centerCircle({ x:sx, y:sy }, r, 10));
|
} else {
|
||||||
m = 2;
|
top.shells.push(newPolygon().centerCircle(p1, 0.08, 10));
|
||||||
}
|
}
|
||||||
// console.log(pop);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// todo: handle closed
|
||||||
|
if (first) {
|
||||||
|
renderPointNormal(p0, ndx, ndy);
|
||||||
|
}
|
||||||
|
if (mid) {
|
||||||
|
renderPointNormal(p0, ((ndx + segp.ndx)/2), ((ndy + segp.ndy)/2));
|
||||||
|
}
|
||||||
|
if (last) {
|
||||||
|
renderPointNormal(p1, ndx, ndy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// interpolate across the length of the chain segment
|
||||||
|
if (showChainInterpPoints)
|
||||||
|
for (let indx = 1; indx < offs.length; indx++) {
|
||||||
|
const inc = indx * step;
|
||||||
|
const cr = p0.r + dr * inc;
|
||||||
|
const cx = p0.x + dx * inc;
|
||||||
|
const cy = p0.y + dy * inc;
|
||||||
|
const xo = ndx * cr;
|
||||||
|
const yo = ndy * cr;
|
||||||
|
if (showMidPoints) {
|
||||||
|
top.shells.push(newPolygon().centerCircle({ x:cx, y:cy }, 0.05, 10));
|
||||||
|
}
|
||||||
|
renderPointNormal({ x:cx, y:cy, r:cr }, ndx, ndy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
POLY.setZ([...thin, ...top.shells], z);
|
||||||
|
// POLY.setZ([...top.shells], z);
|
||||||
|
|
||||||
return { last, gaps };
|
return { last, gaps };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,10 @@ class Engine {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTopOffset(offset = 0) {
|
||||||
|
this.topOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
setOrigin(x, y, z) {
|
setOrigin(x, y, z) {
|
||||||
this.settings.origin = { x, y, z };
|
this.settings.origin = { x, y, z };
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -152,6 +156,7 @@ class Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
slice() {
|
slice() {
|
||||||
|
this.widget.setTopZ((this.settings?.stock?.z || 0) - (this.topOffset || 0));
|
||||||
return new Promise((accept, reject) => {
|
return new Promise((accept, reject) => {
|
||||||
client.clear();
|
client.clear();
|
||||||
client.sync([this.widget]);
|
client.sync([this.widget]);
|
||||||
|
|
|
||||||
|
|
@ -546,11 +546,12 @@ const dispatch = {
|
||||||
|
|
||||||
parse(args, send) {
|
parse(args, send) {
|
||||||
const { settings, code, type } = args;
|
const { settings, code, type } = args;
|
||||||
|
const { process } = settings;
|
||||||
const origin = settings.origin;
|
const origin = settings.origin;
|
||||||
const offset = {
|
const offset = {
|
||||||
x: origin.x,
|
x: origin.x - (process.camOriginOffX ?? 0),
|
||||||
y: -origin.y,
|
y: -origin.y - (process.camOriginOffY ?? 0),
|
||||||
z: origin.z
|
z: origin.z - (process.camOriginOffZ ?? 0)
|
||||||
};
|
};
|
||||||
const device = settings.device;
|
const device = settings.device;
|
||||||
const print = current.print = newPrint(settings, Object.values(wcache));
|
const print = current.print = newPrint(settings, Object.values(wcache));
|
||||||
|
|
|
||||||
|
|
@ -1392,14 +1392,15 @@ const prefs = {
|
||||||
scale_group_Z: true
|
scale_group_Z: true
|
||||||
},
|
},
|
||||||
space: {
|
space: {
|
||||||
|
bounds: false,
|
||||||
|
center: false,
|
||||||
|
dark: false,
|
||||||
|
floor: false,
|
||||||
|
grid: true,
|
||||||
|
select: [],
|
||||||
snap: 1,
|
snap: 1,
|
||||||
snapon: false,
|
snapon: false,
|
||||||
center: false,
|
wire: false
|
||||||
floor: false,
|
|
||||||
wire: false,
|
|
||||||
grid: true,
|
|
||||||
dark: false,
|
|
||||||
select: []
|
|
||||||
},
|
},
|
||||||
sketch: {
|
sketch: {
|
||||||
open_close: false,
|
open_close: false,
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,11 @@
|
||||||
});
|
});
|
||||||
edit.session.setTabSize(4);
|
edit.session.setTabSize(4);
|
||||||
edit.session.setUseSoftTabs(true);
|
edit.session.setUseSoftTabs(true);
|
||||||
let FDMExample = loadExample(demoFDM)
|
if (localStorage.engineCode) {
|
||||||
|
edit.session.setValue(localStorage.engineCode);
|
||||||
|
} else {
|
||||||
|
loadExample(demoFDM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function display_message(msg) {
|
function display_message(msg) {
|
||||||
$('gfoot').innerText = msg ? Object.keys(msg).join(' - ') : '';
|
$('gfoot').innerText = msg ? Object.keys(msg).join(' - ') : '';
|
||||||
|
|
@ -110,7 +114,7 @@
|
||||||
$('dfoot').style.display = 'block';
|
$('dfoot').style.display = 'block';
|
||||||
$('gfoot').style.display = '';
|
$('gfoot').style.display = '';
|
||||||
}
|
}
|
||||||
function loadExample(func){
|
function loadExample(func) {
|
||||||
let fnstr = func.toString().split("\n");
|
let fnstr = func.toString().split("\n");
|
||||||
edit.session.setValue(
|
edit.session.setValue(
|
||||||
fnstr
|
fnstr
|
||||||
|
|
@ -124,6 +128,7 @@
|
||||||
$('gfoot').style.display = 'block';
|
$('gfoot').style.display = 'block';
|
||||||
let code = edit.session.getValue();
|
let code = edit.session.getValue();
|
||||||
eval(`(function(){${code}})()`);
|
eval(`(function(){${code}})()`);
|
||||||
|
localStorage.engineCode = code;
|
||||||
}
|
}
|
||||||
function demoFDM() {
|
function demoFDM() {
|
||||||
new Engine()
|
new Engine()
|
||||||
|
|
|
||||||
|
|
@ -790,6 +790,8 @@ self.lang['en-us'] = {
|
||||||
ou_conv_l: ["milling direction","uncheck for 'climb'"],
|
ou_conv_l: ["milling direction","uncheck for 'climb'"],
|
||||||
ou_depf_s: "depth first",
|
ou_depf_s: "depth first",
|
||||||
ou_depf_l: ["optimize pocket cuts","with depth priority"],
|
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_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_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",
|
ou_forz_s: "force z max",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue