gerber-to-svg
Version:
Render individual Gerber / NC drill files as SVGs
1,269 lines (1,162 loc) • 41.1 kB
JavaScript
// test suite for the plotter to svg transform stream
'use strict'
var assign = require('lodash/assign')
var sinon = require('sinon')
var expect = require('chai').expect
var xmlElement = require('xml-element-string')
var PlotterToSvg = require('../lib/plotter-to-svg')
var HALF_PI = Math.PI / 2
var SVG_ATTR = {
xmlns: 'http://www.w3.org/2000/svg',
version: '1.1',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '0',
'fill-rule': 'evenodd',
width: '0',
height: '0',
viewBox: '0 0 0 0',
}
var EMPTY_BOX = [Infinity, Infinity, -Infinity, -Infinity]
describe('plotter to svg transform stream', function() {
var p
var element
beforeEach(function() {
element = sinon.spy(xmlElement)
p = new PlotterToSvg('id', {}, element)
p.setEncoding('utf8')
})
it('should emit an empty svg if it gets a zero size plot', function(done) {
p.once('data', function() {
expect(element).to.be.calledWith('svg', SVG_ATTR, [])
expect(p.viewBox).to.eql([0, 0, 0, 0])
expect(p.width).to.equal(0)
expect(p.height).to.equal(0)
expect(p.units).to.equal('')
done()
})
p.write({type: 'size', box: EMPTY_BOX, units: ''})
p.end()
})
it('should be able to add attributes', function(done) {
var converter = new PlotterToSvg('foo', {id: 'foo', bar: 'baz'}, element)
var expected = assign({}, SVG_ATTR, {id: 'foo', bar: 'baz'})
converter.once('data', function() {
expect(element).to.be.calledWith('svg', expected)
done()
})
converter.write({type: 'size', box: EMPTY_BOX, units: ''})
converter.end()
})
describe('creating pad shapes', function() {
it('should handle circle primitives', function() {
var toolShape = [{type: 'circle', cx: 0.001, cy: 0.002, r: 0.005}]
var expected = {id: 'id_pad-10', cx: 1, cy: 2, r: 5}
p.write({type: 'shape', tool: '10', shape: toolShape})
expect(element).to.be.calledWith('circle', expected)
expect(p.defs).to.eql([element.returnValues[0]])
})
it('should handle rect primitives', function() {
var toolShape = [
{
type: 'rect',
cx: 0.002,
cy: 0.004,
width: 0.002,
height: 0.004,
r: 0.002,
},
]
var expected = {
id: 'id_pad-10',
x: 1,
y: 2,
rx: 2,
ry: 2,
width: 2,
height: 4,
}
p.write({type: 'shape', tool: '10', shape: toolShape})
expect(element).to.be.calledWith('rect', expected)
expect(p.defs).to.eql([element.returnValues[0]])
})
it('should handle polygon primitives', function() {
var toolShape = [
{
type: 'poly',
points: [
[0, 0],
[1, 0],
[0, 1],
],
},
]
var expected = {id: 'id_pad-12', points: '0,0 1000,0 0,1000'}
p.write({type: 'shape', tool: '12', shape: toolShape})
expect(element).to.be.calledWith('polygon', expected)
expect(p.defs).to.eql([element.returnValues[0]])
})
it('should handle a ring primitives', function() {
var toolShape = [
{type: 'ring', r: 0.02, width: 0.005, cx: 0.05, cy: -0.03},
]
var expected = {
id: 'id_pad-11',
cx: 50,
cy: -30,
r: 20,
'stroke-width': 5,
fill: 'none',
}
p.write({type: 'shape', tool: '11', shape: toolShape})
expect(element).to.be.calledWith('circle', expected)
expect(p.defs).to.eql([element.returnValues[0]])
})
it('should handle a clipped primitive with rects', function() {
var clippedShapes = [
{type: 'rect', cx: 0.003, cy: 0.003, width: 0.004, height: 0.004, r: 0},
{
type: 'rect',
cx: -0.003,
cy: 0.003,
width: 0.004,
height: 0.004,
r: 0,
},
{
type: 'rect',
cx: -0.003,
cy: -0.003,
width: 0.004,
height: 0.004,
r: 0,
},
{
type: 'rect',
cx: 0.003,
cy: -0.003,
width: 0.004,
height: 0.004,
r: 0,
},
]
var ring = {type: 'ring', r: 0.004, width: 0.002, cx: 0, cy: 0}
var toolShape = [{type: 'clip', shape: clippedShapes, clip: ring}]
var expected = [
{cx: 0, cy: 0, r: 4, 'stroke-width': 2, fill: 'none'},
{id: 'id_pad-15_mask-0', stroke: '#fff'},
{x: 1, y: 1, width: 4, height: 4},
{x: -5, y: 1, width: 4, height: 4},
{x: -5, y: -5, width: 4, height: 4},
{x: 1, y: -5, width: 4, height: 4},
{id: 'id_pad-15', mask: 'url(#id_pad-15_mask-0)'},
]
p.write({type: 'shape', tool: '15', shape: toolShape})
expect(element).to.be.calledWith('circle', expected[0])
expect(element).to.be.calledWith('mask', expected[1], [
element.returnValues[0],
])
expect(element).to.be.calledWith('rect', expected[2])
expect(element).to.be.calledWith('rect', expected[3])
expect(element).to.be.calledWith('rect', expected[4])
expect(element).to.be.calledWith('rect', expected[5])
expect(element).to.be.calledWith(
'g',
expected[6],
element.returnValues.slice(2, 6)
)
expect(p.defs).to.eql([element.returnValues[1], element.returnValues[6]])
})
it('should handle a clipped primitive with polys', function() {
var po = 0.001
var ne = -0.005
var mP = po + 0.004
var mN = ne + 0.004
var clippedShapes = [
{
type: 'poly',
points: [
[po, po],
[mP, po],
[mP, mP],
[po, mP],
],
},
{
type: 'poly',
points: [
[ne, po],
[mN, po],
[mN, mP],
[ne, mP],
],
},
{
type: 'poly',
points: [
[ne, ne],
[mN, ne],
[mN, mN],
[ne, mN],
],
},
{
type: 'poly',
points: [
[po, ne],
[mP, ne],
[mP, mN],
[po, mN],
],
},
]
var ring = {type: 'ring', r: 0.004, width: 0.002, cx: 0, cy: 0}
var toolShape = [{type: 'clip', shape: clippedShapes, clip: ring}]
var expected = [
{cx: 0, cy: 0, r: 4, 'stroke-width': 2, fill: 'none'},
{id: 'id_pad-15_mask-0', stroke: '#fff'},
{points: '1,1 5,1 5,5 1,5'},
{points: '-5,1 -1,1 -1,5 -5,5'},
{points: '-5,-5 -1,-5 -1,-1 -5,-1'},
{points: '1,-5 5,-5 5,-1 1,-1'},
{id: 'id_pad-15', mask: 'url(#id_pad-15_mask-0)'},
]
p.write({type: 'shape', tool: '15', shape: toolShape})
expect(element).to.be.calledWith('circle', expected[0])
expect(element).to.be.calledWith('mask', expected[1], [
element.returnValues[0],
])
expect(element).to.be.calledWith('polygon', expected[2])
expect(element).to.be.calledWith('polygon', expected[3])
expect(element).to.be.calledWith('polygon', expected[4])
expect(element).to.be.calledWith('polygon', expected[5])
expect(element).to.be.calledWith(
'g',
expected[6],
element.returnValues.slice(2, 6)
)
expect(p.defs).to.eql([element.returnValues[1], element.returnValues[6]])
})
it('should handle multiple primitives', function() {
var toolShape = [
{type: 'rect', cx: 0.003, cy: 0.003, width: 0.004, height: 0.004, r: 0},
{
type: 'rect',
cx: -0.003,
cy: 0.003,
width: 0.004,
height: 0.004,
r: 0,
},
{
type: 'rect',
cx: -0.003,
cy: -0.003,
width: 0.004,
height: 0.004,
r: 0,
},
{
type: 'rect',
cx: 0.003,
cy: -0.003,
width: 0.004,
height: 0.004,
r: 0,
},
]
var expected = [
{x: 1, y: 1, width: 4, height: 4},
{x: -5, y: 1, width: 4, height: 4},
{x: -5, y: -5, width: 4, height: 4},
{x: 1, y: -5, width: 4, height: 4},
{id: 'id_pad-20'},
]
p.write({type: 'shape', tool: '20', shape: toolShape})
expect(element).to.be.calledWith('rect', expected[0])
expect(element).to.be.calledWith('rect', expected[1])
expect(element).to.be.calledWith('rect', expected[2])
expect(element).to.be.calledWith('rect', expected[3])
expect(element).to.be.calledWith(
'g',
expected[4],
element.returnValues.slice(0, 4)
)
expect(p.defs).to.eql([element.returnValues[4]])
})
it('should handle multiple clipped primitives', function() {
var clippedShapes1 = [
{type: 'rect', cx: 0.003, cy: 0.003, width: 0.004, height: 0.004, r: 0},
{
type: 'rect',
cx: -0.003,
cy: 0.003,
width: 0.004,
height: 0.004,
r: 0,
},
{
type: 'rect',
cx: -0.003,
cy: -0.003,
width: 0.004,
height: 0.004,
r: 0,
},
{
type: 'rect',
cx: 0.003,
cy: -0.003,
width: 0.004,
height: 0.004,
r: 0,
},
]
var clippedShapes2 = [
{type: 'rect', cx: 0.003, cy: 0.003, width: 0.002, height: 0.002, r: 0},
{
type: 'rect',
cx: -0.003,
cy: 0.003,
width: 0.002,
height: 0.002,
r: 0,
},
{
type: 'rect',
cx: -0.003,
cy: -0.003,
width: 0.002,
height: 0.002,
r: 0,
},
{
type: 'rect',
cx: 0.003,
cy: -0.003,
width: 0.002,
height: 0.002,
r: 0,
},
]
var ring1 = {type: 'ring', r: 0.004, width: 0.002, cx: 0, cy: 0}
var ring2 = {type: 'ring', r: 0.002, width: 0.001, cx: 0, cy: 0}
var toolShape = [
{type: 'clip', shape: clippedShapes1, clip: ring1},
{type: 'clip', shape: clippedShapes2, clip: ring2},
]
p.write({type: 'shape', tool: '15', shape: toolShape})
var values = element.returnValues
var expected = [
{cx: 0, cy: 0, r: 4, 'stroke-width': 2, fill: 'none'},
{id: 'id_pad-15_mask-0', stroke: '#fff'},
{x: 1, y: 1, width: 4, height: 4},
{x: -5, y: 1, width: 4, height: 4},
{x: -5, y: -5, width: 4, height: 4},
{x: 1, y: -5, width: 4, height: 4},
{mask: 'url(#id_pad-15_mask-0)'},
{cx: 0, cy: 0, r: 2, 'stroke-width': 1, fill: 'none'},
{id: 'id_pad-15_mask-1', stroke: '#fff'},
{x: 2, y: 2, width: 2, height: 2},
{x: -4, y: 2, width: 2, height: 2},
{x: -4, y: -4, width: 2, height: 2},
{x: 2, y: -4, width: 2, height: 2},
{mask: 'url(#id_pad-15_mask-1)'},
{id: 'id_pad-15'},
]
expect(element).to.be.calledWith('circle', expected[0])
expect(element).to.be.calledWith('mask', expected[1], [values[0]])
expect(element).to.be.calledWith('rect', expected[2])
expect(element).to.be.calledWith('rect', expected[3])
expect(element).to.be.calledWith('rect', expected[4])
expect(element).to.be.calledWith('rect', expected[5])
expect(element).to.be.calledWith('g', expected[6], values.slice(2, 6))
expect(element).to.be.calledWith('circle', expected[7])
expect(element).to.be.calledWith('mask', expected[8], [values[7]])
expect(element).to.be.calledWith('rect', expected[9])
expect(element).to.be.calledWith('rect', expected[10])
expect(element).to.be.calledWith('rect', expected[11])
expect(element).to.be.calledWith('rect', expected[12])
expect(element).to.be.calledWith('g', expected[13], values.slice(9, 13))
expect(element).to.be.calledWith('g', expected[14], [
values[6],
values[13],
])
expect(p.defs).to.eql([values[1], values[8], values[14]])
})
it('should handle polarity changes', function() {
var toolShape = [
{type: 'rect', cx: 0, cy: 0.005, width: 0.006, height: 0.008, r: 0},
{type: 'layer', polarity: 'clear', box: [-0.003, 0.001, 0.003, 0.009]},
{type: 'rect', cx: 0, cy: 0.005, width: 0.004, height: 0.004, r: 0},
{type: 'layer', polarity: 'dark', box: [-0.003, 0.001, 0.003, 0.009]},
{type: 'rect', cx: 0, cy: -0.005, width: 0.006, height: 0.008, r: 0},
{type: 'circle', cx: 0, cy: 0, r: 0.004},
{type: 'layer', polarity: 'clear', box: [-0.004, -0.009, 0.004, 0.004]},
{type: 'rect', cx: 0, cy: -0.005, width: 0.004, height: 0.004, r: 0},
{type: 'circle', cx: 0, cy: 0, r: 0.002},
]
var expected = [
{x: -3, y: 1, width: 6, height: 8},
{mask: 'url(#id_pad-11_1)'},
{x: -2, y: 3, width: 4, height: 4},
{x: -3, y: 1, width: 6, height: 8, fill: '#fff'},
{},
{id: 'id_pad-11_1', fill: '#000', stroke: '#000'},
{x: -3, y: -9, width: 6, height: 8},
{cx: 0, cy: 0, r: 4},
{mask: 'url(#id_pad-11_3)'},
{x: -2, y: -7, width: 4, height: 4},
{cx: 0, cy: 0, r: 2},
{x: -4, y: -9, width: 8, height: 13, fill: '#fff'},
{},
{id: 'id_pad-11_3', fill: '#000', stroke: '#000'},
{id: 'id_pad-11'},
]
p.write({type: 'shape', tool: '11', shape: toolShape})
var values = element.returnValues
expect(element).to.be.calledWith('rect', expected[0])
expect(element).to.be.calledWith('g', expected[1], [values[0]])
expect(element).to.be.calledWith('rect', expected[2])
expect(element).to.be.calledWith('rect', expected[3])
expect(element).to.be.calledWith('g', expected[4], [values[3], values[2]])
expect(element).to.be.calledWith('mask', expected[5], [values[4]])
expect(element).to.be.calledWith('rect', expected[6])
expect(element).to.be.calledWith('circle', expected[7])
expect(element).to.be.calledWith('g', expected[8], [
values[1],
values[6],
values[7],
])
expect(element).to.be.calledWith('rect', expected[9])
expect(element).to.be.calledWith('circle', expected[10])
expect(element).to.be.calledWith('rect', expected[11])
expect(element).to.be.calledWith('g', expected[12], [
values[11],
values[9],
values[10],
])
expect(element).to.be.calledWith('mask', expected[13], [values[12]])
expect(element).to.be.calledWith('g', expected[14], [values[8]])
expect(p.defs).to.eql([values[5], values[13], values[14]])
})
})
it('should be able to add a pad to the layer', function() {
var pad = {type: 'pad', tool: '24', x: 0.02, y: 0.05}
p.write(pad)
expect(element).to.be.calledWith('use', {
'xlink:href': '#id_pad-24',
x: 20,
y: 50,
})
expect(p.layer).to.eql(element.returnValues)
})
describe('fills and strokes', function() {
it('should add a path to the layer for a fill', function() {
var fill = {type: 'fill', path: []}
p.write(fill)
expect(element).to.be.calledWith('path', {d: ''})
expect(p.layer).to.eql(element.returnValues)
})
it('should add a path with width and no fill for a stroke', function() {
var stroke = {type: 'stroke', path: [], width: 0.006}
p.write(stroke)
expect(element).to.be.calledWith('path', {
d: '',
fill: 'none',
'stroke-width': 6,
})
expect(p.layer).to.eql(element.returnValues)
})
it('should know how to add line segments', function() {
var path = [
{type: 'line', start: [0, 0], end: [0.1, 0]},
{type: 'line', start: [0.1, 0], end: [0.1, 0.1]},
{type: 'line', start: [0.1, 0.1], end: [0, 0.1]},
{type: 'line', start: [0, 0.1], end: [0, 0]},
]
var stroke = {type: 'stroke', width: 0.006, path: path}
var expected = {
d: 'M 0 0 100 0 100 100 0 100 0 0',
fill: 'none',
'stroke-width': 6,
}
p.write(stroke)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})
it('should know when to add movetos', function() {
var path = [
{type: 'line', start: [0, 0], end: [0.1, 0.1]},
{type: 'line', start: [0.2, 0.2], end: [0.3, 0.3]},
{type: 'line', start: [0.4, 0.4], end: [0.5, 0.5]},
]
var stroke = {type: 'stroke', width: 0.006, path: path}
var expected = {
d: 'M 0 0 100 100 M 200 200 300 300 M 400 400 500 500',
fill: 'none',
'stroke-width': 6,
}
p.write(stroke)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})
it('should know how to add arcs', function() {
var path = [
{
type: 'arc',
start: [0.1, 0, 0],
end: [0, 0.1, HALF_PI],
center: [0, 0],
sweep: HALF_PI,
radius: 0.1,
dir: 'ccw',
},
{
type: 'arc',
start: [0, 0.1, HALF_PI],
end: [0.1, 0, 0],
center: [0, 0],
sweep: 3 * HALF_PI,
radius: 0.1,
dir: 'ccw',
},
{
type: 'arc',
start: [1.1, 0, 0],
end: [1, 0.1, HALF_PI],
center: [1, 0],
sweep: 3 * HALF_PI,
radius: 0.1,
dir: 'cw',
},
{
type: 'arc',
start: [1, 0.1, HALF_PI],
end: [1.1, 0, 0],
center: [1, 0],
sweep: HALF_PI,
radius: 0.1,
dir: 'cw',
},
]
var stroke = {type: 'stroke', width: 0.006, path: path}
var expectedData = [
'M 100 0 A 100 100 0 0 1 0 100 100 100 0 1 1 100 0',
'M 1100 0 A 100 100 0 1 0 1000 100 100 100 0 0 0 1100 0',
].join(' ')
var expected = {d: expectedData, fill: 'none', 'stroke-width': 6}
p.write(stroke)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})
it('should add zero-length arcs as linetos', function() {
var path = [
{
type: 'arc',
start: [0, 0, 0],
end: [0, 0, 0],
center: [-1, 0],
sweep: 0,
radius: 1,
dir: 'ccw',
},
]
var stroke = {type: 'stroke', width: 0.006, path: path}
var expected = {d: 'M 0 0 0 0', fill: 'none', 'stroke-width': 6}
p.write(stroke)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})
it('should add full circle arcs as two arcs', function() {
var path = [
{
type: 'arc',
start: [0, 0, 0],
end: [0, 0, 0],
center: [-0.1, 0],
sweep: 2 * Math.PI,
radius: 0.1,
dir: 'ccw',
},
]
var stroke = {type: 'stroke', width: 0.006, path: path}
var expected = {
d: 'M 0 0 A 100 100 0 0 1 -200 0 100 100 0 0 1 0 0',
fill: 'none',
'stroke-width': 6,
}
p.write(stroke)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})
it('should only add explicit linetos as needed', function() {
var path = [
{
type: 'arc',
start: [0.1, 0, 0],
end: [-0.1, 0, Math.PI],
center: [0, 0],
sweep: Math.PI,
radius: 0.1,
dir: 'ccw',
},
{type: 'line', start: [-0.1, 0], end: [0, 0]},
]
var stroke = {type: 'stroke', width: 0.006, path: path}
var expected = {
d: 'M 100 0 A 100 100 0 0 1 -100 0 L 0 0',
fill: 'none',
'stroke-width': 6,
}
p.write(stroke)
expect(element).to.be.calledWith('path', expected)
expect(p.layer).to.eql(element.returnValues)
})
})
describe('polarity changes', function() {
it('should wrap the layer in a masked group when polarity becomes clear', function() {
var existing = ['<path d="M 0 0 1 0 1 1 0 1 0 0"/>']
var polarity = {type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]}
p.layer = existing.slice(0)
p.write(polarity)
expect(element).to.be.calledWith(
'g',
{mask: 'url(#id_clear-1)'},
existing
)
expect(p.layer).to.eql(element.returnValues)
})
it('should construct a mask and add to defs when polarity switches back', function() {
var clear = {type: 'polarity', polarity: 'clear', box: [0, 0, 0.5, 0.5]}
var clearPad = {type: 'pad', tool: '10', x: 0.005, y: 0.005}
var dark = {type: 'polarity', polarity: 'dark', box: [0, 0, 0.5, 0.5]}
var darkPad = {type: 'pad', tool: '11', x: 0.005, y: 0.005}
p.write(clear)
p.write(clearPad)
p.write(dark)
p.write(darkPad)
var values = element.returnValues
var expected = [
{mask: 'url(#id_clear-1)'},
{'xlink:href': '#id_pad-10', x: 5, y: 5},
{x: 0, y: 0, width: 500, height: 500, fill: '#fff'},
{},
{id: 'id_clear-1', fill: '#000', stroke: '#000'},
{'xlink:href': '#id_pad-11', x: 5, y: 5},
]
expect(element).to.be.calledWith('g', expected[0], [])
expect(element).to.be.calledWith('use', expected[1])
expect(element).to.be.calledWith('rect', expected[2])
expect(element).to.be.calledWith('g', expected[3], [values[2], values[1]])
expect(element).to.be.calledWith('mask', expected[4], [values[3]])
expect(element).to.be.calledWith('use', expected[5])
expect(p._mask).to.eql([])
expect(p.defs).to.eql([values[4]])
expect(p.layer).to.eql([values[0], values[5]])
})
it('should not do anything with dark polarity if there is no mask', function() {
var dark = {type: 'polarity', polarity: 'dark', box: [0, 0, 1, 1]}
p.write(dark)
expect(element).to.have.callCount(0)
expect(p._mask).to.eql([])
expect(p.defs).to.eql([])
expect(p.layer).to.eql([])
})
})
describe('block repeats', function() {
it('if only one layer, it should wrap the current layer and repeat it', function() {
var offsets = [
[0, 0],
[0, 1],
[1, 0],
[1, 1],
]
var expected = [
{'xlink:href': '#id_pad-10', x: 250, y: 250},
{id: 'id_block-1-1'},
{'xlink:href': '#id_block-1-1', x: 0, y: 0},
{'xlink:href': '#id_block-1-1', x: 0, y: 1000},
{'xlink:href': '#id_block-1-1', x: 1000, y: 0},
{'xlink:href': '#id_block-1-1', x: 1000, y: 1000},
]
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 0.5, 0.5]})
p.write({type: 'pad', tool: '10', x: 0.25, y: 0.25})
p.write({type: 'repeat', offsets: [], box: [0, 0, 1.5, 1.5]})
expect(element).to.be.calledWith('use', expected[0])
expect(element).to.be.calledWith('g', expected[1], [
element.returnValues[0],
])
expect(element).to.be.calledWith('use', expected[2])
expect(element).to.be.calledWith('use', expected[3])
expect(element).to.be.calledWith('use', expected[4])
expect(element).to.be.calledWith('use', expected[5])
expect(p.defs).to.eql([element.returnValues[1]])
expect(p.layer).to.eql(element.returnValues.slice(2, 6))
})
it('should allow several layers in a block', function() {
var offsets = [
[0, 0],
[0, 5],
[5, 0],
[5, 5],
]
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 0.5, 0.5]})
p.write({type: 'pad', tool: '10', x: 0.25, y: 0.25})
p.write({type: 'polarity', polarity: 'clear', box: [0, 0, 0.5, 0.5]})
p.write({type: 'pad', tool: '11', x: 0.5, y: 0.5})
p.write({type: 'polarity', polarity: 'dark', box: [0, 0, 0.75, 0.75]})
p.write({type: 'pad', tool: '12', x: 0.75, y: 0.75})
p.write({type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]})
p.write({type: 'pad', tool: '13', x: 1, y: 1})
p.write({type: 'polarity', polarity: 'dark', box: [0, 0, 1.25, 1.25]})
p.write({type: 'pad', tool: '14', x: 1.25, y: 1.25})
p.write({type: 'repeat', offsets: [], box: [0, 0, 1.5, 1.5]})
var values = element.returnValues
var expected = [
{'xlink:href': '#id_pad-10', x: 250, y: 250},
{id: 'id_block-1-1'},
{'xlink:href': '#id_pad-11', x: 500, y: 500},
{id: 'id_block-1-2'},
{'xlink:href': '#id_pad-12', x: 750, y: 750},
{id: 'id_block-1-3'},
{'xlink:href': '#id_pad-13', x: 1000, y: 1000},
{id: 'id_block-1-4'},
{'xlink:href': '#id_pad-14', x: 1250, y: 1250},
{id: 'id_block-1-5'},
{'xlink:href': '#id_block-1-1', x: 0, y: 0},
{'xlink:href': '#id_block-1-3', x: 0, y: 0},
{'xlink:href': '#id_block-1-5', x: 0, y: 0},
{'xlink:href': '#id_block-1-1', x: 0, y: 5000},
{'xlink:href': '#id_block-1-3', x: 0, y: 5000},
{'xlink:href': '#id_block-1-5', x: 0, y: 5000},
{'xlink:href': '#id_block-1-1', x: 5000, y: 0},
{'xlink:href': '#id_block-1-3', x: 5000, y: 0},
{'xlink:href': '#id_block-1-5', x: 5000, y: 0},
{'xlink:href': '#id_block-1-1', x: 5000, y: 5000},
{'xlink:href': '#id_block-1-3', x: 5000, y: 5000},
{'xlink:href': '#id_block-1-5', x: 5000, y: 5000},
{mask: 'url(#id_block-1-clear)'},
{
'xlink:href': '#id_block-1-1',
x: 0,
y: 0,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-2', x: 0, y: 0},
{
'xlink:href': '#id_block-1-3',
x: 0,
y: 0,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-4', x: 0, y: 0},
{
'xlink:href': '#id_block-1-5',
x: 0,
y: 0,
fill: '#fff',
stroke: '#fff',
},
{
'xlink:href': '#id_block-1-1',
x: 0,
y: 5000,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-2', x: 0, y: 5000},
{
'xlink:href': '#id_block-1-3',
x: 0,
y: 5000,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-4', x: 0, y: 5000},
{
'xlink:href': '#id_block-1-5',
x: 0,
y: 5000,
fill: '#fff',
stroke: '#fff',
},
{
'xlink:href': '#id_block-1-1',
x: 5000,
y: 0,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-2', x: 5000, y: 0},
{
'xlink:href': '#id_block-1-3',
x: 5000,
y: 0,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-4', x: 5000, y: 0},
{
'xlink:href': '#id_block-1-5',
x: 5000,
y: 0,
fill: '#fff',
stroke: '#fff',
},
{
'xlink:href': '#id_block-1-1',
x: 5000,
y: 5000,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-2', x: 5000, y: 5000},
{
'xlink:href': '#id_block-1-3',
x: 5000,
y: 5000,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-4', x: 5000, y: 5000},
{
'xlink:href': '#id_block-1-5',
x: 5000,
y: 5000,
fill: '#fff',
stroke: '#fff',
},
{x: 0, y: 0, width: 500, height: 500, fill: '#fff'},
{},
{id: 'id_block-1-clear', fill: '#000', stroke: '#000'},
]
expect(element).to.be.calledWith('use', expected[0])
expect(element).to.be.calledWith('g', expected[1], [values[0]])
expect(element).to.be.calledWith('use', expected[2])
expect(element).to.be.calledWith('g', expected[3], [values[2]])
expect(element).to.be.calledWith('use', expected[4])
expect(element).to.be.calledWith('g', expected[5], [values[4]])
expect(element).to.be.calledWith('use', expected[6])
expect(element).to.be.calledWith('g', expected[7], [values[6]])
expect(element).to.be.calledWith('use', expected[8])
expect(element).to.be.calledWith('g', expected[9], [values[8]])
expect(element).to.be.calledWith('use', expected[10])
expect(element).to.be.calledWith('use', expected[11])
expect(element).to.be.calledWith('use', expected[12])
expect(element).to.be.calledWith('use', expected[13])
expect(element).to.be.calledWith('use', expected[14])
expect(element).to.be.calledWith('use', expected[15])
expect(element).to.be.calledWith('use', expected[16])
expect(element).to.be.calledWith('use', expected[17])
expect(element).to.be.calledWith('use', expected[18])
expect(element).to.be.calledWith('use', expected[19])
expect(element).to.be.calledWith('use', expected[20])
expect(element).to.be.calledWith('use', expected[21])
expect(element).to.be.calledWith('g', expected[22], values.slice(10, 22))
expect(element).to.be.calledWith('use', expected[23])
expect(element).to.be.calledWith('use', expected[24])
expect(element).to.be.calledWith('use', expected[25])
expect(element).to.be.calledWith('use', expected[26])
expect(element).to.be.calledWith('use', expected[27])
expect(element).to.be.calledWith('use', expected[28])
expect(element).to.be.calledWith('use', expected[29])
expect(element).to.be.calledWith('use', expected[30])
expect(element).to.be.calledWith('use', expected[31])
expect(element).to.be.calledWith('use', expected[32])
expect(element).to.be.calledWith('use', expected[33])
expect(element).to.be.calledWith('use', expected[34])
expect(element).to.be.calledWith('use', expected[35])
expect(element).to.be.calledWith('use', expected[36])
expect(element).to.be.calledWith('use', expected[37])
expect(element).to.be.calledWith('use', expected[38])
expect(element).to.be.calledWith('use', expected[39])
expect(element).to.be.calledWith('use', expected[40])
expect(element).to.be.calledWith('use', expected[41])
expect(element).to.be.calledWith('use', expected[42])
expect(element).to.be.calledWith('rect', expected[43])
expect(element).to.be.calledWith(
'g',
expected[44],
[values[43]].concat(values.slice(23, 43))
)
expect(element).to.be.calledWith('mask', expected[45], [values[44]])
expect(p.defs).to.eql([
values[1],
values[3],
values[5],
values[7],
values[9],
values[45],
])
expect(p.layer).to.eql([values[22]])
})
it('should handle step repeats that start with clear', function() {
var offsets = [
[0, 0],
[0, 0.5],
[0.5, 0],
[0.5, 0.5],
]
p.layer = ['LAYER']
p.write({type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]})
p._mask = ['MASK']
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 1, 1]})
p.write({type: 'pad', tool: '10', x: 0.25, y: 0.25})
p.write({type: 'polarity', polarity: 'dark', box: [0, 0, 1, 1]})
p.write({type: 'pad', tool: '11', x: 0.25, y: 0.25})
p.write({type: 'repeat', offsets: [], box: [0, 0, 1.5, 1.5]})
var values = element.returnValues
var expected = [
{mask: 'url(#id_block-1-clear)'},
{x: 0, y: 0, width: 1000, height: 1000, fill: '#fff'},
{},
{id: 'id_clear-1', fill: '#000', stroke: '#000'},
{'xlink:href': '#id_pad-10', x: 250, y: 250},
{id: 'id_block-1-1'},
{'xlink:href': '#id_pad-11', x: 250, y: 250},
{id: 'id_block-1-2'},
{'xlink:href': '#id_block-1-2', x: 0, y: 0},
{'xlink:href': '#id_block-1-2', x: 0, y: 500},
{'xlink:href': '#id_block-1-2', x: 500, y: 0},
{'xlink:href': '#id_block-1-2', x: 500, y: 500},
{mask: 'url(#id_block-1-clear)'},
{'xlink:href': '#id_block-1-1', x: 0, y: 0},
{
'xlink:href': '#id_block-1-2',
x: 0,
y: 0,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-1', x: 0, y: 500},
{
'xlink:href': '#id_block-1-2',
x: 0,
y: 500,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-1', x: 500, y: 0},
{
'xlink:href': '#id_block-1-2',
x: 500,
y: 0,
fill: '#fff',
stroke: '#fff',
},
{'xlink:href': '#id_block-1-1', x: 500, y: 500},
{
'xlink:href': '#id_block-1-2',
x: 500,
y: 500,
fill: '#fff',
stroke: '#fff',
},
{x: 0, y: 0, width: 1000, height: 1000, fill: '#fff'},
{},
{id: 'id_block-1-clear', fill: '#000', stroke: '#000'},
]
expect(element).to.be.calledWith('g', expected[0])
expect(element).to.be.calledWith('rect', expected[1])
expect(element).to.be.calledWith('g', expected[2], [values[1], 'MASK'])
expect(element).to.be.calledWith('mask', expected[3], [values[2]])
expect(element).to.be.calledWith('use', expected[4])
expect(element).to.be.calledWith('g', expected[5], [values[4]])
expect(element).to.be.calledWith('use', expected[6])
expect(element).to.be.calledWith('g', expected[7], [values[6]])
expect(element).to.be.calledWith('use', expected[8])
expect(element).to.be.calledWith('use', expected[9])
expect(element).to.be.calledWith('use', expected[10])
expect(element).to.be.calledWith('use', expected[11])
expect(element).to.be.calledWith(
'g',
expected[12],
[values[0]].concat(values.slice(8, 12))
)
expect(element).to.be.calledWith('use', expected[13])
expect(element).to.be.calledWith('use', expected[14])
expect(element).to.be.calledWith('use', expected[15])
expect(element).to.be.calledWith('use', expected[16])
expect(element).to.be.calledWith('use', expected[17])
expect(element).to.be.calledWith('use', expected[18])
expect(element).to.be.calledWith('use', expected[19])
expect(element).to.be.calledWith('use', expected[20])
expect(element).to.be.calledWith('rect', expected[21])
expect(element).to.be.calledWith(
'g',
expected[22],
[values[21]].concat(values.slice(13, 21))
)
expect(element).to.be.calledWith('mask', expected[23], [values[22]])
expect(p.defs).to.eql([values[3], values[5], values[7], values[23]])
expect(p.layer).to.eql([values[12]])
})
it('should handle step repeats that start with dark then change to clear', function() {
var offsets = [
[0, 0],
[0, 0.5],
[0.5, 0],
[0.5, 0.5],
]
p.layer = ['SOME_EXISTING_STUFF']
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 1, 1]})
p.write({type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]})
p.write({type: 'pad', tool: '10', x: 0.25, y: 0.25})
p.write({type: 'repeat', offsets: [], box: [0, 0, 1.5, 1.5]})
var values = element.returnValues
var expected = [
{'xlink:href': '#id_pad-10', x: 250, y: 250},
{id: 'id_block-1-1'},
{mask: 'url(#id_block-1-clear)'},
{'xlink:href': '#id_block-1-1', x: 0, y: 0},
{'xlink:href': '#id_block-1-1', x: 0, y: 500},
{'xlink:href': '#id_block-1-1', x: 500, y: 0},
{'xlink:href': '#id_block-1-1', x: 500, y: 500},
{x: 0, y: 0, width: 1000, height: 1000, fill: '#fff'},
{},
{id: 'id_block-1-clear', fill: '#000', stroke: '#000'},
]
expect(element).to.be.calledWith('use', expected[0])
expect(element).to.be.calledWith('g', expected[1], [values[0]])
expect(element).to.be.calledWith('g', expected[2], [
'SOME_EXISTING_STUFF',
])
expect(element).to.be.calledWith('use', expected[3])
expect(element).to.be.calledWith('use', expected[4])
expect(element).to.be.calledWith('use', expected[5])
expect(element).to.be.calledWith('use', expected[6])
expect(element).to.be.calledWith('rect', expected[7])
expect(element).to.be.calledWith(
'g',
expected[8],
[values[7]].concat(values.slice(3, 7))
)
expect(element).to.be.calledWith('mask', expected[9], [values[8]])
expect(p.defs).to.eql([values[1], values[9]])
expect(p.layer).to.eql([values[2]])
})
it('should handle polarity switches with no objects gracefully', function() {
var offsets = [
[0, 0],
[0, 0.5],
[0.5, 0],
[0.5, 0.5],
]
p.layer = ['SOME_EXISTING_STUFF']
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 1, 1]})
p.write({type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]})
p.write({type: 'polarity', polarity: 'dark', box: [0, 0, 1, 1]})
p.write({type: 'polarity', polarity: 'clear', box: [0, 0, 1, 1]})
p.write({type: 'polarity', polarity: 'dark', box: [0, 0, 1, 1]})
expect(element).to.have.callCount(0)
})
it('should handle Infinities in the box', function() {
var offsets = [
[0, 0],
[0, 0.5],
[0.5, 0],
[0.5, 0.5],
]
p.layer = ['SOME_EXISTING_STUFF']
p.write({type: 'repeat', offsets: offsets, box: EMPTY_BOX})
expect(p._blockBox).to.eql([0, 0, 0, 0])
})
})
describe('end of stream', function() {
it('should create a viewbox from a size object', function() {
var size = {type: 'size', box: [-1, -1, 1, 2], units: 'mm'}
p.write(size)
expect(p.viewBox).to.eql([-1000, -1000, 2000, 3000])
expect(p.width).to.equal(2)
expect(p.height).to.equal(3)
expect(p.units).to.equal('mm')
})
it('should contruct an svg from the layer and defs', function(done) {
var size = {type: 'size', box: [-1, -1, 1, 2], units: 'mm'}
var viewBox = '-1000 -1000 2000 3000'
var transform = 'translate(0,1000) scale(1,-1)'
var svgAttr = assign({}, SVG_ATTR, {
width: '2mm',
height: '3mm',
viewBox: viewBox,
})
var layerAttr = {
transform: transform,
fill: 'currentColor',
stroke: 'currentColor',
}
p.on('data', function(result) {
expect(element).to.be.calledWith('defs', {}, ['THESE_ARE_THE_DEFS'])
expect(element).to.be.calledWith('g', layerAttr, ['THIS_IS_THE_LAYER'])
expect(element).to.be.calledWith(
'svg',
svgAttr,
element.returnValues.slice(0, 2)
)
expect(result).to.equal(element.returnValues[2])
done()
})
p.defs = ['THESE_ARE_THE_DEFS']
p.layer = ['THIS_IS_THE_LAYER']
p.write(size)
p.end()
})
it('should omit the defs mode if it is empty', function(done) {
var size = {type: 'size', box: [-1, -1, 1, 2], units: 'mm'}
p.on('data', function() {
expect(element).not.to.be.calledWith('defs')
done()
})
p.defs = []
p.layer = ['THIS_IS_THE_LAYER']
p.write(size)
p.end()
})
it('should finish any in-progress mask', function() {
p._maskId = 'id_clear-1'
p._maskBox = [1, 2, 3, 4]
p._mask = ['SOME STUFF']
p.end()
expect(element).to.be.calledWith('mask', {
id: 'id_clear-1',
fill: '#000',
stroke: '#000',
})
})
it('should finish any in-progress repeat', function() {
var offsets = [
[0, 0],
[0, 1],
[1, 0],
[1, 1],
]
p.write({type: 'repeat', offsets: offsets, box: [0, 0, 0.5, 0.5]})
p.write({type: 'pad', tool: '10', x: 0.25, y: 0.25})
p.end()
expect(element).to.be.calledWith('use', {
'xlink:href': '#id_block-1-1',
x: 0,
y: 0,
})
expect(element).to.be.calledWith('use', {
'xlink:href': '#id_block-1-1',
x: 0,
y: 1000,
})
expect(element).to.be.calledWith('use', {
'xlink:href': '#id_block-1-1',
x: 1000,
y: 0,
})
expect(element).to.be.calledWith('use', {
'xlink:href': '#id_block-1-1',
x: 1000,
y: 1000,
})
})
})
})