Compare commits
10 Commits
convert-to
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e79cdbc32a | ||
![]() |
4642203160 | ||
![]() |
aa4834ffc0 | ||
![]() |
4bbde6097f | ||
![]() |
8e60dcb97b | ||
![]() |
e62b2c3fbc | ||
![]() |
ea95438786 | ||
![]() |
b2bee87c02 | ||
![]() |
0ac018073c | ||
![]() |
d3546313a3 |
12
.gitignore
vendored
12
.gitignore
vendored
@ -75,22 +75,12 @@ typings/
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
@ -107,4 +97,4 @@ dist
|
||||
gerber/
|
||||
hello.txt
|
||||
test/tmp/*
|
||||
test/archiveTest
|
||||
test/arduino/*
|
196
dist/index.js
vendored
Normal file
196
dist/index.js
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ImageGenerator = void 0;
|
||||
//Modules
|
||||
const adm_zip_1 = __importDefault(require("adm-zip"));
|
||||
const fs_extra_1 = require("fs-extra");
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const pcb_stackup_1 = __importDefault(require("pcb-stackup"));
|
||||
const sharp_1 = __importDefault(require("sharp"));
|
||||
const node_stream_1 = require("node:stream");
|
||||
const node_fs_1 = require("node:fs");
|
||||
//Class definition
|
||||
class ImageGenerator {
|
||||
constructor(folderConfig, imgConfig, layerNames) {
|
||||
this.folderConfig = folderConfig;
|
||||
this.imgConfig = imgConfig;
|
||||
this.layerNames = layerNames;
|
||||
//Ensure folders exist
|
||||
if (!validFolder(folderConfig.tmpDir, true))
|
||||
throw new Error('Temp directory is invalid');
|
||||
if (!validFolder(folderConfig.imgDir, true))
|
||||
throw new Error('Image directory is invalid');
|
||||
}
|
||||
//Layer promise
|
||||
getLayers(dir, layerNames) {
|
||||
//Check correct number of layers and folder exists
|
||||
layerNames.forEach((layerName) => {
|
||||
if (!(0, node_fs_1.existsSync)(path_1.default.join(dir, layerName))) {
|
||||
throw `Missing layer: ${layerName}`;
|
||||
}
|
||||
});
|
||||
if (!(0, node_fs_1.existsSync)(dir)) {
|
||||
throw new Error('Folder not there');
|
||||
}
|
||||
//Return layer promise
|
||||
const layersPromise = new Promise(function (resolve, reject) {
|
||||
const layers = layerNames.map((layerName) => ({
|
||||
filename: layerName,
|
||||
gerber: (0, node_fs_1.createReadStream)(path_1.default.join(dir, layerName)),
|
||||
}));
|
||||
if (layers.length === layerNames.length) {
|
||||
resolve(layers);
|
||||
}
|
||||
else {
|
||||
reject('Invalid layer count');
|
||||
}
|
||||
});
|
||||
return layersPromise;
|
||||
}
|
||||
//Clean up the archive folder in the specified directory
|
||||
static cleanupFiles(dir) {
|
||||
try {
|
||||
const folder = path_1.default.join(dir, 'archive');
|
||||
(0, fs_extra_1.emptyDirSync)(folder);
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
// * Take an archive containing gerber files, config object, temporary dir
|
||||
// * and output dir and create a PNG image from the gerber in the output dir
|
||||
// * @param {string} gerber Path to an archive file containing gerber
|
||||
// * @returns {Promise.<string>} Promise to return path to image
|
||||
gerberToImage(gerber) {
|
||||
// Create output dir if it doesn't exist
|
||||
try {
|
||||
(0, fs_extra_1.ensureDirSync)(this.folderConfig.imgDir);
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
// Check temp and output dirs exist
|
||||
if (!(0, node_fs_1.existsSync)(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!(0, node_fs_1.existsSync)(this.folderConfig.tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
if (!(0, node_fs_1.existsSync)(this.folderConfig.imgDir)) {
|
||||
throw Error('Output folder does not exist.');
|
||||
}
|
||||
// Set filenames
|
||||
//Use the filename of the gerber zip to determine the output png filename
|
||||
const imageName = path_1.default.basename(gerber, '.zip');
|
||||
const destFile = `${path_1.default.join(this.folderConfig.imgDir, imageName)}.png`;
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.layerNames) {
|
||||
throw new Error('You must supply an array of layer names.');
|
||||
}
|
||||
//Extract the passed in zip file
|
||||
extractArchive(gerber, this.folderConfig.tmpDir);
|
||||
this.getLayers(path_1.default.join(this.folderConfig.tmpDir, 'archive'), this.layerNames)
|
||||
.then(pcb_stackup_1.default)
|
||||
.then((stackup) => {
|
||||
(0, sharp_1.default)(Buffer.from(stackup.top.svg), {
|
||||
density: this.imgConfig.density,
|
||||
})
|
||||
.resize({ width: this.imgConfig.resizeWidth })
|
||||
.png({ compressionLevel: this.imgConfig.compLevel })
|
||||
.toFile(destFile);
|
||||
})
|
||||
.then(() => {
|
||||
ImageGenerator.cleanupFiles(this.folderConfig.tmpDir);
|
||||
resolve(destFile);
|
||||
})
|
||||
.catch((e) => {
|
||||
ImageGenerator.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(new Error(e));
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Take an archive containing gerber files and return a stream containing
|
||||
* a PNG image from the gerber */
|
||||
gerberToStream(gerber) {
|
||||
// Check temp and output dirs exist
|
||||
if (!(0, node_fs_1.existsSync)(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!(0, node_fs_1.existsSync)(this.folderConfig.tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
if (!(0, node_fs_1.existsSync)(this.folderConfig.imgDir)) {
|
||||
throw Error('Output folder does not exist.');
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
extractArchive(gerber, this.folderConfig.tmpDir);
|
||||
if (!this.layerNames)
|
||||
throw new Error('No layers provided');
|
||||
this.getLayers(path_1.default.join(this.folderConfig.tmpDir, 'archive'), this.layerNames)
|
||||
.then(pcb_stackup_1.default)
|
||||
.then((stackup) => {
|
||||
(0, sharp_1.default)(Buffer.from(stackup.top.svg), {
|
||||
density: this.imgConfig.density,
|
||||
})
|
||||
.resize({ width: this.imgConfig.resizeWidth })
|
||||
.png({ compressionLevel: this.imgConfig.compLevel })
|
||||
.toBuffer()
|
||||
.then((buffer) => {
|
||||
ImageGenerator.cleanupFiles(this.folderConfig.tmpDir);
|
||||
const stream = new node_stream_1.Readable();
|
||||
stream.push(buffer);
|
||||
stream.push(null);
|
||||
resolve(stream);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
ImageGenerator.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(new Error(e));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.ImageGenerator = ImageGenerator;
|
||||
//File methods
|
||||
//Check that a folder exists and is writeable
|
||||
function validFolder(dir, checkPerms) {
|
||||
if (!(0, node_fs_1.existsSync)(dir)) {
|
||||
throw Error('Folder does not exist.');
|
||||
}
|
||||
//Check folder permissions, will throw error if not readable or writeable
|
||||
if (checkPerms) {
|
||||
(0, node_fs_1.accessSync)(dir, node_fs_1.constants.R_OK | node_fs_1.constants.W_OK);
|
||||
(0, node_fs_1.accessSync)(dir, node_fs_1.constants.R_OK | node_fs_1.constants.W_OK);
|
||||
}
|
||||
//All checks passed
|
||||
return true;
|
||||
}
|
||||
function extractArchive(fileName, outputDir) {
|
||||
//Check archive exists
|
||||
if (!(0, node_fs_1.existsSync)(fileName)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
//Check output dir is valid
|
||||
if (!validFolder(outputDir, true))
|
||||
throw new Error('Output directory is not valid');
|
||||
//Attempt to extract archive
|
||||
const zip = new adm_zip_1.default(fileName);
|
||||
try {
|
||||
zip.extractAllTo(path_1.default.join(outputDir, 'archive'));
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return zip.getEntries().length;
|
||||
}
|
381
package-lock.json
generated
381
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@nplayfair/npe_gerber",
|
||||
"version": "0.3.0",
|
||||
"version": "1.0.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@nplayfair/npe_gerber",
|
||||
"version": "0.3.0",
|
||||
"version": "1.0.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.16",
|
||||
@ -18,9 +18,11 @@
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/jest": "^29.5.14",
|
||||
"eslint": "^9.29.0",
|
||||
"jest": "^30.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"ts-jest": "^29.4.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.34.0"
|
||||
@ -1999,6 +2001,187 @@
|
||||
"@types/istanbul-lib-report": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest": {
|
||||
"version": "29.5.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz",
|
||||
"integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"expect": "^29.0.0",
|
||||
"pretty-format": "^29.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/@jest/expect-utils": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
|
||||
"integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jest-get-type": "^29.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/@jest/schemas": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
|
||||
"integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "^0.27.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/@jest/types": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
|
||||
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
"@types/istanbul-reports": "^3.0.0",
|
||||
"@types/node": "*",
|
||||
"@types/yargs": "^17.0.8",
|
||||
"chalk": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/@sinclair/typebox": {
|
||||
"version": "0.27.8",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
||||
"integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/ci-info": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
|
||||
"integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/sibiraj-s"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/expect": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
|
||||
"integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jest/expect-utils": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"jest-matcher-utils": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/jest-diff": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
|
||||
"integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"diff-sequences": "^29.6.3",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/jest-matcher-utils": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
|
||||
"integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"jest-diff": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/jest-message-util": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
|
||||
"integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.12.13",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/stack-utils": "^2.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"micromatch": "^4.0.4",
|
||||
"pretty-format": "^29.7.0",
|
||||
"slash": "^3.0.0",
|
||||
"stack-utils": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/jest-util": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
|
||||
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"ci-info": "^3.2.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"picomatch": "^2.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jest/node_modules/pretty-format": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
|
||||
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^18.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
@ -2727,6 +2910,13 @@
|
||||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/babel-jest": {
|
||||
"version": "30.0.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.0.tgz",
|
||||
@ -2889,6 +3079,19 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/bs-logger": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
|
||||
"integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-json-stable-stringify": "2.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/bser": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
|
||||
@ -3279,6 +3482,16 @@
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/diff-sequences": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
|
||||
"integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
@ -3286,6 +3499,22 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"jake": "^10.8.5"
|
||||
},
|
||||
"bin": {
|
||||
"ejs": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.167",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz",
|
||||
@ -3661,6 +3890,39 @@
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/minimatch": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
@ -4227,6 +4489,25 @@
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jake": {
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
|
||||
"integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"async": "^3.2.3",
|
||||
"chalk": "^4.0.2",
|
||||
"filelist": "^1.0.4",
|
||||
"minimatch": "^3.1.2"
|
||||
},
|
||||
"bin": {
|
||||
"jake": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jest": {
|
||||
"version": "30.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jest/-/jest-30.0.0.tgz",
|
||||
@ -4451,6 +4732,16 @@
|
||||
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-get-type": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
|
||||
"integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-haste-map": {
|
||||
"version": "30.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.0.tgz",
|
||||
@ -4991,6 +5282,13 @@
|
||||
"integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@ -6175,6 +6473,85 @@
|
||||
"typescript": ">=4.8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-jest": {
|
||||
"version": "29.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz",
|
||||
"integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bs-logger": "^0.2.6",
|
||||
"ejs": "^3.1.10",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
"json5": "^2.2.3",
|
||||
"lodash.memoize": "^4.1.2",
|
||||
"make-error": "^1.3.6",
|
||||
"semver": "^7.7.2",
|
||||
"type-fest": "^4.41.0",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"ts-jest": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": ">=7.0.0-beta.0 <8",
|
||||
"@jest/transform": "^29.0.0 || ^30.0.0",
|
||||
"@jest/types": "^29.0.0 || ^30.0.0",
|
||||
"babel-jest": "^29.0.0 || ^30.0.0",
|
||||
"jest": "^29.0.0 || ^30.0.0",
|
||||
"jest-util": "^29.0.0 || ^30.0.0",
|
||||
"typescript": ">=4.3 <6"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@babel/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@jest/transform": {
|
||||
"optional": true
|
||||
},
|
||||
"@jest/types": {
|
||||
"optional": true
|
||||
},
|
||||
"babel-jest": {
|
||||
"optional": true
|
||||
},
|
||||
"esbuild": {
|
||||
"optional": true
|
||||
},
|
||||
"jest-util": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ts-jest/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-jest/node_modules/type-fest": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
|
||||
"integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
|
||||
"dev": true,
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||
|
36
package.json
36
package.json
@ -1,10 +1,13 @@
|
||||
{
|
||||
"name": "@nplayfair/npe_gerber",
|
||||
"version": "0.3.0",
|
||||
"version": "1.0.2",
|
||||
"description": "Create a PCB image from gerber files",
|
||||
"main": "index.js",
|
||||
"main": "dist/index.js",
|
||||
"types": "types/npe_gerber.d.ts",
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"build": "tsc --build",
|
||||
"test": "NODE_ENV=test PORT=7788 jest",
|
||||
"test:watch": "npm run test -- --watchAll",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"repository": {
|
||||
@ -22,6 +25,31 @@
|
||||
"image",
|
||||
"gerber"
|
||||
],
|
||||
"jest": {
|
||||
"verbose": true,
|
||||
"modulePathIgnorePatterns": [
|
||||
"<rootDir>/node_modules"
|
||||
],
|
||||
"roots": [
|
||||
"<rootDir>/test"
|
||||
],
|
||||
"transform": {
|
||||
"^.+\\.tsx?$": "ts-jest"
|
||||
},
|
||||
"testEnvironment": "node",
|
||||
"testPathIgnorePatterns": [
|
||||
"/node_modules"
|
||||
],
|
||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.16",
|
||||
"fs-extra": "^11.3.0",
|
||||
@ -32,9 +60,11 @@
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/jest": "^29.5.14",
|
||||
"eslint": "^9.29.0",
|
||||
"jest": "^30.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"ts-jest": "^29.4.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.34.0"
|
||||
|
264
src/index.ts
264
src/index.ts
@ -1,139 +1,41 @@
|
||||
//Modules
|
||||
// const AdmZip = require('adm-zip');
|
||||
import AdmZip from 'adm-zip';
|
||||
import { emptyDirSync, ensureDirSync } from 'fs-extra';
|
||||
import path from 'path';
|
||||
import pcbStackup from 'pcb-stackup';
|
||||
import sharp from 'sharp';
|
||||
import { Readable } from 'node:stream';
|
||||
import { existsSync, accessSync, createReadStream, constants } from 'node:fs';
|
||||
import { existsSync, createReadStream, accessSync, constants } from 'node:fs';
|
||||
|
||||
//Class definition
|
||||
class ImageGenerator implements ZipExtractor, LayerGenerator {
|
||||
export class ImageGenerator {
|
||||
constructor(
|
||||
public folderConfig: FolderConfig,
|
||||
public imgConfig: ImageConfig,
|
||||
public layerNames?: string[],
|
||||
public layerNames: string[],
|
||||
) {
|
||||
//Ensure folders exist
|
||||
if (!existsSync(folderConfig.tmpDir))
|
||||
throw new Error('Temp dir does not exist');
|
||||
if (!this.validFolder(folderConfig.tmpDir, true))
|
||||
throw new Error('Temp directory is invalid');
|
||||
|
||||
if (!existsSync(folderConfig.imgDir))
|
||||
throw new Error('Image dir does not exist');
|
||||
|
||||
//Check folder permissions
|
||||
accessSync(folderConfig.tmpDir, constants.R_OK | constants.W_OK);
|
||||
accessSync(folderConfig.imgDir, constants.R_OK | constants.W_OK);
|
||||
if (!this.validFolder(folderConfig.imgDir, true))
|
||||
throw new Error('Image directory is invalid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the passed in zip file
|
||||
|
||||
*/
|
||||
public extractArchive(fileName: string, tmpDir: string): number {
|
||||
// Check archive exists
|
||||
if (!existsSync(fileName)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
//Check temp folder exists
|
||||
if (!existsSync(tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
|
||||
const zip = new AdmZip(fileName);
|
||||
zip.extractAllTo(path.join(tmpDir, 'archive'));
|
||||
|
||||
return zip.getEntries().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary test method zip file
|
||||
|
||||
*/
|
||||
public testArchive(fileName: string, tmpDir: string): number {
|
||||
// Check archive exists
|
||||
try {
|
||||
if (!existsSync(fileName)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!existsSync(tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
console.error(e);
|
||||
}
|
||||
const zip = new AdmZip(fileName);
|
||||
return zip.getEntries().length;
|
||||
}
|
||||
|
||||
//Layer promise
|
||||
public getLayers(dir: string, layerNames: string[]): Promise<Layers[]> {
|
||||
//Check correct number of layers and folder exists
|
||||
layerNames.forEach((layerName) => {
|
||||
if (!existsSync(path.join(dir, layerName))) {
|
||||
throw `Missing layer: ${layerName}`;
|
||||
}
|
||||
});
|
||||
if (!existsSync(dir)) {
|
||||
throw new Error('Folder not there');
|
||||
}
|
||||
//Return layer promise
|
||||
const layersPromise = new Promise<Layers[]>(function (resolve, reject) {
|
||||
const layers: Layers[] = layerNames.map((layerName: string) => ({
|
||||
filename: layerName,
|
||||
gerber: createReadStream(path.join(dir, layerName)),
|
||||
}));
|
||||
if (layers.length === layerNames.length) {
|
||||
resolve(layers);
|
||||
} else {
|
||||
reject('Invalid layer count');
|
||||
}
|
||||
});
|
||||
return layersPromise;
|
||||
}
|
||||
|
||||
//Clean up the archive folder in the specified directory
|
||||
public static cleanupFiles(dir: string): void {
|
||||
try {
|
||||
const folder = path.join(dir, 'archive');
|
||||
emptyDirSync(folder);
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// * Take an archive containing gerber files, config object, temporary dir
|
||||
// * and output dir and create a PNG image from the gerber in the output dir
|
||||
// * @param {string} gerber Path to an archive file containing gerber
|
||||
// * @returns {Promise.<string>} Promise to return path to image
|
||||
|
||||
//Take an archive containing gerber files, config object, temporary dir
|
||||
//and output dir and create a PNG image from the gerber in the output dir
|
||||
public gerberToImage(gerber: string) {
|
||||
//Check gerber archive exists
|
||||
if (!existsSync(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
|
||||
// Create output dir if it doesn't exist
|
||||
try {
|
||||
ensureDirSync(this.folderConfig.imgDir);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Check temp and output dirs exist
|
||||
try {
|
||||
if (!existsSync(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!existsSync(this.folderConfig.tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
if (!existsSync(this.folderConfig.imgDir)) {
|
||||
throw Error('Output folder does not exist.');
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,9 +46,19 @@ class ImageGenerator implements ZipExtractor, LayerGenerator {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.layerNames) {
|
||||
throw new Error('You must supply an array of layer names.');
|
||||
reject('You must supply an array of layer names.');
|
||||
}
|
||||
//Extract the passed in zip file
|
||||
this.extractArchive(gerber, this.folderConfig.tmpDir);
|
||||
//Check all layers present
|
||||
this.layerNames.forEach((layerName) => {
|
||||
if (
|
||||
!existsSync(path.join(this.folderConfig.tmpDir, 'archive', layerName))
|
||||
) {
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(`Missing layer: ${layerName}`);
|
||||
}
|
||||
});
|
||||
this.getLayers(
|
||||
path.join(this.folderConfig.tmpDir, 'archive'),
|
||||
this.layerNames,
|
||||
@ -161,11 +73,11 @@ class ImageGenerator implements ZipExtractor, LayerGenerator {
|
||||
.toFile(destFile);
|
||||
})
|
||||
.then(() => {
|
||||
ImageGenerator.cleanupFiles(this.folderConfig.tmpDir);
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
resolve(destFile);
|
||||
})
|
||||
.catch((e) => {
|
||||
ImageGenerator.cleanupFiles(this.folderConfig.tmpDir);
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(new Error(e));
|
||||
});
|
||||
});
|
||||
@ -173,31 +85,26 @@ class ImageGenerator implements ZipExtractor, LayerGenerator {
|
||||
|
||||
/**
|
||||
* Take an archive containing gerber files and return a stream containing
|
||||
* a PNG image from the gerber
|
||||
* @param {string} gerber Path to an archive file containing gerber
|
||||
* @returns {Promise.<stream.Readable>} Promise that resolves to a PNG stream
|
||||
*/
|
||||
* a PNG image from the gerber */
|
||||
|
||||
gerberToStream(gerber: string) {
|
||||
// Check temp and output dirs exist
|
||||
try {
|
||||
// Check gerber archive exists
|
||||
if (!existsSync(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!existsSync(this.folderConfig.tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
if (!existsSync(this.folderConfig.imgDir)) {
|
||||
throw Error('Output folder does not exist.');
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.extractArchive(gerber, this.folderConfig.tmpDir);
|
||||
if (!this.layerNames) throw new Error('No layers provided');
|
||||
//Check all layers present
|
||||
this.layerNames.forEach((layerName) => {
|
||||
if (
|
||||
!existsSync(path.join(this.folderConfig.tmpDir, 'archive', layerName))
|
||||
) {
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(`Missing layer: ${layerName}`);
|
||||
}
|
||||
});
|
||||
this.getLayers(
|
||||
path.join(this.folderConfig.tmpDir, 'archive'),
|
||||
this.layerNames,
|
||||
@ -211,7 +118,7 @@ class ImageGenerator implements ZipExtractor, LayerGenerator {
|
||||
.png({ compressionLevel: this.imgConfig.compLevel })
|
||||
.toBuffer()
|
||||
.then((buffer) => {
|
||||
ImageGenerator.cleanupFiles(this.folderConfig.tmpDir);
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
const stream = new Readable();
|
||||
stream.push(buffer);
|
||||
stream.push(null);
|
||||
@ -219,13 +126,94 @@ class ImageGenerator implements ZipExtractor, LayerGenerator {
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
ImageGenerator.cleanupFiles(this.folderConfig.tmpDir);
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(new Error(e));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//Layer methods
|
||||
//Returns promise that resolves to array of Layers
|
||||
private getLayers(dir: string, layerNames: string[]): Promise<Layers[]> {
|
||||
//Check correct number of layers and folder exists
|
||||
// try {
|
||||
// layerNames.forEach((layerName) => {
|
||||
// if (!existsSync(path.join(dir, layerName))) {
|
||||
// this.cleanupFiles(dir);
|
||||
// throw new Error(`Missing layer: ${layerName}`);
|
||||
// }
|
||||
// });
|
||||
// } catch (error: unknown) {
|
||||
// if (error instanceof Error) {
|
||||
// {
|
||||
// console.error(error.message);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
//Return layer promise
|
||||
const layersPromise = new Promise<Layers[]>(function (resolve, reject) {
|
||||
const layers: Layers[] = layerNames.map((layerName: string) => ({
|
||||
filename: layerName,
|
||||
gerber: createReadStream(path.join(dir, layerName)),
|
||||
}));
|
||||
if (layers.length === layerNames.length) {
|
||||
resolve(layers);
|
||||
} else {
|
||||
reject(new Error('Invalid layer count'));
|
||||
}
|
||||
});
|
||||
return layersPromise;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ImageGenerator,
|
||||
};
|
||||
//File methods
|
||||
//Check that a folder exists and is writeable
|
||||
private validFolder(dir: string, checkPerms?: boolean): boolean {
|
||||
if (!existsSync(dir)) {
|
||||
throw Error('Folder does not exist.');
|
||||
}
|
||||
//Check folder permissions, will throw error if not readable or writeable
|
||||
if (checkPerms) {
|
||||
accessSync(dir, constants.R_OK | constants.W_OK);
|
||||
accessSync(dir, constants.R_OK | constants.W_OK);
|
||||
}
|
||||
|
||||
//All checks passed
|
||||
return true;
|
||||
}
|
||||
|
||||
private extractArchive(fileName: string, outputDir: string): number {
|
||||
//Check archive exists
|
||||
if (!existsSync(fileName)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
|
||||
//Check output dir is valid
|
||||
if (!this.validFolder(outputDir, true))
|
||||
throw new Error('Output directory is not valid');
|
||||
|
||||
//Attempt to extract archive
|
||||
const zip = new AdmZip(fileName);
|
||||
try {
|
||||
zip.extractAllTo(path.join(outputDir, 'archive'));
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return zip.getEntries().length;
|
||||
}
|
||||
|
||||
//Clean up the archive folder in the specified directory
|
||||
private cleanupFiles(dir: string): void {
|
||||
try {
|
||||
const folder = path.join(dir, 'archive');
|
||||
emptyDirSync(folder);
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -1,209 +0,0 @@
|
||||
const path = require('path');
|
||||
const { readdirSync, ReadStream } = require('node:fs');
|
||||
const { Readable } = require('node:stream');
|
||||
const { ImageGenerator } = require('../dist/index.js');
|
||||
require('../dist/index.js');
|
||||
|
||||
const testGerber = path.join(__dirname, 'Arduino-Pro-Mini.zip');
|
||||
const incompleteGerber = path.join(__dirname, 'incomplete.zip');
|
||||
const testLayers = path.join(__dirname, 'layers');
|
||||
const emptyFolder = path.join(__dirname, 'layers', 'Empty');
|
||||
const archiveTestFolder = path.join(__dirname, 'archiveTest');
|
||||
const folderConfig = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
const noTempConfig = {
|
||||
tmpDir: emptyFolder,
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
const tmpNotExist = {
|
||||
tmpDir: path.join(__dirname, 'InvalidFolderName'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
const imgNotExist = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'InvalidFolderName'),
|
||||
};
|
||||
const tmpBadPerms = {
|
||||
tmpDir: path.join(__dirname, 'badPerms'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
const imgBadPerms = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'badPerms'),
|
||||
};
|
||||
const noImageConfig = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: emptyFolder,
|
||||
};
|
||||
const imgConfig = {
|
||||
resizeWidth: 600,
|
||||
density: 1000,
|
||||
compLevel: 1,
|
||||
};
|
||||
const layerNames = [
|
||||
'CAMOutputs/DrillFiles/drills.xln',
|
||||
'CAMOutputs/GerberFiles/copper_top.gbr',
|
||||
'CAMOutputs/GerberFiles/silkscreen_top.gbr',
|
||||
'CAMOutputs/GerberFiles/soldermask_top.gbr',
|
||||
'CAMOutputs/GerberFiles/solderpaste_top.gbr',
|
||||
'CAMOutputs/GerberFiles/profile.gbr',
|
||||
];
|
||||
|
||||
const fileProc = new ImageGenerator(folderConfig, imgConfig, layerNames);
|
||||
const fileProcNoTemp = new ImageGenerator(noTempConfig, imgConfig, layerNames);
|
||||
const fileProcNoImage = new ImageGenerator(
|
||||
noImageConfig,
|
||||
imgConfig,
|
||||
layerNames,
|
||||
);
|
||||
|
||||
/**************
|
||||
* Tests
|
||||
***************/
|
||||
|
||||
// Test constructor
|
||||
describe('Creating an ImageGenerator object', () => {
|
||||
const imgGen = new ImageGenerator(folderConfig, imgConfig);
|
||||
test('should create a valid object when passed the correct files and configuration', () => {
|
||||
expect(imgGen).toBeInstanceOf(ImageGenerator);
|
||||
});
|
||||
// Image processing configuration
|
||||
test('image width should be 600', () => {
|
||||
expect(imgGen.imgConfig.resizeWidth).toBe(600);
|
||||
});
|
||||
test('image density should be 1000', () => {
|
||||
expect(imgGen.imgConfig.density).toBe(1000);
|
||||
});
|
||||
test('image compression level should be 1', () => {
|
||||
expect(imgGen.imgConfig.compLevel).toBe(1);
|
||||
});
|
||||
test('folders should be the ones specified in the folder config parameter', () => {
|
||||
expect(imgGen.folderConfig.tmpDir).toBe(path.join(__dirname, 'tmp'));
|
||||
expect(imgGen.folderConfig.imgDir).toBe(path.join(__dirname, 'tmp'));
|
||||
});
|
||||
});
|
||||
|
||||
// Testing folder config
|
||||
describe('Passing in', () => {
|
||||
test('a non-existent tmp folder should throw error', () => {
|
||||
expect(() => {
|
||||
new ImageGenerator(tmpNotExist, imgConfig);
|
||||
}).toThrow();
|
||||
});
|
||||
test('a tmp folder with invalid permissions should throw error', () => {
|
||||
expect(() => {
|
||||
new ImageGenerator(tmpBadPerms, imgConfig);
|
||||
}).toThrow();
|
||||
});
|
||||
test('a non-existent img folder should throw error', () => {
|
||||
expect(() => {
|
||||
new ImageGenerator(imgNotExist, imgConfig);
|
||||
}).toThrow();
|
||||
});
|
||||
test('an img folder with invalid permissions should throw error', () => {
|
||||
expect(() => {
|
||||
new ImageGenerator(imgBadPerms, imgConfig);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
//Layer methods
|
||||
describe('Getting layers', () => {
|
||||
const imgGen = new ImageGenerator(folderConfig, imgConfig);
|
||||
test('should return a promise of array of layers', () => {
|
||||
expect(imgGen.getLayers(testLayers, layerNames)).resolves.toBeInstanceOf(
|
||||
Array,
|
||||
);
|
||||
});
|
||||
|
||||
// test('should return a promise of array of layers', () => {
|
||||
// return imgGen.getLayers(testLayers, layerNames).then((data) => {
|
||||
// expect(data).toBeInstanceOf(Array);
|
||||
// });
|
||||
// });
|
||||
|
||||
test('should throw error if the layers folder is not valid', () => {
|
||||
expect(() => {
|
||||
imgGen.getLayers('some_invalid_folder', layerNames);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test('should throw error if incorrect number of layers supplied', () => {
|
||||
expect(() => {
|
||||
imgGen.getLayers(emptyFolder, layerNames);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
//Archive methods
|
||||
describe('When extracting an archive', () => {
|
||||
const imgGen = new ImageGenerator(folderConfig, imgConfig);
|
||||
test('a non-existent archive should throw an error', () => {
|
||||
expect(() =>
|
||||
imgGen.extractArchive('invalid.zip', folderConfig.tmpDir),
|
||||
).toThrow();
|
||||
});
|
||||
test('if the temp dir does not exist it should throw an error', () => {
|
||||
expect(() => imgGen.extractArchive(testGerber, 'some_invalid_dir')).toThrow(
|
||||
Error,
|
||||
);
|
||||
});
|
||||
test('it should load the archive and return the number of files extracted', () => {
|
||||
expect(() => {
|
||||
imgGen.testArchive(testGerber, archiveTestFolder);
|
||||
}).not.toThrow();
|
||||
expect(imgGen.testArchive(testGerber, archiveTestFolder)).toEqual(12);
|
||||
});
|
||||
test('it should extract archive and all files should be present', () => {
|
||||
expect(imgGen.testArchive(testGerber, archiveTestFolder)).toEqual(12);
|
||||
imgGen.extractArchive(testGerber, archiveTestFolder);
|
||||
const dirents = readdirSync(archiveTestFolder, {
|
||||
recursive: true,
|
||||
withFileTypes: true,
|
||||
});
|
||||
const numOutputFiles = dirents.filter((dirent) => dirent.isFile());
|
||||
expect(numOutputFiles).toHaveLength(12);
|
||||
});
|
||||
});
|
||||
|
||||
//Gerber methods
|
||||
describe('Converting a gerber to an image', () => {
|
||||
test('temp dir not existing should throw an error', () => {
|
||||
expect(() =>
|
||||
fileProcNoTemp
|
||||
.gerberToImage(testGerber)
|
||||
.toThrow(new Error('Temporary folder does not exist.')),
|
||||
);
|
||||
});
|
||||
test('output dir not existing should throw an error', () => {
|
||||
expect(() =>
|
||||
fileProcNoImage
|
||||
.gerberToImage(testGerber)
|
||||
.toThrow(new Error('Output folder does not exist.')),
|
||||
);
|
||||
});
|
||||
test('invalid archive file should throw an error', () => {
|
||||
expect(() =>
|
||||
fileProc
|
||||
.gerberToImage('invalid.zip')
|
||||
.toThrow(new Error('Archive does not exist.')),
|
||||
);
|
||||
});
|
||||
test('an archive with incomplete set of layers should throw an error', () => {
|
||||
expect(() => fileProc.gerberToImage(incompleteGerber).toThrow(Error));
|
||||
});
|
||||
test('gerber archive should resolve promise and return a filename of an image', () => {
|
||||
expect.assertions(1);
|
||||
return expect(fileProc.gerberToImage(testGerber)).resolves.toEqual(
|
||||
expect.stringContaining('Arduino-Pro-Mini.png'),
|
||||
);
|
||||
});
|
||||
test('Gerber archive should resolve promise and return a png stream', () => {
|
||||
expect.assertions(1);
|
||||
return expect(fileProc.gerberToStream(testGerber)).resolves.toBeInstanceOf(
|
||||
Readable,
|
||||
);
|
||||
});
|
||||
});
|
154
test/index.test.ts
Normal file
154
test/index.test.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import path from 'path';
|
||||
import { readdirSync } from 'node:fs';
|
||||
import { emptyDirSync } from 'fs-extra';
|
||||
import { Readable } from 'node:stream';
|
||||
import { ImageGenerator } from '../src/index';
|
||||
|
||||
//Sample data
|
||||
const arduinoGerber = path.join(__dirname, 'Arduino-Pro-Mini.zip');
|
||||
const incompleteGerber = path.join(__dirname, 'incomplete.zip');
|
||||
|
||||
//Correct folder configuration
|
||||
const folderConfig = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
//Folder configuration with non-existent tmpDir
|
||||
const tmpNotExist = {
|
||||
tmpDir: path.join(__dirname, 'InvalidFolderName'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
//Folder configuration with non-existent imgDir
|
||||
const imgNotExist = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'InvalidFolderName'),
|
||||
};
|
||||
//Folder configuration with bad permissions on tmpDir
|
||||
const tmpBadPerms = {
|
||||
tmpDir: path.join(__dirname, 'badPerms'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
//Folder configuration with bad permissions on imgDir
|
||||
const imgBadPerms = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'badPerms'),
|
||||
};
|
||||
//Correct folder configuration
|
||||
const arduinoConfig = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'arduino'),
|
||||
};
|
||||
|
||||
//Valid image configuration object
|
||||
const imgConfig = {
|
||||
resizeWidth: 600,
|
||||
density: 1000,
|
||||
compLevel: 1,
|
||||
};
|
||||
|
||||
//Valid array of layer names
|
||||
const layerNames = [
|
||||
'CAMOutputs/DrillFiles/drills.xln',
|
||||
'CAMOutputs/GerberFiles/copper_top.gbr',
|
||||
'CAMOutputs/GerberFiles/silkscreen_top.gbr',
|
||||
'CAMOutputs/GerberFiles/soldermask_top.gbr',
|
||||
'CAMOutputs/GerberFiles/solderpaste_top.gbr',
|
||||
'CAMOutputs/GerberFiles/profile.gbr',
|
||||
];
|
||||
|
||||
//===== Tests =====
|
||||
|
||||
//Setup
|
||||
beforeAll(() => {
|
||||
return emptyDirSync(folderConfig.tmpDir);
|
||||
});
|
||||
|
||||
// Test constructor
|
||||
describe('Creating an ImageGenerator object', () => {
|
||||
const imgGen = new ImageGenerator(folderConfig, imgConfig, layerNames);
|
||||
test('should create a valid object when passed the correct files and configuration', () => {
|
||||
expect(imgGen).toBeInstanceOf(ImageGenerator);
|
||||
});
|
||||
// Image processing configuration
|
||||
test('image width should be 600', () => {
|
||||
expect(imgGen.imgConfig.resizeWidth).toBe(600);
|
||||
});
|
||||
test('image density should be 1000', () => {
|
||||
expect(imgGen.imgConfig.density).toBe(1000);
|
||||
});
|
||||
test('image compression level should be 1', () => {
|
||||
expect(imgGen.imgConfig.compLevel).toBe(1);
|
||||
});
|
||||
test('folders should be the ones specified in the folder config parameter', () => {
|
||||
expect(imgGen.folderConfig.tmpDir).toBe(path.join(__dirname, 'tmp'));
|
||||
expect(imgGen.folderConfig.imgDir).toBe(path.join(__dirname, 'tmp'));
|
||||
});
|
||||
test('Layers should match layerNames sample array', () => {
|
||||
expect(imgGen.layerNames).toBe(layerNames);
|
||||
});
|
||||
afterAll(() => {
|
||||
return emptyDirSync(folderConfig.tmpDir);
|
||||
});
|
||||
});
|
||||
|
||||
//Test invalid folder configs
|
||||
describe('Attempting to create ImageGenerator object with', () => {
|
||||
afterAll(() => {
|
||||
return emptyDirSync(path.join(__dirname, 'tmp'));
|
||||
});
|
||||
test('non-existent temp folder should throw error', () => {
|
||||
expect(() => {
|
||||
const badGen = new ImageGenerator(tmpNotExist, imgConfig, layerNames);
|
||||
}).toThrow();
|
||||
});
|
||||
test('non-existent image folder should throw error', () => {
|
||||
expect(() => {
|
||||
const badGen = new ImageGenerator(imgNotExist, imgConfig, layerNames);
|
||||
}).toThrow();
|
||||
});
|
||||
test('temp folder with bad permissions should throw error', () => {
|
||||
expect(() => {
|
||||
const badGen = new ImageGenerator(tmpBadPerms, imgConfig, layerNames);
|
||||
}).toThrow();
|
||||
});
|
||||
test('image folder with bad permissions should throw error', () => {
|
||||
expect(() => {
|
||||
const badGen = new ImageGenerator(imgBadPerms, imgConfig, layerNames);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
//Create image from Arduino Gerber
|
||||
describe('Create image from Arduino gerber', () => {
|
||||
beforeAll(() => {
|
||||
return emptyDirSync(path.join(__dirname, 'arduino'));
|
||||
});
|
||||
beforeEach(() => {
|
||||
return emptyDirSync(folderConfig.tmpDir);
|
||||
});
|
||||
const arduinoGen = new ImageGenerator(arduinoConfig, imgConfig, layerNames);
|
||||
test('should create a valid object when passed the correct files and configuration', () => {
|
||||
expect(arduinoGen).toBeInstanceOf(ImageGenerator);
|
||||
});
|
||||
test('invalid archive file should throw an error', () => {
|
||||
expect(() => arduinoGen.gerberToImage('invalid.zip')).toThrow();
|
||||
});
|
||||
test('arduino archive should resolve promise and return a filename of an image', () => {
|
||||
expect.assertions(1);
|
||||
return expect(arduinoGen.gerberToImage(arduinoGerber)).resolves.toEqual(
|
||||
expect.stringContaining('Arduino-Pro-Mini.png'),
|
||||
);
|
||||
});
|
||||
test('arduino archive should resolve promise and return a png stream', () => {
|
||||
expect.assertions(1);
|
||||
return expect(
|
||||
arduinoGen.gerberToStream(arduinoGerber),
|
||||
).resolves.toBeInstanceOf(Readable);
|
||||
});
|
||||
test('incomplete archive file should throw an error', () => {
|
||||
expect.assertions(1);
|
||||
return expect(arduinoGen.gerberToImage(incompleteGerber)).rejects.toContain(
|
||||
'Missing',
|
||||
);
|
||||
});
|
||||
});
|
@ -1,77 +0,0 @@
|
||||
M48
|
||||
;GenerationSoftware,Autodesk,EAGLE,9.6.2*%
|
||||
;CreationDate,2021-02-01T16:14:31Z*%
|
||||
FMAT,2
|
||||
ICI,OFF
|
||||
METRIC,TZ,000.000
|
||||
T2C0.508
|
||||
T1C1.016
|
||||
%
|
||||
G90
|
||||
M71
|
||||
T1
|
||||
X16510Y24130
|
||||
X13589Y7493
|
||||
X2540Y31750
|
||||
X5080Y31750
|
||||
X7620Y31750
|
||||
X10160Y31750
|
||||
X12700Y31750
|
||||
X15240Y31750
|
||||
X13589Y20447
|
||||
X13589Y17907
|
||||
X1270Y29210
|
||||
X1270Y26670
|
||||
X1270Y24130
|
||||
X1270Y21590
|
||||
X1270Y19050
|
||||
X1270Y16510
|
||||
X1270Y13970
|
||||
X1270Y11430
|
||||
X1270Y8890
|
||||
X1270Y6350
|
||||
X1270Y3810
|
||||
X1270Y1270
|
||||
X16510Y1270
|
||||
X16510Y3810
|
||||
X16510Y6350
|
||||
X16510Y8890
|
||||
X16510Y11430
|
||||
X16510Y13970
|
||||
X16510Y16510
|
||||
X16510Y29210
|
||||
X16510Y26670
|
||||
X13589Y10033
|
||||
X16510Y21590
|
||||
X16510Y19050
|
||||
T2
|
||||
X12192Y13970
|
||||
X2794Y2159
|
||||
X8890Y15621
|
||||
X9525Y16891
|
||||
X9322Y19812
|
||||
X15367Y15240
|
||||
X14046Y27280
|
||||
X11176Y26162
|
||||
X6350Y26416
|
||||
X6629Y22200
|
||||
X7366Y11557
|
||||
X8001Y12700
|
||||
X11303Y14986
|
||||
X8890Y4572
|
||||
X5969Y3048
|
||||
X10414Y11557
|
||||
X11811Y5842
|
||||
X8153Y29261
|
||||
X6401Y24130
|
||||
X11405Y24130
|
||||
X3683Y26035
|
||||
X7722Y16942
|
||||
X14757Y25400
|
||||
X6350Y14478
|
||||
X10414Y20447
|
||||
X5105Y29286
|
||||
X8890Y2667
|
||||
X9652Y14478
|
||||
X11430Y12700
|
||||
M30
|
@ -1,44 +0,0 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%INVias*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10C,1.016000*%
|
||||
|
||||
|
||||
D10*
|
||||
X114300Y127000D03*
|
||||
X96520Y144780D03*
|
||||
X88900Y26670D03*
|
||||
X51054Y292862D03*
|
||||
X104140Y204470D03*
|
||||
X63500Y144780D03*
|
||||
X147574Y254000D03*
|
||||
X77216Y169418D03*
|
||||
X36830Y260350D03*
|
||||
X114046Y241300D03*
|
||||
X64008Y241300D03*
|
||||
X81534Y292608D03*
|
||||
X118110Y58420D03*
|
||||
X104140Y115570D03*
|
||||
X59690Y30480D03*
|
||||
X88900Y45720D03*
|
||||
X113030Y149860D03*
|
||||
X80010Y127000D03*
|
||||
X73660Y115570D03*
|
||||
X66294Y221996D03*
|
||||
X63500Y264160D03*
|
||||
X111760Y261620D03*
|
||||
X140462Y272796D03*
|
||||
X153670Y152400D03*
|
||||
X93218Y198120D03*
|
||||
X95250Y168910D03*
|
||||
X88900Y156210D03*
|
||||
X27940Y21590D03*
|
||||
X121920Y139700D03*
|
||||
M02*
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
||||
{
|
||||
"Header": {
|
||||
"Comment": "All values are metric (mm)",
|
||||
"CreationDate": "2021-02-01T16:14:31Z",
|
||||
"GenerationSoftware": {
|
||||
"Application": "EAGLE",
|
||||
"Vendor": "Autodesk",
|
||||
"Version": "9.6.2"
|
||||
},
|
||||
"Part": "Single"
|
||||
},
|
||||
"Overall": {
|
||||
"BoardThickness": 1.57,
|
||||
"LayerNumber": 2,
|
||||
"Name": {
|
||||
"ProjectId": "Arduino-Pro-Mini"
|
||||
},
|
||||
"Owner": "nick nick <nick@chaosemerald.co.uk>",
|
||||
"Size": {
|
||||
"X": 17.78,
|
||||
"Y": 33.02
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%IN*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10C,0.254000*%
|
||||
|
||||
|
||||
D10*
|
||||
X0Y0D02*
|
||||
X177800Y0D01*
|
||||
X177800Y330200D01*
|
||||
X0Y330200D01*
|
||||
X0Y0D01*
|
||||
M02*
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,56 +0,0 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%INSoldermask Bottom*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10C,2.082800*%
|
||||
%ADD11R,1.203200X1.303200*%
|
||||
|
||||
|
||||
D10*
|
||||
X165100Y292100D03*
|
||||
X165100Y266700D03*
|
||||
X165100Y241300D03*
|
||||
X165100Y215900D03*
|
||||
X165100Y190500D03*
|
||||
X165100Y165100D03*
|
||||
X165100Y139700D03*
|
||||
X165100Y114300D03*
|
||||
X165100Y88900D03*
|
||||
X165100Y63500D03*
|
||||
X165100Y38100D03*
|
||||
X165100Y12700D03*
|
||||
X12700Y12700D03*
|
||||
X12700Y38100D03*
|
||||
X12700Y63500D03*
|
||||
X12700Y88900D03*
|
||||
X12700Y114300D03*
|
||||
X12700Y139700D03*
|
||||
X12700Y165100D03*
|
||||
X12700Y190500D03*
|
||||
X12700Y215900D03*
|
||||
X12700Y241300D03*
|
||||
X12700Y266700D03*
|
||||
X12700Y292100D03*
|
||||
X135890Y179070D03*
|
||||
X135890Y204470D03*
|
||||
X152400Y317500D03*
|
||||
X127000Y317500D03*
|
||||
X101600Y317500D03*
|
||||
X76200Y317500D03*
|
||||
X50800Y317500D03*
|
||||
X25400Y317500D03*
|
||||
D11*
|
||||
X129794Y223148D03*
|
||||
X129794Y240148D03*
|
||||
X142494Y223148D03*
|
||||
X142494Y240148D03*
|
||||
D10*
|
||||
X135890Y74930D03*
|
||||
X135890Y100330D03*
|
||||
M02*
|
@ -1,376 +0,0 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%INSoldermask Top*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10R,1.303200X1.203200*%
|
||||
%ADD11R,1.803200X1.603200*%
|
||||
%ADD12C,0.505344*%
|
||||
%ADD13R,1.203200X1.303200*%
|
||||
%ADD14R,1.403200X0.753200*%
|
||||
%ADD15C,2.082800*%
|
||||
%ADD16R,1.473200X0.762000*%
|
||||
%ADD17R,0.762000X1.473200*%
|
||||
%ADD18C,0.838200*%
|
||||
%ADD19R,1.473200X0.838200*%
|
||||
%ADD20R,1.727200X0.965200*%
|
||||
%ADD21C,0.653200*%
|
||||
%ADD22R,1.534159X3.495041*%
|
||||
|
||||
|
||||
D10*
|
||||
X80382Y204216D03*
|
||||
X63382Y204216D03*
|
||||
D11*
|
||||
X46990Y274350D03*
|
||||
X46990Y246350D03*
|
||||
D10*
|
||||
X97400Y266700D03*
|
||||
X80400Y266700D03*
|
||||
D12*
|
||||
X94434Y222184D02*
|
||||
X94434Y215204D01*
|
||||
X94434Y222184D02*
|
||||
X101414Y222184D01*
|
||||
X101414Y215204D01*
|
||||
X94434Y215204D01*
|
||||
X94434Y220004D02*
|
||||
X101414Y220004D01*
|
||||
X76894Y222184D02*
|
||||
X76894Y215204D01*
|
||||
X76894Y222184D02*
|
||||
X83874Y222184D01*
|
||||
X83874Y215204D01*
|
||||
X76894Y215204D01*
|
||||
X76894Y220004D02*
|
||||
X83874Y220004D01*
|
||||
D13*
|
||||
X45466Y43824D03*
|
||||
X45466Y60824D03*
|
||||
D10*
|
||||
X98670Y72390D03*
|
||||
X81670Y72390D03*
|
||||
D13*
|
||||
X45466Y12074D03*
|
||||
X45466Y29074D03*
|
||||
X137160Y57522D03*
|
||||
X137160Y40522D03*
|
||||
D12*
|
||||
X133670Y11992D02*
|
||||
X140650Y11992D01*
|
||||
X140650Y5012D01*
|
||||
X133670Y5012D01*
|
||||
X133670Y11992D01*
|
||||
X133670Y9812D02*
|
||||
X140650Y9812D01*
|
||||
X140650Y29532D02*
|
||||
X133670Y29532D01*
|
||||
X140650Y29532D02*
|
||||
X140650Y22552D01*
|
||||
X133670Y22552D01*
|
||||
X133670Y29532D01*
|
||||
X133670Y27352D02*
|
||||
X140650Y27352D01*
|
||||
D14*
|
||||
X101901Y232308D03*
|
||||
X101901Y241808D03*
|
||||
X101901Y251308D03*
|
||||
X75899Y251308D03*
|
||||
X75899Y232308D03*
|
||||
D11*
|
||||
X132080Y227300D03*
|
||||
X132080Y255300D03*
|
||||
D15*
|
||||
X165100Y292100D03*
|
||||
X165100Y266700D03*
|
||||
X165100Y241300D03*
|
||||
X165100Y215900D03*
|
||||
X165100Y190500D03*
|
||||
X165100Y165100D03*
|
||||
X165100Y139700D03*
|
||||
X165100Y114300D03*
|
||||
X165100Y88900D03*
|
||||
X165100Y63500D03*
|
||||
X165100Y38100D03*
|
||||
X165100Y12700D03*
|
||||
X12700Y12700D03*
|
||||
X12700Y38100D03*
|
||||
X12700Y63500D03*
|
||||
X12700Y88900D03*
|
||||
X12700Y114300D03*
|
||||
X12700Y139700D03*
|
||||
X12700Y165100D03*
|
||||
X12700Y190500D03*
|
||||
X12700Y215900D03*
|
||||
X12700Y241300D03*
|
||||
X12700Y266700D03*
|
||||
X12700Y292100D03*
|
||||
X135890Y179070D03*
|
||||
X135890Y204470D03*
|
||||
D16*
|
||||
G36*
|
||||
X80860Y181950D02*
|
||||
X70444Y192366D01*
|
||||
X75832Y197754D01*
|
||||
X86248Y187338D01*
|
||||
X80860Y181950D01*
|
||||
G37*
|
||||
G36*
|
||||
X75203Y176293D02*
|
||||
X64787Y186709D01*
|
||||
X70175Y192097D01*
|
||||
X80591Y181681D01*
|
||||
X75203Y176293D01*
|
||||
G37*
|
||||
G36*
|
||||
X69546Y170637D02*
|
||||
X59130Y181053D01*
|
||||
X64518Y186441D01*
|
||||
X74934Y176025D01*
|
||||
X69546Y170637D01*
|
||||
G37*
|
||||
G36*
|
||||
X63889Y164980D02*
|
||||
X53473Y175396D01*
|
||||
X58861Y180784D01*
|
||||
X69277Y170368D01*
|
||||
X63889Y164980D01*
|
||||
G37*
|
||||
G36*
|
||||
X58232Y159323D02*
|
||||
X47816Y169739D01*
|
||||
X53204Y175127D01*
|
||||
X63620Y164711D01*
|
||||
X58232Y159323D01*
|
||||
G37*
|
||||
G36*
|
||||
X52575Y153666D02*
|
||||
X42159Y164082D01*
|
||||
X47547Y169470D01*
|
||||
X57963Y159054D01*
|
||||
X52575Y153666D01*
|
||||
G37*
|
||||
G36*
|
||||
X46919Y148009D02*
|
||||
X36503Y158425D01*
|
||||
X41891Y163813D01*
|
||||
X52307Y153397D01*
|
||||
X46919Y148009D01*
|
||||
G37*
|
||||
G36*
|
||||
X41262Y142352D02*
|
||||
X30846Y152768D01*
|
||||
X36234Y158156D01*
|
||||
X46650Y147740D01*
|
||||
X41262Y142352D01*
|
||||
G37*
|
||||
D17*
|
||||
G36*
|
||||
X36234Y121244D02*
|
||||
X30846Y126632D01*
|
||||
X41262Y137048D01*
|
||||
X46650Y131660D01*
|
||||
X36234Y121244D01*
|
||||
G37*
|
||||
G36*
|
||||
X41891Y115587D02*
|
||||
X36503Y120975D01*
|
||||
X46919Y131391D01*
|
||||
X52307Y126003D01*
|
||||
X41891Y115587D01*
|
||||
G37*
|
||||
G36*
|
||||
X47547Y109930D02*
|
||||
X42159Y115318D01*
|
||||
X52575Y125734D01*
|
||||
X57963Y120346D01*
|
||||
X47547Y109930D01*
|
||||
G37*
|
||||
G36*
|
||||
X53204Y104273D02*
|
||||
X47816Y109661D01*
|
||||
X58232Y120077D01*
|
||||
X63620Y114689D01*
|
||||
X53204Y104273D01*
|
||||
G37*
|
||||
G36*
|
||||
X58861Y98616D02*
|
||||
X53473Y104004D01*
|
||||
X63889Y114420D01*
|
||||
X69277Y109032D01*
|
||||
X58861Y98616D01*
|
||||
G37*
|
||||
G36*
|
||||
X64518Y92959D02*
|
||||
X59130Y98347D01*
|
||||
X69546Y108763D01*
|
||||
X74934Y103375D01*
|
||||
X64518Y92959D01*
|
||||
G37*
|
||||
G36*
|
||||
X70175Y87303D02*
|
||||
X64787Y92691D01*
|
||||
X75203Y103107D01*
|
||||
X80591Y97719D01*
|
||||
X70175Y87303D01*
|
||||
G37*
|
||||
G36*
|
||||
X75832Y81646D02*
|
||||
X70444Y87034D01*
|
||||
X80860Y97450D01*
|
||||
X86248Y92062D01*
|
||||
X75832Y81646D01*
|
||||
G37*
|
||||
D16*
|
||||
G36*
|
||||
X101968Y81646D02*
|
||||
X91552Y92062D01*
|
||||
X96940Y97450D01*
|
||||
X107356Y87034D01*
|
||||
X101968Y81646D01*
|
||||
G37*
|
||||
G36*
|
||||
X107625Y87303D02*
|
||||
X97209Y97719D01*
|
||||
X102597Y103107D01*
|
||||
X113013Y92691D01*
|
||||
X107625Y87303D01*
|
||||
G37*
|
||||
G36*
|
||||
X113282Y92959D02*
|
||||
X102866Y103375D01*
|
||||
X108254Y108763D01*
|
||||
X118670Y98347D01*
|
||||
X113282Y92959D01*
|
||||
G37*
|
||||
G36*
|
||||
X118939Y98616D02*
|
||||
X108523Y109032D01*
|
||||
X113911Y114420D01*
|
||||
X124327Y104004D01*
|
||||
X118939Y98616D01*
|
||||
G37*
|
||||
G36*
|
||||
X124596Y104273D02*
|
||||
X114180Y114689D01*
|
||||
X119568Y120077D01*
|
||||
X129984Y109661D01*
|
||||
X124596Y104273D01*
|
||||
G37*
|
||||
G36*
|
||||
X130253Y109930D02*
|
||||
X119837Y120346D01*
|
||||
X125225Y125734D01*
|
||||
X135641Y115318D01*
|
||||
X130253Y109930D01*
|
||||
G37*
|
||||
G36*
|
||||
X135909Y115587D02*
|
||||
X125493Y126003D01*
|
||||
X130881Y131391D01*
|
||||
X141297Y120975D01*
|
||||
X135909Y115587D01*
|
||||
G37*
|
||||
G36*
|
||||
X141566Y121244D02*
|
||||
X131150Y131660D01*
|
||||
X136538Y137048D01*
|
||||
X146954Y126632D01*
|
||||
X141566Y121244D01*
|
||||
G37*
|
||||
D17*
|
||||
G36*
|
||||
X136538Y142352D02*
|
||||
X131150Y147740D01*
|
||||
X141566Y158156D01*
|
||||
X146954Y152768D01*
|
||||
X136538Y142352D01*
|
||||
G37*
|
||||
G36*
|
||||
X130881Y148009D02*
|
||||
X125493Y153397D01*
|
||||
X135909Y163813D01*
|
||||
X141297Y158425D01*
|
||||
X130881Y148009D01*
|
||||
G37*
|
||||
G36*
|
||||
X125225Y153666D02*
|
||||
X119837Y159054D01*
|
||||
X130253Y169470D01*
|
||||
X135641Y164082D01*
|
||||
X125225Y153666D01*
|
||||
G37*
|
||||
G36*
|
||||
X119568Y159323D02*
|
||||
X114180Y164711D01*
|
||||
X124596Y175127D01*
|
||||
X129984Y169739D01*
|
||||
X119568Y159323D01*
|
||||
G37*
|
||||
G36*
|
||||
X113911Y164980D02*
|
||||
X108523Y170368D01*
|
||||
X118939Y180784D01*
|
||||
X124327Y175396D01*
|
||||
X113911Y164980D01*
|
||||
G37*
|
||||
G36*
|
||||
X108254Y170637D02*
|
||||
X102866Y176025D01*
|
||||
X113282Y186441D01*
|
||||
X118670Y181053D01*
|
||||
X108254Y170637D01*
|
||||
G37*
|
||||
G36*
|
||||
X102597Y176293D02*
|
||||
X97209Y181681D01*
|
||||
X107625Y192097D01*
|
||||
X113013Y186709D01*
|
||||
X102597Y176293D01*
|
||||
G37*
|
||||
G36*
|
||||
X96940Y181950D02*
|
||||
X91552Y187338D01*
|
||||
X101968Y197754D01*
|
||||
X107356Y192366D01*
|
||||
X96940Y181950D01*
|
||||
G37*
|
||||
D15*
|
||||
X152400Y317500D03*
|
||||
X127000Y317500D03*
|
||||
X101600Y317500D03*
|
||||
X76200Y317500D03*
|
||||
X50800Y317500D03*
|
||||
X25400Y317500D03*
|
||||
D10*
|
||||
X136516Y292100D03*
|
||||
X119516Y292100D03*
|
||||
D18*
|
||||
X39370Y292100D03*
|
||||
X124460Y25400D03*
|
||||
D19*
|
||||
X46990Y226369D03*
|
||||
X46990Y218131D03*
|
||||
D20*
|
||||
X118110Y7620D03*
|
||||
X62230Y7620D03*
|
||||
X118110Y45720D03*
|
||||
X62230Y45720D03*
|
||||
D15*
|
||||
X135890Y74930D03*
|
||||
X135890Y100330D03*
|
||||
D21*
|
||||
X49693Y178246D02*
|
||||
X35143Y178246D01*
|
||||
X35143Y190246D02*
|
||||
X49693Y190246D01*
|
||||
X49693Y202246D02*
|
||||
X35143Y202246D01*
|
||||
D22*
|
||||
X42443Y190221D03*
|
||||
M02*
|
@ -1,19 +0,0 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%INSolderpaste Bottom*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10R,1.000000X1.100000*%
|
||||
|
||||
|
||||
D10*
|
||||
X129794Y223148D03*
|
||||
X129794Y240148D03*
|
||||
X142494Y223148D03*
|
||||
X142494Y240148D03*
|
||||
M02*
|
@ -1,341 +0,0 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%INSolderpaste Top*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10R,1.100000X1.000000*%
|
||||
%ADD11R,1.600000X1.400000*%
|
||||
%ADD12C,0.300000*%
|
||||
%ADD13R,1.000000X1.100000*%
|
||||
%ADD14R,1.200000X0.550000*%
|
||||
%ADD15R,1.270000X0.558800*%
|
||||
%ADD16R,0.558800X1.270000*%
|
||||
%ADD17R,1.270000X0.635000*%
|
||||
%ADD18R,2.286000X2.438400*%
|
||||
%ADD19R,1.524000X0.762000*%
|
||||
%ADD20C,0.450000*%
|
||||
|
||||
|
||||
D10*
|
||||
X80382Y204216D03*
|
||||
X63382Y204216D03*
|
||||
D11*
|
||||
X46990Y274350D03*
|
||||
X46990Y246350D03*
|
||||
D10*
|
||||
X97400Y266700D03*
|
||||
X80400Y266700D03*
|
||||
D12*
|
||||
X94424Y222194D02*
|
||||
X94424Y215194D01*
|
||||
X94424Y222194D02*
|
||||
X101424Y222194D01*
|
||||
X101424Y215194D01*
|
||||
X94424Y215194D01*
|
||||
X94424Y218044D02*
|
||||
X101424Y218044D01*
|
||||
X101424Y220894D02*
|
||||
X94424Y220894D01*
|
||||
X76884Y222194D02*
|
||||
X76884Y215194D01*
|
||||
X76884Y222194D02*
|
||||
X83884Y222194D01*
|
||||
X83884Y215194D01*
|
||||
X76884Y215194D01*
|
||||
X76884Y218044D02*
|
||||
X83884Y218044D01*
|
||||
X83884Y220894D02*
|
||||
X76884Y220894D01*
|
||||
D13*
|
||||
X45466Y43824D03*
|
||||
X45466Y60824D03*
|
||||
D10*
|
||||
X98670Y72390D03*
|
||||
X81670Y72390D03*
|
||||
D13*
|
||||
X45466Y12074D03*
|
||||
X45466Y29074D03*
|
||||
X137160Y57522D03*
|
||||
X137160Y40522D03*
|
||||
D12*
|
||||
X133660Y12002D02*
|
||||
X140660Y12002D01*
|
||||
X140660Y5002D01*
|
||||
X133660Y5002D01*
|
||||
X133660Y12002D01*
|
||||
X133660Y7852D02*
|
||||
X140660Y7852D01*
|
||||
X140660Y10702D02*
|
||||
X133660Y10702D01*
|
||||
X133660Y29542D02*
|
||||
X140660Y29542D01*
|
||||
X140660Y22542D01*
|
||||
X133660Y22542D01*
|
||||
X133660Y29542D01*
|
||||
X133660Y25392D02*
|
||||
X140660Y25392D01*
|
||||
X140660Y28242D02*
|
||||
X133660Y28242D01*
|
||||
D14*
|
||||
X101901Y232308D03*
|
||||
X101901Y241808D03*
|
||||
X101901Y251308D03*
|
||||
X75899Y251308D03*
|
||||
X75899Y232308D03*
|
||||
D11*
|
||||
X132080Y227300D03*
|
||||
X132080Y255300D03*
|
||||
D15*
|
||||
G36*
|
||||
X80860Y183387D02*
|
||||
X71881Y192366D01*
|
||||
X75832Y196317D01*
|
||||
X84811Y187338D01*
|
||||
X80860Y183387D01*
|
||||
G37*
|
||||
G36*
|
||||
X75203Y177730D02*
|
||||
X66224Y186709D01*
|
||||
X70175Y190660D01*
|
||||
X79154Y181681D01*
|
||||
X75203Y177730D01*
|
||||
G37*
|
||||
G36*
|
||||
X69546Y172074D02*
|
||||
X60567Y181053D01*
|
||||
X64518Y185004D01*
|
||||
X73497Y176025D01*
|
||||
X69546Y172074D01*
|
||||
G37*
|
||||
G36*
|
||||
X63889Y166417D02*
|
||||
X54910Y175396D01*
|
||||
X58861Y179347D01*
|
||||
X67840Y170368D01*
|
||||
X63889Y166417D01*
|
||||
G37*
|
||||
G36*
|
||||
X58232Y160760D02*
|
||||
X49253Y169739D01*
|
||||
X53204Y173690D01*
|
||||
X62183Y164711D01*
|
||||
X58232Y160760D01*
|
||||
G37*
|
||||
G36*
|
||||
X52575Y155103D02*
|
||||
X43596Y164082D01*
|
||||
X47547Y168033D01*
|
||||
X56526Y159054D01*
|
||||
X52575Y155103D01*
|
||||
G37*
|
||||
G36*
|
||||
X46919Y149446D02*
|
||||
X37940Y158425D01*
|
||||
X41891Y162376D01*
|
||||
X50870Y153397D01*
|
||||
X46919Y149446D01*
|
||||
G37*
|
||||
G36*
|
||||
X41262Y143789D02*
|
||||
X32283Y152768D01*
|
||||
X36234Y156719D01*
|
||||
X45213Y147740D01*
|
||||
X41262Y143789D01*
|
||||
G37*
|
||||
D16*
|
||||
G36*
|
||||
X36234Y122681D02*
|
||||
X32283Y126632D01*
|
||||
X41262Y135611D01*
|
||||
X45213Y131660D01*
|
||||
X36234Y122681D01*
|
||||
G37*
|
||||
G36*
|
||||
X41891Y117024D02*
|
||||
X37940Y120975D01*
|
||||
X46919Y129954D01*
|
||||
X50870Y126003D01*
|
||||
X41891Y117024D01*
|
||||
G37*
|
||||
G36*
|
||||
X47547Y111367D02*
|
||||
X43596Y115318D01*
|
||||
X52575Y124297D01*
|
||||
X56526Y120346D01*
|
||||
X47547Y111367D01*
|
||||
G37*
|
||||
G36*
|
||||
X53204Y105710D02*
|
||||
X49253Y109661D01*
|
||||
X58232Y118640D01*
|
||||
X62183Y114689D01*
|
||||
X53204Y105710D01*
|
||||
G37*
|
||||
G36*
|
||||
X58861Y100053D02*
|
||||
X54910Y104004D01*
|
||||
X63889Y112983D01*
|
||||
X67840Y109032D01*
|
||||
X58861Y100053D01*
|
||||
G37*
|
||||
G36*
|
||||
X64518Y94396D02*
|
||||
X60567Y98347D01*
|
||||
X69546Y107326D01*
|
||||
X73497Y103375D01*
|
||||
X64518Y94396D01*
|
||||
G37*
|
||||
G36*
|
||||
X70175Y88740D02*
|
||||
X66224Y92691D01*
|
||||
X75203Y101670D01*
|
||||
X79154Y97719D01*
|
||||
X70175Y88740D01*
|
||||
G37*
|
||||
G36*
|
||||
X75832Y83083D02*
|
||||
X71881Y87034D01*
|
||||
X80860Y96013D01*
|
||||
X84811Y92062D01*
|
||||
X75832Y83083D01*
|
||||
G37*
|
||||
D15*
|
||||
G36*
|
||||
X101968Y83083D02*
|
||||
X92989Y92062D01*
|
||||
X96940Y96013D01*
|
||||
X105919Y87034D01*
|
||||
X101968Y83083D01*
|
||||
G37*
|
||||
G36*
|
||||
X107625Y88740D02*
|
||||
X98646Y97719D01*
|
||||
X102597Y101670D01*
|
||||
X111576Y92691D01*
|
||||
X107625Y88740D01*
|
||||
G37*
|
||||
G36*
|
||||
X113282Y94396D02*
|
||||
X104303Y103375D01*
|
||||
X108254Y107326D01*
|
||||
X117233Y98347D01*
|
||||
X113282Y94396D01*
|
||||
G37*
|
||||
G36*
|
||||
X118939Y100053D02*
|
||||
X109960Y109032D01*
|
||||
X113911Y112983D01*
|
||||
X122890Y104004D01*
|
||||
X118939Y100053D01*
|
||||
G37*
|
||||
G36*
|
||||
X124596Y105710D02*
|
||||
X115617Y114689D01*
|
||||
X119568Y118640D01*
|
||||
X128547Y109661D01*
|
||||
X124596Y105710D01*
|
||||
G37*
|
||||
G36*
|
||||
X130253Y111367D02*
|
||||
X121274Y120346D01*
|
||||
X125225Y124297D01*
|
||||
X134204Y115318D01*
|
||||
X130253Y111367D01*
|
||||
G37*
|
||||
G36*
|
||||
X135909Y117024D02*
|
||||
X126930Y126003D01*
|
||||
X130881Y129954D01*
|
||||
X139860Y120975D01*
|
||||
X135909Y117024D01*
|
||||
G37*
|
||||
G36*
|
||||
X141566Y122681D02*
|
||||
X132587Y131660D01*
|
||||
X136538Y135611D01*
|
||||
X145517Y126632D01*
|
||||
X141566Y122681D01*
|
||||
G37*
|
||||
D16*
|
||||
G36*
|
||||
X136538Y143789D02*
|
||||
X132587Y147740D01*
|
||||
X141566Y156719D01*
|
||||
X145517Y152768D01*
|
||||
X136538Y143789D01*
|
||||
G37*
|
||||
G36*
|
||||
X130881Y149446D02*
|
||||
X126930Y153397D01*
|
||||
X135909Y162376D01*
|
||||
X139860Y158425D01*
|
||||
X130881Y149446D01*
|
||||
G37*
|
||||
G36*
|
||||
X125225Y155103D02*
|
||||
X121274Y159054D01*
|
||||
X130253Y168033D01*
|
||||
X134204Y164082D01*
|
||||
X125225Y155103D01*
|
||||
G37*
|
||||
G36*
|
||||
X119568Y160760D02*
|
||||
X115617Y164711D01*
|
||||
X124596Y173690D01*
|
||||
X128547Y169739D01*
|
||||
X119568Y160760D01*
|
||||
G37*
|
||||
G36*
|
||||
X113911Y166417D02*
|
||||
X109960Y170368D01*
|
||||
X118939Y179347D01*
|
||||
X122890Y175396D01*
|
||||
X113911Y166417D01*
|
||||
G37*
|
||||
G36*
|
||||
X108254Y172074D02*
|
||||
X104303Y176025D01*
|
||||
X113282Y185004D01*
|
||||
X117233Y181053D01*
|
||||
X108254Y172074D01*
|
||||
G37*
|
||||
G36*
|
||||
X102597Y177730D02*
|
||||
X98646Y181681D01*
|
||||
X107625Y190660D01*
|
||||
X111576Y186709D01*
|
||||
X102597Y177730D01*
|
||||
G37*
|
||||
G36*
|
||||
X96940Y183387D02*
|
||||
X92989Y187338D01*
|
||||
X101968Y196317D01*
|
||||
X105919Y192366D01*
|
||||
X96940Y183387D01*
|
||||
G37*
|
||||
D10*
|
||||
X136516Y292100D03*
|
||||
X119516Y292100D03*
|
||||
D17*
|
||||
X46990Y226369D03*
|
||||
X46990Y218131D03*
|
||||
D18*
|
||||
X46990Y222250D03*
|
||||
D19*
|
||||
X118110Y7620D03*
|
||||
X62230Y7620D03*
|
||||
X118110Y45720D03*
|
||||
X62230Y45720D03*
|
||||
D20*
|
||||
X49693Y178246D02*
|
||||
X35143Y178246D01*
|
||||
X35143Y190246D02*
|
||||
X49693Y190246D01*
|
||||
X49693Y202246D02*
|
||||
X35143Y202246D01*
|
||||
M02*
|
@ -110,6 +110,6 @@
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": ["src"],
|
||||
"include": ["src/**/*", "types/npe_gerber.d.ts"],
|
||||
"exclude": ["node_modules", "dist/**/*", "test/**/*"]
|
||||
}
|
||||
|
0
src/gerber.d.ts → types/npe_gerber.d.ts
vendored
0
src/gerber.d.ts → types/npe_gerber.d.ts
vendored
Loading…
x
Reference in New Issue
Block a user