ea 0.6.112 → 0.6.113

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: 6014a00e5fa8c483688ab06cc70027d3b3a8adcd65afbe98ef6782ea6a5473a3
4
- data.tar.gz: bd75f6912efc6bb4bc1a963c21ef53056ac421de33af7960bc8e94ee00d8e1c1
3
+ metadata.gz: 673f03cddf9ae4a7cb297beaa9b9fab0b3e272e26771d976c90c521726465af5
4
+ data.tar.gz: 1147cce73671336e59d68dc04caeee0cb66276ce946e5af3a8b07d378f21ac7b
5
5
  SHA512:
6
- metadata.gz: ca1099b7bbc4e22f6e874953b49c29afdc4d8bfc11aef61d7f6f55b3d4c6a2062d064f65e7812fb060c0946ebc47edd04d9a86a45e872584f0a40587f6454750
7
- data.tar.gz: 5b56489efbaaf470115c56e180e0e78d8f9f8e46d0c4b21c030ab0443a7bf07bc41317125b16331ec76fef1aa3fae29b2731e35fb38b110743a60a18d0e88183
6
+ metadata.gz: 44fcd7c27d72d01fef2fdf70498bee178904e54af09484506ff61ff2372281cbc03b5c7d23d4750f4ad7f1c2a474ad3fb3c79cfbbcc61e520f151d11a670a2be
7
+ data.tar.gz: 5a5ba8607a019d59be72fa1416cfbd66056e751420318bc02042a0ba02bd82dcc66452a97d25c26b2a0f408f132a2891a37db3e19f14f1c4ad98dfe1f4319c19
@@ -100,12 +100,18 @@ module Ea
100
100
  mult_seat(direction, anchor, mult_box) || mult_pos
101
101
  elsif end_kind == :target && !(tree && zero_offset?(text_box, mult_box))
102
102
  text_pos = target_property_role(connector, heading, anchor, text_box, role) ||
103
+ target_diagonal_property_role(connector, heading, anchor, text_box, role) ||
103
104
  target_vertical_role(connector, heading, anchor, text_box, role) ||
104
105
  target_rightward_role(heading, anchor, text_box, role) || text_pos
105
106
  mult_pos = target_multiplicity_seat(connector, heading, anchor, mult_box) ||
106
107
  target_horizontal_multiplicity(heading, anchor, mult_box, mult) || mult_pos
107
108
  end
108
109
 
110
+ if end_kind == :source
111
+ text_pos = source_top_property_role(connector, heading, anchor, text_box, role) ||
112
+ source_bottom_property_role(connector, heading, anchor, text_box, role) || text_pos
113
+ end
114
+
109
115
  build_texts(role:, mult:, standalone:,
110
116
  text_pos:, mult_pos:, stereo:, text_box:)
111
117
  end
@@ -185,10 +191,10 @@ module Ea
185
191
  [x + SEAT_X_INSET, y - 7]
186
192
  end
187
193
 
188
- # Property-role clusters use a direction-specific right edge;
194
+ # Property-role clusters use a direction-specific horizontal seat;
189
195
  # alignment controls the stereotype line within that cluster.
190
196
  def source_property_seat(connector, direction, anchor, box, role)
191
- return nil unless %i[up left down].include?(direction) && property_role_box?(box, direction)
197
+ return nil unless %i[up left right down].include?(direction) && property_role_box?(box, direction)
192
198
  return nil unless source_property_font?(connector, direction)
193
199
 
194
200
  width = Ea::Fonts::Metrics.text_length(role, font_size, family: font_family)
@@ -196,11 +202,59 @@ module Ea
196
202
 
197
203
  x, y = seat_corner(anchor, box)
198
204
  inset, baseline = property_role_offsets(direction)
199
- left = x - inset - width
205
+ left = property_role_x(direction, x, inset + width)
200
206
  left = [left, MIN_ROLE_X].max if direction == :up
201
207
  [left, y + baseline]
202
208
  end
203
209
 
