-
Notifications
You must be signed in to change notification settings - Fork 8
/
plot_test.py
executable file
·244 lines (184 loc) · 7.25 KB
/
plot_test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#!/usr/bin/python
import os
from optparse import OptionParser
import cairo._cairo as cairo
import gerberDRC as GD
import gerberDRC.util as GU
import math
import plot_modes
usage = "usage: %prog [options] input_dir output_file"
o = OptionParser(usage=usage)
o.add_option("-d", "--debug-level",
type="int", dest="debuglevel", default=1,
help="Debug Level; 0-4, 4 being most verbose")
o.add_option("-m", "--render-mode",
type="str", dest="rendermode", default="REALISTIC_TOP",
help="one of %s" % ", ".join(plot_modes.modes))
o.add_option("--bounds-artwork",
dest="bounds_artwork", default=False, action="store_true",
help="calculate bounds from all artwork, using all segments and layers unless -0 or --visible-only specified.")
o.add_option("-0", "--hairlines-only",
dest="size0", default=False, action="store_true",
help="use only zero width lines for calculating bounding rect\n"
"Only applies in conjunction with --bounds-artwork")
o.add_option("--visible-only",
dest="bounding_from_visible_only", default=False, action="store_true",
help="use only visible layers for calculating bounding rect. Only applies in conjunction with --bounds-artwork")
o.add_option("--fec", "--fallback-exc-stripped",
dest="fec", default="LEADING",
help="Fallback incase the stripping of leading/trailing zeros could not be determined. Default is LEADING zeros stripped")
o.add_option("--son0", "--search-outline-nonzero",
dest="search_outline_nonzero", default=False, action="store_true", help="Also look at non-zero lines when determining board outline [WARNING, SLOW]")
(options, args) = o.parse_args()
if (len(args) != 2):
o.error("incorrect number of arguments")
exit(1)
if not options.rendermode in plot_modes.modes:
o.error("MODE must be one of %s" % ", ".join(plot_modes.modes))
exit(1)
path, outputfilename = args
mode = options.rendermode
GD.setDebugLevel(GD.debug_level_t(options.debuglevel))
plotmode = plot_modes.getPlotSettings(options.rendermode)
def createCairoLineCenterLinePath(obj, cr):
cr.move_to (obj.sx, obj.sy);
cr.line_to (obj.ex, obj.ey);
def renderGerberFile(rep, cr, layer, outlines):
ps = plotmode.getPlotSettings(layer)
cr.push_group()
if not ps.drawinverted:
cr.set_operator(cairo.OPERATOR_OVER)
else:
cr.set_operator(cairo.OPERATOR_OVER)
cr.set_source_rgba(ps.ovr, ps.ovg, ps.ovb, 1)
if outlines:
for i in outlines:
cr.move_to(i[0][0], i[0][1])
for x,y,_ in i[1:]:
cr.line_to(x,y)
cr.close_path()
cr.fill()
else:
cr.paint()
cr.set_operator(cairo.OPERATOR_DEST_OUT)
cr.set_source_rgba(ps.ovr, ps.ovg, ps.ovb, 1)
for l in rep.layers:
for k in l.draws:
if isinstance(k, GD.GerbObj_Line) and (k.width == 0) and ps.strokeZeroWidthLines:
createCairoLineCenterLinePath(k,cr)
cr.stroke()
else:
GD.emitGerbObjectCairoPath(cr, k)
if (ps.drawfilled):
cr.fill()
else:
cr.stroke()
cr.pop_group_to_source()
cr.paint_with_alpha(ps.alpha)
cr.set_operator(ps.renderOperator)
path = os.path.normpath(path) + "/"
# Load all layers from the directory
layers = {}
for i in os.listdir(path):
print "Parsing: %s" % i
identification = GU.identifyLayer(i)
if not identification:
print "Could not identify type of %s - skipping." % i
continue
fmt, layer = identification
if (fmt == 'RS274X'):
f = GD.parseFile(path+i)
if (not f):
print "Could not parse %s" % path+i
continue
p = GD.runRS274XProgram(f)
if (not f):
print "Could not create polygons for %s" % path+i
continue
layers[layer] = p
elif (fmt == 'EXCELLON'):
f = GD.parseExcellon(path + i, options.fec)
# Fail gracefully if the excellon file could not be parsed
if (f):
layers[layer] = f
else:
print "Can't handle file type %s" % fmt
continue
render_order = plotmode.getRenderOrder()
# Calculate the outline path used for drawing the board substrate and calculating
# board coordinates
outline_paths = []
if "MILLING" in layers:
t = layers["MILLING"];
outline_line_list = [k for k in t.all if isinstance(k, GD.GerbObj_Line)]
outline_paths = GU.buildCyclePathsForLineSegments(outline_line_list)
elif "COPPER_TOP" in layers:
t = layers["COPPER_TOP"];
outline_line_list = []
for t_l in t.layers:
if options.search_outline_nonzero:
outline_line_list += [k for k in t_l.draws if isinstance(k, GD.GerbObj_Line)]
else:
outline_line_list += [k for k in t_l.draws if isinstance(k, GD.GerbObj_Line) and k.width == 0]
outline_paths = GU.buildCyclePathsForLineSegments(outline_line_list)
# Calculate a bounding rectangle using either all visible objects, or only zero-width lines
check_layers = [v for k,v in layers.items() if not k.startswith("DRILL")]
if (options.bounding_from_visible_only):
check_layers = [v for k,v in layers.items() if k in render_order and not k.startswith("DRILL")]
artwork_bounds = GU.calculateBoundingRectFromPCBLayers(check_layers, options.size0)
# Calculate the board area, for use in setting up the image / image transform
if outline_paths and not options.bounds_artwork:
srcrect = GU.calculateBoundingRectFromOutlines(outline_paths)
srcrect_sane = True
copper_layers = [v for k,v in layers.iteritems() if k.startswith("COPPER")]
copper_bounds = GU.calculateBoundingRectFromPCBLayers(copper_layers)
# Check if we have artwork outside the source rectangle
if srcrect.getStartPoint().x > copper_bounds.getStartPoint().x or \
srcrect.getStartPoint().y > copper_bounds.getStartPoint().y or \
srcrect.getEndPoint().x < copper_bounds.getEndPoint().x or \
srcrect.getEndPoint().y < copper_bounds.getEndPoint().y:
srcrect_sane = False
if not srcrect_sane:
print "Warning, copper bounds extend outside of outline-calculated-rectangle," +\
"falling back to default 'rectangular' board based on bounds-rect"
srcrect = copper_bounds
outline_paths = [[
(srcrect.getStartPoint().x, srcrect.getStartPoint().y, 0),
(srcrect.getStartPoint().x, srcrect.getEndPoint().y, 0),
(srcrect.getEndPoint().x, srcrect.getEndPoint().y, 0),
(srcrect.getEndPoint().x, srcrect.getStartPoint().y, 0)
]]
if not outline_paths or options.bounds_artwork:
# No outline path found, or bounds were supposed to be found from all artwork
# so use all artwork except for unscaled drill layer and determine coords
srcrect = artwork_bounds
# Try to guess-fit the drill layer
if "DRILL" in layers:
layers["DRILL"].scaleToFit((srcrect.getEndPoint().x, srcrect.getEndPoint().y))
# Calculate the image size and transform
(width, height), transform = GD.prepareCairoTransform(1024, srcrect, pad = 50, trim_to_ratio = True,
**plotmode.getTransformArgs())
# Prepare a surface to render onto
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
cr = cairo.Context(surface)
# Apply the transform to the context
transform(cr)
# Setup the background
plotmode.renderBackground(cr, outline_paths)
for i in reversed(render_order):
try:
t = layers[i];
except KeyError:
continue
print "Rendering %s" % i
renderGerberFile(t, cr, i, outline_paths)
if "DRILL" in layers:
print "Rendering Drills"
cr.set_operator(cairo.OPERATOR_OVER)
cr.set_source_rgba(0,0,0,1);
for tool,x,y in layers["DRILL"].hits:
radius = layers["DRILL"].rack.rack[tool]
cr.arc(x,y,radius/2,0,math.pi * 2)
cr.close_path()
cr.fill()
surface.write_to_png(open(outputfilename, 'w'))