triangular 0.0.1 → 0.0.2

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