Created
October 24, 2016 13:33
-
-
Save anonymous/ffa92d8f8b12fe630e947ceba78da059 to your computer and use it in GitHub Desktop.
Small script to generate resistor-arrays for fritzing. See http://forum.fritzing.org/t/2079 for more information.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# coding=utf-8 | |
# This script generates resistor-arrays of various sizes. | |
# See: http://forum.fritzing.org/t/2079 | |
import svgwrite as svg | |
from svgwrite.base import BaseElement | |
from svgwrite.mixins import Presentation, Markers, Transform | |
import os | |
import shutil | |
import zipfile | |
import getopt | |
import sys | |
# The star type has resistors that are connected to a common pin (bussed). | |
STAR = 1 | |
BUSSED = STAR | |
# The parallel type has isolated resistors that aren't connected to each other. | |
PARALLEL = 3 | |
ISOLATED = PARALLEL | |
# Generates the descriptive part file. | |
# @param output Target file for saving | |
# @param resistors Number of resistors | |
# @param circuittype Type of circuit (STAR or PARALLEL, BUSSED or ISOLATED) | |
# @param resistance Resistance-value | |
# @param img_icon Name of the icon-file | |
# @param img_breaboard Name of the breadboard-file | |
# @param img_schematic Name of the schematics-file | |
# @param img_pcb Name of the pcb-file | |
def gen_fzp(output, resistors, circuittype, resistance, img_icon, img_bredboard, img_schematic, img_pcb): | |
pins = pin_number(resistors, circuittype) | |
fzp = '<?xml version="1.0" encoding="UTF-8"?>\n' \ | |
'<module fritzingVersion="0.9.3b" moduleId="Resistor' | |
fzp += 'Bussed' if circuittype == STAR else 'Isolated' | |
fzp += str(resistors) | |
fzp += 'SIP_ResistorModuleID">\n' \ | |
' <version>4</version>\n' \ | |
' <title>' | |
fzp += 'Bussed' if circuittype == STAR else 'Isolated' | |
fzp += str(resistors) | |
fzp += ' Resistor-' | |
fzp += str(resistors) | |
fzp += ' (SIP-' | |
fzp += str(pins) | |
fzp += ')</title>\n' \ | |
' <label>R</label>\n' \ | |
' <date>Mon Sep 26 2016</date>\n' \ | |
' <author>Stephen Lilley</author>\n' \ | |
' <tags>\n' \ | |
' <tag>resistor</tag>\n' \ | |
' <tag>' | |
fzp += 'parallel' if circuittype == PARALLEL else 'star' | |
fzp += '</tag>\n' \ | |
' <tag>array</tag>\n' \ | |
' <tag>network</tag>\n' \ | |
' <tag>' | |
fzp += 'bussed' if circuittype == STAR else 'isolated' | |
fzp += '</tag>\n' \ | |
' </tags>\n' \ | |
' <properties>\n' \ | |
' <property name="family">Resistor (Networks / Arrays)</property>\n' \ | |
' <property name="package">SIP-' | |
fzp += str(pins) | |
fzp += '</property>\n' \ | |
' <property name="circuit type">Bussed</property>\n' \ | |
' <property name="pins">' | |
fzp += str(pins) | |
fzp += '</property>\n' \ | |
' <property name="resistors">' | |
fzp += str(resistors) | |
fzp += '</property>\n' \ | |
' <property showInLabel="yes" name="Resistance">' | |
fzp += str(resistance) | |
fzp += '</property>\n' \ | |
' <property showInLabel="yes" name="power"></property>\n' \ | |
' <property showInLabel="no" name="tolerance">±5%</property>\n' \ | |
' <property name="part number"></property>\n' \ | |
' <property name="variant">variant 1</property>\n' \ | |
' </properties>\n' \ | |
' <taxonomy>discreteParts.resistor.' | |
fzp += str(resistance) | |
fzp += '</taxonomy>\n' \ | |
' <description>A resistor array is a single package which contains more than one resistor. They are typically used for convenience when several resistors are needed together in the same place in a circuit.</description>\n' \ | |
' <spice><line>RA{instanceTitle} {net connector0} {net connector1} {resistance}</line>\n' \ | |
' </spice>\n' \ | |
' <views>\n' \ | |
' <iconView>\n' \ | |
' <layers image="icon/' + img_icon + '">\n' \ | |
' <layer layerId="icon"/>\n' \ | |
' </layers>\n' \ | |
' </iconView>\n' \ | |
' <breadboardView>\n' \ | |
' <layers image="breadboard/' + img_bredboard + '">\n' \ | |
' <layer layerId="breadboard"/>\n' \ | |
' </layers>\n' \ | |
' </breadboardView>\n' \ | |
' <schematicView>\n' \ | |
' <layers image="schematic/' + img_schematic + '">\n' \ | |
' <layer layerId="schematic"/>\n' \ | |
' </layers>\n' \ | |
' </schematicView>\n' \ | |
' <pcbView>\n' \ | |
' <layers image="pcb/' + img_pcb + '">\n' \ | |
' <layer layerId="copper0"/>\n' \ | |
' <layer layerId="copper1"/>\n' \ | |
' <layer layerId="silkscreen"/>\n' \ | |
' </layers>\n' \ | |
' </pcbView>\n' \ | |
' </views>\n' \ | |
' <connectors>\n' | |
for i in range(pins): | |
fzp += ' <connector id="connector' + str(i) + '" type="male" name="Pin ' + str(i) + '">\n' \ | |
' <description>Pin' + str(i) + '</description>\n' \ | |
' <views>\n' \ | |
' <breadboardView>\n' \ | |
' <p svgId="connector' + str(i) + 'pin" layer="breadboard"/>\n' \ | |
' </breadboardView>\n' \ | |
' <schematicView>\n' \ | |
' <p terminalId="connector' + str(i) + 'terminal" svgId="connector' + str(i) + 'pin" layer="schematic"/>\n' \ | |
' </schematicView>\n' \ | |
' <pcbView>\n' \ | |
' <p svgId="connector' + str(i) + 'pin" layer="copper0"/>\n' \ | |
' <p svgId="connector' + str(i) + 'pin" layer="copper1"/>\n' \ | |
' </pcbView>\n' \ | |
' </views>\n' \ | |
' </connector>\n' | |
fzp += ' </connectors>\n' \ | |
'</module>\n' | |
with open(output, "w") as f: | |
f.write(fzp) | |
# Generates a breadboard-view of the element and saves it to a file. | |
# @param output Target file for saving | |
# @param resistors Number of resistors | |
# @param circuittype Type of circuit (STAR or PARALLEL, BUSSED or ISOLATED) | |
# @param resistance Resistance-value | |
def gen_breadboard(output, resistors, circuittype, resistance): | |
dx = 26494 # Difference between each pin | |
min_width = 73017 # Base width of the viewbox for a three-pin-object | |
width_multiplicator = 0.275642 / min_width | |
# Used to calculate the width of the SVG in inches | |
pins = pin_number(resistors, circuittype) | |
# Calculate the viewbox | |
width = min_width + (pins - 3) * dx | |
temp_name = "temp.svg" | |
draw = svg.Drawing(temp_name, | |
size = (str(width * width_multiplicator) + "in", "0.334972in"), | |
viewBox = "0 0 " + str(width) + " 88734") | |
# Center expandable body | |
expand = (pins - 3) * dx | |
path = [ | |
("M", 48808 + expand, 41602), | |
("l", -123, 17, -24247 - expand, 0, 0, -41521), | |
("c", 7776, -115, 16651, -123, 24370 + expand, -17), | |
("l", 0, 41530, 0, -8), | |
("z") | |
] | |
draw.add(draw.path(d = path, fill = "#EB9922")) | |
path = [ | |
("M", 24439, 97), | |
("c", 7768, -115, 16634, -131, 24370 + expand, -25), | |
("l", 0, 2756), | |
("c", -7735, -106, -16602, -91, -24370 - expand, 25), | |
("l", 0, -2756), | |
("z") | |
] | |
draw.add(draw.path(d = path, fill = "#D3891E")) | |
# Right end | |
path = [ | |
("M", 72013 + expand, 41315), | |
"c0,3109 -1862,5085 -5094,5085l-8186 0c-4643,-7243 11558,0 0,0 -1887,-2937 -5176,-4889 -8924,-4889 -452,0 -894,25 -1329,82l0 -41530c9056,131 16487,418 18440,885 2731,664 4159,2568 5094,5094 2256,6119 0,35263 0,35263l-1 9z" | |
] | |
draw.add(draw.path(d = path, fill = "#EB9922")) | |
path = [ | |
("M", 48488 + expand, 73), | |
"c9121,123 16602,418 18563,894 2731,664 4159,2568 5094,5094 821,2215 985,7710 796,13345 -41,-4650 -115,-8727 -796,-10581 -935,-2526 -2354,-4429 -5094,-5094 -1961,-467 -9441,-762 -18563,-894l0 -2756 0 -7zm23525 41242c0,3109 -1862,5085 -5094,5085 -14371,0 -5340,0 -8186,0 -1887,-2937 -5176,-4889 -8924,-4889 -452,0 -894,25 -1329,82l0 -6545c452,-66 894,-98 1329,-98 3790,0 8440,2666 11016,5930 1124,1477 -6414,-8047 -378,-140 2149,2756 5250,1083 6184,590 2633,-1681 3748,-3683 4151,-5340 1066,-4331 2018,-11271 2158,-22861 197,10753 -935,28184 -935,28184l7 1z" | |
] | |
draw.add(draw.path(d = path, fill = "#D3891E")) | |
path = [ | |
("M", 68921 + expand, 5142), | |
"c-2067,-1542 -8219,-394 -8252,1944 6365,3822 5766,21466 5709,23680 8,4773 2838,5266 3076,5274 2108,41 4700,-26986 -533,-30899l0 1z" | |
] | |
draw.add(draw.path(d = path, fill = "#FEFEFE", fill_opacity="0.549020", enable_background="new")) | |
# Generate the bottom pins | |
params = { "fill": "none", "stroke": "#96989A", "stroke_width": "5297.99", "stroke_linecap": "round" } | |
for i in range(pins): | |
x = 10076 + dx * i | |
# Generate the repeated middle content | |
if (i >= 2): | |
shift = dx * (i - 2) | |
path = [ | |
("M", 49808 + shift, 41520), | |
"c-3748,0 -7046,1944 -8924,4889l-8637 0c-1887,-2937 -5176,-4889 -8924,-4889l0 0 0 -10376 26494 0 0 10376 -8 0z" | |
] | |
draw.add(draw.path(d = path, fill = "#EB9922")) | |
path = [ | |
("M", 49808 + shift, 34958), | |
"l0 6562c-3748,0 -7046,1944 -8924,4889l-8637 0c-1887,-2937 -5176,-4889 -8924,-4889l0 0 0 -6562 0 0c3765,0 8449,2657 11016,5930 1477,1969 2969,1895 4463,0 2559,-3256 7226,-5930 11016,-5930l-9 0z" | |
] | |
draw.add(draw.path(d = path, fill = "#D3891E")) | |
# Place the actual legs | |
draw.add(draw.line((x, 51937), (x, 86085), **params)) | |
draw.add(draw.rect((x - 5906, 46401), (11804, 5299), fill = "#727376")) | |
draw.add(draw.rect((x - 4209, 46401), (1591, 5299), fill = "#E6E7E8")) | |
# Place the connector | |
draw.add(draw.rect((x - 2649, 78136), (5299, 10598), id = "connector" + str(i) + "pin", fill = "none")) | |
# Left end | |
draw.add(draw.path(fill = "#EB9922", d = "M24766 41618c-476,-66 -951,-98 -1443,-98 -3748,0 -7046,1944 -8924,4889l-8440 0c-3289,0 -5094,-4905 -5094,-5094 0,0 -1944,-29143 0,-35255 853,-2682 2608,-4618 5094,-5094 2280,-435 9794,-730 18816,-870l0 41521 -8 -1z")) | |
draw.add(draw.path(fill = "#D3891E", d = "M24766 41618c-467,-66 -951,-98 -1443,-98 -3748,0 -7046,1944 -8924,4889l-8440 0c-3289,0 -5094,-4905 -5094,-5094 0,0 -1017,-15273 -845,-26027 303,9728 812,18924 1214,20367 762,2822 2108,4036 4577,5815 1961,878 4249,1870 6496,-575 2568,-3264 7251,-5930 11016,-5930 467,0 951,41 1443,123l0 6537 -1 -8zm-24640 -22278c-41,-5800 148,-11057 861,-13279 853,-2682 2608,-4618 5094,-5094 2273,-435 9720,-730 18686,-870l0 2756c-8965,140 -16413,435 -18686,870 -2485,476 -4240,2411 -5094,5094 -590,1862 -821,5864 -861,10524l0 -1z")) | |
draw.add(draw.path(fill = "#FEFEFE", fill_opacity="0.549020", enable_background = "new", d="M4817 4511c3249,-1452 10196,-229 10057,2322 -8005,0 -5356,18357 -7653,23591 -1615,3675 -2256,4675 -4749,5577 -1239,-353 -3232,-28996 2346,-31490z")) | |
draw.add(draw.circle((11133, 31398), r = 2649)) | |
# Add the text on top | |
label = gen_label(resistors, circuittype, resistance) | |
draw.add(draw.text(label, x = [ 13782 ], y = [ 31398 ], fill = "#5F6160", font_weight = "normal", font_size = "18395.9", font_family = "Droid Sans")) | |
# Save the file | |
draw.save() | |
add_linebreaks(temp_name, output, True) | |
# Generates a correct label for the pcb-view based on the circuit | |
# @param resistors Number of resistors | |
# @param circuittype Type of circuit (STAR or PARALLEL, BUSSED or ISOLATED) | |
# @param resistance Resistance-value | |
# @returns The generated label | |
def gen_label(resistors, circuittype, resistance): | |
res = str(resistance) | |
res_code = res[:2] + str(len(res) - 2) | |
n = pin_number(resistors, circuittype) | |
return "L {:02d} {} {}".format(n, circuittype, res_code) | |
# Determines how many pins are needed to create a given circuit. | |
# @param resistors Number of resistors | |
# @param circuittype Type of circuit (STAR or PARALLEL, BUSSED or ISOLATED) | |
# @returns Number of pins | |
def pin_number(resistors, circuittype): | |
return resistors + 1 if circuittype == STAR else resistors * 2 | |
# Load the file to add linebreaks | |
# @param input File that should be modified | |
# @param output Location to save result to | |
# @param remove_input Remove inputfile after successful output-generation | |
def add_linebreaks(input, output, remove_input = False): | |
with open(output, "wt") as fout: | |
with open(input, "rt") as fin: | |
for line in fin: | |
fout.write(line.replace(">", ">\r\n")) | |
if (remove_input): os.remove(input) | |
# Generates an icon of the element and saves it to a file. | |
# @param output Target file for saving | |
# @param resistors Number of resistors | |
# @param circuittype Type of circuit (STAR or PARALLEL, BUSSED or ISOLATED) | |
# @param resistance Resistance-value | |
def gen_icon(output, resistors, circuittype, resistance): | |
gen_breadboard(output, resistors, circuittype, resistance) | |
# Generates a pcb-view of the element and saves it to a file. | |
# @param output Target file for saving | |
# @param resistors Number of resistors | |
# @param circuittype Type of circuit (STAR or PARALLEL, BUSSED or ISOLATED) | |
# @param resistance Resistance-value | |
def gen_pcb(output, resistors, circuittype, resistance): | |
dx = 2793 # Difference between each pin | |
min_width = 8657 # Base width of the viewbox for a three-pin-object | |
width_multiplicator = 0.309984 / min_width | |
# Used to calculate the width of the SVG in inches | |
pins = pin_number(resistors, circuittype) | |
# Calculate the viewbox | |
width = min_width + (pins - 3) * dx | |
temp_name = "temp.svg" | |
draw = svg.Drawing(temp_name, | |
size = (str(width * width_multiplicator) + "in", "0.11in"), | |
viewBox = "0 0 " + str(width) + " 3072") | |
# Structure | |
g_pcb = svg.container.Group(id = "pcb") | |
draw.add(g_pcb) | |
# Silkscreen | |
g_silk = svg.container.Group(id = "silkscreen") | |
g_silk.add(draw.rect((140, 140), (dx * pins + 2, 2793), fill = "none", stroke = "#FEFEFE", stroke_width = "279.267")) | |
g_silk.add(draw.line((1117, 2932), (140, 1955), fill = "none", stroke = "white", stroke_width = "139.629")) | |
g_pcb.add(g_silk) | |
# Copper 1 | |
g_cop_par = svg.container.Group(id = "copper1") | |
g_pcb.add(g_cop_par) | |
# Copper 0 | |
g_cop = svg.container.Group(id = "copper0") | |
g_cop_par.add(g_cop) | |
# Add Pins | |
for i in range(pins): | |
g_cop.add(draw.circle((dx * i + 1536, 1536), id = "connector{}pin".format(i), fill = "none", stroke = "#FFBF00", stroke_width = "558.514", r = 810)) | |
# Save the file | |
draw.save() | |
add_linebreaks(temp_name, output, True) | |
# Generates the schematics-view and saves it to a file. | |
# @param output Target file for saving | |
# @param resistors Number of resistors | |
# @param circuittype Type of circuit (STAR or PARALLEL, BUSSED or ISOLATED) | |
# @param resistance Resistance-value | |
def gen_schematics(output, resistors, circuittype, resistance): | |
dy = 7370 # Difference between each pin | |
min_height = 29993 # Base height of the viewbox for a three-pin-object | |
height_multiplicator = 0.406945 / min_height | |
# Used to calculate the height of the SVG in inches | |
pins = pin_number(resistors, circuittype) | |
# Calculate the viewbox | |
height = min_height + (pins - 3) * dy | |
temp_name = "temp.svg" | |
draw = svg.Drawing(temp_name, | |
size = ("0.508346in", str(height * height_multiplicator) + "in"), | |
viewBox = "0 0 37467 " + str(height)) | |
# Expandable body | |
expand = (pins - 3) * dy | |
draw.add(draw.rect((7730, 256), (29481, 29481 + expand), fill = "#FEFEFE", stroke = "#373435", stroke_width = "511.857", stroke_linecap = "round")) | |
if (circuittype == STAR): draw.add(draw.line((33525, 7626), (33525, 22366 + expand), fill = "none", stroke = "#606062", stroke_width = "511.857", stroke_linecap = "round")) | |
# External pins | |
for i in range(pins): | |
y = dy * i | |
draw.add(draw.line((358, 7626 + y), (7720, 7626 + y), id = "connector{}pin".format(i), fill = "none", stroke = "#787878", stroke_width = "716.426", stroke_linecap = "round")) | |
draw.add(draw.rect((321, 7589 + y), (74, 74), fill = "none", id = "connector{}terminal".format(i))) | |
draw.add(draw.text(str(i + 1), x = [ 3057.2 ], y = [ 6551.4 + y ], fill = "#606062", font_weight = "normal", font_size = "3582.71", font_family = "Droid Sans")) | |
# Internal pins | |
for i in range(0, pins, 2): | |
y = dy * i | |
draw.add(draw.line((7730, 7625 + y), (33526, 7625 + y), fill = "none", stroke = "#606062", stroke_width = "511.857", stroke_linecap = "round")) | |
if (circuittype == STAR): break | |
draw.add(draw.line((33526, 7625 + y), (33526, 7625 + y + dy), fill = "none", stroke = "#606062", stroke_width = "511.857", stroke_linecap = "round")) | |
# Internal resistors | |
for i in range(1, pins): | |
if (circuittype == PARALLEL and not i % 2): continue | |
y = dy * (i - 1) | |
path = [ | |
("M", 27998, 14996 + y), | |
"l5527 0m-6265 2949l737 -2949m-2579 -2948l1842 5897m-3685 0l1843 -5897m-3685 0l1842 5897m-3685 0l1843 -5897m-3685 0l1842 5897m-3685 0l1843 -5897m-3685 0l1842 5897m-2948 -2949l1106 -2948m-6633 2948l5527 0" | |
] | |
draw.add(draw.path(fill = "none", stroke = "#606062", stroke_width = "511.857", stroke_linecap = "round", d = path)) | |
# Connectordots | |
if (circuittype == STAR): | |
for i in range(1, pins - 1): | |
y = dy * (i - 1) | |
draw.add(draw.circle((33451, 14996 + y), r = 663, fill = "#606062", stroke = "#606062", stroke_width = "221.109")) | |
# Save the file | |
draw.save() | |
add_linebreaks(temp_name, output, True) | |
# Generates the complete part-package ready for inmport to fritzing. | |
# @param output Target file for saving | |
# @param resistors Number of resistors | |
# @param circuittype Type of circuit (STAR or PARALLEL, BUSSED or ISOLATED) | |
# @param resistance Resistance-value | |
def gen_part(output, resistors, circuittype, resistance): | |
name = 'resistor-' | |
name += 'bussed' if circuittype == PARALLEL else 'isolated' | |
name += '-' + str(resistors) + '-sip' | |
basedir = "temp/" | |
if (os.path.exists(basedir)): shutil.rmtree(basedir) | |
os.makedirs(basedir) | |
f_icon = "svg.icon." + name + "_icon.svg" | |
f_breadboard = "svg.breadboard." + name + "_breadboard.svg" | |
f_schematic = "svg.schematic." + name + "_schematic.svg" | |
f_pcb = "svg.pcb." + name + "_pcb.svg" | |
f_fzp = "part." + name + ".fzp" | |
gen_icon(basedir + f_icon, resistors, circuittype, resistance) | |
gen_breadboard(basedir + f_breadboard, resistors, circuittype, resistance) | |
gen_schematics(basedir + f_schematic, resistors, circuittype, resistance) | |
gen_pcb(basedir + f_pcb, resistors, circuittype, resistance) | |
gen_fzp(basedir + f_fzp, resistors, circuittype, resistance, name + "_icon.svg", name + "_breadboard.svg", name + "_schematic.svg", name + "_pcb.svg") | |
f_zip = zipfile.ZipFile(output, 'w') | |
for f in [f_icon, f_breadboard, f_schematic, f_pcb, f_fzp]: | |
f_zip.write(basedir + f, f) | |
f_zip.close() | |
shutil.rmtree(basedir) | |
# Function that get's called once the script is called in the console. | |
# @param argv Console-parameters. | |
def main(argv): | |
try: | |
opts, args = getopt.getopt(argv, "ho:n:r:pbis", ["help", "output=", "resistors=", "number=", "resistance=", "value=", "parallel", "isolated", "star", "bussed"]) | |
except getopt.GetoptError: | |
print("Type", sys.argv[0], "-h for usage-information.") | |
sys.exit(2) | |
circuittype = 0 | |
output = "" | |
resistors = 0 | |
resistance = 0 | |
for opt, arg in opts: | |
if opt in ("-h", "--help"): | |
print("Small script, that generates resistor-arrays of various sizes.") | |
print("See: http://forum.fritzing.org/t/2079") | |
print() | |
print("Example:", sys.argv[0], "-o test.fzpz -n 5 -p -r 10000") | |
print() | |
print("Parameters:") | |
print("-h, --help: ", "Outputs this help.") | |
print("-o, --output: ", "Output file to save to.") | |
print("-n, --resistors, --number n ", "Number of resistors in the circuit (not pins!).") | |
print("-r, --resistance, --value r ", "Resistance value (e.g. 10000 for 10kOhm).") | |
print("-p, --parallel, -i, --isolated", "Isolated/Parallel circuit-type.") | |
print("-s, --star, -b, --bussed ", "Bussed/Star circuit-type.") | |
sys.exit(0) | |
elif opt in ("-o", "--output"): | |
output = arg | |
elif opt in ("-n", "--resistors", "--number"): | |
resistors = int(arg) | |
elif opt in ("-r", "--resistance", "--value"): | |
resistance = int(arg) | |
elif opt in ("-p", "--parallel", "-i", "--isolated"): | |
circuittype = PARALLEL | |
elif opt in ("-s", "--star", "-b", "--bussed"): | |
circuittype = STAR | |
else: | |
print("Type", sys.argv[0], "-h for usage-information.") | |
sys.exit(3) | |
if (circuittype in (STAR, PARALLEL)) and output != "" and resistors > 0 and resistance > 0: | |
gen_part(output, resistors, circuittype, resistance) | |
sys.exit(0) | |
print("Type", sys.argv[0], "-h for usage-information.") | |
sys.exit(3) | |
# Call the main function upon launch. | |
if __name__ == "__main__": main(sys.argv[1:]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment