laser-cutter 1.0.5 → 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 -5
- data/.plans/001.00-dry-cli-migration/plan.md +29 -0
- data/.relaxed_rubocop.yml +153 -0
- data/.rspec +1 -0
- 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 +110 -87
- data/Rakefile +27 -13
- 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/box.rb +173 -0
- 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 +23 -22
- data/lib/laser/cutter/notching/infinite_iterator.rb +27 -0
- data/lib/{laser-cutter → laser/cutter}/notching/path_generator.rb +76 -94
- 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 +35 -22
- data/spec/support/aruba.rb +10 -0
- metadata +202 -76
- data/.travis.yml +0 -26
- data/BOXMAKER.md +0 -51
- data/LICENSE +0 -22
- data/bin/laser-cutter +0 -19
- data/lib/laser-cutter/aggregator.rb +0 -57
- data/lib/laser-cutter/box.rb +0 -183
- 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/face.rb +0 -18
- data/lib/laser-cutter/geometry.rb +0 -11
- data/lib/laser-cutter/notching.rb +0 -9
- 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 -39
- data/spec/configuration_spec.rb +0 -81
- data/spec/edge_spec.rb +0 -43
- data/spec/renderer_spec.rb +0 -72
- /data/docs/{comparison.jpg → images/comparison.jpg} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4cb71396bbb2f8c49f903912ebe28e43ccf2fa278f7fc390bb68f4002dd678a5
|
|
4
|
+
data.tar.gz: 51bee7e480b3fd7578a6668a9f9c82ef3894e2f388bc3db336b76eefcff89098
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a146752a70a7b9f73c6cf46c0472ec5a7456b50e2623188c7665e057c0cf98bd687d05946137c968995783b569a03fb7c99afca3528e084f712d46298e9ef261
|
|
7
|
+
data.tar.gz: e8b3eecd4f8fe18078599f989f3a14e94bb9c4df0ff6504619f196849b1c016ebbcb48dad5153465e810cbcce38566cf00d5e2c67ce9bb8fbfca9aa4d80b0455
|
data/.envrc
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Ruby
|
|
2
|
+
|
|
3
|
+
on: [push,pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
strategy:
|
|
9
|
+
matrix:
|
|
10
|
+
ruby-version: [4.0.6]
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v2
|
|
14
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
15
|
+
uses: ruby/setup-ruby@v1
|
|
16
|
+
with:
|
|
17
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
18
|
+
bundler-cache: true
|
|
19
|
+
- name: Run Specs
|
|
20
|
+
run: |
|
|
21
|
+
bundle check || bundle install -j 4
|
|
22
|
+
bundle exec rubocop
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Ruby
|
|
2
|
+
|
|
3
|
+
on: [push,pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
strategy:
|
|
9
|
+
matrix:
|
|
10
|
+
ruby-version: [4.0.6]
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v2
|
|
14
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
15
|
+
uses: ruby/setup-ruby@v1
|
|
16
|
+
with:
|
|
17
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
18
|
+
bundler-cache: true
|
|
19
|
+
- name: Run Specs
|
|
20
|
+
run: |
|
|
21
|
+
bundle check || bundle install -j 4
|
|
22
|
+
bundle exec rspec --format documentation
|
data/.gitignore
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
**.pdf
|
|
2
2
|
**.DS_Store
|
|
3
|
+
.rspec_status
|
|
3
4
|
**/*.swp
|
|
4
5
|
*.gem
|
|
5
6
|
*.rbc
|
|
@@ -9,8 +10,6 @@
|
|
|
9
10
|
Gemfile.lock
|
|
10
11
|
InstalledFiles
|
|
11
12
|
_yardoc
|
|
12
|
-
coverage
|
|
13
|
-
lib/bundler/man
|
|
14
13
|
pkg
|
|
15
14
|
rdoc
|
|
16
15
|
spec/reports
|
|
@@ -23,6 +22,7 @@ tmp
|
|
|
23
22
|
*.a
|
|
24
23
|
mkmf.log
|
|
25
24
|
.idea/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
.ruby-lsp
|
|
26
|
+
/coverage
|
|
27
|
+
!docs/coverage/
|
|
28
|
+
/doc
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# 001.00 Migrate the CLI to dry-cli, load with Zeitwerk
|
|
2
|
+
|
|
3
|
+
Branch `kig/migrate-to-dry-cli`, off `kig/2.0.0-alpha`; the PR targets `kig/2.0.0-alpha`.
|
|
4
|
+
|
|
5
|
+
## Decisions
|
|
6
|
+
|
|
7
|
+
- `lib/laser_cutter/` moves to `lib/laser/cutter/`, loaded by `Zeitwerk::Loader.for_gem_extension(Laser)`, eager-loaded. `lib/laser-cutter.rb` and `lib/laser_cutter.rb` remain as shims.
|
|
8
|
+
|
|
9
|
+
- `generate` takes height as `-H` / `--height`: dry-cli owns `-h`, and `-v` means verbose. `version` answers to `-V` / `--version`.
|
|
10
|
+
|
|
11
|
+
- Ruby 4.0.6, pinned in `.ruby-version`.
|
|
12
|
+
|
|
13
|
+
- The executables move from `bin/` to `exe/` and call `Laser::Cutter::Launcher`.
|
|
14
|
+
|
|
15
|
+
- Help and boxes share one width, 90 columns or fewer; a progress bar is 60 cells.
|
|
16
|
+
|
|
17
|
+
- `--inside-box` draws the unkerfed outline, which `--debug` used to do by accident; `--debug` is gone, `--verbose` prints the backtrace.
|
|
18
|
+
|
|
19
|
+
## Work
|
|
20
|
+
|
|
21
|
+
- [x] Zeitwerk: one constant per file (split `configuration.rb` and `path_generator.rb`, move `geometry/shape/{line,rect}.rb` up), eager load
|
|
22
|
+
- [x] dry-cli with dry-cli-help, dry-cli-ui and dry-cli-autocomplete; commands `generate`, `page-sizes`, `examples`, `help`, `version`, `completion`
|
|
23
|
+
- [x] `generate -f/--format pdf|svg`, PDF by default
|
|
24
|
+
- [x] Green progress bar, at most 60 cells wide, advancing once per line drawn
|
|
25
|
+
- [x] SVG output through `victor`
|
|
26
|
+
- [x] Launcher plus in-process Aruba suite
|
|
27
|
+
- [x] Bug: a notch that divides the sides exactly (4x3x2, notch 0.5) makes adjacent sides mismatch
|
|
28
|
+
- [x] README and CLAUDE.md updated
|
|
29
|
+
- [x] Bug: PageManager read the default Symbol units as millimeters
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Relaxed.Ruby.Style
|
|
2
|
+
## Version 2.5
|
|
3
|
+
|
|
4
|
+
Style/Alias:
|
|
5
|
+
Enabled: false
|
|
6
|
+
StyleGuide: https://relaxed.ruby.style/#stylealias
|
|
7
|
+
|
|
8
|
+
Style/AsciiComments:
|
|
9
|
+
Enabled: false
|
|
10
|
+
StyleGuide: https://relaxed.ruby.style/#styleasciicomments
|
|
11
|
+
|
|
12
|
+
Style/BeginBlock:
|
|
13
|
+
Enabled: false
|
|
14
|
+
StyleGuide: https://relaxed.ruby.style/#stylebeginblock
|
|
15
|
+
|
|
16
|
+
Style/BlockDelimiters:
|
|
17
|
+
Enabled: false
|
|
18
|
+
StyleGuide: https://relaxed.ruby.style/#styleblockdelimiters
|
|
19
|
+
|
|
20
|
+
Style/CommentAnnotation:
|
|
21
|
+
Enabled: false
|
|
22
|
+
StyleGuide: https://relaxed.ruby.style/#stylecommentannotation
|
|
23
|
+
|
|
24
|
+
Style/Documentation:
|
|
25
|
+
Enabled: false
|
|
26
|
+
StyleGuide: https://relaxed.ruby.style/#styledocumentation
|
|
27
|
+
|
|
28
|
+
Layout/DotPosition:
|
|
29
|
+
Enabled: false
|
|
30
|
+
StyleGuide: https://relaxed.ruby.style/#layoutdotposition
|
|
31
|
+
|
|
32
|
+
Style/DoubleNegation:
|
|
33
|
+
Enabled: false
|
|
34
|
+
StyleGuide: https://relaxed.ruby.style/#styledoublenegation
|
|
35
|
+
|
|
36
|
+
Style/EndBlock:
|
|
37
|
+
Enabled: false
|
|
38
|
+
StyleGuide: https://relaxed.ruby.style/#styleendblock
|
|
39
|
+
|
|
40
|
+
Style/FormatString:
|
|
41
|
+
Enabled: false
|
|
42
|
+
StyleGuide: https://relaxed.ruby.style/#styleformatstring
|
|
43
|
+
|
|
44
|
+
Style/IfUnlessModifier:
|
|
45
|
+
Enabled: false
|
|
46
|
+
StyleGuide: https://relaxed.ruby.style/#styleifunlessmodifier
|
|
47
|
+
|
|
48
|
+
Style/Lambda:
|
|
49
|
+
Enabled: false
|
|
50
|
+
StyleGuide: https://relaxed.ruby.style/#stylelambda
|
|
51
|
+
|
|
52
|
+
Style/ModuleFunction:
|
|
53
|
+
Enabled: false
|
|
54
|
+
StyleGuide: https://relaxed.ruby.style/#stylemodulefunction
|
|
55
|
+
|
|
56
|
+
Style/MultilineBlockChain:
|
|
57
|
+
Enabled: false
|
|
58
|
+
StyleGuide: https://relaxed.ruby.style/#stylemultilineblockchain
|
|
59
|
+
|
|
60
|
+
Style/NegatedIf:
|
|
61
|
+
Enabled: false
|
|
62
|
+
StyleGuide: https://relaxed.ruby.style/#stylenegatedif
|
|
63
|
+
|
|
64
|
+
Style/NegatedWhile:
|
|
65
|
+
Enabled: false
|
|
66
|
+
StyleGuide: https://relaxed.ruby.style/#stylenegatedwhile
|
|
67
|
+
|
|
68
|
+
Style/NumericPredicate:
|
|
69
|
+
Enabled: false
|
|
70
|
+
StyleGuide: https://relaxed.ruby.style/#stylenumericpredicate
|
|
71
|
+
|
|
72
|
+
Style/ParallelAssignment:
|
|
73
|
+
Enabled: false
|
|
74
|
+
StyleGuide: https://relaxed.ruby.style/#styleparallelassignment
|
|
75
|
+
|
|
76
|
+
Style/PercentLiteralDelimiters:
|
|
77
|
+
Enabled: false
|
|
78
|
+
StyleGuide: https://relaxed.ruby.style/#stylepercentliteraldelimiters
|
|
79
|
+
|
|
80
|
+
Style/PerlBackrefs:
|
|
81
|
+
Enabled: false
|
|
82
|
+
StyleGuide: https://relaxed.ruby.style/#styleperlbackrefs
|
|
83
|
+
|
|
84
|
+
Style/Semicolon:
|
|
85
|
+
Enabled: false
|
|
86
|
+
StyleGuide: https://relaxed.ruby.style/#stylesemicolon
|
|
87
|
+
|
|
88
|
+
Style/SignalException:
|
|
89
|
+
Enabled: false
|
|
90
|
+
StyleGuide: https://relaxed.ruby.style/#stylesignalexception
|
|
91
|
+
|
|
92
|
+
Style/SingleLineBlockParams:
|
|
93
|
+
Enabled: false
|
|
94
|
+
StyleGuide: https://relaxed.ruby.style/#stylesinglelineblockparams
|
|
95
|
+
|
|
96
|
+
Style/SingleLineMethods:
|
|
97
|
+
Enabled: false
|
|
98
|
+
StyleGuide: https://relaxed.ruby.style/#stylesinglelinemethods
|
|
99
|
+
|
|
100
|
+
Layout/SpaceBeforeBlockBraces:
|
|
101
|
+
Enabled: false
|
|
102
|
+
StyleGuide: https://relaxed.ruby.style/#layoutspacebeforeblockbraces
|
|
103
|
+
|
|
104
|
+
Layout/SpaceInsideParens:
|
|
105
|
+
Enabled: false
|
|
106
|
+
StyleGuide: https://relaxed.ruby.style/#layoutspaceinsideparens
|
|
107
|
+
|
|
108
|
+
Style/SpecialGlobalVars:
|
|
109
|
+
Enabled: false
|
|
110
|
+
StyleGuide: https://relaxed.ruby.style/#stylespecialglobalvars
|
|
111
|
+
|
|
112
|
+
Style/StringLiterals:
|
|
113
|
+
Enabled: false
|
|
114
|
+
StyleGuide: https://relaxed.ruby.style/#stylestringliterals
|
|
115
|
+
|
|
116
|
+
Style/TrailingCommaInArguments:
|
|
117
|
+
Enabled: false
|
|
118
|
+
StyleGuide: https://relaxed.ruby.style/#styletrailingcommainarguments
|
|
119
|
+
|
|
120
|
+
Style/TrailingCommaInArrayLiteral:
|
|
121
|
+
Enabled: false
|
|
122
|
+
StyleGuide: https://relaxed.ruby.style/#styletrailingcommainarrayliteral
|
|
123
|
+
|
|
124
|
+
Style/TrailingCommaInHashLiteral:
|
|
125
|
+
Enabled: false
|
|
126
|
+
StyleGuide: https://relaxed.ruby.style/#styletrailingcommainhashliteral
|
|
127
|
+
|
|
128
|
+
Style/SymbolArray:
|
|
129
|
+
Enabled: false
|
|
130
|
+
StyleGuide: http://relaxed.ruby.style/#stylesymbolarray
|
|
131
|
+
|
|
132
|
+
Style/WhileUntilModifier:
|
|
133
|
+
Enabled: false
|
|
134
|
+
StyleGuide: https://relaxed.ruby.style/#stylewhileuntilmodifier
|
|
135
|
+
|
|
136
|
+
Style/WordArray:
|
|
137
|
+
Enabled: false
|
|
138
|
+
StyleGuide: https://relaxed.ruby.style/#stylewordarray
|
|
139
|
+
|
|
140
|
+
Lint/AmbiguousRegexpLiteral:
|
|
141
|
+
Enabled: false
|
|
142
|
+
StyleGuide: https://relaxed.ruby.style/#lintambiguousregexpliteral
|
|
143
|
+
|
|
144
|
+
Lint/AssignmentInCondition:
|
|
145
|
+
Enabled: false
|
|
146
|
+
StyleGuide: https://relaxed.ruby.style/#lintassignmentincondition
|
|
147
|
+
|
|
148
|
+
Layout/LineLength:
|
|
149
|
+
Enabled: false
|
|
150
|
+
|
|
151
|
+
Metrics:
|
|
152
|
+
Enabled: false
|
|
153
|
+
|
data/.rspec
CHANGED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
inherit_from:
|
|
2
|
+
- .rubocop_todo.yml
|
|
3
|
+
- .relaxed_rubocop.yml
|
|
4
|
+
|
|
5
|
+
AllCops:
|
|
6
|
+
NewCops: enable
|
|
7
|
+
SuggestExtensions: false
|
|
8
|
+
TargetRubyVersion: 4.0
|
|
9
|
+
|
|
10
|
+
Layout/LineLength:
|
|
11
|
+
Max: 120
|
|
12
|
+
|
|
13
|
+
Metrics/BlockLength:
|
|
14
|
+
Exclude:
|
|
15
|
+
- "spec/**/*"
|
|
16
|
+
|
|
17
|
+
Lint/ConstantDefinitionInBlock:
|
|
18
|
+
Enabled: false
|
|
19
|
+
|
|
20
|
+
Lint/StructNewOverride:
|
|
21
|
+
Enabled: false
|
|
22
|
+
|
|
23
|
+
Layout/HashAlignment:
|
|
24
|
+
EnforcedColonStyle: table
|
|
25
|
+
EnforcedHashRocketStyle: table
|
|
26
|
+
|
|
27
|
+
Layout/ArgumentAlignment:
|
|
28
|
+
EnforcedStyle: with_first_argument
|
|
29
|
+
|
|
30
|
+
Naming/MethodParameterName:
|
|
31
|
+
Enabled: false
|
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# This configuration was generated by
|
|
2
|
+
# `rubocop --auto-gen-config`
|
|
3
|
+
# on 2026-09-29 22:33:21 UTC using RuboCop version 1.91.0.
|
|
4
|
+
# The point is for the user to remove these configuration records
|
|
5
|
+
# one by one as the offenses are removed from the code base.
|
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
|
8
|
+
|
|
9
|
+
# Offense count: 1
|
|
10
|
+
Gemspec/RequiredRubyVersion:
|
|
11
|
+
Exclude:
|
|
12
|
+
- 'laser-cutter.gemspec'
|
|
13
|
+
|
|
14
|
+
# Offense count: 1
|
|
15
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
16
|
+
Lint/CopDirectiveSyntax:
|
|
17
|
+
Exclude:
|
|
18
|
+
- 'spec/spec_helper.rb'
|
|
19
|
+
|
|
20
|
+
# Offense count: 1
|
|
21
|
+
# Configuration parameters: DelegatingMethods, AllowedCrossFilePaths.
|
|
22
|
+
# DelegatingMethods: delegate
|
|
23
|
+
Lint/DuplicateMethods:
|
|
24
|
+
Exclude:
|
|
25
|
+
- 'lib/laser/cutter/geometry/shape.rb'
|
|
26
|
+
|
|
27
|
+
# Offense count: 2
|
|
28
|
+
# Configuration parameters: AllowedParentClasses.
|
|
29
|
+
Lint/MissingSuper:
|
|
30
|
+
Exclude:
|
|
31
|
+
- 'lib/laser/cutter/geometry/line.rb'
|
|
32
|
+
- 'lib/laser/cutter/renderer/meta_renderer.rb'
|
|
33
|
+
|
|
34
|
+
# Offense count: 1
|
|
35
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
36
|
+
Lint/RedundantCopDisableDirective:
|
|
37
|
+
Exclude:
|
|
38
|
+
- 'spec/spec_helper.rb'
|
|
39
|
+
|
|
40
|
+
# Offense count: 3
|
|
41
|
+
Lint/RescueException:
|
|
42
|
+
Exclude:
|
|
43
|
+
- 'lib/laser/cutter/cli/serializer.rb'
|
|
44
|
+
- 'spec/laser/cutter/renderer/layout_renderer_spec.rb'
|
|
45
|
+
|
|
46
|
+
# Offense count: 1
|
|
47
|
+
# Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, CheckDefinitionPathHierarchyRoots, Regex, IgnoreExecutableScripts, AllowedAcronyms.
|
|
48
|
+
# CheckDefinitionPathHierarchyRoots: lib, spec, test, src
|
|
49
|
+
# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
|
|
50
|
+
Naming/FileName:
|
|
51
|
+
Exclude:
|
|
52
|
+
- 'Rakefile.rb'
|
|
53
|
+
- 'lib/laser-cutter.rb'
|
|
54
|
+
|
|
55
|
+
# Offense count: 3
|
|
56
|
+
# Configuration parameters: ForbiddenDelimiters.
|
|
57
|
+
# ForbiddenDelimiters: (?i-mx:(^|\s)(EO[A-Z]{1}|END)(\s|$))
|
|
58
|
+
Naming/HeredocDelimiterNaming:
|
|
59
|
+
Exclude:
|
|
60
|
+
- 'lib/laser/cutter/cli/opt_parser.rb'
|
|
61
|
+
- 'lib/laser/cutter/renderer/meta_renderer.rb'
|
|
62
|
+
|
|
63
|
+
# Offense count: 3
|
|
64
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
65
|
+
# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, Mode, AllowedMethods, AllowedPatterns.
|
|
66
|
+
# SupportedStyles: annotated, template, unannotated
|
|
67
|
+
Style/FormatStringToken:
|
|
68
|
+
EnforcedStyle: unannotated
|
|
69
|
+
|
|
70
|
+
# Offense count: 1
|
|
71
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
72
|
+
# Configuration parameters: EnforcedStyle.
|
|
73
|
+
# SupportedStyles: always, always_true, never
|
|
74
|
+
Style/FrozenStringLiteralComment:
|
|
75
|
+
Exclude:
|
|
76
|
+
- '**/*.arb'
|
|
77
|
+
- 'lib/laser/cutter/page_manager.rb'
|
|
78
|
+
|
|
79
|
+
# Offense count: 1
|
|
80
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
81
|
+
Style/MapToHash:
|
|
82
|
+
Exclude:
|
|
83
|
+
- 'lib/laser/cutter/configuration.rb'
|
|
84
|
+
|
|
85
|
+
# Offense count: 1
|
|
86
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
87
|
+
# Configuration parameters: Mode.
|
|
88
|
+
Style/StringConcatenation:
|
|
89
|
+
Exclude:
|
|
90
|
+
- 'lib/laser/cutter/renderer/meta_renderer.rb'
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4.0.6
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
`laser-cutter` is a Ruby gem and CLI that generates a PDF of the six notched panels of a box. You cut the panels on a laser cutter and they snap together. Prawn renders the PDF. The notch algorithm favors **symmetric** panels over identical notch lengths.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
The `justfile` drives everything. Its recipes wrap `bundle exec` in `eval "$(rbenv init - bash)"`. `.ruby-version` and CI pin Ruby **4.0.6**; dry-cli-help and dry-cli-ui need 4.0.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
just lint # rubocop
|
|
15
|
+
just format # rubocop -a, plus mdformat on every *.md
|
|
16
|
+
just test # rspec (progress format, random order)
|
|
17
|
+
just test spec/laser/cutter/edge_spec.rb:42 # one file or example
|
|
18
|
+
just test-docs # rspec --format documentation
|
|
19
|
+
just ci # lint + test-coverage
|
|
20
|
+
just doc # YARD docs
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `bundle exec rspec`, `bundle exec rubocop` and `bundle exec rake` (default task `spec`) also work.
|
|
24
|
+
- Every rspec run rewrites `docs/badges/coverage_badge.svg` through the SimpleCov `at_exit` hook in `spec/spec_helper.rb`. That file shows up in `git status` after any test run.
|
|
25
|
+
- `spec_helper.rb` fails the run under 95% line coverage. The Aruba suite in `spec/laser/cutter/cli_spec.rb` counts towards it.
|
|
26
|
+
|
|
27
|
+
## Architecture
|
|
28
|
+
|
|
29
|
+
Everything lives under `Laser::Cutter`, in `lib/laser/cutter/`. `lib/laser/cutter.rb` requires the third-party gems, then sets up `Zeitwerk::Loader.for_gem_extension(Laser)` and **eager-loads** it. So: one constant per file, the file name matches the constant, and no `require` between the gem's own files. `lib/laser-cutter.rb` and `lib/laser_cutter.rb` are shims.
|
|
30
|
+
|
|
31
|
+
Pipeline, from config to file:
|
|
32
|
+
|
|
33
|
+
1. **`Configuration`** is a `Hashie::Mash` with symbolized keys. It parses the `--box WxHxD/T[/N]` shorthand, casts numeric strings to floats, and merges per-unit defaults for kerf, margin, padding and stroke. Notch defaults to `3 × thickness`. `validate!` raises `MissingOption` / `ZeroValueNotAllowed`. `units` defaults to the Symbol `:in`, and arrives as a String from the command line, so compare it with `to_s` or `to_sym`.
|
|
34
|
+
1. **`Renderer.for(format, config)`** picks `LayoutRenderer` (PDF, Prawn) or `SvgRenderer` (SVG, Victor). Both answer `total` (lines to draw) and `render { |line| }`, which yields after each line. That block drives the progress bar.
|
|
35
|
+
- `LayoutRenderer` draws a `BoxRenderer`, a `MetaRenderer` when `config.metadata` is set, and a second red `BoxRenderer` without kerf when `config.debug` is set. It sizes the page from the box enclosure unless `page_size` is given.
|
|
36
|
+
- `SvgRenderer` fits the page to the box, flips y (SVG counts down from the top), and writes the metadata as a `<desc>`.
|
|
37
|
+
1. **`Box`** models the six faces as `Geometry::Rect`s. `position_faces!` lays them out in a cross (see the ASCII diagram in that method). `generate_notches` pairs each side of a face with the matching side of its outer bounding rect (face grown by `thickness`) as a **`Notching::Edge`**. The `conf` table sets per-face alignment: `valign`/`halign` decide whether a side's center notch points `:out` or `:in`. `corners` plus `pick_corners_face` decide which face fills the corner squares.
|
|
38
|
+
1. **`Notching::Edge`** holds the inside and outside lines of one side, both shifted by `kerf / 2`. `calculate_notch_width!` forces an **odd** notch count of at least 3 and recomputes the real notch width, so the requested notch is only a guide. It rounds `length / notch` to `RATIO_DIGITS` before `ceil`: two panels meeting at a joint must get the same count, and float noise used to split them when the notch divided the side exactly.
|
|
39
|
+
1. **`Notching::PathGenerator`** turns an `Edge` into `Geometry::Line`s. It zigzags between the inside and outside lines using `Shift` deltas from two alternating `InfiniteIterator`s. It widens or narrows notches by `kerf` and adds the corner boxes.
|
|
40
|
+
1. **`Aggregator`** merges the lines of a face into the outline to cut. Neighbouring edges draw shared stretches twice, and a shared stretch runs through the material, so each collinear group is combined as a symmetric difference: a point is cut when an odd number of lines cover it. It groups lines by axis and offset and sweeps each group once, so a face takes n log n; the old pairwise version made a 100×80×60 box take 55 seconds.
|
|
41
|
+
|
|
42
|
+
Geometry is unitless, in the config's units. Conversion to PDF points happens only at render time (`value.send(:in)` / `.send(:mm)`). `PageManager#value_from_units` converts PDF points back.
|
|
43
|
+
|
|
44
|
+
### Command line
|
|
45
|
+
|
|
46
|
+
- `exe/laser-cutter` (and `exe/lc`) call `Laser::Cutter::Launcher.new(ARGV).execute!`. The Launcher takes argv, the three streams and `kernel`, and exits only through `kernel.exit`. Aruba runs it in-process (`spec/support/aruba.rb`), so commands must write to `out` and `err`, never to `$stdout`.
|
|
47
|
+
- `CLI` (`cli.rb`) is the dry-cli registry and the `Dry::CLI::Help.configure` block. Commands live in `cli/`: `generate`, `page-sizes`, `examples`, `help`, `version`, `completion`.
|
|
48
|
+
- Every message goes through `ui`: errors in `ui.error` boxes on STDERR (the Launcher draws them), `generate` opens with `ui.info` and closes with `ui.success`.
|
|
49
|
+
- `CLI::Command` is the base: it includes `Dry::CLI::UI`, declares `-v`, and offers `progress(label, total:)`, a green bar of 60 cells. Help and boxes share one width, `CLI.help_width`: the terminal's less 6, and 90 at most.
|
|
50
|
+
- `-h` belongs to help, so height is `-H`.
|
|
51
|
+
|
|
52
|
+
## Known gaps
|
|
53
|
+
|
|
54
|
+
- `just build` is an empty recipe, so `just publish` builds nothing. `rake build` is the real build.
|
|
55
|
+
- The `Rakefile` YARD title is copied from another project, and it references a `CHANGELOG.md` that does not exist.
|
|
56
|
+
- The gemspec lists `tty-*` and `pastel` directly, though only dry-cli-ui uses them.
|
|
57
|
+
- `Box` still carries the comment "badly needs refactoring and tests".
|
|
58
|
+
|
|
59
|
+
## Conventions
|
|
60
|
+
|
|
61
|
+
- `.rubocop.yml` inherits `.relaxed_rubocop.yml` and `.rubocop_todo.yml`, with a 120-column line limit and table-aligned hashes.
|
|
62
|
+
- Specs mirror `lib/` under `spec/laser/cutter/` and use `rspec-its`, `disable_monkey_patching!` and random order.
|
data/CONTRIBUTING.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
Contributing to Laser-Cutter
|
|
2
|
-
======================
|
|
1
|
+
# Contributing to Laser-Cutter
|
|
3
2
|
|
|
4
3
|
You're encouraged to submit [pull requests](https://github.com/kigster/laser-cutter/pulls), [propose features and discuss issues](https://github.com/kigster/laser-cutter/issues).
|
|
5
4
|
|
|
@@ -34,8 +33,7 @@ bundle exec rake
|
|
|
34
33
|
|
|
35
34
|
#### Write Tests
|
|
36
35
|
|
|
37
|
-
Try to write a test that reproduces the problem you're trying to fix or describes a
|
|
38
|
-
feature that you want to build. Add to [spec/laser-cutter](spec/laser-cutter).
|
|
36
|
+
Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. Add to [spec/laser-cutter](spec/laser-cutter).
|
|
39
37
|
|
|
40
38
|
We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix.
|
|
41
39
|
|
data/Gemfile
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
source 'https://rubygems.org'
|
|
2
4
|
|
|
3
5
|
# Specify your gem's dependencies in laser-cutter-cutter.gemspec
|
|
4
6
|
gemspec
|
|
5
7
|
|
|
8
|
+
group 'development', 'test' do
|
|
9
|
+
gem 'aruba'
|
|
10
|
+
gem 'coverage-badge'
|
|
11
|
+
gem 'rake'
|
|
12
|
+
gem 'rspec'
|
|
13
|
+
gem 'rspec-its'
|
|
14
|
+
gem 'rubocop', require: false
|
|
15
|
+
gem 'rubocop-rake', require: false
|
|
16
|
+
gem 'rubocop-rspec', require: false
|
|
17
|
+
gem 'simplecov'
|
|
18
|
+
gem 'yard'
|
|
19
|
+
end
|