laser-cutter 0.5.3 → 1.0.0

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.
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,5 +1,5 @@
1
1
  module Laser
2
2
  module Cutter
3
- VERSION = "0.5.3"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
@@ -0,0 +1,65 @@
1
+ require_relative 'spec_helper'
2
+
3
+ module Laser
4
+ module Cutter
5
+ describe Aggregator do
6
+ let(:p1) { Geometry::Point[0, 0] }
7
+ let(:p2) { Geometry::Point[2, 0] }
8
+ let(:p3) { Geometry::Point[5, 0] }
9
+ let(:p4) { Geometry::Point[10, 0] }
10
+ let(:p5) { Geometry::Point[0, 12] }
11
+
12
+ let(:l1) { Geometry::Line.new(p1, p3) }
13
+ let(:l2) { Geometry::Line.new(p2, p4) }
14
+ let(:l3) { Geometry::Line.new(p2, p5) }
15
+ let(:l4) { Geometry::Line.new(p1, p2) }
16
+ let(:l5) { Geometry::Line.new(p3, p4) }
17
+
18
+ let(:lines) { [ l1, l2 ]}
19
+
20
+ let(:aggregator) { Aggregator.new(lines) }
21
+
22
+ context '#initialize' do
23
+ it 'should initialize with passed in parameters' do
24
+ expect(aggregator.lines.size).to eql(2)
25
+ end
26
+ end
27
+
28
+ context '#dedup' do
29
+ let(:a) { [1,5,3,1,2,2,2,2 ] }
30
+ let(:unique_lines) { [ l1, l2, l3, l4, l5 ]}
31
+ it 'should remove dups from a simple array' do
32
+ expect(Aggregator.new(a).dedup!.lines).to eql([3,5])
33
+ end
34
+ context 'short array' do
35
+ let(:lines) { [ l1, l2, l1, l1 ]}
36
+ let(:result) { [ l2 ]}
37
+ it 'should remove dupes from lines array' do
38
+ expect(Aggregator.new(lines).dedup!.lines.map(&:to_s)).to eql(result.map(&:to_s))
39
+ end
40
+ end
41
+ context 'long array' do
42
+ let(:lines) { [ l4, l1, l2, l3, l4, l3, l4, l5 ]}
43
+ let(:result) { [ l1, l2, l5 ]}
44
+ it 'should remove dupes from lines array' do
45
+ expect(Aggregator.new(lines).dedup!.lines.map(&:to_s)).to eql(result.map(&:to_s))
46
+ end
47
+ end
48
+ end
49
+
50
+ context '#deoverlap' do
51
+ let(:lines) { [ l1, l2, l3 ]}
52
+
53
+ let(:deoverlapped) { [ l4, l5, l3 ].sort }
54
+ it 'should remove lines that overlap' do
55
+ expect(aggregator.lines.size).to eql(3)
56
+ aggregator.deoverlap!
57
+ expect(aggregator.lines.size).to eql(3)
58
+ expect(aggregator.lines.map(&:to_s)).to eql(deoverlapped.map(&:to_s))
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+ p
@@ -20,9 +20,13 @@ module Laser
20
20
  end
21
21
 
22
22
  context '#notches' do
23
+ before do
24
+ box1.generate_notches
25
+ box2.generate_notches
26
+ end
23
27
  it 'should generate notches' do
24
28
  expect(box1.notches).to_not be_nil
25
- expect(box1.notches.size).to eql(320)
29
+ expect(box1.notches.size).to eql(368)
26
30
  end
27
31
 
28
32
  it 'should properly calculate enclosure' do
@@ -1,5 +1,4 @@
1
1
  require_relative 'spec_helper'
2
-
3
2
  module Laser
4
3
  module Cutter
5
4
  module Geometry
