jenncad 1.0.0.pre.alpha13 → 1.0.0.pre.alpha16

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.
@@ -0,0 +1,35 @@
1
+ module JennCad::Primitives
2
+ class Polyhedron < Primitive
3
+ attr_accessor :points, :faces, :convexity
4
+ def initialize(args)
5
+ @opts = args
6
+ @points = args[:points]
7
+ @faces = args[:faces]
8
+ @convexity = args[:convexity] || 10
9
+
10
+ super
11
+ end
12
+
13
+ def face(i)
14
+ unless @faces[i]
15
+ $log.error "polyhedron: Cannot find face #{i}"
16
+ return self
17
+ end
18
+ face = 0
19
+ poly_faces = []
20
+ poly_points = []
21
+ @faces[i].each do |f|
22
+ point = @points[f]
23
+ if point.nil?
24
+ $log.error "polyhedron: Cannot find point #{f} for face #{i}"
25
+ end
26
+ poly_points << point
27
+ poly_faces << face
28
+ face += 1
29
+ #poly_points << [point[0], point[1]]
30
+ end
31
+ #polygon(points: poly_points)
32
+ polyhedron(points: poly_points, faces: [poly_faces, poly_faces.reverse])
33
+ end
34
+ end
35
+ end
@@ -5,14 +5,14 @@ module JennCad::Primitives
5
5
  end
6
6
 
7
7
  def handle_margins
8
- @x = @opts[:x] + @opts[:margins][:x]
9
- @y = @opts[:y] + @opts[:margins][:y]
10
- @z = @opts[:z] + @opts[:margins][:z]
8
+ @x = @opts[:x].to_d + @opts[:margins][:x].to_d
9
+ @y = @opts[:y].to_d + @opts[:margins][:y].to_d
10
+ @z = @opts[:z].to_d + @opts[:margins][:z].to_d
11
11
  end
12
12
 
13
13
  def handle_diameter
14
- @d = opts[:d]
15
- @r = opts[:r]
14
+ @d = opts[:d].to_d
15
+ @r = opts[:r].to_d
16
16
  if @d
17
17
  @r = @d/2.0
18
18
  elsif @r
@@ -64,18 +64,20 @@ module JennCad::Primitives
64
64
  res
65
65
  end
66
66
 
67
- def flat(edge)
67
+ def flat(*edges)
68
68
  @opts[:flat_edges] ||= []
69
- @opts[:flat_edges] << edge
69
+ edges.each do |edge|
70
+ @opts[:flat_edges] << edge
71
+ end
70
72
  self
71
73
  end
72
74
 
73
75
  private
74
76
  def apply_flat_edge(edge)
75
77
  case edge
76
- when :up
78
+ when :up, :top
77
79
  cube(x: @x, y: @y/2.0, z: @z).nc.moveh(y:@y)
78
- when :down
80
+ when :down, :bottom
79
81
  cube(x: @x, y: @y/2.0, z: @z).nc
80
82
  when :right
81
83
  cube(x: @x/2.0, y: @y, z: @z).nc.moveh(x:@x)
@@ -14,16 +14,21 @@ module JennCad::Primitives
14
14
  args = [:d, :z].zip(args.flatten).to_h
15
15
  args.deep_merge!(m)
16
16
  end
17
-
18
- args[:z] ||= args[:h]
17
+ args = parse_xyz_shortcuts(args)
18
+ if args[:z].to_d > 0
19
+ args[:h] = args[:z]
20
+ else
21
+ args[:z] = nil
22
+ end
19
23
 
