jenncad 1.0.0.pre.alpha15 → 1.0.0.pre.alpha18

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,6 +4,7 @@ module JennCad::Primitives
4
4
  include JennCad::Features::Cuttable
5
5
 
6
6
  def initialize(args)
7
+
7
8
  if args.kind_of?(Array) && args[0].kind_of?(Hash)
8
9
  args = args.first
9
10
  end
@@ -35,20 +36,42 @@ module JennCad::Primitives
35
36
  z: 0,
36
37
  },
37
38
  }.deep_merge!(args)
39
+ if args.kind_of? Array
40
+ args.each do |a|
41
+ feed_opts(parse_xyz_shortcuts(a))
42
+ end
43
+ else
44
+ feed_opts(parse_xyz_shortcuts(args))
45
+ end
46
+ init(args)
47
+
38
48
  handle_margins
39
49
  handle_diameter
40
- super(opts)
50
+ if @opts[:z] && opts[:z].to_d > 0
51
+ @dimensions = [:x, :y, :z]
52
+ else
53
+ @dimensions = [:x, :y]
54
+ end
55
+ set_anchors
41
56
  end
42
57
 
43
58
  def to_openscad
44
- return cube(@opts) if @d == 0
59
+ # FIXME: this check needs to be done on object creation
60
+ # otherwise it fails to position it
61
+ if @d == 0
62
+ if @z.to_d > 0
63
+ return cube(@opts)
64
+ else
65
+ return square(@opts)
66
+ end
67
+ end
45
68
  # make diameter not bigger than any side
46
69
  d = [@d, @x, @y].min
47
70
  res = HullObject.new(
48
- cylinder(d: d, h:z+z_margin),
49
- cylinder(d: d).move(x: @x - d, y: 0),
50
- cylinder(d: d).move(x: 0, y: @y - d),
51
- cylinder(d: d).move(x: @x - d, y: @y - d),
71
+ circle(d: d),
72
+ circle(d: d).move(x: @x - d, y: 0),
73
+ circle(d: d).move(x: 0, y: @y - d),
74
+ circle(d: d).move(x: @x - d, y: @y - d),
52
75
  )
53
76
  res = res.move(xy: d/2.0)
54
77
 
@@ -56,6 +79,10 @@ module JennCad::Primitives
56
79
  res += apply_flat_edge(edge)
57
80
  end
58
81
 
82
+ if @z.to_d > 0
83
+ res = res.extrude(z: @z + z_margin)
84
+ end
85
+
59
86
  res = union(res) # put everything we have in a parent union that we can apply the transformations of this object of
60
87
  res.transformations = @transformations
61
88
 
@@ -64,23 +91,25 @@ module JennCad::Primitives
64
91
  res
65
92
  end
66
93
 
67
- def flat(edge)
94
+ def flat(*edges)
68
95
  @opts[:flat_edges] ||= []
69
- @opts[:flat_edges] << edge
96
+ edges.each do |edge|
97
+ @opts[:flat_edges] << edge
98
+ end
70
99
  self
71
100
  end
72
101
 
73
102
  private
74
103
  def apply_flat_edge(edge)
75
104
  case edge
76
- when :up
77
- cube(x: @x, y: @y/2.0, z: @z).nc.moveh(y:@y)
78
- when :down
79
- cube(x: @x, y: @y/2.0, z: @z).nc
105
+ when :up, :top
106
+ square(x: @x, y: @y/2.0).nc.moveh(y:@y)
107
+ when :down, :bottom
108
+ square(x: @x, y: @y/2.0).nc
80
109
  when :right
81
- cube(x: @x/2.0, y: @y, z: @z).nc.moveh(x:@x)
110
+ square(x: @x/2.0, y: @y).nc.moveh(x:@x)
82
111
  when :left
83
- cube(x: @x/2.0, y: @y, z: @z).nc
112
+ square(x: @x/2.0, y: @y).nc
84
113
  else
85
114
  nil
86
115
  end
