ea 0.6.93 → 0.6.94

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: ffc412a948388b91fc07382b857fdcdbb4fda6a6d12bdc4cba7931c29720bf2e
4
- data.tar.gz: 06cf5da1fb2062d414866f36e03798cfc9052fb2fcae45dfb78552e754d07662
3
+ metadata.gz: d2c872554c2534df450bc335593fb39ed9363f3fe20053ef001b1621c5cdec6b
4
+ data.tar.gz: 79cecbb5a25a07a38d5bac1df1aaf413ffbc471cdd39938cd64cdf8bca822ce7
5
5
  SHA512:
6
- metadata.gz: 3d498df6109da7ad7c4368b589f34c9dbe3a48171571e9993fef75d80874e3f1eb6a31e6f890db39c5f413a61e895da8e3bf7f81a6d01b94fdc0a225267fb809
7
- data.tar.gz: ec4537e30476a83f7ecc39c29aacb1d54f6a788f9a87c97bfcf86f665ece6010ca640a31062d30c0ad2f1fa8362bc1e834f207355989b27f9197ede225eff54a
6
+ metadata.gz: d24168a33acbf362b4feff14508abb00562139e6602caed154c7f4b5e8bfa2b0f773b045fd27585f0610946f8fb2e4e9e6dbf2b8667ab785fc1bd71f18e9aa83
7
+ data.tar.gz: 1559678bc826ecdb82d710a72723709fa195907e8a11bb138eed728ba8b9804b30fcab7f75a024f27f8df34558ba52ec7f373e18bf1f46e6c7a89f07af8cf70b
@@ -34,6 +34,50 @@ module Ea
34
34
  tree_bends_fit?(points[1], points[-2], src, tgt)
35
35
  end
36
36
 
37
+ # A width-2 Mode 3 horizontal tree keeps its stored vertical trunk
38
+ # and docks both ends on the facing sides' 1px outer outlines at
39
+ # the bend heights. Returns nil unless the trunk sits strictly
40
+ # inside the open gap and each bend lies inside its own box.
41
+ def horizontal_tree_route(points, link_row, connector, src, tgt)
42
+ return nil unless points.size == 4 && horizontal_tree_style?(link_row, connector)
43
+
44
+ first, last = points.values_at(1, 2)
45
+ src_x, tgt_x = facing_outline_xs(src, tgt)
46
+ return nil unless src_x && trunk_in_gap?(first, last, src_x, tgt_x)
47
+ return nil unless inside_height?(first[1], src) && inside_height?(last[1], tgt)
48
+
49
+ [[src_x, first[1]], first, last, [tgt_x, last[1]]]
50
+ end
51
+
52
+ def horizontal_tree_style?(link_row, connector)
53
+ style = DiagramStyleParser.parse(link_row.style)
54
+ %w[Generalization Aggregation].include?(connector.connector_type) &&
55
+ style[:tree] == "H" && style[:lwidth] == "2" && style[:mode] == "3"
56
+ end
57
+
58
+ def trunk_in_gap?(first, last, src_x, tgt_x)
59
+ first[0] == last[0] && first[1] != last[1] &&
60
+ first[0] > [src_x, tgt_x].min && first[0] < [src_x, tgt_x].max
61
+ end
62
+
63
+ def inside_height?(height, bounds)
64
+ height > bounds.y && height < bounds.y + bounds.height
65
+ end
66
+
67
+ # Source and target x on the facing sides' outer outlines, or nil
68
+ # when the boxes are not horizontally separated.
69
+ def facing_outline_xs(src, tgt)
70
+ source_left = src.x
71
+ source_right = source_left + src.width
72
+ target_left = tgt.x
73
+ target_right = target_left + tgt.width
74
+ if target_right < source_left
75
+ [source_left - 1, target_right + 1]
76
+ elsif source_right < target_left
77
+ [source_right + 1, target_left - 1]
78
+ end
79
+ end
80
+
37
81
  def tree_bends_fit?(first, last, source, target)
38
82
  first[0] != last[0] && first[1] == last[1] &&
39
83
  within_vertical_gap?(first[1], source, target) &&
@@ -646,10 +646,13 @@ module Ea
646
646
  # Between DISJOINT boxes, a stored route that does not dock the
647
647
  # closest facing edge pair is redrawn straight between them.
648
648
  # EDGE=2 routes are excluded; they are already exact.
649
- if %w[Association Aggregation].include?(connector.connector_type) &&
650
- edge_out != 2
651
- src = bounds_from_rect(source_placement)
652
- tgt = bounds_from_rect(target_placement)
649
+ src = bounds_from_rect(source_placement)
650
+ tgt = bounds_from_rect(target_placement)
651
+ tree_route = horizontal_tree_route(points, link_row, connector, src, tgt)
652
+ if tree_route
653
+ points = tree_route
654
+ elsif %w[Association Aggregation].include?(connector.connector_type) &&
655
+ edge_out != 2
653
656
  unless preserve_stored_tree?(points, link_row, connector, src, tgt)
654
657
  points = facing_edge_route(points, src, tgt)
655
658
  end
@@ -3,7 +3,7 @@
3
3
  module Ea
4
4
  module Svg
5
5
  module EaEmitter
6
- # Re-docks DIRECT (auto-routed straight, 2-waypoint) connectors
6
+ # Re-docks direct connectors and lateral tree target legs
7
7
  # against the DRAWN element outlines captured during the element
8
8
  # render pass. EA clips connector rays at drawn boxes — grown
9
9
  # classifier heights and autosized package bodies — never the
@@ -13,17 +13,15 @@ module Ea
13
13
  module ConnectorRedock
14
14
  module_function
15
15
 
