triangular 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,25 +7,58 @@ The main purpose of Triangular is to enable its users to quickly create new soft
7
7
  Please note that Triangular requires Ruby 1.9+. Triangular is currently in the Alpha stage of development which means that the API can and will change, and that new features will be added often!
8
8
 
9
9
  === A Quick Example
10
+
11
+ require "rubygems"
12
+ require "triangular"
10
13
 
11
14
  # Open and parse an STL file
12
15
  solid = Triangular.parse_file("test.stl")
13
16
 
14
- # Cut a section slice of the model at a Z height of 1.0
15
- slice = solid.slice_at_z(1.0)
17
+ # Set the units of measurement for the resulting solid to inches
18
+ solid.units = :inches
19
+
20
+ # Move the solid so that all of it's coordinates are in positive space (ie: greater than 0)
21
+ solid.align_to_origin!
22
+
23
+ # Get the bounding box of the solid
24
+ bounds = solid.get_bounds
25
+
26
+ # Create a section plane ('slice') through the solid on the XY plane at a Z height of 0.7
27
+ slice = solid.slice_at_z(0.7)
28
+
29
+ # Open a file for SVG output
30
+ File.open("slice.svg", "w+") do |file|
16
31
 
17
- # Create an SVG image of the slice for further manipulation
18
- svg = slice.to_svg
32
+ # Output the slice as an SVG document (correctly scaled according to the solid's units)
33
+ file.puts slice.to_svg(bounds[1].x, bounds[1].y, solid.units)
34
+ end
19
35
 
20
36
  === Installation
21
37
 
22
38
  For ease of use the Triangular is packaged as a RubyGem. Providing you already have Ruby and RubyGems installing Triangular is as easy as entering the following command in a terminal:
23
39
 
24
40
  gem install triangular
41
+
42
+ === Performance
43
+
44
+ At the moment Triangular has not been optimized at all. The parser is a relatively naive one that was designed to be easy to read rather than performant. Once the feature-set of Triangular has stabilized I will be doing a pass over it in order to make it fast enough for production use. Right now it could definitely be improved.
45
+
46
+ For example here is some information about run-times when processing a 51Mb STL file:
47
+
48
+ solid = Triangular.parse("big_file.stl")
49
+ # 65 seconds
50
+
51
+ solid.align_to_origin!
52
+ # 8 seconds
53
+
54
+ solid.slice_at_z(1.0)
55
+ # 2 seconds
25
56
 
26
57
  === Author & Credits
27
58
 
28
59
  Author:: {Aaron Gough}[mailto:aaron@aarongough.com]
29
60
 
