Skip to content

Commit

Permalink
added gerber export, changed rendering to support holes
Browse files Browse the repository at this point in the history
  • Loading branch information
leomcelroy committed Oct 22, 2022
1 parent 17785b0 commit d033d97
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 28 deletions.
127 changes: 113 additions & 14 deletions js/events/download.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { changeDpiDataUrl } from "./changeDPI.js";
import { offset2 } from "../../geogram/index.js";
import JSZip from "jszip";
import { saveAs } from "file-saver";


export function downloadSVG(state) {
const svgUrl = makeSVG(state);
Expand Down Expand Up @@ -59,26 +63,121 @@ function format(x) {
export function downloadGerber(state) {
const layers = state.storedPCB.layers;

// console.log(layers);
let str = ''
str += "%MOIN*%\n" // inch units
str += "%LPD*%\n" // layer dark
str += "%FSLAX66Y66*%\n" // format absolute 6.6
str += "G01*\n" // linear interpolation
const expandWire = w => offset2(
w.shape,
w.thickness/2,
{
endType: "etOpenRound",
jointType:"jtRound",
}
);

// this is a list of polylines
const frontCopper = layers["F.Cu"].map( x => {
if (x.type === "wire") return expandWire(x);
else return x;
}).flat();

const drill = layers["drill"].flat().map( x => {

const getCenter = (pts) => {
let totalX = 0;
let totalY = 0;
pts.forEach(pt => {
totalX += pt.x;
totalY += pt.y;
})

return { x: totalX/pts.length, y: totalY/pts.length };
}

const front_copper = layers["F.Cu"];
const strs = front_copper.polylines().map( (x) => {
const { pts } = x;
let ptsString = pts.reduce((acc, cur, i) => `${acc}X${format(cur.x)}Y${format(cur.y)}D0${i === 0 ? 2 : 1}*\n`, "G36*\n")
ptsString += "G37*\n";
const getDistance = (pt0, pt1) => Math.sqrt((pt1.x - pt0.x)**2+(pt1.y - pt0.y)**2);

return ptsString;
const center = getCenter(x);
const dist = Math.round(1000*x.reduce((acc, cur) => acc + getDistance(center, cur), 0)/x.length)/1000;

return {
center,
dist
};
});

str += strs.join("") + "M02*";
const interior = layers["interior"].map( x => {
return x;
}).flat();;

console.log({
drill,
frontCopper,
interior
})

const makeFile = (layer) => {
let str = ''
str += "%MOIN*%\n" // inch units
str += "%LPD*%\n" // layer dark
str += "%FSLAX66Y66*%\n" // format absolute 6.6
str += "G01*\n" // linear interpolation

const strs = layer.map( pts => {
let ptsString = pts.reduce((acc, cur, i) => `${acc}X${format(cur.x)}Y${format(cur.y)}D0${i === 0 ? 2 : 1}*\n`, "G36*\n")
ptsString += "G37*\n";

return ptsString;
});

str += strs.join("") + "M02*";

return str;
}

const makeFileDrill = (drills) => {
const tools = {};
drills.forEach( ({ dist, center }) => {
if (dist in tools) {
tools[dist].push(center);
} else {
tools[dist] = [ center ];
}
})

let str = "";
str += "M48\n" // start of header
str += "INCH,LZ\n" // inch units with leading zeros
str += "VER,1\n" // version 1
str += "FMAT,2\n" // format 2
for (const tool in tools) {
str += 'T'+ tool + 'C'+ tool + "\n"; // +'C'+tool+"\n" // define tools
}
str += "M95\n" // end of header
str += "G05\n" // drill mode
for (const tool in tools) {
str += 'T'+tool+'\n' // tool selection
for (var i = 0; i < tools[tool].length; i++) {
const hole = tools[tool][i];
str += 'X'+format(hole.x)+'Y'+format(hole.y)+'\n'
}
}

str += "M30\n" // end of program

return str;
}


var zip = new JSZip();
zip.file(`${state.name === "" ? "anon" : state.name}-F_Cu.gbr`, makeFile(frontCopper));
zip.file(`${state.name === "" ? "anon" : state.name}-Edge_Cuts.gbr`, makeFile(interior));
zip.file(`${state.name === "" ? "anon" : state.name}-Drill.xln`, makeFileDrill(drill));

zip
.generateAsync({ type:"blob" })
.then((content) => {
// see FileSaver.js
saveAs(content, `${state.name === "" ? "anon" : state.name}-gerber.zip`);
});

downloadText(`${state.name === "" ? "anon" : state.name}.gerber`, str);
// downloadText(`${state.name === "" ? "anon" : state.name}-F_Cu.gbr`, str);
}

export function downloadText(filename, text) {
Expand Down
4 changes: 4 additions & 0 deletions js/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ const menu = state => html`
@click=${() => downloadText(`${state.name === "" ? "anon" : state.name}.js`, state.codemirror.view.state.doc.toString())}>
js
</div class="menu-item">
<div class="menu-item"
@click=${() => downloadGerber(state)}>
gerber
</div class="menu-item">
<input
class="input-item"
style="margin: 3px;"
Expand Down
26 changes: 13 additions & 13 deletions js/views/drawPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,21 @@ export const drawPath = ({ data, color, groupId = ""}) => {
// parts overlap then i want to render on top (first way)
// interior shapes I want to render outside (second way)

d.split(/(?=M)/g).forEach(dstring => {
toRender.push(renderDataString(dstring));
});
// d.split(/(?=M)/g).forEach(dstring => {
// toRender.push(renderDataString(dstring));
// });

// const toAdd = svg`
// <path
// d="${d}"
// fill-rule="evenodd"
// stroke-linecap="round"
// stroke-linejoin="round"
// fill="${color.slice(0, -2)}"
// fill-opacity=${alpha}/>
// `
const toAdd = svg`
<path
d="${d}"
fill-rule="evenodd"
stroke-linecap="round"
stroke-linejoin="round"
fill="${color.slice(0, -2)}"
fill-opacity=${alpha}/>
`

// toRender.push(toAdd);
toRender.push(toAdd);

} else if (d.type === "text") {
toRender.push(renderText(d));
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
"astring": "^1.7.5",
"codemirror": "^6.0.0",
"esprima": "^4.0.1",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"lit-html": "^2.0.1"
}
}
84 changes: 83 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,11 @@ cookies@~0.8.0:
depd "~2.0.0"
keygrip "~1.1.0"

core-util-is@~1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==

crelt@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94"
Expand Down Expand Up @@ -794,6 +799,11 @@ etag@^1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==

file-saver@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==

fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
Expand Down Expand Up @@ -888,12 +898,17 @@ http-errors@~1.6.2:
setprototypeof "1.1.0"
statuses ">= 1.4.0 < 2"

immediate@~3.0.5:
version "3.0.6"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==

[email protected]:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==

[email protected]:
[email protected], inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
Expand Down Expand Up @@ -970,6 +985,11 @@ is-wsl@^2.2.0:
dependencies:
is-docker "^2.0.0"

isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==

isbinaryfile@^4.0.6:
version "4.0.10"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3"
Expand All @@ -980,6 +1000,16 @@ js-tokens@^4.0.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==

jszip@^3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2"
integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
dependencies:
lie "~3.3.0"
pako "~1.0.2"
readable-stream "~2.3.6"
setimmediate "^1.0.5"

keygrip@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226"
Expand Down Expand Up @@ -1053,6 +1083,13 @@ koa@^2.13.0:
type-is "^1.6.16"
vary "^1.1.2"

lie@~3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
dependencies:
immediate "~3.0.5"

lit-html@^2.0.1:
version "2.4.0"
resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-2.4.0.tgz#b510430f39a56ec959167ed1187241a4e3ab1574"
Expand Down Expand Up @@ -1157,6 +1194,11 @@ open@^8.0.2:
is-docker "^2.1.1"
is-wsl "^2.2.0"

pako@~1.0.2:
version "1.0.11"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==

parse5@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
Expand Down Expand Up @@ -1205,11 +1247,29 @@ postcss@^8.4.16:
picocolors "^1.0.0"
source-map-js "^1.0.2"

process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==

punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==

readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"

readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
Expand Down Expand Up @@ -1258,13 +1318,23 @@ [email protected]:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==

safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==

semver@^7.3.4:
version "7.3.8"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
dependencies:
lru-cache "^6.0.0"

setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==

[email protected]:
version "1.1.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
Expand All @@ -1285,6 +1355,13 @@ source-map-js@^1.0.2:
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==

string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"

style-mod@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.0.0.tgz#97e7c2d68b592975f2ca7a63d0dd6fcacfe35a01"
Expand Down Expand Up @@ -1354,6 +1431,11 @@ typical@^5.2.0:
resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066"
integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==

util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==

vary@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
Expand Down

0 comments on commit d033d97

Please sign in to comment.