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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 251161fffac3b07a12851b0abd26a046998a61b19a9418da8c6d6ceea1cbb09f
4
- data.tar.gz: 549486eec86fb19c6275db6ab7dafdcb7987c1a22b12df2740140e60c7bb2690
3
+ metadata.gz: 21ccebe3cc90560d4079f23a067cb41d94cbdeb87bc07873406edc97dd414c71
4
+ data.tar.gz: f9c39d517836ec0c8a6f737761e9f7fbc47ca49f4cfe2256aeccccfa38df4659
5
5
  SHA512:
6
- metadata.gz: c5cccbc615740b54619be7e36e4c6edb6382768d97b8a120841aca69018b73e693d9dadb8480e78c9e1e0a18ccfc09122668caf9680571496d99b44372c593cd
7
- data.tar.gz: 87a9b79c59a7102d10aaf4968c052f5a63c1e707ef4daf2376d1278ca79b1317e2fc7d47988d57cb09c5abe88d57aeb12b611ba7066e421a1820ce07ac7ff581
6
+ metadata.gz: 60a5c35a8607a8169f6e7be4866c07972b37e75f790851e36191ea4ec93a9ac8c793589fedccdbcacef456d44d96dd791687a6deb54ac7943d7d547060685072
7
+ data.tar.gz: b6cef3d3516f3c290ec2c1a05f138213bd3a7f2b18f0f063113f22865c89835804977c733b22f60819d7004c998dd9b98b7b244da8c70505bfb0873d1f184c7b
@@ -34,7 +34,7 @@ colors:
34
34
 
35
35
  # Stroke widths.
36
36
  strokes:
37
- element_border: 2
37
+ element_border: 1
38
38
  connector_line: 2
39
39
  divider: 2
40
40
  marker: 2
@@ -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::Package) ||
20
- context.model_element.is_a?(Ea::Model::Note)
21
- # Notes keep the folder silhouette but never render a
22
- # label (EA publishes note bodies only).
23
- label = package_label(context.model_element) if context.model_element.is_a?(Ea::Model::Package)
24
- stereotype = package_stereotype(context.model_element) if context.model_element.is_a?(Ea::Model::Package)
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; EA strokes it at the 2px box outline.
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
- # Explicit zero-width borders keep that end on the geometric edge;
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
- return box if border_width?(diagram, ref, duid, 0)
250
-
251
- Ea::Model::Bounds.new(x: box.x - 1, y: box.y - 1,
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
- legend.heading_size || Ea::Model::Legend::DEFAULT_HEADING_SIZE
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
- # Diagonal line marking the fold.
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
- %(<path d="M #{Canvas.coord(x + w - fold)} #{Canvas.coord(y)} L #{Canvas.coord(x + w)} #{Canvas.coord(y + fold)}" shape-rendering="auto"/>)
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 %i[up left right down].include?(direction) && property_role_box?(box, direction)
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 && dock_face_heading(points.first, 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
@@ -4,5 +4,5 @@ module Ea
4
4
  # Gem release version. Extracted to its own file so the
5
5
  # `gem-release` tool's `gem bump` can find and update it via the
6
6
  # standard `lib/<gem>/version.rb` convention.
7
- VERSION = "0.6.114"
7
+ VERSION = "0.6.116"
8
8
  end
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.114
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-16 00:00:00.000000000 Z
11
+ date: 2026-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model