laser-cutter 0.5.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/README.md +20 -16
- data/bin/laser-cutter +4 -158
- data/lib/laser-cutter.rb +2 -0
- data/lib/laser-cutter/aggregator.rb +57 -0
- data/lib/laser-cutter/box.rb +37 -24
- data/lib/laser-cutter/cli/opt_parser.rb +131 -0
- data/lib/laser-cutter/cli/serializer.rb +51 -0
- data/lib/laser-cutter/configuration.rb +3 -2
- data/lib/laser-cutter/geometry.rb +0 -3
- data/lib/laser-cutter/geometry/dimensions.rb +3 -3
- data/lib/laser-cutter/geometry/point.rb +0 -46
- data/lib/laser-cutter/geometry/shape/line.rb +40 -1
- data/lib/laser-cutter/geometry/shape/rect.rb +2 -2
- data/lib/laser-cutter/geometry/tuple.rb +73 -27
- data/lib/laser-cutter/notching.rb +10 -0
- data/lib/laser-cutter/notching/base.rb +13 -0
- data/lib/laser-cutter/notching/edge.rb +87 -0
- data/lib/laser-cutter/notching/path_generator.rb +249 -0
- data/lib/laser-cutter/renderer/base.rb +1 -1
- data/lib/laser-cutter/renderer/box_renderer.rb +2 -2
- data/lib/laser-cutter/renderer/layout_renderer.rb +19 -8
- data/lib/laser-cutter/renderer/meta_renderer.rb +8 -9
- data/lib/laser-cutter/version.rb +1 -1
- data/spec/aggregator_spec.rb +65 -0
- data/spec/box_spec.rb +5 -1
- data/spec/dimensions_spec.rb +0 -1
- data/spec/edge_spec.rb +43 -0
- data/spec/line_spec.rb +42 -19
- data/spec/path_generator_spec.rb +30 -36
- data/spec/point_spec.rb +2 -2
- data/spec/rect_spec.rb +1 -1
- data/spec/renderer_spec.rb +14 -5
- metadata +13 -5
- data/lib/laser-cutter/geometry/edge.rb +0 -33
- data/lib/laser-cutter/geometry/notched_path.rb +0 -46
- data/lib/laser-cutter/geometry/path_generator.rb +0 -129
@@ -1,33 +0,0 @@
|
|
1
|
-
module Laser
|
2
|
-
module Cutter
|
3
|
-
module Geometry
|
4
|
-
# This class represents a single edge of one side: both inside
|
5
|
-
# and outside edge of the material. It's also responsible
|
6
|
-
# for calculating the "perfect" notch width.
|
7
|
-
class Edge < Struct.new(:outside, :inside, :notch_width)
|
8
|
-
attr_accessor :notch_count
|
9
|
-
|
10
|
-
def initialize(*args)
|
11
|
-
super(*args)
|
12
|
-
adjust_notch(self.inside)
|
13
|
-
end
|
14
|
-
|
15
|
-
def adjust_notch(line)
|
16
|
-
d = (line.length / notch_width).to_f.ceil
|
17
|
-
pairs = d / 2
|
18
|
-
d = pairs * 2 + 1
|
19
|
-
d = MINIMUM_NOTCHES_PER_SIDE if d < MINIMUM_NOTCHES_PER_SIDE
|
20
|
-
self.notch_width = line.length / (1.0 * d)
|
21
|
-
self.notch_count = d
|
22
|
-
end
|
23
|
-
|
24
|
-
# face_setting determines if we want that face to have center notch
|
25
|
-
# facing out (for a hole, etc). This works well when we have odd number
|
26
|
-
# of notches, but
|
27
|
-
def add_across_line?(face_setting)
|
28
|
-
notch_count % 4 == 1 ? face_setting : !face_setting
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
module Laser
|
2
|
-
module Cutter
|
3
|
-
module Geometry
|
4
|
-
class NotchedPath
|
5
|
-
attr_accessor :lines, :vertices, :corner_boxes
|
6
|
-
def initialize(vertices = [])
|
7
|
-
@vertices = vertices
|
8
|
-
@lines = []
|
9
|
-
@corner_boxes = []
|
10
|
-
end
|
11
|
-
|
12
|
-
def << value
|
13
|
-
self.vertices << value
|
14
|
-
end
|
15
|
-
|
16
|
-
def [] value
|
17
|
-
self.vertices[value]
|
18
|
-
end
|
19
|
-
|
20
|
-
def size
|
21
|
-
self.vertices.size
|
22
|
-
end
|
23
|
-
|
24
|
-
def create_lines
|
25
|
-
self.lines = []
|
26
|
-
self.vertices.each_with_index do |v, i|
|
27
|
-
if v != vertices.last
|
28
|
-
self.lines << Line.new(v, vertices[i+1])
|
29
|
-
end
|
30
|
-
end
|
31
|
-
self.corner_boxes.each do |box|
|
32
|
-
box.relocate!
|
33
|
-
self.lines << box.sides
|
34
|
-
end
|
35
|
-
|
36
|
-
self.lines.flatten!
|
37
|
-
self.lines
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
@@ -1,129 +0,0 @@
|
|
1
|
-
require_relative 'notched_path'
|
2
|
-
|
3
|
-
module Laser
|
4
|
-
module Cutter
|
5
|
-
module Geometry
|
6
|
-
class Shift < Struct.new(:delta, :direction, :dim_index)
|
7
|
-
POINTERS = {[1, 0] => ' ->',
|
8
|
-
[-1, 0] => '<- ',
|
9
|
-
[1, 1] => ' V ',
|
10
|
-
[-1, 1] => ' ^ '}
|
11
|
-
|
12
|
-
def next(point1)
|
13
|
-
p = Point.new(point1.to_a)
|
14
|
-
p.coords[dim_index] += (delta * direction)
|
15
|
-
p
|
16
|
-
end
|
17
|
-
def to_s
|
18
|
-
"shift by:#{sprintf('%.2f', delta)}, #{POINTERS[[direction,dim_index]]}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Alternating iterator
|
23
|
-
class InfiniteIterator < Struct.new(:shift_array)
|
24
|
-
attr_accessor :current_index
|
25
|
-
def next
|
26
|
-
self.current_index = -1 if current_index.nil?
|
27
|
-
self.current_index += 1
|
28
|
-
self.current_index = current_index % shift_array.size
|
29
|
-
shift_array[current_index]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class PathGenerator
|
34
|
-
# Removes the items from the list that appear more than once
|
35
|
-
# Unlike uniq-ing which keeps all elements, just ensures that are not
|
36
|
-
# repeated, here we remove elements completely if they are seen more than once.
|
37
|
-
# This is used to remove lines that join the same two points.
|
38
|
-
def self.deduplicate list
|
39
|
-
new_list = []
|
40
|
-
list.sort!
|
41
|
-
list.each_with_index do |e, i|
|
42
|
-
next if i < (list.size - 1) && e.eql?(list[i + 1])
|
43
|
-
next if i > 0 && e.eql?(list[i - 1])
|
44
|
-
new_list << e
|
45
|
-
end
|
46
|
-
new_list
|
47
|
-
end
|
48
|
-
|
49
|
-
attr_accessor :notch_width, :thickness
|
50
|
-
attr_accessor :center_out, :fill_corners
|
51
|
-
|
52
|
-
def initialize(options = {})
|
53
|
-
@notch_width = options[:notch_width] # only desired, will be adapted for each line
|
54
|
-
@center_out = options[:center_out] # when true, the notch in the middle of the edge is out, not in.
|
55
|
-
@thickness = options[:thickness]
|
56
|
-
@fill_corners = options[:fill_corners]
|
57
|
-
end
|
58
|
-
|
59
|
-
# Calculates a notched path that flows between the outer edge of the box
|
60
|
-
# (outside_line) and inner (inside_line). Relative location of these lines
|
61
|
-
# also defines the direction and orientation of the box, and hence the notches.
|
62
|
-
#
|
63
|
-
# We always want to create a symmetric path that has a notch in the middle
|
64
|
-
# (for center_out = true) or dip in the middle (center_out = false)
|
65
|
-
def path(edge)
|
66
|
-
shifts = define_shifts(edge)
|
67
|
-
|
68
|
-
path = NotchedPath.new
|
69
|
-
|
70
|
-
if fill_corners
|
71
|
-
r1 = Geometry::Rect.new(edge.inside.p1, edge.outside.p1)
|
72
|
-
r2 = Geometry::Rect.new(edge.inside.p2, edge.outside.p2)
|
73
|
-
path.corner_boxes << r1
|
74
|
-
path.corner_boxes << r2
|
75
|
-
end
|
76
|
-
|
77
|
-
point = edge.inside.p1.clone
|
78
|
-
vertices = [point]
|
79
|
-
shifts.each do |shift|
|
80
|
-
point = shift.next(point)
|
81
|
-
vertices << point
|
82
|
-
end
|
83
|
-
path.vertices = vertices
|
84
|
-
path
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
# This method has the bulk of the logic: we create the list of path deltas
|
90
|
-
# to be applied when we walk the edge next.
|
91
|
-
def define_shifts(edge)
|
92
|
-
along_iterator, across_iterator = define_shift_iterators(edge)
|
93
|
-
shifts = []
|
94
|
-
|
95
|
-
shifts << across_iterator.next if edge.add_across_line?(center_out)
|
96
|
-
|
97
|
-
(1..edge.notch_count).to_a.each do |count|
|
98
|
-
shifts << along_iterator.next
|
99
|
-
shifts << across_iterator.next unless count == edge.notch_count
|
100
|
-
end
|
101
|
-
|
102
|
-
shifts << across_iterator.next if edge.add_across_line?(center_out)
|
103
|
-
shifts
|
104
|
-
end
|
105
|
-
|
106
|
-
# As we draw notches, shifts define the 'delta' – movement from one point
|
107
|
-
# to the next. This method defines three types of movements we'll be doing:
|
108
|
-
# one alongside the edge, and two across (towards the box and outward from the box)
|
109
|
-
def define_shift_iterators(edge)
|
110
|
-
alongside_dimension = (edge.inside.p1.x == edge.inside.p2.x) ? 1 : 0
|
111
|
-
alongside_direction = (edge.inside.p1.coords[alongside_dimension] <
|
112
|
-
edge.inside.p2.coords[alongside_dimension]) ? 1 : -1
|
113
|
-
|
114
|
-
across_dimension = (alongside_dimension + 1) % 2
|
115
|
-
across_direction = (edge.inside.p1.coords[across_dimension] >
|
116
|
-
edge.outside.p1.coords[across_dimension]) ? -1 : 1
|
117
|
-
|
118
|
-
[
|
119
|
-
InfiniteIterator.new(
|
120
|
-
[Shift.new(edge.notch_width, alongside_direction, alongside_dimension)]),
|
121
|
-
InfiniteIterator.new(
|
122
|
-
[Shift.new(thickness, across_direction, across_dimension),
|
123
|
-
Shift.new(thickness, -across_direction, across_dimension)])
|
124
|
-
]
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|