diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 3efe4504..a99d38b2 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -1495,7 +1495,6 @@ class Polygon { toClipper(inout) { let poly = this, - cur = [], out = inout || []; out.push(poly.points.map(p => p.toClipper())); if (poly.inner) { @@ -1519,6 +1518,37 @@ class Polygon { return POLY.expand([this], -offset, this.getZ(), output); } + /** + * ofsetting an open line uses a different procedure and options + * + * @param {number} distance + * @param {'square'|'round'|'miter'} type + * @returns {Polygon[]} + */ + offset_open(distance, type = 'miter') { + if (this.isOpen()) { + let cjnt = clib.JoinType, + cety = clib.EndType, + coff = new clib.ClipperOffset(), + ctre = new clib.PolyTree(), + entt = { + 'square' : cety.etOpenSquare, + 'round' : cety.etOpenRound, + 'miter' : cety.etOpenSquare + }[type] || cety.etOpenSquare, + jntt = { + 'square': cjnt.jtSquare, + 'round': cjnt.jtRound, + 'miter': cjnt.jtMiter + }[type] || cjnt.jtMiter; + coff.AddPaths(this.toClipper(), jntt, entt); + coff.Execute(ctre, distance * config.clipper); + return POLY.fromClipperTree(ctre, this.getZ(), null, null, 0); + } else { + return this.offset(distance); + } + } + /** * todo need something more clever for polygons that overlap with * todo differing resolutions (like circles) diff --git a/src/geo/polygons.js b/src/geo/polygons.js index 53debbe3..dfd9fe11 100644 --- a/src/geo/polygons.js +++ b/src/geo/polygons.js @@ -34,7 +34,7 @@ const POLYS = base.polygons = { offset, trimTo, expand, - expand_lines, + // expand_lines, points, length, route, @@ -508,18 +508,18 @@ function sumCirc(polys) { * @param {number} distance offset * @param {number} [z] defaults to 0 */ -function expand_lines(poly, distance, z) { - let fact = config.clipper, - cjnt = clib.JoinType, - cety = clib.EndType, - coff = new clib.ClipperOffset(), - ctre = new clib.PolyTree(); +// function expand_lines(poly, distance, z) { +// let fact = config.clipper, +// cjnt = clib.JoinType, +// cety = clib.EndType, +// coff = new clib.ClipperOffset(), +// ctre = new clib.PolyTree(); - coff.AddPaths(poly.toClipper(), cjnt.jtMiter, cety.etOpenSquare); - coff.Execute(ctre, distance * fact); +// coff.AddPaths(poly.toClipper(), cjnt.jtMiter, cety.etOpenSquare); +// coff.Execute(ctre, distance * fact); - return fromClipperTree(ctre, z, null, null, 0); -} +// return fromClipperTree(ctre, z, null, null, 0); +// } /** * @param {Polygon[]} polys diff --git a/src/kiri-mode/cam/client.js b/src/kiri-mode/cam/client.js index da44193d..101bf540 100644 --- a/src/kiri-mode/cam/client.js +++ b/src/kiri-mode/cam/client.js @@ -123,6 +123,11 @@ CAM.init = function(kiri, api) { const layers = new kiri.Layers(); for (let poly of open) { layers.setLayer("open", {line: 0xff8800}, false).addPoly(poly); + let diam = poly.tool?.shape?.diameter; + if (diam) { + const exp = poly.offset_open(diam, 'round'); + layers.setLayer("open2", {line: 0xff5555}, false).addPolys(exp); + } } for (let poly of closed) { layers.setLayer("close", {line: 0xff0000}, false).addPoly(poly); diff --git a/src/load/gbr.js b/src/load/gbr.js index d919b35a..a70c9122 100644 --- a/src/load/gbr.js +++ b/src/load/gbr.js @@ -44,7 +44,7 @@ function toMesh(text, opt = { z: 1 }) { return []; } - // copper traces + // copper pads const mid = []; for (let poly of flat.filter(p => p.depth === 1)) { mid.push(poly.extrude(z1, z0 - z1)); @@ -55,10 +55,24 @@ function toMesh(text, opt = { z: 1 }) { return top[0]; } - // inner polys for traces which we will punch thru for vias + // copper traces (expand first since open) + const open_exp = open.map(poly => { + const diam = poly.tool?.shape?.diameter; + return diam ? poly.offset_open(diam, 'round') : null; + }).filter(p => p).flat(); + + // union open trace expansions + const opn = []; + for (let poly of POLYS.union(open_exp)) { + const tool = poly.tool; + opn.push(poly.extrude(z0)); + } + + // inner polys for traces which we add back to the top + // and (optionally) remove from the bottom for making vias const low = []; for (let poly of flat.filter(p => p.depth === 2)) { - low.push(poly.extrude(z0)); + low.push(poly.extrude(z1, z0 - z1)); } // convert vertex arrays to csg mesh definitions @@ -66,17 +80,21 @@ function toMesh(text, opt = { z: 1 }) { const midMesh = mid.map(v => CSG.fromPositionArray(v)); // subtract mid from top - const v0Mesh = CSG.subtract(topMesh, ...midMesh); + let meshOut = CSG.subtract(topMesh, ...midMesh); - if (low.length === 0) { - return CSG.toPositionArray(v0Mesh); + if (opn.length) { + // add back raised traces (before removing vias) + const opnMesh = opn.map(v => CSG.fromPositionArray(v)) + meshOut = CSG.union(meshOut, ...opnMesh); } - // subtract low (vias) from remaining - const lowMesh = low.map(v => CSG.fromPositionArray(v)) - const v1Mesh = CSG.subtract(v0Mesh, ...lowMesh); + if (low.length) { + // subtract low (vias) from remaining + const lowMesh = low.map(v => CSG.fromPositionArray(v)) + meshOut = CSG.union(meshOut, ...lowMesh); + } - return CSG.toPositionArray(v1Mesh); + return CSG.toPositionArray(meshOut); } function parse(text) {