laser-cutter 1.0.3 → 2.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.
- checksums.yaml +5 -5
- data/.envrc +2 -0
- data/.github/workflows/lint.yml +22 -0
- data/.github/workflows/rspec.yml +22 -0
- data/.gitignore +5 -4
- data/.plans/001.00-dry-cli-migration/plan.md +29 -0
- data/.relaxed_rubocop.yml +153 -0
- data/.rspec +1 -1
- data/.rubocop.yml +31 -0
- data/.rubocop_todo.yml +90 -0
- data/.ruby-version +1 -0
- data/CLAUDE.md +62 -0
- data/CONTRIBUTING.md +2 -4
- data/Gemfile +14 -0
- data/README.md +83 -111
- data/Rakefile +41 -0
- data/docs/badges/coverage_badge.svg +21 -0
- data/exe/laser-cutter +8 -0
- data/exe/lc +1 -0
- data/justfile +97 -0
- data/laser-cutter.gemspec +26 -14
- data/lib/laser/cutter/aggregator.rb +101 -0
- data/lib/{laser-cutter → laser/cutter}/box.rb +44 -52
- data/lib/laser/cutter/cli/command.rb +46 -0
- data/lib/laser/cutter/cli/completion.rb +21 -0
- data/lib/laser/cutter/cli/config_file.rb +36 -0
- data/lib/laser/cutter/cli/examples.rb +42 -0
- data/lib/laser/cutter/cli/generate.rb +90 -0
- data/lib/laser/cutter/cli/help.rb +20 -0
- data/lib/laser/cutter/cli/page_sizes.rb +19 -0
- data/lib/laser/cutter/cli/version.rb +15 -0
- data/lib/laser/cutter/cli.rb +49 -0
- data/lib/laser/cutter/configuration.rb +101 -0
- data/lib/{laser-cutter → laser/cutter}/geometry/dimensions.rb +2 -3
- data/lib/{laser-cutter/geometry/shape → laser/cutter/geometry}/line.rb +19 -18
- data/lib/{laser-cutter → laser/cutter}/geometry/point.rb +4 -2
- data/lib/{laser-cutter/geometry/shape → laser/cutter/geometry}/rect.rb +6 -7
- data/lib/{laser-cutter → laser/cutter}/geometry/shape.rb +7 -7
- data/lib/{laser-cutter → laser/cutter}/geometry/tuple.rb +40 -36
- data/lib/laser/cutter/launcher.rb +40 -0
- data/lib/laser/cutter/missing_option.rb +7 -0
- data/lib/laser/cutter/notching/base.rb +19 -0
- data/lib/{laser-cutter → laser/cutter}/notching/edge.rb +21 -20
- data/lib/laser/cutter/notching/infinite_iterator.rb +27 -0
- data/lib/{laser-cutter → laser/cutter}/notching/path_generator.rb +27 -60
- data/lib/laser/cutter/notching/shift.rb +17 -0
- data/lib/laser/cutter/page_manager.rb +50 -0
- data/lib/{laser-cutter → laser/cutter}/renderer/base.rb +3 -2
- data/lib/laser/cutter/renderer/box_renderer.rb +40 -0
- data/lib/laser/cutter/renderer/layout_renderer.rb +61 -0
- data/lib/laser/cutter/renderer/line_renderer.rb +21 -0
- data/lib/laser/cutter/renderer/meta_renderer.rb +83 -0
- data/lib/{laser-cutter → laser/cutter}/renderer/rect_renderer.rb +3 -1
- data/lib/laser/cutter/renderer/svg_renderer.rb +65 -0
- data/lib/laser/cutter/renderer.rb +24 -0
- data/lib/laser/cutter/units_converter.rb +15 -0
- data/lib/laser/cutter/version.rb +7 -0
- data/lib/laser/cutter/zero_value_not_allowed.rb +7 -0
- data/lib/laser/cutter.rb +29 -0
- data/lib/laser-cutter.rb +2 -14
- data/lib/laser_cutter.rb +3 -0
- data/spec/laser/cutter/aggregator_spec.rb +70 -0
- data/spec/laser/cutter/box_spec.rb +61 -0
- data/spec/laser/cutter/cli/config_file_spec.rb +31 -0
- data/spec/laser/cutter/cli_spec.rb +186 -0
- data/spec/laser/cutter/configuration_spec.rb +101 -0
- data/spec/{dimensions_spec.rb → laser/cutter/dimensions_spec.rb} +8 -6
- data/spec/laser/cutter/edge_spec.rb +64 -0
- data/spec/{line_spec.rb → laser/cutter/line_spec.rb} +26 -20
- data/spec/{page_manager_spec.rb → laser/cutter/page_manager_spec.rb} +27 -11
- data/spec/{path_generator_spec.rb → laser/cutter/path_generator_spec.rb} +18 -14
- data/spec/{point_spec.rb → laser/cutter/point_spec.rb} +27 -22
- data/spec/{rect_spec.rb → laser/cutter/rect_spec.rb} +9 -5
- data/spec/laser/cutter/renderer/layout_renderer_spec.rb +102 -0
- data/spec/laser/cutter/renderer/svg_renderer_spec.rb +51 -0
- data/spec/laser/cutter/renderer_spec.rb +19 -0
- data/spec/spec_helper.rb +34 -25
- data/spec/support/aruba.rb +10 -0
- metadata +206 -80
- data/.travis.yml +0 -11
- data/LICENSE +0 -22
- data/bin/laser-cutter +0 -19
- data/lib/laser-cutter/aggregator.rb +0 -57
- data/lib/laser-cutter/cli/opt_parser.rb +0 -131
- data/lib/laser-cutter/cli/serializer.rb +0 -51
- data/lib/laser-cutter/configuration.rb +0 -83
- data/lib/laser-cutter/geometry.rb +0 -11
- data/lib/laser-cutter/notching/base.rb +0 -13
- data/lib/laser-cutter/notching.rb +0 -10
- data/lib/laser-cutter/page_manager.rb +0 -43
- data/lib/laser-cutter/renderer/box_renderer.rb +0 -36
- data/lib/laser-cutter/renderer/layout_renderer.rb +0 -59
- data/lib/laser-cutter/renderer/line_renderer.rb +0 -17
- data/lib/laser-cutter/renderer/meta_renderer.rb +0 -77
- data/lib/laser-cutter/renderer.rb +0 -14
- data/lib/laser-cutter/version.rb +0 -5
- data/spec/aggregator_spec.rb +0 -65
- data/spec/box_spec.rb +0 -40
- data/spec/configuration_spec.rb +0 -81
- data/spec/edge_spec.rb +0 -43
- data/spec/renderer_spec.rb +0 -72
- /data/{doc → docs/images}/comparison.jpg +0 -0
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
require 'json'
|
|
2
|
-
require 'laser-cutter'
|
|
3
|
-
|
|
4
|
-
module Laser
|
|
5
|
-
module Cutter
|
|
6
|
-
module CLI
|
|
7
|
-
class Serializer
|
|
8
|
-
attr_accessor :options
|
|
9
|
-
def initialize(options = {})
|
|
10
|
-
self.options = options
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def deserialize
|
|
14
|
-
string = if options.read_file.eql?('-')
|
|
15
|
-
$stdin.read
|
|
16
|
-
elsif File.exist?(options.read_file)
|
|
17
|
-
File.read(options.read_file)
|
|
18
|
-
end
|
|
19
|
-
if string
|
|
20
|
-
options.replace(JSON.load(string))
|
|
21
|
-
end
|
|
22
|
-
rescue Exception => e
|
|
23
|
-
STDERR.puts "Error reading options from file #{options.read_file}, #{e.message}".red
|
|
24
|
-
if options.verbose
|
|
25
|
-
STDERR.puts e.backtrace.join("\n").red
|
|
26
|
-
end
|
|
27
|
-
exit 1
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def serialize
|
|
31
|
-
output = if options.write_file.eql?('-')
|
|
32
|
-
$stdout
|
|
33
|
-
elsif options.write_file
|
|
34
|
-
File.open(options.write_file, 'w')
|
|
35
|
-
else
|
|
36
|
-
nil
|
|
37
|
-
end
|
|
38
|
-
output.puts(JSON.pretty_generate(options))
|
|
39
|
-
output.close if output != $stdout
|
|
40
|
-
rescue Exception => e
|
|
41
|
-
STDERR.puts "Error writing options to file #{options.write_file}, #{e.message}".red
|
|
42
|
-
if options.verbose
|
|
43
|
-
STDERR.puts e.backtrace.join("\n").red
|
|
44
|
-
end
|
|
45
|
-
exit 1
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
require 'hashie/mash'
|
|
2
|
-
require 'prawn/measurement_extensions'
|
|
3
|
-
require 'pdf/core/page_geometry'
|
|
4
|
-
|
|
5
|
-
module Laser
|
|
6
|
-
module Cutter
|
|
7
|
-
class MissingOption < RuntimeError; end
|
|
8
|
-
class ZeroValueNotAllowed < MissingOption; end
|
|
9
|
-
|
|
10
|
-
class UnitsConverter
|
|
11
|
-
def self.mm2in value
|
|
12
|
-
0.039370079 * value
|
|
13
|
-
end
|
|
14
|
-
def self.in2mm value
|
|
15
|
-
25.4 * value
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
class Configuration < Hashie::Mash
|
|
20
|
-
DEFAULTS = {
|
|
21
|
-
units: 'in',
|
|
22
|
-
page_layout: 'portrait',
|
|
23
|
-
metadata: true
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
UNIT_SPECIFIC_DEFAULTS = {
|
|
27
|
-
'in' => {
|
|
28
|
-
kerf: 0.0024, # smallest kerf for thin material, usually it's more than that.
|
|
29
|
-
margin: 0.125,
|
|
30
|
-
padding: 0.1,
|
|
31
|
-
stroke: 0.001,
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
UNIT_SPECIFIC_DEFAULTS['mm'] = UNIT_SPECIFIC_DEFAULTS['in'].map{|k, v| [k, UnitsConverter.in2mm(v)] }.to_h
|
|
36
|
-
|
|
37
|
-
SIZE_REGEXP = /[\d\.]+x[\d\.]+x[\d\.]+\/[\d\.]+(\/[\d\.]+)?/
|
|
38
|
-
|
|
39
|
-
FLOATS = %w(width height depth thickness notch margin padding stroke kerf)
|
|
40
|
-
NON_ZERO = %w(width height depth thickness stroke)
|
|
41
|
-
REQUIRED = %w(width height depth thickness notch file)
|
|
42
|
-
|
|
43
|
-
def initialize(options = {})
|
|
44
|
-
options.delete_if { |k, v| v.nil? }
|
|
45
|
-
if options['units'] && !UNIT_SPECIFIC_DEFAULTS.keys.include?(options['units'])
|
|
46
|
-
options.delete('units')
|
|
47
|
-
end
|
|
48
|
-
self.merge!(DEFAULTS)
|
|
49
|
-
self.merge!(options)
|
|
50
|
-
if self['size'] && self['size'] =~ SIZE_REGEXP
|
|
51
|
-
dim, self['thickness'], self['notch'] = self['size'].split('/')
|
|
52
|
-
self['width'], self['height'], self['depth'] = dim.split('x')
|
|
53
|
-
delete('size')
|
|
54
|
-
end
|
|
55
|
-
FLOATS.each do |k|
|
|
56
|
-
self[k] = self[k].to_f if (self[k] && self[k].is_a?(String))
|
|
57
|
-
end
|
|
58
|
-
self.merge!(UNIT_SPECIFIC_DEFAULTS[self['units']].merge(self))
|
|
59
|
-
self['notch'] = (self['thickness'] * 3.0).round(5) if self['thickness'] && self['notch'].nil?
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def validate!
|
|
63
|
-
missing = []
|
|
64
|
-
REQUIRED.each { |k| missing << k if self[k].nil? }
|
|
65
|
-
raise MissingOption.new("#{missing.join(', ')} #{missing.size > 1 ? 'are' : 'is'} required, but missing.") unless missing.empty?
|
|
66
|
-
|
|
67
|
-
zeros = []
|
|
68
|
-
NON_ZERO.each { |k| zeros << k if self[k] == 0 }
|
|
69
|
-
raise ZeroValueNotAllowed.new("#{zeros.join(', ')} #{zeros.size > 1 ? 'are' : 'is'} required, but is zero.") unless zeros.empty?
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def change_units(new_units)
|
|
73
|
-
return if (self.units.eql?(new_units) || !UNIT_SPECIFIC_DEFAULTS.keys.include?(new_units))
|
|
74
|
-
k = (self.units == 'in') ? UnitsConverter.in2mm(1.0) : UnitsConverter.mm2in(1.0)
|
|
75
|
-
FLOATS.each do |field|
|
|
76
|
-
next if self.send(field.to_sym).nil?
|
|
77
|
-
self.send("#{field}=".to_sym, (self.send(field.to_sym) * k).round(5))
|
|
78
|
-
end
|
|
79
|
-
self.units = new_units
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
require 'hashie/mash'
|
|
2
|
-
require 'prawn/measurement_extensions'
|
|
3
|
-
require 'pdf/core/page_geometry'
|
|
4
|
-
|
|
5
|
-
class Laser::Cutter::PageManager < Struct.new(:units)
|
|
6
|
-
SIZES = PDF::Core::PageGeometry::SIZES.clone.freeze
|
|
7
|
-
|
|
8
|
-
def all_page_sizes
|
|
9
|
-
output = ""
|
|
10
|
-
page_size_values.each do |k|
|
|
11
|
-
output << sprintf("\t%10s:\t%6.1f x %6.1f\n", *k)
|
|
12
|
-
end
|
|
13
|
-
output
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# if from_units is nil, we expect it to be in dots per inch (default
|
|
17
|
-
# measurements for Prawn
|
|
18
|
-
def value_from_units value, from_units = nil
|
|
19
|
-
multiplier = if from_units.nil?
|
|
20
|
-
if units.eql?('in')
|
|
21
|
-
1.0 / 72.0 # PDF units per inch
|
|
22
|
-
else
|
|
23
|
-
25.4 * 1.0 / 72.0
|
|
24
|
-
end
|
|
25
|
-
elsif self.units.eql?(from_units)
|
|
26
|
-
1.0
|
|
27
|
-
elsif self.units.eql?('in') && from_units.eql?('mm')
|
|
28
|
-
(1.0 / 25.4)
|
|
29
|
-
else
|
|
30
|
-
25.4
|
|
31
|
-
end
|
|
32
|
-
value.to_f * multiplier
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def page_size_values
|
|
36
|
-
h = SIZES
|
|
37
|
-
array = []
|
|
38
|
-
h.keys.sort.each do |k|
|
|
39
|
-
array << [k, value_from_units(h[k][0].to_f), value_from_units(h[k][1].to_f)]
|
|
40
|
-
end
|
|
41
|
-
array
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
require 'json'
|
|
3
|
-
module Laser
|
|
4
|
-
module Cutter
|
|
5
|
-
module Renderer
|
|
6
|
-
class BoxRenderer < Base
|
|
7
|
-
alias_method :box, :subject
|
|
8
|
-
|
|
9
|
-
def initialize(config)
|
|
10
|
-
super(config)
|
|
11
|
-
self.subject = Laser::Cutter::Box.new(config)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def ensure_space_for(rect)
|
|
15
|
-
coords = [ rect.p2.x, rect.p2.y ].map{|a| page_manager.value_from_units(a)}
|
|
16
|
-
box.metadata = Geometry::Point.new(coords)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def enclosure
|
|
20
|
-
box.enclosure
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def render pdf = nil
|
|
24
|
-
renderer = self
|
|
25
|
-
pdf.instance_eval do
|
|
26
|
-
self.line_width = renderer.config.stroke.send(renderer.config.units.to_sym)
|
|
27
|
-
stroke_color renderer.config[:color] || BLACK
|
|
28
|
-
renderer.box.generate_notches.each do |notch|
|
|
29
|
-
LineRenderer.new(renderer.config, notch).render(self)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
require 'json'
|
|
3
|
-
module Laser
|
|
4
|
-
module Cutter
|
|
5
|
-
module Renderer
|
|
6
|
-
class LayoutRenderer < Base
|
|
7
|
-
def initialize(config)
|
|
8
|
-
self.config = config
|
|
9
|
-
super(config)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def render
|
|
13
|
-
renderer = self
|
|
14
|
-
renderers = []
|
|
15
|
-
|
|
16
|
-
box_renderer = BoxRenderer.new(config)
|
|
17
|
-
renderers << box_renderer
|
|
18
|
-
|
|
19
|
-
if config.metadata
|
|
20
|
-
meta_renderer = MetaRenderer.new(config)
|
|
21
|
-
renderers << meta_renderer
|
|
22
|
-
box_renderer.ensure_space_for(meta_renderer.enclosure)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
if config.debug
|
|
26
|
-
unkerfed_config = Laser::Cutter::Configuration.new(config.to_hash)
|
|
27
|
-
unkerfed_config.merge!(kerf: 0.0, color: 'DD2211')
|
|
28
|
-
unkerfed_box_renderer = BoxRenderer.new(unkerfed_config)
|
|
29
|
-
unkerfed_box_renderer.ensure_space_for(meta_renderer.enclosure) if meta_renderer
|
|
30
|
-
renderers << unkerfed_box_renderer
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
margin = config.margin.to_f.send(config.units.to_sym)
|
|
34
|
-
page_size = config.page_size || calculate_image_boundary(box_renderer, margin)
|
|
35
|
-
|
|
36
|
-
pdf = Prawn::Document.new(:margin => margin,
|
|
37
|
-
:page_size => page_size,
|
|
38
|
-
:page_layout => self.config.page_layout.to_sym)
|
|
39
|
-
|
|
40
|
-
pdf.instance_eval do
|
|
41
|
-
renderers.each {|r| r.render(self) }
|
|
42
|
-
render_file(renderer.config.file)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
if config.verbose
|
|
46
|
-
puts "PDF saved to #{config.file}."
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def calculate_image_boundary(box_renderer, margin)
|
|
51
|
-
box_renderer.enclosure.to_a[1].map do |c|
|
|
52
|
-
c.send(config.units.to_sym) + 2 * margin
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
module Laser
|
|
2
|
-
module Cutter
|
|
3
|
-
module Renderer
|
|
4
|
-
class LineRenderer < Base
|
|
5
|
-
alias_method :line, :subject
|
|
6
|
-
def render pdf = nil
|
|
7
|
-
pdf.stroke { pdf.line [line.p1.x, line.p1.y].map{ |p| p.send(units) },
|
|
8
|
-
[line.p2.x, line.p2.y].map{ |p| p.send(units) }}
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def enclosure
|
|
12
|
-
self.line
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
class Laser::Cutter::Renderer::MetaRenderer < Laser::Cutter::Renderer::Base
|
|
3
|
-
|
|
4
|
-
META_KEYS = %w(units width height depth thickness notch kerf stroke padding margin page_size page_layout)
|
|
5
|
-
|
|
6
|
-
def initialize(config = {})
|
|
7
|
-
self.config = config
|
|
8
|
-
self.enclosure = Laser::Cutter::Geometry::Rect.create(Laser::Cutter::Geometry::Point[1, 1], 140, 150)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def render pdf = nil
|
|
12
|
-
banner = <<-EOF
|
|
13
|
-
|
|
14
|
-
Made with Laser Cutter Ruby Gem (v#{Laser::Cutter::VERSION})
|
|
15
|
-
Credits to Prawn for ruby PDF generation,
|
|
16
|
-
and BoxMaker for inspiration.
|
|
17
|
-
|
|
18
|
-
Online: http://makeabox.io/
|
|
19
|
-
Source: https://github.com/kigster/laser-cutter
|
|
20
|
-
EOF
|
|
21
|
-
|
|
22
|
-
meta_color = BLUE
|
|
23
|
-
meta_top_height = 55
|
|
24
|
-
|
|
25
|
-
metadata = config.to_hash
|
|
26
|
-
metadata['page_size'] ||= 'custom'
|
|
27
|
-
metadata.delete('page_layout') if metadata['page_size'].eql?('custom')
|
|
28
|
-
|
|
29
|
-
meta_fields = META_KEYS.find_all{|k| metadata[k]}.join(": \n") + ": \n"
|
|
30
|
-
meta_values = META_KEYS.find_all{|k| metadata[k]}.map{|k| metadata[k] }.join("\n")
|
|
31
|
-
|
|
32
|
-
rect = self.enclosure
|
|
33
|
-
|
|
34
|
-
pdf.instance_eval do
|
|
35
|
-
self.line_width = 0.2.mm
|
|
36
|
-
float do
|
|
37
|
-
bounding_box([rect.p1.x, rect.h + rect.p1.y], :width => rect.w, :height => rect.h) do
|
|
38
|
-
stroke_color meta_color
|
|
39
|
-
stroke_bounds
|
|
40
|
-
|
|
41
|
-
# Print banner
|
|
42
|
-
indent 10 do
|
|
43
|
-
font('Helvetica', :size => 6) do
|
|
44
|
-
text banner, :color => meta_color
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# print values of the config, in two parts – keys right aligned first, values left aligned second.
|
|
49
|
-
float do
|
|
50
|
-
bounding_box([0, rect.h - meta_top_height],
|
|
51
|
-
:width => rect.w,
|
|
52
|
-
:height => rect.h - meta_top_height) do
|
|
53
|
-
float do
|
|
54
|
-
bounding_box([0, rect.h - meta_top_height], :width => 70, :height => rect.h - meta_top_height) do
|
|
55
|
-
indent 10 do
|
|
56
|
-
font('Helvetica', :size => 7) do
|
|
57
|
-
text meta_fields, :color => meta_color, align: :right
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
float do
|
|
63
|
-
bounding_box([60, rect.h - meta_top_height], :width => 70, :height => rect.h - meta_top_height) do
|
|
64
|
-
indent 10 do
|
|
65
|
-
font('Helvetica', :size => 7) do
|
|
66
|
-
text meta_values, :color => meta_color
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module Laser
|
|
2
|
-
module Cutter
|
|
3
|
-
module Renderer
|
|
4
|
-
|
|
5
|
-
end
|
|
6
|
-
end
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
require 'laser-cutter/renderer/base'
|
|
10
|
-
require 'laser-cutter/renderer/line_renderer'
|
|
11
|
-
require 'laser-cutter/renderer/rect_renderer'
|
|
12
|
-
require 'laser-cutter/renderer/box_renderer'
|
|
13
|
-
require 'laser-cutter/renderer/meta_renderer'
|
|
14
|
-
require 'laser-cutter/renderer/layout_renderer'
|
data/lib/laser-cutter/version.rb
DELETED
data/spec/aggregator_spec.rb
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
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
|
data/spec/box_spec.rb
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
require_relative 'spec_helper'
|
|
2
|
-
|
|
3
|
-
module Laser
|
|
4
|
-
module Cutter
|
|
5
|
-
describe Box do
|
|
6
|
-
let(:config) { {'width' => 50, 'height' => 60, 'depth' => 70, 'margin' => 5, 'padding' => 3, 'units' => 'mm'} }
|
|
7
|
-
let(:box1) { Box.new(config.merge('thickness' => 6, 'notch' => 10)) }
|
|
8
|
-
let(:box2) { Box.new(config.merge('thickness' => 6, )) }
|
|
9
|
-
|
|
10
|
-
context '#initialize' do
|
|
11
|
-
it 'should initialize with passed in parameters' do
|
|
12
|
-
expect(box1.w).to eq(50.0)
|
|
13
|
-
expect(box1.thickness).to eq(6.0)
|
|
14
|
-
expect(box1.notch_width).to eq(10.0)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
it 'should initialize with default notch' do
|
|
18
|
-
expect(box2.notch_width).to eq(70.0 / 5.0)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
context '#notches' do
|
|
23
|
-
before do
|
|
24
|
-
box1.generate_notches
|
|
25
|
-
box2.generate_notches
|
|
26
|
-
end
|
|
27
|
-
it 'should generate notches' do
|
|
28
|
-
expect(box1.notches).to_not be_nil
|
|
29
|
-
expect(box1.notches.size).to eql(368)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
it 'should properly calculate enclosure' do
|
|
33
|
-
expect(box1.enclosure.to_a.flatten.map(&:round)).to eql([0,0, 232, 317])
|
|
34
|
-
expect(box2.enclosure.to_a.flatten.map(&:round)).to eql([0,0, 232, 317])
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
data/spec/configuration_spec.rb
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
require_relative 'spec_helper'
|
|
2
|
-
|
|
3
|
-
module Laser
|
|
4
|
-
module Cutter
|
|
5
|
-
describe Configuration do
|
|
6
|
-
let(:config) { Laser::Cutter::Configuration.new(opts) }
|
|
7
|
-
|
|
8
|
-
context 'option parsing' do
|
|
9
|
-
let(:opts) { {"size" => "2x3x2/0.125/0.5", "inches" => true} }
|
|
10
|
-
it 'should be able to parse size options' do
|
|
11
|
-
expect(config.width).to eql(2.0)
|
|
12
|
-
expect(config.height).to eql(3.0)
|
|
13
|
-
expect(config.depth).to eql(2.0)
|
|
14
|
-
expect(config.thickness).to eql(0.125)
|
|
15
|
-
expect(config.notch).to eql(0.5)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
context '#validate' do
|
|
19
|
-
context 'missing options' do
|
|
20
|
-
let(:opts) { {"height" => "23"} }
|
|
21
|
-
it 'should be able to validate missing options' do
|
|
22
|
-
expect(config.height).to eql(23.0)
|
|
23
|
-
expect { config.validate! }.to raise_error(Laser::Cutter::MissingOption)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
context 'zero options' do
|
|
27
|
-
let(:opts) { {"size" => "2.0x0.0x2/0.125/0.5", 'file' => '/tmp/a'} }
|
|
28
|
-
it 'should be able to validate missing options' do
|
|
29
|
-
expect(config.height).to eql(0.0)
|
|
30
|
-
expect { config.validate! }.to raise_error(Laser::Cutter::ZeroValueNotAllowed)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
context 'default values' do
|
|
36
|
-
let(:opts) { {"size" => "2.0x1.0x2/0.125", 'file' => '/tmp/a'} }
|
|
37
|
-
it 'should correctly default notch based on thickness' do
|
|
38
|
-
config.validate!
|
|
39
|
-
expect(config.thickness).to eql(0.125)
|
|
40
|
-
expect(config.notch).to eql(config.thickness * 3.0)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
context 'when invalid units are provided' do
|
|
45
|
-
let(:opts) { {"size" => "2x3x2/0.125/0.5", "units" => 'xx'} }
|
|
46
|
-
it 'should default to inches' do
|
|
47
|
-
expect(config.units).to eql('in')
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
context 'when converting between units' do
|
|
52
|
-
context 'all config values' do
|
|
53
|
-
context "to mm" do
|
|
54
|
-
let(:opts) { {'size' => "2.0x3x2/0.125/0.5", 'padding' => '4.2', "units" => 'in'} }
|
|
55
|
-
it 'should be correct' do
|
|
56
|
-
expect(config.width).to eql(2.0)
|
|
57
|
-
config.change_units('in')
|
|
58
|
-
expect(config.width).to eql(2.0)
|
|
59
|
-
config.change_units('mm')
|
|
60
|
-
expect(config.width).to eql(50.8)
|
|
61
|
-
expect(config.padding).to eql(106.68)
|
|
62
|
-
expect(config.units).to eql('mm')
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
context 'to inches' do
|
|
66
|
-
let(:opts) { {'size' => "20.0x30.0x40.0/5/5", 'margin' => '10.0', "units" => 'mm'} }
|
|
67
|
-
it 'should be correct' do
|
|
68
|
-
expect(config.width).to eql(20.0)
|
|
69
|
-
config.change_units('mm')
|
|
70
|
-
expect(config.width).to eql(20.0)
|
|
71
|
-
config.change_units('in')
|
|
72
|
-
expect(config.width).to be_within(0.00001).of(0.787401575)
|
|
73
|
-
expect(config.margin).to be_within(0.00001).of(0.393700787)
|
|
74
|
-
expect(config.units).to eql('in')
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
data/spec/edge_spec.rb
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
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
|