@@ -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
@@ -0,0 +1,181 @@
1
+ module JennCad::Primitives
2
+ class Square < Primitive
3
+ attr_accessor :corners, :sides
4
+
5
+ def initialize(args)
6
+ @opts = {
7
+ x: 0,
8
+ y: 0,
9
+ margins: {
10
+ x: 0,
11
+ y: 0,
12
+ },
13
+ center: true,
14
+ center_y: false,
15
+ center_x: false,
16
+ }
17
+ if args.kind_of? Array
18
+ args.each do |a|
19
+ feed_opts(parse_xyz_shortcuts(a))
20
+ end
21
+ else
22
+ feed_opts(parse_xyz_shortcuts(args))
23
+ end
24
+ init(args)
25
+
26
+ handle_margins
27
+ set_anchors
28
+ @x = args[:x]
29
+ @y = args[:y]
30
+ @dimensions = [:x, :y]
31
+ end
32
+
33
+
34
+ # used for openscad export
35
+ def size
36
+ [@x, @y]
37
+ end
38
+
39
+ def set_anchors
40
+ set_anchors_2d
41
+ end
42
+
43
+ def set_anchors_2d
44
+ @anchors = {} # this resets anchors
45
+
46
+ if @opts[:center] || @opts[:center_x]
47
+ left = -@opts[:x] / 2.0
48
+ right = @opts[:x] / 2.0
49
+ mid_x = 0
50
+ else
51
+ left = 0
52
+ right = @opts[:x]
53
+ mid_x = @opts[:x] / 2.0
54
+ end
55
+ if @opts[:center] || @opts[:center_y]
56
+ bottom = -@opts[:y] / 2.0
57
+ top = @opts[:y] / 2.0
58
+ mid_y = 0
59
+ else
60
+ bottom = 0
61
+ top = @opts[:y]
62
+ mid_y = @opts[:y] / 2.0
63
+ end
64
+
65
+ set_anchor :left, x: left, y: mid_y
66
+ set_anchor :right, x: right, y: mid_y
67
+ set_anchor :top, x: mid_x, y: top
68
+ set_anchor :bottom, x: mid_x, y: bottom
69
+ set_anchor :top_left, x: left, y: top
70
+ set_anchor :top_right, x: right, y: top
71
+ set_anchor :bottom_left, x: left, y: bottom
72
+ set_anchor :bottom_right, x: right, y: bottom
73
+
74
+ # we need to re-do the inner ones, if they were defined
75
+ if @inner_anchor_defs && @inner_anchor_defs.size > 0
76
+ @inner_anchor_defs.each do |anch|
77
+ inner_anchors(anch[:dist], anch[:prefix], true)
78
+ end
79
+ end
80
+
81
+ self
82
+ end
83
+
84
+ def inner_anchors(dist, prefix=:inner_, recreate=false)
85
+ if dist.nil?
86
+ $log.error "Distance of nil passed to inner anchors. Please check the variable name you passed along"
87
+ return self
88
+ end
89
+
90
+ @inner_anchor_defs ||= []
91
+ @inner_anchor_defs << { "dist": dist, "prefix": prefix } unless recreate
92
+
93
+ # $log.info "dist: #{dist}, prefix: #{prefix}"
94
+ sides = {
95
+ left: {x: dist, y: 0},
96
+ right: {x: -dist, y: 0},
97
+ top: {x: 0, y: -dist},
98
+ bottom: {x: 0, y: dist},
99
+ }
100
+ corners = {
101
+ top_left: {x: dist, y: -dist},
102
+ top_right: {x: -dist, y: -dist},
103
+ bottom_left: {x: dist, y: dist},
104
+ bottom_right: {x: -dist, y: dist},
105
+ }
106
+ new_sides = []
107
+ new_corners = []
108
+
109
+ sides.merge(corners).each do |key, vals|
110
+ new_dist = anchor(key).dup
111
+ new_dist[:x] += vals[:x]
112
+ new_dist[:y] += vals[:y]
113
+ name = [prefix, key].join.to_sym
114
+ # $log.info "Set anchor #{name} , new dist #{new_dist}"
115
+ set_anchor name, new_dist
116
+ if sides.include? key
117
+ new_sides << name
118
+ end
119
+ if corners.include? key
120
+ new_corners << name
121
+ end
122
+ end
123
+
124
+ sides_name = [prefix, "sides"].join
125
+ corners_name = [prefix, "corners"].join
126
+ all_name = [prefix, "all"].join
127
+ self.class.__send__(:attr_accessor, sides_name.to_sym)
128
+ self.class.__send__(:attr_accessor, corners_name.to_sym)
129
+ self.class.__send__(:attr_accessor, all_name.to_sym)
130
+ self.__send__("#{sides_name}=", new_sides)
131
+ self.__send__("#{corners_name}=", new_corners)
132
+ self.__send__("#{all_name}=", new_corners+new_sides)
133
+
134
+
135
+ self
136
+ end
137
+
138
+
139
+
140
+ def not_centered
141
+ @opts[:center] = false
142
+ set_anchors
143
+ self
144
+ end
145
+ alias :nc :not_centered
146
+
147
+ def cx
148
+ nc
149
+ @opts[:center_x] = true
150
+ set_anchors
151
+ self
152
+ end
153
+ alias :center_x :cx
154
+
155
+ def cy
156
+ nc
157
+ @opts[:center_y] = true
158
+ set_anchors
159
+ self
160
+ end
161
+ alias :center_y :cy
162
+
163
+
164
+
165
+ def centered_axis
166
+ return [:x, :y] if @opts[:center]
167
+ a = []
168
+ a << :x if @opts[:center_x]
169
+ a << :y if @opts[:center_y]
170
+ a << :z if @opts[:center_z]
171
+ a
172
+ end
173
+
174
+ def to_openscad
175
+ self.mh(centered_axis.to_h{|a| [a, -@opts[a]] }) # center cube
176
+ end
177
+
178
+
179
+
180
+ end
181
+ end
@@ -57,35 +57,47 @@ 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
- if part.referenced_z && part.z != 0.0
63
+ if part.referenced_z && part.z != 0.0 && part.is_3d?
63
64
  case part
