engineering 0 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in engineering.gemspec
4
4
  gemspec
5
+
6
+ gem 'units', github: 'bfoz/units'
@@ -0,0 +1,109 @@
1
+ Engineering for Ruby
2
+ ====================
3
+
4
+ This is a meta-gem for all things related to engineering (particularly CAD stuff). The Engineering module
5
+ is your one stop shop for all of the tools you need for your latest mad-engineering project.
6
+
7
+ Activating a dormant volcano? Adding death rays to your secret moon base? Plotting world domination? No problem! There's a gem for that, and you've found it right here.
8
+
9
+ If your latest and greatest project, and even your older ones, need something
10
+ that isn't in Engineering, either let me know, or fork and add it yourself (and
11
+ send me a pull request). Or feel free to create your own gem that reopens
12
+ the module and adds whatever is missing, if that's more your style.
13
+
14
+ License
15
+ -------
16
+
17
+ Copyright 2012-2013 Brandon Fosdick <bfoz@bfoz.net> and released under the BSD license.
18
+
19
+ Dependencies
20
+ ------------
21
+
22
+ - DXF [GitHub](http://github.com/bfoz/ruby-dxf) [RubyGems](https://rubygems.org/gems/dxf)
23
+ - Units [GitHub](https://github.com/bfoz/units)
24
+ - Geometry [GitHub](https://github.com/bfoz/geometry) [RubyGems](https://rubygems.org/gems/geometry)
25
+ - Sketch [GitHub](https://github.com/bfoz/sketch)
26
+ - Model [GitHub](https://github.com/bfoz/model)
27
+
28
+ Installation
29
+ ------------
30
+
31
+ Engineering has a number of dependencies. Some of which are hosted on rubygems.org
32
+ and can therefore be handled by the gem utility, but others must be installed
33
+ manually. The easiest option is to use [Bundler](http://gembundler.com/), but
34
+ *gem* can be used if you're willing to install the *units* gem manually.
35
+
36
+ ### Using Bundler
37
+
38
+ Installing the *engineering* gem using bundler is very easy, although a little more involved than normal.
39
+
40
+ Start with the normal gem command:
41
+
42
+ gem install engineering
43
+
44
+ Unfortunately, this will either fail, or it will grab the wrong version of the *units* gem. But, not to worry, we can use bundler to fix it:
45
+
46
+ bundle install
47
+
48
+ And that's it. You're done. Get on with taking over the world already.
49
+
50
+ If you happen to be part of the 0.001% of Mad Engineers who don't already have bundler installed, it's very easy to get:
51
+
52
+ gem install bundler
53
+
54
+ ### Using Rubygems
55
+
56
+ Sadly, the *units* gem hosted on [Rubygems](http://rubygems.org) is a bit out-of-date, and generally not the gem we're looking for. So, after *gem* does its thing, we need to do a little cleanup.
57
+
58
+ Start with the normal gem command:
59
+
60
+ gem install engineering
61
+
62
+ Then uninstall the bogus *units* gem:
63
+
64
+ gem uninstall units
65
+
66
+ Clone the gem we're looking for:
67
+
68
+ git clone git://github.com/bfoz/units.git
69
+
70
+ Install it:
71
+
72
+ cd units && rake install
73
+
74
+ You do have [rake](http://rake.rubyforge.org/) installed, right? If not, do this before the previous step:
75
+
76
+ gem install rake
77
+
78
+ And you should be good to go. If you made it through all of that, then I expect to hear about your machinations on the evening news any day now.
79
+
80
+ Examples
81
+ --------
82
+
83
+ Creating a custom Cube class, the hard way:
84
+
85
+ require 'engineering'
86
+
87
+ model :MyCube do
88
+ extrusion 10.cm do
89
+ square 10.cm
90
+ end
91
+ end
92
+
93
+ MyCube.new
94
+
95
+ Of course, this is ruby, so there's always another way to do it
96
+
97
+ extrusion :MyCube do
98
+ rectangle Size[10.cm, 10.cm]
99
+ end
100
+
101
+ MyCube.new length:10.cm
102
+
103
+ ### Exporting
104
+
105
+ Once a Model has been defined, it can be instantiated and exported to SketchUp with a single line
106
+
107
+ SketchUp.write('MyCube.su', MyCube.new)
108
+
109
+ Then, launch SketchUp, open the _Ruby Console_ (it's in the Window menu), and _load 'MyCube.su'_. Your new geometry will replace whatever was already in the SketchUp document (a person if you just opened it), so be careful.
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs.push "lib"
6
+ t.test_files = FileList['test/**/*.rb']
7
+ t.verbose = true
8
+ end
@@ -2,22 +2,23 @@
2
2
  $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
- s.name = "engineering"
6
- s.version = '0'
7
- s.authors = ["Brandon Fosdick"]
8
- s.email = ["bfoz@bfoz.net"]
9
- s.homepage = "http://github.com/bfoz/engineering"
10
- s.summary = %q{Engineering tools}
11
- s.description = %q{Tools for Engineers and those who want to be}
5
+ s.name = "engineering"
6
+ s.version = 0.1
7
+ s.authors = ["Brandon Fosdick"]
8
+ s.email = ["bfoz@bfoz.net"]
9
+ s.homepage = "http://github.com/bfoz/engineering"
10
+ s.summary = %q{Mad Engineering, Ruby style}
11
+ s.description = %q{Tools for Mad Engineers and those who want to be}
12
12
 
13
- s.rubyforge_project = "engineering"
13
+ s.rubyforge_project = "engineering"
14
14
 
15
- s.files = `git ls-files`.split("\n")
16
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
- s.require_paths = ["lib"]
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
19
 
20
- # specify any dependencies here; for example:
21
- # s.add_development_dependency "rspec"
22
- # s.add_runtime_dependency "rest-client"
20
+ s.add_dependency 'dxf'
21
+ s.add_dependency 'model'
22
+ s.add_dependency 'sketch'
23
+ s.add_dependency 'units', '>= 2'
23
24
  end
@@ -1,3 +1,82 @@
1
+ require 'mathn'
2
+
3
+ require 'dxf'
4
+ require 'model'
5
+ require 'sketch'
6
+ require 'units'
7
+
8
+ require_relative 'sketchup'
9
+
10
+ =begin
11
+ A meta-gem for wayward engineering-related gems. Here you can find everything
12
+ you'll need for your latest engineering project.
13
+ =end
14
+
1
15
  module Engineering
2
- # Your code goes here...
16
+ module DSL
17
+ private
18
+
19
+ # Create a new {Extrusion} subclass and initialize it with the given block
20
+ # @param [Symbol] symbol The name of the resulting subclass
21
+ # @return [Extrusion]
22
+ def extrusion(symbol=nil, &block)
23
+ klass = Class.new(Model::Extrusion)
24
+ klass.const_set(:INITIALIZER_BLOCK, block)
25
+ klass.class_eval %q[
26
+ def initialize(*args)
27
+ super
28
+ Model::Extrusion::Builder.new(self).evaluate(&INITIALIZER_BLOCK)
29
+ end
30
+ ]
31
+ symbol ? Object.const_set(symbol, klass) : klass
32
+ end
33
+
34
+ # Create a new {Model} subclass and initialize it with the given block
35
+ # @param [Symbol] symbol The name of the {Model} subclass
36
+ # @return [Model]
37
+ def model(symbol=nil, &block)
38
+ klass = Class.new(Model)
39
+ klass.const_set(:INITIALIZER_BLOCK, block)
40
+ klass.class_eval %q[
41
+ def initialize(*args)
42
+ super
43
+ Model::Builder.new(self).evaluate(&INITIALIZER_BLOCK)
44
+ end
45
+ ]
46
+ symbol ? Object.const_set(symbol, klass) : klass
47
+ end
48
+
49
+ # Create a new {Sketch} subclass and initialize it with the given block
50
+ # @param [Symbol] symbol The name of the {Sketch} subclass
51
+ def sketch(symbol=nil, &block)
52
+ klass = Class.new(Sketch)
53
+ klass.const_set(:INITIALIZER_BLOCK, block)
54
+ klass.class_eval %q[
55
+ def initialize(*args)
56
+ super
57
+ Sketch::Builder.new(self).evaluate(&INITIALIZER_BLOCK)
58
+ end
59
+ ]
60
+ symbol ? Object.const_set(symbol, klass) : klass
61
+ end
62
+
63
+ class Geometry::Polygon
64
+ # Build a {Polygon} instance using the {Sketch} DSL
65
+ # @return [Polygon]
66
+ def self.build(&block)
67
+ Sketch::PolygonBuilder.new.evaluate(&block)
68
+ end
69
+ end
70
+
71
+ class Geometry::Polyline
72
+ # Build a {Polyline} instance using the {Sketch} DSL
73
+ # @return [Polyline]
74
+ def self.build(&block)
75
+ Sketch::PolylineBuilder.new.evaluate(&block)
76
+ end
77
+ end
78
+ end
3
79
  end
80
+
81
+ self.extend Engineering::DSL
82
+ include Geometry # Make Geometry types more readily available
@@ -0,0 +1,164 @@
1
+ require 'geometry'
2
+ require 'model'
3
+ require 'sketch'
4
+ require 'units'
5
+
6
+ module SketchUp
7
+ =begin
8
+ Export to a Ruby script that can be executed by SketchUp to recreate the geometry
9
+ =end
10
+
11
+ HEADER_LINES = [
12
+ 'model = Sketchup.active_model',
13
+ 'model.entities.clear!',
14
+ 'model.definitions.purge_unused',
15
+ ]
16
+
17
+ SKETCHUP_UNITS = {
18
+ 'kilometer' => 'km', 'meter' => 'm', 'centimeter'=> 'cm', 'millimeter'=> 'mm',
19
+ 'mile' => 'mile', 'yard' => 'yard', 'feet' => 'feet', 'inch' => 'inch',
20
+ 'radian' => 'radians',
21
+ 'degrees' => 'degrees',
22
+ }
23
+
24
+ class Builder
25
+ attr_accessor :container
26
+
27
+ # Initialize with a Sketch or a Model
28
+ def initialize(container=nil)
29
+ @container = container
30
+ @definition_names = {}
31
+ end
32
+
33
+ def to_a
34
+ a = to_array(@container) # Generates the definitions as a side effect
35
+ HEADER_LINES + @definition_names.values.flatten + a
36
+ end
37
+
38
+ def to_s
39
+ to_a.join("\n") << "\n"
40
+ end
41
+
42
+ private
43
+
44
+ def name_for_container(container)
45
+ case container
46
+ when Model::Extrusion
47
+ container.class.to_s + "(#{name_for_container(container.sketch)})_#{to_sketchup(container.length)}"
48
+ when Model::Group
49
+ container.class.to_s + "(#{container.object_id.to_s})"
50
+ when Model # !!! Must be after all subclasses of Model
51
+ s = container.class.to_s
52
+ (s == 'Model') ? s + "(#{container.object_id.to_s})" : s
53
+ when Sketch
54
+ s = container.class.to_s
55
+ (s == 'Sketch') ? s + ":#{container.object_id.to_s}" : s
56
+ end
57
+ end
58
+
59
+ def to_definition(container, definition_name)
60
+ case container
61
+ when Model::Extrusion
62
+ lines = to_array(container.sketch, 'd.entities').map {|l| "#{l}.pushpull(#{to_sketchup(-container.length)})"}
63
+ elements = lines.flatten.join("\n\t")
64
+ "lambda {|d|\n\t#{elements}\n}.call(model.definitions.add('#{definition_name}'))"
65
+ when Model::Group
66
+ lines = container.elements.map {|element| to_array(element, 'g.entities') }.flatten
67
+ elements = lines.flatten.join("\n\t")
68
+ "lambda {|g|\n\t#{elements}\n}.call(model.definitions.add('#{definition_name}'))"
69
+ when Model # !!! Must be after all subclasses of Model
70
+ elements = container.elements.map {|element| to_array(element, 'm.entities') }.flatten.join("\n\t")
71
+ "lambda {|m|\n\t#{elements}\n}.call(model.definitions.add('#{definition_name}'))"
72
+ end
73
+ end
74
+
75
+ def add_instance(parent, container)
76
+ definition_name = name_for_container(container)
77
+ unless @definition_names.key?(definition_name)
78
+ @definition_names[definition_name] = to_definition(container, definition_name)
79
+ end
80
+ ["#{parent}.add_instance(model.definitions['#{definition_name}'], #{to_sketchup(container.transformation)})"]
81
+ end
82
+
83
+ # Convert the given container to an array of strings that SketchUp can read
84
+ def to_array(container, parent='model.entities', transformation=nil)
85
+ case container
86
+ when Model::Extrusion
87
+ if container.transformation and not container.transformation.identity?
88
+ add_instance(parent, container)
89
+ else
90
+ to_array(container.sketch, parent, container.transformation).map {|l| "#{l}.pushpull(#{to_sketchup(-container.length)})"}
91
+ end
92
+ when Model::Group
93
+ if container.transformation and not container.transformation.identity?
94
+ add_instance(parent, container)
95
+ else
96
+ container.elements.map {|element| to_array(element, parent) }.flatten
97
+ end
98
+ when Model # !!! Must be after all subclasses of Model
99
+ if container.transformation and not container.transformation.identity?
100
+ add_instance(parent, container)
101
+ else
102
+ container.elements.map {|element| to_array(element, parent) }.flatten
103
+ end
104
+ when Sketch
105
+ container.geometry.map {|element| to_sketchup(element, parent, transformation) }
106
+ end
107
+ end
108
+
109
+ # Convert the given entity to a string that SketchUp can read
110
+ def to_sketchup(entity, parent='model.entities', transformation=nil)
111
+ case entity
112
+ when Array
113
+ entity.map {|v| to_sketchup(v, parent, transformation) }.join(', ')
114
+ when Geometry::Arc
115
+ "#{parent}.add_arc(#{to_sketchup(entity.center)}, [1,0,0], [0,0,1], #{to_sketchup(entity.radius)}, #{to_sketchup(entity.start_angle)}, #{to_sketchup(entity.end_angle)})"
116
+ when Geometry::Circle
117
+ "lambda{ points = #{parent}.add_circle(#{to_sketchup(entity.center)}, [0,0,1], #{to_sketchup(entity.radius)}); points[0].find_faces; points[0].faces[0]}.call"
118
+ when Geometry::Edge
119
+ "#{parent}.add_edges(#{to_sketchup(entity.first)}, #{to_sketchup(entity.last)})"
120
+ when Geometry::Line
121
+ "#{parent}.add_line(#{to_sketchup(entity.first)}, #{to_sketchup(entity.last)})"
122
+ when Geometry::Path
123
+ edges = entity.elements.map {|e| to_sketchup(e, parent, transformation) }.flatten.join '+'
124
+ "#{parent}.add_face(#{edges})"
125
+ when Geometry::Polyline
126
+ vertices = entity.vertices.map {|v| to_sketchup(v, parent, transformation) }.join ', '
127
+ method = entity.is_a?(Geometry::Polygon) ? 'add_face' : 'add_curve'
128
+ "#{parent}.#{method}(#{vertices})"
129
+ when Geometry::Point
130
+ if transformation and not transformation.identity?
131
+ 'Geom::Point3d.new(' + to_sketchup(entity.to_a) + ').transform!(' + to_sketchup(transformation) + ')'
132
+ else
133
+ '[' + to_sketchup(entity.to_a) + ']'
134
+ end
135
+ when Geometry::Polygon
136
+ "#{parent}.add_face(#{to_sketchup(entity.points, parent, transformation)})"
137
+ when Geometry::Rectangle
138
+ "#{parent}.add_face(#{to_sketchup(entity.points, parent, transformation)})"
139
+ when Geometry::Transformation
140
+ pt = '[' + (entity.translation ? to_sketchup(entity.translation.to_a) : '0,0,0') + ']'
141
+ x_axis = '[' + (entity.rotation.x ? to_sketchup(entity.rotation.x.to_a) : '1,0,0') + ']'
142
+ y_axis = '[' + (entity.rotation.y ? to_sketchup(entity.rotation.y.to_a) : '0,1,0') + ']'
143
+ "Geom::Transformation.new(#{[pt,x_axis,y_axis].join(',')})"
144
+ when Geometry::Triangle
145
+ "#{parent}.add_face(#{to_sketchup(entity.points, parent, transformation)})"
146
+ when Float
147
+ entity.to_s
148
+ when Rational
149
+ [entity.to_f, entity.respond_to?(:units) ? entity.units : nil].compact.map {|a| to_sketchup(a)}.join '.'
150
+ when Units
151
+ s = entity.to_s
152
+ SKETCHUP_UNITS[s] or raise "SketchUp won't recognize '#{s}'"
153
+ when Units::Literal
154
+ [entity.value, entity.units].compact.map {|a| to_sketchup(a)}.join '.'
155
+ else
156
+ entity.to_s
157
+ end
158
+ end
159
+ end
160
+
161
+ def self.write(filename, container)
162
+ File.write(filename, Builder.new(container).to_s)
163
+ end
164
+ end
@@ -0,0 +1,203 @@
1
+ require 'minitest/autorun'
2
+ require 'engineering'
3
+
4
+ #LENGTH = 42
5
+
6
+ describe Engineering do
7
+ include Engineering::DSL
8
+
9
+ after do
10
+ # Cleanup the class constants created by each test
11
+ ObjectSpace.each_object(Class).select {|k| (k < Model) or (k < Sketch)}.each {|klass|
12
+ begin
13
+ Object.send(:remove_const, klass.name.to_sym)
14
+ rescue NameError
15
+ end
16
+ }
17
+ end
18
+
19
+ describe "when creating a named Model subclass" do
20
+ before do
21
+ model :TestModel do
22
+ extrude length:10 do
23
+ square 5
24
+ end
25
+ end
26
+ end
27
+ let(:testModel) { TestModel.new }
28
+
29
+ it "must create a global constant" do
30
+ Object.constants.include?(:TestModel).must_equal true
31
+ end
32
+
33
+ it "must support creating instances of the subclass" do
34
+ TestModel.new.must_be_kind_of Model
35
+ TestModel.new.must_be_kind_of TestModel
36
+ end
37
+
38
+ it "must call the initializer block when constructed" do
39
+ TestModel.new.elements.count.must_equal 1
40
+ TestModel.new.elements.first.must_be_instance_of Model::Extrusion
41
+ TestModel.new.elements.first.length.must_equal 10
42
+ end
43
+
44
+ describe "when another model class is created with a new name" do
45
+ before do
46
+ model :TestModel2 do
47
+ extrude length:5 do
48
+ square 10
49
+ end
50
+ end
51
+ end
52
+ let(:testModel2) { TestModel2.new }
53
+
54
+ it "must be able to make new instances" do
55
+ testModel2.must_be_kind_of Model
56
+ testModel2.must_be_instance_of TestModel2
57
+ testModel2.wont_be_instance_of TestModel
58
+ end
59
+
60
+ it "must call the correct initializer block when constructed" do
61
+ testModel2.elements.count.must_equal 1
62
+ testModel2.elements.first.must_be_instance_of Model::Extrusion
63
+ testModel2.elements.first.length.must_equal 5
64
+ end
65
+
66
+ describe "when the original Model class is used again" do
67
+ let(:anotherTestModel) { TestModel.new }
68
+
69
+ it "must call the correct initializer block when constructed" do
70
+ anotherTestModel.elements.count.must_equal 1
71
+ anotherTestModel.elements.first.must_be_instance_of Model::Extrusion
72
+ anotherTestModel.elements.first.length.must_equal 10
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ describe "when creating an Extrusion subclass" do
79
+ after do
80
+ Object.send(:remove_const, :TestExtrusion)
81
+ end
82
+
83
+ before do
84
+ extrusion :TestExtrusion do
85
+ square 5
86
+ end
87
+ end
88
+
89
+ it "must create a global constant" do
90
+ Object.constants.include?(:TestExtrusion).must_equal true
91
+ end
92
+
93
+ it "must be a subclass of Extrusion" do
94
+ (TestExtrusion < Model::Extrusion).must_equal true
95
+ end
96
+
97
+ describe "when initializing a new instance" do
98
+ subject { TestExtrusion.new(length: 5) }
99
+
100
+ it "must create instances of the proper class" do
101
+ subject.must_be_kind_of Model::Extrusion
102
+ end
103
+
104
+ it "must call the initializer block" do
105
+ subject.length.must_equal 5
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "when creating a Model that uses global constants" do
111
+ before do
112
+ LENGTH = 5
113
+ model :TestModel3 do
114
+ extrude length: LENGTH do
115
+ square 5
116
+ end
117
+ end
118
+ end
119
+
120
+ it "must not complain" do
121
+ TestModel3.new
122
+ end
123
+
124
+ end
125
+
126
+ describe "when creating a named Sketch subclass" do
127
+ before do
128
+ sketch :TestSketch do
129
+ square 5
130
+ end
131
+ end
132
+ let(:testSketch) { TestSketch.new }
133
+
134
+ it "must create a global constant" do
135
+ Object.constants.include?(:TestSketch).must_equal true
136
+ end
137
+
138
+ it "must support creating instances of the subclass" do
139
+ testSketch.must_be_kind_of Sketch
140
+ testSketch.must_be_kind_of TestSketch
141
+ end
142
+
143
+ it "must call the initializer block when constructed" do
144
+ testSketch.elements.count.must_equal 1
145
+ testSketch.elements.first.must_be_kind_of Geometry::Square
146
+ end
147
+
148
+ describe "when another sketch class is created with a new name" do
149
+ before do
150
+ sketch :TestSketch2 do
151
+ square 10
152
+ end
153
+ end
154
+ let(:testSketch2) { TestSketch2.new }
155
+
156
+ it "must be able to make new instances" do
157
+ testSketch2.must_be_kind_of Sketch
158
+ testSketch2.must_be_kind_of TestSketch2
159
+ testSketch2.wont_be_kind_of TestSketch
160
+ end
161
+
162
+ it "must call the correct initializer block when constructed" do
163
+ testSketch2.elements.count.must_equal 1
164
+ testSketch2.elements.first.must_be_kind_of Geometry::Square
165
+ end
166
+
167
+ describe "when the original Sketch class is used again" do
168
+ let(:anotherTestSketch) { TestSketch.new }
169
+
170
+ it "must call the correct initializer block when constructed" do
171
+ anotherTestSketch.elements.count.must_equal 1
172
+ anotherTestSketch.elements.first.must_be_kind_of Geometry::Square
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ describe "when creating a Polygon" do
179
+ it "must create a Polygon" do
180
+ polygon = Polygon.build do
181
+ start_at [0,0]
182
+ right 1
183
+ up 1
184
+ left 1
185
+ down 1
186
+ end
187
+ polygon.must_be_instance_of(Geometry::Polygon)
188
+ end
189
+ end
190
+
191
+ describe "when creating a Polyline" do
192
+ it "must create a Polyline" do
193
+ polyline = Polyline.build do
194
+ start_at [0,0]
195
+ right 1
196
+ up 1
197
+ left 1
198
+ down 1
199
+ end
200
+ polyline.must_be_instance_of(Geometry::Polyline)
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,7 @@
1
+ model = Sketchup.active_model
2
+ model.entities.clear!
3
+ model.definitions.purge_unused
4
+ lambda {|d|
5
+ d.entities.add_face\(\[-5.0, -10.0\], \[-5.0, 10.0\], \[5.0, 10.0\], \[5.0, -10.0\]\).pushpull\(-5\)
6
+ }.call\(model.definitions.add\('Model::Extrusion\(Sketch:\d+\)_5'\)\)
7
+ model.entities.add_instance\(model.definitions\['Model::Extrusion\(Sketch:\d+\)_5'\], Geom::Transformation.new\(\[1, 2, 3\],\[1,0,0\],\[0,1,0\]\)\)
@@ -0,0 +1,22 @@
1
+ require 'minitest/autorun'
2
+ require 'geometry'
3
+ require 'units'
4
+
5
+ # This is a bit of integration testing to ensure that the Units gem doesn't break
6
+ # any of the other gems. None of the individual gems know about each other so
7
+ # there's no way to test their integration at a lower level.
8
+
9
+ describe Geometry do
10
+ let(:pointA) { Point[2.meters, 3.meters] }
11
+ let(:pointB) { Point[4.meters, 5.meters] }
12
+ let(:sizeA) { Size[2.meters, 3.meters] }
13
+ let(:sizeB) { Size[4.meters, 5.meters] }
14
+
15
+ describe "Point and Size arithmetic" do
16
+ it "should add" do
17
+ sum = (pointA + sizeA)
18
+ sum.must_be_instance_of(Point)
19
+ sum.must_equal Point[4.meters, 6.meters]
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ require 'minitest/autorun'
2
+ require 'geometry'
3
+ require 'units'
4
+
5
+ # This is a bit of integration testing to ensure that the Units gem doesn't break
6
+ # any of the other gems. None of the individual gems know about each other so
7
+ # there's no way to test their integration at a lower level.
8
+
9
+ describe Geometry::Edge do
10
+ Edge = Geometry::Edge
11
+
12
+ let(:pointA) { Point[2.meters, 3.meters] }
13
+ let(:pointB) { Point[4.meters, 5.meters] }
14
+
15
+ describe "should construct an Edge from Points with Units" do
16
+ let(:edge) { Edge.new(pointA, pointB) }
17
+
18
+ it "should preserve the units" do
19
+ edge.first.must_equal Point[2.meters, 3.meters]
20
+ edge.last.must_equal Point[4.meters, 5.meters]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ require 'minitest/autorun'
2
+ require 'geometry'
3
+ require 'units'
4
+
5
+ # This is a bit of integration testing to ensure that the Units gem doesn't break
6
+ # any of the other gems. None of the individual gems know about each other so
7
+ # there's no way to test their integration at a lower level.
8
+
9
+ describe Geometry::Point do
10
+ Point = Geometry::Point
11
+
12
+ it "should not break normal Point construction" do
13
+ Point[1,2].must_be_instance_of(Point)
14
+ end
15
+
16
+ describe "when the elements have units" do
17
+ let(:pointA) { Point[2.meters, 3.meters] }
18
+ let(:pointB) { Point[4.meters, 5.meters] }
19
+
20
+ describe "arithmetic" do
21
+ it "should add" do
22
+ (pointA+pointB).must_equal Point[6.meters, 8.meters]
23
+ end
24
+
25
+ it "should subtract" do
26
+ (pointB-pointA).must_equal Point[2.meters, 2.meters]
27
+ end
28
+
29
+ it "should multiply by a constant" do
30
+ (pointA * 2).must_equal Point[4.meters, 6.meters]
31
+ end
32
+
33
+ it "should divide by a constant" do
34
+ (pointB / 2).must_equal Point[2.meters, 2.5.meters]
35
+ (pointB / 2.0).must_equal Point[2.0.meters, 2.5.meters]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ require 'minitest/autorun'
2
+ require 'geometry'
3
+ require 'units'
4
+
5
+ # This is a bit of integration testing to ensure that the Units gem doesn't break
6
+ # any of the other gems. None of the individual gems know about each other so
7
+ # there's no way to test their integration at a lower level.
8
+
9
+ describe Geometry::Size do
10
+ Size = Geometry::Size
11
+
12
+ it "should not break normal Size construction" do
13
+ Size[1,2].must_be_instance_of(Size)
14
+ end
15
+
16
+ describe "when the elements have units" do
17
+ let(:sizeA) { Size[2.meters, 3.meters] }
18
+ let(:sizeB) { Size[4.meters, 5.meters] }
19
+
20
+ describe "arithmetic" do
21
+ it "should add" do
22
+ (sizeA+sizeB).must_equal Size[6.meters, 8.meters]
23
+ end
24
+
25
+ it "should subtract" do
26
+ (sizeB-sizeA).must_equal Size[2.meters, 2.meters]
27
+ end
28
+
29
+ it "should multiply by a constant" do
30
+ (sizeA * 2).must_equal Size[4.meters, 6.meters]
31
+ end
32
+
33
+ it "should divide by a constant" do
34
+ (sizeB / 2).must_equal Size[2.meters, 2.5.meters]
35
+ (sizeB / 2.0).must_equal Size[2.0.meters, 2.5.meters]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ require 'minitest/autorun'
2
+ require 'model/builder'
3
+ require 'units'
4
+
5
+ # This is a bit of integration testing to ensure that the Units gem doesn't break
6
+ # any of the other gems. None of the individual gems know about each other so
7
+ # there's no way to test their integration at a lower level.
8
+
9
+ describe Model::Builder do
10
+ Builder = Model::Builder
11
+
12
+ let(:builder) { Builder.new }
13
+
14
+ describe "when adding an Extrusion with a length with units" do
15
+ before do
16
+ builder.evaluate do
17
+ extrude length:10.meters, sketch:Sketch.new do
18
+ rectangle 5, 6
19
+ end
20
+ end
21
+ end
22
+
23
+ it "should have an Extrusion element" do
24
+ builder.model.elements.last.must_be_instance_of Model::Extrusion
25
+ builder.model.elements.last.length.must_equal 10.meters
26
+ end
27
+
28
+ it "should make a Rectangle in the Extrusion's Sketch" do
29
+ extrusion = builder.model.elements.last
30
+ sketch = extrusion.sketch
31
+ sketch.elements.last.must_be_kind_of Geometry::Rectangle
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,32 @@
1
+ require 'minitest/autorun'
2
+ require 'model/extrusion'
3
+ require 'units'
4
+
5
+ # This is a bit of integration testing to ensure that the Units gem doesn't break
6
+ # any of the other gems. None of the individual gems know about each other so
7
+ # there's no way to test their integration at a lower level.
8
+
9
+ describe Model::Extrusion do
10
+ Extrusion = Model::Extrusion
11
+
12
+ it "must not break normal construction" do
13
+ Extrusion.new(length:5, sketch:Sketch.new).must_be_instance_of(Extrusion)
14
+ end
15
+
16
+ describe "when the length parameter has units" do
17
+ let(:extrusionA) { Extrusion.new length:5.meters, sketch:Sketch.new }
18
+
19
+ it "must preserve the units" do
20
+ extrusionA.length.must_equal 5.meters
21
+ end
22
+ end
23
+
24
+ describe "when the length parameter is a variable with units" do
25
+ let(:length) { 6.meters }
26
+ let(:extrusionA) { Extrusion.new length:6.meters, sketch:Sketch.new }
27
+
28
+ it "must preserve the units" do
29
+ extrusionA.length.must_equal 6.meters
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,7 @@
1
+ require 'minitest/autorun'
2
+
3
+ describe SketchUp do
4
+ before do
5
+ @SketchUp = SketchUp.new
6
+ end
7
+ end
@@ -0,0 +1,117 @@
1
+ require 'minitest/autorun'
2
+ require 'sketchup'
3
+
4
+ describe SketchUp::Builder do
5
+ subject { SketchUp::Builder.new }
6
+
7
+ before do
8
+ @builder = SketchUp::Builder.new
9
+ end
10
+
11
+ let(:empty_fixture) { File.read('test/fixtures/sketchup/empty.su') }
12
+ let(:rectangle_sketch_fixture) { File.read('test/fixtures/sketchup/rectangle_sketch.su') }
13
+
14
+ it "should keep private methods private" do
15
+ @builder.wont_respond_to :to_array
16
+ @builder.wont_respond_to :to_sketchup
17
+ end
18
+
19
+ it "should not break Point's to_s method" do
20
+ 5.cm.to_s.must_equal "5"
21
+ end
22
+
23
+ it "should not break Point's inspect method" do
24
+ 5.cm.inspect.must_equal "5 centimeter"
25
+ end
26
+
27
+ describe "when given an empty Model object" do
28
+ before do
29
+ model = Model.new
30
+ model.add_extrusion Model::Extrusion.new(length:5, sketch:Sketch.new)
31
+ @builder.container = model
32
+ end
33
+
34
+ it "should export the correct file" do
35
+ @builder.to_s.must_equal empty_fixture
36
+ end
37
+ end
38
+
39
+ describe "when given a Model of a translated Extrusion" do
40
+ sketch = Sketch.new
41
+ sketch.add_rectangle 10, 20
42
+ before do
43
+ subject.container = Model.new do
44
+ add_extrusion Model::Extrusion.new(length:5, sketch:sketch, transformation:Geometry::Transformation.new(origin:[1,2,3]))
45
+ end
46
+ end
47
+
48
+ it "must generate the correct text" do
49
+ subject.to_s.must_match Regexp.new(File.read('test/fixtures/translated_extrusion.su'))
50
+ end
51
+ end
52
+
53
+ it "should generate the correct text from a Model of a simple extrusion" do
54
+ sketch = Sketch.new
55
+ sketch.add_rectangle 10, 20
56
+ model = Model.new do
57
+ add_extrusion Model::Extrusion.new(length:5, sketch:sketch)
58
+ end
59
+ @builder.container = model
60
+ @builder.to_s.must_equal File.read('test/fixtures/sketchup/simple_extrusion.su')
61
+ end
62
+
63
+ it "should generate the correct text from a Model of a simple extrusion with units" do
64
+ sketch = Sketch.new
65
+ sketch.add_rectangle 1.meter, 10
66
+ model = Model.new
67
+ model.add_extrusion Model::Extrusion.new(length:5.meters, sketch:sketch)
68
+ @builder.container = model
69
+ @builder.to_s.must_equal File.read('test/fixtures/sketchup/simple_extrusion_units.su')
70
+ end
71
+
72
+ it "should generate correct text from an empty Sketch" do
73
+ @builder.container = Sketch.new
74
+ @builder.to_s.must_equal empty_fixture
75
+ end
76
+
77
+ it "should generate correct text from a simple Sketch object" do
78
+ sketch = Sketch.new
79
+ sketch.add_line [0,0], [1,0]
80
+ @builder.container = sketch
81
+ @builder.to_s.must_equal File.read('test/fixtures/sketchup/line_sketch.su')
82
+ end
83
+
84
+ it "should generate correct text from a Sketch object with a single Rectangle" do
85
+ sketch = Sketch.new
86
+ sketch.add_rectangle [0,0], Geometry::Size[1,1]
87
+ @builder.container = sketch
88
+ @builder.to_s.must_equal rectangle_sketch_fixture
89
+ end
90
+
91
+ it "should generate correct text from a Sketch object with a single Polygon" do
92
+ sketch = Sketch.new
93
+ sketch.add_polygon [0,0], [0,1], [1,1], [1,0], [0,0]
94
+ @builder.container = sketch
95
+ @builder.to_s.must_equal rectangle_sketch_fixture
96
+ end
97
+
98
+ it "must handle a Group" do
99
+ builder = SketchUp::Builder.new( Model::Builder.new.evaluate { group :origin => [1,2,3] })
100
+ builder.container.elements.count.must_equal 1
101
+ builder.container.elements.first.must_be_instance_of(Model::Group)
102
+ builder.to_s.must_match %r{model = Sketchup.active_model\nmodel.entities.clear!\nmodel.definitions.purge_unused\nlambda {|g|\n\t\n}.call(model.definitions.add('Model::Group()'))\nmodel.entities.add_instance(model.definitions\['Model::Group(\d+)'\], Geom::Transformation.new(\[1, 2, 3\],\[1,0,0\],\[0,1,0\]))}
103
+ end
104
+
105
+ it "must handle a sub-model" do
106
+ builder = SketchUp::Builder.new( Model::Builder.new.evaluate { push Model.new, :origin => [3,2,1] })
107
+ builder.container.elements.count.must_equal 1
108
+ builder.container.elements.first.must_be_instance_of(Model)
109
+ builder.to_s.must_match %r{model = Sketchup.active_model\nmodel.entities.clear!\nmodel.definitions.purge_unused\nlambda {|m|\n\t\n}.call(model.definitions.add('Model(\d+)'))\nmodel.entities.add_instance(model.definitions\['Model(\d+)'\], Geom::Transformation.new(\[3, 2, 1\],\[1,0,0\],\[0,1,0\]))}
110
+ end
111
+
112
+ it "Path" do
113
+ sketch = Sketch.new
114
+ sketch.add_path [0,0], Geometry::Arc.new([0,0],5,0,90*Math::PI/180), [0,0]
115
+ builder = SketchUp::Builder.new( Model::Builder.new.evaluate { extrude length:5, sketch:sketch })
116
+ end
117
+ end
@@ -0,0 +1,31 @@
1
+ require 'minitest/autorun'
2
+ require 'mathn'
3
+ require 'units'
4
+
5
+ # This is a bit of integration testing to ensure that the mathn gem doesn't break
6
+ # the units gems. Mathn changes a number of the default arithmetic operators,
7
+ # which tends to cause trouble for Units. Neither gem knows about the other so
8
+ # there's no good way to test their integration at a lower level.
9
+
10
+ def Literal(*args)
11
+ Units::Literal.new(*args)
12
+ end
13
+
14
+ describe Units::Literal do
15
+ let(:one_meter) { Literal(1, :meter) }
16
+ let(:three_meters) { Literal(3, :meters) }
17
+ let(:four_meters) { Literal(4, :meters) }
18
+
19
+ describe "coerced arithmetic" do
20
+ it "division" do
21
+ (0 / one_meter).must_equal 0
22
+ (0 / three_meters).must_equal 0
23
+ # (4 / three_meters).must_equal Rational(4,3).meters
24
+ (12.0 / three_meters).must_equal four_meters
25
+ end
26
+
27
+ it "must divide a Rational" do
28
+ (Rational(2,1) / one_meter).must_equal Rational(2,1).meters(-1)
29
+ end
30
+ end
31
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: engineering
3
3
  version: !ruby/object:Gem::Version
4
- version: '0'
4
+ version: '0.1'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,9 +9,73 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-29 00:00:00.000000000 Z
13
- dependencies: []
14
- description: Tools for Engineers and those who want to be
12
+ date: 2013-04-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: dxf
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: model
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: sketch
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: units
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '2'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '2'
78
+ description: Tools for Mad Engineers and those who want to be
15
79
  email:
16
80
  - bfoz@bfoz.net
17
81
  executables: []
@@ -20,10 +84,22 @@ extra_rdoc_files: []
20
84
  files:
21
85
  - .gitignore
22
86
  - Gemfile
23
- - README
87
+ - README.markdown
24
88
  - Rakefile
25
89
  - engineering.gemspec
26
90
  - lib/engineering.rb
91
+ - lib/sketchup.rb
92
+ - test/engineering.rb
93
+ - test/fixtures/translated_extrusion.su
94
+ - test/geometry.rb
95
+ - test/geometry/edge.rb
96
+ - test/geometry/point.rb
97
+ - test/geometry/size.rb
98
+ - test/model/builder.rb
99
+ - test/model/extrusion.rb
100
+ - test/sketchup.rb
101
+ - test/sketchup/builder.rb
102
+ - test/units/literal.rb
27
103
  homepage: http://github.com/bfoz/engineering
28
104
  licenses: []
29
105
  post_install_message:
@@ -44,9 +120,20 @@ required_rubygems_version: !ruby/object:Gem::Requirement
44
120
  version: '0'
45
121
  requirements: []
46
122
  rubyforge_project: engineering
47
- rubygems_version: 1.8.17
123
+ rubygems_version: 1.8.25
48
124
  signing_key:
49
125
  specification_version: 3
50
- summary: Engineering tools
51
- test_files: []
126
+ summary: Mad Engineering, Ruby style
127
+ test_files:
128
+ - test/engineering.rb
129
+ - test/fixtures/translated_extrusion.su
130
+ - test/geometry.rb
131
+ - test/geometry/edge.rb
132
+ - test/geometry/point.rb
133
+ - test/geometry/size.rb
134
+ - test/model/builder.rb
135
+ - test/model/extrusion.rb
136
+ - test/sketchup.rb
137
+ - test/sketchup/builder.rb
138
+ - test/units/literal.rb
52
139
  has_rdoc:
data/README DELETED
File without changes