diff --git a/js/events/download.js b/js/events/download.js index a60688f..e66c62c 100644 --- a/js/events/download.js +++ b/js/events/download.js @@ -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); @@ -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) { diff --git a/js/view.js b/js/view.js index b5e7442..ac3f8c4 100644 --- a/js/view.js +++ b/js/view.js @@ -62,6 +62,10 @@ const menu = state => html` @click=${() => downloadText(`${state.name === "" ? "anon" : state.name}.js`, state.codemirror.view.state.doc.toString())}> js + { // 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` - // - // ` + const toAdd = svg` + + ` - // toRender.push(toAdd); + toRender.push(toAdd); } else if (d.type === "text") { toRender.push(renderText(d)); diff --git a/package.json b/package.json index 65a90bb..3a00fa7 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/yarn.lock b/yarn.lock index dc30bd7..cce4ea0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" @@ -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" @@ -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== + inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== -inherits@2.0.4: +inherits@2.0.4, 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== @@ -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" @@ -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" @@ -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" @@ -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" @@ -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" @@ -1258,6 +1318,11 @@ safe-buffer@5.2.1: 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" @@ -1265,6 +1330,11 @@ semver@^7.3.4: 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== + setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" @@ -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" @@ -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"