ea 0.6.91 → 0.6.93

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: cf0f3e11888e4492386de976f97e4024e39d9dbc95083a5b184c0fb25d8cfead
4
- data.tar.gz: 467687628f21bf50264c0210de75e2fad5013a14d7f2a1572d65dfb218429052
3
+ metadata.gz: ffc412a948388b91fc07382b857fdcdbb4fda6a6d12bdc4cba7931c29720bf2e
4
+ data.tar.gz: 06cf5da1fb2062d414866f36e03798cfc9052fb2fcae45dfb78552e754d07662
5
5
  SHA512:
6
- metadata.gz: 67a177d6e826985a3526c63bae7ed1f10e55b51ca97ec1507e5cb2f89f309b789f734c27cd41b678777e3ea72cb730a44485799972c3bbab916dedfd51f404c5
7
- data.tar.gz: 55d5d873894f3e26fb786a71efaab326ecd4df2d16d167bf3dc7158e1775e139f9a9c2096cd79c5ae136315421c1d19156320d1c895837d691ecd7a97e772a2a
6
+ metadata.gz: 3d498df6109da7ad7c4368b589f34c9dbe3a48171571e9993fef75d80874e3f1eb6a31e6f890db39c5f413a61e895da8e3bf7f81a6d01b94fdc0a225267fb809
7
+ data.tar.gz: ec4537e30476a83f7ecc39c29aacb1d54f6a788f9a87c97bfcf86f665ece6010ca640a31062d30c0ad2f1fa8362bc1e834f207355989b27f9197ede225eff54a
@@ -8,6 +8,21 @@ module Ea
8
8
  module Routing
9
9
  private
10
10
 
11
+ # A lateral tree leaves the side edge before turning upward.
12
+ # Keep that dock only when its first leg actually points outward.
13
+ def lateral_source_exit?(points, link_row, bounds)
14
+ return false unless points.size == 3 && DiagramStyleParser.parse(link_row.style)[:tree] == "LV"
15
+
16
+ anchor, bend = points.take(2)
17
+ anchor[1] == bend[1] && outward_side_exit?(anchor[0], bend[0], bounds)
18
+ end
19
+
20
+ def outward_side_exit?(anchor_x, bend_x, bounds)
21
+ left = bounds.x
22
+ right = left + bounds.width
23
+ (anchor_x == left && bend_x < left) || (anchor_x == right && bend_x > right)
24
+ end
25
+
11
26
  # Keep a vertical tree trunk whose bends still fit both boxes
12
27
  # and the open gap; the closest facing edges need not share its x.
13
28
  def preserve_stored_tree?(points, link_row, connector, src, tgt)
@@ -577,9 +577,8 @@ module Ea
577
577
  # describe the source-side and target-side bend offsets
578
578
  # relative to the edge attachment points.
579
579
  #
580
- # Path takes precedence when present — it's the authoritative
581
- # routing. SX/SY/EX/EY are fallback for auto-routed direct
582
- # connectors with at most one bend on each end.
580
+ # Custom-line Path takes precedence when present. Direct mode
581
+ # ignores old custom bends; SX/SY/EX/EY supply its endpoint ports.
583
582
  #
584
583
  # If we can't resolve the source or target element placement
585
584
  # for this diagram, we emit no waypoints (the connector will
@@ -607,8 +606,10 @@ module Ea
607
606
  element_edge_point(target_placement, :target, edge_out)
608
607
 
609
608
  intermediate = intermediate_waypoints(link_row)
610
- if edge_out == 2 && source_port && target_port &&
611
- !valid_tree_path?(intermediate, source_placement)
609
+ if DiagramStyleParser.parse(link_row.style)[:mode] == "1"
610
+ points = [source_point, target_point]
611
+ elsif edge_out == 2 && source_port && target_port &&
612
+ !valid_tree_path?(intermediate, source_placement)
612
613
  # A stale tree Path (not on the source's facing edge) is
613
614
  # recomputed: source port -> corner at (target dock x,
614
615
  # source exit y) -> target port.
@@ -923,7 +924,7 @@ module Ea
923
924
  target_y = tgt_b.y + tgt_b.height
924
925
  source_y -= inset if points[1][1] < source_y - inset
925
926
  target_y += inset if points[-2][1] > target_y + inset
