complete basic gerber import conversion to mesh for milling traces

This commit is contained in:
Stewart Allen 2024-06-29 23:03:40 -04:00
commit 8b215460a1
6 changed files with 65 additions and 30 deletions

View file

@ -233,6 +233,8 @@ Float32Array.prototype.toShared = function() {
return newvert; return newvert;
}; };
Float32Array.prototype.toFloat32 = function() { return this };
String.prototype.reverse = function() { String.prototype.reverse = function() {
return this.split('').reverse().join(''); return this.split('').reverse().join('');
}; };

View file

@ -9,6 +9,14 @@ gapp.register("geo.csg", [], (root, exports) => {
const { base, ext } = root; const { base, ext } = root;
const debug = true; const debug = true;
function log() {
if (root?.mesh?.log) {
root.mesh.log(...arguments)
} else if (root.debug === true) {
console.log(...arguments);
}
}
const CSG = { const CSG = {
// accepts 2 or more arguments with threejs vertex position arrays // accepts 2 or more arguments with threejs vertex position arrays
union() { union() {
@ -27,7 +35,7 @@ const CSG = {
}, },
moduleOp(name, op) { 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)); const args = [...arguments].slice(2).map(a => new Instance.Manifold(a));
if (args.length < 2) { if (args.length < 2) {
throw "missing one or more meshes"; throw "missing one or more meshes";
@ -80,7 +88,7 @@ const CSG = {
}; };
function indexVertices(pos) { function indexVertices(pos) {
mesh.log(`indexing ${pos.length/3} vertices`); log(`indexing ${pos.length/3} vertices`);
let ipos = 0; let ipos = 0;
const index = []; const index = [];
const vertices = []; const vertices = [];

View file

@ -1852,39 +1852,42 @@ class Polygon {
} }
// turn 2d polygon into a 2.5D ribbon extruded in Z // 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 poly = this.clone().setClockwise();
let faces = []; let faces = [];
let points = poly.points; let points = poly.points;
let length = points.length; let length = points.length;
if (rev) {
points = points.slice().reverse();
}
for (let i=0; i<length; i++) { for (let i=0; i<length; i++) {
let p0 = points[i]; let p0 = points[i];
let p1 = points[(i + 1) % length]; let p1 = points[(i + 1) % length];
faces.push(p0.x, p0.y, p0.z); faces.push(p0.x, p0.y, p0.z + zadd);
faces.push(p1.x, p1.y, p0.z); faces.push(p1.x, p1.y, p1.z + z + zadd);
faces.push(p1.x, p1.y, p1.z + z); faces.push(p1.x, p1.y, p0.z + zadd);
faces.push(p0.x, p0.y, p0.z); faces.push(p0.x, p0.y, p0.z + zadd);
faces.push(p1.x, p1.y, p1.z + z); faces.push(p0.x, p0.y, p0.z + z + zadd);
faces.push(p0.x, p0.y, p0.z + z); faces.push(p1.x, p1.y, p1.z + z + zadd);
} }
return faces; return faces;
} }
// extrude poly (with inner voids) into 3d mesh // extrude poly (with inner voids) into 3d mesh
extrude(z = 1) { extrude(z = 1, zadd = 0) {
let obj = []; let obj = [];
let earcut = this.earcut(); let earcut = this.earcut();
for (let poly of earcut) { for (let poly of earcut) {
for (let point of poly.points) { for (let point of poly.points) {
obj.push(point.x, point.y, z); obj.push(point.x, point.y, z + zadd);
} }
for (let point of poly.points.reverse()) { for (let point of poly.points.reverse()) {
obj.push(point.x, point.y, 0); obj.push(point.x, point.y, zadd);
} }
} }
obj.appendAll(this.ribbonZ(z)); obj.appendAll(this.ribbonZ(z, zadd));
for (let inner of this.inner || []) { for (let inner of this.inner || []) {
obj.appendAll(inner.ribbonZ(z)); obj.appendAll(inner.ribbonZ(z, zadd, true));
} }
return obj; return obj;
} }

View file

@ -116,10 +116,10 @@ CAM.init = function(kiri, api) {
updateStock(); updateStock();
} }
api.event.on("cam.parse.gerber", gerber => { api.event.on("cam.parse.gerber", opts => {
const { open, closed, circs, rects } = load.GBR.parse(gerber); const { data, mesh } = opts;
const widget = api.widgets.all()[0]; const { open, closed, circs, rects } = load.GBR.parse(data);
const stack = new kiri.Stack(widget.mesh); const stack = new kiri.Stack(mesh || moto.space.world.newGroup());
const layers = new kiri.Layers(); const layers = new kiri.Layers();
for (let poly of open) { for (let poly of open) {
layers.setLayer("open", {line: 0xff8800}, false).addPoly(poly); layers.setLayer("open", {line: 0xff8800}, false).addPoly(poly);

View file

@ -819,17 +819,21 @@ function platformLoadFiles(files, group) {
} else { } else {
odon(); odon();
} }
} else if (isgbr && api.conf.get().controller.devel) { } else if (isgbr) {
if (api.mode.is_cam()) { if (api.conf.get().controller.devel) {
api.event.emit('cam.parse.gerber', e.target.result); api.event.emit('cam.parse.gerber', { data: e.target.result });
} else { } else {
let mesh = load.GBR.toMesh(e.target.result); let mesh = load.GBR.toMesh(e.target.result);
platform.add( let wid;
platform.add(wid =
newWidget(undefined, group) newWidget(undefined, group)
.loadVertices(mesh.toFloat32(), true) .loadVertices(mesh, true)
.saveToCatalog(e.target.file.name) .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) { } else if (is3mf) {
let odon = function(models) { let odon = function(models) {

View file

@ -4,24 +4,42 @@
// use: geo.polygon // use: geo.polygon
// use: geo.polygons // use: geo.polygons
// use: ext.gerber // use: ext.gerber
// use: geo.csg
gapp.register("load.gbr", (root, exports) => { gapp.register("load.gbr", (root, exports) => {
const { load } = root; const { load } = root;
const POLYS = base.polygons;
const CSG = base.CSG;
load.GBR = { load.GBR = {
parse, parse,
toMesh toMesh
}; };
function toMesh(text, opt = { z: 3 }) { function toMesh(text, opt = { z: 1 }) {
const { open, closed, circs, rects } = parse(text); const { open, closed, circs, rects } = parse(text);
const nest = base.polygons.nest([...closed, ...circs, ...rects], true); const nest = base.polygons.nest([...closed, ...circs, ...rects], true);
console.log({ nest });
const obj = []; const flat = POLYS.flatten(nest, [], true);
for (let poly of nest) { const z0 = opt.z || opt.z0 || 1;
obj.appendAll(poly.extrude(opt.z || 3)); 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) { function parse(text) {