gerber-plotter
Advanced tools
Comparing version 0.1.4 to 1.0.0
46
API.md
@@ -9,3 +9,3 @@ # gerber plotter api | ||
var gerberPlotter = require('gerber-plotter') | ||
var plotter = gerberParser(OPTIONS) | ||
var plotter = gerberPlotter(OPTIONS) | ||
``` | ||
@@ -15,3 +15,3 @@ | ||
Use the gerber plotter like you would any other [Node stream](https://github.com/substack/stream-handbook). | ||
The plotter is a [Node Transform Stream](https://nodejs.org/api/stream.html#stream_class_stream_transform) in object mode. For more information about working with Node streams, read [@substack's stream handbook](https://github.com/substack/stream-handbook). | ||
@@ -29,9 +29,37 @@ ### options | ||
key | value | description | ||
--------------|--------------|--------------------------------------------- | ||
`units` | `mm` or `in` | PCB units | ||
`backupUnits` | `mm` or `in` | Backup units in case units are missing | ||
`nota` | `A` or `I` | Absolute or incremental coordinate notation | ||
`backupNota` | `A` or `I` | Backup notation in case notation is missing | ||
key | value | default | description | ||
----------------|--------------|---------|--------------------------------------------------------- | ||
`units` | `mm` or `in` | N/A | PCB units | ||
`backupUnits` | `mm` or `in` | `in` | Backup units in case units are missing | ||
`nota` | `A` or `I` | N/A | Absolute or incremental coordinate notation | ||
`backupNota` | `A` or `I` | `A` | Backup notation in case notation is missing | ||
`optimizePaths` | Boolean | `false` | Optimize order of paths in strokes and regions | ||
`plotAsOutline` | Boolean | `false` | Treat layer as an outline by combining all tools for paths | ||
#### units and backup units options | ||
Setting `units` will set the plot units of the file regardless of any units that are specified in the file itself. Setting `backupUnits` specifies a fallback that will only be used if there are no units specified in the file. | ||
#### notation and backup notation options | ||
Coordinates in a Gerber / drill file will either be absolute (common and recommended) or incremental (uncommon and deprecated by the Gerber spec). Setting `nota` will override any settings in the Gerber file, while `backupNota` will be used as a fallback if that setting is missing. | ||
#### optimize paths option | ||
This option is off by default. When `optimizePaths` is true, the plotter will reorganize segments in any given stroke or region for file-size efficiency at the expense of plotting speed. It does this by gathering all points and segments and then re-playing them in adjacency order. This results in smaller stroke and fill objects by removing unnecessary plotter moves. | ||
For example, with `optimizePaths` on, if the Gerber file says `MOVE TO (1, 1); LINE TO (2, 1); MOVE TO (2, 2); LINE TO (2, 1)`, the plotter will convert that to `LINE FROM (1, 1) TO (2, 1); LINE FROM (2, 1) TO (2, 2)` | ||
Setting this option to `false` will speed up plotting at the expense of ensuring paths are as efficient as possible. | ||
#### plot as outline option | ||
This option is off by default. When `plotAsOutline` is true, the plotter will take several actions: | ||
* The `optimizePaths` option will be forced to true | ||
* All stroke tools will be merged into one tool (the first tool in the file used for a stroke) | ||
* The bounding box of the file will be calculated as if all strokes are done in region mode | ||
This option exists to take an image representing an outline layer and optimize it to get at the board information the layer represents. Often, outline layers will (accidentally) use different tools for the same line. Additionally, the size of the board is determined by the center of the edge line, rather than the outside of the line, hence switching the size calculation of the path to region mode. | ||
## public properties | ||
@@ -160,3 +188,3 @@ | ||
Regardless of the order they appear in the gerber file itself, this library will try to ensure that any segments from a given tool until the region mode is changed will be places adjacently in `path`. For example, if the Gerber file says `MOVE TO (1, 1); LINE TO (2, 1); MOVE TO (2, 2); LINE TO (2, 1)`, the library will convert that to `LINE FROM (1, 1) TO (2, 1); LINE FROM (2, 1) TO (2, 2)` | ||
The `path` array of these objects will be smaller if the `optimizePaths` option is set to true. | ||
@@ -163,0 +191,0 @@ ``` javascript |
@@ -147,5 +147,41 @@ // operate the plotter | ||
var drawArc = function(start, end, offset, tool, mode, arc, region, epsilon, pathGraph, plotter) { | ||
var roundToZero = function(number, epsilon) { | ||
return (number >= epsilon) ? number : 0 | ||
} | ||
// find the center of an arc given its endpoints and its radius | ||
// assume the arc is <= 180 degress | ||
// thank you this guy: http://math.stackexchange.com/a/87912 | ||
var arcCenterFromRadius = function(start, end, mode, epsilon, radius) { | ||
var sign = (mode === 'ccw') ? 1 : -1 | ||
var xAve = (start[0] + end[0]) / 2 | ||
var yAve = (start[1] + end[1]) / 2 | ||
var deltaX = end[0] - start[1] | ||
var deltaY = end[1] - start[1] | ||
var distance = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)) | ||
var halfDistance = distance / 2 | ||
var squareDifference = Math.sqrt(Math.pow(radius, 2) - Math.pow(halfDistance, 2)) | ||
var xOffset = -sign * deltaY * squareDifference / distance | ||
var yOffset = sign * deltaX * squareDifference / distance | ||
return [[ | ||
roundToZero(xAve + xOffset, epsilon), | ||
roundToZero(yAve + yOffset, epsilon) | ||
]] | ||
} | ||
var drawArc = function( | ||
start, | ||
end, | ||
offset, | ||
tool, | ||
mode, | ||
arc, | ||
region, | ||
epsilon, | ||
pathGraph, | ||
plotter) { | ||
// get the radius of the arc from the offsets | ||
var r = Math.sqrt(Math.pow(offset[0], 2) + Math.pow(offset[1], 2)) | ||
var r = offset[2] || Math.sqrt(Math.pow(offset[0], 2) + Math.pow(offset[1], 2)) | ||
@@ -179,8 +215,18 @@ // potential candidates for the arc center | ||
// find valid centers by comparing the distance to start and end for equality with the radius | ||
var validCenters = (arc === 'm') ? candidates : filter(candidates, function(c) { | ||
var startDist = Math.sqrt(Math.pow(c[0] - start[0], 2) + Math.pow(c[1] - start[1], 2)) | ||
var endDist = Math.sqrt(Math.pow(c[0] - end[0], 2) + Math.pow(c[1] - end[1], 2)) | ||
var validCenters | ||
if (offset[2]) { | ||
arc = 'm' | ||
validCenters = arcCenterFromRadius(start, end, mode, epsilon, offset[2]) | ||
} | ||
else if (arc === 's') { | ||
validCenters = filter(candidates, function(c) { | ||
var startDist = Math.sqrt(Math.pow(c[0] - start[0], 2) + Math.pow(c[1] - start[1], 2)) | ||
var endDist = Math.sqrt(Math.pow(c[0] - end[0], 2) + Math.pow(c[1] - end[1], 2)) | ||
return ((Math.abs(startDist - r) <= epsilon) && (Math.abs(endDist - r) <= epsilon)) | ||
}) | ||
return ((Math.abs(startDist - r) <= epsilon) && (Math.abs(endDist - r) <= epsilon)) | ||
}) | ||
} | ||
else { | ||
validCenters = candidates | ||
} | ||
@@ -312,3 +358,3 @@ var cenAndAngles = findCenterAndAngles(start, end, mode, arc, validCenters) | ||
if ((region === false) && (tool.trace.length === 0)) { | ||
if (!region && (tool.trace.length === 0)) { | ||
plotter._warn('tool ' + tool.code + ' is not strokable; ignoring interpolate') | ||
@@ -320,3 +366,3 @@ return boundingBox.new() | ||
// add a line to the path normally if region mode is on or the tool is a circle | ||
if ((region === true) || (tool.trace.length === 1)) { | ||
if (region || (tool.trace.length === 1)) { | ||
return drawLine(start, end, tool, region, pathGraph) | ||
@@ -330,3 +376,3 @@ } | ||
// else, make sure we're allowed to be drawing an arc, then draw an arc | ||
if ((tool.trace.length !== 1) && (region === false)) { | ||
if ((tool.trace.length !== 1) && !region) { | ||
plotter._warn('cannot draw an arc with a non-circular tool') | ||
@@ -351,3 +397,4 @@ return boundingBox.new() | ||
((coord.i != null) ? coord.i : 0), | ||
((coord.j != null) ? coord.j : 0) | ||
((coord.j != null) ? coord.j : 0), | ||
coord.a | ||
] | ||
@@ -354,0 +401,0 @@ |
@@ -320,3 +320,3 @@ // returns a pad shape array given a tool definition | ||
var toolShape = tool.shape | ||
var params = tool.val | ||
var params = tool.params | ||
var holeShape | ||
@@ -323,0 +323,0 @@ var shapeAndBox |
@@ -7,2 +7,3 @@ // utilities to create a graph of path segments and traverse that graph | ||
var find = require('lodash.find') | ||
var map = require('lodash.map') | ||
@@ -26,5 +27,6 @@ var pointsEqual = function(point, target) { | ||
var PathGraph = function() { | ||
var PathGraph = function(optimize) { | ||
this._points = [] | ||
this._edges = [] | ||
this._optimize = optimize | ||
@@ -35,10 +37,15 @@ this.length = 0 | ||
PathGraph.prototype.add = function(newSeg) { | ||
var start = find(this._points, function(point) { | ||
return pointsEqual(point.position, newSeg.start) | ||
}) | ||
var start | ||
var end | ||
var end = find(this._points, function(point) { | ||
return pointsEqual(point.position, newSeg.end) | ||
}) | ||
if (this._optimize) { | ||
start = find(this._points, function(point) { | ||
return pointsEqual(point.position, newSeg.start) | ||
}) | ||
end = find(this._points, function(point) { | ||
return pointsEqual(point.position, newSeg.end) | ||
}) | ||
} | ||
if (!start) { | ||
@@ -64,2 +71,6 @@ start = {position: newSeg.start, edges: []} | ||
PathGraph.prototype.traverse = function() { | ||
if (!this._optimize) { | ||
return map(this._edges, 'segment') | ||
} | ||
var walked = fill(Array(this._edges.length), false) | ||
@@ -66,0 +77,0 @@ var discovered = [] |
{ | ||
"name": "gerber-plotter", | ||
"version": "0.1.4", | ||
"version": "1.0.0", | ||
"description": "Transform stream that takes objects from gerber-parser and emits PCB image objects", | ||
"main": "lib/gerber-plotter.js", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"lint": "eslint lib/*.js test/*.js", | ||
"test": "istanbul cover --include-all-sources _mocha", | ||
"test": "istanbul cover --include-all-sources _mocha -- -t 500", | ||
"posttest": "npm run lint", | ||
"test-watch": "mocha --reporter dot --watch", | ||
"browser": "zuul --local -- ./test/*_test.js", | ||
"browser-phantom": "zuul --phantom -- ./test/*_test.js", | ||
"browser-sauce": "zuul -- ./test/*_test.js", | ||
"ci": "npm test && if [ \"${TEST_BROWSERS}\" = \"true\" ]; then npm run ci-browser; fi", | ||
"ci-browser": "if [ \"${TRAVIS_PULL_REQUEST}\" = \"false\" ]; then npm run browser-sauce; fi", | ||
"test:watch": "mocha -t 500 --reporter dot --watch", | ||
"test:browser": "zuul --local -- ./test/*_test.js", | ||
"test:sauce": "zuul -- ./test/*_test.js", | ||
"ci": "npm test && if [ \"${TEST_BROWSERS}\" = \"true\" ]; then npm run ci:browser; fi", | ||
"ci:browser": "if [ \"${TRAVIS_PULL_REQUEST}\" = \"false\" ]; then npm run test:sauce; fi", | ||
"postci": "coveralls < ./coverage/lcov.info" | ||
}, | ||
"pre-commit": [ | ||
"lint" | ||
], | ||
"repository": { | ||
@@ -44,30 +40,28 @@ "type": "git", | ||
"chai": "^3.2.0", | ||
"coveralls": "^2.11.4", | ||
"eslint": "^1.3.1", | ||
"istanbul": "^0.4.0", | ||
"coveralls": "^2.11.9", | ||
"eslint": "^2.9.0", | ||
"istanbul": "^0.4.3", | ||
"mocha": "^2.3.1", | ||
"phantomjs": "^1.9.18", | ||
"pre-commit": "^1.1.1", | ||
"zuul": "^3.4.0" | ||
"zuul": "^3.10.1" | ||
}, | ||
"peerDependencies": { | ||
"gerber-parser": "^0.1.2" | ||
"gerber-parser": "^1.0.0" | ||
}, | ||
"dependencies": { | ||
"lodash.assign": "^3.2.0", | ||
"lodash.clone": "^3.0.3", | ||
"lodash.fill": "^3.2.2", | ||
"lodash.filter": "^3.1.1", | ||
"lodash.find": "^3.2.1", | ||
"lodash.foreach": "^3.0.3", | ||
"lodash.foreachright": "^3.0.2", | ||
"lodash.has": "^3.2.1", | ||
"lodash.isfinite": "^3.2.0", | ||
"inherits": "^2.0.1", | ||
"lodash.clone": "^4.3.2", | ||
"lodash.fill": "^3.3.4", | ||
"lodash.filter": "^4.3.0", | ||
"lodash.find": "^4.3.0", | ||
"lodash.foreach": "^4.2.0", | ||
"lodash.foreachright": "^4.1.0", | ||
"lodash.has": "^4.3.1", | ||
"lodash.isfinite": "^3.3.1", | ||
"lodash.isfunction": "^3.0.6", | ||
"lodash.mapvalues": "^3.0.1", | ||
"lodash.pick": "^3.1.0", | ||
"lodash.reduce": "^3.1.2", | ||
"lodash.transform": "^3.0.4", | ||
"readable-stream": "^2.0.2" | ||
"lodash.map": "^4.3.0", | ||
"lodash.mapvalues": "^4.3.0", | ||
"lodash.reduce": "^4.3.0", | ||
"lodash.transform": "^4.3.0", | ||
"readable-stream": "^2.1.2" | ||
} | ||
} |
# gerber plotter | ||
[](https://www.npmjs.com/package/gerber-plotter) | ||
@@ -25,5 +26,9 @@ [](https://travis-ci.org/mcous/gerber-plotter) | ||
plotter.on('warning', function(w) { | ||
console.warn(`plotter warning at line ${w.line}: ${w.message}`) | ||
console.warn('plotter warning at line ' + w.line + ': ' + w.message) | ||
}) | ||
plotter.once('error', function(e) { | ||
console.error('plotter error: ' + e.message) | ||
}) | ||
fs.createReadStream('/path/to/gerber/file.gbr', {encoding: 'utf8'}) | ||
@@ -33,3 +38,3 @@ .pipe(parser) | ||
.on('data', function(obj) { | ||
console.log(obj) | ||
console.log(JSON.stringify(obj)) | ||
}) | ||
@@ -46,3 +51,3 @@ ``` | ||
Tests are written in [Mocha](http://mochajs.org/) and run in Node, [PhantomJS](http://phantomjs.org/), and a variety of browsers with [Zuul](https://github.com/defunctzombie/zuul) and [Open Sauce](https://saucelabs.com/opensauce/). All PRs should be accompanied by unit tests, with ideally one feature / bugfix per PR. Code linting happens with [ESLint](http://eslint.org/) automatically post-test and pre-commit. | ||
Tests are written in [Mocha](http://mochajs.org/) and run in Node and a variety of browsers with [Zuul](https://github.com/defunctzombie/zuul) and [Open Sauce](https://saucelabs.com/opensauce/). All PRs should be accompanied by unit tests, with ideally one feature / bugfix per PR. Code linting happens with [ESLint](http://eslint.org/) automatically post-test. | ||
@@ -55,9 +60,10 @@ Code is deployed on tags via [TravisCI](https://travis-ci.org/) and code coverage is tracked with [Coveralls](https://coveralls.io/). | ||
* `$ npm run test` - runs Node unit tests | ||
* `$ npm run test-watch` - runs unit tests and re-runs on changes | ||
* `$ npm run browser-test` - runs tests in a local browser | ||
* `$ npm run browser-test-phantom` - runs tests in PhantomJS | ||
* `$ npm run browser-test-sauce` - runs tests in Sauce Labs on multiple browsers | ||
* `$ npm run test:watch` - runs unit tests and re-runs on changes | ||
* `$ npm run test:browser` - runs tests in a local browser | ||
* `$ npm run test:sauce` - runs tests in Sauce Labs on multiple browsers | ||
* Sauce Labs account required | ||
* Local [.zuulrc](https://github.com/defunctzombie/zuul/wiki/Zuulrc) required | ||
* `$ npm run ci` - Script for CI server to run | ||
* Runs `npm test` and sends coverage report to Coveralls | ||
* If you want to run this locally, you'll need to set some environment variables | ||
* If not a PR, runs browser tests in Sauce | ||
* Not designed to (and won't) run locally |
@@ -10,3 +10,3 @@ 'use strict' | ||
beforeEach(function() { | ||
p = new PathGraph() | ||
p = new PathGraph(true) | ||
}) | ||
@@ -130,2 +130,26 @@ | ||
}) | ||
it('should not optimize the path if passed a false during construction', function() { | ||
p = new PathGraph(false) | ||
p.add({type: 'line', start: [0, 0], end: [1, 0]}) | ||
p.add({type: 'line', start: [0, 0], end: [-1, 0]}) | ||
p.add({type: 'line', start: [0, 1], end: [1, 1]}) | ||
p.add({type: 'line', start: [-1, -1], end: [0, -1]}) | ||
p.add({type: 'line', start: [0, 0], end: [0, 1]}) | ||
p.add({type: 'line', start: [0, 0], end: [0, -1]}) | ||
p.add({type: 'line', start: [1, 0], end: [1, 1]}) | ||
p.add({type: 'line', start: [-1, 0], end: [-1, -1]}) | ||
expect(p.traverse()).to.eql([ | ||
{type: 'line', start: [0, 0], end: [1, 0]}, | ||
{type: 'line', start: [0, 0], end: [-1, 0]}, | ||
{type: 'line', start: [0, 1], end: [1, 1]}, | ||
{type: 'line', start: [-1, -1], end: [0, -1]}, | ||
{type: 'line', start: [0, 0], end: [0, 1]}, | ||
{type: 'line', start: [0, 0], end: [0, -1]}, | ||
{type: 'line', start: [1, 0], end: [1, 1]}, | ||
{type: 'line', start: [-1, 0], end: [-1, -1]} | ||
]) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
124781
6
2838
0
66
17
+ Addedinherits@^2.0.1
+ Addedlodash.map@^4.3.0
+ Addedgerber-parser@1.1.1(transitive)
+ Addedlodash.clone@4.5.0(transitive)
+ Addedlodash.filter@4.6.0(transitive)
+ Addedlodash.find@4.6.0(transitive)
+ Addedlodash.foreach@4.5.0(transitive)
+ Addedlodash.foreachright@4.4.0(transitive)
+ Addedlodash.has@4.5.2(transitive)
+ Addedlodash.map@4.6.0(transitive)
+ Addedlodash.mapvalues@4.6.0(transitive)
+ Addedlodash.reduce@4.6.0(transitive)
+ Addedlodash.transform@4.6.0(transitive)
- Removedlodash.assign@^3.2.0
- Removedlodash.pick@^3.1.0
- Removedgerber-parser@0.1.7(transitive)
- Removedlodash._arraycopy@3.0.0(transitive)
- Removedlodash._arrayeach@3.0.0(transitive)
- Removedlodash._arrayfilter@3.0.0(transitive)
- Removedlodash._arraymap@3.0.0(transitive)
- Removedlodash._baseassign@3.2.0(transitive)
- Removedlodash._basecallback@3.3.1(transitive)
- Removedlodash._baseclone@3.3.0(transitive)
- Removedlodash._basecopy@3.0.1(transitive)
- Removedlodash._basecreate@3.0.3(transitive)
- Removedlodash._baseeach@3.0.4(transitive)
- Removedlodash._baseeachright@3.0.3(transitive)
- Removedlodash._basefilter@3.0.0(transitive)
- Removedlodash._basefind@3.0.0(transitive)
- Removedlodash._basefindindex@3.6.0(transitive)
- Removedlodash._baseflatten@3.1.4(transitive)
- Removedlodash._basefor@3.0.3(transitive)
- Removedlodash._baseforright@3.0.2(transitive)
- Removedlodash._baseget@3.7.2(transitive)
- Removedlodash._baseisequal@3.0.7(transitive)
- Removedlodash._basereduce@3.0.2(transitive)
- Removedlodash._baseslice@3.0.3(transitive)
- Removedlodash._bindcallback@3.0.1(transitive)
- Removedlodash._createassigner@3.1.1(transitive)
- Removedlodash._createwrapper@3.2.0(transitive)
- Removedlodash._getnative@3.9.1(transitive)
- Removedlodash._isiterateecall@3.0.9(transitive)
- Removedlodash._pickbyarray@3.0.2(transitive)
- Removedlodash._pickbycallback@3.0.0(transitive)
- Removedlodash._replaceholders@3.0.0(transitive)
- Removedlodash._topath@3.8.1(transitive)
- Removedlodash.assign@3.2.0(transitive)
- Removedlodash.bind@3.1.0(transitive)
- Removedlodash.clone@3.0.3(transitive)
- Removedlodash.filter@3.1.1(transitive)
- Removedlodash.find@3.2.1(transitive)
- Removedlodash.foreach@3.0.3(transitive)
- Removedlodash.foreachright@3.0.2(transitive)
- Removedlodash.has@3.2.1(transitive)
- Removedlodash.isarguments@3.1.0(transitive)
- Removedlodash.isarray@3.0.4(transitive)
- Removedlodash.istypedarray@3.0.6(transitive)
- Removedlodash.keys@3.1.2(transitive)
- Removedlodash.keysin@3.0.8(transitive)
- Removedlodash.map@3.1.4(transitive)
- Removedlodash.mapvalues@3.0.1(transitive)
- Removedlodash.pairs@3.0.1(transitive)
- Removedlodash.partial@3.1.1(transitive)
- Removedlodash.pick@3.1.0(transitive)
- Removedlodash.reduce@3.1.2(transitive)
- Removedlodash.restparam@3.6.1(transitive)
- Removedlodash.set@3.7.4(transitive)
- Removedlodash.transform@3.0.4(transitive)
Updatedlodash.clone@^4.3.2
Updatedlodash.fill@^3.3.4
Updatedlodash.filter@^4.3.0
Updatedlodash.find@^4.3.0
Updatedlodash.foreach@^4.2.0
Updatedlodash.foreachright@^4.1.0
Updatedlodash.has@^4.3.1
Updatedlodash.isfinite@^3.3.1
Updatedlodash.mapvalues@^4.3.0
Updatedlodash.reduce@^4.3.0
Updatedlodash.transform@^4.3.0
Updatedreadable-stream@^2.1.2