diff --git a/lib/gerber/gerberparser.js b/lib/gerber/gerberparser.js index 018b65ce..8463031e 100644 --- a/lib/gerber/gerberparser.js +++ b/lib/gerber/gerberparser.js @@ -1,8 +1,8 @@ function parseGerber(e, name) { - console.log(Gerber.render(e)) + // console.log(Gerber.render(e)) // Added for debugging Gerber render output var t, n = !0; signalBottomPrimitives = mergeAllPrimitives(Gerber.render(e), "#90ffa0"); - // console.log(signalBottomPrimitives) + // console.log(signalBottomPrimitives) // Added for debugging merged primitives var gerber = new THREE.Object3D() // console.log(signalBottomPrimitives) @@ -505,6 +505,39 @@ var GCode = { } } }; +/** + * Approximates an arc by generating a series of line segments. + * Used for converting Gerber circular interpolation commands into polygonal paths. + * @param {number} startX - Starting X coordinate + * @param {number} startY - Starting Y coordinate + * @param {number} endX - Ending X coordinate + * @param {number} endY - Ending Y coordinate + * @param {number} I - X offset to arc center from start point + * @param {number} J - Y offset to arc center from start point + * @param {string} mode - "CWCIRCULAR" or "CCWCIRCULAR" for clockwise/counterclockwise + * @param {number} numSegments - Number of segments to approximate the arc + * @returns {Array} Array of {X, Y} points approximating the arc + */ +function approximateArc(startX, startY, endX, endY, I, J, mode, numSegments) { + var centerX = startX + I; + var centerY = startY + J; + var radius = Math.sqrt((startX - centerX)**2 + (startY - centerY)**2); + var startAngle = Math.atan2(startY - centerY, startX - centerX); + var endAngle = Math.atan2(endY - centerY, endX - centerX); + var sweepAngle = endAngle - startAngle; + if (mode === "CWCIRCULAR" && sweepAngle > 0) sweepAngle -= 2 * Math.PI; + if (mode === "CCWCIRCULAR" && sweepAngle < 0) sweepAngle += 2 * Math.PI; + var segments = []; + for (var i = 0; i <= numSegments; i++) { + var angle = startAngle + (sweepAngle * i / numSegments); + segments.push({ + X: centerX + radius * Math.cos(angle), + Y: centerY + radius * Math.sin(angle) + }); + } + return segments; +} + var apertureScale = 1, Gerber = { render: function(D) { @@ -613,24 +646,40 @@ var apertureScale = 1, return e } - function s(D, X, Y, e, t) { - for (var i = 0; i < t.primitives.length; i++) { - var s = t.primitives[i], - a = r({ - X: D + s.offsetX, - Y: X + s.offsetY - }, { - X: Y + s.offsetX, - Y: e + s.offsetY - }, s.polygon), - o = translatePolygon(D + s.offsetX, X + s.offsetY, s.polygon), - f = translatePolygon(Y + s.offsetX, e + s.offsetY, s.polygon), - l = mergePolygons(a, mergePolygons(o, f)); - fillPolygon(n, 0, 0, l) + // Modified function to accept mode, I, J parameters and handle arc approximation by recursively calling itself with linear segments + function s(D, X, Y, e, t, mode, I, J) { + if (!t || !t.primitives) { + console.warn("No valid aperture for stroke operation"); + return; + } + if (mode !== "LINEAR") { + var points = approximateArc(D, X, Y, e, I, J, mode, 10); + for (var seg = 1; seg < points.length; seg++) { + s(points[seg-1].X, points[seg-1].Y, points[seg].X, points[seg].Y, t, "LINEAR", 0, 0); + } + } else { + for (var i = 0; i < t.primitives.length; i++) { + var prim = t.primitives[i], + a = r({ + X: D + prim.offsetX, + Y: X + prim.offsetY + }, { + X: Y + prim.offsetX, + Y: e + prim.offsetY + }, prim.polygon), + o = translatePolygon(D + prim.offsetX, X + prim.offsetY, prim.polygon), + f = translatePolygon(Y + prim.offsetX, e + prim.offsetY, prim.polygon), + l = mergePolygons(a, mergePolygons(o, f)); + fillPolygon(n, 0, 0, l) + } } } function a(D, X, Y) { + if (!Y || !Y.primitives) { + console.warn("No valid aperture for flash operation"); + return; + } for (var e = 0; e < Y.primitives.length; e++) { var t = Y.primitives[e]; fillPolygon(n, D + t.offsetX, X + t.offsetY, t.polygon) @@ -647,6 +696,7 @@ var apertureScale = 1, } var n = []; D = D.replace(/\r/g, "").replace(/\n/g, "").replace(/%+/g, "%"); + // console.log("Gerber D starts with:", D.substring(0, 200)); // Added for debugging input data for (var f = D.split("%"), l = { integerDigits: 3, fractionDigits: 3, @@ -670,6 +720,7 @@ var apertureScale = 1, } else for (var w = S.split("*"), $ = 0; $ < w.length; $++) { var N = w[$]; + N = N.trim(); // Trim whitespace to handle commands with leading spaces from string splitting if (0 === N.indexOf("IN") && (p = N.substr(2)), N = N.replace(/\s/g, ""), 0 === N.indexOf("G01") && (m = "LINEAR"), @@ -804,17 +855,24 @@ var apertureScale = 1, G = y[DD], G || (G = y.D10) } - if (N.match(/^((G55)?D0?[1-3]$|X[\-0-9]+|Y[\-0-9]+)/)) { - var XD = N.match(/^(G[0-9]+)?(X([\-0-9]+))?(Y([\-0-9]+))?(D0?[1-3])?$/); + // Updated condition to match coordinate commands more broadly while excluding non-coordinate commands like apertures and format specs + if ((N.match(/D[0-9]+/) || N.match(/X[\-0-9]+/) || N.match(/Y[\-0-9]+/)) && !N.match(/^(G54)?D/) && !N.match(/^FS/) && !N.match(/^AD/)) { + var XD = N.match(/^(G[0-9]+)?(X([\-0-9]+))?(Y([\-0-9]+))?(I([\-0-9]+))?(J([\-0-9]+))?(D[0-9]+)?$/); // Changed regex to match any D followed by digits instead of restricting to D01-D03 + // if (!XD) console.log("XD null for N:", N); // Added for debugging regex match failures + var I_val = 0, J_val = 0; // Variables to capture I and J offsets for arc centers XD[3] && (A = X(XD[3]) * O.a), XD[5] && (b = X(XD[5]) * O.b), - XD[6] && (x = XD[6]), - x.match(/D0?1/) ? s(P, C, A, b, G) : x.match(/D0?2/) || x.match(/D0?3/) && a(A, b, G), + XD[10] && (x = XD[10]), // Updated index from XD[6] to XD[10] due to added I and J regex groups + XD[7] && (I_val = X(XD[7]) * O.a), + XD[9] && (J_val = X(XD[9]) * O.b), + // Updated call to s() to pass mode, I_val, J_val for arc handling + x.match(/D0?1/) ? s(P, C, A, b, G || y.D10, m, I_val, J_val) : x.match(/D0?2/) || x.match(/D0?3/) && a(A, b, G || y.D10), P = A, C = b } } } + // console.log("n.length:", n.length); // Added for debugging number of parsed primitives return n }, sampleFile: "G75*G70*%OFA0B0*%%FSLAX24Y24*%%IPPOS*%%LPD*%%AMOC8*5,1,8,0,0,1.08239X$1,22.5*%%ADD10C,0.0160*%%ADD11C,0.0650*%%ADD12R,0.0650X0.0650*%%ADD13R,0.0740X0.0740*%%ADD14C,0.0740*%%ADD15C,0.0520*%%ADD16C,0.0520*%%ADD17OC8,0.0560*%%ADD18C,0.0750*%%ADD19C,0.0660*%%ADD20C,0.0315*%D10*X019633Y004585D02*X019633Y005506D01*X019940Y005813D01*X020247Y005506D01*X020247Y004585D01*X020861Y004585D02*X020861Y005813D01*X020554Y005813D01*X020247Y005506D01*X021475Y005506D02*X021475Y004892D01*X021782Y004585D01*X022395Y004585D01*X022702Y004892D01*X022702Y005506D01*X022395Y005813D01*X021782Y005813D01*X021475Y005506D01*X023316Y005813D02*X024237Y005813D01*X024544Y005506D01*X024544Y004892D01*X024237Y004585D01*X023316Y004585D01*X025158Y004585D02*X025465Y004585D01*X025465Y004892D01*X025158Y004892D01*X025158Y004585D01*X026079Y004892D02*X026079Y005199D01*X026385Y005506D01*X027306Y005506D01*X027920Y006120D02*X028227Y006427D01*X028841Y006427D01*X029148Y006120D01*X029148Y004892D01*X028841Y004585D01*X028227Y004585D01*X027920Y004892D01*X027306Y004585D02*X026385Y004585D01*X026079Y004892D01*X026385Y005506D02*X026079Y005813D01*X026079Y006120D01*X026385Y006427D01*X027306Y006427D01*X027306Y004585D01*X029762Y005506D02*X030069Y005199D01*X030989Y005199D01*X030989Y004585D02*X030989Y006427D01*X030069Y006427D01*X029762Y006120D01*X029762Y005506D01*X031603Y005506D02*X032831Y005506D01*X033445Y005813D02*X034366Y005813D01*X034673Y005506D01*X034673Y004892D01*X034366Y004585D01*X033445Y004585D01*X033445Y006427D01*X035593Y006427D02*X035593Y006734D01*X035593Y005813D02*X035593Y004585D01*X035900Y004585D02*X035286Y004585D01*X036514Y004892D02*X036514Y005506D01*X036821Y005813D01*X037742Y005813D01*X037742Y003971D01*X037742Y004585D02*X036821Y004585D01*X036514Y004892D01*X035900Y005813D02*X035593Y005813D01*X038356Y005506D02*X038356Y004585D01*X039276Y004585D01*X039583Y004892D01*X039276Y005199D01*X038356Y005199D01*X038356Y005506D02*X038663Y005813D01*X039276Y005813D01*X040197Y006120D02*X040197Y005506D01*X040504Y005199D01*X041425Y005199D01*X040811Y005199D02*X040197Y004585D01*X041425Y004585D02*X041425Y006427D01*X040504Y006427D01*X040197Y006120D01*D11*X031005Y012505D03*X031005Y013505D03*X029005Y013505D03*X029005Y012505D03*X029005Y015505D03*X031005Y015505D03*X030505Y022505D03*X029505Y022505D03*X032505Y026005D03*D12*X032505Y024005D03*D13*X026005Y023505D03*D14*X025005Y023505D03*X024005Y023505D03*X023005Y023505D03*X022005Y023505D03*X021005Y023505D03*X021005Y026005D03*X020005Y026005D03*X020505Y027505D03*X022505Y026005D03*X025505Y026005D03*X025505Y027505D03*X027505Y027005D03*X029505Y027005D03*X029505Y024005D03*X027505Y023005D03*X033505Y029505D03*X041505Y029505D03*X026005Y015505D03*X026005Y014505D03*X027505Y012505D03*X022505Y012505D03*D15*X022505Y017245D02*X022505Y017765D01*X023505Y017765D02*X023505Y017245D01*X024505Y017245D02*X024505Y017765D01*X025505Y017765D02*X025505Y017245D01*X026505Y017245D02*X026505Y017765D01*X027505Y017765D02*X027505Y017245D01*X028505Y017245D02*X028505Y017765D01*X029505Y017765D02*X029505Y017245D01*X030505Y017245D02*X030505Y017765D01*X031505Y017765D02*X031505Y017245D01*X032505Y017245D02*X032505Y017765D01*X033505Y017765D02*X033505Y017245D01*X034505Y017245D02*X034505Y017765D01*X034505Y020245D02*X034505Y020765D01*X033505Y020765D02*X033505Y020245D01*X032505Y020245D02*X032505Y020765D01*X031505Y020765D02*X031505Y020245D01*X030505Y020245D02*X030505Y020765D01*X029505Y020765D02*X029505Y020245D01*X028505Y020245D02*X028505Y020765D01*X027505Y020765D02*X027505Y020245D01*X026505Y020245D02*X026505Y020765D01*X025505Y020765D02*X025505Y020245D01*X024505Y020245D02*X024505Y020765D01*X023505Y020765D02*X023505Y020245D01*X022505Y020245D02*X022505Y020765D01*X021505Y020765D02*X021505Y020245D01*X021505Y017765D02*X021505Y017245D01*D16*X038505Y017005D03*X039505Y017005D03*X040505Y017005D03*X041505Y017005D03*X041505Y014005D03*X040505Y014005D03*X039505Y014005D03*X038505Y014005D03*D17*X034505Y013505D03*X034505Y012505D03*X034505Y011505D03*X034505Y010505D03*X033505Y010505D03*X033505Y011505D03*X033505Y012505D03*X033505Y013505D03*X033505Y014505D03*X033505Y015505D03*X034505Y015505D03*X034505Y014505D03*D18*X038105Y013005D03*X041805Y013005D03*X041805Y018005D03*X038105Y018005D03*D19*X023835Y029505D02*X023175Y029505D01*X023175Y030505D02*X023835Y030505D01*X023835Y031505D02*X023175Y031505D01*D20*X023505Y031505D02*X024005Y031505D01*X027505Y028005D01*X027505Y027005D01*X025505Y027505D02*X025505Y026005D01*X023505Y025005D02*X023505Y024505D01*X023005Y024005D01*X023005Y023505D01*X024005Y023505D02*X024005Y023005D01*X023505Y022505D01*X023005Y022505D01*X021505Y021005D01*X021505Y020505D01*X021005Y019505D02*X020505Y020005D01*X020505Y021005D01*X022005Y022505D01*X022005Y023505D01*X022005Y025505D01*X022505Y026005D01*X023505Y025005D02*X034505Y025005D01*X033505Y026005D01*X032505Y026005D01*X031005Y027005D02*X033505Y029505D01*X031005Y027005D02*X029505Y027005D01*X029505Y024005D02*X029505Y022505D01*X029505Y020505D01*X029505Y020005D01*X031005Y018505D01*X035505Y018505D01*X037005Y017005D01*X035505Y015505D01*X034505Y015505D01*X033505Y015505D01*X033505Y014505D02*X034505Y014505D01*X038005Y014505D01*X038505Y014005D01*X038505Y013505D01*X038005Y013005D01*X038105Y013005D01*X038505Y012005D02*X036505Y012005D01*X035005Y013505D01*X034505Y013505D01*X033505Y013505D01*X033505Y012505D02*X034505Y012505D01*X035005Y012505D01*X036505Y011005D01*X038505Y011005D01*X040505Y013005D01*X040505Y014005D01*X039505Y014005D02*X039505Y013005D01*X038505Y012005D01*X038505Y010005D02*X036505Y010005D01*X035005Y011505D01*X034505Y011505D01*X033505Y011505D01*X032005Y011505D01*X027005Y011505D01*X025005Y013505D01*X025005Y015005D01*X025505Y015505D01*X026005Y015505D01*X027005Y015505D01*X028505Y017005D01*X028505Y017505D01*X029505Y017505D02*X029505Y016005D01*X029005Y015505D01*X029005Y013505D01*X029005Y012505D02*X031005Y012505D01*X032005Y011505D01*X033505Y010505D02*X023005Y010505D01*X021505Y012005D01*X021505Y017505D01*X019505Y019505D01*X019505Y022005D01*X021005Y023505D01*X021005Y026005D01*X020005Y026005D02*X020005Y027005D01*X020505Y027505D01*X023505Y029505D02*X023505Y025005D01*X025005Y023505D02*X025005Y023005D01*X023505Y021505D01*X023005Y021505D01*X022505Y021005D01*X022505Y020505D01*X021005Y019505D02*X029005Y019505D01*X029505Y020005D01*X030505Y020505D02*X030505Y022505D01*X037005Y022505D01*X034505Y025005D01*X032505Y024005D02*X029505Y024005D01*X027505Y023005D02*X026505Y022005D01*X026505Y020505D01*X023505Y017505D02*X023505Y013505D01*X022505Y012505D01*X026005Y014505D02*X027505Y013005D01*X027505Y012505D01*X031005Y013505D02*X031005Y015505D01*X030505Y016005D01*X030505Y017505D01*X032005Y019505D02*X031505Y020005D01*X031505Y020505D01*X032505Y020505D02*X032505Y021005D01*X033005Y021505D01*X037005Y021505D01*X040505Y018005D01*X040505Y017005D01*X039505Y017005D02*X039505Y018005D01*X038005Y019505D01*X032005Y019505D01*X037005Y017005D02*X038005Y018005D01*X038105Y018005D01*X038005Y018005D02*X038505Y017505D01*X038505Y017005D01*X041505Y017005D02*X041505Y014005D01*X041505Y013005D01*X041805Y013005D01*X041505Y013005D02*X038505Y010005D01*X034505Y010505D02*X033505Y010505D01*X041505Y017005D02*X041505Y018005D01*X041805Y018005D01*X041505Y018005D02*X037005Y022505D01*X041505Y029505D02*X038505Y032505D01*X023005Y032505D01*X022005Y031505D01*X023005Y030505D01*X023505Y030505D01*M02*" @@ -904,4 +962,4 @@ var apertureScale = 1, // diameter: .3, // angle: 45, // depth: .2 -// }]; \ No newline at end of file +// }];