From fdfea41a48178da286b83c3143611154d1ea89a7 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sat, 29 Jun 2024 19:21:22 -0400 Subject: [PATCH] add gbr to loader classes --- src/geo/polygon.js | 23 ++++++- src/kiri-mode/cam/client.js | 99 +--------------------------- src/kiri-run/worker.js | 2 +- src/kiri/init.js | 1 - src/kiri/platform.js | 35 ++++++---- src/load/file.js | 5 ++ src/load/gbr.js | 128 ++++++++++++++++++++++++++++++++++++ src/load/svg.js | 15 +---- 8 files changed, 179 insertions(+), 129 deletions(-) create mode 100644 src/load/gbr.js diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 89645d55..414ca8c6 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -1851,8 +1851,8 @@ class Polygon { return this; } - // turn 2d polygon into closed 3d mesh faces - extrude(z = 1) { + // turn 2d polygon into a 2.5D ribbon extruded in Z + ribbonZ(z = 1) { let poly = this.clone().setClockwise(); let faces = []; let points = poly.points; @@ -1870,6 +1870,25 @@ class Polygon { return faces; } + // extrude poly (with inner voids) into 3d mesh + extrude(z = 1) { + let obj = []; + let earcut = this.earcut(); + for (let poly of earcut) { + for (let point of poly.points) { + obj.push(point.x, point.y, z); + } + for (let point of poly.points.reverse()) { + obj.push(point.x, point.y, 0); + } + } + obj.appendAll(this.ribbonZ(z)); + for (let inner of this.inner || []) { + obj.appendAll(inner.ribbonZ(z)); + } + return obj; + } + // split long straight lines into segments no longer than max // and return a new polygon segment(max = 1) { diff --git a/src/kiri-mode/cam/client.js b/src/kiri-mode/cam/client.js index f2b2269f..72c28ba6 100644 --- a/src/kiri-mode/cam/client.js +++ b/src/kiri-mode/cam/client.js @@ -5,6 +5,7 @@ // dep: kiri-mode.cam.driver // use: kiri-mode.cam.animate // use: kiri-mode.cam.animate2 +// use: load.gbr gapp.register("kiri-mode.cam.client", [], (root, exports) => { const { base, kiri } = root; @@ -116,102 +117,7 @@ CAM.init = function(kiri, api) { } api.event.on("cam.parse.gerber", gerber => { - let fact = 1000000; - let tools = {}; - let tool; - let poly; - let polys = []; - let circs = []; - let rects = []; - let last = {}; - let bounds = base.newBounds(); - let p = TracespaceParser.createParser(); - p.feed(gerber); - let r = p.results(); - let c = r.children - .filter(c => { - switch (c.type) { - case 'toolDefinition': - tools[c.code] = c.shape; - break; - case 'toolChange': - tool = { code: c.code, shape: tools[c.code] }; - break; - case 'graphic': - c.tool = tool; - return true; - } - return false; - }) - .map(r => { - return { - type: r.graphic, - tool: r.tool, - x: parseInt(r.coordinates.x), - y: parseInt(r.coordinates.y) - }; - }) - .map(r => { - const { x, y, tool, type } = r; - const pos = { x:x/fact, y:y/fact, z: 0}; - bounds.update(pos); - switch (type) { - case 'move': - if (true && poly && last.x === x && last.y === y && last.tool === tool) { - // console.log('continuation'); - } else { - poly = base.newPolygon().setOpen().add(pos.x, pos.y, 0); - poly.tool = tool; - polys.push(poly); - } - break; - case 'segment': - poly.add(pos.x, pos.y, 0); - break; - case 'shape': - poly = undefined; - let { shape } = tool; - switch (shape.type) { - case 'obround': - if (shape.xSize === shape.ySize) { - circs.push(base.newPolygon().centerCircle(pos, shape.xSize / 2, 20)); - } else { - console.log('TODO', shape); - } - break; - case 'circle': - circs.push(base.newPolygon().centerCircle(pos, shape.diameter / 2, 20)); - break; - case 'rectangle': - rects.push(base.newPolygon().centerRectangle(pos, shape.xSize, shape.ySize)); - break; - default: - console.log('TODO', shape); - break; - } - break; - } - last = { x, y, tool }; - return r; - }); - const { minx, maxx, miny, maxy } = bounds; - for (let poly of [...polys, ...circs, ...rects]) { - poly.move({ - x: -((maxx-minx)/2 + minx), - y: -((maxy-miny)/2 + miny), - z: 0 - }); - } - const open = []; - const closed = []; - for (let poly of polys) { - if (poly.appearsClosed()) { - closed.push(...poly.simplify()); - } else { - open.push(poly); - } - } - console.log({ open, closed, circs, rects }); + const { open, closed, circs, rects } = load.GBR.parse(gerber); const widget = api.widgets.all()[0]; const stack = new kiri.Stack(widget.mesh); const layers = new kiri.Layers(); @@ -228,7 +134,6 @@ CAM.init = function(kiri, api) { layers.setLayer("rects", {line: 0x0000ff}, false).addPoly(poly); } stack.addLayers(layers); - console.log(r, tools); }); api.event.on("widget.add", widget => { diff --git a/src/kiri-run/worker.js b/src/kiri-run/worker.js index 576d2e0d..c6f6c36f 100644 --- a/src/kiri-run/worker.js +++ b/src/kiri-run/worker.js @@ -40,7 +40,7 @@ const POLY = polygons; let debug = self.debug === true, ccvalue = this.navigator ? navigator.hardwareConcurrency || 0 : 0, - concurrent = self.Worker && ccvalue > 3 ? ccvalue - 1 : 0, + concurrent = Math.min(4, self.Worker && ccvalue > 3 ? ccvalue - 1 : 0), current = self.worker = { print: null, snap: null, diff --git a/src/kiri/init.js b/src/kiri/init.js index ac916121..070769af 100644 --- a/src/kiri/init.js +++ b/src/kiri/init.js @@ -12,7 +12,6 @@ // dep: kiri.selection // use: kiri.tools // use: kiri.pack -// use: ext.gerber gapp.register("kiri.init", [], (root, exports) => { const { base, kiri } = root; diff --git a/src/kiri/platform.js b/src/kiri/platform.js index 0fc195e1..719392bb 100644 --- a/src/kiri/platform.js +++ b/src/kiri/platform.js @@ -11,6 +11,7 @@ // dep: load.stl // dep: load.obj // dep: load.3mf +// dep: load.gbr // use: kiri-mode.cam.tool gapp.register("kiri.platform", [], (root, exports) => { @@ -753,18 +754,18 @@ function platformLoadFiles(files, group) { const file = files[i], reader = new FileReader(), lower = files[i].name.toLowerCase(), - israw = lower.indexOf(".raw") > 0 || lower.indexOf('.') < 0, - isstl = lower.indexOf(".stl") > 0, - isobj = lower.indexOf(".obj") > 0, - is3mf = lower.indexOf(".3mf") > 0, - issvg = lower.indexOf(".svg") > 0, - ispng = lower.indexOf(".png") > 0, - isjpg = lower.indexOf(".jpg") > 0, - isgcode = lower.indexOf(".gcode") > 0 || lower.indexOf(".nc") > 0, - isset = lower.indexOf(".b64") > 0 || lower.indexOf(".km") > 0, - iskmz = lower.indexOf(".kmz") > 0, - isini = lower.indexOf(".ini") > 0, - isgrb = lower.indexOf(".grb"); + israw = lower.endsWith(".raw") || lower.indexOf('.') < 0, + isstl = lower.endsWith(".stl"), + isobj = lower.endsWith(".obj"), + is3mf = lower.endsWith(".3mf"), + issvg = lower.endsWith(".svg"), + ispng = lower.endsWith(".png"), + isjpg = lower.endsWith(".jpg"), + isgcode = lower.endsWith(".gcode") || lower.endsWith(".nc"), + isset = lower.endsWith(".b64") || lower.endsWith(".km"), + iskmz = lower.endsWith(".kmz"), + isini = lower.endsWith(".ini"), + isgbr = lower.endsWith(".gbr"); reader.file = files[i]; reader.onloadend = function(e) { function load_dec() { @@ -818,11 +819,17 @@ function platformLoadFiles(files, group) { } else { odon(); } - } else if (isgrb && api.conf.get().controller.devel) { + } else if (isgbr && api.conf.get().controller.devel) { if (api.mode.is_cam()) { api.event.emit('cam.parse.gerber', e.target.result); } else { - api.alerts.show('Gerber import not availabe in this device mode'); + let mesh = load.GBR.toMesh(e.target.result); + platform.add( + newWidget(undefined, group) + .loadVertices(mesh.toFloat32(), true) + .saveToCatalog(e.target.file.name) + ); + // api.alerts.show('Gerber import not availabe in this device mode'); } } else if (is3mf) { let odon = function(models) { diff --git a/src/load/file.js b/src/load/file.js index dbf54dd9..41dff84b 100644 --- a/src/load/file.js +++ b/src/load/file.js @@ -13,6 +13,7 @@ if (load.File) return; // dep: load.svg // dep: load.url // dep: load.png +// dep: load.gbr gapp.register('load.file', []); const types = { @@ -48,6 +49,10 @@ const types = { resolve({ mesh: data, file }); } }); + }, + + gbr(data, file, resolve, reject, opt = {}) { + resolve([{ mesh: load.GBR.toMesh(data).toFloat32(), file }]) } }; diff --git a/src/load/gbr.js b/src/load/gbr.js new file mode 100644 index 00000000..0ff4d30d --- /dev/null +++ b/src/load/gbr.js @@ -0,0 +1,128 @@ +/** Copyright Stewart Allen -- All Rights Reserved */ + +// use: geo.bounds +// use: geo.polygon +// use: geo.polygons +// use: ext.gerber +gapp.register("load.gbr", (root, exports) => { + +const { load } = root; + +load.GBR = { + parse, + toMesh +}; + +function toMesh(text, opt = { z: 3 }) { + 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)); + } + return obj; +} + +function parse(text) { + let fact = 1000000; + let tools = {}; + let tool; + let poly; + let polys = []; + let circs = []; + let rects = []; + let last = {}; + let bounds = base.newBounds(); + let p = TracespaceParser.createParser(); + p.feed(text); + let r = p.results(); + let c = r.children + .filter(c => { + switch (c.type) { + case 'toolDefinition': + tools[c.code] = c.shape; + break; + case 'toolChange': + tool = { code: c.code, shape: tools[c.code] }; + break; + case 'graphic': + c.tool = tool; + return true; + } + return false; + }) + .map(r => { + return { + type: r.graphic, + tool: r.tool, + x: parseInt(r.coordinates.x), + y: parseInt(r.coordinates.y) + }; + }) + .map(r => { + const { x, y, tool, type } = r; + const pos = { x:x/fact, y:y/fact, z: 0}; + bounds.update(pos); + switch (type) { + case 'move': + if (true && poly && last.x === x && last.y === y && last.tool === tool) { + // console.log('continuation'); + } else { + poly = base.newPolygon().setOpen().add(pos.x, pos.y, 0); + poly.tool = tool; + polys.push(poly); + } + break; + case 'segment': + poly.add(pos.x, pos.y, 0); + break; + case 'shape': + poly = undefined; + let { shape } = tool; + switch (shape.type) { + case 'obround': + if (shape.xSize === shape.ySize) { + circs.push(base.newPolygon().centerCircle(pos, shape.xSize / 2, 20)); + } else { + console.log('TODO', shape); + } + break; + case 'circle': + circs.push(base.newPolygon().centerCircle(pos, shape.diameter / 2, 20)); + break; + case 'rectangle': + rects.push(base.newPolygon().centerRectangle(pos, shape.xSize, shape.ySize)); + break; + default: + console.log('TODO', shape); + break; + } + break; + } + last = { x, y, tool }; + return r; + }); + const { minx, maxx, miny, maxy } = bounds; + for (let poly of [...polys, ...circs, ...rects]) { + poly.move({ + x: -((maxx-minx)/2 + minx), + y: -((maxy-miny)/2 + miny), + z: 0 + }); + } + const open = []; + const closed = []; + for (let poly of polys) { + if (poly.appearsClosed()) { + closed.push(...poly.simplify()); + } else { + open.push(poly); + } + } + // const nest = base.polygons.nest([...closed, ...circs, ...rects], true); + // console.log({ nest }); + return { open, closed, circs, rects }; +} + +}); diff --git a/src/load/svg.js b/src/load/svg.js index ddfb4836..e3994ed9 100644 --- a/src/load/svg.js +++ b/src/load/svg.js @@ -74,20 +74,7 @@ function parse(text, opt = { soup: true }) { let z = parseFloat(depth.value); for (let poly of nest) { - let obj = []; - let earcut = poly.earcut(); - for (let poly of earcut) { - for (let point of poly.points) { - obj.push(point.x, point.y, z); - } - for (let point of poly.points.reverse()) { - obj.push(point.x, point.y, 0); - } - } - obj.appendAll(poly.extrude(z)); - for (let inner of poly.inner || []) { - obj.appendAll(inner.extrude(z)); - } + let obj = poly.extrude(z); objs.push(obj); // invert y for (let i=1, l=obj.length; i