16
- # Returns replacement waypoint pairs [source_edge, target_edge]
16
+ # Returns replacement waypoint pairs for direct or lateral tree routes,
17
17
  # for the connector, or nil when the connector must keep its
18
18
  # parsed waypoints (not direct, not 2-point, unresolved ends,
19
19
  # self-loop, or no drawn bounds for either end).
20
20
  def pairs_for(connector, diagram, model_index, bounds_map)
21
21
  return nil unless bounds_map
22
- return nil unless direct?(connector) || two_point?(connector)
23
-
24
22
  points = waypoint_pairs(connector)
25
- return nil unless points.size == 2
26
-
23
+ lateral = lateral_tree?(connector, points)
24
+ return nil unless points.size == 2 || lateral
27
25
 
28
26
  rel = relationship_for(connector, model_index)
29
27
  return nil unless rel
@@ -36,6 +34,11 @@ module Ea
36
34
  return nil unless source_bounds && target_bounds
37
35
  return nil if source_ref == target_ref
38
36
 
37
+ if lateral
38
+ target = lateral_target(points[1], target_bounds, connector.style[:tree])
39
+ return target && [points[0], points[1], target]
40
+ end
41
+
39
42
  anchors = [connector.source_anchor, connector.target_anchor]
40
43
  bounds = [source_bounds, target_bounds]
41
44
  original = clipped_pairs(bounds, anchors)
@@ -50,6 +53,49 @@ module Ea
50
53
  forward_route?(original, candidate) ? candidate : original
51
54
  end
52
55
 
56
+ def lateral_tree?(connector, points)
57
+ style = connector.style.to_h
58
+ points.size == 3 && style[:mode] == "3" && style[:lwidth] == "2" &&
59
+ %w[LV LH].include?(style[:tree]) &&
60
+ %w[Generalization Aggregation].include?(connector.connector_type)
61
+ end
62
+ private_class_method :lateral_tree?
63
+
64
+ # The bend selects the near target edge even when the boxes overlap.
65
+ # Preserve the source leg and bend; only the final dock changes.
66
+ def lateral_target(bend, bounds, tree)
67
+ return nil unless bounds.width.positive? && bounds.height.positive?
68
+
69
+ tree == "LV" ? vertical_target(bend, bounds) : horizontal_target(bend, bounds)
70
+ end
71
+ private_class_method :lateral_target
72
+
73
+ def vertical_target(bend, bounds)
74
+ x, y = bend
75
+ return nil unless x.between?(bounds.x, bounds.x + bounds.width)
76
+
77
+ edge = facing_coordinate(y, bounds.y, bounds.height)
78
+ [x, edge] if edge
79
+ end
80
+ private_class_method :vertical_target
81
+
82
+ def horizontal_target(bend, bounds)
83
+ x, y = bend
84
+ return nil unless y.between?(bounds.y, bounds.y + bounds.height)
85
+
86
+ edge = facing_coordinate(x, bounds.x, bounds.width)
87
+ [edge, y] if edge
88
+ end
89
+ private_class_method :horizontal_target
90
+
91
+ def facing_coordinate(bend, start, extent)
92
+ return start if bend < start
93
+ return start + extent if bend > start + extent
94
+
95
+ nil
96
+ end
97
+ private_class_method :facing_coordinate
98
+
53
99
  def stroke_outline?(connector, bounds)
54
100
  connector.style.to_h[:lwidth] == "2" &&
55
101
  %w[Generalization Aggregation].include?(connector.connector_type) &&
@@ -21,6 +21,7 @@ module Ea
21
21
  PROPERTY_STEREOTYPE = "«Property»"
22
22
  SEAT_FONT_SIZE = 7
23
23
  SEAT_X_INSET = 3
24
+ SEAT_STEREOTYPE_GAP = 12
24
25
 
25
26
  attr_reader :canvas, :model_index, :document, :theme,
26
27
  :font_family, :font_size, :font_unit
@@ -177,8 +178,11 @@ module Ea
177
178
  end
178
179
 
179
180
  def source_seat?(end_kind, heading)
180
- end_kind == :source && heading &&
181
- font_size == SEAT_FONT_SIZE && font_unit == "pt"
181
+ end_kind == :source && heading && seat_font?
182
+ end
183
+
184
+ def seat_font?
185
+ font_size == SEAT_FONT_SIZE && font_unit == "pt"
182
186
  end
183
187
 
184
188
  # Boxes with explicit string-keyed OX/OY (the QEA shape) that
@@ -204,7 +208,7 @@ module Ea
204
208
  texts = [text_at(x, y, role)]
205
209
  if stereo
206
210
  stereo_x = x + stereotype_offset(role, stereo, text_box)
207
- texts << text_at(stereo_x, y + property_y_offset, stereo)
211
+ texts << text_at(stereo_x, y + property_y_offset(text_box), stereo)
208
212
  end
209
213
  if mult && !mult.empty? && mult_pos
210
214
  texts << text_at(*mult_pos, mult)
@@ -391,7 +395,13 @@ module Ea
391
395
  [anchor[0] + offset_x.to_i, anchor[1] - offset_y.to_i]
392
396
  end
393
397
 
394
- def property_y_offset
398
+ # The stereotype line sits 12px below the role on 7pt labels
399
+ # with default presentation;
400
+ # other sizes and custom presentation keep size + 6.
401
+ def property_y_offset(text_box)
402
+ return SEAT_STEREOTYPE_GAP if seat_font? && text_box &&
403
+ default_presentation?(text_box)
404
+
395
405
  font_size + 6
396
406
  end
397
407
 
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.93"
7
+ VERSION = "0.6.94"
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.93
4
+ version: 0.6.94
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.