ea 0.6.114 → 0.6.116
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/config/themes/119.yml +1 -1
- data/lib/ea/sources/qea/diagram_builder.rb +13 -0
- data/lib/ea/svg/ea_emitter/compartment/shape.rb +11 -6
- data/lib/ea/svg/ea_emitter/connector_redock.rb +23 -7
- data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +8 -4
- data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +5 -2
- data/lib/ea/svg/ea_emitter/elements.rb +4 -2
- data/lib/ea/svg/ea_emitter/font_resolver.rb +3 -1
- data/lib/ea/svg/ea_emitter/label/end_label.rb +19 -2
- data/lib/ea/svg/ea_emitter/labels.rb +14 -1
- data/lib/ea/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 21ccebe3cc90560d4079f23a067cb41d94cbdeb87bc07873406edc97dd414c71
|
|
4
|
+
data.tar.gz: f9c39d517836ec0c8a6f737761e9f7fbc47ca49f4cfe2256aeccccfa38df4659
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 60a5c35a8607a8169f6e7be4866c07972b37e75f790851e36191ea4ec93a9ac8c793589fedccdbcacef456d44d96dd791687a6deb54ac7943d7d547060685072
|
|
7
|
+
data.tar.gz: b6cef3d3516f3c290ec2c1a05f138213bd3a7f2b18f0f063113f22865c89835804977c733b22f60819d7004c998dd9b98b7b244da8c70505bfb0873d1f184c7b
|
data/config/themes/119.yml
CHANGED
|
@@ -144,10 +144,23 @@ module Ea
|
|
|
144
144
|
font_italic: truthy?(style_hash[:italic]),
|
|
145
145
|
font_underline: truthy?(style_hash[:ul]),
|
|
146
146
|
show_tagged_values: truthy?(style_hash[:tag]),
|
|
147
|
+
line_width: effective_line_width(obj_row, style_hash),
|
|
147
148
|
background_color: style_hash[:bcol]&.to_i
|
|
148
149
|
)
|
|
149
150
|
end
|
|
150
151
|
|
|
152
|
+
# Negative widths inherit the saved object border, then the diagram theme.
|
|
153
|
+
def effective_line_width(obj_row, style)
|
|
154
|
+
object = database.find_object(obj_row.ea_object_id)
|
|
155
|
+
[style[:lwth], object&.borderwidth].each do |raw|
|
|
156
|
+
next if raw.nil? || raw.to_s.empty?
|
|
157
|
+
|
|
158
|
+
width = raw.to_i
|
|
159
|
+
return width if width >= 0
|
|
160
|
+
end
|
|
161
|
+
nil
|
|
162
|
+
end
|
|
163
|
+
|
|
151
164
|
def truthy?(raw)
|
|
152
165
|
raw == "1" || raw == "-1"
|
|
153
166
|
end
|
|
@@ -16,12 +16,17 @@ module Ea
|
|
|
16
16
|
legend: context.model_element.legend,
|
|
17
17
|
family: context.family
|
|
18
18
|
)
|
|
19
|
-
elsif context.model_element.is_a?(Ea::Model::
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
elsif context.model_element.is_a?(Ea::Model::Note)
|
|
20
|
+
Element::NoteShapeRenderer.render(
|
|
21
|
+
context.bounds,
|
|
22
|
+
body: "", fill: context.fill, stroke: context.stroke,
|
|
23
|
+
stroke_width: context.stroke_width,
|
|
24
|
+
family: context.family, size: context.size,
|
|
25
|
+
text_fill: context.text_fill
|
|
26
|
+
)
|
|
27
|
+
elsif context.model_element.is_a?(Ea::Model::Package)
|
|
28
|
+
label = package_label(context.model_element)
|
|
29
|
+
stereotype = package_stereotype(context.model_element)
|
|
25
30
|
Element::PackageShapeRenderer.render(
|
|
26
31
|
context.bounds,
|
|
27
32
|
fill: context.fill, stroke: context.stroke,
|
|
@@ -215,7 +215,7 @@ module Ea
|
|
|
215
215
|
private_class_method :overlap_ray?
|
|
216
216
|
|
|
217
217
|
# The QEA builder flags a model relationship drawn without any
|
|
218
|
-
# diagram link row;
|
|
218
|
+
# diagram link row; its docks follow the resolved element borders.
|
|
219
219
|
def default_model_connector?(connector)
|
|
220
220
|
connector.style.to_h[:phantom_outline] == true && !connector.has_geometry_offsets &&
|
|
221
221
|
%w[Aggregation Association].include?(connector.connector_type)
|
|
@@ -243,16 +243,32 @@ module Ea
|
|
|
243
243
|
end
|
|
244
244
|
private_class_method :border_width?
|
|
245
245
|
|
|
246
|
-
#
|
|
247
|
-
# absent or inherited widths retain the default outer-stroke docking.
|
|
246
|
+
# EA seats docks on whole pixels; a 1px border keeps the geometric edge.
|
|
248
247
|
def outline_bounds(box, diagram, ref, duid)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
width: box.width + 2, height: box.height + 2)
|
|
248
|
+
inset = (outline_width(diagram, ref, duid) / 2.0).floor
|
|
249
|
+
Ea::Model::Bounds.new(x: box.x - inset, y: box.y - inset,
|
|
250
|
+
width: box.width + 2 * inset, height: box.height + 2 * inset)
|
|
253
251
|
end
|
|
254
252
|
private_class_method :outline_bounds
|
|
255
253
|
|
|
254
|
+
def outline_width(diagram, ref, duid)
|
|
255
|
+
widths = end_placements(diagram, ref, duid).map do |element|
|
|
256
|
+
effective_border_width(element, diagram)
|
|
257
|
+
end.uniq
|
|
258
|
+
widths.size == 1 ? widths.first : diagram.theme.stroke_width
|
|
259
|
+
end
|
|
260
|
+
private_class_method :outline_width
|
|
261
|
+
|
|
262
|
+
def effective_border_width(element, diagram)
|
|
263
|
+
return element.line_width if element.line_width && element.line_width >= 0
|
|
264
|
+
|
|
265
|
+
raw = element.style.to_h[:lwth]
|
|
266
|
+
return raw.to_i if raw && raw.to_i >= 0
|
|
267
|
+
|
|
268
|
+
diagram.theme.stroke_width
|
|
269
|
+
end
|
|
270
|
+
private_class_method :effective_border_width
|
|
271
|
+
|
|
256
272
|
def end_placements(diagram, ref, duid)
|
|
257
273
|
placed = Array(diagram&.elements).select { |el| el.model_element_ref == ref }
|
|
258
274
|
named = placed.select { |el| duid && el.duid.to_s.casecmp?(duid.to_s) }
|
|
@@ -151,8 +151,8 @@ module Ea
|
|
|
151
151
|
|
|
152
152
|
# Real font metrics when available; legacy per-char
|
|
153
153
|
# estimate otherwise (families without metric tables).
|
|
154
|
-
def estimated_width(text, size)
|
|
155
|
-
measured = Ea::Fonts::Metrics.text_length(text, size, family: family)
|
|
154
|
+
def estimated_width(text, size, weight: nil)
|
|
155
|
+
measured = Ea::Fonts::Metrics.text_length(text, size, family: family, weight: weight)
|
|
156
156
|
return measured if measured
|
|
157
157
|
|
|
158
158
|
text.to_s.each_char.sum do |ch|
|
|
@@ -169,7 +169,11 @@ module Ea
|
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
def title_font_size
|
|
172
|
-
|
|
172
|
+
return Ea::Model::Legend::DEFAULT_HEADING_SIZE unless legend.heading_size
|
|
173
|
+
|
|
174
|
+
# HEADINGSIZE is stored in pixels; SVG legend text uses points.
|
|
175
|
+
points = legend.heading_size * 72.0 / 96
|
|
176
|
+
points == points.to_i ? points.to_i : points
|
|
173
177
|
end
|
|
174
178
|
|
|
175
179
|
def item_font_size
|
|
@@ -177,7 +181,7 @@ module Ea
|
|
|
177
181
|
end
|
|
178
182
|
|
|
179
183
|
def title_text_width
|
|
180
|
-
estimated_width(legend.title, title_font_size)
|
|
184
|
+
estimated_width(legend.title, title_font_size, weight: 700)
|
|
181
185
|
end
|
|
182
186
|
|
|
183
187
|
def build_text(content, x:, y:, size:, weight:)
|
|
@@ -70,13 +70,16 @@ module Ea
|
|
|
70
70
|
%(<path d="#{d}" shape-rendering="auto"/>)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
#
|
|
73
|
+
# Inner edges of the folded corner; the outer path supplies the diagonal.
|
|
74
74
|
def fold_line
|
|
75
75
|
x = bounds.x
|
|
76
76
|
y = bounds.y
|
|
77
77
|
w = bounds.width
|
|
78
78
|
fold = [FOLD_SIZE, w / 3, bounds.height / 3].min
|
|
79
|
-
|
|
79
|
+
d = "M #{Canvas.coord(x + w - fold)} #{Canvas.coord(y)} " \
|
|
80
|
+
"L #{Canvas.coord(x + w - fold)} #{Canvas.coord(y + fold)} " \
|
|
81
|
+
"L #{Canvas.coord(x + w)} #{Canvas.coord(y + fold)}"
|
|
82
|
+
%(<path d="#{d}" shape-rendering="auto"/>)
|
|
80
83
|
end
|
|
81
84
|
|
|
82
85
|
def text_block
|
|
@@ -273,7 +273,7 @@ module Ea
|
|
|
273
273
|
classifier: classifier,
|
|
274
274
|
fill: resolve_fill(element, classifier),
|
|
275
275
|
stroke: resolve_stroke,
|
|
276
|
-
stroke_width: resolve_stroke_width,
|
|
276
|
+
stroke_width: resolve_stroke_width(element),
|
|
277
277
|
text_fill: theme.text_color,
|
|
278
278
|
family: family, size: size, size_unit: size_unit, logical_height: logical_height,
|
|
279
279
|
header_lines: header_lines,
|
|
@@ -670,7 +670,9 @@ module Ea
|
|
|
670
670
|
color_resolver.stroke_for(nil)
|
|
671
671
|
end
|
|
672
672
|
|
|
673
|
-
def resolve_stroke_width
|
|
673
|
+
def resolve_stroke_width(element)
|
|
674
|
+
return element.line_width if element.line_width && element.line_width >= 0
|
|
675
|
+
|
|
674
676
|
theme.themed? ? theme.stroke_width : DEFAULT_STROKE_WIDTH
|
|
675
677
|
end
|
|
676
678
|
|
|
@@ -102,7 +102,9 @@ module Ea
|
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def element_families
|
|
105
|
-
(diagram.elements || []).map(&:font_family).compact
|
|
105
|
+
(diagram.elements || []).map(&:font_family).compact.reject do |family|
|
|
106
|
+
family.empty? || DEFAULT_MARKER_FAMILIES.include?(family)
|
|
107
|
+
end
|
|
106
108
|
end
|
|
107
109
|
|
|
108
110
|
def tally_values(values)
|
|
@@ -112,6 +112,8 @@ module Ea
|
|
|
112
112
|
source_bottom_property_role(connector, heading, anchor, text_box, role) || text_pos
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
+
text_box = text_box.merge("alignment" => 1) if phantom_property_box?(connector, direction, text_box)
|
|
116
|
+
|
|
115
117
|
build_texts(role:, mult:, standalone:,
|
|
116
118
|
text_pos:, mult_pos:, stereo:, text_box:)
|
|
117
119
|
end
|
|
@@ -194,8 +196,7 @@ module Ea
|
|
|
194
196
|
# Property-role clusters use a direction-specific horizontal seat;
|
|
195
197
|
# alignment controls the stereotype line within that cluster.
|
|
196
198
|
def source_property_seat(connector, direction, anchor, box, role)
|
|
197
|
-
return nil unless
|
|
198
|
-
return nil unless source_property_font?(connector, direction)
|
|
199
|
+
return nil unless source_property_seat?(connector, direction, box)
|
|
199
200
|
|
|
200
201
|
width = Ea::Fonts::Metrics.text_length(role, font_size, family: font_family)
|
|
201
202
|
return nil unless width
|
|
@@ -207,6 +208,22 @@ module Ea
|
|
|
207
208
|
[left, y + baseline]
|
|
208
209
|
end
|
|
209
210
|
|
|
211
|
+
def source_property_seat?(connector, direction, box)
|
|
212
|
+
phantom_property_box?(connector, direction, box) ||
|
|
213
|
+
(%i[up left right down].include?(direction) && property_role_box?(box, direction) &&
|
|
214
|
+
source_property_font?(connector, direction))
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Phantom outlines carry synthesized zero boxes without alignment.
|
|
218
|
+
# Their default Property cluster uses the directional seat with a
|
|
219
|
+
# centered stereotype; persisted or moved boxes keep their own seat.
|
|
220
|
+
def phantom_property_box?(connector, direction, box)
|
|
221
|
+
%i[up left].include?(direction) && seat_font? && cambria_box?(box) &&
|
|
222
|
+
box["alignment"].nil? && stored_offsets_zero?(box) &&
|
|
223
|
+
connector.style.to_h[:phantom_outline] == true &&
|
|
224
|
+
property_label?(connector, :source)
|
|
225
|
+
end
|
|
226
|
+
|
|
210
227
|
def property_role_x(direction, origin, offset)
|
|
211
228
|
direction == :right ? origin + offset : origin - offset
|
|
212
229
|
end
|
|
@@ -128,7 +128,7 @@ module Ea
|
|
|
128
128
|
return heading unless points.size == 2 && heading.none?(&:zero?)
|
|
129
129
|
|
|
130
130
|
box = source_dock_bounds(connector)
|
|
131
|
-
face = box &&
|
|
131
|
+
face = box && source_dock_heading(connector, points.first, box)
|
|
132
132
|
return face if face && face_leads_leg?(connector, face, heading, box)
|
|
133
133
|
|
|
134
134
|
stored_upward_heading(connector, heading)
|
|
@@ -182,6 +182,19 @@ module Ea
|
|
|
182
182
|
drawn_bounds[source]
|
|
183
183
|
end
|
|
184
184
|
|
|
185
|
+
# Saved rays can start outside the box. Project onto its boundary;
|
|
186
|
+
# corners stay ambiguous and the face must still lead the emitted leg.
|
|
187
|
+
def source_dock_heading(connector, point, box)
|
|
188
|
+
return dock_face_heading(point, box) unless connector.style.to_h[:association_ray_outline] == true
|
|
189
|
+
return nil unless box.width.positive? && box.height.positive?
|
|
190
|
+
|
|
191
|
+
dock_face_heading(projected_dock_point(point, box), box)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def projected_dock_point(point, box)
|
|
195
|
+
[point[0].clamp(box.x, box.x + box.width), point[1].clamp(box.y, box.y + box.height)]
|
|
196
|
+
end
|
|
197
|
+
|
|
185
198
|
def dock_face_heading(point, box)
|
|
186
199
|
return nil unless box.width.positive? && box.height.positive?
|
|
187
200
|
|
data/lib/ea/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ea
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.116
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|