926
- unless strict && points[1][1] >= source_y
927
+ unless lateral_source_exit?(points, link_row, src_b) || (strict && points[1][1] >= source_y)
927
928
  points[0] = [points[1][0].clamp(src_b.x, src_b.x + src_b.width), source_y]
928
929
  end
929
930
  unless strict && points[-2][1] <= target_y
@@ -19,6 +19,8 @@ module Ea
19
19
  # for those — only simple role names render.
20
20
  class EndLabel
21
21
  PROPERTY_STEREOTYPE = "«Property»"
22
+ SEAT_FONT_SIZE = 7
23
+ SEAT_X_INSET = 3
22
24
 
23
25
  attr_reader :canvas, :model_index, :document, :theme,
24
26
  :font_family, :font_size, :font_unit
@@ -41,12 +43,23 @@ module Ea
41
43
  # anchor - [x, y] of the connector endpoint
42
44
  # connector - the DiagramConnector (for relationship lookup)
43
45
  # end_kind - :source or :target
46
+ # heading - [dx, dy] of the first leg leaving the anchor
47
+ # stored_labels - the connector has stored label boxes at
48
+ # either end; false keeps the opposite-end
49
+ # fallback
44
50
  def texts(text_box:, mult_box:, anchor:, connector:, end_kind:,
45
- tree: false)
51
+ tree: false, heading: nil, stored_labels: false)
46
52
  from_property, role, mult = role_and_multiplicity(connector, end_kind)
47
53
  if role_empty?(role) && mult_empty?(mult)
48
54
  other_from_prop, other_role, other_mult = role_and_multiplicity(connector,
49
55
  opposite(end_kind))
56
+ if stored_labels
57
+ return [] if role_empty?(other_role) && mult_empty?(other_mult)
58
+
59
+ return own_stereotype_only(text_box, anchor, connector,
60
+ end_kind)
61
+ end
62
+
50
63
  role = other_role if role_empty?(role)
51
64
  mult ||= other_mult
52
65
  from_property ||= other_from_prop
@@ -73,6 +86,11 @@ module Ea
73
86
  # the end has none.
74
87
  stereo = connector_source_dest_stereotype(connector, end_kind)
75
88
  stereo = stereo.nil? || stereo.empty? ? nil : "«#{stereo}»"
89
+ direction = leg_direction(end_kind, heading)
90
+ if direction && !(tree && zero_offset?(text_box, mult_box))
91
+ text_pos = role_seat(direction, anchor, text_box, role) || text_pos
92
+ mult_pos = mult_seat(direction, anchor, mult_box) || mult_pos
93
+ end
76
94
 
77
95
  build_texts(role:, mult:, standalone:,
78
96
  text_pos:, mult_pos:, stereo:, text_box:)
@@ -80,6 +98,19 @@ module Ea
80
98
 
81
99
  private
82
100
 
101
+ # With stored boxes on the connector, an end that carries no
102
+ # data of its own draws only its own stereotype; the data end
103
+ # draws the role and multiplicity; with no data anywhere nothing
104
+ # is drawn. Connectors with no stored box keep the opposite-end
105
+ # fallback.
106
+ def own_stereotype_only(text_box, anchor, connector, end_kind)
107
+ stereo = connector_source_dest_stereotype(connector, end_kind)
108
+ pos = position_at(text_box, anchor)
109
+ return [] if stereo.nil? || stereo.empty? || pos.nil?
110
+
111
+ [text_at(pos[0], pos[1], "«#{stereo}»")]
112
+ end
113
+
83
114
  # EA's tree-route label clusters (hand-verified against the
84
115
  # EA-published reference, SY 8542 with canvas=(0,0)):
85
116
  # source-end pairs stack at the exit point (role +3/-23,
@@ -109,6 +140,63 @@ module Ea
109
140
  end
110
141
  end
111
142
 
