From 8b215460a13c51434f0213556f03e8535cf567ec Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sat, 29 Jun 2024 23:03:40 -0400 Subject: [PATCH] complete basic gerber import conversion to mesh for milling traces --- src/add/array.js | 2 ++ src/geo/csg.js | 12 ++++++++++-- src/geo/polygon.js | 27 +++++++++++++++------------ src/kiri-mode/cam/client.js | 8 ++++---- src/kiri/platform.js | 16 ++++++++++------ src/load/gbr.js | 30 ++++++++++++++++++++++++------ 6 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/add/array.js b/src/add/array.js index c9c603ce..e40b59e2 100644 --- a/src/add/array.js +++ b/src/add/array.js @@ -233,6 +233,8 @@ Float32Array.prototype.toShared = function() { return newvert; }; +Float32Array.prototype.toFloat32 = function() { return this }; + String.prototype.reverse = function() { return this.split('').reverse().join(''); }; diff --git a/src/geo/csg.js b/src/geo/csg.js index fc14225d..2433fdf7 100644 --- a/src/geo/csg.js +++ b/src/geo/csg.js @@ -9,6 +9,14 @@ gapp.register("geo.csg", [], (root, exports) => { const { base, ext } = root; const debug = true; +function log() { + if (root?.mesh?.log) { + root.mesh.log(...arguments) + } else if (root.debug === true) { + console.log(...arguments); + } +} + const CSG = { // accepts 2 or more arguments with threejs vertex position arrays union() { @@ -27,7 +35,7 @@ const CSG = { }, moduleOp(name, op) { - mesh.log(`${name} ${arguments.length} meshes`); + log(`${name} ${arguments.length} meshes`); const args = [...arguments].slice(2).map(a => new Instance.Manifold(a)); if (args.length < 2) { throw "missing one or more meshes"; @@ -80,7 +88,7 @@ const CSG = { }; function indexVertices(pos) { - mesh.log(`indexing ${pos.length/3} vertices`); + log(`indexing ${pos.length/3} vertices`); let ipos = 0; const index = []; const vertices = []; diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 414ca8c6..c52aab73 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -1852,39 +1852,42 @@ class Polygon { } // turn 2d polygon into a 2.5D ribbon extruded in Z - ribbonZ(z = 1) { + ribbonZ(z = 1, zadd = 0, rev) { let poly = this.clone().setClockwise(); let faces = []; let points = poly.points; let length = points.length; + if (rev) { + points = points.slice().reverse(); + } for (let i=0; i { - const { open, closed, circs, rects } = load.GBR.parse(gerber); - const widget = api.widgets.all()[0]; - const stack = new kiri.Stack(widget.mesh); + api.event.on("cam.parse.gerber", opts => { + const { data, mesh } = opts; + const { open, closed, circs, rects } = load.GBR.parse(data); + const stack = new kiri.Stack(mesh || moto.space.world.newGroup()); const layers = new kiri.Layers(); for (let poly of open) { layers.setLayer("open", {line: 0xff8800}, false).addPoly(poly); diff --git a/src/kiri/platform.js b/src/kiri/platform.js index 719392bb..24fa52a5 100644 --- a/src/kiri/platform.js +++ b/src/kiri/platform.js @@ -819,17 +819,21 @@ function platformLoadFiles(files, group) { } else { odon(); } - } else if (isgbr && api.conf.get().controller.devel) { - if (api.mode.is_cam()) { - api.event.emit('cam.parse.gerber', e.target.result); + } else if (isgbr) { + if (api.conf.get().controller.devel) { + api.event.emit('cam.parse.gerber', { data: e.target.result }); } else { let mesh = load.GBR.toMesh(e.target.result); - platform.add( + let wid; + platform.add(wid = newWidget(undefined, group) - .loadVertices(mesh.toFloat32(), true) + .loadVertices(mesh, true) .saveToCatalog(e.target.file.name) ); - // api.alerts.show('Gerber import not availabe in this device mode'); + if (api.mode.is_cam()) { + // attach raw illustration + api.event.emit('cam.parse.gerber', { data: e.target.result, mesh: wid.mesh }); + } } } else if (is3mf) { let odon = function(models) { diff --git a/src/load/gbr.js b/src/load/gbr.js index 0ff4d30d..e53edd4a 100644 --- a/src/load/gbr.js +++ b/src/load/gbr.js @@ -4,24 +4,42 @@ // use: geo.polygon // use: geo.polygons // use: ext.gerber +// use: geo.csg gapp.register("load.gbr", (root, exports) => { const { load } = root; +const POLYS = base.polygons; +const CSG = base.CSG; load.GBR = { parse, toMesh }; -function toMesh(text, opt = { z: 3 }) { +function toMesh(text, opt = { z: 1 }) { const { open, closed, circs, rects } = parse(text); + const nest = base.polygons.nest([...closed, ...circs, ...rects], true); - console.log({ nest }); - const obj = []; - for (let poly of nest) { - obj.appendAll(poly.extrude(opt.z || 3)); + + const flat = POLYS.flatten(nest, [], true); + const z0 = opt.z || opt.z0 || 1; + const z1 = opt.z1 || 0.25; + + const top = []; + for (let poly of flat.filter(p => p.depth === 0)) { + top.push(poly.extrude(z0)); } - return obj; + + const mid = []; + for (let poly of flat.filter(p => p.depth === 1)) { + mid.push(poly.extrude(z1, z0 - z1)); + } + + const topMesh = CSG.fromPositionArray(top[0]); + const midMesh = mid.map(v => CSG.fromPositionArray(v)); + const subMesh = CSG.subtract(topMesh, ...midMesh); + + return CSG.toPositionArray(subMesh); } function parse(text) {