@@ -0,0 +1,43 @@
1
+ require_relative 'spec_helper'
2
+
3
+ module Laser
4
+ module Cutter
5
+
6
+ describe "Notching::Edge" do
7
+ context 'left vertical side' do
8
+ let(:notch_width) { 2 }
9
+ let(:inside) { Geometry::Line.new(Geometry::Point[1, 1], Geometry::Point[1, 9])}
10
+ let(:outside) { Geometry::Line.new(Geometry::Point[0, 0], Geometry::Point[0, 10]) }
11
+ let(:edge) { Notching::Edge.new(inside, outside,
12
+ center_out: true,
13
+ fill_corners: true,
14
+ notch_width: notch_width,
15
+ kerf: 0.02,
16
+ thickness: 1) }
17
+
18
+ it 'should create a node correctly' do
19
+ expect(inside.length).to eql(8.0)
20
+ expect(outside.length).to eql(10.0)
21
+ expect(edge.center_out).to be_truthy
22
+ expect(edge.kerf).to be_within(0.0001).of(0.02)
23
+ expect(edge.thickness).to be_within(0.0001).of(1)
24
+ expect(edge.notch_width).to be_within(notch_width / 3.0).of(notch_width)
25
+ end
26
+
27
+ it 'should calculate notch width correctly' do
28
+ expect(inside.length).to eql(8.0)
29
+ expect(outside.length).to eql(10.0)
30
+ expect(edge.center_out).to be_truthy
31
+
32
+ expect(edge.kerf).to be_within(0.0001).of(0.02)
33
+ expect(edge.thickness).to be_within(0.0001).of(1)
34
+ end
35
+
36
+ it 'should correctly calculate v1 and v2' do
37
+ expect(edge.v1.to_a).to eql([1.0,1.0])
38
+ expect(edge.v2.to_a).to eql([1.0,-1.0])
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -6,7 +6,7 @@ module Laser
6
6
  describe Line do
7
7
  let(:p1) { Point.new(1, 1) }
8
8
  let(:p2) { Point.new(7, 11) }
9
- let(:center) { Point.new( (7 + 1) / 2, (11 + 1) / 2) }
9
+ let(:center) { Point.new((7 + 1) / 2, (11 + 1) / 2) }
10
10
  let(:line) { Line.new(p1, p2) }
11
11
 
12
12
  context '#center' do
@@ -16,11 +16,11 @@ module Laser
16
16
  end
17
17
 
18
18
  context '#initialize' do
19
- let(:line2) { Line.new(from: [1,1], to: [7,11])}
20
- let(:line3) { Line.new(from: Point.new(1,1), to: Point.new(7,11))}
19
+ let(:line2) { Line.new(from: [1, 1], to: [7, 11]) }
20
+ let(:line3) { Line.new(from: Point.new(1, 1), to: Point.new(7, 11)) }
21
21
  it 'should create' do
22
- expect(line2.p1).to eql(Point.new(1,1))
23
- expect(line2.p2).to eql(Point.new(7,11))
22
+ expect(line2.p1).to eql(Point.new(1, 1))
23
+ expect(line2.p2).to eql(Point.new(7, 11))
24
24
  end
25
25
  it 'should properly equal identical line' do
26
26
  expect(line).to eql(line2)
@@ -41,38 +41,61 @@ module Laser
41
41
  end
42
42
 
43
43
  context 'ordering and equality' do
44
- let(:l1) { Line.new(Point[0,0], Point[10,10]) }
45
- let(:l2) { Line.new(Point[0,1], Point[10,10]) }
46
- let(:l3) { Line.new(Point[0,0], Point[11,10]) }
47
- let(:l4) { Line.new(Point[20,20], Point[1,1]) }
48
- let(:l5) { Line.new(Point[11,10], Point[0,0]) }
44
+ let(:l1) { Line.new(Point[0, 0], Point[10, 10]) }
45
+ let(:l2) { Line.new(Point[0, 1], Point[10, 10]) }
46
+ let(:l3) { Line.new(Point[0, 0], Point[11, 10]) }
47
+ let(:l4) { Line.new(Point[20, 20], Point[1, 1]) }
48
+ let(:l5) { Line.new(Point[11, 10], Point[0, 0]) }
49
49
  it 'should detect equality' do
50
50
  expect(l1).to eql(Line.new(l1.p1, l1.p2))
51
- expect(l1).to_not eql(Line.new(l1.p1, Point[2,4]))
51
+ expect(l1).to_not eql(Line.new(l1.p1, Point[2, 4]))
52
52
  expect(l5).to eql(l3)
53
53
  expect(l5.hash).to eql(l3.hash)
54
54
  end
55
55
 
56
56
  it 'should properly compare' do
57
- list = [l4,l3,l1,l2]
57
+ list = [l4, l3, l1, l2]
58
58
  list.sort!
59
- expect(list).to eql([l1,l3,l2,l4])
59
+ expect(list).to eql([l1, l3, l2, l4])
60
60
  end