64
- when JennCad::Circle
65
65
  when JennCad::BooleanObject
66
66
  else
67
- $log.debug part if part.opts[:debug]
67
+ # $log.debug part if part.opts[:debug]
68
68
  part.opts[:margins][:z] ||= 0.0
69
69
  unless part.opts[:margins][:z] == 0.2
70
+ $log.debug "fixing possible z fighting for referenced object: #{part.class} #{part.z} 0.1 down" if part.opts[:debug]
70
71
  part.opts[:margins][:z] = 0.2
71
72
  part.mz(-0.1)
72
73
  end
73
74
  end
74
75
  elsif part.z == compare_h
75
76
  $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)
77
+ add_z = 0.008
78
+ move_z = -0.004
78
79
  elsif part.calc_z == compare_z
79
80
  # puts "z fighting at bottom: #{part.calc_z}"
80
- part.opts[:margins][:z] += 0.004
81
+ add_z = 0.004
81
82
  # part.z+=0.004
82
- part.mz(-0.002)
83
+ move_z = -0.002
83
84
  elsif part.calc_z.to_d+part.calc_h.to_d == compare_h
84
85
  # puts "z fighting at top: #{compare_h}"
85
86
  #part.z+=0.004
86
- part.opts[:margins][:z] += 0.004
87
- part.mz(0.002)
87
+ add_z = 0.004
88
+ move_z = 0.002
88
89
  end
90
+
91
+ if add_z && part.is_3d?
92
+ if part.kind_of? Part
93
+ part.modify_values(part, {z: add_z}, {mode: :add})
94
+ end
95
+ part.opts[:margins][:z] += add_z
96
+ part.mz(move_z)
97
+ end
98
+
99
+
100
+
89
101
  end
90
102
  end
91
103
  end
@@ -1,10 +1,12 @@
1
1
  require "jenncad/primitives/primitive"
2
2
  require "jenncad/primitives/circle"
3
+ require "jenncad/primitives/square"
3
4
  require "jenncad/primitives/cylinder"
4
5
  require "jenncad/primitives/sphere"
5
6
  require "jenncad/primitives/cube"
6
7
  require "jenncad/primitives/rounded_cube"
7
8
  require "jenncad/primitives/polygon"
9
+ require "jenncad/primitives/polyhedron"
8
10
  require "jenncad/primitives/slot"
9
11
  require "jenncad/primitives/boolean_object"
10
12
  require "jenncad/primitives/union_object"
