eaglecad 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.
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