61
61
 
62
62
  it 'should properly uniq' do
63
63
  list = [l4, l1, l4, l2, l3, l3, l2, l1]
64
64
  list.sort!.uniq!
65
- expect(list).to eql([l1,l3,l2,l4])
65
+ expect(list).to eql([l1, l3, l2, l4])
66
66
  end
67
+ end
68
+ context 'overlaps and such' do
69
+ let(:l1) { Line.new(Point[0, 0], Point[10, 0]) }
70
+ let(:l2) { Line.new(Point[7, 0], Point[15, 0]) }
71
+ let(:l3) { Line.new(Point[20, 0], Point[25, 0]) }
72
+ let(:l4) { Line.new(Point[0, 1], Point[0, 3]) }
73
+ let(:l5) { Line.new(Point[0, -1], Point[0, 2]) }
74
+
67
75
 
68
- it 'should properly deduplicate' do
69
- list = [l1, l4, l2, l3, l5, l3, l1, l3, l3, l2]
70
- new_list = PathGenerator.deduplicate(list)
71
- expect(new_list).to eql([l4])
76
+ context '#overlaps?' do
77
+ it 'should detect overlap' do
78
+ expect(l1.overlaps?(l2)).to be_truthy
79
+ expect(l1.overlaps?(l3)).to be_falsey
80
+ expect(l2.overlaps?(l3)).to be_falsey
81
+ expect(l1.overlaps?(l4)).to be_falsey
82
+ expect(l2.overlaps?(l4)).to be_falsey
83
+ expect(l4.overlaps?(l5)).to be_truthy
84
+ end
72
85
  end
73
86
 
87
+ context '#xor' do
88
+ let(:xor) { [ Line.new(Point[0, 0], Point[7, 0]), Line.new(Point[10, 0], Point[15, 0])]}
89
+ it 'should subtract lines' do
90
+ expect(l1.xor(l2)).to eql(xor)
91
+ expect(l1.overlaps?(l3)).to be_falsey
92
+ expect(l2.overlaps?(l3)).to be_falsey
93
+ expect(l1.overlaps?(l4)).to be_falsey
94
+ expect(l2.overlaps?(l4)).to be_falsey
95
+ expect(l4.overlaps?(l5)).to be_truthy
96
+ end
97
+ end
74
98
  end
75
-
76
99
  end
77
100
  end
78
101
  end
@@ -2,19 +2,23 @@ require_relative 'spec_helper'
2
2
 
3
3
  module Laser
4
4
  module Cutter
5
- module Geometry
5
+ module Notching
6
6
  describe PathGenerator do
7
7
  let(:notch) { 2 }
8
8
  let(:thickness) { 1 }
9
9
  let(:center_out) { true }
10
- let(:fill_edge) { true }
11
- let(:outside) { Line.new(from: [0, 0], to: [10, 0]) }
12
- let(:inside) { Line.new(from: [1, 1], to: [9, 1]) }
13
- let(:edge) { Edge.new(outside, inside, notch) }
14
- let(:generator) { PathGenerator.new(notch_width: notch,
15
- thickness: thickness,
16
- center_out: center_out,
17
- fill_edge: fill_edge) }
10
+ let(:corners) { true }
11
+
12
+ let(:options) { {notch_width: notch,
13
+ thickness: thickness,
14
+ center_out: center_out,
15
+ corners: corners} }
16
+
17
+ let(:outside) { Geometry::Line.new(from: [0, 0], to: [10, 0]) }
18
+ let(:inside) { Geometry::Line.new(from: [1, 1], to: [9, 1]) }
19
+ let(:edge) { Edge.new(outside, inside, options) }
20
+ let(:generator) { PathGenerator.new(edge) }
21
+
18
22
  context 'edge' do
19
23
  it 'should properly calculate notch size' do
20
24
  expect(edge.notch_width).to be_within(0.001).of(1.6)
@@ -29,19 +33,20 @@ module Laser
29
33
  end
30
34
 
31
35
  context 'alternating iterator' do
32
- let(:iterator) { InfiniteIterator.new([:a, :b, :c]) }
36
+ let(:a) { "hello" }
37
+ let(:b) { "again" }
38
+ let(:iterator) { InfiniteIterator.new([a,b]) }
33
39
  it 'returns things in alternating order' do
