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

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