eaglecad 0 → 1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/Gemfile +5 -1
  2. data/README.markdown +29 -0
  3. data/Rakefile +9 -0
  4. data/eaglecad.gemspec +4 -2
  5. data/lib/eaglecad.rb +15 -1
  6. data/lib/eaglecad/attribute.rb +47 -0
  7. data/lib/eaglecad/board.rb +184 -0
  8. data/lib/eaglecad/clearance.rb +31 -0
  9. data/lib/eaglecad/design_rules.rb +38 -0
  10. data/lib/eaglecad/deviceset.rb +113 -0
  11. data/lib/eaglecad/drawing.rb +124 -0
  12. data/lib/eaglecad/geometry.rb +259 -0
  13. data/lib/eaglecad/layer.rb +40 -0
  14. data/lib/eaglecad/library.rb +73 -0
  15. data/lib/eaglecad/package.rb +74 -0
  16. data/lib/eaglecad/part.rb +29 -0
  17. data/lib/eaglecad/schematic.rb +83 -0
  18. data/lib/eaglecad/sheet.rb +256 -0
  19. data/lib/eaglecad/symbol.rb +73 -0
  20. data/test/eaglecad.rb +12 -0
  21. data/test/eaglecad/board.rb +68 -0
  22. data/test/eaglecad/design_rules.rb +38 -0
  23. data/test/eaglecad/deviceset.rb +52 -0
  24. data/test/eaglecad/drawing.rb +94 -0
  25. data/test/eaglecad/library.rb +47 -0
  26. data/test/eaglecad/package.rb +60 -0
  27. data/test/eaglecad/schematic.rb +50 -0
  28. data/test/eaglecad/sheet.rb +24 -0
  29. data/test/eaglecad/symbol.rb +41 -0
  30. data/test/fixtures/board.xml +859 -0
  31. data/test/fixtures/demo1.sch +10932 -0
  32. data/test/fixtures/design_rules.xml +70 -0
  33. data/test/fixtures/deviceset.xml +683 -0
  34. data/test/fixtures/drawing_board.xml +910 -0
  35. data/test/fixtures/drawing_schematic.xml +10928 -0
  36. data/test/fixtures/hexapodu.brd +1863 -0
  37. data/test/fixtures/library.xml +175 -0
  38. data/test/fixtures/package.xml +28 -0
  39. data/test/fixtures/schematic.xml +10861 -0
  40. data/test/fixtures/sheet.xml +318 -0
  41. data/test/fixtures/symbol.xml +11 -0
  42. metadata +79 -5
  43. data/README.md +0 -29
