ea 0.6.122 → 0.6.123
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 +4 -4
- data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +22 -18
- data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +10 -1
- data/lib/ea/svg/ea_emitter/element/header_renderer.rb +44 -29
- data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +14 -2
- data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +17 -5
- data/lib/ea/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d897b1e2165c9993420958c081ee3e2eedebbfbb8ac4db1ea8f160fd45913a3d
|
|
4
|
+
data.tar.gz: 667e5cce39c543c97f96fd7b64c994d72808daf3bf6a875ae112e0efe4b2623b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e8789c4187a4c72534f2e4d17f1caf650e7da1748aa08bed49764fa6fde0dafdfc1bf8c6811ab7a33e31b6da1ffe0a71397b6ca60b1a8c2e7ee0e65a4798368d
|
|
7
|
+
data.tar.gz: 37bf77d8c14e921888081ee961506f835b5cd0de1bb28de790d5ba848003915922e83c83e2b29d6db0f58141f25dc1d643059f642950ee6cd2028da87d42b42f
|
|
@@ -5,12 +5,10 @@ module Ea
|
|
|
5
5
|
module EaEmitter
|
|
6
6
|
module Compartment
|
|
7
7
|
# Instance-name underline for object diagrams. EA draws a
|
|
8
|
-
# short horizontal line
|
|
9
|
-
#
|
|
10
|
-
# only when the diagram's `diagram_type` is "Object".
|
|
8
|
+
# short horizontal line below each instance name to denote
|
|
9
|
+
# that it is an instance (UML convention).
|
|
11
10
|
module InstanceUnderline
|
|
12
|
-
Y_OFFSET =
|
|
13
|
-
WIDTH_FACTOR = 0.65
|
|
11
|
+
Y_OFFSET = 1
|
|
14
12
|
|
|
15
13
|
module_function
|
|
16
14
|
|
|
@@ -19,11 +17,22 @@ module Ea
|
|
|
19
17
|
return nil unless object_diagram?(context)
|
|
20
18
|
return nil if context.header_lines.empty?
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
header = Element::HeaderRenderer.line_layout(
|
|
21
|
+
context.header_lines,
|
|
22
|
+
bounds: context.bounds,
|
|
23
|
+
first_y: context.geometry.header_first_y,
|
|
24
|
+
family: context.family,
|
|
25
|
+
size: context.size,
|
|
26
|
+
size_unit: context.size_unit,
|
|
27
|
+
logical_height: context.logical_height,
|
|
28
|
+
weight_normal: context.theme.text_weight_normal,
|
|
29
|
+
weight_bold: context.theme.text_weight_bold,
|
|
30
|
+
width_factor: context.theme.text_width_factor,
|
|
31
|
+
line_offset: context.theme.compartments.header_line_offset
|
|
32
|
+
).last
|
|
33
|
+
return nil unless header && !header.text.to_s.empty?
|
|
25
34
|
|
|
26
|
-
underline_path(
|
|
35
|
+
underline_path(header)
|
|
27
36
|
end
|
|
28
37
|
|
|
29
38
|
def object_diagram?(context)
|
|
@@ -31,18 +40,13 @@ module Ea
|
|
|
31
40
|
end
|
|
32
41
|
module_function :object_diagram?
|
|
33
42
|
|
|
34
|
-
def underline_path(
|
|
35
|
-
|
|
36
|
-
text_width = TextRenderer.estimate_width(text, context.size,
|
|
37
|
-
WIDTH_FACTOR)
|
|
38
|
-
x_start = bounds.x + (bounds.width - text_width) / 2.0
|
|
39
|
-
y_pos = context.geometry.header_first_y + Y_OFFSET
|
|
40
|
-
build_path_tag(x_start, y_pos, text_width)
|
|
43
|
+
def underline_path(header)
|
|
44
|
+
build_path_tag(header.x, header.y + Y_OFFSET, header.text_length)
|
|
41
45
|
end
|
|
42
46
|
module_function :underline_path
|
|
43
47
|
|
|
44
|
-
def build_path_tag(x_start, y_pos,
|
|
45
|
-
x_end = x_start +
|
|
48
|
+
def build_path_tag(x_start, y_pos, text_length)
|
|
49
|
+
x_end = x_start + text_length
|
|
46
50
|
coords = format_coords(x_start, y_pos, x_end)
|
|
47
51
|
d = "M #{coords[:x1]} #{coords[:y1]} L #{coords[:x2]} #{coords[:y1]}"
|
|
48
52
|
style = "stroke-width:1;stroke-linecap:square;" \
|
|
@@ -14,6 +14,7 @@ module Ea
|
|
|
14
14
|
ROW_HEIGHT = 16
|
|
15
15
|
ICON_X_OFFSET = 5
|
|
16
16
|
TEXT_X_OFFSET = 23
|
|
17
|
+
FIRST_BASELINE_OFFSET = 15
|
|
17
18
|
|
|
18
19
|
module_function
|
|
19
20
|
|
|
@@ -22,7 +23,7 @@ module Ea
|
|
|
22
23
|
return nil if rows.nil? || rows.empty?
|
|
23
24
|
|
|
24
25
|
bounds = context.bounds
|
|
25
|
-
first_y = context
|
|
26
|
+
first_y = first_baseline(context)
|
|
26
27
|
blocks = rows.each_with_index.flat_map do |row, idx|
|
|
27
28
|
y_top = first_y - 11 + (idx * ROW_HEIGHT)
|
|
28
29
|
y_text = first_y + (idx * ROW_HEIGHT)
|
|
@@ -34,6 +35,14 @@ module Ea
|
|
|
34
35
|
wrap_group(blocks.join("\n"))
|
|
35
36
|
end
|
|
36
37
|
|
|
38
|
+
def first_baseline(context)
|
|
39
|
+
context.bounds.y + Element::PackageShapeRenderer.tab_height_for(
|
|
40
|
+
stereotype: Shape.package_stereotype(context.model_element),
|
|
41
|
+
family: context.family, size: context.size, size_unit: context.size_unit
|
|
42
|
+
) + FIRST_BASELINE_OFFSET
|
|
43
|
+
end
|
|
44
|
+
private_class_method :first_baseline
|
|
45
|
+
|
|
37
46
|
def wrap_group(body)
|
|
38
47
|
%(<g style="#{group_style}">\n#{body}\n</g>)
|
|
39
48
|
end
|
|
@@ -11,55 +11,70 @@ module Ea
|
|
|
11
11
|
# the left edge from text-width approximation since SVG text
|
|
12
12
|
# x= is the start position, not the center).
|
|
13
13
|
class HeaderRenderer
|
|
14
|
+
HeaderLine = Struct.new(:text, :style, :x, :y, :text_length,
|
|
15
|
+
:weight, :font_style, keyword_init: true)
|
|
16
|
+
|
|
14
17
|
def self.render(lines, bounds:, first_y:, family:,
|
|
15
18
|
size:, size_unit: "pt", logical_height: nil,
|
|
16
19
|
fill: "#000000", weight_normal: 400, weight_bold: 700,
|
|
17
20
|
stroke_in_text: "#000000", width_factor: 0.612,
|
|
18
21
|
line_offset: 6)
|
|
19
22
|
logical_height = nil unless size_unit == "pt"
|
|
23
|
+
layout = line_layout(
|
|
24
|
+
lines, bounds: bounds, first_y: first_y, family: family,
|
|
25
|
+
size: size, size_unit: size_unit, logical_height: logical_height,
|
|
26
|
+
weight_normal: weight_normal, weight_bold: weight_bold,
|
|
27
|
+
width_factor: width_factor, line_offset: line_offset
|
|
28
|
+
)
|
|
29
|
+
text_blocks = layout.map do |line|
|
|
30
|
+
TextRenderer.new(
|
|
31
|
+
content: line.text,
|
|
32
|
+
x: line.x, y: line.y,
|
|
33
|
+
family: family, size: size, size_unit: size_unit, logical_height: logical_height,
|
|
34
|
+
weight: line.weight, style: line.font_style, fill: fill,
|
|
35
|
+
stroke_in_text: stroke_in_text, width_factor: width_factor,
|
|
36
|
+
text_length: line.text_length
|
|
37
|
+
).to_svg
|
|
38
|
+
end
|
|
39
|
+
%(<g style="#{Style::TEXT_GROUP}">\n#{text_blocks.join("\n")}\n</g>)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Returns the exact integer placement and width used for each
|
|
43
|
+
# emitted header line so dependent compartments can align to it.
|
|
44
|
+
def self.line_layout(lines, bounds:, first_y:, family:, size:,
|
|
45
|
+
size_unit: "pt", logical_height: nil,
|
|
46
|
+
weight_normal: 400, weight_bold: 700,
|
|
47
|
+
width_factor: 0.612, line_offset: 6)
|
|
48
|
+
logical_height = nil unless size_unit == "pt"
|
|
20
49
|
line_h = Ea::Fonts::Metrics.compartment_line_height(
|
|
21
|
-
size, family: family, unit: size_unit, offset: line_offset,
|
|
50
|
+
size, family: family, unit: size_unit, offset: line_offset,
|
|
51
|
+
logical_height: logical_height
|
|
22
52
|
)
|
|
23
53
|
ghost_shift = HeaderLines.parent_ghost_gap(lines)
|
|
24
|
-
|
|
25
|
-
weight =
|
|
26
|
-
when :bold, :bold_italic then weight_bold
|
|
27
|
-
else weight_normal
|
|
28
|
-
end
|
|
54
|
+
lines.each_with_index.map do |(text, style), idx|
|
|
55
|
+
weight = %i[bold bold_italic].include?(style) ? weight_bold : weight_normal
|
|
29
56
|
font_style = %i[italic italic_tight bold_italic].include?(style) ? "italic" : "normal"
|
|
57
|
+
text_length = TextRenderer.estimate_width(
|
|
58
|
+
text, size, width_factor, family: family, weight: weight,
|
|
59
|
+
style: font_style, logical_height: logical_height
|
|
60
|
+
).round
|
|
30
61
|
y = first_y + (idx * line_h) +
|
|
31
62
|
(idx >= 1 && ghost_shift.positive? ? ghost_shift : 0)
|
|
32
63
|
x = if %i[italic italic_tight].include?(style)
|
|
33
|
-
|
|
34
|
-
bounds.x + bounds.width - 3 -
|
|
35
|
-
Ea::Svg::EaEmitter::TextRenderer.estimate_width(
|
|
36
|
-
text, size, width_factor,
|
|
37
|
-
family: family, weight: weight, style: font_style, logical_height: logical_height
|
|
38
|
-
).round
|
|
64
|
+
bounds.x + bounds.width - 3 - text_length
|
|
39
65
|
else
|
|
40
|
-
center_x_for(
|
|
41
|
-
family: family, weight: weight,
|
|
42
|
-
style: font_style, logical_height: logical_height)
|
|
66
|
+
center_x_for(text_length, bounds)
|
|
43
67
|
end
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
family: family, size: size, size_unit: size_unit, logical_height: logical_height,
|
|
48
|
-
weight: weight, style: font_style, fill: fill,
|
|
49
|
-
stroke_in_text: stroke_in_text, width_factor: width_factor
|
|
50
|
-
).to_svg
|
|
68
|
+
HeaderLine.new(text: text, style: style, x: x, y: y,
|
|
69
|
+
text_length: text_length, weight: weight,
|
|
70
|
+
font_style: font_style)
|
|
51
71
|
end
|
|
52
|
-
%(<g style="#{Style::TEXT_GROUP}">\n#{text_blocks.join("\n")}\n</g>)
|
|
53
72
|
end
|
|
54
73
|
|
|
55
74
|
# EA centers text by its INTEGER textLength and floors the
|
|
56
75
|
# resulting x (verified exact against EA-published SVGs).
|
|
57
|
-
def self.center_x_for(
|
|
58
|
-
|
|
59
|
-
len = Ea::Svg::EaEmitter::TextRenderer.estimate_width(
|
|
60
|
-
text, size, width_factor, family: family, weight: weight, style: style, logical_height: logical_height
|
|
61
|
-
).round
|
|
62
|
-
(bounds.x + (bounds.width - len) / 2.0).floor
|
|
76
|
+
def self.center_x_for(text_length, bounds)
|
|
77
|
+
(bounds.x + (bounds.width - text_length) / 2.0).floor
|
|
63
78
|
end
|
|
64
79
|
private_class_method :center_x_for
|
|
65
80
|
end
|
|
@@ -26,9 +26,10 @@ module Ea
|
|
|
26
26
|
text_blocks = []
|
|
27
27
|
has_receptions = operations.any?(&:is_reception)
|
|
28
28
|
if has_receptions
|
|
29
|
+
heading_width = reception_heading_width(family, size, size_unit, logical_height)
|
|
29
30
|
text_blocks << TextRenderer.new(
|
|
30
31
|
content: "receptions",
|
|
31
|
-
x: bounds.x + (bounds.width / 2.0) -
|
|
32
|
+
x: bounds.x + (bounds.width / 2.0) - heading_width / 2.0,
|
|
32
33
|
y: first_y,
|
|
33
34
|
family: family, size: size, size_unit: size_unit, logical_height: logical_height,
|
|
34
35
|
style: "italic", fill: "#000000"
|
|
@@ -36,12 +37,14 @@ module Ea
|
|
|
36
37
|
end
|
|
37
38
|
ops = has_receptions ? operations : operations
|
|
38
39
|
offset = has_receptions ? 1 : 0
|
|
40
|
+
content_x_offset = CONTENT_X_OFFSET + 2 * (size - 7)
|
|
41
|
+
content_x_offset -= 1 if Ea::Fonts::Metrics.compact_compartment_font?(size, family: family, unit: size_unit)
|
|
39
42
|
ops.each_with_index do |op, idx|
|
|
40
43
|
y = first_y + ((idx + offset) * line_h)
|
|
41
44
|
text_blocks << build_text(bounds.x + VISIBILITY_X_OFFSET, y,
|
|
42
45
|
visibility_prefix(op), family, size, size_unit, logical_height: logical_height)
|
|
43
46
|
text = operation_text(op, parameter_detail: parameter_detail)
|
|
44
|
-
text_blocks << build_text(bounds.x +
|
|
47
|
+
text_blocks << build_text(bounds.x + content_x_offset, y,
|
|
45
48
|
text, family, size, size_unit, logical_height: logical_height)
|
|
46
49
|
end
|
|
47
50
|
%(<g style="#{Style::TEXT_GROUP}">\n#{text_blocks.join("\n")}\n</g>)
|
|
@@ -102,6 +105,15 @@ module Ea
|
|
|
102
105
|
end
|
|
103
106
|
private_class_method :reception_params
|
|
104
107
|
|
|
108
|
+
def self.reception_heading_width(family, size, size_unit, logical_height)
|
|
109
|
+
TextRenderer.estimate_width(
|
|
110
|
+
"receptions", size,
|
|
111
|
+
family: family, weight: 400, style: "italic",
|
|
112
|
+
logical_height: size_unit == "pt" ? logical_height : nil
|
|
113
|
+
).round
|
|
114
|
+
end
|
|
115
|
+
private_class_method :reception_heading_width
|
|
116
|
+
|
|
105
117
|
def self.build_text(x, y, content, family, size, size_unit, logical_height: nil)
|
|
106
118
|
TextRenderer.new(content: content, x: x, y: y,
|
|
107
119
|
family: family, size: size, size_unit: size_unit,
|
|
@@ -45,6 +45,21 @@ module Ea
|
|
|
45
45
|
family, size, size_unit, text_fill).to_svg
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def self.tab_height_for(stereotype:, family:, size:, size_unit: "pt")
|
|
49
|
+
rows = stereotype && !stereotype.empty? ? 2 : 1
|
|
50
|
+
nominal_height = rows == 2 ? TAB_HEIGHT_DOUBLE : TAB_HEIGHT_SINGLE
|
|
51
|
+
line_height = if size_unit == "pt"
|
|
52
|
+
Ea::Fonts::Metrics.compartment_line_height(
|
|
53
|
+
size, family: family, unit: size_unit, offset: TAB_TEXT_PADDING
|
|
54
|
+
)
|
|
55
|
+
else
|
|
56
|
+
TAB_LABEL_Y_OFFSET
|
|
57
|
+
end
|
|
58
|
+
# Preserve the nominal padding while each row follows the same
|
|
59
|
+
# font line height used to place the package header text.
|
|
60
|
+
nominal_height + rows * (line_height - TAB_LABEL_Y_OFFSET)
|
|
61
|
+
end
|
|
62
|
+
|
|
48
63
|
def initialize(bounds, fill, stroke, stroke_width, label,
|
|
49
64
|
stereotype, family, size, size_unit, text_fill)
|
|
50
65
|
@bounds = bounds
|
|
@@ -147,11 +162,8 @@ module Ea
|
|
|
147
162
|
end
|
|
148
163
|
|
|
149
164
|
def tab_height
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
# Preserve the nominal padding while each row follows the same
|
|
153
|
-
# font line height used to place the package header text.
|
|
154
|
-
nominal_height + rows * (tab_line_height - TAB_LABEL_Y_OFFSET)
|
|
165
|
+
self.class.tab_height_for(stereotype: stereotype, family: family,
|
|
166
|
+
size: size, size_unit: size_unit)
|
|
155
167
|
end
|
|
156
168
|
|
|
157
169
|
def body_points
|
data/lib/ea/version.rb
CHANGED