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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/README.md +20 -16
  4. data/bin/laser-cutter +4 -158
  5. data/lib/laser-cutter.rb +2 -0
  6. data/lib/laser-cutter/aggregator.rb +57 -0
  7. data/lib/laser-cutter/box.rb +37 -24
  8. data/lib/laser-cutter/cli/opt_parser.rb +131 -0
  9. data/lib/laser-cutter/cli/serializer.rb +51 -0
  10. data/lib/laser-cutter/configuration.rb +3 -2
  11. data/lib/laser-cutter/geometry.rb +0 -3
  12. data/lib/laser-cutter/geometry/dimensions.rb +3 -3
  13. data/lib/laser-cutter/geometry/point.rb +0 -46
  14. data/lib/laser-cutter/geometry/shape/line.rb +40 -1
  15. data/lib/laser-cutter/geometry/shape/rect.rb +2 -2
  16. data/lib/laser-cutter/geometry/tuple.rb +73 -27
  17. data/lib/laser-cutter/notching.rb +10 -0
  18. data/lib/laser-cutter/notching/base.rb +13 -0
  19. data/lib/laser-cutter/notching/edge.rb +87 -0
  20. data/lib/laser-cutter/notching/path_generator.rb +249 -0
  21. data/lib/laser-cutter/renderer/base.rb +1 -1
  22. data/lib/laser-cutter/renderer/box_renderer.rb +2 -2
  23. data/lib/laser-cutter/renderer/layout_renderer.rb +19 -8
  24. data/lib/laser-cutter/renderer/meta_renderer.rb +8 -9
  25. data/lib/laser-cutter/version.rb +1 -1
  26. data/spec/aggregator_spec.rb +65 -0
  27. data/spec/box_spec.rb +5 -1
  28. data/spec/dimensions_spec.rb +0 -1
  29. data/spec/edge_spec.rb +43 -0
  30. data/spec/line_spec.rb +42 -19
  31. data/spec/path_generator_spec.rb +30 -36
  32. data/spec/point_spec.rb +2 -2
  33. data/spec/rect_spec.rb +1 -1
  34. data/spec/renderer_spec.rb +14 -5
  35. metadata +13 -5
  36. data/lib/laser-cutter/geometry/edge.rb +0 -33
  37. data/lib/laser-cutter/geometry/notched_path.rb +0 -46
  38. 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