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