@@ -3,6 +3,11 @@ module JennCad
3
3
  Circle.new(args).set_parent(self)
4
4
  end
5
5
 
6
+ def square(args)
7
+ Square.new(args).set_parent(self)
8
+ end
9
+ alias :sq :square
10
+
6
11
  def cylinder(*args)
7
12
  Cylinder.new(args).set_parent(self)
8
13
  end
@@ -18,6 +23,10 @@ module JennCad
18
23
  Polygon.new(args).set_parent(self)
19
24
  end
20
25
 
26
+ def polyhedron(args)
27
+ Polyhedron.new(args).set_parent(self)
28
+ end
29
+
21
30
  def slot(*args)
22
31
  Slot.new(args).set_parent(self)
23
32
  end
@@ -32,6 +41,7 @@ module JennCad
32
41
  end
33
42
  alias :rcube :rounded_cube
34
43
  alias :rc :rounded_cube
44
+ alias :rsq :rounded_cube
35
45
 
36
46
  # import/use OpenScad library
37
47
  def import(import,name,args)
data/lib/jenncad/thing.rb CHANGED
@@ -11,6 +11,10 @@ module JennCad
11
11
  attr_accessor :parent
12
12
 
13
13
  def initialize(args={})
14
+ init(args)
15
+ end
16
+
17
+ def init(args={})
14
18
  @transformations = []
15
19
  # calculated origin; only works for move atm
16
20
  @calc_x = 0
@@ -18,7 +22,7 @@ module JennCad
18
22
  @calc_z = 0
19
23
  @calc_h = args[:z] || 0
20
24
  @anchors = {}
21
- @parent = args[:parent]
25
+ @parent = args[:parent] || nil
22
26
  @opts ||= args
23
27
  @cache = nil
24
28
  end
@@ -35,12 +39,70 @@ module JennCad
35
39
 
36
40
  def set_flag(key)
37
41
  set_option(key, true)
42
+ self
38
43
  end
39
44
 
40
45
  def unset_flag(key)
41
46
  set_option(key, false)
47
+ self
48
+ end
49
+
50
+
51
+ def cut_to(face, part=nil, args={})
52
+ an = anchor(face, part)
53
+ unless an
54
+ $log.error "Cannot find anchor to cut_to"
55
+ return self
56
+ end
57
+ if an[:z].to_d == 0.0
58
+ $log.error "cut_to only supports cuts to an anchor with Z set. This anchor: #{an}"
59
+ return self
60
+ end
61
+ modify_values(self, z: an[:z].to_d)
62
+ self.name="#{self.class}_cut_to_#{an[:z].to_f}"
63
+ self
64
+ end
65
+
66
+ def modify_values(parts, value, opts = {})
67
+ case parts
68
+ when Array
69
+ parts.each do |pa|
70
+ modify_values(pa, value, opts)
71
+ end
72
+ else
73
+ if parts.kind_of?(BooleanObject)
74
+ modify_values(parts.only_additives_of(parts), value, opts)
75
+ elsif parts.kind_of?(Part)
76
+ modify_values(parts.part, value, opts)
77
+ modify_values(parts.get_contents, value, opts)
78
+ parts.modify_values!(value, opts)
79
+ elsif parts.kind_of?(Primitive)
80
+ parts.modify_values!(value, opts)
81
+ end
82
+ end
83
+ end
84
+
85
+ def modify_values!(values, opts)
86
+ $log.info "Modify value! #{self.class} #{values}" if self.debug?
87
+ values.each do |key, val|
88
+ if @opts
89
+ case opts[:mode]
90
+ when :add
91
+ @opts[key] = @opts[key].to_d + val.to_d
92
+ when :sub
93
+ @opts[key] = @opts[key].to_d - val.to_d
94
+ else
95
+ @opts[key] = val
96
+ end
97
+ end
98
+ if self.respond_to? key
99
+ self.send("#{key}=", @opts[key])
100
+ end
101
+ end
102
+ $log.info "Modified value now: #{self.inspect}" if self.debug?
42
103
  end
43
104
 
105
+
44
106
  def debug?
45
107
  option(:debug) || false
46
108
  end
@@ -55,20 +117,22 @@ module JennCad
55
117
  self
56
118
  end
57
119
 
