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
data/Gemfile CHANGED
@@ -1,3 +1,7 @@
1
1
  source 'https://rubygems.org'
2
-
3
2
  gemspec
3
+
4
+ group :test do
5
+ gem 'rake'
6
+ end
7
+
@@ -0,0 +1,29 @@
1
+ # EagleCAD
2
+
3
+ [![Build Status](https://travis-ci.org/bfoz/eaglecad-ruby.png)](https://travis-ci.org/bfoz/eaglecad-ruby)
4
+
5
+ The EagleCAD gem provides tools for working with the files generated by the popular [EagleCAD](http://www.cadsoftusa.com/) PCB design software.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'eaglecad'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install eaglecad
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ require 'eaglecad'
25
+
26
+ drawing = EagleCAD.read('my_schematic.sch')
27
+
28
+ drawing.write('cloned_schematic.sch')
29
+ ```
data/Rakefile CHANGED
@@ -1 +1,10 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs.push "lib"
8
+ t.test_files = FileList['test/**/*.rb']
9
+ t.verbose = true
10
+ end
@@ -4,12 +4,12 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "eaglecad"
7
- spec.version = '0'
7
+ spec.version = '1'
8
8
  spec.authors = ["Brandon Fosdick"]
9
9
  spec.email = ["bfoz@bfoz.net"]
10
10
  spec.description = %q{Everything you need to bend Eagle CAD projects to your will}
11
11
  spec.summary = %q{Read and write Eagle CAD files}
12
- spec.homepage = "http://github.com/bfoz/ruby-eaglecad"
12
+ spec.homepage = "http://github.com/bfoz/eaglecad-ruby"
13
13
  spec.license = "BSD"
14
14
 
15
15
  spec.files = `git ls-files`.split($/)
@@ -19,4 +19,6 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.add_development_dependency "bundler", "~> 1.3"
21
21
  spec.add_development_dependency "rake"
22
+
23
+ spec.add_dependency 'geometry', '~> 6'
22
24
  end
@@ -1,3 +1,17 @@
1
+ require 'rexml/document'
2
+
3
+ require_relative 'eaglecad/drawing'
4
+
1
5
  module EagleCAD
2
- # Your code goes here...
6
+ # Load and parse the given {Schematic} or {Board} file
7
+ # @return [Drawing] A new {EagleCAD::Drawing}
8
+ def self.read(filename)
9
+ parse(REXML::Document.new File.open(filename))
10
+ end
11
+
12
+ # @param [REXML::Document] An XML document to parse
13
+ # @return [Drawing] A new {EagleCAD::Drawing}, or nil if there was an error
14
+ def self.parse(document)
15
+ Drawing.from_xml(document.root.elements['drawing'])
16
+ end
3
17
  end
@@ -0,0 +1,47 @@
1
+ require_relative 'geometry'
2
+
3
+ module EagleCAD
4
+ Attribute = Struct.new :name, :value, :origin, :size, :layer_number, :font, :ratio, :rotation, :display, :constant do
5
+ def self.from_xml(element)
6
+ Attribute.new.tap do |attribute|
7
+ attribute.name = element.attributes['name']
8
+ attribute.value = element.attributes['value']
9
+ attribute.origin = Geometry.point_from(element)
10
+ attribute.size = element.attributes['size']
11
+ attribute.layer_number = element.attributes['layer']
12
+ attribute.ratio = element.attributes['ratio'].to_i
13
+ attribute.rotation = element.attributes['rot']
14
+
15
+ element.attributes.each do |name, value|
16
+ case name
17
+ when 'constant' then attribute.constant = ('yes' == element.attribute['constant'])
18
+ when 'display' then attribute.display = value
19
+ when 'font' then attribute.font = value
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def initialize
26
+ super
27
+ @display = 'value'
28
+ end
29
+
30
+ # @return [REXML::Element]
31
+ def to_xml
32
+ REXML::Element.new('attribute').tap do |element|
33
+ element.add_attribute('name', name)
34
+ element.add_attribute('value', value)
35
+ element.add_attribute('x', origin.x)
36
+ element.add_attribute('y', origin.y)
37
+ element.add_attribute('size', size)
38
+ element.add_attribute('layer', layer_number)
39
+ element.add_attribute('ratio', ratio) unless 0 == ratio
40
+ element.add_attribute('rot', rotation)
41
+ element.add_attribute('constant', 'yes') if constant
42
+ element.add_attribute('display', display)
43
+ element.add_attribute('font', font)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,184 @@
1
+ require 'rexml/document'
2
+
3
+ require_relative 'attribute'
4
+ require_relative 'clearance'
5
+ require_relative 'design_rules'
6
+ require_relative 'library'
7
+
8
+ module EagleCAD
9
+ class Board
10
+ attr_accessor :design_rules, :description
11
+ attr_reader :attributes, :classes, :elements, :libraries, :passes, :plain
12
+
13
+ ContactReference = Struct.new :element, :pad, :route, :route_tag
14
+ Via = Struct.new :origin, :extent, :drill
15
+
16
+ class Element
17
+ attr_accessor :name, :library, :package, :value, :origin, :locked, :smashed, :rotation
18
+ attr_reader :attributes, :variants
19
+
20
+ def self.from_xml(element)
21
+ self.new(element.attributes['name'], element.attributes['library'], element.attributes['package'], element.attributes['value'], Geometry.point_from(element)).tap do |object|
22
+ element.elements.each do |element|
23
+ case element.name
24
+ when 'attribute'
25
+ object.attributes.push Attribute.from_xml(element)
26
+ when 'variant'
27
+ variants = {}
28
+ element.attributes.each {|name, value| variants[name] = value }
29
+ object.variants.push variants
30
+ else
31
+ raise StandardError, "Unrecognized Element element '#{element.name}'"
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def initialize(name, library, package, value, origin)
38
+ @name = name
39
+ @library = library
40
+ @package = package
41
+ @value = value
42
+ @origin = origin
43
+ @locked = false
44
+ @smashed = false
45
+ @rotation = 0
46
+
47
+ @attributes = []
48
+ @variants = []
49
+ end
50
+
51
+ # @return [REXML::Element]
52
+ def to_xml
53
+ REXML::Element.new('element').tap do |element|
54
+ element.add_attributes({'name' => name, 'library' => library, 'package' => package, 'value' => value, 'x' => origin.x, 'y' => origin.y})
55
+ element.add_attribute('rot', "R#{rotation}") if 0 != rotation
56
+ element.add_attribute('locked', 'yes') if locked
57
+ element.add_attribute('smashed', 'yes') if smashed
58
+
59
+ attributes.each {|attribute| element.add_element attribute.to_xml }
60
+ variants.each {|variant| element.add_element('variant', variant)}
61
+ end
62
+ end
63
+ end
64
+
65
+ class Signal
66
+ attr_accessor :air_wires_hidden, :clearance_class, :name
67
+ attr_reader :contact_references, :vias
68
+
69
+ def self.from_xml(element)
70
+ self.new(element.attributes['name']).tap do |signal|
71
+ element.attributes.each do |name, value|
72
+ case name
73
+ when 'airwireshidden'
74
+ signal.air_wires_hidden = ('no' != value)
75
+ when 'class'
76
+ signal.clearance_class = value.to_i
77
+ when 'name' # Ignore; already handled
78
+ else
79
+ raise StandardError, "Unrecognized Signal element '#{element.name}'"
80
+ end
81
+ end
82
+
83
+ element.elements.each do |element|
84
+ case element.name
85
+ when 'contactref'
86
+ signal.contact_references.push ContactReference.new element.attributes['element'], element.attributes['pad'], element.attributes['route'], element.attributes['routetag']
87
+ when 'polygon', 'wire'
88
+ signal.push Geometry.from_xml(element)
89
+ when 'via'
90
+ signal.vias.push Via.new Geometry.from_point(element), element.attributes['extent'], element.attributes['drill']
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def initialize(name)
97
+ @contact_references = []
98
+ @name = name
99
+ @vias = []
100
+ end
101
+ end
102
+
103
+ def self.from_xml(element)
104
+ Board.new.tap do |board|
105
+ element.elements.each do |element|
106
+ case element.name
107
+ when 'attributes'
108
+ element.elements.each {|attribute| board.attributes.push Attribute.from_xml(attribute) }
109
+ when 'autorouter'
110
+ element.elements.each do |pass_element|
111
+ pass = board.passes[pass_element.attributes['name']]
112
+ pass_element.elements.each {|parameter| pass[parameter.attributes['name']] = parameter.attributes['value'] }
113
+ end
114
+ when 'classes'
115
+ element.elements.each {|clearance| board.classes.push Clearance.from_xml(clearance) }
116
+ when 'description'
117
+ board.description = element.text
118
+ when 'designrules'
119
+ board.design_rules = DesignRules.from_xml(element)
120
+ when 'elements'
121
+ element.elements.each {|object| board.elements.push Element.from_xml(object) }
122
+ when 'libraries'
123
+ element.elements.each {|library| board.libraries[library.attributes['name']] = Library.from_xml(library) }
124
+ when 'plain'
125
+ element.elements.each {|object| board.plain.push Geometry.from_xml(object) }
126
+ when 'signals'
127
+ when 'variantdefs'
128
+ else
129
+ raise StandardError, "Unrecognized Board element '#{element.name}'"
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def initialize
136
+ @attributes = []
137
+ @classes = []
138
+ @contact_references = []
139
+ @elements = []
140
+ @libraries = {}
141
+ @passes = Hash.new {|hash, key| hash[key] = Hash.new }
142
+ @plain = []
143
+ end
144
+
145
+ # @return [REXML::Element]
146
+ def to_xml
147
+ REXML::Element.new('board').tap do |element|
148
+ element.add_element('attributes').tap do |attribute_element|
149
+ attributes.each {|attribute| attribute_element.add_element attribute.to_xml }
150
+ end
151
+
152
+ element.add_element('description').text = description
153
+
154
+ element.add_element('autorouter').tap do |autorouter_element|
155
+ passes.each do |name, pass|
156
+ autorouter_element.add_element('pass', {'name' => name}).tap do |pass_element|
157
+ pass.each {|name, value| pass_element.add_element('param', {'name' => name, 'value' => value})}
158
+ end
159
+ end
160
+ end
161
+
162
+ element.add_element('classes').tap do |classes_element|
163
+ classes.each do |clearance|
164
+ classes_element.add_element clearance.to_xml
165
+ end
166
+ end
167
+
168
+ element.add_element(design_rules.to_xml) if design_rules
169
+
170
+ element.add_element('elements').tap do |element_element|
171
+ elements.each {|object| element_element.add_element object.to_xml }
172
+ end
173
+
174
+ element.add_element('libraries').tap do |libraries_element|
175
+ libraries.each {|name, library| libraries_element.add_element library.to_xml }
176
+ end
177
+
178
+ element.add_element('plain').tap do |plain_element|
179
+ plain.each {|object| plain_element.add_element object.to_xml }
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,31 @@
1
+ require 'rexml/document'
2
+
3
+ module EagleCAD
4
+ class Clearance
5
+ attr_accessor :name, :number, :width, :drill
6
+ attr_reader :values
7
+
8
+ def self.from_xml(element)
9
+ Clearance.new(element.attributes['name'], element.attributes['number'].to_i).tap do |clearance|
10
+ clearance.width = (element.attributes['width'] || 0).to_f
11
+ clearance.drill = (element.attributes['drill'] || 0).to_f
12
+
13
+ element.elements.each {|element| clearance.values.push (element.text || 0).to_f }
14
+ end
15
+ end
16
+
17
+ def initialize(name, number)
18
+ @name = name
19
+ @number = number
20
+ @values = []
21
+ end
22
+
23
+ # @param [REXML::Element]
24
+ def to_xml
25
+ REXML::Element.new('class').tap do |element|
26
+ element.add_attributes({'name' => name, 'number' => number, 'width' => width, 'drill' => drill})
27
+ values.each {|value| element.add_element('clearance', {'class' => number, 'value' => value})}
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ require 'rexml/document'
2
+
3
+ module EagleCAD
4
+ class DesignRules
5
+ attr_accessor :description, :name
6
+ attr_reader :parameters
7
+
8
+ def self.from_xml(element)
9
+ self.new(element.attributes['name']).tap do |rule|
10
+ element.elements.each do |element|
11
+ case element.name
12
+ when 'description'
13
+ rule.description = element.text
14
+ when 'param'
15
+ rule.parameters[element.attributes['name']] = element.attributes['value']
16
+ else
17
+ raise StandardError, "Unrecognized Design Rule element '#{element.name}'"
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def initialize(name)
24
+ @name = name
25
+ @parameters = {}
26
+ end
27
+
28
+ # @return [REXML::Element]
29
+ def to_xml
30
+ REXML::Element.new('designrules').tap do |element|
31
+ element.add_attribute 'name', name
32
+ element.add_element('description').text = description if description
33
+
34
+ parameters.each {|key, value| element.add_element('param', {'name' => key, 'value' => value})}
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,113 @@
1
+ require 'rexml/document'
2
+
3
+ require_relative 'geometry'
4
+
5
+ module EagleCAD
6
+ class DeviceSet
7
+ attr_accessor :description, :name, :prefix, :uservalue
8
+ attr_reader :devices, :gates
9
+
10
+ Connect = Struct.new :gate, :pin, :pad, :route do
11
+ def self.from_xml(element)
12
+ Connect.new element.attributes['gate'], element.attributes['pin'], element.attributes['pad'], element.attributes['route']
13
+ end
14
+
15
+ # @return [REXML::Element]
16
+ def to_xml
17
+ REXML::Element.new('connect').tap {|element| element.add_attributes({'gate' => gate, 'pad' => pad, 'pin' => pin}) }
18
+ end
19
+ end
20
+
21
+ class Device
22
+ attr_accessor :name, :package
23
+ attr_reader :connects, :technologies
24
+
25
+ def self.from_xml(element)
26
+ Device.new(element.attributes['name']).tap do |device|
27
+ device.package = element.attributes['package']
28
+
29
+ element.elements.each do |element|
30
+ case element.name
31
+ when 'connects'
32
+ element.elements.each {|connect| device.connects.push Connect.from_xml(connect) }
33
+ when 'technologies'
34
+ element.elements.each {|technology| device.technologies.push technology.attributes['name'] }
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def initialize(name)
41
+ @connects = []
42
+ @name = name
43
+ @technologies = []
44
+ end
45
+
46
+ # Generate XML for the {DeviceSet} element
47
+ # @return [REXML::Element]
48
+ def to_xml
49
+ REXML::Element.new('device').tap do |element|
50
+ element.add_attributes({'name' => name, 'package' => package})
51
+
52
+ connects_element = element.add_element('connects')
53
+ connects.each {|connect| connects_element.add_element connect.to_xml }
54
+
55
+ technologies_element = element.add_element('technologies')
56
+ technologies.each {|technology| technologies_element.add_element('technology', {'name' => technology}) }
57
+ end
58
+ end
59
+ end
60
+
61
+ Gate = Struct.new :name, :symbol, :origin, :addlevel, :swaplevel do
62
+ def self.from_xml(element)
63
+ Gate.new element.attributes['name'], element.attributes['symbol'], Geometry.point_from(element, 'x', 'y'), element.attributes['addlevel'], element.attributes['swaplevel']
64
+ end
65
+
66
+ # @return [REXML::Element]
67
+ def to_xml
68
+ REXML::Element.new('gate').tap {|element| element.add_attributes({'name' => name, 'symbol' => symbol, 'x' => origin.x, 'y' => origin.y, 'addlevel' => addlevel, 'swaplevel' => swaplevel}) }
69
+ end
70
+ end
71
+
72
+ # Create a new {DeviceSet} from an {REXML::Element}
73
+ # @param [Element] element The {REXML::Element} to parse
74
+ def self.from_xml(element)
75
+ DeviceSet.new(element.attributes['name']).tap do |deviceset|
76
+ deviceset.prefix = element.attributes['prefix']
77
+ deviceset.uservalue = ('yes' == element.attributes['uservalue'])
78
+
79
+ element.elements.each do |element|
80
+ case element.name
81
+ when 'devices'
82
+ element.elements.each {|device| deviceset.devices.push Device.from_xml(device) }
83
+ when 'description'
84
+ deviceset.description = element.text
85
+ when 'gates'
86
+ element.elements.each {|gate| deviceset.gates.push Gate.from_xml(gate) }
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def initialize(name)
93
+ @devices = []
94
+ @gates = []
95
+ @name = name
96
+ end
97
+
98
+ # Generate XML for the {DeviceSet} element
99
+ # @return [REXML::Element]
100
+ def to_xml
101
+ REXML::Element.new('deviceset').tap do |element|
102
+ element.add_attributes({'name' => name, 'prefix' => prefix, 'uservalue' => (uservalue ? 'yes' : 'no')})
103
+ element.add_element('description').text = description
104
+
105
+ gates_element = element.add_element('gates')
106
+ gates.each {|gate| gates_element.add_element gate.to_xml }
107
+
108
+ devices_element = element.add_element('devices')
109
+ devices.each {|device| devices_element.add_element device.to_xml }
110
+ end
111
+ end
112
+ end
113
+ end