rcad 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +25 -0
- data/Gemfile +4 -0
- data/README.md +22 -0
- data/Rakefile +6 -0
- data/ext/_rcad/_rcad.cpp +795 -0
- data/ext/_rcad/extconf.rb +22 -0
- data/lib/rcad.rb +384 -0
- data/lib/rcad/gears.rb +144 -0
- data/lib/rcad/nuts.rb +87 -0
- data/lib/rcad/version.rb +3 -0
- data/pedestal.rb +13 -0
- data/rcad.gemspec +27 -0
- metadata +122 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'mkmf-rice'
|
2
|
+
|
3
|
+
OCE_INCLUDE_DIR = '/usr/local/include/oce'
|
4
|
+
OCE_LIB_DIR = '/usr/local/lib'
|
5
|
+
|
6
|
+
dir_config('TKG3d', OCE_INCLUDE_DIR, OCE_LIB_DIR)
|
7
|
+
dir_config('TKBRep', OCE_INCLUDE_DIR, OCE_LIB_DIR)
|
8
|
+
dir_config('TKPrim', OCE_INCLUDE_DIR, OCE_LIB_DIR)
|
9
|
+
dir_config('TKOffset', OCE_INCLUDE_DIR, OCE_LIB_DIR)
|
10
|
+
dir_config('TKBO', OCE_INCLUDE_DIR, OCE_LIB_DIR)
|
11
|
+
dir_config('TKSTL', OCE_INCLUDE_DIR, OCE_LIB_DIR)
|
12
|
+
dir_config('qhull', '/usr/include/qhull', '/usr/lib')
|
13
|
+
|
14
|
+
have_library('TKG3d') or raise
|
15
|
+
have_library('TKBRep') or raise
|
16
|
+
have_library('TKPrim') or raise
|
17
|
+
have_library('TKOffset') or raise
|
18
|
+
have_library('TKBO') or raise
|
19
|
+
have_library('TKSTL') or raise
|
20
|
+
have_library('qhull') or raise
|
21
|
+
|
22
|
+
create_makefile('rcad/_rcad')
|
data/lib/rcad.rb
ADDED
@@ -0,0 +1,384 @@
|
|
1
|
+
require 'rcad/version'
|
2
|
+
require 'rcad/_rcad'
|
3
|
+
|
4
|
+
|
5
|
+
class Numeric
|
6
|
+
def mm
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def cm
|
11
|
+
self * 10.0
|
12
|
+
end
|
13
|
+
|
14
|
+
def um
|
15
|
+
self / 1000.0
|
16
|
+
end
|
17
|
+
|
18
|
+
def in
|
19
|
+
self * 25.4
|
20
|
+
end
|
21
|
+
|
22
|
+
def deg_to_rad
|
23
|
+
self * Math::PI / 180
|
24
|
+
end
|
25
|
+
|
26
|
+
def rad_to_deg
|
27
|
+
self * 180 / Math::PI
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def to_polar(r, a)
|
33
|
+
return [r * Math::cos(a), r * Math::sin(a)]
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
TOLERANCE = 50.um
|
38
|
+
|
39
|
+
|
40
|
+
class Shape
|
41
|
+
# if @shape isn't defined in a Shape's initialize() method, then render()
|
42
|
+
# should be overridden to create and return it on-the-fly.
|
43
|
+
def render
|
44
|
+
@shape
|
45
|
+
end
|
46
|
+
|
47
|
+
def +(right)
|
48
|
+
Union.new(self, right)
|
49
|
+
end
|
50
|
+
|
51
|
+
def -(right)
|
52
|
+
Difference.new(self, right)
|
53
|
+
end
|
54
|
+
|
55
|
+
def *(right)
|
56
|
+
Intersection.new(self, right)
|
57
|
+
end
|
58
|
+
|
59
|
+
def ~@
|
60
|
+
if $shape_mode == :hull
|
61
|
+
$shape << self
|
62
|
+
else
|
63
|
+
$shape = ($shape == nil) ? self : $shape.send($shape_mode, self)
|
64
|
+
end
|
65
|
+
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def move_x(delta)
|
70
|
+
move(delta, 0, 0)
|
71
|
+
end
|
72
|
+
|
73
|
+
def move_y(delta)
|
74
|
+
move(0, delta, 0)
|
75
|
+
end
|
76
|
+
|
77
|
+
def move_z(delta)
|
78
|
+
move(0, 0, delta)
|
79
|
+
end
|
80
|
+
|
81
|
+
def rot_x(angle)
|
82
|
+
rotate(angle, [1, 0, 0])
|
83
|
+
end
|
84
|
+
|
85
|
+
def rot_y(angle)
|
86
|
+
rotate(angle, [0, 1, 0])
|
87
|
+
end
|
88
|
+
|
89
|
+
def rot_z(angle)
|
90
|
+
rotate(angle, [0, 0, 1])
|
91
|
+
end
|
92
|
+
|
93
|
+
def scale_x(factor)
|
94
|
+
scale(factor, 1, 1)
|
95
|
+
end
|
96
|
+
|
97
|
+
def scale_y(factor)
|
98
|
+
scale(1, factor, 1)
|
99
|
+
end
|
100
|
+
|
101
|
+
def scale_z(factor)
|
102
|
+
scale(1, 1, factor)
|
103
|
+
end
|
104
|
+
|
105
|
+
def extrude(height, twist=0)
|
106
|
+
LinearExtrusion.new(self, height, twist)
|
107
|
+
end
|
108
|
+
|
109
|
+
def revolve(angle=360.deg_to_rad)
|
110
|
+
Revolution.new(self, angle)
|
111
|
+
end
|
112
|
+
|
113
|
+
def bbox
|
114
|
+
# TODO
|
115
|
+
end
|
116
|
+
|
117
|
+
def min_x
|
118
|
+
bbox[0].x
|
119
|
+
end
|
120
|
+
|
121
|
+
def min_y
|
122
|
+
bbox[0].y
|
123
|
+
end
|
124
|
+
|
125
|
+
def min_z
|
126
|
+
bbox[0].z
|
127
|
+
end
|
128
|
+
|
129
|
+
def max_x
|
130
|
+
bbox[1].x
|
131
|
+
end
|
132
|
+
|
133
|
+
def max_y
|
134
|
+
bbox[1].y
|
135
|
+
end
|
136
|
+
|
137
|
+
def max_z
|
138
|
+
bbox[1].z
|
139
|
+
end
|
140
|
+
|
141
|
+
def x_size
|
142
|
+
max_x - min_x
|
143
|
+
end
|
144
|
+
|
145
|
+
def y_size
|
146
|
+
max_y - min_y
|
147
|
+
end
|
148
|
+
|
149
|
+
def z_size
|
150
|
+
max_z - min_z
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
$shape_stack = []
|
155
|
+
$shape = nil
|
156
|
+
$shape_mode = :+
|
157
|
+
|
158
|
+
def _shape_mode_block(mode, &block)
|
159
|
+
$shape_stack.push([$shape, $shape_mode])
|
160
|
+
$shape = nil
|
161
|
+
$shape_mode = mode
|
162
|
+
|
163
|
+
block.call
|
164
|
+
res = $shape
|
165
|
+
|
166
|
+
$shape, $shape_mode = $shape_stack.pop
|
167
|
+
res
|
168
|
+
end
|
169
|
+
|
170
|
+
def add(&block)
|
171
|
+
_shape_mode_block(:+, &block)
|
172
|
+
end
|
173
|
+
|
174
|
+
def sub(&block)
|
175
|
+
_shape_mode_block(:-, &block)
|
176
|
+
end
|
177
|
+
|
178
|
+
def mul(&block)
|
179
|
+
_shape_mode_block(:*, &block)
|
180
|
+
end
|
181
|
+
|
182
|
+
def hull(&block)
|
183
|
+
$shape_stack.push([$shape, $shape_mode])
|
184
|
+
$shape = []
|
185
|
+
$shape_mode = :hull
|
186
|
+
|
187
|
+
block.call
|
188
|
+
res = _hull($shape)
|
189
|
+
|
190
|
+
$shape, $shape_mode = $shape_stack.pop
|
191
|
+
res
|
192
|
+
end
|
193
|
+
|
194
|
+
def write_stl(*args)
|
195
|
+
$shape != nil or raise
|
196
|
+
$shape.write_stl(*args)
|
197
|
+
end
|
198
|
+
|
199
|
+
def clear_shape
|
200
|
+
$shape = nil
|
201
|
+
end
|
202
|
+
|
203
|
+
at_exit do
|
204
|
+
if $shape && ($! == nil)
|
205
|
+
output_file = File.basename($0, ".*") + ".stl"
|
206
|
+
printf("Rendering '%s'\n", output_file)
|
207
|
+
write_stl(output_file)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
class Polygon
|
213
|
+
attr_reader :points, :paths
|
214
|
+
|
215
|
+
def initialize(points, paths=nil)
|
216
|
+
@points = points
|
217
|
+
@paths = paths || [(0...points.size).to_a]
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
class RegularPolygon < Polygon
|
223
|
+
attr_reader :sides, :radius
|
224
|
+
|
225
|
+
def initialize(sides, radius)
|
226
|
+
@sides = sides
|
227
|
+
@radius = radius
|
228
|
+
|
229
|
+
angles = (1..sides).map { |i| i * 2 * Math::PI / sides }
|
230
|
+
points = angles.map { |a| to_polar(radius, a) }
|
231
|
+
super(points)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
class Square < Polygon
|
237
|
+
attr_reader :size
|
238
|
+
|
239
|
+
def initialize(size)
|
240
|
+
@size = size
|
241
|
+
|
242
|
+
super([[0,0], [size,0], [size,size], [0,size]])
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
class Circle < Shape
|
248
|
+
attr_accessor :dia
|
249
|
+
|
250
|
+
def initialize(dia)
|
251
|
+
@dia = dia
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
class Text < Shape
|
257
|
+
# TODO
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
class Box < Shape
|
262
|
+
attr_accessor :xsize, :ysize, :zsize
|
263
|
+
|
264
|
+
def initialize(xsize, ysize, zsize)
|
265
|
+
@xsize = xsize
|
266
|
+
@ysize = ysize
|
267
|
+
@zsize = zsize
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
class Cube < Box
|
272
|
+
def initialize(size)
|
273
|
+
super(size, size, size)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
|
278
|
+
class Cylinder < Shape
|
279
|
+
attr_accessor :height, :dia
|
280
|
+
|
281
|
+
def initialize(height, dia)
|
282
|
+
@height = height
|
283
|
+
@dia = dia
|
284
|
+
end
|
285
|
+
|
286
|
+
def radius
|
287
|
+
dia / 2.0
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
class Sphere < Shape
|
293
|
+
attr_accessor :dia
|
294
|
+
|
295
|
+
def initialize(dia)
|
296
|
+
@dia = dia
|
297
|
+
end
|
298
|
+
|
299
|
+
def radius
|
300
|
+
dia / 2.0
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
|
305
|
+
class Polyhedron < Shape
|
306
|
+
attr_accessor :points, :faces
|
307
|
+
|
308
|
+
def initialize(points, faces)
|
309
|
+
@points = points
|
310
|
+
@faces = faces
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
class Torus < Shape
|
316
|
+
attr_accessor :inner_dia, :outer_dia, :angle
|
317
|
+
|
318
|
+
def initialize(inner_dia, outer_dia, angle=nil)
|
319
|
+
@inner_dia = inner_dia
|
320
|
+
@outer_dia = outer_dia
|
321
|
+
@angle = angle
|
322
|
+
end
|
323
|
+
|
324
|
+
def inner_radius
|
325
|
+
inner_dia / 2.0
|
326
|
+
end
|
327
|
+
|
328
|
+
def outer_radius
|
329
|
+
outer_dia / 2.0
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
class LinearExtrusion < Shape
|
335
|
+
attr_reader :profile, :height
|
336
|
+
|
337
|
+
def initialize(profile, height, twist=0)
|
338
|
+
@profile = profile
|
339
|
+
@height = height
|
340
|
+
@twist = twist
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
|
345
|
+
class Revolution < Shape
|
346
|
+
attr_reader :profile, :angle
|
347
|
+
|
348
|
+
def initialize(profile, angle=nil)
|
349
|
+
@profile = profile
|
350
|
+
@angle = angle
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
class RegularPrism < LinearExtrusion
|
356
|
+
attr_reader :sides, :radius
|
357
|
+
|
358
|
+
def initialize(sides, radius, height)
|
359
|
+
@sides = sides
|
360
|
+
@radius = radius
|
361
|
+
|
362
|
+
poly = RegularPolygon.new(sides, radius)
|
363
|
+
super(poly, height)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
|
368
|
+
def make_maker(name, klass)
|
369
|
+
Object.send(:define_method, name, &klass.method(:new))
|
370
|
+
end
|
371
|
+
|
372
|
+
make_maker :polygon, Polygon
|
373
|
+
make_maker :reg_poly, RegularPolygon
|
374
|
+
make_maker :square, Square
|
375
|
+
make_maker :circle, Circle
|
376
|
+
make_maker :text, Text
|
377
|
+
|
378
|
+
make_maker :box, Box
|
379
|
+
make_maker :cube, Cube
|
380
|
+
make_maker :cylinder, Cylinder
|
381
|
+
make_maker :sphere, Sphere
|
382
|
+
make_maker :polyhedron, Polyhedron
|
383
|
+
make_maker :torus, Torus
|
384
|
+
make_maker :reg_prism, RegularPrism
|
data/lib/rcad/gears.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# calculations based on:
|
2
|
+
# * http://makezine.com/2010/06/28/make-your-own-gears/
|
3
|
+
# based in turn on http://www.bostongear.com/pdf/gear_theory.pdf
|
4
|
+
# (can be found in Wayback Machine)
|
5
|
+
# * http://en.wikipedia.org/wiki/Gear
|
6
|
+
# * http://www.metrication.com/engineering/gears.html
|
7
|
+
|
8
|
+
|
9
|
+
require 'rcad'
|
10
|
+
|
11
|
+
|
12
|
+
class GearProfile < Shape
|
13
|
+
attr_reader :pitch_dia, :module_, :p_angle
|
14
|
+
|
15
|
+
# pitch_dia - effective diameter of gear
|
16
|
+
# (not the same as outer diameter)
|
17
|
+
# module_ - ratio of pitch diameter to number of teeth (basically the
|
18
|
+
# arc length of the tooth spacing)
|
19
|
+
# p_angle - pressure angle.
|
20
|
+
# it seems 20 deg angle is better for torque, but
|
21
|
+
# 14.5 deg angle is better for backlash.
|
22
|
+
def initialize(pitch_dia, module_=4, p_angle=20)
|
23
|
+
# converting everything to floats ensures that floating point
|
24
|
+
# division will be performed later
|
25
|
+
@pitch_dia = pitch_dia.to_f
|
26
|
+
@module_ = module_.to_f
|
27
|
+
@p_angle = p_angle.to_f
|
28
|
+
|
29
|
+
if @pitch_dia % @module_ != 0
|
30
|
+
raise ArgumentError, "non-integer number of teeth!"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def num_teeth
|
35
|
+
(pitch_dia / module_).to_int
|
36
|
+
end
|
37
|
+
|
38
|
+
def diametrical_pitch
|
39
|
+
1.0 / module_
|
40
|
+
end
|
41
|
+
|
42
|
+
def circular_pitch
|
43
|
+
Math::PI / diametrical_pitch
|
44
|
+
end
|
45
|
+
|
46
|
+
def addendum
|
47
|
+
1.0 / diametrical_pitch
|
48
|
+
end
|
49
|
+
|
50
|
+
def outer_dia
|
51
|
+
pitch_dia + 2.0 * addendum
|
52
|
+
end
|
53
|
+
|
54
|
+
def whole_depth
|
55
|
+
module_ < 1.25 ? (2.4 * module_) : (2.25 * module_)
|
56
|
+
end
|
57
|
+
|
58
|
+
def dedendum
|
59
|
+
whole_depth - addendum
|
60
|
+
end
|
61
|
+
|
62
|
+
def root_dia
|
63
|
+
pitch_dia - 2 * dedendum
|
64
|
+
end
|
65
|
+
|
66
|
+
# tooth thickness at pitch dia
|
67
|
+
def tooth_thickness
|
68
|
+
Math::PI / 2.0 / diametrical_pitch
|
69
|
+
end
|
70
|
+
|
71
|
+
def render
|
72
|
+
# tooth thickness at tooth tip (TODO: is this correct?)
|
73
|
+
tooth_tip_thickness = tooth_thickness - addendum * Math::sin(p_angle)
|
74
|
+
|
75
|
+
# half of thickness at root/center/tip in degrees
|
76
|
+
half_t_root_angle = Math::atan(tooth_thickness / 2 / (root_dia / 2))
|
77
|
+
half_t_angle = Math::atan(tooth_thickness / 2 / (pitch_dia / 2))
|
78
|
+
half_t_tip_angle = Math::atan(tooth_tip_thickness / 2 / (outer_dia / 2))
|
79
|
+
|
80
|
+
root_r = root_dia / 2
|
81
|
+
pitch_r = pitch_dia / 2
|
82
|
+
outer_r = outer_dia / 2
|
83
|
+
|
84
|
+
points = []
|
85
|
+
(1..num_teeth).each do |i|
|
86
|
+
angle = (2 * Math::PI / num_teeth) * i
|
87
|
+
points << to_polar(root_r, angle - half_t_root_angle)
|
88
|
+
points << to_polar(pitch_r, angle - half_t_angle)
|
89
|
+
points << to_polar(outer_r, angle - half_t_tip_angle)
|
90
|
+
points << to_polar(outer_r, angle + half_t_tip_angle)
|
91
|
+
points << to_polar(pitch_r, angle + half_t_angle)
|
92
|
+
points << to_polar(root_r, angle + half_t_root_angle)
|
93
|
+
end
|
94
|
+
|
95
|
+
polygon(points)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
class SpurGear < Shape
|
101
|
+
attr_reader :height, :profile
|
102
|
+
|
103
|
+
def initialize(height, *args)
|
104
|
+
@height = height
|
105
|
+
@profile = GearProfile.new(*args)
|
106
|
+
@shape = profile.extrude(height)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
class HelicalGear < Shape
|
112
|
+
attr_reader :height, :helix_angle, :profile
|
113
|
+
|
114
|
+
def initialize(pitch_dia, height, helix_angle=Math::PI / 3.0)
|
115
|
+
@height = height
|
116
|
+
@helix_angle = helix_angle
|
117
|
+
@profile = GearProfile.new(pitch_dia)
|
118
|
+
|
119
|
+
@shape = profile.extrude(height, twist)
|
120
|
+
end
|
121
|
+
|
122
|
+
def twist
|
123
|
+
twist_length = Math::tan(helix_angle) * height
|
124
|
+
pitch_circumference = Math::PI * profile.pitch_dia
|
125
|
+
|
126
|
+
twist_length * (2 * Math::PI / pitch_circumference)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
class HerringboneGear < Shape
|
132
|
+
attr_reader :helical_gear
|
133
|
+
|
134
|
+
def initialize(pitch_dia, height, helix_angle=Math::PI / 3.0)
|
135
|
+
@helical_gear = HelicalGear.new(pitch_dia, height / 2.0, helix_angle)
|
136
|
+
|
137
|
+
@shape = add do
|
138
|
+
~helical_gear
|
139
|
+
~helical_gear
|
140
|
+
.scale_z(-1)
|
141
|
+
.move_z(height)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|