58
- def anchor(name, thing=nil)
120
+ def anchor(name, thing=nil, args={})
59
121
  if thing
60
- res = thing.anchor(name)
122
+ res = thing.anchor(name, nil, args)
61
123
  return res unless res.nil?
62
124
  end
63
125
  @anchors ||= {}
64
126
  if anch = @anchors[name]
65
127
  return anch
128
+ elsif args[:fail_quick] && args[:fail_quick] == true
129
+ return
66
130
  elsif @parent
67
131
  return @parent.anchor(name)
68
132
  elsif self.respond_to? :get_contents
69
133
  con = get_contents
70
134
  if con.respond_to? :anchor
71
- con.anchor(name)
135
+ con.anchor(name, nil, fail_quick: true)
72
136
  end
73
137
  end
74
138
  end
@@ -82,9 +146,19 @@ module JennCad
82
146
  alias :sa :set_anchor
83
147
 
84
148
  def set_anchor_from(name, new_name, args={})
85
- a = anchor(name).dup
149
+ unless name.kind_of? Symbol or name.kind_of? String
150
+ $log.error "set_anchor_from: name must be a string or symbol. Supplied: #{name}"
151
+ return
152
+ end
153
+ unless new_name.kind_of? Symbol or new_name.kind_of? String
154
+ $log.error "set_anchor_from: new_name must be a string or symbol. Supplied: #{new_name}"
155
+ return
156
+ end
157
+
158
+
159
+ a = anchor(name, args[:from]).dup
86
160
  if !a
87
- log.error "set_anchor_from couldn't find anchor #{name}"
161
+ $log.error "set_anchor_from couldn't find anchor #{name}"
88
162
  return
89
163
  end
90
164
 
@@ -213,6 +287,27 @@ module JennCad
213
287
  return args
214
288
  end
215
289
 
290
+ # reset last move
291
+ def reset_last_move
292
+ lt = @transformations.last
293
+ unless lt.class == Move
294
+ $log.error "Tried to call rst_move but last object is a #{lt.class}"
295
+ return self
296
+ end
297
+ @transformations.delete_at(-1)
298
+
299
+ self
300
+ end
301
+ alias :rstlm :reset_last_move
302
+
303
+ # resets all transformations
304
+ def reset
305
+ @transformations = []
306
+ self
307
+ end
308
+ alias :rst :reset
309
+
310
+
216
311
  def move(args={})
217
312
  return self if args.nil? or args.empty?
218
313
 
@@ -275,7 +370,7 @@ module JennCad
275
370
  thing = nil
276
371
  end
277
372
 
278
- an = anchor(key, thing)
373
+ an = anchor(key, thing, args)
279
374
 
280
375
  unless an
281
376
  $log.error "Error: Anchor #{key} not found"
@@ -293,6 +388,7 @@ module JennCad
293
388
  end
294
389
  end
295
390
  end
391
+ alias :ma :movea
296
392
 
297
393
  # move to anchor - inverted
298
394
  def moveai(key, thing=nil, args={})
@@ -303,7 +399,7 @@ module JennCad
303
399
  args[:inverted] = true
304
400
  movea(key, thing, args)
305
401
  end
306
-
402
+ alias :mai :moveai
307
403
 
308
404
  # move half
309
405
  def moveh(args={})
@@ -549,7 +645,7 @@ module JennCad
549
645
  return @parts unless @parts.nil?
550
646
 
551
647
  if @cache
552
- return @cache
648
+ return @cache unless option(:no_cache) == true
553
649
  end
554
650
 
555
651
  if self.respond_to? :part
@@ -661,5 +757,25 @@ module JennCad
661
757
  end
662
758
  end
663
759
 
760
+ def to_mod(name)
761
+ a = Aggregation.new(name, self)
762
+ a.transformations = @transformations
763
+ a
764
+ end
765
+
766
+ def is_2d?
767
+ !is_3d?
768
+ end
769
+
770
+ def is_3d?
771
+ if self.respond_to?(:dimensions) && self.dimensions
772
+ return true if self.dimensions && self.dimensions.include?(:z)
773
+ else
774
+ # assume true if not set
775
+ return true
776
+ end
777
+ false
778
+ end
779
+
664
780
  end
665
781
  end