210
+ def property_role_x(direction, origin, offset)
211
+ direction == :right ? origin + offset : origin - offset
212
+ end
213
+
214
+ # EDGE=1 certifies the top dock independently of the clipped ray's
215
+ # heading. An absent source box uses the upward Property seat.
216
+ def source_top_property_role(connector, heading, anchor, box, role)
217
+ return nil unless source_seat?(:source, heading) && heading[0].negative? && heading[1].positive?
218
+ return nil unless zero_source_property_box?(box)
219
+
220
+ return nil unless source_top_property_route?(connector.style.to_h)
221
+
222
+ source_property_seat(connector, :up, anchor, box, role)
223
+ end
224
+
225
+ def zero_source_property_box?(box)
226
+ box && box["alignment"]&.zero? && stored_offsets_zero?(box)
227
+ end
228
+
229
+ def source_top_property_route?(style)
230
+ style[:stored_top_dock] == true && style.values_at(:mode, :lwidth, :tree) == ["3", "2", nil]
231
+ end
232
+
233
+ # A saved external ray that first meets the source's bottom face
234
+ # seats an absent source Property role like the downward Property
235
+ # seat. The caller reports that face as a downward heading.
236
+ def source_bottom_property_role(connector, heading, anchor, box, role)
237
+ return nil unless source_bottom_dock?(connector, heading, box)
238
+
239
+ width = Ea::Fonts::Metrics.text_length(role, font_size, family: font_family)
240
+ return nil unless width
241
+
242
+ x, y = seat_corner(anchor, box)
243
+ inset, baseline = property_role_offsets(:down)
244
+ [x - inset - width, y + baseline]
245
+ end
246
+
247
+ def source_bottom_dock?(connector, heading, box)
248
+ source_seat?(:source, heading) && heading[0].zero? && heading[1].positive? &&
249
+ zero_source_property_box?(box) && default_presentation?(box) &&
250
+ source_bottom_property_route?(connector.style.to_h) &&
251
+ source_property_font?(connector, :down)
252
+ end
253
+
254
+ def source_bottom_property_route?(style)
255
+ style[:stored_association_ray] == true && style.values_at(:mode, :lwidth, :tree) == ["3", "2", nil]
256
+ end
257
+
204
258
  def plain_upward_seat(connector, direction, anchor, box, role)
205
259
  return nil unless direction == :up && cambria_box?(box)
206
260
  return nil unless [0, 1].include?(box["alignment"])
@@ -233,6 +287,31 @@ module Ea
233
287
  property_label?(connector, :target) && downward_property_route?(connector.style.to_h)
234
288
  end
235
289
 
290
+ # Zero-offset diagonal labels use the dominant leg's Property
291
+ # right edge. Moved boxes keep their explicit placement.
292
+ def target_diagonal_property_role(connector, heading, anchor, box, role)
293
+ return nil unless diagonal_property_box?(connector, box)
294
+
295
+ inset = diagonal_property_inset(heading)
296
+ width = Ea::Fonts::Metrics.text_length(role, font_size, family: font_family) if inset
297
+ [anchor[0] - inset - width, anchor[1] - 7] if width
298
+ end
299
+
300
+ def diagonal_property_box?(connector, box)
301
+ plain_vertical_box?(box) && box["alignment"] == 1 && stored_offsets_zero?(box) &&
302
+ property_label?(connector, :target) &&
303
+ connector.style.to_h.values_at(:mode, :lwidth, :tree) == ["3", "0", nil]
304
+ end
305
+
306
+ def diagonal_property_inset(heading)
307
+ return nil unless heading && !heading.include?(0)
308
+
309
+ dx, dy = heading
310
+ return property_role_offsets(:left).first if dx.negative? && dx.abs > dy.abs
311
+
312
+ property_role_offsets(:down).first if dy.negative? && dy.abs > dx.abs
313
+ end
314
+
236
315
  def target_vertical_role(connector, heading, anchor, box, role)
237
316
  return nil unless heading && heading[0].zero? && !heading[1].zero?
238
317
 
@@ -301,7 +380,7 @@ module Ea
301
380
  def property_role_offsets(direction)
302
381
  case direction
303
382
  when :up then [60, -6]
304
- when :left then [60, -7]
383
+ when :left, :right then [60, -7]
305
384
  else [65, 15]
306
385
  end
307
386
  end
@@ -315,14 +394,16 @@ module Ea
315
394
  def source_property_route?(style, direction)
316
395
  case direction
317
396
  when :left then left_property_route?(style)
