From 145a576d570f319e3e17980808124c2c36326df8 Mon Sep 17 00:00:00 2001
From: Grace Copplestone <grace.copplestone@cba.mit.edu>
Date: Wed, 1 Nov 2017 16:02:51 -0400
Subject: [PATCH] Add new file

---
 eagle/eagle_png.py | 185 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 185 insertions(+)
 create mode 100644 eagle/eagle_png.py

diff --git a/eagle/eagle_png.py b/eagle/eagle_png.py
new file mode 100644
index 0000000..3534fe3
--- /dev/null
+++ b/eagle/eagle_png.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import platform
+import glob
+import subprocess
+import hashlib
+
+def find_eagle():
+    if platform.uname()[0] == 'Darwin':
+        try:
+            eagle_dir = glob.glob('/Applications/EAGLE*')[-1]
+        except IndexError:
+            sys.stderr.write("Error: EAGLE not found.\n")
+            sys.exit(1)
+
+        return eagle_dir + '/EAGLE.app/Contents/MacOS/EAGLE'
+    else:
+        if subprocess.call(['which','eagle'],
+                           stdout = open(os.devnull, 'w')):
+            sys.stderr.write("Error: EAGLE not found.\n")
+            sys.exit(1)
+        return 'eagle'
+
+def create_images(name, resolution = 1500):
+    for img in ['top','bottom','cutout','holes','vias']:
+        file = '%s.%s.png' % (name, img)
+        if os.path.isfile(file):
+            os.remove(file)
+
+    script = '''
+ratsnest; write;
+set palette black; window;
+display none top vias pads;
+export image '{name}.top.png' monochrome {resolution};
+display none bottom vias pads;
+export image '{name}.bottom.png' monochrome {resolution};
+display none milling;
+export image '{name}.cutout.png' monochrome {resolution};
+display none holes;
+export image '{name}.holes.png' monochrome {resolution};
+display none vias pads;
+export image '{name}.vias.png' monochrome {resolution};
+quit'''.format(name = name, resolution = resolution)
+    subprocess.call([find_eagle(), '-C', script, name + '.brd'])
+
+def md5(filename):
+    with open(filename,'rb') as f:
+        m = hashlib.md5()
+        for chunk in iter(lambda: f.read(m.block_size*128), ''):
+            m.update(chunk)
+    return m.digest()
+
+def clean_up(name):
+    preserve = ['top','bottom','cutout']
+    for img in ['top','bottom','cutout','holes','vias']:
+        file = '%s.%s.png'   % (name, img)
+        file_ = '%s.%s_.png' % (name, img)
+        if os.path.isfile(file) and img not in preserve:
+            os.remove(file)
+        if os.path.isfile(file_):
+            os.remove(file_)
+
+def print_help():
+    print """command line: eagle_png [options] target.brd
+   target.brd = EAGLE brd file to render
+   The board outline should be a solid polygon on the 'milling' layer
+   Internal cutouts should be solid shapes on the 'holes' layer
+
+   Valid options:
+       --resolution NUM : sets output image resolution
+       --doublesided : forces double-sided mode"""
+    sys.exit(1)
+
+if __name__ == '__main__':
+    if len(sys.argv) == 1:
+        print_help()
+        sys.exit(1)
+
+    # Parse arguments
+    sys.argv = sys.argv[1:]
+    resolution = 1500
+    force_doublesided = False
+
+    while sys.argv:
+        if sys.argv[0] == '--resolution':
+            try:
+                resolution = sys.argv[1]
+                sys.argv = sys.argv[2:]
+            except IndexError:
+                sys.stderr.write("Error: No resolution provided.\n")
+                sys.exit(1)
+            try:
+                resolution = int(resolution)
+            except ValueError:
+                sys.stderr.write("Error: Invalid resolution.\n")
+                sys.exit(1)
+
+        elif sys.argv[0] == '--doublesided':
+            force_doublesided = True
+            sys.argv = sys.argv[1:]
+
+        elif len(sys.argv) == 1:
+            break
+    else:
+        sys.stderr.write("Error: No filename provided.\n")
+        sys.exit(1)
+
+    name = sys.argv[0].replace('.brd','')
+    if not os.path.isfile(name+'.brd'):
+        sys.stderr.write("Error: .brd file does not exist.\n")
+        sys.exit(1)
+
+    vias    = name + '.vias.png'
+    cutout  = name + '.cutout.png'
+    top     = name + '.top.png'
+    bottom  = name + '.bottom.png'
+    holes   = name + '.holes.png'
+
+    print "Rendering images."
+    create_images(name, resolution)
+
+    # Check to make sure that imagemagick is installed.
+    if subprocess.call(['which','convert'], stdout = open(os.devnull, 'w')):
+        sys.stderr.write("""Error: 'convert' not found.
+ImageMagick command-line tools must be installed to use eagle_png.""")
+        sys.exit(1)
+
+    print "Processing images."
+
+    # The following command is a set of ImageMagick instructions that
+    # combine all of the images.
+
+    # The following steps take place:
+    #   - Perform a white flood fill on the vias image, starting in the upper
+    #       left corner.  This makes the via image a set of black holes on
+    #       a uniform white background
+    #   - Multiply the vias and cutout images, to cut the via holes from
+    #       the cutout region.
+    #   - Invert the cutout image.
+    #   - Lighten the top and bottom traces with the inverted cutout.  This
+    #       ensures that we don't waste time milling traces in regions that
+    #       will be cut out of the PCB.
+    #   - Subtract the holes image from the original cutout image
+    #   - Save this combined cutout image
+
+    command = [ 'convert',
+                vias, '-fill', 'white', '-draw', 'color 0,0 floodfill',
+                cutout, '-compose', 'Darken', '-composite',
+                '-compose','Lighten',
+                '(',
+                    '+clone',
+                    '-negate'
+              ]
+
+    # If this is a two-sided board, then process the bottom layer
+    if md5(bottom) != md5(vias) or force_doublesided:
+        command += [
+                    '(',
+                        '+clone', bottom, '-composite',
+                        '-flop', '-write', bottom, '+delete',
+                    ')'
+                    ]
+    else:
+        os.remove(bottom)
+
+    # Process the top layer
+    command += [
+                top, '-composite', '-write', top,
+                    '+delete',
+                ')',
+                holes,  '-compose', 'Minus_Src', '-composite', cutout
+                ]
+
+    # Execute this whole mess
+    subprocess.call(command)
+
+    os.remove(vias)
+    os.remove(holes)
+
+    if bottom in command:
+        print "Generated %s, %s, %s." % (top, bottom, cutout)
+    else:
+        print "Generated %s, %s." % (top, cutout)
-- 
GitLab