diff --git a/notes.md b/notes.md index 9174e69e..89a90e97 100644 --- a/notes.md +++ b/notes.md @@ -74,6 +74,8 @@ * `B` need to force cut line at synthetic z bottom (midlines, etc) * `B` contour does not honor clip to stock +* `F` add pause operation with optional gcode +* `F` add option for op-major ordering (instead of part major) * `F` animate only selected range (as an option) * `F` add `plunge max` to contouring that can override z feed limit * `F` roughing flats should be constrained to flat region, not create a layer diff --git a/src/kiri-mode/cam/ops.js b/src/kiri-mode/cam/ops.js index 5d8ae91a..106dfe1d 100644 --- a/src/kiri-mode/cam/ops.js +++ b/src/kiri-mode/cam/ops.js @@ -1005,10 +1005,10 @@ class OpPocket extends CamOp { } } } - let surfaces = op.surfaces[widget.id]; + let surfaces = op.surfaces[widget.id] || []; let vert = widget.getVertices().array.map(v => v.round(4)); let prog = 0, pinc= 1 / surfaces.length; - for (let surface of surfaces || []) { + for (let surface of surfaces) { let outline = []; let faces = CAM.surface_find(widget, [surface]); let zmin = Infinity; @@ -1030,7 +1030,7 @@ class OpPocket extends CamOp { if (false) newSliceOut(zmin).output() .setLayer("pocket area", {line: 0x1188ff}, false) .addPolys(outline) - clearZ(outline, zmin, down); + clearZ(outline, zmin + 0.0001, down); progress(prog += pinc, "pocket"); } } diff --git a/src/load/3mf.js b/src/load/3mf.js index 02477cd2..a72e328b 100644 --- a/src/load/3mf.js +++ b/src/load/3mf.js @@ -15,19 +15,54 @@ load.TMF = { parseAsync }; +load.XML = { + query +}; + let { BufferAttribute, Matrix4 } = THREE; -function query(node, path, fn) { - let collect = false; - let match = path[0]; - if (match[0] === '+') { - match = match.slice(1); - collect = true; +// simple query api for map structures +function query_map(node, path, fn) { + let collect = {}; + let match = path[0].split('|').map(key => { + if (key[0] === '+') { + key = key.slice(1); + collect[key] = true; + } + return key; + }); + for (let [tag, value] of Object.entries(node)) { + if (!Array.isArray(value)) { + value = [ value ]; + } + if (match.indexOf(tag) >= 0) { + for (let node of value) { + if (collect[tag]) { + fn(tag, node); + } + if (path.length > 1) { + query(node, path.slice(1), fn); + } + } + } } +} + +// simple query api for xml structures +function query(node, path, fn) { + let collect = {}; + let match = path[0].split('|').map(key => { + if (key[0] === '+') { + key = key.slice(1); + collect[key] = true; + } + return key; + }); for (let child of [...node.childNodes]) { - if (child.tagName === match) { - if (collect) { - fn(match, child); + let { tagName } = child; + if (match.indexOf(tagName) >= 0) { + if (collect[tagName]) { + fn(tagName, child); } if (path.length > 1) { query(child, path.slice(1), fn); diff --git a/src/load/file.js b/src/load/file.js index 6aab2945..9a4f28e3 100644 --- a/src/load/file.js +++ b/src/load/file.js @@ -15,7 +15,47 @@ if (load.File) return; // dep: load.png gapp.register('load.file', []); +const types = { + stl(data, file, resolve, reject) { + resolve([{ + mesh: new load.STL().parse(data), file + }]); + }, + + obj(data, file, resolve, reject) { + resolve(load.OBJ.parse(data).map(m => { + return { mesh: m.toFloat32(), file: nameOf(file, m.name, 1) } + })); + }, + + "3mf"(data, file, resolve, reject) { + let i = 1; + load.TMF.parseAsync(data).then((meshes) => { + resolve(meshes.map(m => { + return { mesh: m.faces.toFloat32(), file: nameOf(file, m.name, i++) } + })); + }); + }, + + svg(data, file, resolve, reject) { + resolve(load.SVG.parse(data).map(m => { return {mesh: m.toFloat32(), file} })); + }, + + png(data, file, resolve, reject) { + load.PNG.parse(data, { + ...opt, + done(data) { + resolve({ mesh: data, file }); + } + }); + } +}; + +const as_buffer = [ "stl", "png", "3mf" ]; + load.File = { + types, + as_buffer, load_data, load: load_file }; @@ -31,39 +71,11 @@ function nameOf(file, part, i) { function load_data(data, file, ext, opt = {}) { ext = ext || name.toLowerCase().split('.').pop(); return new Promise((resolve, reject) => { - let i = 1; - switch (ext) { - case "stl": - resolve([{ - mesh: new load.STL().parse(data), file - }]); - break; - case "obj": - resolve(load.OBJ.parse(data).map(m => { - return { mesh: m.toFloat32(), file: nameOf(file, m.name, i++) } - })); - break; - case "3mf": - load.TMF.parseAsync(data).then((meshes) => { - resolve(meshes.map(m => { - return { mesh: m.faces.toFloat32(), file: nameOf(file, m.name, i++) } - })); - }); - break; - case "svg": - resolve(load.SVG.parse(data).map(m => { return {mesh: m.toFloat32(), file} })); - break; - case "png": - load.PNG.parse(data, { - ...opt, - done(data) { - resolve({ mesh: data, file }); - } - }); - break; - default: - reject(`unknown file type: "${ext}" from ${file}`); - break; + let fn = types[ext]; + if (fn) { + fn(data, file, resolve, reject); + } else { + reject(`unknown file type: "${ext}" from ${file}`); } }); } @@ -85,7 +97,7 @@ function load_file(file, opt) { .then(data => resolve(data)) .catch(e => reject(e)); }; - if (["stl","png","3mf"].indexOf(ext) >= 0) { + if (as_buffer.indexOf(ext) >= 0) { reader.readAsArrayBuffer(reader.file); } else { reader.readAsBinaryString(reader.file); diff --git a/src/main/mesh.js b/src/main/mesh.js index 8675c263..30a0f375 100644 --- a/src/main/mesh.js +++ b/src/main/mesh.js @@ -522,7 +522,8 @@ function load_files_opt(files, opt) { // add object loader function space_load(data) { - mesh.api.group.new(data.flat().map(el => new mesh.model(el))) + if (data && data.length && (data = data.flat()).length) + mesh.api.group.new(data.map(el => new mesh.model(el))) .promote() .focus(); } diff --git a/web/kiri/engine.html b/web/kiri/engine.html index 711ee395..fd2a51ac 100644 --- a/web/kiri/engine.html +++ b/web/kiri/engine.html @@ -141,7 +141,7 @@
-