@@ -0,0 +1,74 @@
1
+ require 'rexml/document'
2
+
3
+ require_relative 'geometry'
4
+
5
+ module EagleCAD
6
+ Point = ::Geometry::Point
7
+ Size = ::Geometry::Size
8
+
9
+ class Package
10
+ attr_accessor :name, :description
11
+ attr_reader :holes, :layers, :pads
12
+
13
+ # Create a new {Package} from an {REXML::Element}
14
+ # @param [REXML::Element] element The {REXML::Element} to parse
15
+ def self.from_xml(element)
16
+ package = Package.new element.attributes['name']
17
+
18
+ element.elements.each do |element|
19
+ layer_number = element.attributes['layer'].to_i if element.attributes.has_key?('layer')
20
+ case element.name
21
+ when 'description'
22
+ package.description = element.text
23
+ when 'hole'
24
+ package.holes.push Geometry::Hole.from_xml(element)
25
+ when 'pad'
26
+ package.pads.push Geometry::Pad.from_xml(element)
27
+ else
28
+ g = Geometry.from_xml(element)
29
+ if g
30
+ package.push layer_number, g
31
+ else
32
+ raise StandardError, "Unrecognized package element '#{element.name}'"
33
+ end
34
+ end
35
+ end
36
+
37
+ package
38
+ end
39
+
40
+ # @param [String] name The name of the {Package}
41
+ def initialize(name)
42
+ @holes = []
43
+ @layers = {}
44
+ @layers.default_proc = proc {|hash, key| hash[key] = []}
45
+ @name = name
46
+ @pads = []
47
+ end
48
+
49
+ # Push a new element to the given layer number
50
+ # @param [Numeric] layer_number The layer to add the element to
51
+ # @param [Object] element The thing to push
52
+ def push(layer_number, element)
53
+ layer = @layers[layer_number]
54
+ layer.push element
55
+ end
56
+
57
+ # Generate XML for the {Package} element
58
+ # @return [REXML::Element]
59
+ def to_xml
60
+ REXML::Element.new('package').tap do |element|
61
+ element.add_attribute('name', name)
62
+ element.add_element('description').text = description
63
+
64
+ holes.each {|hole| element.add_element hole.to_xml }
65
+ pads.each {|pad| element.add_element pad.to_xml }
66
+
67
+ layers.each do |number, layer|
68
+ layer.each {|obj| element.add_element(obj.to_xml, {'layer' => number}) }
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,29 @@
1
+ require 'rexml/document'
2
+
3
+ module EagleCAD
4
+ class Part
5
+ attr_accessor :name, :library, :deviceset, :device, :technology, :value
6
+
7
+ def self.from_xml(element)
8
+ Part.new(element.attributes['name'], element.attributes['library'], element.attributes['deviceset'], element.attributes['device']).tap do |part|
9
+ part.technology = element.attributes['technology'] if element.attributes['technology']
10
+ part.value = element.attributes['value'] if element.attributes['value']
11
+ end
12
+ end
13
+
14
+ def initialize(name, library, deviceset, device)
15
+ @name = name
16
+ @library = library
17
+ @deviceset = deviceset
18
+ @device = device
19
+ @technology = ''
20
+ end
21
+
22
+ def to_xml
23
+ REXML::Element.new('part').tap do |element|
24
+ element.add_attributes({'name' => name, 'library' => library, 'deviceset' => deviceset, 'device' => device, 'technology' => technology})
25
+ element.add_attribute('value', value) if value
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,83 @@
1
+ require 'rexml/document'
2
+
3
+ require_relative 'attribute'
4
+ require_relative 'clearance'
5
+ require_relative 'geometry'
6
+ require_relative 'library'
7
+ require_relative 'part'
8
+ require_relative 'sheet'
9
+
10
+ module EagleCAD
11
+ class Schematic
12
+ attr_accessor :description
13
+ attr_reader :attributes, :classes, :libraries, :parts, :sheets
14
+
15
+ # Create a new {Schematic} from an {REXML::Element}
16
+ # @param [REXML::Element] element The {REXML::Element} to parse
17
+ def self.from_xml(element)
18
+ Schematic.new.tap do |schematic|
19
+ element.elements.each do |element|
20
+ case element.name
21
+ when 'attributes'
22
+ element.elements.each {|attribute| schematic.attributes.push Attribute.from_xml(attribute) }
23
+ when 'classes'
24
+ element.elements.each {|clearance| schematic.classes.push Clearance.from_xml(clearance) }
25
+ when 'description'
26
+ schematic.description = element.text
27
+ when 'libraries'
28
+ element.elements.each {|library| schematic.libraries[library.attributes['name']] = Library.from_xml(library) }
29
+ when 'parts'
30
+ element.elements.each {|part| schematic.parts.push Part.from_xml(part) }
31
+ when 'sheets'
32
+ element.elements.each {|sheet| schematic.sheets.push Sheet.from_xml(sheet) }
33
+ when 'variantdefs'
34
+ else
35
+ raise StandardError, "Unrecognized element '#{element.name}'"
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def initialize
42
+ @attributes = []
43
+ @classes = []
44
+ @libraries = {}
45
+ @parts = []
46
+ @sheets = []
47
+ end
48
+
49
+ # Generate XML for the {Schematic} element
50
+ # @return [REXML::Element]
51
+ def to_xml
52
+ REXML::Element.new('schematic').tap do |element|
53
+ element.add_element('description').text = description if description
54
+
55
+ # Libraries must be output before parts or Eagle will fail to load the file
56
+ element.add_element('libraries').tap do |libraries_element|
57
+ libraries.each do |name, library|
58
+ libraries_element.add_element library.to_xml
59
+ end
60
+ end
61
+
62
+ REXML::Element.new('attributes').tap do |attributes_element|
63
+ attributes.each {|attribute| attributes_element.add_element attribute.to_xml }
64
+ element.add_element(attributes_element) if attributes_element.has_elements?
65
+ end
66
+
67
+ element.add_element('variantdefs')
68
+
69
+ element.add_element('classes').tap do |classes_element|
70
+ classes.each {|object| classes_element.add_element object.to_xml }
71
+ end
72
+
73
+ element.add_element('parts').tap do |parts_element|
74
+ parts.each {|part| parts_element.add_element part.to_xml }
75
+ end
76
+
77
+ element.add_element('sheets').tap do |sheets_element|
78
+ sheets.each {|sheet| sheets_element.add_element sheet.to_xml }
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,256 @@
1
+ require_relative 'attribute'
2
+ require_relative 'geometry'
3
+
4
+ module EagleCAD
5
+ class Sheet
6
+ attr_accessor :description
7
+ attr_reader :busses, :instances, :nets
8
+
9
+ PinReference = Struct.new :part, :gate, :pin do
10
+ def self.from_xml(element)
11
+ Sheet::PinReference.new element.attributes['part'], element.attributes['gate'], element.attributes['pin']
12
+ end
13
+
14
+ def to_xml
15
+ REXML::Element.new('pinref').tap do |element|
16
+ element.add_attributes('gate' => gate, 'part' => part, 'pin' => pin)
17
+ end
18
+ end
19
+ end
20
+
21
+ class Bus
22
+ attr_accessor :name
23
+ attr_reader :segments
24
+
25
+ def self.from_xml(element)
26
+ Bus.new(element.attributes['name']).tap do |bus|
27
+ element.elements.each {|segment| bus.segments.push Segment.from_xml(segment) }
28
+ end
29
+ end
30
+
31
+ def initialize(name)
32
+ @name = name
33
+ @segments = []
34
+ end
35
+
36
+ def to_xml
37
+ REXML::Element.new('bus').tap do |element|
38
+ element.add_attribute 'name', name
39
+ segments.each {|segment| element.add_element segment.to_xml }
40
+ end
41
+ end
42
+ end
43
+
44
+ class Instance
45
+ attr_accessor :part, :gate, :origin, :smashed, :rotation
46
+ attr_reader :attributes
47
+
48
+ def self.from_xml(element)
49
+ Instance.new(element.attributes['part'], element.attributes['gate'], Geometry.point_from(element)).tap do |instance|
50
+ element.attributes.each do |name, value|
51
+ case name
52
+ when 'smashed' then instance.smashed = ('no' != element.attributes['smashed'])
53
+ when 'rot' then instance.rotation = element.attributes['rot']
54
+ when 'part', 'gate', 'x', 'y' # Ignore; already handled
55
+ else
56
+ raise StandardError, "Unrecognized Instance attribute '#{name}'"
57
+ end
58
+ end
59
+
60
+ element.elements.each {|attribute| instance.attributes.push Attribute.from_xml(attribute) }
61
+ end
62
+ end
63
+
64
+ def initialize(part, gate, origin)
65
+ @attributes = []
66
+ @part = part
67
+ @gate = gate
68
+ @origin = origin
69
+ @smashed = false
70
+ @rotation = 'R0'
71
+ end
72
+
73
+ def to_xml
74
+ REXML::Element.new('instance').tap do |element|
75
+ element.add_attributes({'part' => part, 'gate' => gate, 'x' => origin.x, 'y' => origin.y})
76
+ element.add_attribute('smashed', 'yes') if smashed
77
+ element.add_attribute('rot', rotation)
78
+
79
+ attributes.each {|attribute| element.add_element attribute.to_xml }
80
+ end
81
+ end
82
+ end
83
+
84
+ class Label
85
+ attr_accessor :origin, :size, :layer_number, :font, :ratio, :rotation, :cross_reference
86
+
87
+ def self.from_xml(element)
88
+ Label.new(Geometry.point_from(element), element.attributes['size'].to_f, element.attributes['layer'].to_i).tap do |label|
89
+ element.attributes.each do |name, value|
90
+ case name
91
+ when 'font' then label.font = value.to_sym
92
+ when 'ratio' then label.ratio = value.to_i
93
+ when 'rot' then label.rotation = value
94
+ when 'xref' then label.cross_reference = ('no' != value)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def initialize(origin, size, layer_number)
101
+ @origin = origin
102
+ @size = size
103
+ @layer_number = layer_number
104
+ @font = :proportional
105
+ @ratio = 8
106
+ @rotation = 'R0'
107
+ @cross_reference = false
108
+ end
109
+
110
+ def to_xml
111
+ REXML::Element.new('label').tap do |element|
112
+ element.add_attributes({'x' => origin.x, 'y' => origin.y, 'layer' => layer_number ,'size' => size})
113
+ element.add_attribute('font', font)
114
+ element.add_attribute('ratio', ratio) unless 8 == ratio
115
+ element.add_attribute('rot', rotation)
116
+ element.add_attribute('xref', cross_reference) if cross_reference
117
+ end
118
+ end
119
+ end
120
+
121
+ class Net
122
+ attr_accessor :clearance_class, :name
123
+ attr_reader :segments
124
+
125
+ def self.from_xml(element)
126
+ Net.new(element.attributes['name'], element.attributes['class'].to_i).tap do |net|
127
+ element.elements.each {|segment| net.segments.push Segment.from_xml(segment) }
128
+ end
129
+ end
130
+
131
+ def initialize(name, clearance_class)
132
+ @clearance_class = clearance_class
133
+ @name = name
134
+ @segments = []
135
+ end
136
+
137
+ def to_xml
138
+ REXML::Element.new('net').tap do |element|
139
+ element.add_attribute('name', name)
140
+ element.add_attribute('class', clearance_class) unless 0 == clearance_class
141
+
142
+ segments.each {|segment| element.add_element segment.to_xml }
143
+ end
144
+ end
145
+ end
146
+
147
+ class Segment
148
+ attr_reader :elements, :layers
149
+
150
+ def self.from_xml(element)
151
+ Segment.new.tap do |segment|
152
+ element.elements.each do |element|
153
+ case element.name
154
+ when 'junction'
155
+ segment.elements.push Geometry.point_from(element)
156
+ when 'label'
157
+ segment.push element.attributes['layer'], Label.from_xml(element)
158
+ when 'pinref'
159
+ segment.elements.push PinReference.from_xml(element)
160
+ when 'wire'
161
+ segment.push element.attributes['layer'], Geometry::Line.from_xml(element)
162
+ else
163
+ raise StandardError, "Unrecognized Segment element '#{element.name}"
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ def initialize
170
+ @elements = []
171
+ @layers = {}
172
+ @layers.default_proc = proc {|hash, key| hash[key] = []}
173
+ end
174
+
175
+ # Push a new element to the given layer number
176
+ # @param [Numeric] layer_number The layer to add the element to
177
+ # @param [Object] element The thing to push
178
+ def push(layer_number, element)
179
+ layer = @layers[layer_number]
180
+ layer.push element
181
+ end
182
+
183
+ def to_xml
184
+ REXML::Element.new('segment').tap do |element|
185
+ elements.each do |object|
186
+ if object.is_a? Point
187
+ element.add_element('junction', {'x' => object.x, 'y' => object.y})
188
+ else
189
+ element.add_element object.to_xml
190
+ end
191
+ end
192
+
193
+ layers.each do |number, layer|
194
+ layer.each {|obj| element.add_element(obj.to_xml, {'layer' => number}) }
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ def self.from_xml(element)
201
+ Sheet.new.tap do |sheet|
202
+ element.elements.each do |element|
203
+ case element.name
204
+ when 'busses'
205
+ element.elements.each {|bus| sheet.push Bus.from_xml(bus) }
206
+ when 'description'
207
+ sheet.description = element.text
208
+ when 'instances'
209
+ element.elements.each {|instance| sheet.push Instance.from_xml(instance) }
210
+ when 'nets'
211
+ element.elements.each {|part| sheet.push Net.from_xml(part) }
212
+ when 'plain' #Ignore
213
+ else
214
+ raise StandardError, "Unrecognized Sheet element '#{element.name}'"
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ def initialize
221
+ @busses = []
222
+ @instances = []
223
+ @nets = []
224
+ @parts = []
225
+ end
226
+
227
+ # Add the passed {Sheet} element to the {Sheet}
228
+ def push(arg)
229
+ case arg
230
+ when Bus then busses.push arg
231
+ when Instance then instances.push arg
232
+ when Net then nets.push arg
233
+ else
234
+ raise ArgumentError, "Unrecognized object '#{arg.class}'"
235
+ end
236
+ end
237
+
238
+ def to_xml
239
+ REXML::Element.new('sheet').tap do |element|
240
+ element.add_element('description').text = description
241
+
242
+ element.add_element('instances').tap do |instances_element|
243
+ instances.each {|instance| instances_element.add_element instance.to_xml }
244
+ end
245
+
246
+ element.add_element('busses').tap do |busses_element|
247
+ busses.each {|bus| busses_element.add_element bus.to_xml }
248
+ end
249
+
250
+ element.add_element('nets').tap do |nets_element|
251
+ nets.each {|net| nets_element.add_element net.to_xml }
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,73 @@
1
+ require 'rexml/document'
2
+
3
+ require_relative 'geometry'
4
+
5
+ module EagleCAD
6
+ class Symbol
7
+ attr_accessor :name, :description
8
+ attr_reader :layers, :pins
9
+
10
+ # Create a new {Symbol} from an {REXML::Element}
11
+ # @param [Element] element The {REXML::Element} to parse
12
+ def self.from_xml(element)
13
+ symbol = Symbol.new element.attributes['name']
14
+
15
+ element.elements.each do |element|
16
+ layer_number = element.attributes['layer'].to_i if element.attributes.has_key?('layer')
17
+ case element.name
18
+ when 'circle'
19
+ symbol.push layer_number, Geometry::Circle.from_xml(element)
20
+ when 'description'
21
+ symbol.description = element.text
22
+ when 'pin'
23
+ symbol.pins.push Geometry::Pin.from_xml(element)
24
+ when 'polygon'
25
+ symbol.push layer_number, Geometry::Polygon.from_xml(element)
26
+ when 'rectangle'
27
+ symbol.push layer_number, Geometry::Rectangle.from_xml(element)
28
+ when 'smd'
29
+ symbol.push layer_number, Geometry::SMD.from_xml(element)
30
+ when 'text'
31
+ symbol.push layer_number, Geometry::Text.from_xml(element)
32
+ when 'wire'
33
+ symbol.push layer_number, Geometry::Line.from_xml(element)
34
+ else
35
+ raise StandardError, "Unrecognized symbol element '#{element.name}'"
36
+ end
37
+ end
38
+
39
+ symbol
40
+ end
41
+
42
+ # @param [String] name The name of the {Package}
43
+ def initialize(name)
44
+ @layers = {}
45
+ @layers.default_proc = proc {|hash, key| hash[key] = []}
46
+ @name = name
47
+ @pins = []
48
+ end
49
+
50
+ # Push a new element to the given layer number
51
+ # @param [Numeric] layer_number The layer to add the element to
52
+ # @param [Object] element The thing to push
53
+ def push(layer_number, element)
54
+ layer = @layers[layer_number]
55
+ layer.push element
56
+ end
57
+
58
+ # Generate XML for the {Symbol} element
59
+ # @return [REXML::Element]
60
+ def to_xml
61
+ REXML::Element.new('symbol').tap do |element|
62
+ element.add_attribute 'name', name
63
+ element.add_element('description').text = description if description
64
+
65
+ pins.each {|pin| element.add_element pin.to_xml }
66
+
67
+ layers.each do |number, layer|
68
+ layer.each {|obj| element.add_element(obj.to_xml, {'layer' => number}) }
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end