dieses 0.0.1
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 +7 -0
- data/BEN/304/260OKU.md +0 -0
- data/CHANGELOG.md +14 -0
- data/LICENSE.md +675 -0
- data/README.md +18 -0
- data/bin/dieses +10 -0
- data/bin/diesis +10 -0
- data/dieses.gemspec +36 -0
- data/lib/dieses.rb +7 -0
- data/lib/dieses/application.rb +24 -0
- data/lib/dieses/application/batch.rb +127 -0
- data/lib/dieses/application/canvas.rb +51 -0
- data/lib/dieses/application/cli.rb +6 -0
- data/lib/dieses/application/cli/multi.rb +106 -0
- data/lib/dieses/application/cli/single.rb +100 -0
- data/lib/dieses/application/common.rb +27 -0
- data/lib/dieses/application/mixins.rb +5 -0
- data/lib/dieses/application/mixins/lines.rb +105 -0
- data/lib/dieses/application/mixins/scribes.rb +91 -0
- data/lib/dieses/application/mixins/squares.rb +23 -0
- data/lib/dieses/application/paper.rb +146 -0
- data/lib/dieses/application/pen.rb +161 -0
- data/lib/dieses/application/sheet.rb +111 -0
- data/lib/dieses/application/sheets.rb +58 -0
- data/lib/dieses/application/sheets/copperplate.rb +20 -0
- data/lib/dieses/application/sheets/cursive.rb +19 -0
- data/lib/dieses/application/sheets/graph.rb +27 -0
- data/lib/dieses/application/sheets/italics.rb +19 -0
- data/lib/dieses/application/sheets/lettering.rb +30 -0
- data/lib/dieses/application/sheets/lined.rb +24 -0
- data/lib/dieses/application/sheets/print.rb +19 -0
- data/lib/dieses/application/sheets/ruled.rb +25 -0
- data/lib/dieses/application/sheets/spencerian.rb +20 -0
- data/lib/dieses/application/sheets/thumbnail.rb +37 -0
- data/lib/dieses/error.rb +5 -0
- data/lib/dieses/geometry.rb +8 -0
- data/lib/dieses/geometry/element.rb +66 -0
- data/lib/dieses/geometry/equation.rb +57 -0
- data/lib/dieses/geometry/equation/slant.rb +88 -0
- data/lib/dieses/geometry/equation/steep.rb +70 -0
- data/lib/dieses/geometry/error.rb +7 -0
- data/lib/dieses/geometry/line.rb +67 -0
- data/lib/dieses/geometry/point.rb +98 -0
- data/lib/dieses/geometry/rect.rb +173 -0
- data/lib/dieses/geometry/support.rb +3 -0
- data/lib/dieses/support.rb +9 -0
- data/lib/dieses/support/class.rb +100 -0
- data/lib/dieses/support/const.rb +78 -0
- data/lib/dieses/support/enum.rb +63 -0
- data/lib/dieses/support/float.rb +43 -0
- data/lib/dieses/support/hash.rb +19 -0
- data/lib/dieses/support/kernel.rb +36 -0
- data/lib/dieses/support/math.rb +20 -0
- data/lib/dieses/version.rb +5 -0
- metadata +226 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
module Dieses
|
6
|
+
module Application
|
7
|
+
Error = Class.new Error
|
8
|
+
|
9
|
+
NotImplementedError = Class.new Error
|
10
|
+
NonApplicableError = Class.new Error
|
11
|
+
|
12
|
+
struct = Class.new OpenStruct do
|
13
|
+
def self.call(**kwargs)
|
14
|
+
new(**kwargs)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_a
|
18
|
+
to_h.values
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Style = Class.new struct
|
23
|
+
Param = Class.new struct
|
24
|
+
|
25
|
+
Orientation = Geometry::Rect::Orientation
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dieses
|
4
|
+
module Application
|
5
|
+
module Mixins
|
6
|
+
module Lines
|
7
|
+
module ClassMethods
|
8
|
+
Line = Struct.new :tag, :after, :style, :step, keyword_init: true
|
9
|
+
|
10
|
+
def hline(tag, after: Undefined, style: EMPTY_HASH)
|
11
|
+
(param.hlines ||= []) << Line.new(tag: tag, after: after, style: style)
|
12
|
+
end
|
13
|
+
|
14
|
+
def vline(tag, after: Undefined, style: EMPTY_HASH)
|
15
|
+
(param.vlines ||= []) << Line.new(tag: tag, after: after, style: style)
|
16
|
+
end
|
17
|
+
|
18
|
+
Cross = Struct.new :tag, :angle, :style, keyword_init: true
|
19
|
+
|
20
|
+
def cline(tag, angle:, style: EMPTY_HASH)
|
21
|
+
(param.clines ||= []) << Cross.new(tag: tag, angle: angle, style: style)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module InstanceMethods
|
26
|
+
def lines(unit: Undefined, multiple: Undefined)
|
27
|
+
step_lines(param)
|
28
|
+
|
29
|
+
unit = Undefined.default(unit, param.unit)
|
30
|
+
multiple = Undefined.default(multiple, param.multiple || param.hlines&.map(&:step)&.sum)
|
31
|
+
|
32
|
+
draw_hlines unit: unit, multiple: multiple
|
33
|
+
draw_vlines unit: unit, multiple: multiple
|
34
|
+
draw_clines unit: unit, multiple: multiple
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def step_lines(param)
|
40
|
+
[*param.hlines, *param.vlines].compact.each do |line|
|
41
|
+
line.step = case line.after
|
42
|
+
when Proc then param.instance_exec(&line.after)
|
43
|
+
when Numeric then line.after
|
44
|
+
when Undefined, NilClass then 1
|
45
|
+
else raise ArgumentError, "Wrong type for after: #{line.after.class}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def draw_hlines(unit:, multiple:)
|
51
|
+
return unless param.hlines
|
52
|
+
|
53
|
+
param = self.param
|
54
|
+
|
55
|
+
draw unit: unit, multiple: multiple do
|
56
|
+
repeat do
|
57
|
+
param.hlines.each do |line|
|
58
|
+
hline line.tag, style: line.style
|
59
|
+
down line.step
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def draw_vlines(unit:, multiple:)
|
66
|
+
return unless param.vlines
|
67
|
+
|
68
|
+
param = self.param
|
69
|
+
|
70
|
+
draw unit: unit, multiple: multiple do
|
71
|
+
repeat do
|
72
|
+
param.vlines.each do |line|
|
73
|
+
vline line.tag, style: line.style
|
74
|
+
right line.step
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def draw_clines(unit:, multiple:) # rubocop:disable Metrics/MethodLength
|
81
|
+
return unless param.clines
|
82
|
+
|
83
|
+
param = self.param
|
84
|
+
|
85
|
+
draw unit: unit, multiple: multiple do
|
86
|
+
repeat do
|
87
|
+
param.clines.each do |slant|
|
88
|
+
repeat do
|
89
|
+
cline slant.tag, angle: slant.angle, style: slant.style
|
90
|
+
cross
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.included(base)
|
99
|
+
base.extend ClassMethods
|
100
|
+
base.include InstanceMethods
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dieses
|
4
|
+
module Application
|
5
|
+
module Mixins
|
6
|
+
class Scribes < Module
|
7
|
+
def self.[](type)
|
8
|
+
raise ArgumentError, "No such Scribes type available: #{type}" unless Bundle.method_defined? type
|
9
|
+
|
10
|
+
new(type)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(type)
|
14
|
+
super()
|
15
|
+
@type = type.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def with(unit:, ratio: [1/1r], gap: [0])
|
19
|
+
tap do
|
20
|
+
@unit = unit
|
21
|
+
@ratio = ratio
|
22
|
+
@gap = gap
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def variate_scribes(unit:, ratio: [1/1r], gap: [0])
|
28
|
+
variate(unit: unit, ratio: ratio, gap: gap) do
|
29
|
+
self.name = :"#{self.unit}#{self.ratio.to_s.delete('/')}#{self.gap}"
|
30
|
+
self.desc = "#{self.unit} mm x-height with #{self.ratio} ratio and #{self.gap} mm gap"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module InstanceMethods
|
36
|
+
def scribes
|
37
|
+
param.x_height = 1.0
|
38
|
+
param.height = param.hlines.size > 2 ? 2 * param.ratio / (param.hlines.size - 2) : 1
|
39
|
+
|
40
|
+
lines
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module Bundle
|
45
|
+
def quartet
|
46
|
+
hline :ascender, after: proc { height },
|
47
|
+
style: Style.(stroke: 'blue', 'stroke-width': '0.2')
|
48
|
+
hline :waist, after: proc { x_height },
|
49
|
+
style: Style.(stroke: 'grey', 'stroke-width': '0.1', 'stroke-dasharray': '2, 2')
|
50
|
+
hline :base, after: proc { height },
|
51
|
+
style: Style.(stroke: 'red', 'stroke-width': '0.1', 'stroke-dasharray': '2, 2')
|
52
|
+
hline :descender, after: proc { gap },
|
53
|
+
style: Style.(stroke: 'blue', 'stroke-width': '0.2')
|
54
|
+
end
|
55
|
+
|
56
|
+
# rubocop:disable Metrics/MethodLength
|
57
|
+
# codebeat:disable[ABC]
|
58
|
+
def sextet
|
59
|
+
hline :ascender2, after: proc { height },
|
60
|
+
style: Style.(stroke: 'blue', 'stroke-width': '0.2')
|
61
|
+
hline :ascender1, after: proc { height },
|
62
|
+
style: Style.(stroke: 'grey', 'stroke-width': '0.1', 'stroke-dasharray': '2, 2')
|
63
|
+
hline :waist, after: proc { x_height },
|
64
|
+
style: Style.(stroke: 'grey', 'stroke-width': '0.1')
|
65
|
+
hline :base, after: proc { height },
|
66
|
+
style: Style.(stroke: 'red', 'stroke-width': '0.1')
|
67
|
+
hline :descender1, after: proc { height },
|
68
|
+
style: Style.(stroke: 'grey', 'stroke-width': '0.1', 'stroke-dasharray': '2, 2')
|
69
|
+
hline :descender2, after: proc { gap },
|
70
|
+
style: Style.(stroke: 'blue', 'stroke-width': '0.2')
|
71
|
+
end
|
72
|
+
# codebeat:enable[ABC]
|
73
|
+
# rubocop:enable Metrics/MethodLength
|
74
|
+
end
|
75
|
+
|
76
|
+
def included(base)
|
77
|
+
base.include Lines
|
78
|
+
|
79
|
+
base.extend ClassMethods
|
80
|
+
base.include InstanceMethods
|
81
|
+
|
82
|
+
base.extend Bundle
|
83
|
+
|
84
|
+
base.variate_scribes(unit: @unit, ratio: @ratio, gap: @gap) if @unit
|
85
|
+
|
86
|
+
base.send(@type)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dieses
|
4
|
+
module Application
|
5
|
+
module Mixins
|
6
|
+
module Squares
|
7
|
+
def squares(unit: Undefined, multiple: Undefined)
|
8
|
+
param = self.param
|
9
|
+
|
10
|
+
draw unit: Undefined.default(unit, param.unit), multiple: Undefined.default(multiple, param.multiple) do
|
11
|
+
repeat do
|
12
|
+
repeat do
|
13
|
+
square :square, width: multiple, style: Style.(stroke: 'blue', 'stroke-width': '0.2', fill: 'none')
|
14
|
+
right multiple
|
15
|
+
end
|
16
|
+
down multiple
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module Dieses
|
6
|
+
module Application
|
7
|
+
module Paper
|
8
|
+
Dim = Struct.new(*%i[width height], keyword_init: true) do
|
9
|
+
def self.call(width, height)
|
10
|
+
new width: width, height: height
|
11
|
+
end
|
12
|
+
|
13
|
+
def short
|
14
|
+
values.min
|
15
|
+
end
|
16
|
+
|
17
|
+
def long
|
18
|
+
values.max
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Margin = Struct.new(*%i[top right bottom left], keyword_init: true) do # in CSS margin order
|
23
|
+
def self.call(*args)
|
24
|
+
values = case args.size
|
25
|
+
when 1 then Array.new(members.size, *args)
|
26
|
+
when members.size then args
|
27
|
+
else raise ArgumentError, "Incorrect number of arguments: #{args}"
|
28
|
+
end
|
29
|
+
|
30
|
+
new(Hash[*members.zip(values).flatten])
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.build(dim, short:, long:)
|
34
|
+
kwargs = if dim.height > dim.width
|
35
|
+
{ top: long, right: short, bottom: long, left: short }
|
36
|
+
else
|
37
|
+
{ top: short, right: long, bottom: short, left: long }
|
38
|
+
end
|
39
|
+
|
40
|
+
new(**kwargs)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
Variant = Struct.new :type, :name, :width, :height, :floor, :scale, keyword_init: true do
|
45
|
+
def build # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
46
|
+
dim = Dim.(width, height)
|
47
|
+
|
48
|
+
if scale.nil? || (short = Support.approx(dim.short * scale)) < floor
|
49
|
+
short = floor
|
50
|
+
scale = floor / dim.short
|
51
|
+
end
|
52
|
+
|
53
|
+
long = Support.approx(dim.long * scale)
|
54
|
+
margin = Margin.build(dim, short: short, long: long)
|
55
|
+
|
56
|
+
variant = self
|
57
|
+
|
58
|
+
Paper.define_singleton_method(name) do |**kwargs|
|
59
|
+
klass = Class.new(Instance)
|
60
|
+
|
61
|
+
klass.type variant.type
|
62
|
+
klass.variant variant.name
|
63
|
+
|
64
|
+
klass.new(*dim.values, **margin.to_h.merge(**kwargs))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module DSL
|
70
|
+
VARIANTS = [
|
71
|
+
{ suffix: 'n', floor: 7.0 }, # Narrow margins (default)
|
72
|
+
{ suffix: 'm', floor: 12.0 }, # Medium margins: bare minimum margin to accommodate ISO 838 filing holes
|
73
|
+
{ suffix: 'w', floor: 20.0 } # Wide margins: safe minimum margin to accommodate ISO 838 filing holes
|
74
|
+
].freeze
|
75
|
+
|
76
|
+
def family(type, width:, height:, scale: nil)
|
77
|
+
VARIANTS.map do |hash|
|
78
|
+
name = :"#{type}#{hash[:suffix]}"
|
79
|
+
floor = hash[:floor]
|
80
|
+
Variant.new(type: type, name: name, width: width, height: height, floor: floor, scale: scale).tap(&:build)
|
81
|
+
end.first.tap do |variant| # rubocop:disable Style/MultilineBlockChain
|
82
|
+
# set the first variant as the default paper
|
83
|
+
(class << self; self; end).alias_method type, variant.name
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Instance < DelegateClass(Geometry::Rect)
|
89
|
+
extend Support::ClassAttribute
|
90
|
+
|
91
|
+
define :type, instance_reader: true
|
92
|
+
define :variant, instance_reader: true
|
93
|
+
|
94
|
+
extend Forwardable
|
95
|
+
def_delegators :@margin, *Margin.members
|
96
|
+
|
97
|
+
def initialize(width, height, **margin)
|
98
|
+
@margin = Margin.new(**margin)
|
99
|
+
super(Geometry::Rect.new(width, height))
|
100
|
+
end
|
101
|
+
|
102
|
+
def inner
|
103
|
+
@inner ||= shrink(width: left + right, height: top + bottom)
|
104
|
+
end
|
105
|
+
|
106
|
+
def orient(orientation)
|
107
|
+
self.class.new((rect = super).width, rect.height, **margin.to_h)
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_h
|
111
|
+
super.merge(margin.to_h)
|
112
|
+
end
|
113
|
+
|
114
|
+
protected
|
115
|
+
|
116
|
+
attr_reader :margin
|
117
|
+
end
|
118
|
+
|
119
|
+
extend DSL
|
120
|
+
|
121
|
+
A4 = Dim.new width: 210.0, height: 297.0
|
122
|
+
B4 = Dim.new width: 250.0, height: 353.0
|
123
|
+
US = Dim.new width: 215.9, height: 279.4
|
124
|
+
|
125
|
+
family :a3, width: A4.height, height: A4.width * 2
|
126
|
+
family :a4, width: A4.width, height: A4.height
|
127
|
+
family :a5, width: A4.height / 2, height: A4.width
|
128
|
+
family :a6, width: A4.width / 2, height: A4.height / 2
|
129
|
+
|
130
|
+
family :b3, width: B4.height, height: B4.width * 2
|
131
|
+
family :b4, width: B4.width, height: B4.height
|
132
|
+
family :b5, width: B4.height / 2, height: B4.width
|
133
|
+
family :b6, width: B4.width / 2, height: B4.height / 2
|
134
|
+
|
135
|
+
family :us, width: US.width, height: US.height
|
136
|
+
|
137
|
+
class << self
|
138
|
+
alias letter us
|
139
|
+
|
140
|
+
def default
|
141
|
+
:a4
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dieses
|
4
|
+
module Application
|
5
|
+
class Pen
|
6
|
+
attr_reader :canvas
|
7
|
+
|
8
|
+
def initialize(canvas)
|
9
|
+
@canvas = canvas
|
10
|
+
end
|
11
|
+
|
12
|
+
def draw(unit:, multiple: nil, &block)
|
13
|
+
Draw.(self, Ruler.(unit, multiple), &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
Ruler = Struct.new :unit, :multiple do
|
17
|
+
def self.call(unit, multiple = nil)
|
18
|
+
new unit, (multiple || 1)
|
19
|
+
end
|
20
|
+
|
21
|
+
def major
|
22
|
+
@major ||= multiple * unit
|
23
|
+
end
|
24
|
+
|
25
|
+
def even(length)
|
26
|
+
major * (length / major).to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
def measure(n)
|
30
|
+
n * unit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Draw
|
35
|
+
extend Forwardable
|
36
|
+
|
37
|
+
def_delegators :@pen, :canvas
|
38
|
+
def_delegators :@buffer, :<<
|
39
|
+
def_delegators :@ruler, :unit, :multiple
|
40
|
+
|
41
|
+
def self.call(pen, ruler, &block)
|
42
|
+
Draw.new(pen, ruler).(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(pen, ruler, pos: Undefined)
|
46
|
+
@pen = pen
|
47
|
+
@pos = Undefined.default(pos, Geometry::Point::Mutable.cast(canvas.position))
|
48
|
+
@ruler = ruler
|
49
|
+
@buffer = Set.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def repeat(count = nil, &block)
|
53
|
+
self.class.new(pen, ruler, pos: pos.dup).instance_exec do
|
54
|
+
1.step(count) do
|
55
|
+
prev = pos.dup
|
56
|
+
instance_exec(&block)
|
57
|
+
put
|
58
|
+
|
59
|
+
break if pos == prev || perfect.outside?(pos)
|
60
|
+
rescue Offsite
|
61
|
+
break
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def call(&block)
|
67
|
+
instance_exec(&block)
|
68
|
+
put
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
attr_reader :pen, :pos, :ruler, :buffer
|
74
|
+
|
75
|
+
def perfect
|
76
|
+
@perfect ||= Geometry::Rect.new(ruler.even(canvas.width), ruler.even(canvas.height))
|
77
|
+
end
|
78
|
+
|
79
|
+
Offsite = Class.new StopIteration
|
80
|
+
|
81
|
+
module Elements
|
82
|
+
def hline(tag = Undefined, length: Undefined, style: Undefined)
|
83
|
+
length = Undefined.equal?(length) ? perfect.width : ruler.measure(length)
|
84
|
+
add Geometry::Line.new(pos, pos.translate(x: length)), tag, style
|
85
|
+
end
|
86
|
+
|
87
|
+
def vline(tag = Undefined, length: Undefined, style: Undefined)
|
88
|
+
length = Undefined.equal?(length) ? perfect.height : ruler.measure(length)
|
89
|
+
add Geometry::Line.new(pos, pos.translate(y: length)), tag, style
|
90
|
+
end
|
91
|
+
|
92
|
+
def cline(tag = Undefined, angle:, style: Undefined)
|
93
|
+
add perfect.intersect(Geometry::Equation.slant_from_direction(point: pos, angle: -angle)), tag, style
|
94
|
+
end
|
95
|
+
|
96
|
+
def rect(tag = Undefined, width:, height:, style: Undefined)
|
97
|
+
width, height = ruler.measure(width), ruler.measure(height)
|
98
|
+
add Geometry::Rect.new(width, height, position: pos), tag, style
|
99
|
+
end
|
100
|
+
|
101
|
+
def square(tag = Undefined, width:, style: Undefined)
|
102
|
+
rect(tag, width: width, height: width, style: style)
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
BASE_STYLE = { stroke: 'black', 'stroke-width': '0.1' }.freeze
|
108
|
+
|
109
|
+
def add(element, tag, style)
|
110
|
+
raise Offsite unless element && perfect.cover?(element)
|
111
|
+
|
112
|
+
tag = Undefined.default(tag, caller_locations(1, 1).first.label.to_sym)
|
113
|
+
|
114
|
+
element.tap do
|
115
|
+
buffer << element.classify(tag, **BASE_STYLE.merge(Undefined.default(style, EMPTY_HASH).to_h))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def put
|
120
|
+
canvas.tap do
|
121
|
+
buffer.each { |element| canvas << element }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module Movements
|
127
|
+
def move(x: Undefined, y: Undefined)
|
128
|
+
tap do
|
129
|
+
pos.translate!(x: x * ruler.unit) unless Undefined.equal?(x)
|
130
|
+
pos.translate!(y: y * ruler.unit) unless Undefined.equal?(y)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def up(y = 1)
|
135
|
+
move y: -y
|
136
|
+
end
|
137
|
+
|
138
|
+
def down(y = 1)
|
139
|
+
move y: y
|
140
|
+
end
|
141
|
+
|
142
|
+
def left(x = 1)
|
143
|
+
move x: -x
|
144
|
+
end
|
145
|
+
|
146
|
+
def right(x = 1)
|
147
|
+
move x: x
|
148
|
+
end
|
149
|
+
|
150
|
+
def cross(distance = 1, angle: Undefined)
|
151
|
+
radian = Undefined.equal?(angle) ? perfect.angle : Support.to_radian(angle)
|
152
|
+
move x: distance * Math.cos(radian), y: distance * Math.sin(radian)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
include Elements
|
157
|
+
include Movements
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|