engineering 0 → 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 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