2015-08-28 11:34:49 +02:00
|
|
|
// library for creating CuVoodoo Land Pattern (cvlp) JSON files and render them
|
|
|
|
|
|
|
|
// create a set of elements with an line element
|
2015-09-05 10:20:23 +02:00
|
|
|
function cvlp_line(x1, y1, x2, y2, thickness) {
|
2015-08-28 11:34:49 +02:00
|
|
|
var line = {}
|
|
|
|
line.type = "line"
|
|
|
|
line.x1 = x1
|
|
|
|
line.y1 = y1
|
|
|
|
line.x2 = x2
|
|
|
|
line.y2 = y2
|
|
|
|
line.thickness = thickness
|
|
|
|
return [line]
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a set of elements with an arc element
|
|
|
|
// arc goes angle degrees clockwise from start
|
2015-09-05 10:20:23 +02:00
|
|
|
function cvlp_arc(x, y, radius, start, angle, thickness) {
|
2015-08-28 11:34:49 +02:00
|
|
|
var arc = {}
|
|
|
|
arc.type = "arc"
|
|
|
|
arc.x = x
|
|
|
|
arc.y = y
|
|
|
|
arc.radius = radius
|
|
|
|
arc.start = start
|
|
|
|
arc.angle = angle
|
|
|
|
arc.thickness = thickness
|
|
|
|
return [arc]
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a set of elements with an pad element
|
2015-09-05 10:37:43 +02:00
|
|
|
function cvlp_pad(number, x, y, width, height, clearance, soldermask, round=false) {
|
2015-08-28 11:34:49 +02:00
|
|
|
var pad = {}
|
|
|
|
pad.type = "pad"
|
|
|
|
pad.number = number
|
2015-09-02 10:03:14 +02:00
|
|
|
pad.x = x
|
|
|
|
pad.y = y
|
|
|
|
pad.width = width
|
|
|
|
pad.height = height
|
2015-08-28 11:34:49 +02:00
|
|
|
pad.clearance = clearance
|
|
|
|
pad.soldermask = soldermask
|
|
|
|
pad.round = round
|
|
|
|
return [pad]
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a set of elements with an pin element
|
2015-09-05 10:37:43 +02:00
|
|
|
function cvlp_pin(number, x, y, thickness, drill, clearance, soldermask, round=false) {
|
2015-08-28 11:34:49 +02:00
|
|
|
var pin = {}
|
|
|
|
pin.type = "pin"
|
|
|
|
pin.number = number
|
|
|
|
pin.x = x
|
|
|
|
pin.y = y
|
|
|
|
pin.thickness = thickness
|
|
|
|
pin.drill = drill
|
|
|
|
pin.clearance = clearance
|
|
|
|
pin.soldermask = soldermask
|
|
|
|
pin.round = round
|
|
|
|
return [pin]
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a set of elements representing a chain of lines
|
|
|
|
// provide a list of [x,y] coordinates
|
|
|
|
function cvlp_polyline(points, thickness, closed=false) {
|
|
|
|
var polyline = []
|
|
|
|
for (var i=0; i<points.length-1; i++) {
|
|
|
|
polyline = polyline.concat(cvlp_line(points[i][0], points[i][1], points[i+1][0], points[i+1][1], thickness))
|
|
|
|
}
|
|
|
|
if (closed) {
|
|
|
|
if (points[0][0]!=points[points.length-1][0] && points[0][1]!=points[points.length-1][1]) {
|
|
|
|
polyline = polyline.concat(cvlp_line(points[points.length-1][0], points[points.length-1][1], points[0][0], points[0][1], thickness))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return polyline
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a set of elements representing a rectangle
|
2015-09-02 22:22:11 +02:00
|
|
|
// provide top corner, size, an radius of corner
|
2015-09-05 10:20:23 +02:00
|
|
|
function cvlp_rectangle(x, y, width, height, thickness, radius=0) {
|
2015-08-28 11:34:49 +02:00
|
|
|
var rectangle = []
|
2015-09-05 10:20:23 +02:00
|
|
|
rectangle = rectangle.concat(cvlp_line(x+radius, y, x+width-radius, y, thickness))
|
|
|
|
rectangle = rectangle.concat(cvlp_line(x+width, y+radius, x+width, y+height-radius, thickness))
|
|
|
|
rectangle = rectangle.concat(cvlp_line(x+width-radius, y+height, x+radius, y+height, thickness))
|
|
|
|
rectangle = rectangle.concat(cvlp_line(x, y+height-radius, x, y+radius, thickness))
|
2015-08-28 11:34:49 +02:00
|
|
|
if (radius!=0) {
|
2015-09-05 10:20:23 +02:00
|
|
|
rectangle = rectangle.concat(cvlp_arc(x+radius, y+radius, radius, 180, 90, thickness))
|
|
|
|
rectangle = rectangle.concat(cvlp_arc(x+width-radius, y+radius, radius, 270, 90, thickness))
|
|
|
|
rectangle = rectangle.concat(cvlp_arc(x+width-radius, y+width-radius, radius, 0, 90, thickness))
|
|
|
|
rectangle = rectangle.concat(cvlp_arc(x+radius, y+width-radius, radius, 90, 90, thickness))
|
2015-08-28 11:34:49 +02:00
|
|
|
}
|
|
|
|
return rectangle
|
|
|
|
}
|
|
|
|
|
2015-08-30 10:15:31 +02:00
|
|
|
// convert value from one unit to another
|
|
|
|
// units allowed: mm, in, mil
|
|
|
|
// returns converted value, on null if unknown unit
|
|
|
|
function convert_unit(value, from, to) {
|
|
|
|
// don't convert if the unit is the same (avoid calculation imprecisions)
|
|
|
|
if (from==to) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
// convert to µm
|
|
|
|
var mum = 0;
|
|
|
|
switch (from) {
|
|
|
|
case 'mm':
|
|
|
|
mum = value*1000;
|
|
|
|
break;
|
|
|
|
case 'in':
|
|
|
|
mum = value*1000*25.40;
|
|
|
|
break;
|
|
|
|
case 'mil':
|
|
|
|
mum = value*25.40;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
switch (to) {
|
|
|
|
case 'mm':
|
|
|
|
mum = mum/1000;
|
|
|
|
break;
|
|
|
|
case 'in':
|
|
|
|
mum = mum/1000/25.40;
|
|
|
|
break;
|
|
|
|
case 'mil':
|
|
|
|
mum = mum/25.40;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return mum;
|
|
|
|
}
|
|
|
|
|
2015-08-28 11:34:49 +02:00
|
|
|
// convert JSON to SVG
|
|
|
|
function json2svg(json) {
|
|
|
|
// create svg
|
|
|
|
var svgNS = "http://www.w3.org/2000/svg"
|
|
|
|
svg = document.createElementNS(svgNS,'svg')
|
|
|
|
svg.setAttribute('xmlns',svgNS)
|
|
|
|
svg.setAttribute('version',1.1)
|
|
|
|
svg.setAttribute('xmlns:inkscape','http://www.inkscape.org/namespaces/inkscape')
|
2015-08-28 12:20:11 +02:00
|
|
|
// set unit
|
|
|
|
if (json.unit=="mil") { // mil units are not supported in SVG, convert to inch
|
|
|
|
svg.setAttribute('height',(json.height/1000)+'in')
|
|
|
|
svg.setAttribute('width',(json.width/1000)+'in')
|
|
|
|
} else {
|
|
|
|
svg.setAttribute('height',json.height+''+json.unit)
|
|
|
|
svg.setAttribute('width',json.width+''+json.unit)
|
|
|
|
}
|
|
|
|
svg.setAttribute('viewBox','0 0 '+json.width+' '+json.height)
|
2015-08-28 11:34:49 +02:00
|
|
|
// add info
|
|
|
|
var svg_title = document.createElementNS(svgNS,'title');
|
|
|
|
svg_title.textContent = json.name;
|
|
|
|
svg.appendChild(svg_title);
|
|
|
|
// add copper and silkscreen layers
|
|
|
|
layers_desc = [ { name: 'copper', stroke: 'black', fill: 'black', display: 'inline'},
|
|
|
|
{ name: 'drill', stroke: 'none', fill: 'white', display: 'inline'},
|
|
|
|
{ name: 'clearance', stroke: 'red', fill: 'none', display: 'none'},
|
|
|
|
{ name: 'soldermask', stroke: 'green', fill: 'none', display: 'none'},
|
|
|
|
{ name: 'silkscreen', stroke: 'gray', fill: 'none', display: 'inline'}
|
|
|
|
]
|
|
|
|
layers = []
|
|
|
|
for (var layer_i in layers_desc) {
|
|
|
|
var layer_desc = layers_desc[layer_i]
|
|
|
|
var layer = document.createElementNS(svgNS,'g')
|
|
|
|
layer.setAttribute('name',layer_desc.name)
|
|
|
|
layer.setAttribute('inkscape:groupmode','layer')
|
|
|
|
layer.setAttribute('inkscape:label',layer_desc.name)
|
|
|
|
layer.setAttribute('display',layer_desc.display)
|
|
|
|
layer.style.setProperty('stroke',layer_desc.stroke)
|
|
|
|
layer.style.setProperty('fill',layer_desc.fill)
|
|
|
|
svg.appendChild(layer)
|
|
|
|
layers[layer_desc.name] = layer
|
|
|
|
}
|
|
|
|
for (var element_i in json.elements) {
|
|
|
|
var element = json.elements[element_i]
|
|
|
|
switch (element.type) {
|
|
|
|
case 'line':
|
|
|
|
var line = document.createElementNS(svgNS,'line')
|
|
|
|
line.setAttribute('x1',element.x1)
|
|
|
|
line.setAttribute('y1',element.y1)
|
|
|
|
line.setAttribute('x2',element.x2)
|
|
|
|
line.setAttribute('y2',element.y2)
|
2015-09-05 10:20:23 +02:00
|
|
|
line.style.setProperty('stroke-width',element.thickness)
|
|
|
|
line.style.setProperty('stroke-linecap','round')
|
2015-08-28 11:34:49 +02:00
|
|
|
layers['silkscreen'].appendChild(line)
|
|
|
|
break
|
|
|
|
case 'arc':
|
2015-09-03 00:05:54 +02:00
|
|
|
element.start = (element.start+360) % 360
|
|
|
|
element.stop = (element.stop+360) % 360
|
2015-08-28 11:34:49 +02:00
|
|
|
var arc = null
|
|
|
|
if (element.start==element.stop) {
|
|
|
|
arc = document.createElementNS(svgNS,'circle')
|
|
|
|
arc.setAttribute('cx',element.x)
|
|
|
|
arc.setAttribute('cy',element.y)
|
|
|
|
arc.setAttribute('r',element.radius)
|
|
|
|
} else {
|
2015-09-03 11:32:29 +02:00
|
|
|
var x1 = element.x+element.radius*Math.cos(((-1*element.start)%360)/360.0*Math.PI*2)
|
|
|
|
var y1 = element.y-element.radius*Math.sin(((-1*element.start)%360)/360.0*Math.PI*2)
|
|
|
|
var x2 = element.x+element.radius*Math.cos(((-1*(element.start+element.angle))%360)/360.0*Math.PI*2)
|
|
|
|
var y2 = element.y-element.radius*Math.sin(((-1*(element.start+element.angle))%360)/360.0*Math.PI*2)
|
2015-08-28 11:34:49 +02:00
|
|
|
arc = document.createElementNS(svgNS,'path')
|
2015-09-03 11:32:29 +02:00
|
|
|
arc.setAttribute('d','M '+x1+' '+y1+' A '+element.radius+' '+element.radius+' 0 '+((element.angle)%360>180 ? '1' : '0')+' 1 '+x2+' '+y2+' ')
|
2015-08-28 11:34:49 +02:00
|
|
|
}
|
|
|
|
arc.style.setProperty('stroke-width',element.thickness)
|
2015-09-05 10:20:23 +02:00
|
|
|
arc.style.setProperty('stroke-linecap','round')
|
2015-08-28 11:34:49 +02:00
|
|
|
layers['silkscreen'].appendChild(arc)
|
|
|
|
break
|
|
|
|
case 'pad':
|
2015-09-02 22:07:23 +02:00
|
|
|
var copper = document.createElementNS(svgNS,'rect')
|
|
|
|
copper.style.setProperty('stroke-width',0)
|
|
|
|
copper.setAttribute('x',element.x-element.width/2)
|
|
|
|
copper.setAttribute('y',element.y-element.height/2)
|
|
|
|
copper.setAttribute('width',element.width)
|
|
|
|
copper.setAttribute('height',element.height)
|
2015-08-28 11:34:49 +02:00
|
|
|
if (element.round) {
|
2015-09-02 22:07:23 +02:00
|
|
|
copper.setAttribute('rx',Math.min(element.width,element.height)/2)
|
|
|
|
copper.setAttribute('ry',Math.min(element.width,element.height)/2)
|
2015-08-28 11:34:49 +02:00
|
|
|
} else {
|
2015-09-02 22:07:23 +02:00
|
|
|
copper.setAttribute('rx',0)
|
|
|
|
copper.setAttribute('ry',0)
|
2015-08-28 11:34:49 +02:00
|
|
|
}
|
|
|
|
layers['copper'].appendChild(copper)
|
2015-09-02 22:07:23 +02:00
|
|
|
var clearance = document.createElementNS(svgNS,'rect')
|
|
|
|
clearance.style.setProperty('stroke-width',0)
|
|
|
|
clearance.setAttribute('x',element.x-element.width/2-element.clearance)
|
|
|
|
clearance.setAttribute('y',element.y-element.height/2-element.clearance)
|
|
|
|
clearance.setAttribute('width',element.width+element.clearance*2)
|
|
|
|
clearance.setAttribute('height',element.height+element.clearance*2)
|
2015-08-28 11:34:49 +02:00
|
|
|
if (element.round) {
|
2015-09-02 22:07:23 +02:00
|
|
|
clearance.setAttribute('rx',Math.min(element.width,element.height)/2+element.clearance)
|
|
|
|
clearance.setAttribute('ry',Math.min(element.width,element.height)/2+element.clearance)
|
2015-08-28 11:34:49 +02:00
|
|
|
} else {
|
2015-09-02 22:07:23 +02:00
|
|
|
clearance.setAttribute('rx',0)
|
|
|
|
clearance.setAttribute('ry',0)
|
2015-08-28 11:34:49 +02:00
|
|
|
}
|
|
|
|
layers['clearance'].appendChild(clearance)
|
2015-09-02 22:07:23 +02:00
|
|
|
var soldermask = document.createElementNS(svgNS,'rect')
|
|
|
|
soldermask.style.setProperty('stroke-width',0)
|
|
|
|
soldermask.setAttribute('x',element.x-element.width/2-element.soldermask/2)
|
|
|
|
soldermask.setAttribute('y',element.y-element.height/2-element.soldermask/2)
|
|
|
|
soldermask.setAttribute('width',element.width+element.soldermask*2)
|
|
|
|
soldermask.setAttribute('height',element.height+element.soldermask*2)
|
2015-08-28 11:34:49 +02:00
|
|
|
if (element.round) {
|
2015-09-02 22:07:23 +02:00
|
|
|
soldermask.setAttribute('rx',Math.min(element.width,element.height)/2+element.soldermask)
|
|
|
|
soldermask.setAttribute('ry',Math.min(element.width,element.height)/2+element.soldermask)
|
2015-08-28 11:34:49 +02:00
|
|
|
} else {
|
2015-09-02 22:07:23 +02:00
|
|
|
soldermask.setAttribute('rx',0)
|
|
|
|
soldermask.setAttribute('ry',0)
|
2015-08-28 11:34:49 +02:00
|
|
|
}
|
|
|
|
layers['soldermask'].appendChild(soldermask)
|
|
|
|
break
|
|
|
|
case 'pin':
|
2015-09-02 22:07:23 +02:00
|
|
|
break
|
2015-08-28 11:34:49 +02:00
|
|
|
var copper = document.createElementNS(svgNS,'rect')
|
|
|
|
copper.style.setProperty('stroke-width',0)
|
|
|
|
copper.setAttribute('x',element.x-element.thickness/2)
|
|
|
|
copper.setAttribute('y',element.y-element.thickness/2)
|
|
|
|
copper.setAttribute('height',element.thickness)
|
|
|
|
copper.setAttribute('width',element.thickness)
|
|
|
|
if (element.round) {
|
|
|
|
copper.setAttribute('rx',element.thickness/2)
|
|
|
|
copper.setAttribute('ry',element.thickness/2)
|
|
|
|
} else {
|
|
|
|
copper.setAttribute('rx',0)
|
|
|
|
copper.setAttribute('ry',0)
|
|
|
|
}
|
|
|
|
layers['copper'].appendChild(copper)
|
2015-09-03 11:32:29 +02:00
|
|
|
var drill = document.createElementNS(svgNS,'circle')
|
2015-08-28 11:34:49 +02:00
|
|
|
drill.style.setProperty('stroke-width',0)
|
2015-09-03 11:32:29 +02:00
|
|
|
drill.setAttribute('cx',element.x)
|
|
|
|
drill.setAttribute('cy',element.y)
|
|
|
|
drill.setAttribute('r',element.drill/2)
|
2015-08-28 11:34:49 +02:00
|
|
|
layers['drill'].appendChild(drill)
|
|
|
|
var clearance = document.createElementNS(svgNS,'rect')
|
|
|
|
clearance.style.setProperty('stroke-width',0)
|
|
|
|
clearance.setAttribute('x',element.x-element.thickness/2-element.clearance)
|
|
|
|
clearance.setAttribute('y',element.y-element.thickness/2-element.clearance)
|
|
|
|
clearance.setAttribute('height',element.thickness+element.clearance*2)
|
|
|
|
clearance.setAttribute('width',element.thickness+element.clearance*2)
|
|
|
|
if (element.round) {
|
|
|
|
clearance.setAttribute('rx',element.thickness/2+element.clearance)
|
|
|
|
clearance.setAttribute('ry',element.thickness/2+element.clearance)
|
|
|
|
} else {
|
|
|
|
clearance.setAttribute('rx',0)
|
|
|
|
clearance.setAttribute('ry',0)
|
|
|
|
}
|
|
|
|
layers['clearance'].appendChild(clearance)
|
|
|
|
var soldermask = document.createElementNS(svgNS,'rect')
|
|
|
|
soldermask.style.setProperty('stroke-width',0)
|
|
|
|
soldermask.setAttribute('x',element.x-element.soldermask/2)
|
|
|
|
soldermask.setAttribute('y',element.y-element.soldermask/2)
|
|
|
|
soldermask.setAttribute('height',element.soldermask)
|
|
|
|
soldermask.setAttribute('width',element.soldermask)
|
|
|
|
if (element.round) {
|
2015-09-03 11:32:29 +02:00
|
|
|
soldermask.setAttribute('rx',element.thickness/2+element.soldermask)
|
|
|
|
soldermask.setAttribute('ry',element.thickness/2+element.soldermask)
|
2015-08-28 11:34:49 +02:00
|
|
|
} else {
|
|
|
|
soldermask.setAttribute('rx',0)
|
|
|
|
soldermask.setAttribute('ry',0)
|
|
|
|
}
|
|
|
|
layers['soldermask'].appendChild(soldermask)
|
|
|
|
break
|
|
|
|
default:
|
2015-09-03 11:32:29 +02:00
|
|
|
console.log('unknown element type: '+element.type)
|
2015-08-28 11:34:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return svg
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert CuVoodoo Land Pattern JSON to gEDA pcb footprint
|
|
|
|
function json2pcb(json) {
|
|
|
|
// create file and add info
|
|
|
|
var pcb = ''
|
|
|
|
pcb += '# footprint generated from CuVoodoo Land Pattern\n'
|
|
|
|
pcb += '# author: '+json.author+'\n'
|
|
|
|
pcb += '# version: '+json.version+'\n'
|
|
|
|
pcb += '# date: '+json.date+'\n'
|
|
|
|
pcb += 'Element["" "'+json.name+'" "" "" 0 0 0 0 0 100 ""]\n'
|
|
|
|
pcb += '(\n'
|
|
|
|
// add element parts
|
|
|
|
for (var element_i in json.elements) {
|
|
|
|
var element = json.elements[element_i]
|
|
|
|
switch (element.type) {
|
|
|
|
case 'line':
|
|
|
|
pcb += 'ElementLine['+element.x1+''+json.unit+' '+element.y1+''+json.unit+' '+element.x2+''+json.unit+' '+element.y2+''+json.unit+' '+element.thickness+''+json.unit+']\n'
|
|
|
|
break
|
|
|
|
case 'arc':
|
|
|
|
pcb += 'ElementArc['+element.x+''+json.unit+' '+element.y+''+json.unit+' '+element.radius+''+json.unit+' '+element.radius+''+json.unit+' '+((((180-element.start)%360)+360)%360)+' '+(-1*(element.angle))+' '+element.thickness+''+json.unit+']\n'
|
|
|
|
break
|
|
|
|
case 'pad':
|
2015-09-02 23:17:09 +02:00
|
|
|
var x1, y1, x2, y2, thickness
|
|
|
|
if (element.width>element.height) {
|
|
|
|
x1 = element.x-element.width/2+element.height/2
|
|
|
|
y1 = element.y
|
|
|
|
x2 = element.x+element.width/2-element.height/2
|
|
|
|
y2 = element.y
|
|
|
|
thickness = element.height
|
|
|
|
} else {
|
|
|
|
x1 = element.x
|
|
|
|
y1 = element.y-element.height/2+element.width/2
|
|
|
|
x2 = element.x
|
|
|
|
y2 = element.y+element.height/2-element.width/2
|
|
|
|
thickness = element.width
|
|
|
|
}
|
|
|
|
pcb += 'Pad['+x1+''+json.unit+' '+y1+''+json.unit+' '+x2+''+json.unit+' '+y2+''+json.unit+' '+thickness+''+json.unit+' '+element.clearance*2+''+json.unit+' '+Math.max(thickness+element.soldermask,0)+''+json.unit+' "" "'+element.number+'" "'
|
2015-08-28 11:34:49 +02:00
|
|
|
if (!element.round) {
|
|
|
|
pcb += 'square'
|
|
|
|
}
|
|
|
|
pcb += '"]\n'
|
|
|
|
break
|
|
|
|
case 'pin':
|
2015-09-03 11:32:29 +02:00
|
|
|
pcb += 'Pin['+element.x+''+json.unit+' '+element.y+''+json.unit+' '+element.thickness+''+json.unit+' '+element.clearance*2+''+json.unit+' '+Math.max(thickness+element.soldermask,0)+''+json.unit+' '+element.drill+''+json.unit+' "" "'+element.number+'" "'
|
2015-08-28 11:34:49 +02:00
|
|
|
if (!element.round) {
|
|
|
|
pcb += 'square'
|
|
|
|
}
|
|
|
|
pcb += '"]\n'
|
|
|
|
break
|
|
|
|
default:
|
2015-09-03 11:32:29 +02:00
|
|
|
console.log('unknown element type: '+element.type)
|
2015-08-28 11:34:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pcb += ')\n'
|
|
|
|
return pcb
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert CuVoodoo Land Pattern JSON to KiCad s-expression footprint module/file
|
|
|
|
function json2kicad(json) {
|
|
|
|
// create file and add info
|
|
|
|
var kicad = ''
|
|
|
|
kicad += '# footprint generated from CuVoodoo Land Pattern\n'
|
|
|
|
kicad += '# author: '+json.author+'\n'
|
|
|
|
kicad += '# version: '+json.version+'\n'
|
|
|
|
kicad += '# date: '+json.date+'\n'
|
|
|
|
kicad += '(module "'+json.name+'" (layer F.Cu)\n'
|
|
|
|
// add element parts
|
|
|
|
for (var element_i in json.elements) {
|
|
|
|
var element = json.elements[element_i]
|
|
|
|
switch (element.type) {
|
|
|
|
case 'line':
|
2015-08-30 19:36:25 +02:00
|
|
|
kicad += '(fp_line (start '+convert_unit(element.x1,json.unit,'mm')+' '+convert_unit(element.y1,json.unit,'mm')+') (end '+convert_unit(element.x2,json.unit,'mm')+' '+convert_unit(element.y2,json.unit,'mm')+') (layer F.SilkS) (width '+convert_unit(element.thickness,json.unit,'mm')+'))\n'
|
2015-08-28 11:34:49 +02:00
|
|
|
break
|
|
|
|
case 'arc':
|
2015-08-30 19:36:25 +02:00
|
|
|
kicad += '(fp_arc (start '+convert_unit(element.x,json.unit,'mm')+' '+convert_unit(element.y,json.unit,'mm')+') (end '+convert_unit((element.x+element.radius*Math.cos(((-1*element.start)%360)/360.0*Math.PI*2)),json.unit,'mm')+' '+convert_unit((element.y-element.radius*Math.sin(((-1*element.start)%360)/360.0*Math.PI*2)),json.unit,'mm')+') (angle '+element.angle+') (layer F.SilkS) (width '+convert_unit(element.thickness,json.unit,'mm')+'))\n'
|
2015-08-28 11:34:49 +02:00
|
|
|
break
|
|
|
|
case 'pad':
|
|
|
|
kicad += '(pad "'+element.number+'" smd '
|
|
|
|
if (element.round) {
|
|
|
|
kicad += 'oval'
|
|
|
|
} else {
|
|
|
|
kicad += 'rect'
|
|
|
|
}
|
2015-09-02 23:33:23 +02:00
|
|
|
kicad += ' (at '+convert_unit(element.x,json.unit,'mm')+' '+convert_unit(element.y,json.unit,'mm')+')'
|
|
|
|
kicad += ' (size '+convert_unit(element.width,json.unit,'mm')+' '+convert_unit(element.height,json.unit,'mm')+')'
|
2015-08-28 11:34:49 +02:00
|
|
|
kicad += ' (layers F.Cu F.Paste F.Mask)'
|
2015-08-30 19:36:25 +02:00
|
|
|
kicad += ' (clearance '+convert_unit(element.clearance,json.unit,'mm')+')'
|
2015-09-02 23:33:23 +02:00
|
|
|
kicad += ' (solder_mask_margin '+convert_unit(element.soldermask,json.unit,'mm')+')'
|
2015-08-28 11:34:49 +02:00
|
|
|
kicad += ')\n'
|
|
|
|
break
|
|
|
|
case 'pin':
|
2015-08-30 10:34:35 +02:00
|
|
|
var x = (element.x1+element.x2)/2
|
|
|
|
var y = (element.y1+element.y2)/2
|
|
|
|
var length = Math.sqrt(Math.pow(element.x2-element.x1,2)+Math.pow(element.y2-element.y1,2))
|
|
|
|
var angle = Math.atan2(element.y2-element.y1,element.x2-element.x1)*180/Math.PI;
|
|
|
|
kicad += '(pad "'+element.number+'" smd '
|
|
|
|
if (element.round) {
|
|
|
|
kicad += 'oval'
|
|
|
|
} else {
|
|
|
|
kicad += 'rect'
|
|
|
|
}
|
2015-08-30 19:36:25 +02:00
|
|
|
kicad += ' (at '+convert_unit(x,json.unit,'mm')+' '+convert_unit(y,json.unit,'mm')+' '+angle+')'
|
|
|
|
kicad += ' (size '+convert_unit(length,json.unit,'mm')+' '+convert_unit(element.thickness,json.unit,'mm')+')'
|
|
|
|
kicad += ' (drill '+convert_unit(element.drill,json.unit,'mm')
|
2015-08-30 10:34:35 +02:00
|
|
|
kicad += ' (layers F.Cu F.Paste F.Mask)'
|
2015-08-30 19:36:25 +02:00
|
|
|
kicad += ' (clearance '+convert_unit(element.clearance,json.unit,'mm')+')'
|
2015-09-03 11:32:29 +02:00
|
|
|
kicad += ' (solder_mask_margin '+convert_unit(element.soldermask,json.unit,'mm')+')'
|
2015-08-30 10:34:35 +02:00
|
|
|
kicad += ')\n'
|
2015-08-28 11:34:49 +02:00
|
|
|
break
|
|
|
|
default:
|
2015-09-03 11:32:29 +02:00
|
|
|
console.log('unknown element type: '+element.type)
|
2015-08-28 11:34:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
kicad += ')\n'
|
|
|
|
return kicad
|
|
|
|
}
|
2015-08-30 19:36:25 +02:00
|
|
|
|
|
|
|
// convert JSON to eagle library
|
|
|
|
function json2eagle(json) {
|
|
|
|
// create eagle xml library
|
|
|
|
var docType = document.implementation.createDocumentType('eagle', 'SYSTEM', 'eagle.dtd');
|
|
|
|
var xml = document.implementation.createDocument('', 'eagle', docType);
|
|
|
|
xml.documentElement.setAttribute('version','6.0')
|
2015-09-05 09:24:53 +02:00
|
|
|
var comment = xml.createComment('footprint generated from CuVoodoo Land Pattern, author: '+json.author+', version: '+json.version+', date: '+json.date)
|
|
|
|
xml.documentElement.appendChild(comment)
|
2015-08-30 19:36:25 +02:00
|
|
|
var drawing = xml.createElement('drawing')
|
|
|
|
xml.documentElement.appendChild(drawing)
|
|
|
|
// add layers
|
|
|
|
var layers = xml.createElement('layers')
|
|
|
|
drawing.appendChild(layers)
|
|
|
|
layers_desc = [ { number: 1, name: 'Top', color: 4, fill: 1, visible: 'yes', active: 'yes'},
|
2015-09-03 11:32:29 +02:00
|
|
|
{ number: 17, name: 'Pads', color: 2, fill: 1, visible: 'yes', active: 'yes'},
|
2015-08-30 19:36:25 +02:00
|
|
|
{ number: 21, name: 'tPLace', color: 7, fill: 1, visible: 'yes', active: 'yes'},
|
|
|
|
{ number: 29, name: 'tStop', color: 7, fill: 3, visible: 'no', active: 'yes'},
|
|
|
|
{ number: 39, name: 'tKeepout', color: 4, fill: 11, visible: 'no', active: 'yes'},
|
|
|
|
]
|
|
|
|
for (layer_i in layers_desc) {
|
|
|
|
var layer_desc = layers_desc[layer_i]
|
|
|
|
var layer = xml.createElement('layer')
|
|
|
|
for (key in layer_desc) {
|
|
|
|
layer.setAttribute(key,layer_desc[key])
|
|
|
|
}
|
|
|
|
layers.appendChild(layer)
|
|
|
|
}
|
|
|
|
// create library
|
|
|
|
var library = xml.createElement('library')
|
|
|
|
drawing.appendChild(library)
|
|
|
|
// add description
|
|
|
|
var description = xml.createElement('description')
|
2015-09-05 09:24:53 +02:00
|
|
|
description.textContent = json.name
|
2015-08-30 19:36:25 +02:00
|
|
|
library.appendChild(description)
|
|
|
|
// add package
|
|
|
|
var packages = xml.createElement('packages')
|
|
|
|
library.appendChild(packages)
|
|
|
|
var packag = xml.createElement('package')
|
2015-09-05 09:24:53 +02:00
|
|
|
packag.setAttribute('name','OSHW')
|
2015-08-30 19:36:25 +02:00
|
|
|
packages.appendChild(packag)
|
|
|
|
var desc = xml.createElement('description')
|
2015-09-05 09:24:53 +02:00
|
|
|
desc.textContent = json.name
|
2015-08-30 19:36:25 +02:00
|
|
|
packag.appendChild(desc)
|
|
|
|
// add element parts
|
|
|
|
// note: in eagle the origin is the bottom left corner (negate y to be compatible with the json coordinate system)
|
|
|
|
for (var element_i in json.elements) {
|
|
|
|
var element = json.elements[element_i]
|
|
|
|
switch (element.type) {
|
|
|
|
case 'line':
|
2015-09-05 10:20:23 +02:00
|
|
|
var line = xml.createElement('wire')
|
|
|
|
line.setAttribute('x1',convert_unit(element.x1,json.unit,'mm'))
|
|
|
|
line.setAttribute('y1',convert_unit(-1*element.y1,json.unit,'mm'))
|
|
|
|
line.setAttribute('x2',convert_unit(element.x2,json.unit,'mm'))
|
|
|
|
line.setAttribute('y2',convert_unit(-1*element.y2,json.unit,'mm'))
|
|
|
|
line.setAttribute('width',convert_unit(element.thickness,json.unit,'mm'))
|
|
|
|
line.setAttribute('layer','21')
|
|
|
|
packag.appendChild(line)
|
2015-08-30 19:36:25 +02:00
|
|
|
break
|
|
|
|
case 'arc':
|
|
|
|
var wire = xml.createElement('wire')
|
|
|
|
wire.setAttribute('x1',convert_unit(element.x+element.radius*Math.cos(((-1*element.start)%360)/360.0*Math.PI*2),json.unit,'mm'))
|
|
|
|
wire.setAttribute('y1',convert_unit(-1*element.y+element.radius*Math.sin(((-1*element.start)%360)/360.0*Math.PI*2),json.unit,'mm'))
|
|
|
|
wire.setAttribute('x2',convert_unit(element.x+element.radius*Math.cos(((-1*(element.start+element.angle))%360)/360.0*Math.PI*2),json.unit,'mm'))
|
|
|
|
wire.setAttribute('y2',convert_unit(-1*element.y+element.radius*Math.sin(((-1*(element.start+element.angle))%360)/360.0*Math.PI*2),json.unit,'mm'))
|
|
|
|
wire.setAttribute('curve',-1*element.angle)
|
|
|
|
wire.setAttribute('width',convert_unit(element.thickness,json.unit,'mm'))
|
|
|
|
wire.setAttribute('layer','21')
|
2015-09-05 10:20:23 +02:00
|
|
|
wire.setAttribute('cap','round')
|
2015-08-30 19:36:25 +02:00
|
|
|
packag.appendChild(wire)
|
|
|
|
break
|
|
|
|
case 'pad':
|
2015-09-03 11:32:29 +02:00
|
|
|
var pad = xml.createElement('smd')
|
|
|
|
pad.setAttribute('name',element.number)
|
|
|
|
pad.setAttribute('x',convert_unit(element.x,json.unit,'mm'))
|
|
|
|
pad.setAttribute('y',convert_unit(-1*element.y,json.unit,'mm'))
|
|
|
|
pad.setAttribute('dx',convert_unit(element.width,json.unit,'mm'))
|
|
|
|
pad.setAttribute('dy',convert_unit(element.height,json.unit,'mm'))
|
|
|
|
pad.setAttribute('layer','1')
|
2015-08-30 19:36:25 +02:00
|
|
|
if (element.round) {
|
2015-09-03 11:32:29 +02:00
|
|
|
pad.setAttribute('roundness',100)
|
2015-08-30 19:36:25 +02:00
|
|
|
} else {
|
2015-09-03 11:32:29 +02:00
|
|
|
pad.setAttribute('roundness',0)
|
2015-08-30 19:36:25 +02:00
|
|
|
}
|
2015-09-03 11:32:29 +02:00
|
|
|
packag.appendChild(pad)
|
2015-09-03 00:05:54 +02:00
|
|
|
if (element.round) {
|
|
|
|
var x1, y1, x2, y2, thickness
|
|
|
|
if (element.width>element.height) {
|
|
|
|
x1 = element.x-element.width/2+element.height/2
|
|
|
|
y1 = element.y
|
|
|
|
x2 = element.x+element.width/2-element.height/2
|
|
|
|
y2 = element.y
|
|
|
|
thickness = element.height
|
|
|
|
} else {
|
|
|
|
x1 = element.x
|
|
|
|
y1 = element.y-element.height/2+element.width/2
|
|
|
|
x2 = element.x
|
|
|
|
y2 = element.y+element.height/2-element.width/2
|
|
|
|
thickness = element.width
|
|
|
|
}
|
|
|
|
var clearance = xml.createElement('wire')
|
|
|
|
clearance.setAttribute('x1',convert_unit(x1,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('y1',convert_unit(-1*y1,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('x2',convert_unit(x2,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('y2',convert_unit(-1*y2,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('width',convert_unit(Math.max(thickness+element.clearance*2,0),json.unit,'mm'))
|
|
|
|
clearance.setAttribute('layer','39')
|
|
|
|
packag.appendChild(clearance)
|
|
|
|
var soldermask = xml.createElement('wire')
|
|
|
|
soldermask.setAttribute('x1',convert_unit(x1,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('y1',convert_unit(-1*y1,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('x2',convert_unit(x2,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('y2',convert_unit(-1*y2,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('width',convert_unit(Math.max(thickness+element.soldermask*2,0),json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('layer','29')
|
|
|
|
packag.appendChild(soldermask)
|
|
|
|
} else {
|
|
|
|
var clearance = xml.createElement('rectangle')
|
|
|
|
clearance.setAttribute('x1',convert_unit(element.x-element.width/2-element.clearance,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('y1',convert_unit(-1*(element.y-element.height/2-element.clearance),json.unit,'mm'))
|
|
|
|
clearance.setAttribute('x2',convert_unit(element.x+element.width/2+element.clearance,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('y2',convert_unit(-1*(element.y+element.height/2+element.clearance),json.unit,'mm'))
|
|
|
|
clearance.setAttribute('layer','39')
|
2015-09-05 10:37:43 +02:00
|
|
|
packag.appendChild(clearance)
|
2015-09-03 00:05:54 +02:00
|
|
|
var soldermask = xml.createElement('rectangle')
|
|
|
|
soldermask.setAttribute('x1',convert_unit(element.x-element.width/2-element.soldermask,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('y1',convert_unit(-1*(element.y-element.height/2-element.soldermask),json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('x2',convert_unit(element.x+element.width/2+element.soldermask,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('y2',convert_unit(-1*(element.y+element.height/2+element.soldermask),json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('layer','29')
|
2015-09-05 10:37:43 +02:00
|
|
|
packag.appendChild(soldermask)
|
2015-09-03 00:05:54 +02:00
|
|
|
}
|
2015-09-03 11:32:29 +02:00
|
|
|
break
|
2015-08-30 19:36:25 +02:00
|
|
|
case 'pin':
|
2015-09-03 11:32:29 +02:00
|
|
|
var pin = xml.createElement('pad')
|
|
|
|
pin.setAttribute('name',element.number)
|
|
|
|
pin.setAttribute('x',convert_unit(element.x,json.unit,'mm'))
|
|
|
|
pin.setAttribute('y',convert_unit(-1*element.y,json.unit,'mm'))
|
|
|
|
pin.setAttribute('drill',convert_unit(element.drill,json.unit,'mm'))
|
|
|
|
pin.setAttribute('diameter',convert_unit(element.thickness,json.unit,'mm'))
|
|
|
|
pin.setAttribute('layer','17')
|
|
|
|
if (element.round) {
|
|
|
|
pin.setAttribute('shape','round')
|
|
|
|
} else {
|
|
|
|
pin.setAttribute('shape','square')
|
|
|
|
}
|
|
|
|
packag.appendChild(pin)
|
|
|
|
if (element.round) {
|
|
|
|
var clearance = xml.createElement('wire')
|
|
|
|
clearance.setAttribute('x1',convert_unit(x,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('y2',convert_unit(-1*y,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('x2',convert_unit(x,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('y2',convert_unit(-1*y,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('width',convert_unit(Math.max(element.thickness+element.clearance*2,0),json.unit,'mm'))
|
|
|
|
clearance.setAttribute('layer','39')
|
|
|
|
packag.appendChild(clearance)
|
|
|
|
var soldermask = xml.createElement('wire')
|
|
|
|
soldermask.setAttribute('x1',convert_unit(x,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('y1',convert_unit(-1*y,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('x2',convert_unit(x,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('y2',convert_unit(-1*y,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('width',convert_unit(Math.max(element.thickness+element.soldermask*2,0),json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('layer','29')
|
|
|
|
packag.appendChild(soldermask)
|
|
|
|
} else {
|
|
|
|
var clearance = xml.createElement('rectangle')
|
|
|
|
clearance.setAttribute('x1',convert_unit(element.x-element.thickness/2-element.clearance,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('y1',convert_unit(-1*(element.y-element.thickness/2-element.clearance),json.unit,'mm'))
|
|
|
|
clearance.setAttribute('x2',convert_unit(element.x+element.thickness/2+element.clearance,json.unit,'mm'))
|
|
|
|
clearance.setAttribute('y2',convert_unit(-1*(element.y+element.thickness/2+element.clearance),json.unit,'mm'))
|
|
|
|
clearance.setAttribute('layer','39')
|
2015-09-05 10:37:43 +02:00
|
|
|
packag.appendChild(clearance)
|
2015-09-03 11:32:29 +02:00
|
|
|
var soldermask = xml.createElement('rectangle')
|
|
|
|
soldermask.setAttribute('x1',convert_unit(element.x-element.thickness/2-element.soldermask,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('y1',convert_unit(-1*(element.y-element.thickness/2-element.soldermask),json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('x2',convert_unit(element.x+element.thickness/2+element.soldermask,json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('y2',convert_unit(-1*(element.y+element.thickness/2+element.soldermask),json.unit,'mm'))
|
|
|
|
soldermask.setAttribute('layer','29')
|
2015-09-05 10:37:43 +02:00
|
|
|
packag.appendChild(soldermask)
|
2015-09-03 11:32:29 +02:00
|
|
|
}
|
|
|
|
break
|
2015-08-30 19:36:25 +02:00
|
|
|
break
|
|
|
|
default:
|
2015-09-03 11:32:29 +02:00
|
|
|
console.log('unknown element type: '+element.type)
|
2015-08-30 19:36:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return xml
|
|
|
|
}
|