20
24
  @opts = {
21
25
  d: 0,
22
26
  a: 0,
23
- z: nil,
24
27
  r: nil,
25
28
  x: 0,
26
29
  y: 0,
30
+ z: nil,
31
+ cz: false,
27
32
  margins: {
28
33
  r: 0,
29
34
  d: 0,
@@ -33,9 +38,13 @@ module JennCad::Primitives
33
38
 
34
39
  super(opts)
35
40
 
36
- @d = @opts[:d]
37
- @a = @opts[:a]
38
- @h = @opts[:h]
41
+ @d = @opts[:d].to_d
42
+ @a = @opts[:a].to_d
43
+ @h = @opts[:h].to_d
44
+ @z = @h
45
+ @x = @opts[:x].to_d
46
+ @y = @opts[:y].to_d
47
+
39
48
  @r = @opts[:r] || nil
40
49
  if @r
41
50
  @d = @r * 2
@@ -54,7 +63,61 @@ module JennCad::Primitives
54
63
 
55
64
  # TODO: this needs anchors like cube
56
65
  # TODO: color on this needs to apply to hull, not on the cylinders.
66
+ set_anchors
67
+ end
68
+
69
+ def cz
70
+ @opts[:cz] = true
71
+ @transformations << Move.new(z: -@z / 2.0)
72
+ set_anchors
73
+ self
74
+ end
75
+
76
+
77
+ def set_anchors
78
+ @anchors = {} # reset anchors
79
+ if @opts[:d]
80
+ rad = @opts[:d] / 2.0
81
+ else
82
+ rad = @opts[:r]
83
+ end
57
84
 
85
+ if @x > 0
86
+ set_anchor :left, x: - rad
87
+ set_anchor :right, x: @x + rad
88
+ elsif @x < 0
89
+ set_anchor :left, x: @x - rad
90
+ set_anchor :right, x: rad
91
+ else
92
+ set_anchor :left, x: -rad
93
+ set_anchor :right, x: rad
94
+ end
95
+ if @y > 0
96
+ set_anchor :bottom, y: - rad
97
+ set_anchor :top, y: @y + rad
98
+ elsif @y < 0
99
+ set_anchor :bottom, y: @y - rad
100
+ set_anchor :top, y: rad
101
+ else
102
+ set_anchor :bottom, y: -rad
103
+ set_anchor :top, y: rad
104
+ end
105
+
106
+ set_anchor :center1, xy: 0
107
+ set_anchor :center2, x: @x, y: @y
108
+
109
+ # TODO: figure out if we also want to have "corners"
110
+ # - possibly move it like a cube
111
+ # - points at 45 ° angles might not be that useful unless you can get the point on the circle at a given angle
112
+ # - inner/outer points could be useful for small $fn values
113
+
114
+ if @opts[:cz]
115
+ set_anchor :bottom_face, z: -@z/2.0
116
+ set_anchor :top_face, z: @z/2.0
117
+ else
118
+ set_anchor :bottom_face, z: 0
119
+ set_anchor :top_face, z: @z
120
+ end
58
121
  end
59
122
 
60
123
  def to_openscad
@@ -79,16 +142,16 @@ module JennCad::Primitives
79
142
  end
80
143
 
81
144
  def end_vector
82
- if @a.to_f == 0.0
145
+ if @a.to_d == 0.0
83
146
  return [@len_x, 0] if @len_x
84
147
  return [0, @len_y] if @len_y
85
148
  end
86
149
  if @len_x
87
- x = cos(PI*@a/180.0)*@len_x.to_f
88
- y = sin(PI*@a/180.0)*@len_x.to_f
150
+ x = cos(PI*@a/180.0)*@len_x.to_d
151
+ y = sin(PI*@a/180.0)*@len_x.to_d
89
152
  else
90
- x = -1* sin(PI*@a/180.0)*@len_y.to_f
91
- y = cos(PI*@a/180.0)*@len_y.to_f
153
+ x = -1* sin(PI*@a/180.0)*@len_y.to_d
154
+ y = cos(PI*@a/180.0)*@len_y.to_d
92
155
  end
93
156
  [x,y]
94
157
  end
@@ -2,13 +2,13 @@ module JennCad::Primitives
2
2
  class SubtractObject < BooleanObject
3
3
  def inherit_z
4
4
  @z = 0
5
- @calc_z = parts.first.calc_z.to_f
5
+ @calc_z = parts.first.calc_z.to_d
6
6
 
7
7
  only_additives_of(@parts).each do |p|
8
8
  if option(:debug)
9
9
  $log.debug "inherit_z checks for: #{p}"
10
10
  end
11
- z = p.z.to_f
11
+ z = p.z.to_d
12
12
  @z = z if z > @z
13
13
  end
14
14
  $log.debug "inherit_z called, biggest z found: #{@z}" if option(:debug)
@@ -57,6 +57,7 @@ module JennCad::Primitives
57
57
  others.each do |part|
58
58
  #puts part.inspect
59
59
  #puts "#{part.calc_z+part.calc_h} ; #{compare_h}"
60
+ add_z = nil
60
61
  if part.respond_to? :z
61
62
  part.opts[:margins] ||= {}
62
63
  if part.referenced_z && part.z != 0.0
@@ -64,28 +65,40 @@ module JennCad::Primitives
64
65
  when JennCad::Circle
65
66
  when JennCad::BooleanObject
66
67
  else
67
- $log.debug part if part.opts[:debug]
68
+ # $log.debug part if part.opts[:debug]
68
69
  part.opts[:margins][:z] ||= 0.0
69
70
  unless part.opts[:margins][:z] == 0.2
71
+ $log.debug "fixing possible z fighting for referenced object: #{part.class} #{part.z} 0.1 down" if part.opts[:debug]
70
72
  part.opts[:margins][:z] = 0.2
71
73
  part.mz(-0.1)
72
74
  end
73
75
  end
74
76
  elsif part.z == compare_h
75
77
  $log.debug "fixing possible z fighting: #{part.class} #{part.z}" if part.opts[:debug]
76
- part.opts[:margins][:z] += 0.008
77
- part.mz(-0.004)
78
+ add_z = 0.008
79
+ move_z = -0.004
78
80
  elsif part.calc_z == compare_z
79
81
  # puts "z fighting at bottom: #{part.calc_z}"
80
- part.opts[:margins][:z] += 0.004
82
+ add_z = 0.004
81
83
  # part.z+=0.004
82
- part.mz(-0.002)
83
- elsif part.calc_z.to_f+part.calc_h.to_f == compare_h
84
+ move_z = -0.002
85
+ elsif part.calc_z.to_d+part.calc_h.to_d == compare_h
84
86
  # puts "z fighting at top: #{compare_h}"
85
87
  #part.z+=0.004
86
- part.opts[:margins][:z] += 0.004
87
- part.mz(0.002)
88
+ add_z = 0.004
89
+ move_z = 0.002
88
90
  end
91
+
92
+ if add_z
93
+ if part.kind_of? Part
94
+ part.modify_values(part, {z: add_z}, {mode: :add})
95
+ end
96
+ part.opts[:margins][:z] += add_z
97
+ part.mz(move_z)
98
+ end
99
+
100
+
101
+
89
102
  end
90
103
  end
91
104
  end
@@ -5,6 +5,7 @@ require "jenncad/primitives/sphere"
5
5
  require "jenncad/primitives/cube"
6
6
  require "jenncad/primitives/rounded_cube"
7
7
  require "jenncad/primitives/polygon"
8
+ require "jenncad/primitives/polyhedron"
8
9
  require "jenncad/primitives/slot"
9
10
  require "jenncad/primitives/boolean_object"
10
11
  require "jenncad/primitives/union_object"
@@ -18,6 +18,10 @@ module JennCad
18
18
  Polygon.new(args).set_parent(self)
19
19
  end
20
20
 
21
+ def polyhedron(args)
22
+ Polyhedron.new(args).set_parent(self)
23
+ end
24
+
21
25
  def slot(*args)
22
26
  Slot.new(args).set_parent(self)
23
27
  end
@@ -93,8 +97,8 @@ module JennCad
93
97
  private
94
98
  def boolean_operation(part, klass)
95
99
  if part.respond_to? :transformations
96
- # Since ruby doesn't provide a way to make a deep clone, this seems to be the simplest solution that will effectively do that:
97
- part = Marshal.load(Marshal.dump(part))
100
+ # Clone the part in place
101
+ part = part.fix
98
102
  end
99
103
 
100
104
  case self
@@ -103,7 +107,12 @@ module JennCad
103
107
  when klass
104
108
  add_or_new(part)
105
109
  else
106
- klass.new(self,part)
110
+ own_part = if self.respond_to? :transformations
111
+ self.fix # clone self
112
+ else
113
+ self
114
+ end
115
+ klass.new(own_part,part)
107
116
  end
108
117
  end
109
118
  end
data/lib/jenncad/thing.rb CHANGED
@@ -33,25 +33,102 @@ module JennCad
33
33
  @opts[key] = val
34
34
  end
35
35
 
36
+ def set_flag(key)
37
+ set_option(key, true)
38
+ self
39
+ end
40
+
41
+ def unset_flag(key)
42
+ set_option(key, false)
43
+ self
44
+ end
45
+
46
+
47
+ def cut_to(face, part=nil, args={})
48
+ an = anchor(face, part)
49
+ unless an
50
+ $log.error "Cannot find anchor to cut_to"
51
+ return self
52
+ end
53
+ if an[:z].to_d == 0.0
54
+ $log.error "cut_to only supports cuts to an anchor with Z set. This anchor: #{an}"
55
+ return self
56
+ end
57
+ modify_values(self, z: an[:z].to_d)
58
+ self.name="#{self.class}_cut_to_#{an[:z].to_f}"
59
+ self
60
+ end
61
+
62
+ def modify_values(parts, value, opts = {})
63
+ case parts
64
+ when Array
65
+ parts.each do |pa|
66
+ modify_values(pa, value, opts)
67
+ end
68
+ else
69
+ if parts.kind_of?(BooleanObject)
70
+ modify_values(parts.only_additives_of(parts), value, opts)
71
+ elsif parts.kind_of?(Part)
72
+ modify_values(parts.part, value, opts)
73
+ modify_values(parts.get_contents, value, opts)
74
+ parts.modify_values!(value, opts)
75
+ elsif parts.kind_of?(Primitive)
76
+ parts.modify_values!(value, opts)
77
+ end
78
+ end
79
+ end
80
+
81
+ def modify_values!(values, opts)
82
+ $log.info "Modify value! #{self.class} #{values}" if self.debug?
83
+ values.each do |key, val|
84
+ if @opts
85
+ case opts[:mode]
86
+ when :add
87
+ @opts[key] = @opts[key].to_d + val.to_d
88
+ when :sub
89
+ @opts[key] = @opts[key].to_d - val.to_d
90
+ else
91
+ @opts[key] = val
92
+ end
93
+ end
94
+ if self.respond_to? key
95
+ self.send("#{key}=", @opts[key])
96
+ end
97
+ end
98
+ $log.info "Modified value now: #{self.inspect}" if self.debug?
99
+ end
100
+
101
+
102
+ def debug?
103
+ option(:debug) || false
104
+ end
105
+
106
+ def fixate
107
+ Marshal.load(Marshal.dump(self))
108
+ end
109
+ alias :fix :fixate
110
+
36
111
  def set_parent(parent)
37
112
  @parent = parent
38
113
  self
39
114
  end
40
115
 
41
- def anchor(name, thing=nil)
116
+ def anchor(name, thing=nil, args={})
42
117
  if thing
43
- res = thing.anchor(name)
118
+ res = thing.anchor(name, nil, args)
44
119
  return res unless res.nil?
45
120
  end
46
121
  @anchors ||= {}
47
122
  if anch = @anchors[name]
48
123
  return anch
124
+ elsif args[:fail_quick] && args[:fail_quick] == true
125
+ return
49
126
  elsif @parent
50
127
  return @parent.anchor(name)
51
128
  elsif self.respond_to? :get_contents
52
129
  con = get_contents
53
130
  if con.respond_to? :anchor
54
- con.anchor(name)
131
+ con.anchor(name, nil, fail_quick: true)
55
132
  end
56
133
  end
57
134
  end
@@ -64,6 +141,32 @@ module JennCad
64
141
  end
65
142
  alias :sa :set_anchor
66
143
 
144
+ def set_anchor_from(name, new_name, args={})
145
+ unless name.kind_of? Symbol or name.kind_of? String
146
+ $log.error "set_anchor_from: name must be a string or symbol. Supplied: #{name}"
147
+ return
148
+ end
149
+ unless new_name.kind_of? Symbol or new_name.kind_of? String
150
+ $log.error "set_anchor_from: new_name must be a string or symbol. Supplied: #{new_name}"
151
+ return
152
+ end
153
+
154
+
155
+ a = anchor(name, args[:from]).dup
156
+ if !a
157
+ $log.error "set_anchor_from couldn't find anchor #{name}"
158
+ return
159
+ end
160
+
161
+ [:x, :y, :z, :xy, :xz, :xyz, :yz].each do |key|
162
+ a[key] ||= 0.to_d
163
+ args[key] ||= 0.to_d
164
+ a[key] += args[key]
165
+ end
166
+ set_anchor new_name, a
167
+ end
168
+ alias :saf :set_anchor_from
169
+
67
170
  def auto_extrude
68
171
  ret = self.extrude
69
172
  ret.set_option(:auto_extrude, true)
@@ -93,7 +196,7 @@ module JennCad
93
196
  case self
94
197
  when UnionObject
95
198
  ref = self.parts.first
96
- rz = self.z.to_f + self.calc_h.to_f
199
+ rz = self.z.to_d + self.calc_h.to_d
97
200
  when BooleanObject
98
201
  ref = self.parts.first
99
202
  rz = ref.calc_z + ref.calc_h
@@ -121,7 +224,7 @@ module JennCad
121
224
  alias :fy :flip_y
122
225
 
123
226
  def radians(a)
124
- a.to_f/180.0*PI
227
+ a.to_d/180.0*PI
125
228
  end
126
229
 
127
230
  # experiment
@@ -180,22 +283,65 @@ module JennCad
180
283
  return args
181
284
  end
182
285
 
286
+ # reset last move
287
+ def reset_last_move
288
+ lt = @transformations.last
289
+ unless lt.class == Move
290
+ $log.error "Tried to call rst_move but last object is a #{lt.class}"
291
+ return self
292
+ end
293
+ @transformations.delete_at(-1)
294
+
295
+ self
296
+ end
297
+ alias :rstlm :reset_last_move
298
+
299
+ # resets all transformations
300
+ def reset
301
+ @transformations = []
302
+ self
303
+ end
304
+ alias :rst :reset
305
+
306
+
183
307
  def move(args={})
308
+ return self if args.nil? or args.empty?
309
+
184
310
  if args.kind_of? Array
185
311
  x,y,z = args
186
312
  return move(x:x, y:y, z:z)
187
313
  end
188
314
  args = parse_xyz_shortcuts(args)
189
315
 
316
+ if args[:x].to_d == 0.0 && args[:y].to_d == 0.0 && args[:z].to_d == 0.0
317
+ return self
318
+ end
319
+
190
320
  @transformations ||= []
191
321
  if args[:prepend]
192
322
  @transformations.prepend(Move.new(args))
193
323
  else
194
- @transformations << Move.new(args)
324
+ lt = @transformations.last
325
+
326
+ chain = if args[:chain]
327
+ args[:chain]
328
+ else
329
+ $jenncad_profile.chain_moves
330
+ end
331
+
332
+ if lt && lt.class == Move && chain == false
333
+ $log.debug "#{self} at move: Adding to previous move #{lt.inspect} , args: #{args}" if self.debug?
334
+ lt.x += args[:x].to_d
335
+ lt.y += args[:y].to_d
336
+ lt.z += args[:z].to_d
337
+ else
338
+ $log.debug "#{self} at move: Adding move of #{args} to transformations" if self.debug?
339
+ @transformations << Move.new(args)
340
+ end
195
341
  end
196
- @calc_x += args[:x].to_f
197
- @calc_y += args[:y].to_f
198
- @calc_z += args[:z].to_f
342
+ @calc_x += args[:x].to_d
343
+ @calc_y += args[:y].to_d
344
+ @calc_z += args[:z].to_d
199
345
  self
200
346
  end
201
347
  alias :translate :move
@@ -214,30 +360,42 @@ module JennCad
214
360
  end
215
361
 
216
362
  # move to anchor
217
- def movea(key, thing=nil)
218
- an = anchor(key, thing)
363
+ def movea(key, thing=nil, args={})
364
+ if thing.kind_of? Hash # if you leave out thing, args may be interpreted as thing
365
+ args = thing
366
+ thing = nil
367
+ end
368
+
369
+ an = anchor(key, thing, args)
219
370
 
220
371
  unless an
221
372
  $log.error "Error: Anchor #{key} not found"
222
373
  $log.error "Available anchors: #{@anchors}"
223
374
  return self
224
375
  else
225
- self.move(an.dup)
376
+ m = an.dup
377
+ if args[:chain]
378
+ m[:chain] = args[:chain]
379
+ end
380
+ if args[:inverted]
381
+ self.movei(m)
382
+ else
383
+ self.move(m)
384
+ end
226
385
  end
227
386
  end
387
+ alias :ma :movea
228
388
 
229
389
  # move to anchor - inverted
230
- def moveai(key, thing=nil)
231
- an = anchor(key, thing)
232
- unless an
233
- $log.error "Error: Anchor #{key} not found"
234
- $log.error "Available anchors: #{@anchors}"
235
- return self
236
- else
237
- self.movei(an.dup)
390
+ def moveai(key, thing=nil, args={})
391
+ if thing.kind_of? Hash # if you leave out thing, args may be interpreted as thing
392
+ args = thing
393
+ thing = nil
238
394
  end
395
+ args[:inverted] = true
396
+ movea(key, thing, args)
239
397
  end
240
-
398
+ alias :mai :moveai
241
399
 
242
400
  # move half
243
401
  def moveh(args={})
@@ -272,6 +430,7 @@ module JennCad
272
430
  to[key] = args[key]*-1
273
431
  end
274
432
  end
433
+ to[:chain] = args[:chain]
275
434
  move(to)
276
435
  end
277
436
 
@@ -335,7 +494,7 @@ module JennCad
335
494
  end
336
495
 
337
496
  def top_of(other_object)
338
- self.move(z:other_object.z+other_object.calc_z.to_f)
497
+ self.move(z:other_object.z+other_object.calc_z.to_d)
339
498
  end
340
499
 
341
500
  def on_top_of(other_object)
@@ -456,6 +615,8 @@ module JennCad
456
615
  return "##{args}"
457
616
  when String
458
617
  return args
618
+ when Symbol
619
+ return args.to_s
459
620
  end
460
621
  nil
461
622
  end
@@ -480,7 +641,7 @@ module JennCad
480
641
  return @parts unless @parts.nil?
481
642
 
482
643
  if @cache
483
- return @cache
644
+ return @cache unless option(:no_cache) == true
484
645
  end
485
646
 
486
647
  if self.respond_to? :part
@@ -564,7 +725,7 @@ module JennCad
564
725
  end
565
726
 
566
727
  def referenced_z
567
- return false if @z.to_f != 0.0
728
+ return false if @z.to_d != 0.0
568
729
  return option(:zref) if option(:zref)
569
730
  return false
570
731
  end
@@ -579,7 +740,7 @@ module JennCad
579
740
  when nil, false
580
741
  @z + z_margin
581
742
  else
582
- ref.z.to_f + ref.z_margin.to_f
743
+ ref.z.to_d + ref.z_margin.to_d
583
744
  end
584
745
  end
585
746
 
@@ -588,9 +749,16 @@ module JennCad
588
749
  when nil, {}
589
750
  0.0
590
751
  else
591
- m[:z].to_f
752
+ m[:z].to_d
592
753
  end
593
754
  end
594
755
 
756
+
757
+ def to_mod(name)
758
+ a = Aggregation.new(name, self)
759
+ a.transformations = @transformations
760
+ a
761
+ end
762
+
595
763
  end
596
764
  end
@@ -16,7 +16,7 @@ module JennCad
16
16
 
17
17
  def self.check_color_array(a)
18
18
  if a.max > 1.0
19
- a.map{|l| l.to_f/255.0}
19
+ a.map{|l| l.to_d/255.0}
20
20
  else
21
21
  a
22
22
  end
@@ -1,3 +1,3 @@
1
1
  module JennCad
2
- VERSION = "1.0.0-alpha13"
2
+ VERSION = "1.0.0-alpha16"
3
3
  end
data/lib/jenncad.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  require "logger"
2
2
  $log = Logger.new(STDOUT)
3
3
 
4
+ require 'bigdecimal/util'
4
5
  require "geo3d"
5
6
  require "deep_merge"
6
7
  require "fileutils"
7
8
  require "observr"
8
- require "hanami/cli"
9
9
  require "active_support"
10
10
 
11
11
  include Math