add gbr to loader classes

This commit is contained in:
Stewart Allen 2024-06-29 19:21:22 -04:00
commit fdfea41a48
8 changed files with 179 additions and 129 deletions

View file

@ -1851,8 +1851,8 @@ class Polygon {
return this; return this;
} }
// turn 2d polygon into closed 3d mesh faces // turn 2d polygon into a 2.5D ribbon extruded in Z
extrude(z = 1) { ribbonZ(z = 1) {
let poly = this.clone().setClockwise(); let poly = this.clone().setClockwise();
let faces = []; let faces = [];
let points = poly.points; let points = poly.points;
@ -1870,6 +1870,25 @@ class Polygon {
return faces; 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 // split long straight lines into segments no longer than max
// and return a new polygon // and return a new polygon
segment(max = 1) { segment(max = 1) {

View file

@ -5,6 +5,7 @@
// dep: kiri-mode.cam.driver // dep: kiri-mode.cam.driver
// use: kiri-mode.cam.animate // use: kiri-mode.cam.animate
// use: kiri-mode.cam.animate2 // use: kiri-mode.cam.animate2
// use: load.gbr
gapp.register("kiri-mode.cam.client", [], (root, exports) => { gapp.register("kiri-mode.cam.client", [], (root, exports) => {
const { base, kiri } = root; const { base, kiri } = root;
@ -116,102 +117,7 @@ CAM.init = function(kiri, api) {
} }
api.event.on("cam.parse.gerber", gerber => { api.event.on("cam.parse.gerber", gerber => {
let fact = 1000000; const { open, closed, circs, rects } = load.GBR.parse(gerber);
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 widget = api.widgets.all()[0]; const widget = api.widgets.all()[0];
const stack = new kiri.Stack(widget.mesh); const stack = new kiri.Stack(widget.mesh);
const layers = new kiri.Layers(); const layers = new kiri.Layers();
@ -228,7 +134,6 @@ CAM.init = function(kiri, api) {
layers.setLayer("rects", {line: 0x0000ff}, false).addPoly(poly); layers.setLayer("rects", {line: 0x0000ff}, false).addPoly(poly);
} }
stack.addLayers(layers); stack.addLayers(layers);
console.log(r, tools);
}); });
api.event.on("widget.add", widget => { api.event.on("widget.add", widget => {

View file

@ -40,7 +40,7 @@ const POLY = polygons;
let debug = self.debug === true, let debug = self.debug === true,
ccvalue = this.navigator ? navigator.hardwareConcurrency || 0 : 0, 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 = { current = self.worker = {
print: null, print: null,
snap: null, snap: null,

View file

@ -12,7 +12,6 @@
// dep: kiri.selection // dep: kiri.selection
// use: kiri.tools // use: kiri.tools
// use: kiri.pack // use: kiri.pack
// use: ext.gerber
gapp.register("kiri.init", [], (root, exports) => { gapp.register("kiri.init", [], (root, exports) => {
const { base, kiri } = root; const { base, kiri } = root;

View file

@ -11,6 +11,7 @@
// dep: load.stl // dep: load.stl
// dep: load.obj // dep: load.obj
// dep: load.3mf // dep: load.3mf
// dep: load.gbr
// use: kiri-mode.cam.tool // use: kiri-mode.cam.tool
gapp.register("kiri.platform", [], (root, exports) => { gapp.register("kiri.platform", [], (root, exports) => {
@ -753,18 +754,18 @@ function platformLoadFiles(files, group) {
const file = files[i], const file = files[i],
reader = new FileReader(), reader = new FileReader(),
lower = files[i].name.toLowerCase(), lower = files[i].name.toLowerCase(),
israw = lower.indexOf(".raw") > 0 || lower.indexOf('.') < 0, israw = lower.endsWith(".raw") || lower.indexOf('.') < 0,
isstl = lower.indexOf(".stl") > 0, isstl = lower.endsWith(".stl"),
isobj = lower.indexOf(".obj") > 0, isobj = lower.endsWith(".obj"),
is3mf = lower.indexOf(".3mf") > 0, is3mf = lower.endsWith(".3mf"),
issvg = lower.indexOf(".svg") > 0, issvg = lower.endsWith(".svg"),
ispng = lower.indexOf(".png") > 0, ispng = lower.endsWith(".png"),
isjpg = lower.indexOf(".jpg") > 0, isjpg = lower.endsWith(".jpg"),
isgcode = lower.indexOf(".gcode") > 0 || lower.indexOf(".nc") > 0, isgcode = lower.endsWith(".gcode") || lower.endsWith(".nc"),
isset = lower.indexOf(".b64") > 0 || lower.indexOf(".km") > 0, isset = lower.endsWith(".b64") || lower.endsWith(".km"),
iskmz = lower.indexOf(".kmz") > 0, iskmz = lower.endsWith(".kmz"),
isini = lower.indexOf(".ini") > 0, isini = lower.endsWith(".ini"),
isgrb = lower.indexOf(".grb"); isgbr = lower.endsWith(".gbr");
reader.file = files[i]; reader.file = files[i];
reader.onloadend = function(e) { reader.onloadend = function(e) {
function load_dec() { function load_dec() {
@ -818,11 +819,17 @@ function platformLoadFiles(files, group) {
} else { } else {
odon(); odon();
} }
} else if (isgrb && api.conf.get().controller.devel) { } else if (isgbr && api.conf.get().controller.devel) {
if (api.mode.is_cam()) { if (api.mode.is_cam()) {
api.event.emit('cam.parse.gerber', e.target.result); api.event.emit('cam.parse.gerber', e.target.result);
} else { } 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) { } else if (is3mf) {
let odon = function(models) { let odon = function(models) {

View file

@ -13,6 +13,7 @@ if (load.File) return;
// dep: load.svg // dep: load.svg
// dep: load.url // dep: load.url
// dep: load.png // dep: load.png
// dep: load.gbr
gapp.register('load.file', []); gapp.register('load.file', []);
const types = { const types = {
@ -48,6 +49,10 @@ const types = {
resolve({ mesh: data, file }); resolve({ mesh: data, file });
} }
}); });
},
gbr(data, file, resolve, reject, opt = {}) {
resolve([{ mesh: load.GBR.toMesh(data).toFloat32(), file }])
} }
}; };

128
src/load/gbr.js Normal file
View file

@ -0,0 +1,128 @@
/** Copyright Stewart Allen <sa@grid.space> -- 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 };
}
});

View file

@ -74,20 +74,7 @@ function parse(text, opt = { soup: true }) {
let z = parseFloat(depth.value); let z = parseFloat(depth.value);
for (let poly of nest) { for (let poly of nest) {
let obj = []; let obj = poly.extrude(z);
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));
}
objs.push(obj); objs.push(obj);
// invert y // invert y
for (let i=1, l=obj.length; i<l; i += 3) { for (let i=1, l=obj.length; i<l; i += 3) {