34
- expect(iterator.next).to eq(:a)
35
- expect(iterator.next).to eq(:b)
36
- expect(iterator.next).to eq(:c)
37
- expect(iterator.next).to eq(:a)
40
+ expect(iterator.next).to eq(a)
41
+ expect(iterator.next).to eq(b)
42
+ expect(iterator.next).to eq(a)
38
43
  end
39
44
  end
40
45
 
41
46
  context 'shift definition' do
42
47
 
43
48
  it 'correctly defines shifts' do
44
- shifts = generator.send(:define_shifts, edge)
49
+ shifts = generator.send(:define_shifts)
45
50
  expect(edge.outside.length).to eql(10.0)
46
51
  expect(edge.inside.length).to eql(8.0)
47
52
  expect(edge.notch_width).to be_within(0.001).of(1.6)
@@ -53,41 +58,30 @@ module Laser
53
58
 
54
59
  context 'path generation' do
55
60
  # let(:outside) { Line.new(
56
- # from: inside.p1.move_by(-thickness, -thickness),
57
- # to: inside.p2.move_by(thickness, -thickness)) }
61
+ # from: inside.p1.plus(-thickness, -thickness),
62
+ # to: inside.p2.plus(thickness, -thickness)) }
58
63
 
59
64
  context 'center out' do
60
65
  it 'generates correct path vertices' do
61
- inside.freeze
62
66
  expect(inside.p1).to_not eql(inside.p2)
63
- path = generator.path(edge)
64
- expect(path).to be_a_kind_of(NotchedPath)
65
- expect(path.size).to be > 5
67
+ lines = generator.generate
68
+ expect(lines.size).to be > 5
66
69
 
67
- expect(Line.new(path.vertices.first, inside.p1).length).to be_within(0.001).of(0)
68
- expect(Line.new(path.vertices.last, inside.p2).length).to be_within(0.001).of(0)
70
+ expect(Geometry::Line.new(lines.first.p1, inside.p1).length).to be_within(0.001).of(0)
71
+ expect(Geometry::Line.new(lines.last.p2, inside.p2).length).to be_within(0.001).of(0)
69
72
 
70
73
  # Sanity Check
71
- expect(Point.new(1, 1)).to eql(inside.p1)
72
- expect(Point.new(9, 1)).to eql(inside.p2)
74
+ expect(Geometry::Point.new(1, 1)).to eql(inside.p1)
75
+ expect(Geometry::Point.new(9, 1)).to eql(inside.p2)
73
76
  end
74
77
 
75
78
  it 'generates correct lines' do
76
- path = generator.path(edge)
77
- lines = path.create_lines
78
- expect(path.size).to eq(12)
79
- expect(lines.size).to be > 1
79
+ lines = generator.generate
80
+ expect(lines.size).to eq(19)
80
81
  end
81
82
  end
82
83
  end
83
84
 
84
- context 'remove dupes' do
85
- let(:a) { [1,5,3,1,2,2,2,2 ] }
86
- it 'should remove dups' do
87
- expect(PathGenerator.deduplicate(a)).to eql([3,5])
88
- end
89
- end
90
-
91
85
  end
92
86
  end
93
87
  end
@@ -42,12 +42,12 @@ module Laser
42
42
 
43
43
  context 'move by' do
44
44
  it 'should move properly' do
45
- p = p1.move_by(10, -2)
45
+ p = p1.plus(10, -2)
46
46
  expect(p.x).to be_within(0.001).of(11)
47
47
  expect(p.y).to be_within(0.001).of(0)
48
48
  end
49
49
  it 'should move cloned version properly' do
50
- p2 = p1.clone.move_by(10, -2)
50
+ p2 = p1.clone.plus(10, -2)
51
51
  expect(p2.x).to be_within(0.001).of(11)
52
52
  expect(p2.y).to be_within(0.001).of(0)
53
53
  end
@@ -28,7 +28,7 @@ module Laser
28
28
  expect(rect1.sides.first.p1).to eql(p1)
29
29
  expect(rect1.sides[0].p1.to_s).to eql("{1.00000,3.00000}")
30
30
  expect(rect1.sides[1].p1.to_s).to eql("{11.00000,3.00000}")
31
- expect(rect1.sides[2].p1).to eql(p1.move_by(10, 20))
31
+ expect(rect1.sides[2].p1).to eql(p1.plus(10, 20))
32
32
  end
