laser-cutter 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cbd3a3886b9dfc9351a9843f874b93194f24a96f
4
+ data.tar.gz: 9409ff279e98f2f723595ca983dd1aa2f6b345d4
5
+ SHA512:
6
+ metadata.gz: 2619eb894a05c15591d27115b8b6bbc0df9d969727de76dd35f4d91927a1162aeee91c5339f3b27e2b284a6f827a308166de4fa1177d9cdc37ae9d9fc47d8275
7
+ data.tar.gz: 32f24e47a6434d75b71fdc935a610fe0c0caac431f421e0941ebcc3594c21052391601e272d26b0ebbdbd50d2273fd49d07cc40e2cff0385177e56ec85f499b7
@@ -0,0 +1,27 @@
1
+ **.pdf
2
+ **.DS_Store
3
+ **/*.swp
4
+ *.gem
5
+ *.rbc
6
+ .bundle
7
+ .config
8
+ .yardoc
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
21
+ *.bundle
22
+ *.so
23
+ *.o
24
+ *.a
25
+ mkmf.log
26
+ .idea/
27
+ **output.pdf
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1 @@
1
+ 2.1.2
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ script: "bundle exec rspec"
5
+ notifications:
6
+ email:
7
+ recipients:
8
+ - kigster@gmail.com
9
+ on_success: never
10
+ on_failure: always
11
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in laser-cutter-cutter.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Konstantin Gredeskoul
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Konstantin Gredeskoul
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,67 @@
1
+ [![Build status](https://secure.travis-ci.org/kigster/laser-cutter.png)](http://travis-ci.org/kigster/laser-cutter)
2
+ [![Code Climate](https://codeclimate.com/github/kigster/laser-cutter.png)](https://codeclimate.com/github/kigster/laser-cutter)
3
+
4
+ LaserCutter
5
+ ============
6
+
7
+ Similar to boxmaker, this ruby gem generates PDFs that can be used as a
8
+ basis for cutting boxes on a typical laser cutter.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'laser-cutter'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install laser-cutter
23
+
24
+ ## Usage
25
+
26
+ ```bash
27
+ > gem install bundler
28
+ > git clone https://github.com/kigster/laser-cutter.git
29
+ > cd laser-cutter && bundle
30
+ > bundle exec bin/laser-cutter --help
31
+ Usage: laser-cutter [options]'
32
+
33
+ eg: laser-cutter --units in -s 2x3x2/0.125/0.5 -o box.pdf'
34
+ laser-cutter -w 30 -h 20 -d 10 -t 4.3 -n 10 -o box.pdf
35
+
36
+
37
+ Specific options:
38
+ -s, --size WxHxD/T/N Combined internal dimensions: W = width, H = height,
39
+ D = depth, T = thickness, N = notch length
40
+
41
+ -w, --width WIDTH Internal width of the box
42
+ -h, --height HEIGHT Internal height of the box
43
+ -d, --depth DEPTH Internal depth of the box
44
+ -t, --thickness THICKNESS Thickness of the box material
45
+ -n, --notch NOTCH Preferred notch length (used only as a guide)
46
+ -o, --file FILE Output filename of the PDF
47
+ -u, --units UNITS Either 'mm' (default) or 'in'
48
+ -m, --margin MARGIN Margins from the edge of the document
49
+ -p, --padding PADDING Space between the boxes on the page
50
+ -P, --page_size LETTER Page size, see docs on Prawn for more options
51
+ -L, --page_layout portrait Page layout, other option is 'landscape'
52
+ -S, --stroke WIDTH Numeric stroke width of the line
53
+ -O, --open Open generated file with system viewer before exiting
54
+ -v, --[no-]verbose Run verbosely
55
+
56
+ Common options:
57
+ --help Show this message
58
+ --version Show version
59
+ ```
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it ( https://github.com/[my-github-username]/laser-cutter/fork )
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env ruby
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3
+ require 'rubygems'
4
+ require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
5
+ require 'laser-cutter'
6
+ require 'laser-cutter/renderer'
7
+ require 'laser-cutter/configuration'
8
+ require 'optparse'
9
+ require 'colored'
10
+ require 'json'
11
+
12
+ module Laser
13
+ module Cutter
14
+ class OptParse
15
+ def self.parse(args)
16
+ banner_text = <<-EOF
17
+ Usage: laser-cutter [options]'
18
+
19
+ eg: laser-cutter --units in -s 2x3x2/0.125/0.5 -o box.pdf'
20
+ laser-cutter -w 30 -h 20 -d 10 -t 4.3 -n 10 -o box.pdf
21
+ EOF
22
+
23
+ options = OpenStruct.new
24
+ options.verbose = false
25
+
26
+ opt_parser = OptionParser.new do |opts|
27
+ opts.banner = banner_text.cyan
28
+ opts.separator ""
29
+ opts.separator "Specific options:"
30
+
31
+ opts.on("-s", "--size WxHxD/T/N",
32
+ "Combined internal dimensions: W = width, H = height,\n#{" " * 37}D = depth, T = thickness, N = notch length\n\n") do |size|
33
+ options.size = size
34
+ end
35
+
36
+ opts.on("-w", "--width WIDTH", "Internal width of the box") { |value| options.width = value }
37
+ opts.on("-h", "--height HEIGHT", "Internal height of the box") { |value| options.height = value }
38
+ opts.on("-d", "--depth DEPTH", "Internal depth of the box") { |value| options.depth= value }
39
+
40
+ opts.on("-t", "--thickness THICKNESS", "Thickness of the box material") { |value| options.thickness = value }
41
+ opts.on("-n", "--notch NOTCH", "Preferred notch length (used only as a guide)") { |value| options.notch = value }
42
+ opts.on("-o", "--file FILE", "Output filename of the PDF") { |value| options.file = value }
43
+
44
+ opts.on("-u", "--units UNITS", "Either 'mm' (default) or 'in'") { |value| options.units = value }
45
+ opts.on("-m", "--margin MARGIN", "Margins from the edge of the document") { |value| options.margin = value }
46
+ opts.on("-p", "--padding PADDING", "Space between the boxes on the page") { |value| options.padding = value }
47
+
48
+ opts.on("-P", "--page_size LETTER", "Page size, see docs on Prawn for more options") { |value| options.page_size = value }
49
+ opts.on("-L", "--page_layout portrait", "Page layout, other option is 'landscape' ") { |value| options.page_layout = value }
50
+ opts.on("-S", "--stroke WIDTH", "Numeric stroke width of the line") { |value| options.stroke = value }
51
+
52
+ opts.on("-O", "--open", "Open generated file with system viewer before exiting") { |v| options.open = v }
53
+ opts.on("-v", "--[no-]verbose", "Run verbosely") { |v| options.verbose = v }
54
+
55
+ opts.separator ""
56
+ opts.separator "Common options:"
57
+
58
+ opts.on_tail("--help", "Show this message") do
59
+ puts opts
60
+ exit
61
+ end
62
+ opts.on_tail("--version", "Show version") do
63
+ puts Laser::Cutter::VERSION
64
+ exit
65
+ end
66
+ end
67
+ opt_parser.parse!(args)
68
+ options
69
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
70
+ puts opt_parser.help
71
+ puts
72
+ puts "#{e.message}".red
73
+ exit 1
74
+ end # end parse()
75
+ end
76
+ end
77
+ end # class OptParse
78
+
79
+ options = Laser::Cutter::OptParse.parse(ARGV)
80
+ config = Laser::Cutter::Configuration.new(options.to_h)
81
+
82
+ if options.verbose
83
+ puts "Starting with the following configuration:"
84
+ puts JSON.pretty_generate(config.to_h).green
85
+ end
86
+
87
+ begin
88
+ config.validate!
89
+ rescue Exception => e
90
+ STDERR.puts "\nSorry, #{e}".red
91
+ STDERR.puts "Try --help for more info..."
92
+ exit 1
93
+ end
94
+
95
+ begin
96
+ box = Laser::Cutter::Box.new(config)
97
+ Laser::Cutter::Renderer::BoxRenderer.new(box, config).render
98
+ if config.open
99
+ `open #{config.file}`
100
+ end
101
+ rescue Exception => e
102
+ puts "#{e}".red
103
+ STDERR.puts "Try --help for more info...".yellow
104
+ if options.verbose
105
+ STDERR.puts "Exception: #{e.inspect}".red
106
+ STDERR.puts e.backtrace.join("\n").red
107
+ end
108
+ end
109
+
110
+
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'laser-cutter/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "laser-cutter"
8
+ spec.version = Laser::Cutter::VERSION
9
+ spec.authors = ['Konstantin Gredeskoul']
10
+ spec.email = ["kigster@gmail.com"]
11
+ spec.summary = %q{Creates box outlines for laser-cut boxes.}
12
+ spec.description = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'prawn'
22
+ spec.add_dependency 'hashie'
23
+ spec.add_dependency 'colored'
24
+
25
+ spec.add_development_dependency 'bundler', "~> 1.6"
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'rspec'
28
+
29
+ end
@@ -0,0 +1,12 @@
1
+ require 'laser-cutter/version'
2
+ require 'laser-cutter/configuration'
3
+ require 'laser-cutter/geometry'
4
+ require 'laser-cutter/box'
5
+ require 'laser-cutter/renderer'
6
+ require 'prawn'
7
+ require 'prawn/measurement_extensions'
8
+
9
+ module Laser
10
+ module Cutter
11
+ end
12
+ end
@@ -0,0 +1,136 @@
1
+ module Laser
2
+ module Cutter
3
+ class Box
4
+ # Everything is in millimeters
5
+
6
+ attr_accessor :dim, :thickness, :notch_width
7
+ attr_accessor :padding, :units
8
+
9
+ attr_accessor :front, :back, :top, :bottom, :left, :right
10
+ attr_accessor :faces, :bounds, :conf
11
+
12
+ def initialize(config = {})
13
+ self.dim = Geometry::Dimensions.new(config['width'], config['height'], config['depth'])
14
+ self.thickness = config['thickness']
15
+
16
+ self.notch_width = config['notch'] || (1.0 * self.longest / 5.0)
17
+ self.padding = config['padding']
18
+ self.units = config['units']
19
+
20
+ create_faces! # generates dimensions for each side
21
+ self.faces = [top, front, bottom, back, left, right]
22
+
23
+ position_faces!
24
+ self.conf = {
25
+ valign: [ :out, :out, :out, :out, :in, :in],
26
+ halign: [ :in, :out, :in, :out, :in, :in],
27
+ corners:[ :no, :yes, :no, :yes, :no, :no]
28
+ }
29
+
30
+ self
31
+ end
32
+
33
+ # Returns an flattened array of lines representing notched
34
+ # rectangle.
35
+ def notches
36
+ generate_bounding_boxes!
37
+
38
+ notches = []
39
+
40
+ faces.each_with_index do |face, face_index|
41
+ bound = bounds[face_index]
42
+ bound.sides.each_with_index do |bounding_side, side_index |
43
+ key = side_index.odd? ? :valign : :halign
44
+ path = Geometry::PathGenerator.new(:notch_width => notch_width,
45
+ :center_out => (self.conf[key][face_index] == :out) ,
46
+ :fill_corners => (self.conf[:corners][face_index] == :yes && side_index.odd?),
47
+ :thickness => thickness
48
+ ).path(Geometry::Edge.new(bounding_side, face.sides[side_index], self.notch_width))
49
+ notches << path.create_lines
50
+ end
51
+ end
52
+
53
+ Geometry::PathGenerator.deduplicate(notches.flatten.sort)
54
+ end
55
+
56
+ def w; dim.w; end
57
+ def h; dim.h; end
58
+ def d; dim.d; end
59
+
60
+ def longest
61
+ [w, h, d].max()
62
+ end
63
+
64
+ def to_s
65
+ "Box:\nH:#{dim.h} W:#{dim.w} D:#{dim.d}\nThickness:#{thickness}, Notch:#{notch_width}"
66
+ end
67
+
68
+ private
69
+
70
+ def generate_bounding_boxes!
71
+ self.bounds = []
72
+ self.bounds = faces.map do |face|
73
+ b = face.clone
74
+ b.move_to(b.position.move_by(-thickness, -thickness))
75
+ b.p2 = b.p2.move_by(2 * thickness, 2 * thickness)
76
+ b.relocate!
77
+ b
78
+ end
79
+ end
80
+
81
+ #___________________________________________________________________
82
+ #
83
+ # +-----------------+
84
+ # | |
85
+ # | back: W x H |
86
+ # | |
87
+ # +-----------------+
88
+ # +-----------------+
89
+ # | bottom: W x D |
90
+ # +-----------------+
91
+ # +--------+ +-----------------+ +--------+
92
+ # | | | | | |
93
+ # | left | | front: W x H | | right |
94
+ # | D x H | | | | D x H |
95
+ # +--------+ +-----------------+ +--------+
96
+ # +-----------------+
97
+ # | top : W x D |
98
+ # +-----------------+
99
+ #
100
+ # 0,0
101
+ #___________________________________________________________________
102
+
103
+ def position_faces!
104
+ offset = padding + d + 3 * thickness
105
+ left.x = top.y = thickness
106
+ [bottom, front, top, back].each do |s|
107
+ s.x = offset
108
+ end
109
+
110
+ right.x = 2 * padding + w + d + 5 * thickness
111
+ [left, front, right].each do |s|
112
+ s.y = offset
113
+ end
114
+
115
+ bottom.y = d + 2 * padding + h + 5 * thickness
116
+ back.y = 3 * padding + 2 * d + h + 7*thickness
117
+
118
+ faces.each(&:relocate!)
119
+
120
+ end
121
+
122
+ def create_faces!
123
+ zero = Geometry::Point.new(0, 0)
124
+ self.front = Geometry::Rect.create(zero, dim.w, dim.h, "front")
125
+ self.back = Geometry::Rect.create(zero, dim.w, dim.h, "back")
126
+
127
+ self.top = Geometry::Rect.create(zero, dim.w, dim.d, "top")
128
+ self.bottom = Geometry::Rect.create(zero, dim.w, dim.d, "bottom")
129
+
130
+ self.left = Geometry::Rect.create(zero, dim.d, dim.h, "left")
131
+ self.right = Geometry::Rect.create(zero, dim.d, dim.h, "right")
132
+ end
133
+
134
+ end
135
+ end
136
+ end