143
+ # Stored source-end boxes on a 7pt label whose first leg
144
+ # leaves straight up or straight right are seated at the box
145
+ # corner, anchor + (OX, OY) with no sign flip, 3px to its
146
+ # right: the multiplicity 6px above it on an upward leg and
147
+ # 15px below it on a rightward one, the role 7px above it on
148
+ # a rightward one. Other sizes, headings, the target end,
149
+ # custom presentation keep their offsets; zero multiplicity boxes keep their default seat.
150
+ def leg_direction(end_kind, heading)
151
+ return nil unless source_seat?(end_kind, heading)
152
+
153
+ dx, dy = heading
154
+ return :up if dx.zero? && dy.negative?
155
+ return :right if dy.zero? && dx.positive?
156
+
157
+ nil
158
+ end
159
+
160
+ # Role text on a rightward leg is left-aligned at the corner,
161
+ # 7px above it. Upward legs keep the plain role offset.
162
+ def role_seat(direction, anchor, box, role)
163
+ return nil unless direction == :right
164
+ return nil if box.nil? || role_empty?(role)
165
+ return nil unless default_presentation?(box)
166
+
167
+ x, y = seat_corner(anchor, box)
168
+ [x + SEAT_X_INSET, y - 7]
169
+ end
170
+
171
+ def mult_seat(direction, anchor, box)
172
+ return nil if box.nil? || stored_offsets_zero?(box)
173
+ return nil unless default_presentation?(box)
174
+
175
+ x, y = seat_corner(anchor, box)
176
+ [x + SEAT_X_INSET, direction == :right ? y + 15 : y - 6]
177
+ end
178
+
179
+ def source_seat?(end_kind, heading)
180
+ end_kind == :source && heading &&
181
+ font_size == SEAT_FONT_SIZE && font_unit == "pt"
182
+ end
183
+
184
+ # Boxes with explicit string-keyed OX/OY (the QEA shape) that
185
+ # are unrotated, unhidden, unstyled and left/center aligned.
186
+ def default_presentation?(box)
187
+ return false unless box["ox"] && box["oy"]
188
+ return false unless [nil, 0, 1].include?(box["alignment"])
189
+ return false unless [nil, -1].include?(box["color"])
190
+
191
+ box["rotation"].to_i.zero? && box["direction"].to_i.zero? &&
192
+ box.values_at("hidden", "bold", "italic", "underline")
193
+ .none?
194
+ end
195
+
196
+ def seat_corner(anchor, box)
197
+ [anchor[0] + box["ox"].to_i, anchor[1] + box["oy"].to_i]
198
+ end
199
+
112
200
  def build_texts(role:, mult:, standalone:, text_pos:, mult_pos:,
113
201
  stereo:, text_box:)
114
202
  if role && !role.empty? && text_pos
@@ -78,19 +78,23 @@ module Ea
78
78
  boxes = connector.label_boxes || {}
79
79
  tree = tree_shaped?(points)
80
80
 
81
- src_tb, src_mb = end_boxes(boxes, :llt, :llb) ||
82
- default_label_boxes(points, :source)
83
- tgt_tb, tgt_mb = end_boxes(boxes, :lrt, :lrb) ||
84
- default_label_boxes(points, :target)
81
+ stored_src = end_boxes(boxes, :llt, :llb)
82
+ stored_tgt = end_boxes(boxes, :lrt, :lrb)
83
+ stored_labels = !(stored_src.nil? && stored_tgt.nil?)
84
+ src_tb, src_mb = stored_src || default_label_boxes(points, :source)
85
+ tgt_tb, tgt_mb = stored_tgt || default_label_boxes(points, :target)
85
86
 
86
87
  texts = []
87
88
  end_renderer.texts(text_box: src_tb, mult_box: src_mb,
88
89
  anchor: source_pt, connector: connector,
89
- end_kind: :source, tree: tree && tree[:source]).each { |t| texts << t }
90
+ end_kind: :source, tree: tree && tree[:source],
91
+ heading: stored_src && leg_heading(points),
92
+ stored_labels: stored_labels).each { |t| texts << t }
90
93
  end_renderer.texts(text_box: tgt_tb,
91
94
  mult_box: tgt_mb,
92
95
  anchor: target_pt, connector: connector,
93
- end_kind: :target, tree: tree && tree[:target]).each { |t| texts << t }
96
+ end_kind: :target, tree: tree && tree[:target],
97
+ stored_labels: stored_labels).each { |t| texts << t }
94
98
  texts
95
99
  end
96
100
 
@@ -105,6 +109,10 @@ module Ea
105
109
  { source: true, target: true }
106
110
  end
107
111
 
112
+ def leg_heading(points)
113
+ [points[1][0] - points[0][0], points[1][1] - points[0][1]]
114
+ end
115
+
108
116
  def end_boxes(boxes, text_key, mult_key)
109
117
  return nil if boxes[text_key].nil? && boxes[mult_key].nil?
110
118
 
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.91"
7
+ VERSION = "0.6.93"
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.91
4
+ version: 0.6.93
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.