33
33
  it 'can be moved' do
34
34
  expect(rect1.sides[0].p1.to_s).to eql("{1.00000,3.00000}")
@@ -9,17 +9,26 @@ module Laser
9
9
  let(:file) { File.expand_path("../../laser-cutter-pdf-test.#{$$}.pdf", __FILE__) }
10
10
 
11
11
  def render_file filename
12
+ real_file = ENV['RSPEC_SAVE_PDF'] ? true : false
12
13
  config.validate!
13
- expect(!File.exists?(filename))
14
+ expect(!File.exists?(filename)) if real_file
14
15
  renderer.render
15
- expect(File.exist?(filename))
16
- expect(File.size(filename) > 0)
16
+ expect(File.exist?(filename)) if real_file
17
+ expect(File.size(filename) > 0) if real_file
17
18
  rescue Exception => e
18
19
  STDERR.puts e.backtrace.join("\n")
19
20
  fail e.message
20
21
  ensure
21
- File.delete(filename) rescue nil
22
- expect(!File.exists?(filename))
22
+ if real_file
23
+ File.delete(filename) rescue nil
24
+ expect(!File.exists?(filename))
25
+ end
26
+ end
27
+
28
+ before do
29
+ unless ENV['RSPEC_SAVE_PDF']
30
+ expect_any_instance_of(Prawn::Document).to receive(:render_file).once
31
+ end
23
32
  end
24
33
 
25
34
  context 'metric' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: laser-cutter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-05 00:00:00.000000000 Z
11
+ date: 2014-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prawn
@@ -146,18 +146,22 @@ files:
146
146
  - doc/comparison.jpg
147
147
  - laser-cutter.gemspec
148
148
  - lib/laser-cutter.rb
149
+ - lib/laser-cutter/aggregator.rb
149
150
  - lib/laser-cutter/box.rb
151
+ - lib/laser-cutter/cli/opt_parser.rb
152
+ - lib/laser-cutter/cli/serializer.rb
150
153
  - lib/laser-cutter/configuration.rb
151
154
  - lib/laser-cutter/geometry.rb
152
155
  - lib/laser-cutter/geometry/dimensions.rb
153
- - lib/laser-cutter/geometry/edge.rb
154
- - lib/laser-cutter/geometry/notched_path.rb
155
- - lib/laser-cutter/geometry/path_generator.rb
156
156
  - lib/laser-cutter/geometry/point.rb
157
157
  - lib/laser-cutter/geometry/shape.rb
158
158
  - lib/laser-cutter/geometry/shape/line.rb
159
159
  - lib/laser-cutter/geometry/shape/rect.rb
160
160
  - lib/laser-cutter/geometry/tuple.rb
161
+ - lib/laser-cutter/notching.rb
162
+ - lib/laser-cutter/notching/base.rb
163
+ - lib/laser-cutter/notching/edge.rb
164
+ - lib/laser-cutter/notching/path_generator.rb
161
165
  - lib/laser-cutter/page_manager.rb
162
166
  - lib/laser-cutter/renderer.rb
163
167
  - lib/laser-cutter/renderer/base.rb
@@ -167,9 +171,11 @@ files:
167
171
  - lib/laser-cutter/renderer/meta_renderer.rb
168
172
  - lib/laser-cutter/renderer/rect_renderer.rb
169
173
  - lib/laser-cutter/version.rb
174
+ - spec/aggregator_spec.rb
170
175
  - spec/box_spec.rb
171
176
  - spec/configuration_spec.rb
172
177
  - spec/dimensions_spec.rb
178
+ - spec/edge_spec.rb
173
179
  - spec/line_spec.rb
174
180
  - spec/page_manager_spec.rb
175
181
  - spec/path_generator_spec.rb
@@ -203,9 +209,11 @@ specification_version: 4
203
209
  summary: Creates notched box outlines for laser-cut boxes which are geometrically
204
210
  symmetric and pleasing to the eye.
205
211
  test_files:
212
+ - spec/aggregator_spec.rb
206
213
  - spec/box_spec.rb
207
214
  - spec/configuration_spec.rb
208
215
  - spec/dimensions_spec.rb
216
+ - spec/edge_spec.rb
209
217
  - spec/line_spec.rb
210
218
  - spec/page_manager_spec.rb
211
219
  - spec/path_generator_spec.rb