laser-cutter 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -1
- data/Gemfile +2 -0
- data/README.md +101 -75
- data/bin/laser-cutter +58 -73
- data/doc/comparison.jpg +0 -0
- data/lib/laser-cutter/box.rb +29 -19
- data/lib/laser-cutter/configuration.rb +20 -2
- data/lib/laser-cutter/renderer/box_renderer.rb +4 -1
- data/lib/laser-cutter/version.rb +1 -1
- data/spec/configuration_spec.rb +21 -5
- data/spec/renderer_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -1
- metadata +3 -5
- data/doc/boxmaker.jpg +0 -0
- data/doc/laser-cutter.jpg +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df5fc0b8025663e5d008e970a56fa3fe49f5a275
|
4
|
+
data.tar.gz: c9a4201c05edf34506255706906d979249047b92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88b3fbacb5fbc6f0049f7799a7721f9b22ccff4ddfe313496a051b931ce5b9184efd273b68981234038f3d5967e1b073d0d8bbe6aa88a35a925bd4f2ab7b343f
|
7
|
+
data.tar.gz: 4c816f055a6f32109305ece7f3b7e2cb98d69d71b70d2e8cc6f7c566edd475c3cb6cb8a53de4a1598187f0b88ba170026acecd1a09329b0d8f0f4567e2581c08
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,68 +1,28 @@
|
|
1
1
|
[![Gem Version](https://badge.fury.io/rb/laser-cutter.svg)](http://badge.fury.io/rb/laser-cutter)
|
2
2
|
[![Build status](https://secure.travis-ci.org/kigster/laser-cutter.png)](http://travis-ci.org/kigster/laser-cutter)
|
3
3
|
[![Code Climate](https://codeclimate.com/github/kigster/laser-cutter.png)](https://codeclimate.com/github/kigster/laser-cutter)
|
4
|
+
[![Test Coverage](https://codeclimate.com/github/kigster/laser-cutter/badges/coverage.svg)](https://codeclimate.com/github/kigster/laser-cutter)
|
4
5
|
|
5
6
|
## LaserCutter
|
6
7
|
|
7
8
|
Similar to [BoxMaker](https://github.com/rahulbot/boxmaker/) (which is written in Java a long time ago),
|
8
|
-
this ruby gem generates PDFs that can be used as a basis for
|
9
|
+
this ruby gem generates PDFs that can be used as a basis for creating a "snap-in" boxes with notched
|
10
|
+
sides on a typical laser cutter, by providing dimensions, material thickness and output file name.
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
as existing test suite offers confidence around not introducing bugs or regressions.
|
12
|
+
For more detailed comparison with BoxMaker and motivation behind this project, please see the section
|
13
|
+
at the bottom of this README.
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
```laser-cutter```'s algorithm will create a _symmetric design for most panels_, but it might sacrifice
|
19
|
-
identical notch length. Depending on the box dimensions you may end up with a slightly different notch
|
20
|
-
length on each side of the box.
|
21
|
-
|
22
|
-
Having said that, one of the design goals of this ruby version is to provide a highly extensible platform,
|
23
|
-
where alternative algorithms can be implemented and supported by command line options.
|
24
|
-
|
25
|
-
```laser-cutter``` has quite a few options that allow you to set stroke width, page size,
|
26
|
-
layout, margins, padding (spacing between boxes), open the PDF file using system viewer right
|
27
|
-
after generation, and many more are coming soon.
|
28
|
-
|
29
|
-
The choice ultimately comes down to the preference and feature set, so here I show you two boxes made with
|
30
|
-
each program, so you can pick what you prefer.
|
31
|
-
|
32
|
-
#### Disclaimer
|
33
|
-
|
34
|
-
Important quick note that the author believes that BoxMaker is a greatly useful piece of software
|
35
|
-
generously open sourced by the author, and so in no way this project disputes it's viability.
|
36
|
-
In fact BoxMaker was an inspiration for this project. This project simply attempts to advance
|
37
|
-
further and provide additional solutions to this complex problem of creating 3D designs on a 2D
|
38
|
-
surface using laser cutter.
|
39
|
-
|
40
|
-
### Example Outputs
|
41
|
-
|
42
|
-
Below are two examples of boxes with identical dimensions produced with ```laser-cutter``` and ```boxmaker```:
|
43
|
-
|
44
|
-
#### BoxMaker
|
45
|
-
|
46
|
-
```bash
|
47
|
-
> java -cp BOX.jar com.rahulbotics.boxmaker.BoxMaker -i -W 2.5 -H 2 -D 1 -T 0.25 -n 0.5 -f file.pdf
|
48
|
-
```
|
49
|
-
|
50
|
-
![BoxMaker Example](doc/boxmaker.jpg).
|
15
|
+
One of the design goals of this project is to provide a highly extensible platform for creating
|
16
|
+
laser-cut designs, where alternative strategies can be added over time, and supported by various
|
17
|
+
command line options, and perhaps a light weight web application. If you are interested in
|
18
|
+
contributing to the project, please see [contributing](CONTRIBUTING.md) for more details.
|
51
19
|
|
52
|
-
|
20
|
+
```laser-cutter``` supports many flexible command line options that allow setting dimensions,
|
21
|
+
stroke width, page size, layout, margins, padding (spacing between the boxes), and many more.
|
53
22
|
|
54
|
-
|
55
|
-
> laser-cutter -u in -s 2.5x1x2/0.25/0.5 -o file.pdf
|
56
|
-
```
|
57
|
-
|
58
|
-
![LaserCutter Example](doc/laser-cutter.jpg).
|
23
|
+
## Dependencies
|
59
24
|
|
60
|
-
|
61
|
-
|
62
|
-
* Creating T-style joins, using various sized nuts and bolts (such as common #4-40 and M2 sizes)
|
63
|
-
* Creating lids and front panels that are larger than the box
|
64
|
-
* Rendering to CSS to show live preview on a web app
|
65
|
-
* Your brilliant idea can be here! Please see [contributing](CONTRIBUTING.md) for more info.
|
25
|
+
The gem depends primarily on [Prawn](http://prawnpdf.org) – a fantastic PDF generation library.
|
66
26
|
|
67
27
|
## Installation
|
68
28
|
|
@@ -81,44 +41,110 @@ Or install it yourself as:
|
|
81
41
|
## Usage
|
82
42
|
|
83
43
|
```bash
|
84
|
-
Usage: laser-cutter [options]
|
85
|
-
|
86
|
-
|
87
|
-
Examples:
|
88
|
-
1. Create a box defined in inches, and open PDF in preview right after:
|
89
|
-
|
90
|
-
laser-cutter --units in -s 3x2x2/0.125/0.5 -O -o box.pdf
|
91
|
-
|
92
|
-
2. Create a box defined in millimeters, print verbose info and set
|
93
|
-
page size to A3, and layout to landscape, and stroke width to 2mm:
|
94
|
-
|
95
|
-
laser-cutter -w70 -h20 -d50 -t4.3 -n5 -PA3 -L landscape -S 0.5 -v -O -o box.pdf
|
96
|
-
|
97
|
-
|
98
|
-
Specific options:
|
99
|
-
-s, --size WxHxD/T/N Combined internal dimensions: W = width, H = height,
|
100
|
-
D = depth, T = thickness, N = notch length
|
44
|
+
Usage: laser-cutter [options] -o filename.pdf
|
45
|
+
eg: laser-cutter -i -s 1x1.5x2/0.125/0.125 -O -o box.pdf
|
101
46
|
|
47
|
+
Specific Options:
|
102
48
|
-w, --width WIDTH Internal width of the box
|
103
49
|
-h, --height HEIGHT Internal height of the box
|
104
50
|
-d, --depth DEPTH Internal depth of the box
|
105
51
|
-t, --thickness THICKNESS Thickness of the box material
|
106
52
|
-n, --notch NOTCH Preferred notch length (used only as a guide)
|
107
|
-
|
108
|
-
-u, --units UNITS Either 'mm' (default) or 'in'
|
53
|
+
|
109
54
|
-m, --margin MARGIN Margins from the edge of the document
|
110
55
|
-p, --padding PADDING Space between the boxes on the page
|
56
|
+
-S, --stroke WIDTH Numeric stroke width of the line
|
111
57
|
-P, --page_size LETTER Page size, see --list-all-page-sizes for more info.
|
112
58
|
-L, --page_layout portrait Page layout, other option is 'landscape'
|
113
|
-
|
59
|
+
|
114
60
|
-O, --open Open generated file with system viewer before exiting
|
61
|
+
-u, --units UNITS Either 'mm' (default) or 'in'
|
62
|
+
|
115
63
|
-A, --list-all-page-sizes Print all available page sizes with dimensions and exit
|
116
64
|
-v, --[no-]verbose Run verbosely
|
117
65
|
|
118
|
-
|
66
|
+
--examples Show detailed usage examples
|
119
67
|
--help Show this message
|
120
68
|
--version Show version
|
121
|
-
|
69
|
+
|
70
|
+
Common Options:
|
71
|
+
-o, --file FILE Required output filename of the PDF
|
72
|
+
-s, --size WxHxD/T/N Combined internal dimensions: W = width, H = height,
|
73
|
+
D = depth, T = thickness, N = notch length
|
74
|
+
|
75
|
+
-i, --inches Switch measurements to inches instead of millimeters
|
76
|
+
|
77
|
+
Examples:
|
78
|
+
1. Create a box defined in inches, and open PDF in preview right after:
|
79
|
+
|
80
|
+
laser-cutter -i -s 3x2x2/0.125/0.5 -O -o box.pdf
|
81
|
+
|
82
|
+
2. Create a box defined in millimeters, print verbose info and set
|
83
|
+
page size to A3, and layout to landscape, and stroke width to 1/2mm:
|
84
|
+
|
85
|
+
laser-cutter -w70 -h20 -d50 -t4.3 -n5 -PA3 -L landscape -S0.5 -v -O -o box.pdf
|
86
|
+
|
87
|
+
3. List all possible page sizes in metric or imperial systems:
|
88
|
+
|
89
|
+
laser-cutter --list-all-page-sizes
|
90
|
+
laser-cutter --list-all-page-sizes --inches
|
91
|
+
```
|
92
|
+
|
93
|
+
## Future Features
|
94
|
+
|
95
|
+
* Extensibility with various layout strategies, notch drawing strategies, basically plug and play
|
96
|
+
model for adding new algorithms for path creation and box joining
|
97
|
+
* Support more shapes than just box
|
98
|
+
* Create T-style joins, using various standard sizes of nuts and bolts (such as common #4-40 and M2 sizes)
|
99
|
+
* Supporting lids and front panels, that are larger than the box itself and have holes for notches.
|
100
|
+
* A web-app that uses the gem and renders box live in CSS using CSS 2D to show preview.
|
101
|
+
* Your brilliant idea can be here too! Please see [contributing](CONTRIBUTING.md) for more info.
|
102
|
+
|
103
|
+
## Comparison with BoxMaker
|
104
|
+
|
105
|
+
It's important to note that the author believes that BoxMaker is a greatly useful piece of software
|
106
|
+
generously open sourced by the author, and so in no way this project disputes BoxMaker's viability.
|
107
|
+
|
108
|
+
In fact BoxMaker was an inspiration for this project. Laser-Cutter library attempts to further advance
|
109
|
+
concept of programmatically creating laser-cut boxes, provide additional tuning, options, strategies
|
110
|
+
and most importantly – extensibility.
|
111
|
+
|
112
|
+
Unlike ```BoxMaker```, this gem has a suit of automated tests (rspecs) around creating the geometry.
|
113
|
+
In addition, we welcome new feature contributions, or bug fixes from other developers, and in that
|
114
|
+
regard rspecs offer confidence that functionality still works.
|
115
|
+
|
116
|
+
BoxMaker's algorithm _tries to ensures that the same notch length is across all sides, but sacrifices
|
117
|
+
symmetry as a result_. So you may have a front panel's left and right edges be simply non symmetric.
|
118
|
+
And that might be entirely OK with you :)
|
119
|
+
|
120
|
+
```laser-cutter```'s algorithm will create a _symmetric design for most panels_, but it might sacrifice
|
121
|
+
identical notch length. Depending on the box dimensions you may end up with a slightly different notch
|
122
|
+
length on each side of the box.
|
123
|
+
|
124
|
+
The choice ultimately comes down to the preference and feature set, so here I show you two boxes made with
|
125
|
+
each program, so you can pick what you prefer.
|
126
|
+
|
127
|
+
### Example Outputs
|
128
|
+
|
129
|
+
Below are two examples of boxes with identical dimensions produced with ```laser-cutter``` and ```boxmaker```:
|
130
|
+
|
131
|
+
This is how you would make a box with Adam Phelp's fork of BoxMaker (which adds flags and a lot of
|
132
|
+
niceties):
|
133
|
+
|
134
|
+
```bash
|
135
|
+
git clone https://github.com/aphelps/boxmaker && cd boxmaker && ant
|
136
|
+
java -cp BOX.jar com.rahulbotics.boxmaker.BoxMaker \
|
137
|
+
-i -W 1 -H 2 -D 1.5 -T 0.125 -n 0.125 -f box.pdf
|
138
|
+
```
|
139
|
+
|
140
|
+
And laser-cutter:
|
141
|
+
|
142
|
+
```bash
|
143
|
+
gem install laser-cutter
|
144
|
+
laser-cutter -i -s 1x1.5x2/0.125/0.125 -O -o box.pdf
|
145
|
+
```
|
146
|
+
|
147
|
+
![LaserCutter Comparison](doc/comparison.jpg).
|
122
148
|
|
123
149
|
## Contributing
|
124
150
|
|
data/bin/laser-cutter
CHANGED
@@ -8,122 +8,107 @@ require 'hashie/mash'
|
|
8
8
|
module Laser
|
9
9
|
module Cutter
|
10
10
|
class OptParse
|
11
|
+
def self.puts_error(e)
|
12
|
+
STDERR.puts "Whoops, #{e}".red
|
13
|
+
STDERR.puts "Try --help or --examples for more info...".yellow
|
14
|
+
end
|
15
|
+
|
11
16
|
def self.parse(args)
|
12
17
|
banner_text = <<-EOF
|
13
|
-
|
14
|
-
|
18
|
+
#{('Laser-Cutter v'+ Laser::Cutter::VERSION).bold}
|
19
|
+
|
20
|
+
Usage: laser-cutter [options] -o filename.pdf
|
21
|
+
eg: laser-cutter -i -s 1x1.5x2/0.125/0.125 -O -o box.pdf
|
22
|
+
EOF
|
23
|
+
|
24
|
+
examples = <<-EOF
|
15
25
|
|
16
26
|
Examples:
|
17
27
|
1. Create a box defined in inches, and open PDF in preview right after:
|
18
28
|
|
19
|
-
laser-cutter
|
29
|
+
laser-cutter -i -s 3x2x2/0.125/0.5 -O -o box.pdf
|
20
30
|
|
21
31
|
2. Create a box defined in millimeters, print verbose info and set
|
22
|
-
page size to A3, and layout to landscape, and stroke width to 2mm:
|
32
|
+
page size to A3, and layout to landscape, and stroke width to 1/2mm:
|
23
33
|
|
24
|
-
laser-cutter -w70 -h20 -d50 -t4.3 -n5 -PA3 -L landscape -
|
25
|
-
|
34
|
+
laser-cutter -w70 -h20 -d50 -t4.3 -n5 -PA3 -L landscape -S0.5 -v -O -o box.pdf
|
35
|
+
|
36
|
+
3. List all possible page sizes in metric or imperial systems:
|
26
37
|
|
38
|
+
laser-cutter --list-all-page-sizes
|
39
|
+
laser-cutter --list-all-page-sizes --inches
|
40
|
+
EOF
|
27
41
|
options = Hashie::Mash.new
|
28
42
|
options.verbose = false
|
29
43
|
options.units = 'mm'
|
30
44
|
|
31
45
|
opt_parser = OptionParser.new do |opts|
|
32
|
-
opts.banner = banner_text.
|
33
|
-
opts.separator ""
|
34
|
-
opts.separator "Specific options:"
|
35
|
-
|
36
|
-
opts.on("-s", "--size WxHxD/T/N",
|
37
|
-
"Combined internal dimensions: W = width, H = height,\n#{" " * 37}D = depth, T = thickness, N = notch length\n\n") do |size|
|
38
|
-
options.size = size
|
39
|
-
end
|
40
|
-
|
46
|
+
opts.banner = banner_text.blue
|
47
|
+
opts.separator "Specific Options:"
|
41
48
|
opts.on("-w", "--width WIDTH", "Internal width of the box") { |value| options.width = value }
|
42
49
|
opts.on("-h", "--height HEIGHT", "Internal height of the box") { |value| options.height = value }
|
43
50
|
opts.on("-d", "--depth DEPTH", "Internal depth of the box") { |value| options.depth= value }
|
44
|
-
|
45
51
|
opts.on("-t", "--thickness THICKNESS", "Thickness of the box material") { |value| options.thickness = value }
|
46
52
|
opts.on("-n", "--notch NOTCH", "Preferred notch length (used only as a guide)") { |value| options.notch = value }
|
47
|
-
opts.
|
48
|
-
|
49
|
-
opts.on("-u", "--units UNITS", "Either 'mm' (default) or 'in'") { |value| options.units = value }
|
53
|
+
opts.separator ""
|
50
54
|
opts.on("-m", "--margin MARGIN", "Margins from the edge of the document") { |value| options.margin = value }
|
51
55
|
opts.on("-p", "--padding PADDING", "Space between the boxes on the page") { |value| options.padding = value }
|
52
|
-
|
56
|
+
opts.on("-S", "--stroke WIDTH", "Numeric stroke width of the line") { |value| options.stroke = value }
|
53
57
|
opts.on("-P", "--page_size LETTER", "Page size, see --list-all-page-sizes for more info.") { |value| options.page_size = value }
|
54
58
|
opts.on("-L", "--page_layout portrait", "Page layout, other option is 'landscape' ") { |value| options.page_layout = value }
|
55
|
-
opts.
|
56
|
-
|
59
|
+
opts.separator ""
|
57
60
|
opts.on("-O", "--open", "Open generated file with system viewer before exiting") { |v| options.open = v }
|
58
|
-
|
61
|
+
opts.on("-u", "--units UNITS", "Either 'mm' (default) or 'in'") { |value| options.units = value }
|
62
|
+
opts.separator ""
|
59
63
|
opts.on("-A", "--list-all-page-sizes", "Print all available page sizes with dimensions and exit") { |v| options.list_all_page_sizes = true }
|
60
|
-
|
61
64
|
opts.on("-v", "--[no-]verbose", "Run verbosely") { |v| options.verbose = v }
|
62
|
-
|
63
65
|
opts.separator ""
|
64
|
-
opts.
|
65
|
-
|
66
|
-
opts.
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
opts.on_tail("
|
71
|
-
|
72
|
-
|
66
|
+
opts.on("--examples", "Show detailed usage examples") { puts opts; puts examples.yellow; exit }
|
67
|
+
opts.on("--help", "Show this message") { puts opts; exit }
|
68
|
+
opts.on("--version", "Show version") { puts Laser::Cutter::VERSION; exit }
|
69
|
+
opts.separator ""
|
70
|
+
opts.separator "Common Options:"
|
71
|
+
opts.on_tail("-o", "--file FILE", "Required output filename of the PDF") { |value| options.file = value }
|
72
|
+
opts.on_tail("-s", "--size WxHxD/T/N",
|
73
|
+
"Combined internal dimensions: W = width, H = height,\n#{" " * 37}D = depth, T = thickness, N = notch length\n\n") do |size|
|
74
|
+
options.size = size
|
73
75
|
end
|
76
|
+
opts.on_tail("-i", "--inches", "Switch measurements to inches instead of millimeters") { |value| options.units = 'in' }
|
74
77
|
end
|
78
|
+
|
75
79
|
opt_parser.parse!(args)
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
80
|
+
|
81
|
+
config = Laser::Cutter::Configuration.new(options.to_hash)
|
82
|
+
if config.list_all_page_sizes
|
83
|
+
puts config.all_page_sizes
|
84
|
+
exit 0
|
85
|
+
end
|
86
|
+
|
87
|
+
if options.verbose
|
88
|
+
puts "Starting with the following configuration:"
|
89
|
+
puts JSON.pretty_generate(config.to_hash).green
|
90
|
+
end
|
91
|
+
|
92
|
+
config.validate!
|
93
|
+
config
|
94
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument, Laser::Cutter::MissingOption => e
|
95
|
+
puts opt_parser.banner.blue
|
96
|
+
puts_error(e)
|
81
97
|
exit 1
|
82
98
|
end # end parse()
|
83
99
|
end
|
84
100
|
end
|
85
101
|
end # class OptParse
|
86
102
|
|
87
|
-
|
88
|
-
|
89
|
-
if options.list_all_page_sizes
|
90
|
-
puts
|
91
|
-
unit = 1.0 / 72.0 # PDF units per inch
|
92
|
-
multiplier = options.units == 'in' ? 1.0 : 25.4
|
93
|
-
hash = PDF::Core::PageGeometry::SIZES
|
94
|
-
hash.keys.sort.each do |k|
|
95
|
-
printf("\t%10s:\t%6.1f x %6.1f\n",
|
96
|
-
k,
|
97
|
-
multiplier * hash[k][0].to_f * unit,
|
98
|
-
multiplier * hash[k][1].to_f * unit )
|
99
|
-
end
|
100
|
-
exit 0
|
101
|
-
end
|
102
|
-
|
103
|
-
config = Laser::Cutter::Configuration.new(options.to_hash)
|
104
|
-
|
105
|
-
if options.verbose
|
106
|
-
puts "Starting with the following configuration:"
|
107
|
-
puts JSON.pretty_generate(config.to_hash).green
|
108
|
-
end
|
109
|
-
|
110
|
-
begin
|
111
|
-
config.validate!
|
112
|
-
rescue Exception => e
|
113
|
-
STDERR.puts "\nSorry, #{e}".red
|
114
|
-
STDERR.puts "Try --help for more info..."
|
115
|
-
exit 1
|
116
|
-
end
|
103
|
+
config = Laser::Cutter::OptParse.parse(ARGV)
|
117
104
|
|
118
105
|
begin
|
119
|
-
|
120
|
-
Laser::Cutter::Renderer::BoxRenderer.new(box, config).render
|
106
|
+
Laser::Cutter::Renderer::BoxRenderer.new(config).render
|
121
107
|
if config.open
|
122
108
|
`open #{config.file}`
|
123
109
|
end
|
124
110
|
rescue Exception => e
|
125
|
-
|
126
|
-
STDERR.puts "Try --help for more info...".yellow
|
111
|
+
puts_error(e)
|
127
112
|
if options.verbose
|
128
113
|
STDERR.puts "Exception: #{e.inspect}".red
|
129
114
|
STDERR.puts e.backtrace.join("\n").red
|
data/doc/comparison.jpg
ADDED
Binary file
|
data/lib/laser-cutter/box.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Laser
|
2
2
|
module Cutter
|
3
|
+
# Note: this class badly needs refactoring and tests. Both are coming.
|
4
|
+
|
3
5
|
class Box
|
4
6
|
# Everything is in millimeters
|
5
7
|
|
@@ -7,7 +9,7 @@ module Laser
|
|
7
9
|
attr_accessor :padding, :units
|
8
10
|
|
9
11
|
attr_accessor :front, :back, :top, :bottom, :left, :right
|
10
|
-
attr_accessor :faces, :bounds, :conf
|
12
|
+
attr_accessor :faces, :bounds, :conf, :corner_face
|
11
13
|
|
12
14
|
def initialize(config = {})
|
13
15
|
self.dim = Geometry::Dimensions.new(config['width'], config['height'], config['depth'])
|
@@ -18,32 +20,35 @@ module Laser
|
|
18
20
|
self.units = config['units']
|
19
21
|
|
20
22
|
create_faces! # generates dimensions for each side
|
21
|
-
self.faces =
|
23
|
+
self.faces = [top, front, bottom, back, left, right]
|
22
24
|
|
23
25
|
position_faces!
|
26
|
+
|
24
27
|
self.conf = {
|
25
|
-
valign: [
|
26
|
-
halign: [
|
27
|
-
corners:
|
28
|
+
valign: [ :out, :out, :out, :out, :in, :in],
|
29
|
+
halign: [ :in, :out, :in, :out, :in, :in],
|
30
|
+
corners: {
|
31
|
+
front: [ :no, :yes, :no, :yes, :no, :no], # our default choice, but may not work
|
32
|
+
top: [ :yes, :no, :yes, :no, :no, :no] # 2nd choice, has to work if 1st doesn't
|
33
|
+
},
|
28
34
|
}
|
29
|
-
|
30
35
|
self
|
31
36
|
end
|
32
37
|
|
38
|
+
|
33
39
|
# Returns an flattened array of lines representing notched
|
34
40
|
# rectangle.
|
35
41
|
def notches
|
36
|
-
|
42
|
+
corner_face = pick_corners_face
|
37
43
|
|
38
44
|
notches = []
|
39
|
-
|
40
45
|
faces.each_with_index do |face, face_index|
|
41
|
-
bound =
|
46
|
+
bound = face_bounding_rect(face)
|
42
47
|
bound.sides.each_with_index do |bounding_side, side_index |
|
43
48
|
key = side_index.odd? ? :valign : :halign
|
44
49
|
path = Geometry::PathGenerator.new(:notch_width => notch_width,
|
45
50
|
:center_out => (self.conf[key][face_index] == :out) ,
|
46
|
-
:fill_corners => (self.conf[:corners][face_index] == :yes && side_index.odd?),
|
51
|
+
:fill_corners => (self.conf[:corners][corner_face][face_index] == :yes && side_index.odd?),
|
47
52
|
:thickness => thickness
|
48
53
|
).path(Geometry::Edge.new(bounding_side, face.sides[side_index], self.notch_width))
|
49
54
|
notches << path.create_lines
|
@@ -67,15 +72,11 @@ module Laser
|
|
67
72
|
|
68
73
|
private
|
69
74
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
b.p2 = b.p2.move_by(2 * thickness, 2 * thickness)
|
76
|
-
b.relocate!
|
77
|
-
b
|
78
|
-
end
|
75
|
+
def face_bounding_rect(face)
|
76
|
+
b = face.clone
|
77
|
+
b.move_to(b.position.move_by(-thickness, -thickness))
|
78
|
+
b.p2 = b.p2.move_by(2 * thickness, 2 * thickness)
|
79
|
+
b.relocate!
|
79
80
|
end
|
80
81
|
|
81
82
|
#___________________________________________________________________
|
@@ -131,6 +132,15 @@ module Laser
|
|
131
132
|
self.right = Geometry::Rect.create(zero, dim.d, dim.h, "right")
|
132
133
|
end
|
133
134
|
|
135
|
+
def pick_corners_face
|
136
|
+
b = face_bounding_rect(front)
|
137
|
+
edges = []
|
138
|
+
front.sides[0..1].each_with_index do |face, index|
|
139
|
+
edges << Geometry::Edge.new(b.sides[index], face, notch_width)
|
140
|
+
end
|
141
|
+
edges.map(&:notch_count).all?{|c| c % 4 == 3} ? :top : :front
|
142
|
+
end
|
143
|
+
|
134
144
|
end
|
135
145
|
end
|
136
146
|
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'hashie/mash'
|
2
2
|
require 'prawn/measurement_extensions'
|
3
|
-
|
3
|
+
require 'pdf/core/page_geometry'
|
4
4
|
|
5
5
|
module Laser
|
6
6
|
module Cutter
|
7
|
+
class MissingOption < RuntimeError
|
8
|
+
end
|
7
9
|
class Configuration < Hashie::Mash
|
8
10
|
DEFAULTS = {
|
9
11
|
units: 'mm',
|
@@ -49,7 +51,23 @@ module Laser
|
|
49
51
|
REQUIRED.each do |k|
|
50
52
|
missing << k if self[k].nil?
|
51
53
|
end
|
52
|
-
|
54
|
+
unless missing.empty?
|
55
|
+
raise MissingOption.new("#{missing.join(', ')} #{missing.size > 1 ? 'are' : 'is'} required, but missing.")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def all_page_sizes
|
60
|
+
unit = 1.0 / 72.0 # PDF units per inch
|
61
|
+
multiplier = (self.units == 'in') ? 1.0 : 25.4
|
62
|
+
h = PDF::Core::PageGeometry::SIZES
|
63
|
+
output = "\n"
|
64
|
+
h.keys.sort.each do |k|
|
65
|
+
output << sprintf("\t%10s:\t%6.1f x %6.1f\n",
|
66
|
+
k,
|
67
|
+
multiplier * h[k][0].to_f * unit,
|
68
|
+
multiplier * h[k][1].to_f * unit )
|
69
|
+
end
|
70
|
+
output
|
53
71
|
end
|
54
72
|
end
|
55
73
|
end
|
@@ -5,12 +5,15 @@ module Laser
|
|
5
5
|
module Renderer
|
6
6
|
class BoxRenderer < AbstractRenderer
|
7
7
|
alias_method :box, :subject
|
8
|
+
def initialize(options = {})
|
9
|
+
self.options = options
|
10
|
+
self.subject = Laser::Cutter::Box.new(options)
|
11
|
+
end
|
8
12
|
|
9
13
|
def render pdf = nil
|
10
14
|
pdf = Prawn::Document.new(:margin => options.margin.send(options.units),
|
11
15
|
:page_size => options.page_size,
|
12
16
|
:page_layout => options.page_layout.to_sym)
|
13
|
-
|
14
17
|
header = <<-EOF
|
15
18
|
|
16
19
|
Produced with Laser Cutter Ruby Gem (v#{Laser::Cutter::VERSION})
|
data/lib/laser-cutter/version.rb
CHANGED
data/spec/configuration_spec.rb
CHANGED
@@ -3,10 +3,10 @@ require_relative 'spec_helper'
|
|
3
3
|
module Laser
|
4
4
|
module Cutter
|
5
5
|
describe Configuration do
|
6
|
-
let(:config) { Laser::Cutter::Configuration.new(opts)}
|
6
|
+
let(:config) { Laser::Cutter::Configuration.new(opts) }
|
7
7
|
|
8
8
|
context 'option parsing' do
|
9
|
-
let(:opts) { {
|
9
|
+
let(:opts) { {"size" => "2x3x2/0.125/0.5", "inches" => true} }
|
10
10
|
it 'should be able to parse size options' do
|
11
11
|
expect(config.width).to eql(2.0)
|
12
12
|
expect(config.height).to eql(3.0)
|
@@ -15,14 +15,30 @@ module Laser
|
|
15
15
|
expect(config.notch).to eql(0.5)
|
16
16
|
end
|
17
17
|
end
|
18
|
-
context 'validate' do
|
19
|
-
let(:opts)
|
18
|
+
context '#validate' do
|
19
|
+
let(:opts) { {"height" => "23"} }
|
20
20
|
it 'should be able to validate missing options' do
|
21
21
|
expect(config.height).to eql(23.0)
|
22
|
-
expect { config.validate! }
|
22
|
+
expect { config.validate! }.to raise_error(Laser::Cutter::MissingOption)
|
23
23
|
end
|
24
24
|
end
|
25
|
+
context '#list_page_sizes' do
|
26
|
+
context 'formatting of output' do
|
27
|
+
context 'when using inches' do
|
28
|
+
let(:opts) { {"units" => "in"} }
|
29
|
+
it 'should return the list in inches' do
|
30
|
+
expect(config.all_page_sizes).to match %r(.*B10\:\s+1\.2\s+x\s+1\.7)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
context 'when using mm' do
|
34
|
+
let(:opts) { {"units" => "mm"} }
|
35
|
+
it 'should return the list in mm' do
|
36
|
+
expect(config.all_page_sizes).to match %r(.*B10\:\s+31\.0\s+x\s+44\.0)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
25
40
|
|
41
|
+
end
|
26
42
|
end
|
27
43
|
end
|
28
44
|
end
|
data/spec/renderer_spec.rb
CHANGED
@@ -6,7 +6,7 @@ module Laser
|
|
6
6
|
describe 'BoxRenderer' do
|
7
7
|
context '#render' do
|
8
8
|
let(:box) { Laser::Cutter::Box.new(config) }
|
9
|
-
let(:renderer) { BoxRenderer.new(
|
9
|
+
let(:renderer) { BoxRenderer.new(config) }
|
10
10
|
let(:file) { File.expand_path("../../laser-cutter-pdf-test.#{$$}.pdf", __FILE__) }
|
11
11
|
|
12
12
|
def render_file filename
|
data/spec/spec_helper.rb
CHANGED
@@ -5,12 +5,18 @@
|
|
5
5
|
#
|
6
6
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
7
|
|
8
|
+
if ENV['CODECLIMATE_REPO_TOKEN']
|
9
|
+
require 'codeclimate-test-reporter'
|
10
|
+
CodeClimate::TestReporter.start
|
11
|
+
end
|
12
|
+
|
8
13
|
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __FILE__)
|
9
14
|
require 'rubygems'
|
10
15
|
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
|
11
16
|
require 'laser-cutter'
|
17
|
+
require "codeclimate-test-reporter"
|
18
|
+
|
12
19
|
|
13
|
-
#Dir['spec/support/**/*.rb'].each { |filename| require_relative "../#{filename}" }
|
14
20
|
|
15
21
|
RSpec.configure do |config|
|
16
22
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
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.
|
4
|
+
version: 0.4.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-09-
|
11
|
+
date: 2014-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prawn
|
@@ -115,8 +115,7 @@ files:
|
|
115
115
|
- README.md
|
116
116
|
- Rakefile
|
117
117
|
- bin/laser-cutter
|
118
|
-
- doc/
|
119
|
-
- doc/laser-cutter.jpg
|
118
|
+
- doc/comparison.jpg
|
120
119
|
- laser-cutter.gemspec
|
121
120
|
- lib/laser-cutter.rb
|
122
121
|
- lib/laser-cutter/box.rb
|
@@ -180,4 +179,3 @@ test_files:
|
|
180
179
|
- spec/rect_spec.rb
|
181
180
|
- spec/renderer_spec.rb
|
182
181
|
- spec/spec_helper.rb
|
183
|
-
has_rdoc:
|
data/doc/boxmaker.jpg
DELETED
Binary file
|
data/doc/laser-cutter.jpg
DELETED
Binary file
|