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