rcad 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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')
@@ -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
@@ -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