oshw_logo/cvlp_lib.js

739 lines
27 KiB
JavaScript

/* library for creating CuVoodoo Land Pattern (cvlp) JSON files and render them
Copyright (C) 2015 King Kévin <kingkevin@cuvoodoo.info>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// create a set of elements with an line element
function cvlp_line(x1, y1, x2, y2, thickness) {
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
function cvlp_arc(x, y, radius, start, angle, thickness) {
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
function cvlp_pad(number, x, y, width, height, round) {
var pad = {}
pad.type = "pad"
pad.number = number
pad.x = x
pad.y = y
pad.width = width
pad.height = height
pad.round = round || true
return [pad]
}
// create a set of elements with an pin element
function cvlp_pin(number, x, y, thickness, hole, round) {
var pin = {}
pin.type = "pin"
pin.number = number
pin.x = x
pin.y = y
pin.thickness = thickness
pin.hole = hole
pin.round = round || true
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) {
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 || false) {
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
// provide top corner, size, an radius of corner
function cvlp_rectangle(x, y, width, height, thickness, radius) {
var rectangle = []
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))
if (radius !== 'undefined' && radius != 0) {
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))
}
return rectangle
}
// 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;
}
// 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')
// 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)
// add info
var svg_title = document.createElementNS(svgNS, 'title');
svg_title.textContent = json.name;
svg.appendChild(svg_title);
// add layers
layers_desc = [ { name: 'copper', stroke: 'gray', fill: 'gray'},
{ name: 'hole', stroke: 'none', fill: 'white'},
{ name: 'silkscreen', stroke: 'black', fill: 'none', 'stroke-linecap': 'round'}
]
layers = []
for (layer_i in layers_desc) {
var layer_desc = layers_desc[layer_i]
var layer = document.createElementNS(svgNS, 'g')
layer.setAttribute('inkscape:groupmode', 'layer')
layer.setAttribute('inkscape:label', layer_desc.name)
for (key in layer_desc) {
layer.setAttribute(key, layer_desc[key])
}
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)
line.style.setProperty('stroke-width', element.thickness)
layers['silkscreen'].appendChild(line)
break
case 'arc':
element.start = (element.start + 360) % 360
element.stop = (element.stop + 360) % 360
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 {
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)
arc = document.createElementNS(svgNS, 'path')
arc.setAttribute('d', 'M ' + x1 + ' ' + y1 + ' A ' + element.radius + ' ' + element.radius + ' 0 ' + ((element.angle) % 360 > 180 ? '1' : '0') + ' 1 ' + x2 + ' ' + y2 + ' ')
}
arc.style.setProperty('stroke-width', element.thickness)
layers['silkscreen'].appendChild(arc)
break
case 'pad':
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)
if (element.round) {
copper.setAttribute('rx', Math.min(element.width, element.height) / 2)
copper.setAttribute('ry', Math.min(element.width, element.height) / 2)
} else {
copper.setAttribute('rx', 0)
copper.setAttribute('ry', 0)
}
layers['copper'].appendChild(copper)
break
case 'pin':
break
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)
var hole = document.createElementNS(svgNS, 'circle')
hole.style.setProperty('stroke-width', 0)
hole.setAttribute('cx', element.x)
hole.setAttribute('cy', element.y)
hole.setAttribute('r', element.hole / 2)
layers['hole'].appendChild(hole)
break
default:
console.log('unknown element type: ' + element.type)
}
}
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':
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 + ' ' + 0 + '' + json.unit + ' ' + thickness + '' + json.unit + ' "" "' + element.number + '" "'
if (!element.round) {
pcb += 'square'
}
pcb += '"]\n'
break
case 'pin':
pcb += 'Pin[' + element.x + '' + json.unit + ' ' + element.y + '' + json.unit + ' ' + element.thickness + '' + json.unit + ' ' + 0 + '' + json.unit + ' ' + thickness + '' + json.unit + ' ' + element.hole + '' + json.unit + ' "" "' + element.number + '" "'
if (!element.round) {
pcb += 'square'
}
pcb += '"]\n'
break
default:
console.log('unknown element type: ' + element.type)
}
}
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':
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'
break
case 'arc':
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'
break
case 'pad':
kicad += '(pad "' + element.number + '" smd '
if (element.round) {
kicad += 'oval'
} else {
kicad += 'rect'
}
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') + ')'
kicad += ' (layers F.Cu F.Paste F.Mask)'
kicad += ')\n'
break
case 'pin':
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'
}
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.hole, json.unit, 'mm')
kicad += ' (layers F.Cu F.Paste F.Mask)'
kicad += ')\n'
break
default:
console.log('unknown element type: ' + element.type)
}
}
kicad += ')\n'
return kicad
}
// 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')
var comment = xml.createComment('footprint generated from CuVoodoo Land Pattern, author: ' + json.author + ', version: ' + json.version + ', date: ' + json.date)
xml.documentElement.appendChild(comment)
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'},
{ number: 17, name: 'Pads', color: 2, fill: 1, visible: 'yes', active: 'yes'},
{ number: 21, name: 'tPLace', color: 7, fill: 1, visible: 'yes', 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')
description.textContent = json.name
library.appendChild(description)
// add package
var packages = xml.createElement('packages')
library.appendChild(packages)
var packag = xml.createElement('package')
packag.setAttribute('name','OSHW')
packages.appendChild(packag)
var desc = xml.createElement('description')
desc.textContent = json.name
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':
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)
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')
wire.setAttribute('cap', 'round')
packag.appendChild(wire)
break
case 'pad':
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')
if (element.round) {
pad.setAttribute('roundness', 100)
} else {
pad.setAttribute('roundness', 0)
}
packag.appendChild(pad)
break
case 'pin':
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.hole, 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)
break
default:
console.log('unknown element type: ' + element.type)
}
}
return xml
}
// convert CuVoodoo Land Pattern JSON to coralEDA subscircuit
function json2subc(json)
{
// create file and add info
var subc = ''
subc += '# footprint generated from CuVoodoo Land Pattern\n'
subc += '# author: ' + json.author + '\n'
subc += '# version: ' + json.version + '\n'
subc += '# date: ' + json.date + '\n'
id = 1 // global subc id
subc += 'li:pcb-rnd-subcircuit-v6 {\n'
subc += ' ha:subc.' + (id++) + ' {\n'
subc += ' uid = any_24_ASCII_characters_\n'
subc += ' ha:attributes {\n'
subc += ' footprint = ' + json.name + '\n'
subc += ' }\n'
subc += ' ha:data {\n'
// try to group the pads/pins
function element_generic(element) {
generic = JSON.parse(JSON.stringify(element))
delete generic.x
delete generic.y
delete generic.number
return generic
}
// get pins/pads
padstacks = []
for (var element_i in json.elements) {
var element = json.elements[element_i]
if (element.type == 'pin' || element.type == 'pad') {
padstacks.push(element_generic(element))
}
}
padstacks = padstacks.map((value) => JSON.stringify(value))
padstacks = padstacks.filter((value, index, self) => self.indexOf(value) == index)
padstacks = padstacks.map((value) => JSON.parse(value))
// add padstacks prototypes
id = 2
subc += ' li:padstack_prototypes {\n'
for (var padstack_i in padstacks) {
var padstack = padstacks[padstack_i]
switch (padstack.type) {
case 'pad':
subc += ' ha:ps_proto_v6.' + (id++) + ' {\n'
subc += ' htop = 0\n'
subc += ' hbottom = 0\n'
subc += ' hdia = 0\n'
subc += ' hplated = 0\n'
subc += ' li:shape {\n'
var line = ''
line += ' ha:ps_line {\n'
var x1, y1, x2, y2, thickness
if (padstack.width > padstack.height) {
x1 = - padstack.width / 2 + padstack.height / 2
y1 = 0
x2 = padstack.width / 2 - padstack.height / 2
y2 = 0
thickness = padstack.height
} else {
x1 = 0
y1 = - padstack.height / 2 + padstack.width / 2
x2 = 0
y2 = padstack.height / 2 - padstack.width / 2
thickness = padstack.width
}
line += ' x1 = ' + x1 + json.unit + '\n'
line += ' y1 = ' + y1 + json.unit + '\n'
line += ' x2 = ' + x2 + json.unit + '\n'
line += ' y2 = ' + y2 + json.unit + '\n'
line += ' thickness =' + thickness + json.unit + '\n'
line += ' square = ' + (padstack.round ? '0' : '1') + '\n'
line += ' }\n'
subc += ' ha:ps_shape_v4 {\n'
subc += ' clearance = 0\n'
subc += line
subc += ' ha:layer_mask {\n'
subc += ' top = 1\n'
subc += ' copper = 1\n'
subc += ' }\n'
subc += ' }\n'
subc += ' ha:ps_shape_v4 {\n'
subc += ' clearance = 0\n'
subc += line
subc += ' ha:layer_mask {\n'
subc += ' top = 1\n'
subc += ' mask = 1\n'
subc += ' }\n'
subc += ' ha:combining {\n'
subc += ' sub = 1\n'
subc += ' auto = 1\n'
subc += ' }\n'
subc += ' }\n'
subc += ' ha:ps_shape_v4 {\n'
subc += ' clearance = 0\n'
subc += line
subc += ' ha:layer_mask {\n'
subc += ' top = 1\n'
subc += ' paste = 1\n'
subc += ' }\n'
subc += ' ha:combining {\n'
subc += ' auto = 1\n'
subc += ' }\n'
subc += ' }\n'
subc += ' }\n'
subc += ' }\n'
break
case 'pin':
subc += ' ha:ps_proto_v6.' + (id++) + ' {\n'
subc += ' htop = 0\n'
subc += ' hbottom = 0\n'
subc += ' hdia = ' + element.hole + json.unit + '\n'
subc += ' hplated = ' + (element.thickness > element.hole ? '1' : '0') + '\n'
subc += ' li:shape {\n'
var circle = ''
circle += ' ha:ps_circ {\n'
circle += ' x = 0\n'
circle += ' y = 0\n'
circle += ' dia = ' + element.thickness + json.unit + '\n'
circle += ' }\n'
subc += ' ha:ps_shape_v4 {\n'
subc += ' clearance = 0\n'
subc += circle
subc += ' ha:layer_mask {\n'
subc += ' top = 1\n'
subc += ' copper = 1\n'
subc += ' }\n'
subc += ' }\n'
subc += ' ha:ps_shape_v4 {\n'
subc += ' clearance = 0\n'
subc += circle
subc += ' ha:layer_mask {\n'
subc += ' top = 1\n'
subc += ' mask = 1\n'
subc += ' }\n'
subc += ' ha:combining {\n'
subc += ' sub = 1\n'
subc += ' auto = 1\n'
subc += ' }\n'
subc += ' }\n'
subc += ' ha:ps_shape_v4 {\n'
subc += ' clearance = 0\n'
subc += circle
subc += ' ha:layer_mask {\n'
subc += ' intern = 1\n'
subc += ' copper = 1\n'
subc += ' }\n'
subc += ' }\n'
subc += ' ha:ps_shape_v4 {\n'
subc += ' clearance = 0\n'
subc += circle
subc += ' ha:layer_mask {\n'
subc += ' bottom = 1\n'
subc += ' copper = 1\n'
subc += ' }\n'
subc += ' }\n'
subc += ' ha:ps_shape_v4 {\n'
subc += ' clearance = 0\n'
subc += circle
subc += ' ha:layer_mask {\n'
subc += ' bottom = 1\n'
subc += ' mask = 1\n'
subc += ' }\n'
subc += ' ha:combining {\n'
subc += ' sub = 1\n'
subc += ' auto = 1\n'
subc += ' }\n'
subc += ' }\n'
subc += ' }\n'
subc += ' }\n'
break
}
}
subc += ' }\n' // end padstack_prototypes
// add pads/pins
subc += ' li:objects {\n'
for (var element_i in json.elements) {
var element = json.elements[element_i]
if (element.type != 'pin' && element.type != 'pad') {
continue
}
padstack = padstacks.map((value) => JSON.stringify(value)).indexOf(JSON.stringify(element_generic(element)))
if (padstack < 0) {
continue
}
subc += ' ha:padstack_ref.' + (id++) + '{\n'
subc += ' proto = ' + (2 + padstack) + '\n'
subc += ' rot = 0\n'
subc += ' x = ' + element.x + json.unit + '\n'
subc += ' y = ' + element.y + json.unit + '\n'
subc += ' ha:attributes {\n'
subc += ' term = ' + element.number + '\n'
subc += ' name = ' + element.number + '\n'
subc += ' }\n'
subc += ' }\n'
}
subc += ' }\n' // end objects
// add silkscreen
subc += ' li:layers {\n'
subc += ' ha:top-silkscreen {\n'
subc += ' lid = 1\n'
subc += ' ha:type {\n'
subc += ' top = 1\n'
subc += ' silk = 1\n'
subc += ' }\n'
subc += ' li:objects {\n'
for (var element_i in json.elements) {
var element = json.elements[element_i]
switch (element.type) {
case 'line':
subc += ' ha:line.' + (id++) + '{\n'
subc += ' clearance = 0\n'
subc += ' x1 = ' + element.x1 + json.unit + '\n'
subc += ' y1 = ' + element.y1 + json.unit + '\n'
subc += ' x2 = ' + element.x2 + json.unit + '\n'
subc += ' y2 = ' + element.y2 + json.unit + '\n'
subc += ' thickness = ' + element.thickness + json.unit + '\n'
subc += ' }\n'
break
case 'arc':
subc += ' ha:arc.' + (id++) + '{\n'
subc += ' clearance = 0\n'
subc += ' x = ' + element.x + json.unit + '\n'
subc += ' y = ' + element.y + json.unit + '\n'
subc += ' width = ' + element.radius + json.unit + '\n'
subc += ' height = ' + element.radius + json.unit + '\n'
subc += ' thickness = ' + element.thickness + json.unit + '\n'
subc += ' astart = ' + ((element.start * -1 + 180) % 360) + '\n'
subc += ' adelta = ' + (element.angle * -1) + '\n'
subc += ' }\n'
break
}
if (element.type != 'pin' && element.type != 'pad') {
continue
}
padstack = padstacks.map((value) => JSON.stringify(value)).indexOf(JSON.stringify(element_generic(element)))
if (padstack < 0) {
continue
}
subc += ' ha:padstack_ref.' + (id++) + '{\n'
subc += ' proto = ' + (2 + padstack) + '\n'
subc += ' rot = 0\n'
subc += ' x = ' + element.x + json.unit + '\n'
subc += ' y = ' + element.y + json.unit + '\n'
subc += ' ha:attributes {\n'
subc += ' term = ' + element.number + '\n'
subc += ' name = ' + element.number + '\n'
subc += ' }\n'
subc += ' }\n'
}
subc += ' }\n' // end objects
subc += ' }\n' // end silkscreen
subc += ' }\n' // end layers
subc += ' }\n' // end data
subc += ' }\n' // end subc
subc += '}\n' // end pcb-rnd-subcircuit-v6
return subc
}