eaglecad 0 → 1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -1
- data/README.markdown +29 -0
- data/Rakefile +9 -0
- data/eaglecad.gemspec +4 -2
- data/lib/eaglecad.rb +15 -1
- data/lib/eaglecad/attribute.rb +47 -0
- data/lib/eaglecad/board.rb +184 -0
- data/lib/eaglecad/clearance.rb +31 -0
- data/lib/eaglecad/design_rules.rb +38 -0
- data/lib/eaglecad/deviceset.rb +113 -0
- data/lib/eaglecad/drawing.rb +124 -0
- data/lib/eaglecad/geometry.rb +259 -0
- data/lib/eaglecad/layer.rb +40 -0
- data/lib/eaglecad/library.rb +73 -0
- data/lib/eaglecad/package.rb +74 -0
- data/lib/eaglecad/part.rb +29 -0
- data/lib/eaglecad/schematic.rb +83 -0
- data/lib/eaglecad/sheet.rb +256 -0
- data/lib/eaglecad/symbol.rb +73 -0
- data/test/eaglecad.rb +12 -0
- data/test/eaglecad/board.rb +68 -0
- data/test/eaglecad/design_rules.rb +38 -0
- data/test/eaglecad/deviceset.rb +52 -0
- data/test/eaglecad/drawing.rb +94 -0
- data/test/eaglecad/library.rb +47 -0
- data/test/eaglecad/package.rb +60 -0
- data/test/eaglecad/schematic.rb +50 -0
- data/test/eaglecad/sheet.rb +24 -0
- data/test/eaglecad/symbol.rb +41 -0
- data/test/fixtures/board.xml +859 -0
- data/test/fixtures/demo1.sch +10932 -0
- data/test/fixtures/design_rules.xml +70 -0
- data/test/fixtures/deviceset.xml +683 -0
- data/test/fixtures/drawing_board.xml +910 -0
- data/test/fixtures/drawing_schematic.xml +10928 -0
- data/test/fixtures/hexapodu.brd +1863 -0
- data/test/fixtures/library.xml +175 -0
- data/test/fixtures/package.xml +28 -0
- data/test/fixtures/schematic.xml +10861 -0
- data/test/fixtures/sheet.xml +318 -0
- data/test/fixtures/symbol.xml +11 -0
- metadata +79 -5
- data/README.md +0 -29
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
require_relative 'board'
|
4
|
+
require_relative 'layer'
|
5
|
+
require_relative 'schematic'
|
6
|
+
|
7
|
+
module EagleCAD
|
8
|
+
class Drawing
|
9
|
+
attr_accessor :board, :schematic
|
10
|
+
attr_reader :layers
|
11
|
+
|
12
|
+
# Grid attributes
|
13
|
+
attr_accessor :distance, :unitdistance, :unit, :style, :multiple, :display, :altdistance, :altunitdist, :altunit
|
14
|
+
|
15
|
+
# Settings attributes
|
16
|
+
attr_accessor :always_vector_font, :vertical_text
|
17
|
+
|
18
|
+
# @param element [REXML::Element]
|
19
|
+
def self.from_xml(element)
|
20
|
+
self.new.tap do |drawing|
|
21
|
+
element.elements.each do |element|
|
22
|
+
case element.name
|
23
|
+
when 'board'
|
24
|
+
raise StandardError, "Drawing files must contain only one Board element" if drawing.board
|
25
|
+
drawing.board = Board.from_xml(element)
|
26
|
+
|
27
|
+
when 'grid'
|
28
|
+
element.attributes.each do |name, value|
|
29
|
+
case name
|
30
|
+
when 'altdistance' then drawing.altdistance = value.to_f
|
31
|
+
when 'altunit' then drawing.altunit = value.to_sym
|
32
|
+
when 'altunitdist' then drawing.altunitdist = value.to_sym
|
33
|
+
when 'display' then drawing.display = ('no' != value)
|
34
|
+
when 'distance' then drawing.distance = value.to_f
|
35
|
+
when 'unit' then drawing.unit = value.to_sym
|
36
|
+
when 'unitdist' then drawing.unitdistance = value.to_sym
|
37
|
+
when 'multiple' then drawing.multiple = value.to_i
|
38
|
+
when 'style' then drawing.style = value.to_sym
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
when 'layers'
|
43
|
+
element.elements.each {|element| drawing.layers.push Layer.from_xml(element) }
|
44
|
+
|
45
|
+
when 'schematic'
|
46
|
+
raise StandardError, "Drawing files must contain only one Schematic element" if drawing.schematic
|
47
|
+
drawing.schematic = Schematic.from_xml(element)
|
48
|
+
|
49
|
+
when 'settings'
|
50
|
+
element.elements.each do |element|
|
51
|
+
element.attributes.each do |name, value|
|
52
|
+
case name
|
53
|
+
when 'alwaysvectorfont' then drawing.always_vector_font = ('no' != value)
|
54
|
+
when 'verticaltext' then drawing.vertical_text = value.to_sym
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
else
|
60
|
+
raise StandardError, "Unrecognized Drawing element '#{element.name}'"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def initialize()
|
67
|
+
@layers = []
|
68
|
+
self.vertical_text = :up
|
69
|
+
|
70
|
+
self.display = false
|
71
|
+
self.multiple = 1
|
72
|
+
self.style = :lines
|
73
|
+
end
|
74
|
+
|
75
|
+
# Generate XML for the {Drawing} element
|
76
|
+
# @return [REXML::element]
|
77
|
+
def to_xml
|
78
|
+
drawing_element = REXML::Element.new 'drawing'
|
79
|
+
|
80
|
+
settings = REXML::Element.new 'settings', drawing_element
|
81
|
+
settings.add_element 'setting', {'alwaysvectorfont' => (always_vector_font ? 'yes' : 'no')}
|
82
|
+
settings.add_element 'setting', {'verticaltext' => vertical_text}
|
83
|
+
|
84
|
+
grid_element = REXML::Element.new 'grid', drawing_element
|
85
|
+
grid_element.add_attributes({ 'altdistance' => altdistance,
|
86
|
+
'altunit' => altunit,
|
87
|
+
'altunitdist' => altunitdist,
|
88
|
+
'display' => (display ? 'yes' : 'no'),
|
89
|
+
'distance' => distance,
|
90
|
+
'multiple' => multiple,
|
91
|
+
'unit' => unit,
|
92
|
+
'unitdist' => unitdistance,
|
93
|
+
'style' => style,
|
94
|
+
})
|
95
|
+
|
96
|
+
layers_element = REXML::Element.new 'layers', drawing_element
|
97
|
+
layers.each {|layer| layers_element.add_element layer.to_xml }
|
98
|
+
|
99
|
+
drawing_element.add_element(board.to_xml) if board
|
100
|
+
drawing_element.add_element(schematic.to_xml) if schematic
|
101
|
+
|
102
|
+
drawing_element
|
103
|
+
end
|
104
|
+
|
105
|
+
# @param filename [String] The path to write the output to
|
106
|
+
def write(output)
|
107
|
+
document = REXML::Document.new('<?xml version="1.0" encoding="utf-8"?><!DOCTYPE eagle SYSTEM "eagle.dtd">')
|
108
|
+
|
109
|
+
eagle = REXML::Element.new('eagle')
|
110
|
+
eagle.add_attribute('version', '6.0')
|
111
|
+
eagle.add_element to_xml
|
112
|
+
document.add eagle
|
113
|
+
|
114
|
+
output = File.open(output, 'w') if output.is_a? String
|
115
|
+
|
116
|
+
# This is a hack to force REXML to output PCDATA text inline with the enclosing element. Eagle has problems with the extra newlines that REXML tends to add.
|
117
|
+
formatter = REXML::Formatters::Pretty.new(0)
|
118
|
+
formatter.compact = true
|
119
|
+
formatter.write(document, output)
|
120
|
+
|
121
|
+
output.close
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
require 'geometry'
|
4
|
+
|
5
|
+
module EagleCAD
|
6
|
+
module Geometry
|
7
|
+
Point = ::Geometry::Point
|
8
|
+
|
9
|
+
class Circle < ::Geometry::Circle
|
10
|
+
attr_accessor :line_width
|
11
|
+
|
12
|
+
# Create a {Circle} from an {REXML::Element}
|
13
|
+
# @param [Element] element The {REXML::Element} to parse
|
14
|
+
def self.from_xml(element)
|
15
|
+
self.new center:Geometry.point_from(element, 'x', 'y'), radius:element.attributes['radius'].to_f, line_width:element.attributes['width'].to_f
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(options={})
|
19
|
+
@line_width = options.delete(:line_width)
|
20
|
+
super options[:center], options[:radius]
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [REXML::Element]
|
24
|
+
def to_xml
|
25
|
+
REXML::Element.new('circle').tap {|element| element.add_attributes({'x' => Geometry.format(center.x), 'y' => Geometry.format(center.y), 'radius' => Geometry.format(radius), 'width' => line_width}) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Hole = Struct.new :origin, :drill do
|
30
|
+
def self.from_xml(element)
|
31
|
+
Geometry::Hole.new Geometry.point_from(element, 'x', 'y'), element.attributes['drill']
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [REXML::Element]
|
35
|
+
def to_xml
|
36
|
+
REXML::Element.new('hole').tap {|element| element.add_attributes({'x' => Geometry.format(origin.x), 'y' => Geometry.format(origin.y), 'drill' => drill}) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Line < ::Geometry::TwoPointLine
|
41
|
+
attr_accessor :cap, :curve, :line_width
|
42
|
+
|
43
|
+
# Create a {Line} from an {REXML::Element}
|
44
|
+
# @param [Element] element The {REXML::Element} to parse
|
45
|
+
def self.from_xml(element)
|
46
|
+
self.new(from:Geometry.point_from(element, 'x1', 'y1'), to:Geometry::point_from(element, 'x2', 'y2'), line_width:element.attributes['width'].to_f, cap: element.attributes['cap'], curve: element.attributes['curve'].to_f)
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(options={})
|
50
|
+
@cap = options.delete :cap
|
51
|
+
@curve = options.delete :curve
|
52
|
+
@line_width = options.delete(:line_width)
|
53
|
+
super options[:from], options[:to]
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [REXML::Element]
|
57
|
+
def to_xml
|
58
|
+
REXML::Element.new('wire').tap do |element|
|
59
|
+
element.add_attributes({'x1' => Geometry.format(first.x), 'y1' => Geometry.format(first.y), 'x2' => Geometry.format(last.x), 'y2' => Geometry.format(last.y), 'width' => line_width})
|
60
|
+
element.add_attribute('cap', cap) unless 'round' == cap
|
61
|
+
element.add_attribute('curve', curve) unless 0 == curve
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
Pad = Struct.new :diameter, :drill, :name, :origin, :rotation, :shape do
|
67
|
+
def self.from_xml(element)
|
68
|
+
origin = Geometry.point_from(element, 'x', 'y')
|
69
|
+
Geometry::Pad.new(element.attributes['diameter'], element.attributes['drill'], element.attributes['name'], origin, element.attributes['rot'], element.attributes['shape'])
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [REXML::Element]
|
73
|
+
def to_xml
|
74
|
+
REXML::Element.new('pad').tap do |element|
|
75
|
+
element.add_attributes({'name' => name, 'x' => Geometry.format(origin.x), 'y' => Geometry.format(origin.y), 'diameter' => diameter, 'drill' => drill, 'shape' => shape})
|
76
|
+
element.add_attribute('rot', rotation) unless 'R0' == rotation
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
Pin = Struct.new :direction, :function, :length, :name, :origin, :swaplevel, :rotation, :visible do
|
82
|
+
def self.from_xml(element)
|
83
|
+
origin = Geometry.point_from(element, 'x', 'y')
|
84
|
+
Geometry::Pin.new(element.attributes['direction'], element.attributes['function'], element.attributes['length'], element.attributes['name'], origin, element.attributes['swaplevel'], element.attributes['rot'], element.attributes['visible'])
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [REXML::Element]
|
88
|
+
def to_xml
|
89
|
+
REXML::Element.new('pin').tap do |element|
|
90
|
+
element.add_attributes({'name' => name,
|
91
|
+
'x' => Geometry.format(origin.x),
|
92
|
+
'y' => Geometry.format(origin.y),
|
93
|
+
'direction' => direction,
|
94
|
+
'function' => function,
|
95
|
+
'length' => length,
|
96
|
+
'swaplevel' => swaplevel,
|
97
|
+
'rot' => rotation,
|
98
|
+
'visible' => visible})
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class Polygon < ::Geometry::Polygon
|
104
|
+
attr_accessor :line_width
|
105
|
+
|
106
|
+
# Create a {Polygon} from an {REXML::Element}
|
107
|
+
# @param [Element] element The {REXML::Element} to parse
|
108
|
+
def self.from_xml(element)
|
109
|
+
width = element.attributes['width']
|
110
|
+
vertices = element.elements.map {|vertex| Geometry::point_from(vertex, 'x', 'y') }
|
111
|
+
self.new(*vertices, line_width:width)
|
112
|
+
end
|
113
|
+
|
114
|
+
def initialize(*args)
|
115
|
+
options, args = args.partition {|a| a.is_a? Hash}
|
116
|
+
options = options.reduce({}, :merge)
|
117
|
+
|
118
|
+
@line_width = options.delete(:line_width)
|
119
|
+
|
120
|
+
super *args
|
121
|
+
end
|
122
|
+
|
123
|
+
# @return [REXML::Element]
|
124
|
+
def to_xml
|
125
|
+
REXML::Element.new('polygon').tap do |element|
|
126
|
+
element.add_attribute 'width', line_width
|
127
|
+
vertices.each {|vertex| element.add_element('vertex', {'x' => Geometry.format(vertex.x), 'y' => Geometry.format(vertex.y)}) }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class Rectangle < ::Geometry::Rectangle
|
133
|
+
# Create a {Rectangle} from an {REXML::Element}
|
134
|
+
# @param [Element] element The {REXML::Element} to parse
|
135
|
+
def self.from_xml(element)
|
136
|
+
first = Geometry.point_from(element, 'x1', 'y1')
|
137
|
+
last = Geometry.point_from(element, 'x2', 'y2')
|
138
|
+
self.new(first, last)
|
139
|
+
end
|
140
|
+
|
141
|
+
# @return [REXML::Element]
|
142
|
+
def to_xml
|
143
|
+
REXML::Element.new('rectangle').tap {|element| element.add_attributes({'x1' => Geometry.format(origin.x),
|
144
|
+
'y1' => Geometry.format(origin.y),
|
145
|
+
'x2' => Geometry.format(max.x),
|
146
|
+
'y2' => Geometry.format(max.y)}) }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class SMD < ::Geometry::SizedRectangle
|
151
|
+
attr_accessor :cream, :name, :roundness, :rotation, :stop, :thermals
|
152
|
+
|
153
|
+
# Create a {SMD} from an {REXML::Element}
|
154
|
+
# @param [Element] element The {REXML::Element} to parse
|
155
|
+
def self.from_xml(element)
|
156
|
+
size = Size[element.attributes['dx'].to_f, element.attributes['dy'].to_f]
|
157
|
+
SMD.new(origin:Geometry.point_from(element, 'x', 'y'), size:size).tap do |smd|
|
158
|
+
smd.cream = ('no' != element.attributes['cream'])
|
159
|
+
smd.name = element.attributes['name']
|
160
|
+
smd.rotation = element.attributes['rot']
|
161
|
+
smd.roundness = element.attributes['roundness'].to_i
|
162
|
+
smd.stop = ('no' != element.attributes['stop'])
|
163
|
+
smd.thermals = ('no' != element.attributes['thermals'])
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# @return [REXML::Element]
|
168
|
+
def to_xml
|
169
|
+
REXML::Element.new('smd').tap do |element|
|
170
|
+
element.add_attributes({'name' => name,
|
171
|
+
'x' => Geometry.format(origin.x),
|
172
|
+
'y' => Geometry.format(origin.y),
|
173
|
+
'dx' => Geometry.format(size.width),
|
174
|
+
'dy' => Geometry.format(size.height)})
|
175
|
+
element.add_attribute('cream', 'no') unless cream
|
176
|
+
element.add_attribute('rot', rotation) if rotation
|
177
|
+
element.add_attribute('roundness', Geometry.format(roundness)) unless 0 == roundness
|
178
|
+
element.add_attribute('stop', 'no') unless stop
|
179
|
+
element.add_attribute('thermals', 'no') unless thermals
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class Text
|
185
|
+
attr_accessor :align, :distance, :origin, :font, :layer, :ratio, :rotation, :size, :text
|
186
|
+
|
187
|
+
def self.from_xml(element)
|
188
|
+
Geometry::Text.new(Geometry.point_from(element, 'x', 'y'), element.attributes['layer'], element.attributes['size'].to_f, element.text).tap do |object|
|
189
|
+
object.align = element.attributes['align'] || object.align
|
190
|
+
object.distance = element.attributes['distance'] || object.distance
|
191
|
+
object.font = element.attributes['font'] || object.font
|
192
|
+
object.ratio = element.attributes['ratio'] || object.ratio
|
193
|
+
object.rotation = element.attributes['rot'] || object.rotation
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def initialize(origin, layer, size, text, options={})
|
198
|
+
@origin = origin
|
199
|
+
@layer = layer
|
200
|
+
@size = size
|
201
|
+
@text = text
|
202
|
+
|
203
|
+
@align = options['align'] || 'bottom-left'
|
204
|
+
@distance = options['distance'] || 50
|
205
|
+
@font = options['font'] || 'proportional'
|
206
|
+
@ratio = options['ratio'] || 8
|
207
|
+
@rotation = options['rot'] || 'R0'
|
208
|
+
end
|
209
|
+
|
210
|
+
# @return [REXML::Element]
|
211
|
+
def to_xml
|
212
|
+
REXML::Element.new('text').tap do |element|
|
213
|
+
element.add_attributes({'x' => Geometry.format(origin.x), 'y' => Geometry.format(origin.y), 'layer' => layer, 'size' => Geometry.format(size)})
|
214
|
+
element.add_attribute('align', align)
|
215
|
+
element.add_attribute('distance', distance)
|
216
|
+
element.add_attribute('font', font)
|
217
|
+
element.add_attribute('ratio', ratio)
|
218
|
+
element.add_attribute('rot', rotation)
|
219
|
+
element.text = text
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def self.from_xml(element)
|
225
|
+
case element.name
|
226
|
+
when 'circle'
|
227
|
+
Geometry::Circle.from_xml(element)
|
228
|
+
when 'hole'
|
229
|
+
Geometry::Hole.from_xml(element)
|
230
|
+
when 'pad'
|
231
|
+
Geometry::Pad.from_xml(element)
|
232
|
+
when 'pin'
|
233
|
+
Geometry::Pin.from_xml(element)
|
234
|
+
when 'polygon'
|
235
|
+
Geometry::Polygon.from_xml(element)
|
236
|
+
when 'rectangle'
|
237
|
+
Geometry::Rectangle.from_xml(element)
|
238
|
+
when 'smd'
|
239
|
+
Geometry::SMD.from_xml(element)
|
240
|
+
when 'text'
|
241
|
+
Geometry::Text.from_xml(element)
|
242
|
+
when 'wire'
|
243
|
+
Geometry::Line.from_xml(element)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Create a {Point} from the given {REXML::Element} using the passed attribute names
|
248
|
+
# @param [REXML::Element] element The {REXML::Element} to parse
|
249
|
+
# @param [String] x_name The name of the attribute containing the X coordinate
|
250
|
+
# @param [String] y_name The name of the attribute containing the Y coordinate
|
251
|
+
def self.point_from(element, x_name='x', y_name='y')
|
252
|
+
Point[element.attributes[x_name].to_f, element.attributes[y_name].to_f]
|
253
|
+
end
|
254
|
+
|
255
|
+
def self.format(value)
|
256
|
+
"%g" % value
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module EagleCAD
|
4
|
+
class Layer
|
5
|
+
attr_accessor :active, :color, :fill, :name, :number, :visible
|
6
|
+
|
7
|
+
def self.from_xml(element)
|
8
|
+
self.new(element.attributes['name'], element.attributes['number'], element.attributes['color'], element.attributes['fill']).tap do |layer|
|
9
|
+
element.attributes.each do |name, value|
|
10
|
+
case name
|
11
|
+
when 'active' then layer.active = ('no' != value)
|
12
|
+
when 'visible' then layer.visible = ('no' != value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(name, number, color, fill)
|
19
|
+
@active = true
|
20
|
+
@color = color
|
21
|
+
@fill = fill
|
22
|
+
@name = name
|
23
|
+
@number = number
|
24
|
+
@visible = true
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [REXML::Element]
|
28
|
+
def to_xml
|
29
|
+
element = REXML::Element.new 'layer'
|
30
|
+
element.add_attributes({'number' => number,
|
31
|
+
'name' => name,
|
32
|
+
'color' => color,
|
33
|
+
'fill' => fill,
|
34
|
+
})
|
35
|
+
element.add_attribute('active', active ? 'yes' : 'no')
|
36
|
+
element.add_attribute('visible', visible ? 'yes' : 'no')
|
37
|
+
element
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
require_relative 'deviceset'
|
4
|
+
require_relative 'package'
|
5
|
+
require_relative 'symbol'
|
6
|
+
|
7
|
+
module EagleCAD
|
8
|
+
class Library
|
9
|
+
attr_accessor :description, :name, :packages, :symbols, :device_sets
|
10
|
+
|
11
|
+
# Create a new {Library} from an {REXML::Element}
|
12
|
+
# @param [REXML::Element] element The {REXML::Element} to parse
|
13
|
+
def self.from_xml(element)
|
14
|
+
Library.new(name:element.attributes['name']).tap do |library|
|
15
|
+
library.description = element.elements['description']
|
16
|
+
|
17
|
+
element.elements.each do |element|
|
18
|
+
case element.name
|
19
|
+
when 'devicesets'
|
20
|
+
element.elements.each {|symbol| library.push DeviceSet.from_xml(symbol) }
|
21
|
+
when 'packages'
|
22
|
+
element.elements.each {|package| library.push Package.from_xml(package) }
|
23
|
+
when 'symbols'
|
24
|
+
element.elements.each {|symbol| library.push Symbol.from_xml(symbol) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(options={})
|
31
|
+
options.each {|k,v| send("#{k}=", v) }
|
32
|
+
|
33
|
+
@device_sets = []
|
34
|
+
@packages = {}
|
35
|
+
@symbols = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def push(arg)
|
39
|
+
case arg
|
40
|
+
when DeviceSet
|
41
|
+
@device_sets.push arg
|
42
|
+
when Package
|
43
|
+
@packages[arg.name] = arg
|
44
|
+
when Symbol
|
45
|
+
@symbols.push arg
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Generate XML for the {Library} element
|
50
|
+
# @return [REXML::Element]
|
51
|
+
def to_xml
|
52
|
+
REXML::Element.new('library').tap do |element|
|
53
|
+
element.add_attribute 'name', name
|
54
|
+
|
55
|
+
# Packages must be output before devicesets or Eagle will fail to load the file
|
56
|
+
element.add_element('packages').tap do |packages_element|
|
57
|
+
packages.each {|name, package| packages_element.add_element package.to_xml }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Symbols must be output before devicessets or Eagle will fail to load the file
|
61
|
+
element.add_element('symbols').tap do |symbols_element|
|
62
|
+
symbols.each {|symbol| symbols_element.add_element symbol.to_xml }
|
63
|
+
end
|
64
|
+
|
65
|
+
if device_sets and device_sets.count
|
66
|
+
element.add_element('devicesets').tap do |devicesets_element|
|
67
|
+
device_sets.each {|deviceset| devicesets_element.add_element deviceset.to_xml }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|