diff options
author | jaseg <git@jaseg.net> | 2019-09-26 19:45:54 +0200 |
---|---|---|
committer | jaseg <git@jaseg.net> | 2019-09-26 19:45:54 +0200 |
commit | b2eb56076d47af08a11e10fa53446a00b849a13c (patch) | |
tree | 4537c06e2e9bc5b78a731231e6c049c8cd5f376c /support | |
parent | 82b88f920a85487372cc6e0b46633e4aa328eb69 (diff) | |
download | pogojig-b2eb56076d47af08a11e10fa53446a00b849a13c.tar.gz pogojig-b2eb56076d47af08a11e10fa53446a00b849a13c.tar.bz2 pogojig-b2eb56076d47af08a11e10fa53446a00b849a13c.zip |
Pogojig mostly done: KiCAD export works
Diffstat (limited to 'support')
-rw-r--r-- | support/asymptote/__main__.py | 63 | ||||
-rwxr-xr-x | support/generate_kicad.py | 322 | ||||
-rwxr-xr-x[-rw-r--r--] | support/inkscape_exporter.py (renamed from support/inkscape/__main__.py) | 53 | ||||
-rw-r--r-- | support/lib/make.py | 5 | ||||
-rw-r--r-- | support/lib/util.py | 130 | ||||
-rw-r--r-- | support/openscad/__init__.py | 0 | ||||
-rw-r--r-- | support/openscad/__main__.py | 46 | ||||
-rw-r--r-- | support/pogojig/__init__.py (renamed from support/asymptote/__init__.py) | 0 | ||||
-rw-r--r-- | support/pogojig/inkscape/__init__.py (renamed from support/inkscape/__init__.py) | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | support/pogojig/inkscape/bezmisc.py (renamed from support/inkscape/bezmisc.py) | 38 | ||||
-rw-r--r--[-rwxr-xr-x] | support/pogojig/inkscape/cspsubdiv.py (renamed from support/inkscape/cspsubdiv.py) | 7 | ||||
-rw-r--r--[-rwxr-xr-x] | support/pogojig/inkscape/cubicsuperpath.py (renamed from support/inkscape/cubicsuperpath.py) | 2 | ||||
-rw-r--r-- | support/pogojig/inkscape/dxf_footer.txt (renamed from support/inkscape/dxf_footer.txt) | 0 | ||||
-rw-r--r-- | support/pogojig/inkscape/dxf_header.txt (renamed from support/inkscape/dxf_header.txt) | 0 | ||||
-rw-r--r-- | support/pogojig/inkscape/effect.py (renamed from support/inkscape/effect.py) | 17 | ||||
-rw-r--r--[-rwxr-xr-x] | support/pogojig/inkscape/ffgeom.py (renamed from support/inkscape/ffgeom.py) | 18 | ||||
-rw-r--r--[-rwxr-xr-x] | support/pogojig/inkscape/inkex.py (renamed from support/inkscape/inkex.py) | 13 | ||||
-rw-r--r-- | support/pogojig/inkscape/inkscape.py (renamed from support/inkscape/inkscape.py) | 50 | ||||
-rw-r--r--[-rwxr-xr-x] | support/pogojig/inkscape/simplepath.py (renamed from support/inkscape/simplepath.py) | 15 | ||||
-rw-r--r--[-rwxr-xr-x] | support/pogojig/inkscape/simpletransform.py (renamed from support/inkscape/simpletransform.py) | 7 | ||||
-rw-r--r-- | support/pogojig/kicad/__init__.py (renamed from support/lib/__init__.py) | 0 | ||||
-rw-r--r-- | support/pogojig/kicad/kicad-cache.lib | 21 | ||||
-rw-r--r-- | support/pogojig/kicad/kicad.pro | 34 |
23 files changed, 482 insertions, 359 deletions
diff --git a/support/asymptote/__main__.py b/support/asymptote/__main__.py deleted file mode 100644 index 77bbdc7..0000000 --- a/support/asymptote/__main__.py +++ /dev/null @@ -1,63 +0,0 @@ -import sys, os, shutil -from lib import util, make - - -def _asymptote(in_path, out_path, asymptote_dir, working_dir): - args = [os.environ['ASYMPTOTE'], '-vv', '-f', 'pdf', '-o', out_path, in_path] - - with util.command_context(args, set_env={'ASYMPTOTE_DIR': asymptote_dir}, working_dir=working_dir, use_stderr=True) as process: - def get_loaded_file(line): - if any(line.startswith(j) for j in ['Loading ', 'Including ']): - parts = line.rstrip('\n').split(' ') - - if len(parts) == 4: - _, _, from_, path = parts - - if from_ == 'from': - return path - - return None - - def iter_loaded_files(): - for i in process.stderr: - loaded_file = get_loaded_file(i) - - if loaded_file is not None: - yield loaded_file - elif not any(i.startswith(j) for j in ['cd ', 'Using configuration ']): - print >> sys.stderr, i, - - loaded_files = list(iter_loaded_files()) - - return loaded_files - - -@util.main -def main(in_path, out_path): - try: - _, out_suffix = os.path.splitext(out_path) - - with util.TemporaryDirectory() as temp_dir: - absolute_in_path = os.path.abspath(in_path) - temp_out_path = os.path.join(temp_dir, 'out.pdf') - - # Asymptote creates A LOT of temp files (presumably when invoking - # LaTeX) and leaves some of them behind. Thus we run asymptote - # in a temporary directory. - loaded_files = _asymptote( - absolute_in_path, - 'out', - os.path.dirname(absolute_in_path), - temp_dir) - - if not os.path.exists(temp_out_path): - raise util.UserError('Asymptote did not generate a PDF file.', in_path) - - # All dependencies as paths relative to the project root. - dependencies = set(map(os.path.relpath, loaded_files)) - - # Write output files. - make.write_dependencies(out_path + '.d', out_path, dependencies - {in_path}) - shutil.copyfile(temp_out_path, out_path) - except util.UserError as e: - raise util.UserError('While processing {}: {}', in_path, e) diff --git a/support/generate_kicad.py b/support/generate_kicad.py new file mode 100755 index 0000000..77f58fa --- /dev/null +++ b/support/generate_kicad.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python3 + +import os +import sys +import time +from os import path +from textwrap import dedent +import pkgutil +import subprocess +import xml.etree.ElementTree as xe + +import ezdxf + + +__version__ = '0.1' + +PIN_TS_BASE = 0x23420000 +TEDIT_BASE = 0x23430000 +PATH_BASE = 0x23440000 + + +def sch_template(name, num_pins, yspace=200): + templ = f''' + EESchema Schematic File Version 5 + EELAYER 30 0 + EELAYER END + $Descr A3 16535 11693 + encoding utf-8 + Sheet 1 1 + Title "{name}" + Date "{time.strftime("%d %b %Y")}" + Rev "" + Comp "" + Comment1 "" + Comment2 "" + Comment3 "" + Comment4 "" + Comment5 "" + Comment6 "" + Comment7 "" + Comment8 "" + Comment9 "" + $EndDescr + {{components}} + $EndSCHEMATC + ''' + + components = [] + for i in range(num_pins): + identifier = f'TP{i}' + value = 'pogopin' + x, y = 1000, 1000 + i*yspace + components.append(dedent(f''' + $Comp + L Connector:Conn_01x01_Female {identifier} + U 1 1 {PIN_TS_BASE + i:08X} + P {x} {y} + F 0 "{identifier}" H {x-50} {y+50} 50 0000 R CNN + F 1 "{value}" H {x+50} {y} 50 0000 L CNN + F 2 "Pogopin:AutogeneratedPogopinFootprint" H {x} {y} 50 0001 C CNN + F 3 "~" H {x} {y} 50 0001 C CNN + 1 {x} {y} + -1 0 0 1 + $EndComp + ''').strip()) + + return dedent(templ).lstrip().format(components='\n'.join(components)) + +def pcb_template(outline, pins, annular=0.5): + pcb_templ = f''' + (kicad_pcb (version 20190605) (host pogojig "({__version__})") + + (general + (thickness 1.6) + (drawings {len(pins)}) + (tracks 0) + (modules {len(pins)}) + (nets {len(pins)+1}) + ) + + (page "A4") + (layers + (0 "F.Cu" signal) + (31 "B.Cu" signal) + (32 "B.Adhes" user) + (33 "F.Adhes" user) + (34 "B.Paste" user) + (35 "F.Paste" user) + (36 "B.SilkS" user) + (37 "F.SilkS" user) + (38 "B.Mask" user) + (39 "F.Mask" user) + (40 "Dwgs.User" user) + (41 "Cmts.User" user) + (42 "Eco1.User" user) + (43 "Eco2.User" user) + (44 "Edge.Cuts" user) + (45 "Margin" user) + (46 "B.CrtYd" user) + (47 "F.CrtYd" user) + (48 "B.Fab" user) + (49 "F.Fab" user) + ) + + (setup + (last_trace_width 0.25) + (trace_clearance 0.2) + (zone_clearance 0.508) + (zone_45_only no) + (trace_min 0.2) + (via_size 0.8) + (via_drill 0.4) + (via_min_size 0.4) + (via_min_drill 0.3) + (uvia_size 0.3) + (uvia_drill 0.1) + (uvias_allowed no) + (uvia_min_size 0.2) + (uvia_min_drill 0.1) + (max_error 0.005) + (defaults + (edge_clearance 0.01) + (edge_cuts_line_width 0.05) + (courtyard_line_width 0.05) + (copper_line_width 0.2) + (copper_text_dims (size 1.5 1.5) (thickness 0.3) keep_upright) + (silk_line_width 0.12) + (silk_text_dims (size 1 1) (thickness 0.15) keep_upright) + (other_layers_line_width 0.1) + (other_layers_text_dims (size 1 1) (thickness 0.15) keep_upright) + ) + (pad_size 3.14159 3.14159) + (pad_drill 1.41421) + (pad_to_mask_clearance 0.051) + (solder_mask_min_width 0.25) + (aux_axis_origin 0 0) + (visible_elements FFFFFF7F) + (pcbplotparams + (layerselection 0x010fc_ffffffff) + (usegerberextensions false) + (usegerberattributes false) + (usegerberadvancedattributes false) + (creategerberjobfile false) + (excludeedgelayer true) + (linewidth 0.100000) + (plotframeref false) + (viasonmask false) + (mode 1) + (useauxorigin false) + (hpglpennumber 1) + (hpglpenspeed 20) + (hpglpendiameter 15.000000) + (psnegative false) + (psa4output false) + (plotreference true) + (plotvalue true) + (plotinvisibletext false) + (padsonsilk false) + (subtractmaskfromsilk false) + (outputformat 1) + (mirror false) + (drillshape 1) + (scaleselection 1) + (outputdirectory "")) + ) + + (net 0 "") + {{net_defs}} + + (net_class "Default" "This is the default net class." + (clearance 0.2) + (trace_width 0.25) + (via_dia 0.8) + (via_drill 0.4) + (uvia_dia 0.3) + (uvia_drill 0.1) + {{net_class_defs}} + ) + + {{module_defs}} + + {{edge_cuts}} + )''' + + module_defs = [] + for i, pin in enumerate(pins): + (x, y), hole_dia = pin # all dimensions in mm here + pad_dia = hole_dia + 2*annular + mod = f''' + (module "Pogopin:AutogeneratedPogopinFootprint" (layer "F.Cu") (tedit {TEDIT_BASE + i:08X}) (tstamp {PIN_TS_BASE + i:08X}) + (at {x} {y}) + (descr "Pogo pin {i}") + (tags "test point plated hole") + (path "/{PATH_BASE + i:08X}") + (attr virtual) + (fp_text reference "TP{i}" (at 0 -{pad_dia/2 + 1}) (layer "F.SilkS") + (effects (font (size 1 1) (thickness 0.15))) + ) + (fp_text value "pogo pin {i}" (at 0 {pad_dia/2 + 1}) (layer "F.Fab") + (effects (font (size 1 1) (thickness 0.15))) + ) + (fp_text user "%R" (at 0 -{pad_dia/2 + 1}) (layer "F.Fab") + (effects (font (size 1 1) (thickness 0.15))) + ) + (fp_circle (center 0 0) (end {pad_dia} 0) (layer "F.CrtYd") (width 0.05)) + (fp_circle (center 0 0) (end 0 -{pad_dia}) (layer "F.SilkS") (width 0.12)) + (pad "1" thru_hole circle (at 0 0) (size {pad_dia} {pad_dia}) (drill {hole_dia}) (layers *.Cu *.Mask) + (net {i+1} "pogo{i}")) + )''' + module_defs.append(mod) + + edge_cuts = [ f'(gr_line (start {x1} {y1}) (end {x2} {y2}) (layer "Edge.Cuts") (width 0.05))' + for (x1, y1), (x2, y2) in outline ] + + net_defs = [ f'(net {i+1} "pogo{i}")' for i, _pin in enumerate(pins) ] + net_class_defs = [ f'(add_net "pogo{i}")' for i, _pin in enumerate(pins) ] + return pcb_templ.format( + net_defs='\n'.join(net_defs), + net_class_defs='\n'.join(net_class_defs), + module_defs='\n'.join(module_defs), + edge_cuts='\n'.join(edge_cuts)) + +def inkscape_query_all(filename): + proc = subprocess.run([ os.environ.get('INKSCAPE', 'inkscape'), filename, '--query-all'], capture_output=True) + proc.check_returncode() + data = [ line.split(',') for line in proc.stdout.decode().splitlines() ] + return { id: (float(x), float(y), float(w), float(h)) for id, x, y, w, h in data } + +SVG_NS = { + 'svg': 'http://www.w3.org/2000/svg', + 'inkscape': 'http://www.inkscape.org/namespaces/inkscape', + 'sodipodi': 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' +} + +def svg_find_elements(doc, tag, layer=None): + for i, g in enumerate(doc.findall('svg:g', SVG_NS)): + if g.attrib.get(f'{{{SVG_NS["inkscape"]}}}groupmode') != 'layer': + continue + + label = g.attrib.get(f'{{{SVG_NS["inkscape"]}}}label', '') + if not layer or label == layer: + yield from g.iter(tag) + +# def svg_get_scale(doc): +# w = doc.attrib['width'] +# h = doc.attrib['height'] +# +# if not w.endswith('mm') and h.endswith('mm'): +# raise ValueError('Document dimensions in SVG must be set to millimeters') +# +# w, h = float(w[:-2]), float(h[:-2]) +# _x, _y, vb_w, vb_h = map(float, doc.attrib['viewBox'].split()) +# scale_x, scale_y = vb_w / w, vb_h / h +# assert abs(1 - scale_x/scale_y) < 0.001 +# return scale_x + +def svg_get_viewbox_mm(doc): + w = doc.attrib['width'] + h = doc.attrib['height'] + + if not w.endswith('mm') and h.endswith('mm'): + raise ValueError('Document dimensions in SVG must be set to millimeters') + + w, h = float(w[:-2]), float(h[:-2]) + x, y, vb_w, vb_h = map(float, doc.attrib['viewBox'].split()) + scale_x, scale_y = vb_w / w, vb_h / h + return x/scale_x, y/scale_y, w, h + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('svg', metavar='pogo_map.svg', help='Input inkscape SVG pogo pin map (use provided template!)') + parser.add_argument('outline', metavar='outline.dxf', help='Board outline DXF generated by OpenSCAD') + parser.add_argument('output', default='kicad', help='Output directory/project name and path') + parser.add_argument('-y', '--yspace', type=int, default=200, help='Schematic pin Y spacing in mil (default: 200)') + parser.add_argument('-a', '--annular', type=float, default=0.5, help='Pogo pin annular ring width in mm (default: 0.5)') + parser.add_argument('-l', '--svg-layer', type=str, default='Test Points', help='Name of SVG layer containing pogo pins') + args = parser.parse_args() + + if not path.exists(args.output): + os.mkdir(args.output) + + if not path.isdir(args.output): + raise SystemError(f'Output path "{args.output}" is not a directory') + + with open(args.svg, 'r') as f: + doc = xe.fromstring(f.read()) + pogo_circle_ids = [ circle.attrib['id'] for circle in svg_find_elements(doc, f'{{{SVG_NS["svg"]}}}circle', args.svg_layer) ] + # scale = svg_get_scale(doc) + page_x, page_y, page_w, page_h = svg_get_viewbox_mm(doc) + MM_PER_IN = 25.4 + SVG_DEF_DPI = 96 + px_to_mm = lambda px: px/SVG_DEF_DPI * MM_PER_IN + query = inkscape_query_all(args.svg) + dims = [ query[id] for id in pogo_circle_ids ] + assert all( abs(1 - w/h) < 0.001 for _x, _y, w, h in dims ) + print('origin:', page_x, page_y) + print('dims:', page_w, page_h) + pins = [ ( + (page_x + px_to_mm(x) + px_to_mm(w)/2, + page_y - page_h + px_to_mm(y) + px_to_mm(w)/2), + px_to_mm(w)) for x, y, w, h in dims ] + + doc = ezdxf.readfile(args.outline) + outline = [] + for line in doc.modelspace().query('LINE'): + (x1, y1, _z1), (x2, y2, _z2) = line.dxf.start, line.dxf.end + outline.append(((x1, -y1), (x2, -y2))) + + out_name = path.basename(args.output) + with open(path.join(args.output, f'{out_name}.sch'), 'w', encoding='utf8') as sch: + sch.write(sch_template(f'{out_name} generated schematic (PogoJig v{__version__})', len(pins), yspace=args.yspace)) + + with open(path.join(args.output, f'{out_name}.kicad_pcb'), 'w', encoding='utf8') as pcb: + pcb.write(pcb_template(outline, pins, annular=args.annular)) + + with open(path.join(args.output, f'{out_name}.pro'), 'w', encoding='utf8') as f: + f.write(pkgutil.get_data('pogojig.kicad', 'kicad.pro').decode('utf8')) + + with open(path.join(args.output, f'{out_name}-cache.lib'), 'w', encoding='utf8') as f: + f.write(pkgutil.get_data('pogojig.kicad', 'kicad-cache.lib').decode('utf8')) + diff --git a/support/inkscape/__main__.py b/support/inkscape_exporter.py index 29ac745..4e80c63 100644..100755 --- a/support/inkscape/__main__.py +++ b/support/inkscape_exporter.py @@ -1,6 +1,10 @@ -import os, shutil -from lib import util -from . import effect, inkscape +#!/usr/bin/env python3 + +import os +import shutil +import tempfile + +from pogojig.inkscape import effect, inkscape def _unfuck_svg_document(temp_svg_path): @@ -32,33 +36,26 @@ def _unfuck_svg_document(temp_svg_path): command_line.delete_layer(copy) command_line.apply_to_document('FileSave', 'FileClose', 'FileQuit') - command_line.run() -@util.main -def main(in_path, out_path): - try: - _, out_suffix = os.path.splitext(out_path) +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('infile', metavar='input.svg', help='Inkscape SVG input file') + parser.add_argument('outfile', metavar='output.dxf', help='DXF output file') + args = parser.parse_args() + + effect.ExportEffect.check_document_units(args.infile) + + with tempfile.TemporaryDirectory() as tmpdir: + temp_svg_path = os.path.join(tmpdir, os.path.basename(args.infile)) + shutil.copyfile(args.infile, temp_svg_path) + + _unfuck_svg_document(temp_svg_path) - effect.ExportEffect.check_document_units(in_path) + export_effect = effect.ExportEffect() + export_effect.affect(args=[temp_svg_path], output=False) - with util.TemporaryDirectory() as temp_dir: - temp_svg_path = os.path.join(temp_dir, os.path.basename(in_path)) - - shutil.copyfile(in_path, temp_svg_path) - - _unfuck_svg_document(temp_svg_path) - - export_effect = effect.ExportEffect() - export_effect.affect(args=[temp_svg_path], output=False) - - with open(out_path, 'w') as file: - if out_suffix == '.dxf': - export_effect.write_dxf(file) - elif out_suffix == '.asy': - export_effect.write_asy(file) - else: - raise Exception('Unknown file type: {}'.format(out_suffix)) - except util.UserError as e: - raise util.UserError('While processing {}: {}', in_path, e) + with open(args.outfile, 'w') as f: + export_effect.write_dxf(f) diff --git a/support/lib/make.py b/support/lib/make.py deleted file mode 100644 index c89b306..0000000 --- a/support/lib/make.py +++ /dev/null @@ -1,5 +0,0 @@ -from . import util - - -def write_dependencies(path, target, dependencies): - util.write_file(path, '{}: {}\n'.format(target, ' '.join(dependencies)).encode()) diff --git a/support/lib/util.py b/support/lib/util.py deleted file mode 100644 index c00a5fe..0000000 --- a/support/lib/util.py +++ /dev/null @@ -1,130 +0,0 @@ -import contextlib -import inspect -import os -import re -import shutil -import subprocess -import sys -import tempfile - - -class UserError(Exception): - def __init__(self, message, *args): - super(UserError, self).__init__(message.format(*args)) - - -def main(fn): - """ - Decorator for "main" functions. Decorates a function that should be - called when the containing module is run as a script (e.g. via python -m - <module>). - """ - frame = inspect.currentframe().f_back - - def wrapped_fn(*args, **kwargs): - try: - fn(*args, **kwargs) - except UserError as e: - print >> sys.stderr, 'Error:', e - sys.exit(1) - except KeyboardInterrupt: - sys.exit(2) - - if frame.f_globals['__name__'] == '__main__': - wrapped_fn(*sys.argv[1:]) - - # Allow the main function also to be called explicitly - return wrapped_fn - - -def rename_atomic(source_path, target_path): - """ - Move the file at source_path to target_path. - - If both paths reside on the same device, os.rename() is used, otherwise - the file is copied to a temporary name next to target_path and moved from - there using os.rename(). - """ - source_dir_stat = os.stat(os.path.dirname(source_path)) - target_dir_stat = os.stat(os.path.dirname(target_path)) - - if source_dir_stat.st_dev == target_dir_stat.st_dev: - os.rename(source_path, target_path) - else: - temp_path = target_path + '~' - - shutil.copyfile(source_path, temp_path) - os.rename(temp_path, target_path) - - -@contextlib.contextmanager -def TemporaryDirectory(): - dir = tempfile.mkdtemp() - - try: - yield dir - finally: - shutil.rmtree(dir) - - -@contextlib.contextmanager -def command_context(args, remove_env=[], set_env={}, working_dir=None, use_stderr=False): - env = dict(os.environ) - - for i in remove_env: - del env[i] - - for k, v in set_env.items(): - env[k] = v - - if use_stderr: - stderr = subprocess.PIPE - else: - stderr = None - - try: - process = subprocess.Popen(args, env=env, cwd=working_dir, stderr=stderr) - except OSError as e: - raise UserError('Error running {}: {}', args[0], e) - - try: - yield process - except: - try: - process.kill() - except OSError: - # Ignore exceptions here so we don't mask the - # already-being-thrown exception. - pass - - raise - finally: - # Use communicate so that we won't deadlock if the process generates - # some unread output. - process.communicate() - - if process.returncode: - raise UserError('Command failed: {}', ' '.join(args)) - - -def command(args, remove_env=[], set_env={}, working_dir=None): - with command_context(args, remove_env, set_env, working_dir): - pass - - -def bash_escape_string(string): - return "'{}'".format(re.sub("'", "'\"'\"'", string)) - - -def write_file(path, data): - temp_path = path + '~' - - with open(temp_path, 'wb') as file: - file.write(data) - - os.rename(temp_path, path) - - -def read_file(path): - with open(path, 'rb') as file: - return file.read() diff --git a/support/openscad/__init__.py b/support/openscad/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/support/openscad/__init__.py +++ /dev/null diff --git a/support/openscad/__main__.py b/support/openscad/__main__.py deleted file mode 100644 index 85f8b99..0000000 --- a/support/openscad/__main__.py +++ /dev/null @@ -1,46 +0,0 @@ -import os - -from lib import util, make - - -def _openscad(in_path, out_path, deps_path): - util.command([os.environ['OPENSCAD'], '-o', out_path, '-d', deps_path, in_path]) - - -@util.main -def main(in_path, out_path): - cwd = os.getcwd() - - def relpath(path): - return os.path.relpath(path, cwd) - - with util.TemporaryDirectory() as temp_dir: - temp_deps_path = os.path.join(temp_dir, 'deps') - temp_mk_path = os.path.join(temp_dir, 'mk') - temp_files_path = os.path.join(temp_dir, 'files') - - _, out_ext = os.path.splitext(out_path) - - # OpenSCAD requires the output file name to end in .stl or .dxf. - temp_out_path = os.path.join(temp_dir, 'out' + out_ext) - - _openscad(in_path, temp_out_path, temp_deps_path) - - mk_content = '%:; echo "$@" >> {}'.format(util.bash_escape_string(temp_files_path)) - - # Use make to parse the dependency makefile written by OpenSCAD. - util.write_file(temp_mk_path, mk_content.encode()) - util.command( - ['make', '-s', '-B', '-f', temp_mk_path, '-f', temp_deps_path], - remove_env=['MAKELEVEL', 'MAKEFLAGS']) - - # All dependencies as paths relative to the project root. - deps = set(map(relpath, util.read_file(temp_files_path).decode().splitlines())) - - # Relative paths to all files that should not appear in the - # dependency makefile. - ignored_files = set(map(relpath, [in_path, temp_deps_path, temp_mk_path, temp_out_path])) - - # Write output files. - make.write_dependencies(out_path + '.d', out_path, deps - ignored_files) - util.rename_atomic(temp_out_path, out_path) diff --git a/support/asymptote/__init__.py b/support/pogojig/__init__.py index e69de29..e69de29 100644 --- a/support/asymptote/__init__.py +++ b/support/pogojig/__init__.py diff --git a/support/inkscape/__init__.py b/support/pogojig/inkscape/__init__.py index e69de29..e69de29 100644 --- a/support/inkscape/__init__.py +++ b/support/pogojig/inkscape/__init__.py diff --git a/support/inkscape/bezmisc.py b/support/pogojig/inkscape/bezmisc.py index b7f5429..68a338d 100755..100644 --- a/support/inkscape/bezmisc.py +++ b/support/pogojig/inkscape/bezmisc.py @@ -55,8 +55,9 @@ def rootWrapper(a,b,c,d): return 1.0*(-d/c), return () -def bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))): +def bezierparameterize(points): #parametric bezier + ((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)) = points x0=bx0 y0=by0 cx=3*(bx1-x0) @@ -69,8 +70,10 @@ def bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))): return ax,ay,bx,by,cx,cy,x0,y0 #ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))) -def linebezierintersect(((lx1,ly1),(lx2,ly2)),((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))): +def linebezierintersect(line, bezier): #parametric line + ((lx1,ly1),(lx2,ly2)) = line + ((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)) = bezier dd=lx1 cc=lx2-lx1 bb=ly1 @@ -99,19 +102,23 @@ def linebezierintersect(((lx1,ly1),(lx2,ly2)),((bx0,by0),(bx1,by1),(bx2,by2),(bx retval.append(bezierpointatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),i)) return retval -def bezierpointatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t): +def bezierpointatt(xxx_todo_changeme3,t): + ((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)) = xxx_todo_changeme3 ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))) x=ax*(t**3)+bx*(t**2)+cx*t+x0 y=ay*(t**3)+by*(t**2)+cy*t+y0 return x,y -def bezierslopeatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t): +def bezierslopeatt(xxx_todo_changeme4,t): + ((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)) = xxx_todo_changeme4 ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))) dx=3*ax*(t**2)+2*bx*t+cx dy=3*ay*(t**2)+2*by*t+cy return dx,dy -def beziertatslope(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),(dy,dx)): +def beziertatslope(xxx_todo_changeme5, xxx_todo_changeme6): + ((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)) = xxx_todo_changeme5 + (dy,dx) = xxx_todo_changeme6 ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))) #quadratic coefficents of slope formula if dx: @@ -136,9 +143,12 @@ def beziertatslope(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),(dy,dx)): retval.append(i) return retval -def tpoint((x1,y1),(x2,y2),t): +def tpoint(xxx_todo_changeme7, xxx_todo_changeme8,t): + (x1,y1) = xxx_todo_changeme7 + (x2,y2) = xxx_todo_changeme8 return x1+t*(x2-x1),y1+t*(y2-y1) -def beziersplitatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t): +def beziersplitatt(xxx_todo_changeme9,t): + ((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)) = xxx_todo_changeme9 m1=tpoint((bx0,by0),(bx1,by1),t) m2=tpoint((bx1,by1),(bx2,by2),t) m3=tpoint((bx2,by2),(bx3,by3),t) @@ -167,7 +177,9 @@ Jens Gravesen <gravesen@mat.dth.dk> mat-report no. 1992-10, Mathematical Institute, The Technical University of Denmark. ''' -def pointdistance((x1,y1),(x2,y2)): +def pointdistance(xxx_todo_changeme10, xxx_todo_changeme11): + (x1,y1) = xxx_todo_changeme10 + (x2,y2) = xxx_todo_changeme11 return math.sqrt(((x2 - x1) ** 2) + ((y2 - y1) ** 2)) def Gravesen_addifclose(b, len, error = 0.001): box = 0 @@ -208,19 +220,21 @@ def Simpson(f, a, b, n_limit, tolerance): asum += bsum bsum = 0.0 est0 = est1 - for i in xrange(1, n, 2): + for i in range(1, n, 2): bsum += f(a + (i * interval)) est1 = multiplier * (endsum + (2.0 * asum) + (4.0 * bsum)) #print multiplier, endsum, interval, asum, bsum, est1, est0 return est1 -def bezierlengthSimpson(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)), tolerance = 0.001): +def bezierlengthSimpson(xxx_todo_changeme12, tolerance = 0.001): + ((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)) = xxx_todo_changeme12 global balfax,balfbx,balfcx,balfay,balfby,balfcy ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))) balfax,balfbx,balfcx,balfay,balfby,balfcy = 3*ax,2*bx,cx,3*ay,2*by,cy return Simpson(balf, 0.0, 1.0, 4096, tolerance) -def beziertatlength(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)), l = 0.5, tolerance = 0.001): +def beziertatlength(xxx_todo_changeme13, l = 0.5, tolerance = 0.001): + ((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)) = xxx_todo_changeme13 global balfax,balfbx,balfcx,balfay,balfby,balfcy ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))) balfax,balfbx,balfcx,balfay,balfby,balfcy = 3*ax,2*bx,cx,3*ay,2*by,cy @@ -267,7 +281,7 @@ if __name__ == '__main__': print s, st ''' for curve in curves: - print beziertatlength(curve,0.5) + print(beziertatlength(curve,0.5)) # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99 diff --git a/support/inkscape/cspsubdiv.py b/support/pogojig/inkscape/cspsubdiv.py index c34236a..72da473 100755..100644 --- a/support/inkscape/cspsubdiv.py +++ b/support/pogojig/inkscape/cspsubdiv.py @@ -1,8 +1,9 @@ #!/usr/bin/env python -from bezmisc import * -from ffgeom import * +from .bezmisc import * +from .ffgeom import * -def maxdist(((p0x,p0y),(p1x,p1y),(p2x,p2y),(p3x,p3y))): +def maxdist(points): + ((p0x,p0y),(p1x,p1y),(p2x,p2y),(p3x,p3y)) = points p0 = Point(p0x,p0y) p1 = Point(p1x,p1y) p2 = Point(p2x,p2y) diff --git a/support/inkscape/cubicsuperpath.py b/support/pogojig/inkscape/cubicsuperpath.py index 861b9da..a594660 100755..100644 --- a/support/inkscape/cubicsuperpath.py +++ b/support/pogojig/inkscape/cubicsuperpath.py @@ -19,7 +19,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ -import simplepath +from . import simplepath from math import * def matprod(mlist): diff --git a/support/inkscape/dxf_footer.txt b/support/pogojig/inkscape/dxf_footer.txt index a225dd7..a225dd7 100644 --- a/support/inkscape/dxf_footer.txt +++ b/support/pogojig/inkscape/dxf_footer.txt diff --git a/support/inkscape/dxf_header.txt b/support/pogojig/inkscape/dxf_header.txt index 341cb1b..341cb1b 100644 --- a/support/inkscape/dxf_header.txt +++ b/support/pogojig/inkscape/dxf_header.txt diff --git a/support/inkscape/effect.py b/support/pogojig/inkscape/effect.py index 313032c..89824e9 100644 --- a/support/inkscape/effect.py +++ b/support/pogojig/inkscape/effect.py @@ -10,7 +10,6 @@ import pkgutil import re from lxml import etree -from lib import util from . import inkex, simpletransform, cubicsuperpath, cspsubdiv, inkscape @@ -39,7 +38,7 @@ class ExportEffect(inkex.Effect): def __init__(self): inkex.Effect.__init__(self) - self._flatness = float(os.environ['DXF_FLATNESS']) + self._flatness = float(os.environ.get('INKSCAPE_DXF_FLATNESS', 0.1)) self._layers = None self._paths = None @@ -139,11 +138,11 @@ class ExportEffect(inkex.Effect): layer_indices = {l: i for i, l in enumerate(self._layers)} - file.write(pkgutil.get_data(__name__, 'dxf_header.txt')) + file.write(pkgutil.get_data(__name__, 'dxf_header.txt').decode('ASCII')) def write_instruction(code, value): - print >> file, code - print >> file, value + print(code, file=file) + print(value, file=file) handle_iter = itertools.count(256) @@ -165,7 +164,7 @@ class ExportEffect(inkex.Effect): write_instruction(21, repr(y2 / unit_factor)) write_instruction(31, 0.0) - file.write(pkgutil.get_data(__name__, 'dxf_footer.txt')) + file.write(pkgutil.get_data(__name__, 'dxf_footer.txt').decode('ASCII')) def write_asy(self, file): def write_line(format, *args): @@ -263,18 +262,18 @@ class ExportEffect(inkex.Effect): height_attr = document.getroot().get('height') if height_attr is None: - raise util.UserError( + raise ValueError( 'SVG document has no height attribute. See ' 'https://github.com/Feuermurmel/openscad-template/wiki/Absolute-Measurements') _, height_unit = cls._parse_measure(height_attr) if height_unit is None or height_unit == 'px': - raise util.UserError( + raise ValueError( 'Height of SVG document is not an absolute measure. See ' 'https://github.com/Feuermurmel/openscad-template/wiki/Absolute-Measurements') if document.getroot().get('viewBox') is None: - raise util.UserError( + raise ValueError( 'SVG document has no viewBox attribute. See ' 'https://github.com/Feuermurmel/openscad-template/wiki/Absolute-Measurements') diff --git a/support/inkscape/ffgeom.py b/support/pogojig/inkscape/ffgeom.py index ef8799b..368a29a 100755..100644 --- a/support/inkscape/ffgeom.py +++ b/support/pogojig/inkscape/ffgeom.py @@ -20,11 +20,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ import math -try: - NaN = float('NaN') -except ValueError: - PosInf = 1e300000 - NaN = PosInf/PosInf class Point: precision = 5 @@ -72,11 +67,11 @@ class Segment: def slope(self): if self.delta_x() != 0: return self.delta_x() / self.delta_y() - return NaN + return math.nan def intercept(self): if self.delta_x() != 0: return self[1]['y'] - (self[0]['x'] * self.slope()) - return NaN + return math.nan def distanceToPoint(self, p): s2 = Segment(self[0],p) c1 = dot(s2,self) @@ -88,7 +83,8 @@ class Segment: return self.perpDistanceToPoint(p) def perpDistanceToPoint(self, p): len = self.length() - if len == 0: return NaN + if len == 0: + return math.nan return math.fabs(((self[1]['x'] - self[0]['x']) * (self[0]['y'] - p['y'])) - \ ((self[0]['x'] - p['x']) * (self[1]['y'] - self[0]['y']))) / len def angle(self): @@ -96,13 +92,13 @@ class Segment: def length(self): return math.sqrt((self.delta_x() ** 2) + (self.delta_y() ** 2)) def pointAtLength(self, len): - if self.length() == 0: return Point(NaN, NaN) + if self.length() == 0: return Point(math.nan, math.nan) ratio = len / self.length() x = self[0]['x'] + (ratio * self.delta_x()) y = self[0]['y'] + (ratio * self.delta_y()) return Point(x, y) def pointAtRatio(self, ratio): - if self.length() == 0: return Point(NaN, NaN) + if self.length() == 0: return Point(math.nan, math.nan) x = self[0]['x'] + (ratio * self.delta_x()) y = self[0]['y'] + (ratio * self.delta_y()) return Point(x, y) @@ -132,7 +128,7 @@ def intersectSegments(s1, s2): x = x1 + ((num / denom) * (x2 - x1)) y = y1 + ((num / denom) * (y2 - y1)) return Point(x, y) - return Point(NaN, NaN) + return Point(math.nan, math.nan) def dot(s1, s2): return s1.delta_x() * s2.delta_x() + s1.delta_y() * s2.delta_y() diff --git a/support/inkscape/inkex.py b/support/pogojig/inkscape/inkex.py index 19e860b..609ffeb 100755..100644 --- a/support/inkscape/inkex.py +++ b/support/pogojig/inkscape/inkex.py @@ -35,6 +35,8 @@ import re import sys from math import * +from lxml import etree + #a dictionary of all of the xmlns prefixes in a standard inkscape doc NSS = { u'sodipodi' :u'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd', @@ -107,15 +109,6 @@ def are_near_relative(a, b, eps): else: return False - -# third party library -try: - from lxml import etree -except Exception, e: - localize() - errormsg(_("The fantastic lxml wrapper for libxml2 is required by inkex.py and therefore this extension. Please download and install the latest version from http://cheeseshop.python.org/pypi/lxml/, or install it through your package manager by a command like: sudo apt-get install python-lxml\n\nTechnical details:\n%s" % (e,))) - sys.exit() - def check_inkbool(option, opt, value): if str(value).capitalize() == 'True': return True @@ -126,7 +119,7 @@ def check_inkbool(option, opt, value): def addNS(tag, ns=None): val = tag - if ns!=None and len(ns)>0 and NSS.has_key(ns) and len(tag)>0 and tag[0]!='{': + if ns!=None and len(ns)>0 and ns in NSS and len(tag)>0 and tag[0]!='{': val = "{%s}%s" % (NSS[ns], tag) return val diff --git a/support/inkscape/inkscape.py b/support/pogojig/inkscape/inkscape.py index 9fd9528..efe7677 100644 --- a/support/inkscape/inkscape.py +++ b/support/pogojig/inkscape/inkscape.py @@ -1,46 +1,34 @@ import os +import subprocess import xml.etree.ElementTree as etree -from lib import util - - def get_inkscape_layers(svg_path): document = etree.parse(svg_path) - def iter_layers(): - nodes = document.findall( - '{http://www.w3.org/2000/svg}g[@{http://www.inkscape.org/namespaces/inkscape}groupmode="layer"]') + layers = [] + nodes = document.findall( + '{http://www.w3.org/2000/svg}g[@{http://www.inkscape.org/namespaces/inkscape}groupmode="layer"]') + + for i in nodes: + inkscape_name = i.get('{http://www.inkscape.org/namespaces/inkscape}label').strip() - for i in nodes: - inkscape_name = i.get('{http://www.inkscape.org/namespaces/inkscape}label').strip() + if inkscape_name.endswith(']'): + export_name, args = inkscape_name[:-1].rsplit('[', 1) - if inkscape_name.endswith(']'): - export_name, args = inkscape_name[:-1].rsplit('[', 1) - - export_name = export_name.strip() - args = args.strip() - - use_paths = 'p' in args - else: - use_paths = False - export_name = inkscape_name + export_name = export_name.strip() + args = args.strip() - yield Layer(inkscape_name, export_name, use_paths) - - return list(iter_layers()) + use_paths = 'p' in args + else: + use_paths = False + export_name = inkscape_name + + layers.append(Layer(inkscape_name, export_name, use_paths)) + return layers def _inkscape(svg_path, verbs): - def iter_args(): - yield os.environ['INKSCAPE'] - - for i in verbs: - yield '--verb' - yield i - - yield svg_path - - util.command(list(iter_args())) + subprocess.run([os.environ.get('INKSCAPE', 'inkscape'), *(x for verb in verbs for x in ('--verb', verb)), svg_path]) class Layer(object): diff --git a/support/inkscape/simplepath.py b/support/pogojig/inkscape/simplepath.py index 94ab092..8d4f0a7 100755..100644 --- a/support/inkscape/simplepath.py +++ b/support/pogojig/inkscape/simplepath.py @@ -49,7 +49,7 @@ def lexPath(d): offset = m.end() continue #TODO: create new exception - raise Exception, 'Invalid path data!' + raise ValueError('Invalid path data!') ''' pathdefs = {commandfamily: [ @@ -71,6 +71,7 @@ pathdefs = { 'A':['A', 7, [float, float, float, int, int, float, float], ['r','r','a',0,'s','x','y']], 'Z':['L', 0, [], []] } + def parsePath(d): """ Parse SVG path and return an array of segments. @@ -87,14 +88,14 @@ def parsePath(d): while 1: try: - token, isCommand = lexer.next() + token, isCommand = next(lexer) except StopIteration: break params = [] needParam = True if isCommand: if not lastCommand and token.upper() != 'M': - raise Exception, 'Invalid path, must begin with moveto.' + raise ValueError('Invalid path, must begin with moveto.') else: command = token else: @@ -107,16 +108,16 @@ def parsePath(d): else: command = pathdefs[lastCommand.upper()][0].lower() else: - raise Exception, 'Invalid path, no initial command.' + raise ValueError('Invalid path, no initial command.') numParams = pathdefs[command.upper()][1] while numParams > 0: if needParam: try: - token, isCommand = lexer.next() + token, isCommand = next(lexer) if isCommand: - raise Exception, 'Invalid number of parameters' + raise ValueError('Invalid number of parameters') except StopIteration: - raise Exception, 'Unexpected end of path' + raise ValueError('Unexpected end of path') cast = pathdefs[command.upper()][2][-numParams] param = cast(token) if command.islower(): diff --git a/support/inkscape/simpletransform.py b/support/pogojig/inkscape/simpletransform.py index 55082ed..610cb57 100755..100644 --- a/support/inkscape/simpletransform.py +++ b/support/pogojig/inkscape/simpletransform.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python ''' Copyright (C) 2006 Jean-Francois Barraud, barraud@math.univ-lille1.fr Copyright (C) 2010 Alvin Penner, penner@vaxxine.com @@ -21,9 +20,11 @@ barraud@math.univ-lille1.fr This code defines several functions to make handling of transform attribute easier. ''' -import inkex, cubicsuperpath + import math, re +from . import inkex, cubicsuperpath + def parseTransform(transf,mat=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]): if transf=="" or transf==None: return(mat) @@ -117,7 +118,7 @@ def applyTransformToPath(mat,path): def fuseTransform(node): if node.get('d')==None: #FIXME: how do you raise errors? - raise AssertionError, 'can not fuse "transform" of elements that have no "d" attribute' + raise AssertionError('can not fuse "transform" of elements that have no "d" attribute') t = node.get("transform") if t == None: return diff --git a/support/lib/__init__.py b/support/pogojig/kicad/__init__.py index e69de29..e69de29 100644 --- a/support/lib/__init__.py +++ b/support/pogojig/kicad/__init__.py diff --git a/support/pogojig/kicad/kicad-cache.lib b/support/pogojig/kicad/kicad-cache.lib new file mode 100644 index 0000000..a98cd73 --- /dev/null +++ b/support/pogojig/kicad/kicad-cache.lib @@ -0,0 +1,21 @@ +EESchema-LIBRARY Version 2.4 +#encoding utf-8 +# +# Connector_Conn_01x01_Female +# +DEF Connector_Conn_01x01_Female J 0 40 Y N 1 F N +F0 "J" 0 100 50 H V C CNN +F1 "Connector_Conn_01x01_Female" 0 -100 50 H V C CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + Connector*:* +$ENDFPLIST +DRAW +A 0 0 20 901 -901 1 1 6 N 0 20 0 -20 +P 2 1 1 6 -50 0 -20 0 N +X Pin_1 1 -200 0 150 R 50 50 1 1 P +ENDDRAW +ENDDEF +# +#End Library diff --git a/support/pogojig/kicad/kicad.pro b/support/pogojig/kicad/kicad.pro new file mode 100644 index 0000000..5cd0983 --- /dev/null +++ b/support/pogojig/kicad/kicad.pro @@ -0,0 +1,34 @@ +update=05/04/2019 20:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +CopperEdgeClearance=0.000000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] |