397
+ when :right then style.values_at(:mode, :lwidth, :tree) == %w[3 2 H]
318
398
  when :down then downward_property_route?(style)
319
399
  else %w[0 2].include?(style[:lwidth]) && style[:tree] != "OS"
320
400
  end
321
401
  end
322
402
 
323
403
  def left_property_route?(style)
324
- style[:mode] == "3" && %w[0 2].include?(style[:lwidth]) &&
325
- [nil, "H", "LV"].include?(style[:tree])
404
+ downward_property_route?(style) ||
405
+ (style[:mode] == "3" && %w[0 2].include?(style[:lwidth]) &&
406
+ [nil, "H", "LV"].include?(style[:tree]))
326
407
  end
327
408
 
328
409
  def downward_property_route?(style)
@@ -96,8 +96,8 @@ module Ea
96
96
  LINE_PITCH = 13
97
97
 
98
98
  def label_position(connector, points)
99
- name_seat = stored_name_position(connector, points)
100
- return name_seat if name_seat
99
+ stored_seat = stored_stereotype_position(connector, points) || stored_name_position(connector, points)
100
+ return stored_seat if stored_seat
101
101
 
102
102
  return midpoint(points) unless default_substitutable_seat?(connector) &&
103
103
  upward_four_point_route?(points)
@@ -106,9 +106,23 @@ module Ea
106
106
  [source[0] + 3, ((source[1] + bend[1]) / 2).floor - 1]
107
107
  end
108
108
 
109
+ # LMB is the row below the midpoint; LMT carries the name above it.
110
+ def stored_stereotype_position(connector, points)
111
+ return nil unless connector.is_a?(Ea::Model::DiagramConnector) && measured_name_font?
112
+ return nil unless stereotype_label(connector)
113
+
114
+ box = connector.label_boxes.to_h[:lmb]
115
+ return nil unless default_name_box?(box)
116
+
117
+ stored_position(connector, points, box, LINE_PITCH)
118
+ end
119
+
109
120
  # At 7pt, a centered name's stored corner is 3px below its baseline.
110
121
  def stored_name_position(connector, points)
111
- box = stored_name_box(connector)
122
+ stored_position(connector, points, stored_name_box(connector), -3)
123
+ end
124
+
125
+ def stored_position(connector, points, box, baseline)
112
126
  return nil unless box
113
127
 
114
128
  width = Ea::Fonts::Metrics.text_length(midpoint_content(connector), font_size, family: font_family)
@@ -116,7 +130,7 @@ module Ea
116
130
 
117
131
  x, y = midpoint(points)
118
132
  offset_x, offset_y = box.values_at("ox", "oy").map(&:to_i)
119
- [x + offset_x - (width / 2.0).floor, y + offset_y - 3]
133
+ [x + offset_x - (width / 2.0).floor, y + offset_y + baseline]
120
134
  end
121
135
 
122
136
  def stored_name_box(connector)
@@ -129,7 +129,7 @@ module Ea
129
129
 
130
130
  box = source_dock_bounds(connector)
131
131
  face = box && dock_face_heading(points.first, box)
132
- return face if face && face.zip(heading).any? { |axis, delta| (axis * delta).positive? }
132
+ return face if face && face_leads_leg?(connector, face, heading, box)
133
133
 
134
134
  stored_upward_heading(connector, heading)
135
135
  end
@@ -147,6 +147,18 @@ module Ea
147
147
  original.any?(&:zero?) ? original : heading
148
148
  end
149
149
 
150
+ # A saved external ray enters the source through the face it first
151
+ # meets, even when the clipped leg then runs back across the box.
152
+ def face_leads_leg?(connector, face, heading, box)
153
+ anchor = connector.source_anchor
154
+ if face == [0, 1] && anchor && anchor.y > box.y + box.height &&
155
+ connector.style.to_h[:stored_association_ray] == true
156
+ return true
157
+ end
158
+
159
+ face.zip(heading).any? { |axis, delta| (axis * delta).positive? }
160
+ end
161
+
150
162
  def stored_upward_heading(connector, heading)
151
163
  return heading unless heading[1].negative?
152
164
 
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.112"
7
+ VERSION = "0.6.113"
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ea
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.112
4
+ version: 0.6.113
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.