UNPKG

gerber-to-svg

Version:

Gerber and NC drill file to SVG converter

1,938 lines (1,818 loc) 64.6 kB
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.gerberToSvg=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ /* @license copyright 2014 by mike cousins <mike@cousins.io> (http://cousins.io) shared under the terms of the MIT license view source at http://github.com/mcous/gerber-to-svg */ var DEFAULT_OPTS, Plotter, builder; builder = require('./obj-to-xml'); Plotter = require('./plotter'); DEFAULT_OPTS = { drill: false, pretty: false, object: false }; module.exports = function(gerber, options) { var Parser, Reader, a, error, height, key, opts, p, val, width, xml, xmlObject, _ref; if (options == null) { options = {}; } opts = {}; for (key in DEFAULT_OPTS) { val = DEFAULT_OPTS[key]; opts[key] = val; } for (key in options) { val = options[key]; opts[key] = val; } if (typeof gerber === 'object') { if (gerber.svg != null) { return builder(gerber, { pretty: opts.pretty }); } else { throw new Error("non SVG object cannot be converted to an SVG string"); } } if (opts.drill) { Reader = require('./drill-reader'); Parser = require('./drill-parser'); } else { Reader = require('./gerber-reader'); Parser = require('./gerber-parser'); } p = new Plotter(gerber, Reader, Parser); try { xmlObject = p.plot(); } catch (_error) { error = _error; throw new Error("Error at line " + p.reader.line + " - " + error.message); } if (!(p.bbox.xMin >= p.bbox.xMax)) { width = p.bbox.xMax - p.bbox.xMin; } else { p.bbox.xMin = 0; p.bbox.xMax = 0; width = 0; } if (!(p.bbox.yMin >= p.bbox.yMax)) { height = p.bbox.yMax - p.bbox.yMin; } else { p.bbox.yMin = 0; p.bbox.yMax = 0; height = 0; } xml = { svg: { xmlns: 'http://www.w3.org/2000/svg', version: '1.1', 'xmlns:xlink': 'http://www.w3.org/1999/xlink', width: "" + width + p.units, height: "" + height + p.units, viewBox: [p.bbox.xMin, p.bbox.yMin, width, height], _: [] } }; _ref = p.attr; for (a in _ref) { val = _ref[a]; xml.svg[a] = val; } if (p.defs.length) { xml.svg._.push({ defs: { _: p.defs } }); } if (p.group.g._.length) { xml.svg._.push(p.group); } if (!opts.object) { return builder(xml, { pretty: opts.pretty }); } else { return xml; } }; },{"./drill-parser":3,"./drill-reader":4,"./gerber-parser":5,"./gerber-reader":6,"./obj-to-xml":9,"./plotter":11}],2:[function(require,module,exports){ var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; module.exports = function(coord, format) { var divisor, key, parse, result, val, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7; if (coord == null) { return {}; } if (!((format.zero != null) && (format.places != null))) { throw new Error('format undefined'); } parse = {}; result = {}; parse.x = (_ref = coord.match(/X[+-]?[\d\.]+/)) != null ? (_ref1 = _ref[0]) != null ? _ref1.slice(1) : void 0 : void 0; parse.y = (_ref2 = coord.match(/Y[+-]?[\d\.]+/)) != null ? (_ref3 = _ref2[0]) != null ? _ref3.slice(1) : void 0 : void 0; parse.i = (_ref4 = coord.match(/I[+-]?[\d\.]+/)) != null ? (_ref5 = _ref4[0]) != null ? _ref5.slice(1) : void 0 : void 0; parse.j = (_ref6 = coord.match(/J[+-]?[\d\.]+/)) != null ? (_ref7 = _ref6[0]) != null ? _ref7.slice(1) : void 0 : void 0; for (key in parse) { val = parse[key]; if (val != null) { if (__indexOf.call(val, '.') >= 0) { result[key] = Number(val); } else { divisor = 1; if (val[0] === '+' || val[0] === '-') { if (val[0] === '-') { divisor = -1; } val = val.slice(1); } if (format.zero === 'L') { divisor *= Math.pow(10, format.places[1]); } else if (format.zero === 'T') { divisor *= Math.pow(10, val.length - format.places[0]); } else { throw new Error('invalid zero suppression format'); } result[key] = Number(val) / divisor; } } } return result; }; },{}],3:[function(require,module,exports){ var ABS_COMMAND, DrillParser, INCH_COMMAND, INC_COMMAND, METRIC_COMMAND, PLACES_BACKUP, ZERO_BACKUP, parseCoord, reCOORD; parseCoord = require('./coord-parser'); INCH_COMMAND = { 'FMAT,1': 'M70', 'FMAT,2': 'M72' }; METRIC_COMMAND = 'M71'; ABS_COMMAND = 'G90'; INC_COMMAND = 'G91'; reCOORD = /[XY]{1,2}/; ZERO_BACKUP = 'L'; PLACES_BACKUP = [2, 4]; DrillParser = (function() { function DrillParser() { this.format = { zero: null, places: null }; this.fmat = 'FMAT,2'; } DrillParser.prototype.parseCommand = function(block) { var code, command, dia, k, v, _ref, _ref1, _ref2; command = {}; if (block[0] === ';') { return command; } if (block === 'FMAT,1') { this.fmat = block; } else if (block === 'M30' || block === 'M00') { command.set = { done: true }; } else if (block === INCH_COMMAND[this.fmat] || block.match(/INCH/)) { this.format.places = [2, 4]; command.set = { units: 'in' }; } else if (block === METRIC_COMMAND || block.match(/METRIC/)) { this.format.places = [3, 3]; command.set = { units: 'mm' }; } else if (block === ABS_COMMAND) { command.set = { notation: 'abs' }; } else if (block === INC_COMMAND) { command.set = { notation: 'inc' }; } else if ((code = (_ref = block.match(/T\d+/)) != null ? _ref[0] : void 0)) { while (code[1] === '0') { code = code[0] + code.slice(2); } if ((dia = (_ref1 = block.match(/C[\d\.]+(?=.*$)/)) != null ? _ref1[0] : void 0)) { dia = Number(dia.slice(1)); command.tool = {}; command.tool[code] = { dia: dia }; } else { command.set = { currentTool: code }; } } if (block.match(/TZ/)) { this.format.zero = 'L'; } else if (block.match(/LZ/)) { this.format.zero = 'T'; } if (block.match(reCOORD)) { command.op = { "do": 'flash' }; if (this.format.zero == null) { console.warn('no drill file zero suppression specified. assuming leading zero suppression (same as no zero suppression)'); this.format.zero = ZERO_BACKUP; } if (this.format.places == null) { console.warn('no drill file units specified; assuming 2:4 inches format'); this.format.places = PLACES_BACKUP; } _ref2 = parseCoord(block, this.format); for (k in _ref2) { v = _ref2[k]; command.op[k] = v; } } return command; }; return DrillParser; })(); module.exports = DrillParser; },{"./coord-parser":2}],4:[function(require,module,exports){ var DrillReader; DrillReader = (function() { function DrillReader(drillFile) { this.line = 0; this.blocks = drillFile.split(/\r?\n/); } DrillReader.prototype.nextBlock = function() { if (this.line < this.blocks.length) { return this.blocks[++this.line - 1]; } else { return false; } }; return DrillReader; })(); module.exports = DrillReader; },{}],5:[function(require,module,exports){ var GerberParser, parseCoord, reCOORD; parseCoord = require('./coord-parser'); reCOORD = /([XYIJ][+-]?\d+){1,4}/g; GerberParser = (function() { function GerberParser() { this.format = { zero: null, places: null }; } GerberParser.prototype.parseFormat = function(p, c) { var nota, places, zero; zero = p[2] === 'L' || p[2] === 'T' ? p[2] : null; nota = p[3] === 'A' || p[3] === 'I' ? p[3] : null; if (p[4] === 'X' && p[7] === 'Y' && p.slice(5, 7) === p.slice(8, 10) && p[5] < 8 && p[6] < 8) { places = [+p[5], +p[6]]; } if ((places == null) || (nota == null) || (zero == null)) { throw new Error("invalid format specification"); } this.format.zero = zero; this.format.places = places; if (c.set == null) { c.set = {}; } return c.set.notation = nota; }; GerberParser.prototype.parseToolDef = function(p, c) { var code, hole, m, mods, shape, _ref, _ref1; if (c.tool == null) { c.tool = {}; } code = (_ref = p.match(/^ADD\d{2,}/)) != null ? _ref[0].slice(2) : void 0; _ref1 = p.slice(2 + code.length).split(','), shape = _ref1[0], mods = _ref1[1]; mods = mods != null ? mods.split('X') : void 0; while (code[1] === '0') { code = code[0] + code.slice(2); } switch (shape) { case 'C': if (mods.length > 2) { hole = { width: +mods[1], height: +mods[2] }; } else if (mods.length > 1) { hole = { dia: +mods[1] }; } c.tool[code] = { dia: +mods[0] }; if (hole != null) { return c.tool[code].hole = hole; } break; case 'R': case 'O': if (mods.length > 3) { hole = { width: +mods[2], height: +mods[3] }; } else if (mods.length > 2) { hole = { dia: +mods[2] }; } c.tool[code] = { width: +mods[0], height: +mods[1] }; if (shape === 'O') { c.tool[code].obround = true; } if (hole != null) { return c.tool[code].hole = hole; } break; case 'P': if (mods.length > 4) { hole = { width: +mods[3], height: +mods[4] }; } else if (mods.length > 3) { hole = { dia: +mods[3] }; } c.tool[code] = { dia: +mods[0], verticies: +mods[1] }; if (mods.length > 2) { c.tool[code].degrees = +mods[2]; } if (hole != null) { return c.tool[code].hole = hole; } break; default: mods = (function() { var _i, _len, _ref2, _results; _ref2 = mods != null ? mods : []; _results = []; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { m = _ref2[_i]; _results.push(+m); } return _results; })(); return c.tool[code] = { macro: shape, mods: mods }; } }; GerberParser.prototype.parseCommand = function(block) { var axis, c, code, coord, i, j, m, op, p, param, tool, u, val, x, y, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9; if (block == null) { block = {}; } c = {}; if (param = block.param) { for (_i = 0, _len = param.length; _i < _len; _i++) { p = param[_i]; switch (code = p.slice(0, 2)) { case 'FS': this.parseFormat(p, c); break; case 'MO': u = p.slice(2, 4); if (c.set == null) { c.set = {}; } if (u === 'IN') { c.set.units = 'in'; } else if (u === 'MM') { c.set.units = 'mm'; } else { throw new Error("" + p + " is an invalid units setting"); } break; case 'AD': this.parseToolDef(p, c); break; case 'AM': return { macro: param }; case 'LP': if (c["new"] == null) { c["new"] = {}; } if (p[2] === 'D' || p[2] === 'C') { c["new"].layer = p[2]; } if (c["new"].layer == null) { throw new Error('invalid level polarity'); } break; case 'SR': if (c["new"] == null) { c["new"] = {}; } x = (_ref = (_ref1 = p.match(/X[+-]?[\d\.]+/)) != null ? _ref1[0].slice(1) : void 0) != null ? _ref : 1; y = (_ref2 = (_ref3 = p.match(/Y[+-]?[\d\.]+/)) != null ? _ref3[0].slice(1) : void 0) != null ? _ref2 : 1; i = (_ref4 = p.match(/I[+-]?[\d\.]+/)) != null ? _ref4[0].slice(1) : void 0; j = (_ref5 = p.match(/J[+-]?[\d\.]+/)) != null ? _ref5[0].slice(1) : void 0; if (x < 1 || y < 1 || (x > 1 && (i == null) || i < 0) || (y > 1 && (j == null) || j < 0)) { throw new Error('invalid step repeat'); } c["new"].sr = { x: +x, y: +y }; if (i != null) { c["new"].sr.i = +i; } if (j != null) { c["new"].sr.j = +j; } } } } else if (block = block.block) { if (block === 'M02') { return { set: { done: true } }; } else if (block[0] === 'G') { switch (code = (_ref6 = block.slice(1).match(/^\d{1,2}/)) != null ? _ref6[0] : void 0) { case '4': case '04': return {}; case '1': case '01': case '2': case '02': case '3': case '03': code = code[code.length - 1]; m = code === '1' ? 'i' : code === '2' ? 'cw' : 'ccw'; c.set = { mode: m }; break; case '36': case '37': c.set = { region: code === '36' }; break; case '70': case '71': c.set = { backupUnits: code === '70' ? 'in' : 'mm' }; break; case '74': case '75': c.set = { quad: code === '74' ? 's' : 'm' }; } } coord = parseCoord((_ref7 = block.match(reCOORD)) != null ? _ref7[0] : void 0, this.format); if (op = ((_ref8 = block.match(/D0?[123]$/)) != null ? _ref8[0] : void 0) || Object.keys(coord).length) { if (op != null) { op = op[op.length - 1]; } op = (function() { switch (op) { case '1': return 'int'; case '2': return 'move'; case '3': return 'flash'; default: return 'last'; } })(); c.op = { "do": op }; for (axis in coord) { val = coord[axis]; c.op[axis] = val; } } else if (tool = (_ref9 = block.match(/D\d+$/)) != null ? _ref9[0] : void 0) { c.set = { currentTool: tool }; } } return c; }; return GerberParser; })(); module.exports = GerberParser; },{"./coord-parser":2}],6:[function(require,module,exports){ var GerberReader; GerberReader = (function() { function GerberReader(gerberFile) { this.gerberFile = gerberFile; this.line = 0; this.charIndex = 0; this.end = this.gerberFile.length; } GerberReader.prototype.nextBlock = function() { var char, current, parameter; if (this.index >= this.end) { return false; } current = ''; parameter = false; if (this.line === 0) { this.line++; } while (!(this.charIndex >= this.end)) { char = this.gerberFile[this.charIndex++]; if (char === '%') { if (!parameter) { parameter = []; } else { return { param: parameter }; } } else if (char === '*') { if (parameter) { parameter.push(current); current = ''; } else { return { block: current }; } } else if (char === '\n') { this.line++; } else if ((' ' <= char && char <= '~')) { current += char; } } return false; }; return GerberReader; })(); module.exports = GerberReader; },{}],7:[function(require,module,exports){ var NUMBER, OPERATOR, TOKEN, isNumber, parse, tokenize; OPERATOR = /[\+\-\/xX\(\)]/; NUMBER = /[\$\d\.]+/; TOKEN = new RegExp("(" + OPERATOR.source + ")|(" + NUMBER.source + ")", 'g'); tokenize = function(arith) { var results; return results = arith.match(TOKEN); }; isNumber = function(token) { return NUMBER.test(token); }; parse = function(arith) { var consume, index, parseExpression, parseMultiplication, parsePrimary, peek, tokens; tokens = tokenize(arith); index = 0; peek = function() { return tokens[index]; }; consume = function(t) { if (t === peek()) { return index++; } }; parsePrimary = function() { var exp, t; t = peek(); consume(t); if (isNumber(t)) { exp = { type: 'n', val: t }; } else if (t === '(') { exp = parseExpression(); if (peek() !== ')') { throw new Error("expected ')'"); } else { consume(')'); } } else { throw new Error("" + t + " is unexpected in an arithmetic string"); } return exp; }; parseMultiplication = function() { var exp, rhs, t; exp = parsePrimary(); t = peek(); while (t === 'x' || t === '/' || t === 'X') { consume(t); if (t === 'X') { console.warn("Warning: uppercase 'X' as multiplication symbol is incorrect; macros should use lowercase 'x' to multiply"); t = 'x'; } rhs = parsePrimary(); exp = { type: t, left: exp, right: rhs }; t = peek(); } return exp; }; parseExpression = function() { var exp, rhs, t; exp = parseMultiplication(); t = peek(); while (t === '+' || t === '-') { consume(t); rhs = parseMultiplication(); exp = { type: t, left: exp, right: rhs }; t = peek(); } return exp; }; return parseExpression(); }; module.exports = { tokenize: tokenize, isNumber: isNumber, parse: parse }; },{}],8:[function(require,module,exports){ var MacroTool, calc, shapes, unique; shapes = require('./pad-shapes'); calc = require('./macro-calc'); unique = require('./unique-id'); MacroTool = (function() { function MacroTool(blocks) { this.modifiers = {}; this.name = blocks[0].slice(2); this.blocks = blocks.slice(1); this.shapes = []; this.masks = []; this.lastExposure = null; this.bbox = [null, null, null, null]; } MacroTool.prototype.run = function(tool, modifiers) { var b, group, i, m, pad, padId, s, shape, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2; if (modifiers == null) { modifiers = []; } this.lastExposure = null; this.shapes = []; this.masks = []; this.bbox = [null, null, null, null]; this.modifiers = {}; for (i = _i = 0, _len = modifiers.length; _i < _len; i = ++_i) { m = modifiers[i]; this.modifiers["$" + (i + 1)] = m; } _ref = this.blocks; for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { b = _ref[_j]; this.runBlock(b); } padId = "tool-" + tool + "-pad-" + (unique()); pad = []; _ref1 = this.masks; for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { m = _ref1[_k]; pad.push(m); } if (this.shapes.length > 1) { group = { id: padId, _: [] }; _ref2 = this.shapes; for (_l = 0, _len3 = _ref2.length; _l < _len3; _l++) { s = _ref2[_l]; group._.push(s); } pad = [ { g: group } ]; } else if (this.shapes.length === 1) { shape = Object.keys(this.shapes[0])[0]; this.shapes[0][shape].id = padId; pad.push(this.shapes[0]); } return { pad: pad, padId: padId, bbox: this.bbox, trace: false }; }; MacroTool.prototype.runBlock = function(block) { var a, args, i, mod, val, _i, _len, _ref; switch (block[0]) { case '$': mod = (_ref = block.match(/^\$\d+(?=\=)/)) != null ? _ref[0] : void 0; val = block.slice(1 + mod.length); return this.modifiers[mod] = this.getNumber(val); case '1': case '2': case '20': case '21': case '22': case '4': case '5': case '6': case '7': args = block.split(','); for (i = _i = 0, _len = args.length; _i < _len; i = ++_i) { a = args[i]; args[i] = this.getNumber(a); } return this.primitive(args); default: if (block[0] !== '0') { throw new Error("'" + block + "' unrecognized tool macro block"); } } }; MacroTool.prototype.primitive = function(args) { var group, i, key, m, mask, maskId, points, rot, rotation, s, shape, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _n, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _results; mask = false; rotation = false; shape = null; switch (args[0]) { case 1: shape = shapes.circle({ dia: args[2], cx: args[3], cy: args[4] }); if (args[1] === 0) { mask = true; } else { this.addBbox(shape.bbox); } break; case 2: case 20: shape = shapes.vector({ width: args[2], x1: args[3], y1: args[4], x2: args[5], y2: args[6] }); if (args[7]) { shape.shape.line.transform = "rotate(" + args[7] + ")"; } if (args[1] === 0) { mask = true; } else { this.addBbox(shape.bbox, args[7]); } break; case 21: shape = shapes.rect({ cx: args[4], cy: args[5], width: args[2], height: args[3] }); if (args[6]) { shape.shape.rect.transform = "rotate(" + args[6] + ")"; } if (args[1] === 0) { mask = true; } else { this.addBbox(shape.bbox, args[6]); } break; case 22: shape = shapes.lowerLeftRect({ x: args[4], y: args[5], width: args[2], height: args[3] }); if (args[6]) { shape.shape.rect.transform = "rotate(" + args[6] + ")"; } if (args[1] === 0) { mask = true; } else { this.addBbox(shape.bbox, args[6]); } break; case 4: points = []; for (i = _i = 3, _ref = 3 + 2 * args[2]; _i <= _ref; i = _i += 2) { points.push([args[i], args[i + 1]]); } shape = shapes.outline({ points: points }); if (rot = args[args.length - 1]) { shape.shape.polygon.transform = "rotate(" + rot + ")"; } if (args[1] === 0) { mask = true; } else { this.addBbox(shape.bbox, args[args.length - 1]); } break; case 5: if (args[6] !== 0 && (args[3] !== 0 || args[4] !== 0)) { throw new RangeError('polygon center must be 0,0 if rotated in macro'); } shape = shapes.polygon({ cx: args[3], cy: args[4], dia: args[5], verticies: args[2], degrees: args[6] }); if (args[1] === 0) { mask = true; } else { this.addBbox(shape.bbox); } break; case 6: if (args[9] !== 0 && (args[1] !== 0 || args[2] !== 0)) { throw new RangeError('moiré center must be 0,0 if rotated in macro'); } shape = shapes.moire({ cx: args[1], cy: args[2], outerDia: args[3], ringThx: args[4], ringGap: args[5], maxRings: args[6], crossThx: args[7], crossLength: args[8] }); if (args[9]) { _ref1 = shape.shape; for (_j = 0, _len = _ref1.length; _j < _len; _j++) { s = _ref1[_j]; if (s.line != null) { s.line.transform = "rotate(" + args[9] + ")"; } } } this.addBbox(shape.bbox, args[9]); break; case 7: if (args[9] !== 0 && (args[1] !== 0 || args[2] !== 0)) { throw new RangeError('thermal center must be 0,0 if rotated in macro'); } shape = shapes.thermal({ cx: args[1], cy: args[2], outerDia: args[3], innerDia: args[4], gap: args[5] }); if (args[6]) { _ref2 = shape.shape; for (_k = 0, _len1 = _ref2.length; _k < _len1; _k++) { s = _ref2[_k]; if (s.mask != null) { _ref3 = s.mask._; for (_l = 0, _len2 = _ref3.length; _l < _len2; _l++) { m = _ref3[_l]; if (m.rect != null) { m.rect.transform = "rotate(" + args[6] + ")"; } } } } } this.addBbox(shape.bbox, args[6]); break; default: throw new Error("" + args[0] + " is not a valid primitive code"); } if (mask) { for (key in shape.shape) { shape.shape[key].fill = '#000'; } if (this.lastExposure !== 0) { this.lastExposure = 0; maskId = "macro-" + this.name + "-mask-" + (unique()); m = { mask: { id: maskId } }; m.mask._ = [ { rect: { x: this.bbox[0], y: this.bbox[1], width: this.bbox[2] - this.bbox[0], height: this.bbox[3] - this.bbox[1], fill: '#fff' } } ]; if (this.shapes.length === 1) { for (key in this.shapes[0]) { this.shapes[0][key].mask = "url(#" + maskId + ")"; } } else if (this.shapes.length > 1) { group = { mask: "url(#" + maskId + ")", _: [] }; _ref4 = this.shapes; for (_m = 0, _len3 = _ref4.length; _m < _len3; _m++) { s = _ref4[_m]; group._.push(s); } this.shapes = [ { g: group } ]; } this.masks.push(m); } return this.masks[this.masks.length - 1].mask._.push(shape.shape); } else { this.lastExposure = 1; if (!Array.isArray(shape.shape)) { return this.shapes.push(shape.shape); } else { _ref5 = shape.shape; _results = []; for (_n = 0, _len4 = _ref5.length; _n < _len4; _n++) { s = _ref5[_n]; if (s.mask != null) { _results.push(this.masks.push(s)); } else { _results.push(this.shapes.push(s)); } } return _results; } } }; MacroTool.prototype.addBbox = function(bbox, rotation) { var b, c, p, points, s, x, y, _i, _len; if (rotation == null) { rotation = 0; } if (!rotation) { if (this.bbox[0] === null || bbox[0] < this.bbox[0]) { this.bbox[0] = bbox[0]; } if (this.bbox[1] === null || bbox[1] < this.bbox[1]) { this.bbox[1] = bbox[1]; } if (this.bbox[2] === null || bbox[2] > this.bbox[2]) { this.bbox[2] = bbox[2]; } if (this.bbox[3] === null || bbox[3] > this.bbox[3]) { return this.bbox[3] = bbox[3]; } } else { s = Math.sin(rotation * Math.PI / 180); c = Math.cos(rotation * Math.PI / 180); if (Math.abs(s) < 0.000000001) { s = 0; } if (Math.abs(c) < 0.000000001) { c = 0; } points = [[bbox[0], bbox[1]], [bbox[2], bbox[1]], [bbox[2], bbox[3]], [bbox[0], bbox[3]]]; for (_i = 0, _len = points.length; _i < _len; _i++) { p = points[_i]; x = p[0] * c - p[1] * s; y = p[0] * s + p[1] * c; if (this.bbox[0] === null || x < this.bbox[0]) { this.bbox[0] = x; } if (this.bbox[1] === null || y < this.bbox[1]) { this.bbox[1] = y; } if (this.bbox[2] === null || x > this.bbox[2]) { this.bbox[2] = x; } if (this.bbox[3] === null || y > this.bbox[3]) { this.bbox[3] = y; } } return this.bbox = (function() { var _j, _len1, _ref, _results; _ref = this.bbox; _results = []; for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { b = _ref[_j]; _results.push(b === -0 ? 0 : b); } return _results; }).call(this); } }; MacroTool.prototype.getNumber = function(s) { if (s.match(/^[+-]?[\d.]+$/)) { return Number(s); } else if (s.match(/^\$\d+$/)) { return Number(this.modifiers[s]); } else { return this.evaluate(calc.parse(s)); } }; MacroTool.prototype.evaluate = function(op) { switch (op.type) { case 'n': return this.getNumber(op.val); case '+': return this.evaluate(op.left) + this.evaluate(op.right); case '-': return this.evaluate(op.left) - this.evaluate(op.right); case 'x': return this.evaluate(op.left) * this.evaluate(op.right); case '/': return this.evaluate(op.left) / this.evaluate(op.right); } }; return MacroTool; })(); module.exports = MacroTool; },{"./macro-calc":7,"./pad-shapes":10,"./unique-id":13}],9:[function(require,module,exports){ var CKEY, DTAB, objToXml, repeat; repeat = function(pattern, count) { var result; result = ''; if (count === 0) { return ''; } while (count > 1) { if (count & 1) { result += pattern; } count >>= 1; pattern += pattern; } return result + pattern; }; CKEY = '_'; DTAB = ' '; objToXml = function(obj, op) { var children, dec, decimals, elem, i, ind, key, nl, o, pre, tb, v, val, xml, _i, _len, _ref, _ref1, _ref2; if (op == null) { op = {}; } pre = op.pretty; ind = (_ref = op.indent) != null ? _ref : 0; dec = (_ref1 = op.maxDec) != null ? _ref1 : false; decimals = function(n) { if (typeof n === 'number') { return Number(n.toFixed(dec)); } else { return n; } }; nl = pre ? '\n' : ''; tb = nl ? (typeof pre === 'string' ? pre : DTAB) : ''; tb = repeat(tb, ind); xml = ''; if (typeof obj === 'function') { obj = obj(); } if (Array.isArray(obj)) { for (i = _i = 0, _len = obj.length; _i < _len; i = ++_i) { o = obj[i]; xml += (i !== 0 ? nl : '') + (objToXml(o, op)); } } else if (typeof obj === 'object') { children = false; elem = Object.keys(obj)[0]; if (elem != null) { xml = "" + tb + "<" + elem; if (typeof obj[elem] === 'function') { obj[elem] = obj[elem](); } _ref2 = obj[elem]; for (key in _ref2) { val = _ref2[key]; if (typeof val === 'function') { val = val(); } if (key === CKEY) { children = val; } else { if (Array.isArray(val)) { if (dec) { val = (function() { var _j, _len1, _results; _results = []; for (_j = 0, _len1 = val.length; _j < _len1; _j++) { v = val[_j]; _results.push(decimals(v)); } return _results; })(); } val = val.join(' '); } if (dec) { val = decimals(val); } xml += " " + key + "=\"" + val + "\""; } } if (children) { xml += '>' + nl + objToXml(children, { pretty: pre, indent: ind + 1 }); } if (obj[elem]._ != null) { xml += "" + nl + tb + "</" + elem + ">"; } else { xml += '/>'; } } } else { xml += "" + obj + " "; } return xml; }; module.exports = objToXml; },{}],10:[function(require,module,exports){ var circle, lowerLeftRect, moire, outline, polygon, rect, thermal, unique, vector; unique = require('./unique-id'); circle = function(p) { var r; if (p.dia == null) { throw new Error('circle function requires diameter'); } if (p.cx == null) { throw new Error('circle function requires x center'); } if (p.cy == null) { throw new Error('circle function requires y center'); } r = p.dia / 2; return { shape: { circle: { cx: p.cx, cy: p.cy, r: r } }, bbox: [p.cx - r, p.cy - r, p.cx + r, p.cy + r] }; }; rect = function(p) { var radius, rectangle, x, y; if (p.width == null) { throw new Error('rectangle requires width'); } if (p.height == null) { throw new Error('rectangle requires height'); } if (p.cx == null) { throw new Error('rectangle function requires x center'); } if (p.cy == null) { throw new Error('rectangle function requires y center'); } x = p.cx - p.width / 2; y = p.cy - p.height / 2; rectangle = { shape: { rect: { x: x, y: y, width: p.width, height: p.height } }, bbox: [x, y, x + p.width, y + p.height] }; if (p.obround) { radius = 0.5 * Math.min(p.width, p.height); rectangle.shape.rect.rx = radius; rectangle.shape.rect.ry = radius; } return rectangle; }; polygon = function(p) { var i, points, r, rx, ry, start, step, theta, x, xMax, xMin, y, yMax, yMin, _i, _ref; if (p.dia == null) { throw new Error('polygon requires diameter'); } if (p.verticies == null) { throw new Error('polygon requires verticies'); } if (p.cx == null) { throw new Error('polygon function requires x center'); } if (p.cy == null) { throw new Error('polygon function requires y center'); } start = p.degrees != null ? p.degrees * Math.PI / 180 : 0; step = 2 * Math.PI / p.verticies; r = p.dia / 2; points = ''; xMin = null; yMin = null; xMax = null; yMax = null; for (i = _i = 0, _ref = p.verticies; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { theta = start + i * step; rx = r * Math.cos(theta); ry = r * Math.sin(theta); if (Math.abs(rx) < 0.000000001) { rx = 0; } if (Math.abs(ry) < 0.000000001) { ry = 0; } x = p.cx + rx; y = p.cy + ry; if (x < xMin || xMin === null) { xMin = x; } if (x > xMax || xMax === null) { xMax = x; } if (y < yMin || yMin === null) { yMin = y; } if (y > yMax || yMax === null) { yMax = y; } points += " " + x + "," + y; } return { shape: { polygon: { points: points.slice(1) } }, bbox: [xMin, yMin, xMax, yMax] }; }; vector = function(p) { var theta, xDelta, yDelta; if (p.x1 == null) { throw new Error('vector function requires start x'); } if (p.y1 == null) { throw new Error('vector function requires start y'); } if (p.x2 == null) { throw new Error('vector function requires end x'); } if (p.y2 == null) { throw new Error('vector function requires end y'); } if (p.width == null) { throw new Error('vector function requires width'); } theta = Math.abs(Math.atan((p.y2 - p.y1) / (p.x2 - p.x1))); xDelta = p.width / 2 * Math.sin(theta); yDelta = p.width / 2 * Math.cos(theta); if (xDelta < 0.0000001) { xDelta = 0; } if (yDelta < 0.0000001) { yDelta = 0; } return { shape: { line: { x1: p.x1, x2: p.x2, y1: p.y1, y2: p.y2, 'stroke-width': p.width, 'stroke-linecap': 'butt' } }, bbox: [(Math.min(p.x1, p.x2)) - xDelta, (Math.min(p.y1, p.y2)) - yDelta, (Math.max(p.x1, p.x2)) + xDelta, (Math.max(p.y1, p.y2)) + yDelta] }; }; lowerLeftRect = function(p) { if (p.width == null) { throw new Error('lower left rect requires width'); } if (p.height == null) { throw new Error('lower left rect requires height'); } if (p.x == null) { throw new Error('lower left rectangle requires x'); } if (p.y == null) { throw new Error('lower left rectangle requires y'); } return { shape: { rect: { x: p.x, y: p.y, width: p.width, height: p.height } }, bbox: [p.x, p.y, p.x + p.width, p.y + p.height] }; }; outline = function(p) { var point, pointString, x, xLast, xMax, xMin, y, yLast, yMax, yMin, _i, _len, _ref; if (!(Array.isArray(p.points) && p.points.length > 1)) { throw new Error('outline function requires points array'); } xMin = null; yMin = null; xMax = null; yMax = null; pointString = ''; _ref = p.points; for (_i = 0, _len = _ref.length; _i < _len; _i++) { point = _ref[_i]; if (!(Array.isArray(point) && point.length === 2)) { throw new Error('outline function requires points array'); } x = point[0]; y = point[1]; if (x < xMin || xMin === null) { xMin = x; } if (x > xMax || xMax === null) { xMax = x; } if (y < yMin || yMin === null) { yMin = y; } if (y > yMax || yMax === null) { yMax = y; } pointString += " " + x + "," + y; } xLast = p.points[p.points.length - 1][0]; yLast = p.points[p.points.length - 1][1]; if (!(xLast === p.points[0][0] && yLast === p.points[0][1])) { throw new RangeError('last point must match first point of outline'); } return { shape: { polygon: { points: pointString.slice(1) } }, bbox: [xMin, yMin, xMax, yMax] }; }; moire = function(p) { var r, rings, shape; if (p.cx == null) { throw new Error('moiré requires x center'); } if (p.cy == null) { throw new Error('moiré requires y center'); } if (p.outerDia == null) { throw new Error('moiré requires outer diameter'); } if (p.ringThx == null) { throw new Error('moiré requires ring thickness'); } if (p.ringGap == null) { throw new Error('moiré requires ring gap'); } if (p.maxRings == null) { throw new Error('moiré requires max rings'); } if (p.crossLength == null) { throw new Error('moiré requires crosshair length'); } if (p.crossThx == null) { throw new Error('moiré requires crosshair thickness'); } shape = [ { line: { x1: p.cx - p.crossLength / 2, y1: 0, x2: p.cx + p.crossLength / 2, y2: 0, 'stroke-width': p.crossThx, 'stroke-linecap': 'butt' } }, { line: { x1: 0, y1: p.cy - p.crossLength / 2, x2: 0, y2: p.cy + p.crossLength / 2, 'stroke-width': p.crossThx, 'stroke-linecap': 'butt' } } ]; r = (p.outerDia - p.ringThx) / 2; rings = 0; while (r >= p.ringThx && rings < p.maxRings) { shape.push({ circle: { cx: p.cx, cy: p.cy, r: r, fill: 'none', 'stroke-width': p.ringThx } }); rings++; r -= p.ringThx + p.ringGap; } r += 0.5 * p.ringThx; if (r > 0 && rings < p.maxRings) { shape.push({ circle: { cx: p.cx, cy: p.cy, r: r } }); } return { shape: shape, bbox: [Math.min(p.cx - p.crossLength / 2, p.cx - p.outerDia / 2), Math.min(p.cy - p.crossLength / 2, p.cy - p.outerDia / 2), Math.max(p.cx + p.crossLength / 2, p.cx + p.outerDia / 2), Math.max(p.cy + p.crossLength / 2, p.cy + p.outerDia / 2)] }; }; thermal = function(p) { var halfGap, maskId, outerR, r, thx, xMax, xMin, yMax, yMin; if (p.cx == null) { throw new Error('thermal requires x center'); } if (p.cy == null) { throw new Error('thermal requires y center'); } if (p.outerDia == null) { throw new Error('thermal requires outer diameter'); } if (p.innerDia == null) { throw new Error('thermal requires inner diameter'); } if (p.gap == null) { throw new Error('thermal requires gap'); } maskId = "thermal-mask-" + (unique()); thx = (p.outerDia - p.innerDia) / 2; outerR = p.outerDia / 2; r = outerR - thx / 2; xMin = p.cx - outerR; xMax = p.cx + outerR; yMin = p.cy - outerR; yMax = p.cy + outerR; halfGap = p.gap / 2; return { shape: [ { mask: { id: maskId, _: [ { circle: { cx: p.cx, cy: p.cy, r: outerR, 'stroke-width': 0, fill: '#fff' } }, { rect: { x: xMin, y: -halfGap, width: p.outerDia, height: p.gap, 'stroke-width': 0, fill: '#000' } }, { rect: { x: -halfGap, y: yMin, width: p.gap, height: p.outerDia, 'stroke-width': 0, fill: '#000' } } ] } }, { circle: { cx: p.cx, cy: p.cy, r: r, fill: 'none', 'stroke-width': thx, mask: "url(#" + maskId + ")" } } ], bbox: [xMin, yMin, xMax, yMax] }; }; module.exports = { circle: circle, rect: rect, polygon: polygon, vector: vector, lowerLeftRect: lowerLeftRect, outline: outline, moire: moire, thermal: thermal }; },{"./unique-id":13}],11:[function(require,module,exports){ var ASSUMED_UNITS, HALF_PI, Macro, Plotter, THREEHALF_PI, TWO_PI, arcEps, tool, unique; unique = require('./unique-id'); Macro = require('./macro-tool'); tool = require('./standard-tool'); HALF_PI = Math.PI / 2; THREEHALF_PI = 3 * HALF_PI; TWO_PI = 2 * Math.PI; arcEps = 0.0000001; ASSUMED_UNITS = 'in'; Plotter = (function() { function Plotter(file, Reader, Parser) { if (file == null) { file = ''; } if (Reader != null) { this.reader = new Reader(file); } if (Parser != null) { this.parser = new Parser; } this.macros = {}; this.tools = {}; this.currentTool = ''; this.defs = []; this.group = { g: { _: [] } }; this.polarity = 'D'; this.current = []; this.stepRepeat = { x: 1, y: 1, i: 0, j: 0 }; this.srOverClear = false; this.srOverCurrent = []; this.units = null; this.mode = null; this.quad = null; this.lastOp = null; this.region = false; this.done = false; this.pos = { x: 0, y: 0 }; this.path = []; this.attr = { 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': 0, stroke: '#000' }; this.bbox = { xMin: Infinity, yMin: Infinity, xMax: -Infinity, yMax: -Infinity }; this.layerBbox = { xMin: Infinity, yMin: Infinity, xMax: -Infinity, yMax: -Infinity }; } Plotter.prototype.addTool = function(code, params) { var obj, t; if (this.tools[code] != null) { throw new Error("cannot reassign tool " + code); } if (params.macro != null) { t = this.macros[params.macro].run(code, params.mods); } else { t = tool(code, params); } this.tools[code] = { trace: t.trace, pad: (function() { var _i, _len, _ref, _results; _ref = t.pad; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { obj = _ref[_i]; _results.push(obj); } return _results; })(), flash: function(x, y) { return { use: { x: x, y: y, 'xlink:href': '#' + t.padId } }; }, bbox: function(x, y) { if (x == null) { x = 0; } if (y == null) { y = 0; } return { xMin: x + t.bbox[0], yMin: y + t.bbox[1], xMax: x + t.bbox[2], yMax: y + t.bbox[3] }; } }; return this.changeTool(code); }; Plotter.prototype.changeTool = function(code) { var _ref; this.finishPath(); if (this.region) { throw new Error("cannot change tool when in region mode"); } if (this.tools[code] == null) { if (!((_ref = this.parser) != null ? _ref.fmat : void 0)) { throw new Error("tool " + code + " is not defined"); } } else { return this.currentTool = code; } }; Plotter.prototype.command = function(c) { var code, m, params, state, val, _ref, _ref1, _ref2; if (c.macro != null) { m = new Macro(c.macro); this.macros[m.name] = m; return; } _ref = c.set; for (state in _ref) { val = _ref[state]; if (state === 'units' && (this.units != null) && (((_ref1 = this.parser) != null ? _ref1.fmat : void 0) == null)) { throw new Error('cannot redefine units'); } else if (state === 'notation' && (this.notation != null)) { throw new Error('cannot redefine notation'); } if (state === 'region') { this.finishPath(); } if (state === 'currentTool') { this.changeTool(val); } else { this[state] = val; } } if (c.tool != null) { _ref2 = c.tool; for (code in _ref2) { params = _ref2[code]; this.addTool(code, params); } } if (c.op != null) { this.operate(c.op); } if (c["new"] != null) { this.finishLayer(); if (c["new"].layer != null) { return this.polarity = c["new"].layer; } else if (c["new"].sr != null) { this.finishSR(); return this.stepRepeat = c["new"].sr; } } }; Plotter.prototype.plot = function() { var block, _ref; while (!this.done) { block = this.reader.nextBlock(); if (block === false) { if (((_ref = this.parser) != null ? _ref.fmat : void 0) == null) { throw new Error('end of file encountered before required M02 command'); } else { throw new Error('end of drill file encountered before M00/M30 command'); } } else { this.command(this.parser.parseCommand(block)); } } return this.finish(); }; Plotter.prototype.finish = function() { this.finishPath(); this.finishLayer(); this.finishSR(); this.group.g.fill = 'currentColor'; this.group.g.stroke = 'currentColor'; return this.group.g.transform = "translate(0," + (this.bbox.yMin + this.bbox.yMax) + ") scale(1,-1)"; }; Plotter.prototype.finishSR = function() { var layer, m, maskId, u, x, y, _i, _j, _k, _len, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; if (this.srOverClear && this.srOverCurrent) { maskId = "gerber-sr-mask_" + (unique()); m = { mask: { color: '#000', id: maskId, _: [] } }; m.mask._.push({ rect: { fill: '#fff', x: this.bbox.xMin, y: this.bbox.yMin, width: this.bbox.xMax - this.bbox.xMin, height: this.bbox.yMax - this.bbox.yMin } }); for (x = _i = 0, _ref = this.stepRepeat.x * this.stepRepeat.i, _ref1 = this.stepRepeat.i; _ref1 > 0 ? _i < _ref : _i > _ref; x = _i += _ref1) { for (y = _j = 0, _ref2 = this.stepRepeat.y * this.stepRepeat.j, _ref3 = this.stepRepeat.j; _ref3 > 0 ? _j < _ref2 : _j > _ref2; y = _j += _ref3) { _ref4 = this.srOverCurrent; for (_k = 0, _len = _ref4.length; _k < _len; _k++) { layer = _ref4[_k]; u = { use: {} }; if (x !== 0) { u.use.x = x; } if (y !== 0) { u.use.y = y; } u.use['xlink:href'] = '#' + ((_ref5 = layer.C) != null ? _ref5 : layer.D); if (layer.D != null) { u.use.fill = '#fff'; } m.mask._.push(u); } } } this.srOverClear = false; this.srOverCurrent = []; this.defs.push(m); return this.group.g.mask = "url(#" + maskId + ")"; } }; Plotter.prototype.finishLayer = function() { var c, h, id, obj, srId, u, w, x, y, _i, _j, _k, _len, _ref, _ref1, _ref2; this.finishPath(); if (!this.current.length) { return; } if (this.stepRepeat.x > 1 || this.stepRepeat.y > 1) { srId = "gerber-sr_" + (unique()); this.current = [ { g: { id: srId, _: this.current } } ]; if (this.srOverClear || this.stepRepeat.i < this.layerBbox.xMax - this.layerBbox.xMin || this.stepRepeat.j < this.layerBbox.yMax - this.layerBbox.yMin) { obj = {}; obj[this.polarity] = srId; this.srOverCurrent.push(obj); if (this.polarity === 'C') { this.srOverClear = true;