#------------------------------------------------------------------------------ # # MakePerfTemplate.py - by Don Cross, 24 July 2009. # # This program requires the following software be installed. # (Versions other than the ones below may work, but have not been tested.) # Python 2.6.2 # Python Image Library (PIL) version 1.1.6. # # The following will be helpful to understand the PIL interface: # http://www.pythonware.com/library/pil/handbook/image.htm # http://www.pythonware.com/library/pil/handbook/imagedraw.htm # #------------------------------------------------------------------------------ import os import sys import Image import ImageDraw import ImageFont RGB_DEFAULT = (0xff, 0xff, 0xff) RGB_OUTLINE = (0x00, 0x10, 0xff) RGB_HOLE_OUTLINE = (0xa0, 0xa0, 0xa0) RGB_HOLE_FILL = (0xc0, 0xc0, 0xc0) RGB_TRACE = (0xff, 0xd0, 0xd0) RGB_TEXT = (0x00, 0x00, 0x00) RGB_PUNCHOUT = (0x9f, 0x9f, 0x9f) PUNCHOUT_RADIUS = 9 HOLE_RADIUS = 4 TRACE_DIST = 7 def DrawHoles (pic, holes): for col in holes: for x, y in col: pic.ellipse ([(x-HOLE_RADIUS,y-HOLE_RADIUS), (x+HOLE_RADIUS,y+HOLE_RADIUS)], fill=RGB_HOLE_FILL, outline=RGB_HOLE_OUTLINE) def etchx (hole, i): w = len(hole) if i == 0: x = hole[i][0][0] - TRACE_DIST elif i >= w: x = hole[w-1][0][0] + TRACE_DIST else: x = (hole[i][0][0] + hole[i-1][0][0]) / 2 return x def etchy (hole, j): h = len(hole[0]) if j == 0: y = hole[0][0][1] - TRACE_DIST elif j >= h: y = hole[0][h-1][1] + TRACE_DIST else: y = (hole[0][j][1] + hole[0][j-1][1]) / 2 return y def EtchVer (pic, hole, ij, dist): # Etch through the copper above and to the left of hole[ij] downward for dist squares. i, j = ij xc = etchx (hole, i) y1 = etchy (hole, j) y2 = etchy (hole, j + dist) for x in range(xc-1,xc+2): pic.line ([(x,y1), (x,y2)], fill=RGB_DEFAULT) def EtchHor (pic, hole, ij, dist): i, j = ij yc = etchy (hole, j) x1 = etchx (hole, i) x2 = etchx (hole, i + dist) for y in range(yc-1,yc+2): pic.line ([(x1,y), (x2,y)], fill=RGB_DEFAULT) def FillWithCopper (pic, hole): width = len(hole) height = len(hole[0]) x1c, y1c = hole[0][0] x2c, y2c = hole[width-1][height-1] x1 = x1c - TRACE_DIST y1 = y1c - TRACE_DIST x2 = x2c + TRACE_DIST y2 = y2c + TRACE_DIST pic.rectangle ([(x1,y1), (x2,y2)], outline=RGB_TRACE, fill=RGB_TRACE) def DrawTraces (pic, hole, hole2): # Flood-fill the hole regions with copper color. FillWithCopper (pic, hole) FillWithCopper (pic, hole2) # Etch out the lines by drawing back the default background color. for i in range(1,25): if (i % 5) in [0, 1, 4]: EtchVer (pic, hole, (i,1), 28) for i in range(1,15): EtchVer (pic, hole2, (i,0), 2) for n in range(5): i = 5*n + 1 EtchHor (pic, hole, (i-1, 1), 1) EtchHor (pic, hole, (i+3,29), 1) for j in range(1,30): EtchHor (pic, hole, (i,j), 3) def DrawText (pic, font, text, hole, ij, dx, dy): i, j = ij x, y = hole[i][j] tx, ty = pic.textsize (text, font=font) pic.text ((x+dx-tx/2,y+dy-ty/2), text, fill=RGB_TEXT, font=font) def DrawLettersAndNumbers (pic, hole, hole2): font = ImageFont.truetype ("Arial.ttf", 14) letterDx = 0 letterDy = -19 for n in range(6): i = 24 - 5*n if i < 0: # Note that the 'F' is a special case i = 0 DrawText (pic, font, chr(n+ord("A")), hole, (i,0), letterDx, letterDy) # Note that the actual RadioShack board has inconsistent row numbering: # The labels 5, 10, 15, 20, 25 are all one hole below where they should be, # based on how 1 and 30 are placed. # But because I need to work with this perf board regardless of its mistakes, # I will minimize confusion by making my picture match the real thing. numberDx = 17 numberDy = 0 for n in range(7): j = 5*n if n==0: text = str(j+1) elif n==6: text = str(j) j = j-1 else: text = str(j) DrawText (pic, font, text, hole, (24,j), numberDx, numberDy) subNumberDx = 0 subNumberDy = 19 for n in range(4): if n==0: i = 14 text = "1" else: i = 15 - 5*n text = str(5*n) DrawText (pic, font, text, hole2, (i,1), subNumberDx, subNumberDy) def DrawPunchout (pic, x, y): pic.ellipse ([(x-PUNCHOUT_RADIUS,y-PUNCHOUT_RADIUS), (x+PUNCHOUT_RADIUS,y+PUNCHOUT_RADIUS)], outline=RGB_PUNCHOUT, fill=RGB_PUNCHOUT) def DrawPerfPicture (pic, width, height): HOLES_WIDE = 25 HOLES_HIGH = 30 BASE_WIDTH = 420 BASE_HEIGHT = 560 holeX1 = (width * 37) / BASE_WIDTH holeX2 = (width * 398) / BASE_WIDTH holeY1 = (height * 86) / BASE_HEIGHT holeY2 = (height * 517) / BASE_HEIGHT NUM_HOLE_COLS = 25 NUM_HOLE_ROWS = 30 for x in [(width*53)/BASE_WIDTH, (width*386)/BASE_WIDTH]: for y in [(height*19)/BASE_HEIGHT, (height*540)/BASE_HEIGHT]: DrawPunchout (pic, x, y) # Calculate hole coordinates for the main block of holes... hole = [ [ (holeX1 + ((holeX2 - holeX1) * i) / (NUM_HOLE_COLS - 1), holeY1 + ((holeY2 - holeY1) * j) / (NUM_HOLE_ROWS - 1)) for j in range(NUM_HOLE_ROWS) ] for i in range(NUM_HOLE_COLS) ] # Calculate hole coordinates for the small 15x2 block of holes... hole2 = [ [ (holeX1 + ((holeX2 - holeX1) * i) / (NUM_HOLE_COLS - 1), holeY1 + ((holeY2 - holeY1) * j) / (NUM_HOLE_ROWS - 1)) for j in range(-5, -3) ] for i in range(5, 20) ] DrawTraces (pic, hole, hole2) DrawHoles (pic, hole) DrawHoles (pic, hole2) DrawLettersAndNumbers (pic, hole, hole2) # Draw a box around the whole picture pic.rectangle ([0, 0, width-1, height-1], fill=None, outline=RGB_OUTLINE) def CreatePerfImage (width): # The original photo I am working from has a height/width ratio of about 1.33. # We will generate an image with an 4/3 ratio, rounded to the nearest integer pixel... height = (4 * width) / 3 perf = Image.new ("RGB", (width,height), RGB_DEFAULT) draw = ImageDraw.Draw (perf) DrawPerfPicture (draw, width, height) return perf perf = CreatePerfImage(500) perf.save ("perf_template.png") #---------------------------------------------------------------------------------------- # $Log: MakePerfTemplate.py,v $ # Revision 1.7 2009/07/24 21:12:35 Don.Cross # Looks pretty good now! I have all holes but the 2 singletons, # all the traces and etchings, letters, numbers, and punchouts. # # Revision 1.6 2009/07/24 20:17:44 Don.Cross # Starting to figure out drawing text. # # Revision 1.5 2009/07/24 19:19:03 Don.Cross # Vertical trace etches look correct. # # Revision 1.4 2009/07/24 19:12:58 Don.Cross # Starting to draw traces. # # Revision 1.3 2009/07/24 18:25:55 Don.Cross # Reworked so I calculate all the hole coordinates up front, then use them. # This should help me figure out how to draw the weird trace paths around the holes. # # Revision 1.2 2009/07/24 18:06:39 Don.Cross # I found out I don't need to re-invent the wheel with all the graphics primitives: # they are in another module called ImageDraw. # So now I am drawing all the little holes in the perf board. # # Revision 1.1 2009/07/24 15:41:06 Don.Cross # Starting to generate a graphics file in Python. # #----------------------------------------------------------------------------------------