UNPKG

gerber-plotter

Version:

Streaming Gerber / NC drill layer image plotter

301 lines (253 loc) 7.25 kB
// gerber plotter 'use strict' var Transform = require('readable-stream').Transform var inherits = require('inherits') var PathGraph = require('./path-graph') var warning = require('./_warning') var padShape = require('./_pad-shape') var operate = require('./_operate') var boundingBox = require('./_box') var MAX_GAP = 0.00011 var isFormatKey = function(key) { return ( key === 'units' || key === 'backupUnits' || key === 'nota' || key === 'backupNota' ) } var Plotter = function( units, backupUnits, nota, backupNota, optimizePaths, plotAsOutline ) { Transform.call(this, { readableObjectMode: true, writableObjectMode: true, }) this.format = { units: units, backupUnits: backupUnits || 'in', nota: nota, backupNota: backupNota || 'A', } this._formatLock = { units: units != null, backupUnits: backupUnits != null, nota: nota != null, backupNota: backupNota != null, } this._plotAsOutline = plotAsOutline === true ? MAX_GAP : plotAsOutline // plotAsOutline parameter is always in mm if ((units || this.format.backupUnits) === 'in') { this._plotAsOutline = this._plotAsOutline / 25.4 } this._optimizePaths = optimizePaths || plotAsOutline this._line = 0 this._done = false this._tool = null this._outTool = null this._tools = {} this._macros = {} this._pos = [0, 0] this._box = boundingBox.new() this._mode = null this._arc = null this._region = false this._path = new PathGraph(this._optimizePaths, this._plotAsOutline) this._epsilon = null this._lastOp = null this._stepRep = [] } inherits(Plotter, Transform) Plotter.prototype._finishPath = function(doNotOptimize) { var path = this._path.traverse() this._path = new PathGraph( !doNotOptimize && this._optimizePaths, this._plotAsOutline ) if (path.length) { // check for outline tool var tool = !this._plotAsOutline ? this._tool : this._outTool if (!this._region && tool.trace.length === 1) { this.push({type: 'stroke', width: tool.trace[0], path: path}) } else { this.push({type: 'fill', path: path}) } } } Plotter.prototype._warn = function(message) { this.emit('warning', warning(message, this._line)) } Plotter.prototype._checkFormat = function() { if (!this.format.units) { this.format.units = this.format.backupUnits this._warn('units not set; using backup units: ' + this.format.units) } if (!this.format.nota) { this.format.nota = this.format.backupNota this._warn('notation not set; using backup notation: ' + this.format.nota) } } Plotter.prototype._updateBox = function(box) { var stepRepLen = this._stepRep.length if (!stepRepLen) { this._box = boundingBox.add(this._box, box) } else { var repeatBox = boundingBox.repeat(box, this._stepRep[stepRepLen - 1]) this._box = boundingBox.add(this._box, repeatBox) } } Plotter.prototype._transform = function(chunk, encoding, done) { var type = chunk.type this._line = chunk.line if (this._done) { this._warn('ignoring extra command recieved after done command') return done() } // check for an operation if (type === 'op') { this._checkFormat() var op = chunk.op var coord = chunk.coord if (this.nota === 'I') { var _this = this coord = Object.keys(coord).reduce(function(result, key) { var value = coord[key] if (key === 'x') { result[key] = _this._pos[0] + value } else if (key === 'y') { result[key] = _this._pos[1] + value } else { result[key] = value } return result }, {}) } if (op === 'last') { this._warn('modal operation commands are deprecated') op = this._lastOp } if (op === 'int') { if (this._mode == null) { this._warn('no interpolation mode specified; assuming linear') this._mode = 'i' } if (this._arc == null && this._mode.slice(-2) === 'cw' && !coord.a) { this._warn('quadrant mode unspecified; assuming single quadrant') this._arc = 's' } } if (this._plotAsOutline) { this._outTool = this._tool } var result = operate( op, coord, this._pos, this._tool, this._mode, this._arc, this._region || this._plotAsOutline, this._path, this._epsilon, this ) this._lastOp = op this._pos = result.pos this._updateBox(result.box) } else if (type === 'set') { var prop = chunk.prop var value = chunk.value // if region change, finish the path if (prop === 'region') { this._finishPath(value) this._region = value } else if (isFormatKey(prop) && !this._formatLock[prop]) { // else we might need to set the format this.format[prop] = value if (prop === 'units' || prop === 'nota') { this._formatLock[prop] = true } } else if (prop === 'tool') { // else if we're dealing with a tool change, finish the path and change if (this._region) { this._warn('cannot change tool while region mode is on') } else if (!this._tools[value]) { this._warn('tool ' + value + ' is not defined') } else if (!this._outTool) { this._finishPath() this._tool = this._tools[value] } } else { // else set interpolation or arc mode this['_' + prop] = value } } else if (type === 'tool') { // else tool commands var code = chunk.code var toolDef = chunk.tool if (this._tools[code]) { this._warn('tool ' + code + ' is already defined; overwriting definition') } var shapeAndBox = padShape(toolDef, this._macros) var tool = { code: code, trace: [], pad: shapeAndBox.shape, flashed: false, box: shapeAndBox.box, } if (toolDef.shape === 'circle' || toolDef.shape === 'rect') { if (toolDef.hole.length === 0) { tool.trace = toolDef.params } } if (!this._outTool) { this._finishPath() this._tools[code] = tool this._tool = tool } } else if (type === 'macro') { this._macros[chunk.name] = chunk.blocks } else if (type === 'level') { var level = chunk.level var levelValue = chunk.value this._finishPath() if (level === 'polarity') { this.push({ type: 'polarity', polarity: levelValue === 'C' ? 'clear' : 'dark', box: this._box.slice(0), }) } else { // calculate new offsets var offsets = [] if (levelValue.x > 1 || levelValue.y > 1) { for (var x = 0; x < levelValue.x; x++) { for (var y = 0; y < levelValue.y; y++) { offsets.push([x * levelValue.i, y * levelValue.j]) } } } this._stepRep = offsets this.push({ type: 'repeat', offsets: this._stepRep.slice(0), box: this._box.slice(0), }) } } else if (type === 'done') { this._done = true } return done() } Plotter.prototype._flush = function(done) { this._finishPath() this.push({type: 'size', box: this._box, units: this.format.units}) done() } module.exports = Plotter