gerber 0 → 1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -1
- data/README.markdown +42 -0
- data/Rakefile +7 -0
- data/gerber.gemspec +4 -2
- data/lib/gerber.rb +43 -2
- data/lib/gerber/aperture.rb +34 -0
- data/lib/gerber/layer.rb +35 -0
- data/lib/gerber/layer/parser.rb +207 -0
- data/lib/gerber/parser.rb +275 -0
- data/test/gerber.rb +38 -0
- data/test/gerber/example2.gerber +89 -0
- data/test/gerber/hexapod.gerber +1590 -0
- data/test/gerber/layer.rb +6 -0
- data/test/gerber/layer/parser.rb +481 -0
- data/test/gerber/m02_not_at_end.gerber +24 -0
- data/test/gerber/parser.rb +202 -0
- data/test/gerber/sample_4pcb.gerber +23 -0
- data/test/gerber/two_boxes.gerber +19 -0
- data/test/gerber/wikipedia.gerber +23 -0
- metadata +45 -5
@@ -0,0 +1,481 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'gerber/layer/parser'
|
3
|
+
|
4
|
+
class Gerber
|
5
|
+
class Layer
|
6
|
+
class Parser
|
7
|
+
def geometry
|
8
|
+
self.layer.geometry
|
9
|
+
end
|
10
|
+
|
11
|
+
def dcode
|
12
|
+
@dcode
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Gerber::Layer::Parser do
|
19
|
+
Arc = Gerber::Arc
|
20
|
+
Line = Gerber::Line
|
21
|
+
Point = Gerber::Point
|
22
|
+
|
23
|
+
let(:parser) { Gerber::Layer::Parser.new }
|
24
|
+
|
25
|
+
it "must default to absolute mode" do
|
26
|
+
parser.coordinate_mode.must_equal :absolute
|
27
|
+
end
|
28
|
+
|
29
|
+
it "must default to single quadrant mode" do
|
30
|
+
parser.quadrant_mode.must_equal :single
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "when validating Geometry" do
|
34
|
+
it "must accept Arcs" do
|
35
|
+
parser.is_valid_geometry(Arc.new(nil, nil, nil, nil)).must_equal true
|
36
|
+
end
|
37
|
+
|
38
|
+
it "must accept Lines" do
|
39
|
+
parser.is_valid_geometry(Line.new).must_equal true
|
40
|
+
end
|
41
|
+
|
42
|
+
it "must accept Points" do
|
43
|
+
parser.is_valid_geometry(Point[0,0]).must_equal true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "when parsing a D code" do
|
48
|
+
it "must start drawing for D01" do
|
49
|
+
parser.parse_dcode('D01')
|
50
|
+
parser.dcode.must_equal 1
|
51
|
+
end
|
52
|
+
|
53
|
+
it "must stop drawing for D02" do
|
54
|
+
parser.parse_dcode('D02')
|
55
|
+
parser.dcode.must_equal 2
|
56
|
+
end
|
57
|
+
|
58
|
+
it "must flash for D03" do
|
59
|
+
parser.parse_dcode('D03')
|
60
|
+
parser.dcode.must_equal 3
|
61
|
+
end
|
62
|
+
|
63
|
+
it "must set the current aperture number" do
|
64
|
+
original_state = parser.dcode
|
65
|
+
parser.parse_dcode('D11')
|
66
|
+
parser.dcode.must_equal original_state
|
67
|
+
parser.current_aperture.must_equal 11
|
68
|
+
parser.geometry[11].must_be_instance_of(Array)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "when parsing a G code" do
|
73
|
+
it "must raise an exception if the units have not been set" do
|
74
|
+
lambda { parser.parse_gcode(1, 2, 3, 4, 5, 1) }.must_raise Gerber::ParseError
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "when a G74 is parsed" do
|
78
|
+
before do
|
79
|
+
parser.parse_gcode(74)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "must stay in single quadrant mode" do
|
83
|
+
parser.quadrant_mode.must_equal :single
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "when a G75 is parsed" do
|
88
|
+
before do
|
89
|
+
parser.parse_gcode(75)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "must set multi quadrant mode" do
|
93
|
+
parser.quadrant_mode.must_equal :multi
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "when a G74 is parsed" do
|
97
|
+
before do
|
98
|
+
parser.parse_gcode(74)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "must return to single quadrant mode" do
|
102
|
+
parser.quadrant_mode.must_equal :single
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "when G90 is parsed" do
|
108
|
+
before do
|
109
|
+
parser.parse_gcode(90)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "must stay in absolute mode" do
|
113
|
+
parser.coordinate_mode.must_equal :absolute
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "when G91 is parsed" do
|
118
|
+
before do
|
119
|
+
parser.parse_gcode(91)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "must set incremental mode" do
|
123
|
+
parser.coordinate_mode.must_equal :incremental
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "when a G90 is parsed" do
|
127
|
+
before do
|
128
|
+
parser.parse_gcode(90)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "must return to absolute mode" do
|
132
|
+
parser.coordinate_mode.must_equal :absolute
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "when units have been set" do
|
138
|
+
before do
|
139
|
+
parser.parse_gcode(71, nil, nil, nil, nil, nil)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "must raise an exception if an aperture has not been set" do
|
143
|
+
lambda { parser.parse_gcode(1, 2, 3, 4, 5, 1) }.must_raise Gerber::ParseError
|
144
|
+
end
|
145
|
+
|
146
|
+
it "must parse the deprecated G54 and set the current aperture" do
|
147
|
+
parser.parse_gcode(54, nil, nil, nil, nil, 10)
|
148
|
+
parser.current_aperture.must_equal 10
|
149
|
+
parser.geometry[10].must_be_instance_of(Array)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "must reject a G54 without a D code" do
|
153
|
+
skip "Not relevant in the latest version of the file format"
|
154
|
+
lambda { parser.parse_gcode(54, nil, nil, nil, nil, nil) }.must_raise Gerber::ParseError
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "when an aperture has been set" do
|
158
|
+
before do
|
159
|
+
parser.current_aperture = 10
|
160
|
+
end
|
161
|
+
|
162
|
+
it "G1D1 must generate a Line" do
|
163
|
+
parser.parse_gcode(1, 2, 3, 4, 5, 1)
|
164
|
+
parser.geometry.last.last.must_be_kind_of(Geometry::Line)
|
165
|
+
end
|
166
|
+
|
167
|
+
it "G1D2 must move the current position but not generate geometry" do
|
168
|
+
parser.parse_gcode(1, 2, 3, 4, 5, 2)
|
169
|
+
parser.geometry.last.length.must_equal 0
|
170
|
+
parser.instance_variable_get(:@position).must_equal Geometry::Point[2.mm,3.mm]
|
171
|
+
end
|
172
|
+
|
173
|
+
it "G1D3 must generate a Point" do
|
174
|
+
parser.parse_gcode(1, 2, 3, 4, 5, 3)
|
175
|
+
parser.geometry.last.last.must_be_kind_of(Geometry::Point)
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "when circular interpolation" do
|
179
|
+
it "must reject a dcode other than 1 or 2" do
|
180
|
+
skip
|
181
|
+
lambda { parser.parse_gcode(2, 1, 2, 3, 4, 5) }.must_raise Gerber::ParseError
|
182
|
+
lambda { parser.parse_gcode(3, 1, 2, 3, 4, 5) }.must_raise Gerber::ParseError
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "when in absolute mode" do
|
186
|
+
describe "when in single quadrant mode" do
|
187
|
+
before do
|
188
|
+
parser.parse_gcode(74)
|
189
|
+
parser.parse_gcode(1, 11, 6, nil, nil, 2) # Set the current position
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "when parsing four clockwise arcs" do
|
193
|
+
before do
|
194
|
+
parser.parse_gcode(2, 7, 2, 4, 0, 1)
|
195
|
+
parser.parse_gcode(2, 3, 6, 0, 4, nil)
|
196
|
+
parser.parse_gcode(nil, 7, 10, 4, 0, nil)
|
197
|
+
parser.parse_gcode(nil, 11, 6, 0, 4, nil)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "must generate 4 Arcs" do
|
201
|
+
parser.geometry[10].length.must_equal 4
|
202
|
+
parser.geometry[10].all? {|a| a.kind_of?(Geometry::Arc)}.must_equal(true)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "must generate a proper first Arc" do
|
206
|
+
arc = parser.geometry[10].first
|
207
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
208
|
+
arc.first.must_equal Point[7.mm, 2.mm]
|
209
|
+
arc.last.must_equal Point[11.mm, 6.mm]
|
210
|
+
end
|
211
|
+
|
212
|
+
it "must generate a proper second Arc" do
|
213
|
+
arc = parser.geometry[10][1]
|
214
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
215
|
+
arc.first.must_equal Point[3.mm, 6.mm]
|
216
|
+
arc.last.must_equal Point[7.mm, 2.mm]
|
217
|
+
end
|
218
|
+
|
219
|
+
it "must generate a proper third Arc" do
|
220
|
+
arc = parser.geometry[10][2]
|
221
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
222
|
+
arc.first.must_equal Point[7.mm, 10.mm]
|
223
|
+
arc.last.must_equal Point[3.mm, 6.mm]
|
224
|
+
end
|
225
|
+
|
226
|
+
it "must generate a proper fourth Arc" do
|
227
|
+
arc = parser.geometry[10].last
|
228
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
229
|
+
arc.first.must_equal Point[11.mm, 6.mm]
|
230
|
+
arc.last.must_equal Point[7.mm, 10.mm]
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
describe "when parsing a counterclockwise arc" do
|
235
|
+
before do
|
236
|
+
parser.parse_gcode(3, 7, 10, 4, 0, 1)
|
237
|
+
end
|
238
|
+
|
239
|
+
it "must generate an Arc" do
|
240
|
+
parser.geometry[10].last.must_be_kind_of(Geometry::Arc)
|
241
|
+
arc = parser.geometry[10].last
|
242
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
243
|
+
arc.first.must_equal Point[11.mm, 6.mm]
|
244
|
+
arc.last.must_equal Point[7.mm, 10.mm]
|
245
|
+
end
|
246
|
+
|
247
|
+
it "must set the current position to the Arc's end point" do
|
248
|
+
parser.position.must_equal Point[7.mm, 10.mm]
|
249
|
+
end
|
250
|
+
|
251
|
+
describe "when parsing another arc with a modal function number" do
|
252
|
+
before do
|
253
|
+
parser.parse_gcode(nil, 3, 6, 0, 4, nil)
|
254
|
+
end
|
255
|
+
|
256
|
+
it "must generate an Arc" do
|
257
|
+
parser.geometry[10].last.must_be_kind_of(Geometry::Arc)
|
258
|
+
arc = parser.geometry[10].last
|
259
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
260
|
+
arc.first.must_equal Point[7.mm, 10.mm]
|
261
|
+
arc.last.must_equal Point[3.mm, 6.mm]
|
262
|
+
end
|
263
|
+
|
264
|
+
it "must set the current position to the Arc's end point" do
|
265
|
+
parser.position.must_equal Point[3.mm, 6.mm]
|
266
|
+
end
|
267
|
+
|
268
|
+
describe "when parsing a third arc" do
|
269
|
+
before do
|
270
|
+
parser.parse_gcode(nil, 7, 2, 4, 0, nil)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "must generate an Arc" do
|
274
|
+
parser.geometry[10].last.must_be_kind_of(Geometry::Arc)
|
275
|
+
arc = parser.geometry[10].last
|
276
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
277
|
+
arc.first.must_equal Point[3.mm, 6.mm]
|
278
|
+
arc.last.must_equal Point[7.mm, 2.mm]
|
279
|
+
end
|
280
|
+
|
281
|
+
it "must set the current position to the Arc's end point" do
|
282
|
+
parser.position.must_equal Point[7.mm, 2.mm]
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "when parsing the final arc in the circle" do
|
286
|
+
before do
|
287
|
+
parser.parse_gcode(nil, 11, 6, 0, 4, nil)
|
288
|
+
end
|
289
|
+
|
290
|
+
it "must generate an Arc" do
|
291
|
+
parser.geometry[10].last.must_be_kind_of(Geometry::Arc)
|
292
|
+
arc = parser.geometry[10].last
|
293
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
294
|
+
arc.first.must_equal Point[7.mm, 2.mm]
|
295
|
+
arc.last.must_equal Point[11.mm, 6.mm]
|
296
|
+
end
|
297
|
+
|
298
|
+
it "must set the current position to the Arc's end point" do
|
299
|
+
parser.position.must_equal Point[11.mm, 6.mm]
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
describe "when in multi quadrant mode" do
|
308
|
+
before do
|
309
|
+
parser.parse_gcode(75)
|
310
|
+
parser.parse_gcode(1, 3, -2, nil, nil, 2) # Set the current position
|
311
|
+
end
|
312
|
+
|
313
|
+
describe "when parsing a clockwise arc" do
|
314
|
+
before do
|
315
|
+
parser.parse_gcode(2, -3, -2, -3, 4, 1)
|
316
|
+
end
|
317
|
+
|
318
|
+
it "must generate an Arc" do
|
319
|
+
parser.geometry[10].last.must_be_kind_of(Geometry::Arc)
|
320
|
+
arc = parser.geometry[10].last
|
321
|
+
arc.center.must_equal Point[0.mm, 2.mm]
|
322
|
+
arc.first.must_equal Point[-3.mm, -2.mm]
|
323
|
+
arc.last.must_equal Point[3.mm, -2.mm]
|
324
|
+
end
|
325
|
+
|
326
|
+
it "must set the current position to the endpoint of the Arc" do
|
327
|
+
parser.position.must_equal Point[-3.mm, -2.mm]
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
describe "when parsing a counterclockwise arc" do
|
332
|
+
before do
|
333
|
+
parser.parse_gcode(3, -3, -2, -3, 4, 1)
|
334
|
+
end
|
335
|
+
|
336
|
+
it "must generate an Arc" do
|
337
|
+
parser.geometry[10].last.must_be_kind_of(Geometry::Arc)
|
338
|
+
arc = parser.geometry[10].last
|
339
|
+
arc.center.must_equal Point[0.mm, 2.mm]
|
340
|
+
arc.first.must_equal Point[3.mm, -2.mm]
|
341
|
+
arc.last.must_equal Point[-3.mm, -2.mm]
|
342
|
+
end
|
343
|
+
|
344
|
+
it "must set the current position to the endpoint of the Arc" do
|
345
|
+
parser.position.must_equal Point[-3.mm, -2.mm]
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
describe "when in incremental mode" do
|
352
|
+
before do
|
353
|
+
parser.parse_gcode(91)
|
354
|
+
end
|
355
|
+
|
356
|
+
describe "when in single quadrant mode" do
|
357
|
+
before do
|
358
|
+
parser.parse_gcode(74)
|
359
|
+
parser.parse_gcode(1, 11, 6, nil, nil, 2) # Set the current position
|
360
|
+
end
|
361
|
+
|
362
|
+
describe "when parsing four clockwise arcs" do
|
363
|
+
before do
|
364
|
+
parser.parse_gcode(2, -4, -4, 4, 0, 1)
|
365
|
+
parser.parse_gcode(2, -4, 4, 0, 4, nil)
|
366
|
+
parser.parse_gcode(nil, 4, 4, 4, 0, nil)
|
367
|
+
parser.parse_gcode(nil, 4, -4, 0, 4, nil)
|
368
|
+
end
|
369
|
+
|
370
|
+
it "must generate 4 Arcs" do
|
371
|
+
parser.geometry[10].length.must_equal 4
|
372
|
+
parser.geometry[10].all? {|a| a.kind_of?(Geometry::Arc)}.must_equal(true)
|
373
|
+
end
|
374
|
+
|
375
|
+
it "must generate a proper first Arc" do
|
376
|
+
arc = parser.geometry[10].first
|
377
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
378
|
+
arc.first.must_equal Point[7.mm, 2.mm]
|
379
|
+
arc.last.must_equal Point[11.mm, 6.mm]
|
380
|
+
end
|
381
|
+
|
382
|
+
it "must generate a proper second Arc" do
|
383
|
+
arc = parser.geometry[10][1]
|
384
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
385
|
+
arc.first.must_equal Point[3.mm, 6.mm]
|
386
|
+
arc.last.must_equal Point[7.mm, 2.mm]
|
387
|
+
end
|
388
|
+
|
389
|
+
it "must generate a proper third Arc" do
|
390
|
+
arc = parser.geometry[10][2]
|
391
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
392
|
+
arc.first.must_equal Point[7.mm, 10.mm]
|
393
|
+
arc.last.must_equal Point[3.mm, 6.mm]
|
394
|
+
end
|
395
|
+
|
396
|
+
it "must generate a proper fourth Arc" do
|
397
|
+
arc = parser.geometry[10].last
|
398
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
399
|
+
arc.first.must_equal Point[11.mm, 6.mm]
|
400
|
+
arc.last.must_equal Point[7.mm, 10.mm]
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
describe "when parsing a counterclockwise arc" do
|
405
|
+
before do
|
406
|
+
parser.parse_gcode(3, -4, 4, 4, 0, 1)
|
407
|
+
end
|
408
|
+
|
409
|
+
it "must generate an Arc" do
|
410
|
+
parser.geometry[10].last.must_be_kind_of(Geometry::Arc)
|
411
|
+
arc = parser.geometry[10].last
|
412
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
413
|
+
arc.first.must_equal Point[11.mm, 6.mm]
|
414
|
+
arc.last.must_equal Point[7.mm, 10.mm]
|
415
|
+
end
|
416
|
+
|
417
|
+
it "must set the current position to the Arc's end point" do
|
418
|
+
parser.position.must_equal Point[7.mm, 10.mm]
|
419
|
+
end
|
420
|
+
|
421
|
+
describe "when parsing another arc with a modal function number" do
|
422
|
+
before do
|
423
|
+
parser.parse_gcode(nil, -4, -4, 0, 4, nil)
|
424
|
+
end
|
425
|
+
|
426
|
+
it "must generate an Arc" do
|
427
|
+
parser.geometry[10].last.must_be_kind_of(Geometry::Arc)
|
428
|
+
arc = parser.geometry[10].last
|
429
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
430
|
+
arc.first.must_equal Point[7.mm, 10.mm]
|
431
|
+
arc.last.must_equal Point[3.mm, 6.mm]
|
432
|
+
end
|
433
|
+
|
434
|
+
it "must set the current position to the Arc's end point" do
|
435
|
+
parser.position.must_equal Point[3.mm, 6.mm]
|
436
|
+
end
|
437
|
+
|
438
|
+
describe "when parsing a third arc" do
|
439
|
+
before do
|
440
|
+
parser.parse_gcode(nil, 4, -4, 4, 0, nil)
|
441
|
+
end
|
442
|
+
|
443
|
+
it "must generate an Arc" do
|
444
|
+
parser.geometry[10].last.must_be_kind_of(Geometry::Arc)
|
445
|
+
arc = parser.geometry[10].last
|
446
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
447
|
+
arc.first.must_equal Point[3.mm, 6.mm]
|
448
|
+
arc.last.must_equal Point[7.mm, 2.mm]
|
449
|
+
end
|
450
|
+
|
451
|
+
it "must set the current position to the Arc's end point" do
|
452
|
+
parser.position.must_equal Point[7.mm, 2.mm]
|
453
|
+
end
|
454
|
+
|
455
|
+
describe "when parsing the final arc in the circle" do
|
456
|
+
before do
|
457
|
+
parser.parse_gcode(nil, 4, 4, 0, 4, nil)
|
458
|
+
end
|
459
|
+
|
460
|
+
it "must generate an Arc" do
|
461
|
+
parser.geometry[10].last.must_be_kind_of(Geometry::Arc)
|
462
|
+
arc = parser.geometry[10].last
|
463
|
+
arc.center.must_equal Point[7.mm, 6.mm]
|
464
|
+
arc.first.must_equal Point[7.mm, 2.mm]
|
465
|
+
arc.last.must_equal Point[11.mm, 6.mm]
|
466
|
+
end
|
467
|
+
|
468
|
+
it "must set the current position to the Arc's end point" do
|
469
|
+
parser.position.must_equal Point[11.mm, 6.mm]
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|