jenncad 1.0.0.pre.alpha2 → 1.0.0.pre.alpha6
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.
- checksums.yaml +4 -4
- data/README.md +83 -0
- data/jenncad.gemspec +1 -1
- data/lib/jenncad/commands.rb +30 -7
- data/lib/jenncad/exporters/openscad.rb +15 -3
- data/lib/jenncad/features/aggregation.rb +16 -0
- data/lib/jenncad/features/climb.rb +95 -0
- data/lib/jenncad/features/cuttable.rb +35 -0
- data/lib/jenncad/features/feature.rb +4 -0
- data/lib/jenncad/{primitives → features}/openscad_include.rb +1 -1
- data/lib/jenncad/features/stl_import.rb +10 -0
- data/lib/jenncad/features.rb +11 -0
- data/lib/jenncad/part.rb +3 -4
- data/lib/jenncad/patches/array.rb +1 -2
- data/lib/jenncad/primitives/cube.rb +27 -9
- data/lib/jenncad/primitives/cylinder.rb +29 -1
- data/lib/jenncad/primitives/linear_extrude.rb +7 -3
- data/lib/jenncad/primitives/rotate_extrude.rb +1 -1
- data/lib/jenncad/primitives/rounded_cube.rb +12 -7
- data/lib/jenncad/primitives/subtract_object.rb +1 -0
- data/lib/jenncad/primitives.rb +0 -2
- data/lib/jenncad/shortcuts.rb +5 -1
- data/lib/jenncad/thing.rb +198 -28
- data/lib/jenncad/transformation/scale.rb +2 -0
- data/lib/jenncad/version.rb +1 -1
- data/lib/jenncad.rb +1 -1
- metadata +18 -12
- data/lib/jenncad/primitives/aggregation.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8dc72aa8cfaaa41cf4b96038f454073f708502cea9d9ce84a13fc28203ff4dc2
|
4
|
+
data.tar.gz: c5f0cbe4442bf7b59c39ee2f69e2096d0166fa4ae336c48029f8856606da3d9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 425dda2b4dda1793baa9ef4d6f30155325acc949df98c2ca214b7abb931a1a9aa7757f6b906a89cf12d7a17c4447ef3b92b3b999120d455cfb8693a2b5cfe0af
|
7
|
+
data.tar.gz: 706f8e591aab9c10702577c09fc95a4e5e7cd1444cb3fbda88e715872e4aab67fb8b35449a97a27a1b874325e3804cf20377884ebb67896484e9bc9cee5a6625
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
|
2
|
+
# jenncad
|
3
|
+
Create physical objects in Ruby, OpenScad export
|
4
|
+
|
5
|
+
This is a successor to my older project CrystalScad.
|
6
|
+
|
7
|
+
# Installation
|
8
|
+
|
9
|
+
A packaged release is not yet available, please build your own package in the meantime:
|
10
|
+
|
11
|
+
$ git clone git@github.com:jglauche/jenncad.git
|
12
|
+
$ cd jenncad
|
13
|
+
$ rake install
|
14
|
+
|
15
|
+
This will create a gem and a binary jenncad.
|
16
|
+
|
17
|
+
# Using Jenncad
|
18
|
+
|
19
|
+
**To create a new project directory, run:**
|
20
|
+
|
21
|
+
$ jenncad create meow
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
This will generate a directory meow/ and an executable ruby file meow.rb in its directory
|
27
|
+
|
28
|
+
$ cd meow
|
29
|
+
$ ./meow
|
30
|
+
|
31
|
+
This will generate a dummy project which generates a dummy cube as OpenSCAD output:
|
32
|
+
|
33
|
+
$ cat output/meow.scad
|
34
|
+
> $fn=64;
|
35
|
+
> translate([-5, -5, 0])cube([10, 10, 10.0]);
|
36
|
+
|
37
|
+
**Automatically refresh OpenSCAD output while developing**
|
38
|
+
|
39
|
+
Jenncad bundles the observr gem which will check if files were changed while developing. In the project directory run:
|
40
|
+
|
41
|
+
$ jenncad
|
42
|
+
This should display something like:
|
43
|
+
> refreshing...
|
44
|
+
> ok
|
45
|
+
> JennCad running, refreshing on file changes. Press ctrl+c to exit
|
46
|
+
|
47
|
+
**Note:** This does not check for new files in the project. You will have to restart it when you create a new part in a new file
|
48
|
+
|
49
|
+
**Create new part**
|
50
|
+
|
51
|
+
In your project directory, run:
|
52
|
+
|
53
|
+
$ jenncad new cat
|
54
|
+
part parts/cat.rb created. In your meow.rb add to class Meow:
|
55
|
+
def cat
|
56
|
+
Cat.new(config)
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
You will have to link the part to the project manually into the meow.rb file in your project directory. When you add it, your meow.rb should look like this:
|
61
|
+
|
62
|
+
#!/usr/bin/env ruby
|
63
|
+
require "jenncad"
|
64
|
+
include JennCad
|
65
|
+
|
66
|
+
class Meow < Project
|
67
|
+
def config
|
68
|
+
{}
|
69
|
+
end
|
70
|
+
|
71
|
+
def meow
|
72
|
+
cube(10,10,10)
|
73
|
+
end
|
74
|
+
|
75
|
+
def cat
|
76
|
+
Cat.new(config)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
Meow.new.run
|
81
|
+
|
82
|
+
|
83
|
+
|
data/jenncad.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.required_ruby_version = ">= 2.6.0"
|
23
23
|
gem.add_runtime_dependency "geo3d"
|
24
24
|
gem.add_runtime_dependency "deep_merge"
|
25
|
-
gem.add_runtime_dependency "hanami-cli"
|
25
|
+
gem.add_runtime_dependency "hanami-cli", "0.3.1"
|
26
26
|
gem.add_runtime_dependency "activesupport"
|
27
27
|
gem.add_runtime_dependency "observr"
|
28
28
|
end
|
data/lib/jenncad/commands.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module JennCad
|
2
2
|
module Commands
|
3
3
|
extend Hanami::CLI::Registry
|
4
|
+
MAGIC = "jenncad-append-project-magic"
|
4
5
|
|
5
6
|
class Run < Hanami::CLI::Command
|
6
7
|
argument :name, required: false
|
@@ -122,10 +123,34 @@ module JennCad
|
|
122
123
|
f.puts " end"
|
123
124
|
f.puts "end"
|
124
125
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
126
|
+
|
127
|
+
lines = File.readlines(executable)
|
128
|
+
magic_line = nil;
|
129
|
+
lines.each_with_index do |l, i|
|
130
|
+
if l.rindex(MAGIC)
|
131
|
+
magic_line = i
|
132
|
+
end
|
133
|
+
end
|
134
|
+
puts "part #{filename} created."
|
135
|
+
if !magic_line
|
136
|
+
puts "In your #{executable} add to class #{executable_class}:"
|
137
|
+
puts " def #{name}"
|
138
|
+
puts " #{classname}.new(config)"
|
139
|
+
puts " end"
|
140
|
+
puts ""
|
141
|
+
puts "For jenncad to insert this line automatically, add this line to your project file before the \"end\"-statement of your class:"
|
142
|
+
puts "##{MAGIC}"
|
143
|
+
else
|
144
|
+
data = "\n"
|
145
|
+
data += " def #{name}\n"
|
146
|
+
data += " #{classname}.new(config)\n"
|
147
|
+
data += " end\n"
|
148
|
+
lines.insert(magic_line, data)
|
149
|
+
f = File.open(executable, "w")
|
150
|
+
f.write(lines.join)
|
151
|
+
f.close
|
152
|
+
end
|
153
|
+
|
129
154
|
|
130
155
|
end
|
131
156
|
|
@@ -152,9 +177,7 @@ module JennCad
|
|
152
177
|
f.puts " {}"
|
153
178
|
f.puts " end"
|
154
179
|
f.puts ""
|
155
|
-
f.puts "
|
156
|
-
f.puts " cube(10,10,10)"
|
157
|
-
f.puts " end"
|
180
|
+
f.puts " # #{MAGIC}"
|
158
181
|
f.puts "end"
|
159
182
|
f.puts ""
|
160
183
|
f.puts "#{classname}.new.run"
|
@@ -94,7 +94,12 @@ module JennCad::Exporters
|
|
94
94
|
if !v.kind_of?(Array) && !v.kind_of?(TrueClass) && !v.kind_of?(FalseClass) && v == v.to_i
|
95
95
|
v = v.to_i
|
96
96
|
end
|
97
|
-
|
97
|
+
if v.kind_of? String
|
98
|
+
q = "\""
|
99
|
+
else
|
100
|
+
q = ""
|
101
|
+
end
|
102
|
+
res << "#{k}=#{q}#{v}#{q}"
|
98
103
|
end
|
99
104
|
res.join(",").gsub("size=","")
|
100
105
|
else
|
@@ -165,6 +170,8 @@ module JennCad::Exporters
|
|
165
170
|
new_obj(part, :projection, collect_params(part), parse(part.parts))
|
166
171
|
when JennCad::Primitives::Polygon
|
167
172
|
new_obj(part, :polygon, collect_params(part))
|
173
|
+
when JennCad::StlImport
|
174
|
+
new_obj(part, :import, collect_params(part))
|
168
175
|
when JennCad::Part
|
169
176
|
parse(part.part)
|
170
177
|
when nil
|
@@ -192,8 +199,11 @@ module JennCad::Exporters
|
|
192
199
|
end
|
193
200
|
|
194
201
|
def collect_params(part)
|
202
|
+
if part.respond_to? :openscad_params
|
203
|
+
return part.openscad_params
|
204
|
+
end
|
195
205
|
res = {}
|
196
|
-
[:d, :h, :d1, :d2, :size, :fn, :points].each do |var|
|
206
|
+
[:d, :h, :d1, :d2, :size, :fn, :points, :file].each do |var|
|
197
207
|
if part.respond_to? var
|
198
208
|
res[var] = part.send var
|
199
209
|
end
|
@@ -221,6 +231,8 @@ module JennCad::Exporters
|
|
221
231
|
OpenScadObject.new(:translate, t.coordinates, transform(part, &block))
|
222
232
|
when JennCad::Rotate, JennCad::Mirror
|
223
233
|
OpenScadObject.new(demodulize(t.class).downcase, t.coordinates, transform(part, &block))
|
234
|
+
when JennCad::Scale
|
235
|
+
OpenScadObject.new(:scale, t.scale, transform(part, &block))
|
224
236
|
when JennCad::Multmatrix
|
225
237
|
OpenScadObject.new(:multmatrix, t.m, transform(part, &block))
|
226
238
|
else
|
@@ -237,7 +249,7 @@ module JennCad::Exporters
|
|
237
249
|
|
238
250
|
# accept aggregation
|
239
251
|
def register_module(part)
|
240
|
-
@modules[part.name] = OpenScadObject.new(:module,part.name, parse(part.part))
|
252
|
+
@modules[part.name] = OpenScadObject.new(:module, part.name, parse(part.part))
|
241
253
|
end
|
242
254
|
|
243
255
|
def handle_import(part)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module JennCad::Features
|
2
|
+
class Aggregation < Feature
|
3
|
+
attr_accessor :parts
|
4
|
+
|
5
|
+
def initialize(name=nil, part=nil)
|
6
|
+
super({})
|
7
|
+
@name = name
|
8
|
+
@parts = [part] # NOTE: single length arrayto make checking children easier
|
9
|
+
end
|
10
|
+
|
11
|
+
def part
|
12
|
+
@parts.first
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module JennCad::Features
|
2
|
+
class Climb < Feature
|
3
|
+
def initialize(opts, block)
|
4
|
+
@opts = {
|
5
|
+
offset: :auto,
|
6
|
+
step: nil,
|
7
|
+
steps: nil,
|
8
|
+
bottom: nil,
|
9
|
+
top: nil,
|
10
|
+
z: nil,
|
11
|
+
}.deep_merge!(opts)
|
12
|
+
if @opts[:step].nil? && @opts[:steps].nil?
|
13
|
+
raise "please define at least one of :step or :steps for climb"
|
14
|
+
end
|
15
|
+
super(@opts)
|
16
|
+
@block = block
|
17
|
+
end
|
18
|
+
|
19
|
+
def z_or_referenced
|
20
|
+
case z = @opts[:z]
|
21
|
+
when nil
|
22
|
+
referenced_z.z
|
23
|
+
else
|
24
|
+
z
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_step(z)
|
29
|
+
case step = @opts[:step]
|
30
|
+
when nil, :auto, 0, 0.0
|
31
|
+
steps = @opts[:steps]
|
32
|
+
(z / steps).floor
|
33
|
+
else
|
34
|
+
step.to_f
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_offset(z)
|
39
|
+
case offset = @opts[:offset]
|
40
|
+
when :auto
|
41
|
+
step = get_step(z)
|
42
|
+
((z % step) + step) / 2.0
|
43
|
+
when nil, 0, 0.0
|
44
|
+
0.0
|
45
|
+
else
|
46
|
+
offset
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def climb_from_bottom(offset, step, n)
|
51
|
+
n.times.map{ |i| @block.yield.mz(offset+step*i) }.union
|
52
|
+
end
|
53
|
+
|
54
|
+
def climb_from_top(z, offset, step, n)
|
55
|
+
n.times.map{ |i| @block.yield.mz(z-offset-step*i) }.union
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_openscad
|
59
|
+
ref_z = z_or_referenced
|
60
|
+
step = get_step(ref_z)
|
61
|
+
steps, top, bottom = @opts.values_at(:steps, :top, :bottom)
|
62
|
+
|
63
|
+
offset = get_offset(ref_z)
|
64
|
+
|
65
|
+
lo = (ref_z-offset*2).to_f % step.to_f
|
66
|
+
unless lo.to_f == 0.0
|
67
|
+
puts "[Warning]: climb has leftover offset #{lo}"
|
68
|
+
end
|
69
|
+
|
70
|
+
if steps
|
71
|
+
top = steps
|
72
|
+
bottom = steps
|
73
|
+
end
|
74
|
+
|
75
|
+
unless top or bottom
|
76
|
+
climb_from_bottom(offset, step, ((ref_z-offset*2) / step).floor + 1 )
|
77
|
+
else
|
78
|
+
res = nil
|
79
|
+
if top
|
80
|
+
res += climb_from_top(ref_z, offset, step, top)
|
81
|
+
end
|
82
|
+
if bottom
|
83
|
+
res += climb_from_bottom(offset, step, bottom)
|
84
|
+
end
|
85
|
+
res
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
def climb(args, &block)
|
92
|
+
Climb.new(args, block)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module JennCad::Features
|
2
|
+
module Cuttable
|
3
|
+
def cut(args, &block)
|
4
|
+
if args[:x]
|
5
|
+
l = args[:x].min * @opts[:y] / 2.0
|
6
|
+
r = args[:x].max * @opts[:y] / 2.0
|
7
|
+
prepare_cut(l, r, &block).flip_x
|
8
|
+
elsif args[:y]
|
9
|
+
l = args[:y].min * @opts[:y] / 2.0
|
10
|
+
r = args[:y].max * @opts[:y] / 2.0
|
11
|
+
prepare_cut(l, r, &block).flip_y
|
12
|
+
elsif args[:z]
|
13
|
+
raise "cut for Z is not implemented yet"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def prepare_cut(l, r, &block)
|
18
|
+
part = block.call
|
19
|
+
if part.z.to_f > 0.0
|
20
|
+
part.opts[:margins][:z] = 0.2
|
21
|
+
if l == 0.0
|
22
|
+
part.mz(r+0.1)
|
23
|
+
else
|
24
|
+
part.mz(l+part.z.to_f-0.2)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
part.opts[:margins][:z] = 0.2
|
28
|
+
part.z = l.abs + r.abs + 0.2
|
29
|
+
part.mz(-0.1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "jenncad/features/feature"
|
2
|
+
require "jenncad/features/aggregation"
|
3
|
+
require "jenncad/features/cuttable"
|
4
|
+
require "jenncad/features/openscad_include"
|
5
|
+
require "jenncad/features/climb"
|
6
|
+
require "jenncad/features/stl_import"
|
7
|
+
|
8
|
+
|
9
|
+
module JennCad
|
10
|
+
include Features
|
11
|
+
end
|
data/lib/jenncad/part.rb
CHANGED
@@ -2,11 +2,10 @@ module JennCad
|
|
2
2
|
# Part should be inherited from the user when making parts
|
3
3
|
class Part < Thing
|
4
4
|
|
5
|
-
def to_openscad
|
6
|
-
|
7
|
-
a = Aggregation.new(self.class.to_s, self.part) #make_openscad_compatible!(self.part))
|
5
|
+
def to_openscad
|
6
|
+
a = Aggregation.new(self.class.to_s, self.part)
|
8
7
|
a.transformations = @transformations
|
9
|
-
a.color(
|
8
|
+
a.color(:auto)
|
10
9
|
a
|
11
10
|
end
|
12
11
|
|
@@ -12,8 +12,7 @@ class Array
|
|
12
12
|
else
|
13
13
|
res = part
|
14
14
|
end
|
15
|
-
|
16
|
-
res, z = res.move(z:z), z + res.z.to_f + 0.01 unless skip_z
|
15
|
+
res, z = res.mz(z), z + res.z.to_f unless skip_z
|
17
16
|
res
|
18
17
|
end
|
19
18
|
.union
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module JennCad::Primitives
|
2
2
|
class Cube < Primitive
|
3
|
+
extend JennCad::Features::Cuttable
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
5
|
+
|
6
|
+
def feed_opts(args)
|
7
|
+
# FIXME: this doesn't seem to work
|
8
8
|
if args.kind_of? Array
|
9
9
|
m = {}
|
10
10
|
if args.last.kind_of? Hash
|
@@ -12,7 +12,13 @@ module JennCad::Primitives
|
|
12
12
|
end
|
13
13
|
args = [:x, :y, :z].zip(args.flatten).to_h
|
14
14
|
args.deep_merge!(m)
|
15
|
+
@opts.deep_merge!(args)
|
16
|
+
else
|
17
|
+
@opts.deep_merge!(args)
|
15
18
|
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(args)
|
16
22
|
@opts = {
|
17
23
|
x: 0,
|
18
24
|
y: 0,
|
@@ -26,10 +32,18 @@ module JennCad::Primitives
|
|
26
32
|
center_y: false,
|
27
33
|
center_x: false,
|
28
34
|
center_z: false,
|
29
|
-
}
|
30
|
-
|
35
|
+
}
|
36
|
+
if args.kind_of? Array
|
37
|
+
args.each do |a|
|
38
|
+
feed_opts(parse_xyz_shortcuts(a))
|
39
|
+
end
|
40
|
+
else
|
41
|
+
feed_opts(parse_xyz_shortcuts(args))
|
42
|
+
end
|
43
|
+
|
31
44
|
|
32
|
-
|
45
|
+
handle_margins
|
46
|
+
super(z: @opts[:z])
|
33
47
|
@h = @z.dup
|
34
48
|
@calc_h = @z.dup
|
35
49
|
end
|
@@ -41,30 +55,34 @@ module JennCad::Primitives
|
|
41
55
|
|
42
56
|
def not_centered
|
43
57
|
@opts[:center] = false
|
58
|
+
self
|
44
59
|
end
|
45
60
|
alias :nc :not_centered
|
46
61
|
|
47
62
|
def cx
|
48
63
|
nc
|
49
64
|
@opts[:center_x] = true
|
65
|
+
self
|
50
66
|
end
|
51
67
|
|
52
68
|
def cy
|
53
69
|
nc
|
54
70
|
@opts[:center_y] = true
|
71
|
+
self
|
55
72
|
end
|
56
73
|
|
57
74
|
def cz
|
58
75
|
nc
|
59
76
|
@opts[:center_z] = true
|
77
|
+
self
|
60
78
|
end
|
61
79
|
|
62
80
|
def centered_axis
|
63
81
|
return [:x, :y] if @opts[:center]
|
64
82
|
a = []
|
65
83
|
a << :x if @opts[:center_x]
|
66
|
-
a << :y if @opts[:
|
67
|
-
a << :z if @opts[:
|
84
|
+
a << :y if @opts[:center_y]
|
85
|
+
a << :z if @opts[:center_z]
|
68
86
|
a
|
69
87
|
end
|
70
88
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module JennCad::Primitives
|
2
2
|
class Cylinder < Primitive
|
3
|
-
attr_accessor :d, :r, :fn
|
3
|
+
attr_accessor :d, :d1, :d2, :r, :fn
|
4
4
|
def initialize(args)
|
5
5
|
if args.kind_of?(Array) && args[0].kind_of?(Hash)
|
6
6
|
args = args.first
|
@@ -43,6 +43,20 @@ module JennCad::Primitives
|
|
43
43
|
super(args)
|
44
44
|
end
|
45
45
|
|
46
|
+
def openscad_params
|
47
|
+
res = {}
|
48
|
+
if @opts[:d1]
|
49
|
+
[:d1, :d2, :h, :fn].each do |n|
|
50
|
+
res[n] = self.send n
|
51
|
+
end
|
52
|
+
else
|
53
|
+
[:d, :h, :fn].each do |n|
|
54
|
+
res[n] = self.send n
|
55
|
+
end
|
56
|
+
end
|
57
|
+
res
|
58
|
+
end
|
59
|
+
|
46
60
|
def handle_fn
|
47
61
|
case @opts[:fn]
|
48
62
|
when nil, 0
|
@@ -70,6 +84,20 @@ module JennCad::Primitives
|
|
70
84
|
@d = @opts[:d].to_f + @opts[:margins][:d].to_f
|
71
85
|
@r = @d / 2.0
|
72
86
|
end
|
87
|
+
|
88
|
+
case @opts[:d1]
|
89
|
+
when 0, nil
|
90
|
+
else
|
91
|
+
@d1 = @opts[:d1].to_f + @opts[:margins][:d].to_f
|
92
|
+
@d2 = @opts[:d2].to_f + @opts[:margins][:d].to_f
|
93
|
+
end
|
94
|
+
|
95
|
+
case @opts[:r1]
|
96
|
+
when 0, nil
|
97
|
+
else
|
98
|
+
@d1 = 2 * @opts[:r1].to_f + @opts[:margins][:d].to_f
|
99
|
+
@d2 = 2 * @opts[:r2].to_f + @opts[:margins][:d].to_f
|
100
|
+
end
|
73
101
|
end
|
74
102
|
|
75
103
|
def h
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module JennCad::Primitives
|
2
|
-
attr_accessor :center_bool, :convexity, :twist, :slices
|
2
|
+
attr_accessor :center_bool, :convexity, :twist, :slices
|
3
3
|
class LinearExtrude < JennCad::Thing
|
4
|
-
def initialize(part, args)
|
4
|
+
def initialize(part, args={})
|
5
5
|
@transformations = []
|
6
6
|
@parts = [part]
|
7
|
-
@
|
7
|
+
@z = args[:h] || args[:height]
|
8
8
|
@center_bool = args[:center]
|
9
9
|
@convexity = args[:convexity]
|
10
10
|
@twist = args[:twist]
|
@@ -12,6 +12,10 @@ module JennCad::Primitives
|
|
12
12
|
@fn = args[:fn]
|
13
13
|
end
|
14
14
|
|
15
|
+
def height
|
16
|
+
@z
|
17
|
+
end
|
18
|
+
|
15
19
|
def openscad_params
|
16
20
|
res = {}
|
17
21
|
[:height, :convexity, :twist, :slices, :fn].each do |n|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module JennCad::Primitives
|
2
|
-
class RoundedCube <
|
2
|
+
class RoundedCube < Cube
|
3
3
|
attr_accessor :d, :r
|
4
|
+
include JennCad::Features::Cuttable
|
4
5
|
|
5
6
|
def initialize(args)
|
6
7
|
if args.kind_of?(Array) && args[0].kind_of?(Hash)
|
@@ -22,6 +23,10 @@ module JennCad::Primitives
|
|
22
23
|
z: nil,
|
23
24
|
r: nil,
|
24
25
|
flat_edges: nil,
|
26
|
+
center: true,
|
27
|
+
center_y: false,
|
28
|
+
center_x: false,
|
29
|
+
center_z: false,
|
25
30
|
margins: {
|
26
31
|
r: 0,
|
27
32
|
d: 0,
|
@@ -40,16 +45,16 @@ module JennCad::Primitives
|
|
40
45
|
# make diameter not bigger than any side
|
41
46
|
@d = [@d, @x, @y].min
|
42
47
|
res = HullObject.new(
|
43
|
-
cylinder(d:@d, h
|
44
|
-
cylinder(d:@d).
|
45
|
-
cylinder(d:@d).
|
46
|
-
cylinder(d:@d).
|
47
|
-
)
|
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),
|
52
|
+
).moveh(xy: @d)
|
48
53
|
|
49
54
|
res += flat_edge(@opts[:flat_edges])
|
50
55
|
|
51
56
|
res.transformations = @transformations
|
52
|
-
res
|
57
|
+
res.moveh(centered_axis.to_h{|a| [a, -@opts[a]] })
|
53
58
|
end
|
54
59
|
|
55
60
|
def flat_edge(edge)
|
data/lib/jenncad/primitives.rb
CHANGED
data/lib/jenncad/shortcuts.rb
CHANGED
data/lib/jenncad/thing.rb
CHANGED
@@ -6,7 +6,8 @@ module JennCad
|
|
6
6
|
attr_accessor :x, :y, :diameter
|
7
7
|
attr_accessor :calc_x, :calc_y, :calc_z, :calc_h
|
8
8
|
attr_accessor :shape
|
9
|
-
attr_accessor :angle, :
|
9
|
+
attr_accessor :angle, :fn
|
10
|
+
attr_accessor :anchor
|
10
11
|
|
11
12
|
def initialize(args={})
|
12
13
|
@transformations = []
|
@@ -28,6 +29,41 @@ module JennCad
|
|
28
29
|
@opts[key] = val
|
29
30
|
end
|
30
31
|
|
32
|
+
def set_anchor(args)
|
33
|
+
self.anchor ||= {}
|
34
|
+
args.each do |key, val|
|
35
|
+
self.anchor[key] = val
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def movea(key)
|
40
|
+
a = self.anchor[key]
|
41
|
+
unless a
|
42
|
+
puts "Error: Anchor #{key} not found"
|
43
|
+
puts "Available anchor: #{self.anchor}"
|
44
|
+
return self
|
45
|
+
else
|
46
|
+
self.movei(a)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def movei(args)
|
51
|
+
to = {}
|
52
|
+
[:x, :y, :z].each do |key|
|
53
|
+
if args[key]
|
54
|
+
to[key] = args[key]*-1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
move(to)
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def auto_extrude
|
62
|
+
ret = self.extrude
|
63
|
+
ret.set_option(:auto_extrude, true)
|
64
|
+
ret
|
65
|
+
end
|
66
|
+
|
31
67
|
def rotate(args)
|
32
68
|
@transformations ||= []
|
33
69
|
@transformations << Rotate.new(args)
|
@@ -49,17 +85,22 @@ module JennCad
|
|
49
85
|
|
50
86
|
def flip(direction)
|
51
87
|
case self
|
88
|
+
when UnionObject
|
89
|
+
ref = self.parts.first
|
90
|
+
rz = self.z.to_f + self.calc_h.to_f
|
52
91
|
when BooleanObject
|
53
92
|
ref = self.parts.first
|
93
|
+
rz = ref.calc_z + ref.calc_h
|
54
94
|
else
|
55
95
|
ref = self
|
96
|
+
rz = self.z + self.calc_h
|
56
97
|
end
|
57
98
|
|
58
99
|
case direction
|
59
100
|
when :x
|
60
|
-
self.ry(90).mh(x: -
|
101
|
+
self.ry(90).mh(x: -rz, z: ref.x)
|
61
102
|
when :y
|
62
|
-
self.rx(90).mh(y:
|
103
|
+
self.rx(90).mh(y: rz, z: ref.y)
|
63
104
|
end
|
64
105
|
end
|
65
106
|
|
@@ -91,17 +132,43 @@ module JennCad
|
|
91
132
|
[v.x,v.y,v.z]
|
92
133
|
end
|
93
134
|
|
94
|
-
# todo: check if that works
|
95
135
|
def rotate_around(point,args)
|
96
|
-
x,y,z= point
|
136
|
+
x,y,z= point[:x], point[:y], point[:z]
|
97
137
|
self.move(x:-x,y:-y,z:-z).rotate(args).move(x:x,y:y,z:z)
|
98
138
|
end
|
99
139
|
|
140
|
+
def parse_xyz_shortcuts(args)
|
141
|
+
[:x, :y, :z].each do |key|
|
142
|
+
args[key] ||= 0.0
|
143
|
+
end
|
144
|
+
|
145
|
+
if args[:xy]
|
146
|
+
args[:x] += args[:xy]
|
147
|
+
args[:y] += args[:xy]
|
148
|
+
end
|
149
|
+
if args[:xyz]
|
150
|
+
args[:x] += args[:xyz]
|
151
|
+
args[:y] += args[:xyz]
|
152
|
+
args[:z] += args[:xyz]
|
153
|
+
end
|
154
|
+
if args[:xz]
|
155
|
+
args[:x] += args[:xz]
|
156
|
+
args[:z] += args[:xz]
|
157
|
+
end
|
158
|
+
if args[:yz]
|
159
|
+
args[:y] += args[:yz]
|
160
|
+
args[:z] += args[:yz]
|
161
|
+
end
|
162
|
+
return args
|
163
|
+
end
|
164
|
+
|
100
165
|
def move(args)
|
101
166
|
if args.kind_of? Array
|
102
167
|
x,y,z = args
|
103
168
|
return move(x:x, y:y, z:z)
|
104
169
|
end
|
170
|
+
args = parse_xyz_shortcuts(args)
|
171
|
+
|
105
172
|
@transformations ||= []
|
106
173
|
if args[:prepend]
|
107
174
|
@transformations.prepend(Move.new(args))
|
@@ -134,9 +201,9 @@ module JennCad
|
|
134
201
|
x,y,z = args
|
135
202
|
args = {x: x, y: y, z: z}
|
136
203
|
end
|
137
|
-
|
138
|
-
|
139
|
-
|
204
|
+
[:x, :y, :z, :xy, :xyz, :xz, :yz].each do |key|
|
205
|
+
args[key] = args[key] / 2.0 unless args[key] == nil
|
206
|
+
end
|
140
207
|
|
141
208
|
move(args)
|
142
209
|
end
|
@@ -223,8 +290,7 @@ module JennCad
|
|
223
290
|
end
|
224
291
|
res
|
225
292
|
end
|
226
|
-
|
227
|
-
def make_openscad_compatible
|
293
|
+
=begin def make_openscad_compatible
|
228
294
|
make_openscad_compatible!(self)
|
229
295
|
end
|
230
296
|
|
@@ -245,39 +311,111 @@ module JennCad
|
|
245
311
|
end
|
246
312
|
item
|
247
313
|
end
|
314
|
+
=end
|
315
|
+
|
316
|
+
def has_explicit_color?
|
317
|
+
if option(:auto_color) == false
|
318
|
+
return true
|
319
|
+
end
|
320
|
+
return false
|
321
|
+
end
|
322
|
+
|
323
|
+
def only_color?(parts, lvl=0)
|
324
|
+
return true if parts == nil
|
325
|
+
|
326
|
+
parts.each do |part|
|
327
|
+
# puts " " * lvl + "[only_color?] #{part}"
|
328
|
+
if part.has_explicit_color?
|
329
|
+
# puts " " * lvl + "found explicit color here"
|
330
|
+
return false
|
331
|
+
end
|
332
|
+
if !only_color?(part.parts, lvl+1)
|
333
|
+
return false
|
334
|
+
end
|
335
|
+
end
|
336
|
+
true
|
337
|
+
end
|
338
|
+
|
339
|
+
def set_auto_color_for_children(col, parts, lvl=0)
|
340
|
+
return if parts == nil
|
341
|
+
|
342
|
+
parts.each do |part|
|
343
|
+
unless part.has_explicit_color?
|
344
|
+
if only_color?(part.parts, lvl+1)
|
345
|
+
# puts " " * lvl + "children have no explicit color, setting it here"
|
346
|
+
part.set_auto_color(col)
|
347
|
+
else
|
348
|
+
# puts " " * lvl + "[set_auto_color_for_children] #{part}"
|
349
|
+
set_auto_color_for_children(col, part.parts, lvl+1)
|
350
|
+
end
|
351
|
+
else
|
352
|
+
# puts " " * lvl + "[set_auto_color_for_children] this part has a color, ignoring their children"
|
353
|
+
end
|
354
|
+
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
def set_auto_color(col)
|
359
|
+
set_option :color, col
|
360
|
+
set_option :auto_color, true
|
361
|
+
end
|
248
362
|
|
249
363
|
def color(args=nil)
|
250
|
-
|
251
|
-
when nil
|
364
|
+
if args == nil
|
252
365
|
return option(:color)
|
253
|
-
|
254
|
-
|
366
|
+
end
|
367
|
+
|
368
|
+
if args == :auto
|
369
|
+
ac = auto_color
|
370
|
+
unless ac.nil?
|
371
|
+
#puts "auto color to #{ac}"
|
372
|
+
if only_color?(get_contents)
|
373
|
+
set_option :color, ac
|
374
|
+
set_option :auto_color, true
|
375
|
+
else
|
376
|
+
set_auto_color_for_children(ac, get_contents)
|
377
|
+
end
|
378
|
+
|
379
|
+
end
|
380
|
+
return self
|
381
|
+
end
|
382
|
+
|
383
|
+
c = color_parse(args)
|
384
|
+
unless c.nil?
|
385
|
+
set_option :color, c
|
386
|
+
set_option :auto_color, false
|
387
|
+
end
|
388
|
+
|
389
|
+
self
|
390
|
+
end
|
391
|
+
|
392
|
+
def color_parse(args=nil)
|
393
|
+
case args
|
255
394
|
when :none
|
256
395
|
set_option :no_auto_color, true
|
257
396
|
when :random
|
258
|
-
|
397
|
+
return Color.random
|
259
398
|
when Array
|
260
|
-
|
399
|
+
return Color.parse(args)
|
261
400
|
when /(?<=#)(?<!^)(\h{6}|\h{3})/
|
262
|
-
|
401
|
+
return args
|
263
402
|
when /(?<!^)(\h{6}|\h{3})/
|
264
|
-
|
403
|
+
return "##{args}"
|
265
404
|
when String
|
266
|
-
|
267
|
-
else
|
268
|
-
puts "meow"
|
405
|
+
return args
|
269
406
|
end
|
270
|
-
|
407
|
+
nil
|
271
408
|
end
|
272
409
|
|
273
410
|
def auto_color
|
274
411
|
if option(:color) == nil && !option(:no_auto_color)
|
275
|
-
auto_color!
|
412
|
+
return auto_color!
|
276
413
|
end
|
414
|
+
nil
|
277
415
|
end
|
278
416
|
|
279
417
|
def auto_color!
|
280
|
-
|
418
|
+
color_parse($jenncad_profile.colors.pop)
|
281
419
|
end
|
282
420
|
|
283
421
|
def color_or_fallback
|
@@ -285,12 +423,44 @@ module JennCad
|
|
285
423
|
option(:color)
|
286
424
|
end
|
287
425
|
|
288
|
-
def
|
289
|
-
|
290
|
-
|
291
|
-
|
426
|
+
def get_contents
|
427
|
+
return @parts unless @parts.nil?
|
428
|
+
if self.respond_to? :part
|
429
|
+
return [part]
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def calculated_h
|
434
|
+
return @z unless @z.nil? || @z == 0
|
435
|
+
return @h unless @h.nil? || @h == 0
|
436
|
+
return @calc_h unless @calc_h.nil? || @calc_h == 0
|
437
|
+
return @calc_z unless @calc_z.nil? || @calc_z == 0
|
438
|
+
end
|
439
|
+
|
440
|
+
def find_calculated_h(parts)
|
441
|
+
return if parts == nil
|
442
|
+
parts.each do |part|
|
443
|
+
if z = calculated_h
|
444
|
+
return z
|
445
|
+
end
|
446
|
+
get_calculated_h(part.get_contents)
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
def set_heights_for_auto_extrude(parts, parent=nil)
|
451
|
+
return if parts.nil?
|
452
|
+
parts.each do |part|
|
453
|
+
if part.option(:auto_extrude)
|
454
|
+
part.z = parent.calculated_h
|
292
455
|
end
|
456
|
+
set_heights_for_auto_extrude(part.get_contents, part)
|
293
457
|
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def openscad(file)
|
461
|
+
set_heights_for_auto_extrude(get_contents)
|
462
|
+
|
463
|
+
@parts = get_contents
|
294
464
|
|
295
465
|
JennCad::Exporters::OpenScad.new(self).save(file)
|
296
466
|
end
|
data/lib/jenncad/version.rb
CHANGED
data/lib/jenncad.rb
CHANGED
@@ -20,10 +20,10 @@ require "jenncad/part"
|
|
20
20
|
require "jenncad/project"
|
21
21
|
require "jenncad/commands"
|
22
22
|
|
23
|
+
require "jenncad/features"
|
23
24
|
require "jenncad/primitives"
|
24
25
|
|
25
26
|
|
26
|
-
|
27
27
|
require "jenncad/transformation/transformation"
|
28
28
|
require "jenncad/transformation/move"
|
29
29
|
require "jenncad/transformation/rotate"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jenncad
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.pre.
|
4
|
+
version: 1.0.0.pre.alpha6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jennifer Glauche
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: geo3d
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: hanami-cli
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.3.1
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.3.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: activesupport
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,6 +88,7 @@ executables:
|
|
88
88
|
extensions: []
|
89
89
|
extra_rdoc_files: []
|
90
90
|
files:
|
91
|
+
- README.md
|
91
92
|
- Rakefile
|
92
93
|
- bin/jenncad
|
93
94
|
- examples/old/cube.rb
|
@@ -108,10 +109,16 @@ files:
|
|
108
109
|
- lib/jenncad/extras/din934.rb
|
109
110
|
- lib/jenncad/extras/hardware.rb
|
110
111
|
- lib/jenncad/extras/iso7380.rb
|
112
|
+
- lib/jenncad/features.rb
|
113
|
+
- lib/jenncad/features/aggregation.rb
|
114
|
+
- lib/jenncad/features/climb.rb
|
115
|
+
- lib/jenncad/features/cuttable.rb
|
116
|
+
- lib/jenncad/features/feature.rb
|
117
|
+
- lib/jenncad/features/openscad_include.rb
|
118
|
+
- lib/jenncad/features/stl_import.rb
|
111
119
|
- lib/jenncad/part.rb
|
112
120
|
- lib/jenncad/patches/array.rb
|
113
121
|
- lib/jenncad/primitives.rb
|
114
|
-
- lib/jenncad/primitives/aggregation.rb
|
115
122
|
- lib/jenncad/primitives/boolean_object.rb
|
116
123
|
- lib/jenncad/primitives/circle.rb
|
117
124
|
- lib/jenncad/primitives/cube.rb
|
@@ -119,7 +126,6 @@ files:
|
|
119
126
|
- lib/jenncad/primitives/hull_object.rb
|
120
127
|
- lib/jenncad/primitives/intersection_object.rb
|
121
128
|
- lib/jenncad/primitives/linear_extrude.rb
|
122
|
-
- lib/jenncad/primitives/openscad_include.rb
|
123
129
|
- lib/jenncad/primitives/polygon.rb
|
124
130
|
- lib/jenncad/primitives/primitive.rb
|
125
131
|
- lib/jenncad/primitives/projection.rb
|
@@ -147,7 +153,7 @@ homepage: ''
|
|
147
153
|
licenses:
|
148
154
|
- LGPL-3
|
149
155
|
metadata: {}
|
150
|
-
post_install_message:
|
156
|
+
post_install_message:
|
151
157
|
rdoc_options: []
|
152
158
|
require_paths:
|
153
159
|
- lib
|
@@ -162,8 +168,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
168
|
- !ruby/object:Gem::Version
|
163
169
|
version: 1.3.1
|
164
170
|
requirements: []
|
165
|
-
rubygems_version: 3.0.
|
166
|
-
signing_key:
|
171
|
+
rubygems_version: 3.0.9
|
172
|
+
signing_key:
|
167
173
|
specification_version: 4
|
168
174
|
summary: TBD
|
169
175
|
test_files: []
|