61
+ Special thanks go out to {Alkas Baybas}[https://github.com/abaybas] for lending me his massive brain!
62
+
30
63
  Copyright (c) 2011 {Aaron Gough}[http://thingsaaronmade.com/] ({thingsaaronmade.com}[http://thingsaaronmade.com/]), released under the MIT license
31
64
 
@@ -2,8 +2,13 @@ require "bundler/setup"
2
2
  require "triangular"
3
3
 
4
4
  solid = Triangular.parse_file("#{File.dirname(__FILE__)}/example_files/y-axis-spacer.stl")
5
- polyline = solid.slice_at_z(-0.5)
5
+ solid.units = :inches
6
+
7
+ solid.align_to_origin!
8
+ bounds = solid.get_bounds
9
+
10
+ polyline = solid.slice_at_z(0.7)
6
11
 
7
12
  File.open(File.expand_path("~/Desktop/slice.svg"), "w+") do |file|
8
- file.puts polyline.to_svg(10, 10)
13
+ file.puts polyline.to_svg(bounds[1].x, bounds[1].y, solid.units)
9
14
  end
@@ -4,6 +4,7 @@ require 'triangular/vector'
4
4
  require 'triangular/line'
5
5
  require 'triangular/polyline'
6
6
  require 'triangular/facet'
7
+ require 'triangular/units'
7
8
  require 'triangular/solid'
8
9
 
9
10
  module Triangular
@@ -44,6 +44,12 @@ module Triangular
44
44
  end
45
45
  end
46
46
 
47
+ def translate!(x, y, z)
48
+ @vertices.each do |vertex|
49
+ vertex.translate!(x, y, z)
50
+ end
51
+ end
52
+
47
53
  def self.parse(string)
48
54
  facets = []
49
55
 
@@ -31,8 +31,8 @@ module Triangular
31
31
  Point.new(x_intersect, y_intersect, z_plane)
32
32
  end
33
33
 
34
- def to_svg_path
35
- "<path d=\"M #{@start.x} #{@start.y} L #{@end.x} #{@end.y}\" fill=\"none\" stroke=\"black\" stroke-width=\"1\" />"
34
+ def to_svg_path(units)
35
+ "<path d=\"M #{@start.x} #{@start.y} L #{@end.x} #{@end.y}\" fill=\"none\" stroke=\"black\" stroke-width=\"#{Units.stroke_width(units)}\" />"
36
36
  end
37
37
  end
38
38
  end
@@ -13,6 +13,12 @@ module Triangular
13
13
  "#{@x.to_f} #{@y.to_f} #{@z.to_f}"
14
14
  end
15
15
 
16
+ def translate!(x, y, z)
17
+ @x += x
18
+ @y += y
19
+ @z += z
20
+ end
21
+
16
22
  def ==(other)
17
23
  return false unless other.is_a?(Point)
18
24
  self.x == other.x && self.y == other.y && self.z == other.z
@@ -7,14 +7,14 @@ module Triangular
7
7
  @lines = lines
8
8
  end
9
9
 
10
- def to_svg(x_offset = 20, y_offset = 20)
10
+ def to_svg(width, height, units, x_offset = 0, y_offset = 0)
11
11
  output = '<?xml version="1.0" standalone="no"?>' + "\n"
12
12
  output << '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' + "\n"
13
- output << '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">' + "\n"
14
- output << " <g transform=\"translate(#{x_offset},#{y_offset})\">\n"
13
+ output << "<svg x=\"0\" y=\"0\" width=\"#{width}#{Units.svg_name(units)}\" height=\"#{height}#{Units.svg_name(units)}\" viewBox=\"0 0 #{width} #{height}\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n"
14
+ output << " <g transform=\"translate(#{x_offset}#{Units.svg_name(units)},#{y_offset}#{Units.svg_name(units)})\">\n"
15
15
 
16
16
  @lines.each do |line|
17
- output << " " + line.to_svg_path + "\n"
17
+ output << " " + line.to_svg_path(units) + "\n"
18
18
  end
19
19
 
20
20
  output << ' </g>' + "\n"
@@ -1,11 +1,12 @@
1
1
  module Triangular
2
2
  class Solid
3
3
 
4
- attr_accessor :name, :facets
4
+ attr_accessor :name, :facets, :units
5
5
 
6
6
  def initialize(name, *args)
7
7
  @name = name
8
8
  @facets = args
9
+ @units = units
9
10
  end
10
11
 
11
12
  def to_s
@@ -24,6 +25,45 @@ module Triangular
24
25
  output
25
26
  end
26
27
 
28
+ def get_bounds
29
+ largest_x = @facets[0].vertices[0].x
30
+ largest_y = @facets[0].vertices[0].y
31
+ largest_z = @facets[0].vertices[0].z
32
+
33
+ smallest_x = @facets[0].vertices[0].x
34
+ smallest_y = @facets[0].vertices[0].y
35
+ smallest_z = @facets[0].vertices[0].z
36
+
37
+ @facets.each do |facet|
38
+ facet.vertices.each do |vertex|
39
+ largest_x = vertex.x if vertex.x > largest_x
40
+ largest_y = vertex.y if vertex.y > largest_y
41
+ largest_z = vertex.z if vertex.z > largest_z
42
+
43
+ smallest_x = vertex.x if vertex.x < smallest_x
44
+ smallest_y = vertex.y if vertex.y < smallest_y
45
+ smallest_z = vertex.z if vertex.z < smallest_z
46
+ end
47
+ end
48
+
49
+ [Point.new(smallest_x, smallest_y, smallest_z), Point.new(largest_x, largest_y, largest_z)]
50
+ end
51
+
52
+ def align_to_origin!
53
+ bounds = self.get_bounds
54
+ self.translate!(-bounds[0].x, -bounds[0].y, -bounds[0].z)
55
+ end
56
+
57
+ def center!
58
+ bounds = self.get_bounds
59
+
60
+ x_translation = ((bounds[1].x - bounds[0].x).abs / 2) + -bounds[1].x
61
+ y_translation = ((bounds[1].y - bounds[0].y).abs / 2) + -bounds[1].y
62
+ z_translation = ((bounds[1].z - bounds[0].z).abs / 2) + -bounds[1].z
63
+
64
+ self.translate!(x_translation, y_translation, z_translation)
65
+ end
66
+
27
67
  def slice_at_z(z_plane)
28
68
  lines = @facets.map {|facet| facet.intersection_at_z(z_plane) }
29
69
  lines.compact!
@@ -31,6 +71,12 @@ module Triangular
31
71
  Polyline.new(lines)
32
72
  end
33
73
 
74
+ def translate!(x, y, z)
75
+ @facets.each do |facet|
76
+ facet.translate!(x, y, z)
77
+ end
78
+ end
79
+
34
80
  def self.parse(string)
35
81
  partial_pattern = /\s* solid\s+ (?<name> [a-zA-Z0-9\-\_\.]+)?/x
36
82
  match_data = string.match(partial_pattern)
@@ -0,0 +1,30 @@
1
+ require 'forwardable'
2
+
3
+ module Triangular
4
+ class Units
5
+
6
+ UNITS = {
7
+ :inches => {:name => 'inches', :svg_name => 'in', :stroke_width => 0.005},
8
+ :centimeters => {:name => 'centimeters', :svg_name => 'cm', :stroke_width => 0.01},
9
+ :millimeters => {:name => 'millimeters', :svg_name => 'mm', :stroke_width => 0.1},
10
+ :none => {:name => 'none', :svg_name => '', :stroke_width => 0.1}
11
+ }
12
+
13
+ def self.get_property(unit, name)
14
+ raise "Unknown unit: #{unit}" unless UNITS.has_key?(unit)
15
+ UNITS[unit][name]
16
+ end
17
+
18
+ def self.name(unit)
19
+ self.get_property(unit, :name)
20
+ end
21
+
22
+ def self.svg_name(unit)
23
+ self.get_property(unit, :svg_name)
24
+ end
25
+
26
+ def self.stroke_width(unit)
27
+ self.get_property(unit, :stroke_width)
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
1
  module Triangular
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -7,6 +7,7 @@ module Triangular
7
7
  def_delegator :@point, :x, :x
8
8
  def_delegator :@point, :y, :y
9
9
  def_delegator :@point, :z, :z
10
+ def_delegator :@point, :translate!, :translate!
10
11
 
11
12
  attr_accessor :point
12
13
 
@@ -154,52 +154,6 @@ describe Facet do
154
154
  end
155
155
  end
156
156
 
157
- context "for a facet that intersects the target Z plane at an angle" do
158
- before do
159
- vertex1 = Vertex.new(0.0, 0.0, 0.0)
160
- vertex2 = Vertex.new(0.0, 6.0, 6.0)
161
- vertex3 = Vertex.new(6.0, 6.0, 6.0)
162
-
163
- @facet = Facet.new(nil, vertex1, vertex2, vertex3)
164
- end
165
-
166
- context "when the target Z plane is 3.0" do
167
- it "should return a line object" do
168
- @facet.intersection_at_z(3.0).should be_a Line
169
- end
170
-
171
- it "should return a line with the correct start value" do
172
- @facet.intersection_at_z(3.0).start.x.should == 0.0
173
- @facet.intersection_at_z(3.0).start.y.should == 3.0
174
- @facet.intersection_at_z(3.0).start.z.should == 3.0
175
- end
176
-
177
- it "should return a line with the correct end value" do
178
- @facet.intersection_at_z(3.0).end.x.should == 3.0
179
- @facet.intersection_at_z(3.0).end.y.should == 3.0
180
- @facet.intersection_at_z(3.0).end.z.should == 3.0
181
- end
182
- end
183
-
184
- context "when the target Z plane is 6.0" do
185
- it "should return a line object" do
186
- @facet.intersection_at_z(6.0).should be_a Line
187
- end
188
-
189
- it "should return a line with the correct start value" do
190
- @facet.intersection_at_z(6.0).start.x.should == 0.0
191
- @facet.intersection_at_z(6.0).start.y.should == 6.0
192
- @facet.intersection_at_z(6.0).start.z.should == 6.0
193
- end
194
-
195
- it "should return a line with the correct end value" do
196
- @facet.intersection_at_z(6.0).end.x.should == 6.0
197
- @facet.intersection_at_z(6.0).end.y.should == 6.0
198
- @facet.intersection_at_z(6.0).end.z.should == 6.0
199
- end
200
- end
201
- end
202
-
203
157
  context "with vertices in both positive and negative space" do
204
158
  before do
205
159
  @facet = Facet.parse(<<-EOD)
@@ -254,4 +208,26 @@ describe Facet do
254
208
  end
255
209
  end
256
210
  end
211
+
212
+ describe "#translate!" do
213
+ before do
214
+ @facet = Facet.parse(<<-EOD)
215
+ facet normal 0.0 0.0 -1.0
216
+ outer loop
217
+ vertex -16.5 0.0 -0.75
218
+ vertex 0.0 -9.5 -0.75
219
+ vertex 0.0 0.0 -0.75
220
+ endloop
221
+ endfacet
222
+ EOD
223
+ end
224
+
225
+ it "should call translate on each of it's Vertices" do
226
+ @facet.vertices[0].should_receive(:translate!).with(16.5, 9.5, 0.75)
227
+ @facet.vertices[1].should_receive(:translate!).with(16.5, 9.5, 0.75)
228
+ @facet.vertices[2].should_receive(:translate!).with(16.5, 9.5, 0.75)
229
+
230
+ @facet.translate!(16.5, 9.5, 0.75)
231
+ end
232
+ end
257
233
  end
@@ -37,115 +37,33 @@ describe Line do
37
37
 
38
38
  describe "#intersection_at_z" do
39
39
  context "for a line that intersects the target Z plane" do
40
- context "and is located at the origin" do
40
+ context "and spans both positive and negative space" do
41
41
  context "with a positive Z vector" do
42
42
  before do
43
- @line = Line.new(Vertex.new(0.0, 0.0, 0.0), Vertex.new(0.0, 0.0, 6.0))
44
- end
45
-
46
- it "should return a a Point" do
47
- @line.intersection_at_z(3.0).should be_a Point
43
+ @line = Line.new(Vertex.new(-1.0, 1.0, 1.0), Vertex.new(1.0, 1.0, -1.0))
48
44
  end
49
45
 
50
46
  it "should return a Point representing the intersection" do
51
- @line.intersection_at_z(3.0).x.should == 0.0
52
- @line.intersection_at_z(3.0).y.should == 0.0
53
- @line.intersection_at_z(3.0).z.should == 3.0
47
+ @line.intersection_at_z(0).x.should == 0.0
48
+ @line.intersection_at_z(0).y.should == 1.0
49
+ @line.intersection_at_z(0).z.should == 0.0
54
50
  end
55
51
  end
56
52
 
57
53
  context "with a negative Z vector" do
58
54
  before do
59
- @line = Line.new(Vertex.new(0.0, 0.0, 6.0), Vertex.new(0.0, 0.0, 0.0))
55
+ @line = Line.new(Vertex.new(1.0, 1.0, 1.0), Vertex.new(-1.0, -1.0, -1.0))
60
56
  end
61
57
 
62
58
  it "should return a Point representing the intersection" do
63
- @line.intersection_at_z(3.0).x.should == 0.0
64
- @line.intersection_at_z(3.0).y.should == 0.0
65
- @line.intersection_at_z(3.0).z.should == 3.0
66
- end
67
- end
68
- end
69
-
70
- context "and is located away from the origin" do
71
- context "with a positive Z vector" do
72
- before do
73
- @line = Line.new(Vertex.new(7.0, 7.0, 1.0), Vertex.new(7.0, 7.0, 7.0))
74
- end
75
-
76
- it "should return a Point representing the intersection" do
77
- @line.intersection_at_z(4.0).x.should == 7.0
78
- @line.intersection_at_z(4.0).y.should == 7.0
79
- @line.intersection_at_z(4.0).z.should == 4.0
80
- end
81
- end
82
-
83
- context "with a negative Z vector" do
84
- before do
85
- @line = Line.new(Vertex.new(7.0, 7.0, 7.0), Vertex.new(1.0, 1.0, 1.0))
86
- end
87
-
88
- it "should return a Point representing the intersection" do
89
- @line.intersection_at_z(4.0).x.should == 4.0
90
- @line.intersection_at_z(4.0).y.should == 4.0
91
- @line.intersection_at_z(4.0).z.should == 4.0
59
+ @line.intersection_at_z(0).x.should == 0
60
+ @line.intersection_at_z(0).y.should == 0
61
+ @line.intersection_at_z(0).z.should == 0
92
62
  end
93
63
  end
94
64
  end
95
65
  end
96
-
97
- context "and is located in negative space" do
98
- context "with a negative Z vector" do
99
- before do
100
- @line = Line.new(Vertex.new(-1.0, -1.0, -1.0), Vertex.new(-7.0, -7.0, -7.0))
101
- end
102
-
103
- it "should return a Point representing the intersection" do
104
- @line.intersection_at_z(-4.0).x.should == -4.0
105
- @line.intersection_at_z(-4.0).y.should == -4.0
106
- @line.intersection_at_z(-4.0).z.should == -4.0
107
- end
108
- end
109
-
110
- context "with a positive Z vector" do
111
- before do
112
- @line = Line.new(Vertex.new(-7.0, -7.0, -7.0), Vertex.new(-1.0, -1.0, -1.0))
113
- end
114
-
115
- it "should return a Point representing the intersection" do
116
- @line.intersection_at_z(-4.0).x.should == -4.0
117
- @line.intersection_at_z(-4.0).y.should == -4.0
118
- @line.intersection_at_z(-4.0).z.should == -4.0
119
- end
120
- end
121
- end
122
-
123
- context "and spans both positive and negative space" do
124
- context "with a positive Z vector" do
125
- before do
126
- @line = Line.new(Vertex.new(-1.0, 1.0, 1.0), Vertex.new(1.0, 1.0, -1.0))
127
- end
128
-
129
- it "should return a Point representing the intersection" do
130
- @line.intersection_at_z(0).x.should == 0.0
131
- @line.intersection_at_z(0).y.should == 1.0
132
- @line.intersection_at_z(0).z.should == 0.0
133
- end
134
- end
135
66
 
136
- context "with a negative Z vector" do
137
- before do
138
- @line = Line.new(Vertex.new(1.0, 1.0, 1.0), Vertex.new(-1.0, -1.0, -1.0))
139
- end
140
-
141
- it "should return a Point representing the intersection" do
142
- @line.intersection_at_z(0).x.should == 0
143
- @line.intersection_at_z(0).y.should == 0
144
- @line.intersection_at_z(0).z.should == 0
145
- end
146
- end
147
- end
148
-
149
67
  context "for a line that lies on the target Z plane" do
150
68
  before do
151
69
  @line = Line.new(Vertex.new(0.0, 0.0, 3.0), Vertex.new(0.0, 6.0, 3.0))
@@ -182,9 +100,9 @@ describe Line do
182
100
  describe "#to_svg_path" do
183
101
  it "should return a string containing an SVG path" do
184
102
  line = Line.new(Vertex.new(0.0, 0.0, 0.0), Vertex.new(1.0, 1.0, 1.0))
185
- expected_output = '<path d="M 0.0 0.0 L 1.0 1.0" fill="none" stroke="black" stroke-width="1" />'
103
+ expected_output = '<path d="M 0.0 0.0 L 1.0 1.0" fill="none" stroke="black" stroke-width="0.005" />'
186
104
 
187
- line.to_svg_path.should == expected_output
105
+ line.to_svg_path(:inches).should == expected_output
188
106
  end
189
107
  end
190
108
  end
@@ -64,4 +64,25 @@ describe Point do
64
64
  (Point.new(1.0, 2.0, -3.1) == Point.new(1.0, 2.0, -3.2)).should be_false
65
65
  end
66
66
  end
67
+
68
+ describe "#translate!" do
69
+ before do
70
+ @point = Point.new(1.0, 1.0, 1.0)
71
+ end
72
+
73
+ it "should add the supplied value to X" do
74
+ @point.translate!(16.5, 9.5, 0.75)
75
+ @point.x.should == 17.5
76
+ end
77
+
78
+ it "should add the supplied value to Y" do
79
+ @point.translate!(16.5, 9.5, 0.75)
80
+ @point.y.should == 10.5
81
+ end
82
+
83
+ it "should add the supplied value to Z" do
84
+ @point.translate!(16.5, 9.5, 0.75)
85
+ @point.z.should == 1.75
86
+ end
87
+ end
67
88
  end
@@ -8,13 +8,13 @@ describe Polyline do
8
8
 
9
9
  expected_svg = '<?xml version="1.0" standalone="no"?>' + "\n"
10
10
  expected_svg += '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' + "\n"
11
- expected_svg += '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">' + "\n"
12
- expected_svg += ' <g transform="translate(2,2)">' + "\n"
13
- expected_svg += " #{line.to_svg_path}\n"
11
+ expected_svg += '<svg x="0" y="0" width="20in" height="30in" viewBox="0 0 20 30" xmlns="http://www.w3.org/2000/svg" version="1.1">' + "\n"
12
+ expected_svg += ' <g transform="translate(1in,2in)">' + "\n"
13
+ expected_svg += " #{line.to_svg_path(:inches)}\n"
14
14
  expected_svg += ' </g>' + "\n"
15
15
  expected_svg += '</svg>'
16
16
 
17
- polyline.to_svg(2, 2).should == expected_svg
17
+ polyline.to_svg(20, 30, :inches, 1, 2).should == expected_svg
18
18
  end
19
19
  end
20
20
  end
@@ -79,7 +79,130 @@ describe Solid do
79
79
  it "should return a Polyline" do
80
80
  @solid.slice_at_z(0).should be_a Polyline
81
81
  end
82
+ end
83
+
84
+ describe "#get_bounds" do
85
+ before do
86
+ @solid = Solid.parse(<<-EOD)
87
+ solid y-axis-spacer
88
+ facet normal 0.0 0.0 -1.0
89
+ outer loop
90
+ vertex -16.5 0.0 -0.75
91
+ vertex 0.0 -9.5 -0.75
92
+ vertex 0.0 0.0 -0.75
93
+ endloop
94
+ endfacet
95
+ facet normal -0.0 1.0 0.0
96
+ outer loop
97
+ vertex 0.0 -1.87 0.0
98
+ vertex 16.5 -1.87 -0.13
99
+ vertex 0.0 1.87 -0.13
100
+ endloop
101
+ endfacet
102
+ endsolid y-axis-spacer
103
+ EOD
104
+ end
105
+
106
+ it "should return an array" do
107
+ @solid.get_bounds.should be_a Array
108
+ end
109
+
110
+ it "should return a point with the smallest bounds" do
111
+ @solid.get_bounds[0].x.should == -16.5
112
+ @solid.get_bounds[0].y.should == -9.5
113
+ @solid.get_bounds[0].z.should == -0.75
114
+ end
82
115
 
116
+ it "should return a point with the largest bounds" do
117
+ @solid.get_bounds[1].x.should == 16.5
118
+ @solid.get_bounds[1].y.should == 1.87
119
+ @solid.get_bounds[1].z.should == 0.0
120
+ end
121
+ end
122
+
123
+ describe "#translate!" do
124
+ before do
125
+ @solid = Solid.parse(<<-EOD)
126
+ solid y-axis-spacer
127
+ facet normal 0.0 0.0 -1.0
128
+ outer loop
129
+ vertex -16.5 0.0 -0.75
130
+ vertex 0.0 -9.5 -0.75
131
+ vertex 0.0 0.0 -0.75
132
+ endloop
133
+ endfacet
134
+ facet normal -0.0 1.0 0.0
135
+ outer loop
136
+ vertex 0.0 -1.87 0.0
137
+ vertex 16.5 -1.87 -0.13
138
+ vertex 0.0 1.87 -0.13
139
+ endloop
140
+ endfacet
141
+ endsolid y-axis-spacer
142
+ EOD
143
+ end
83
144
 
145
+ it "should call translate on each of it's Facets" do
146
+ @solid.facets[0].should_receive(:translate!).with(16.5, 9.5, 0.75)
147
+ @solid.facets[1].should_receive(:translate!).with(16.5, 9.5, 0.75)
148
+
149
+ @solid.translate!(16.5, 9.5, 0.75)
150
+ end
151
+ end
152
+
153
+ describe "#align_to_origin!" do
154
+ before do
155
+ @solid = Solid.parse(<<-EOD)
156
+ solid y-axis-spacer
157
+ facet normal 0.0 0.0 -1.0
158
+ outer loop
159
+ vertex -16.5 0.0 5.0
160
+ vertex 0.0 -9.5 10.0
161
+ vertex 0.0 0.0 5.0
162
+ endloop
163
+ endfacet
164
+ facet normal -0.0 1.0 0.0
165
+ outer loop
166
+ vertex 0.0 -1.87 5.0
167
+ vertex 16.5 -1.87 11.0
168
+ vertex 0.0 1.87 6.0
169
+ endloop
170
+ endfacet
171
+ endsolid y-axis-spacer
172
+ EOD
173
+ end
174
+
175
+ it "should translate solid so the lowermost XYZ edges are all 0.0" do
176
+ @solid.should_receive(:translate!).with(16.5, 9.5, -5.0)
177
+ @solid.align_to_origin!
178
+ end
179
+ end
180
+
181
+ describe "#center!" do
182
+ before do
183
+ @solid = Solid.parse(<<-EOD)
184
+ solid y-axis-spacer
185
+ facet normal 0.0 0.0 -1.0
186
+ outer loop
187
+ vertex -16.5 0.0 5.0
188
+ vertex 0.0 -9.5 10.0
189
+ vertex 0.0 0.0 5.0
190
+ endloop
191
+ endfacet
192
+ facet normal -0.0 1.0 0.0
193
+ outer loop
194
+ vertex 0.0 -1.87 5.0
195
+ vertex 17.5 -1.87 11.0
196
+ vertex 0.0 1.5 6.0
197
+ endloop
198
+ endfacet
199
+ endsolid y-axis-spacer
200
+ EOD
201
+ end
202
+
203
+ it "should translate solid so the lowermost XYZ edges are all 0.0" do
204
+ @solid.should_receive(:translate!).with(-0.5, 4.0, -8.0)
205
+ @solid.center!
206
+ end
84
207
  end
85
208
  end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe Units do
4
+ describe ".name" do
5
+ it "should return 'inches' for :inches" do
6
+ Units.name(:inches).should == 'inches'
7
+ end
8
+
9
+ it "should return 'centimeters' for :centimeters" do
10
+ Units.name(:centimeters).should == 'centimeters'
11
+ end
12
+
13
+ it "should return 'millimeters' for :millimeters" do
14
+ Units.name(:millimeters).should == 'millimeters'
15
+ end
16
+
17
+ it "should return 'none' for :none" do
18
+ Units.name(:none).should == 'none'
19
+ end
20
+
21
+ it "should raise an exception if called with an unknown unit" do
22
+ lambda {
23
+ Units.name(:error)
24
+ }.should raise_error
25
+ end
26
+ end
27
+
28
+ describe ".svg_name" do
29
+ it "should return 'in' for :inches" do
30
+ Units.svg_name(:inches).should == 'in'
31
+ end
32
+
33
+ it "should return 'cm' for :centimeters" do
34
+ Units.svg_name(:centimeters).should == 'cm'
35
+ end
36
+
37
+ it "should return 'mm' for :millimeters" do
38
+ Units.svg_name(:millimeters).should == 'mm'
39
+ end
40
+
41
+ it "should return '' for :none" do
42
+ Units.svg_name(:none).should == ''
43
+ end
44
+
45
+ it "should raise an exception if called with an unknown unit" do
46
+ lambda {
47
+ Units.svg_name(:error)
48
+ }.should raise_error
49
+ end
50
+ end
51
+
52
+ describe ".stroke_width" do
53
+ it "should return 'in' for :inches" do
54
+ Units.stroke_width(:inches).should == 0.005
55
+ end
56
+
57
+ it "should return 'cm' for :centimeters" do
58
+ Units.stroke_width(:centimeters).should == 0.01
59
+ end
60
+
61
+ it "should return 'mm' for :millimeters" do
62
+ Units.stroke_width(:millimeters).should == 0.1
63
+ end
64
+
65
+ it "should return '' for :none" do
66
+ Units.stroke_width(:none).should == 0.1
67
+ end
68
+
69
+ it "should raise an exception if called with an unknown unit" do
70
+ lambda {
71
+ Units.stroke_width(:error)
72
+ }.should raise_error
73
+ end
74
+ end
75
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: triangular
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.1
5
+ version: 0.0.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - Aaron Gough
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-10-17 00:00:00 Z
13
+ date: 2011-10-19 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -59,6 +59,7 @@ files:
59
59
  - lib/triangular/point.rb
60
60
  - lib/triangular/polyline.rb
61
61
  - lib/triangular/solid.rb
62
+ - lib/triangular/units.rb
62
63
  - lib/triangular/vector.rb
63
64
  - lib/triangular/version.rb
64
65
  - lib/triangular/vertex.rb
@@ -71,6 +72,7 @@ files:
71
72
  - spec/solid_spec.rb
72
73
  - spec/spec_helper.rb
73
74
  - spec/triangular_spec.rb
75
+ - spec/units_spec.rb
74
76
  - spec/vertex_spec.rb
75
77
  - triangular.gemspec
76
78
  homepage: http://rubygems.org/gems/triangular