ea 0.6.101 → 0.6.102

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: 7c722ef7c5b7c7968f5beb08538b31b8090a3ab1533b0418a9600752c2724ef3
4
- data.tar.gz: 818694e4771383a00ae453f76a7b3f98e79b6b5a839d9459d415ec72129e4226
3
+ metadata.gz: c453170a8a782786ce0a1061623c8de93ed75536228bb99d9b6c1857163f58f3
4
+ data.tar.gz: 19c5dc1e01e47508e51dfc87add4cd24059b4035b83ed45c5aa98d46d4edcb8b
5
5
  SHA512:
6
- metadata.gz: b00b8fddbd7701f724dc20bd6640dfc53b7a99903d7a075581be94c9f3674254a114fa976a2a895d5ba231ca3b0c93f7b8ce241c42ef4d5c9e106f82c7b660a6
7
- data.tar.gz: c41701279192e06adba33b39063dac26a9355dedf1a46fd687079013c341c880f8f3d83278876593cf27733c527f41e22032140b901246b0b6e335cf29dcb308
6
+ metadata.gz: 26fbc91c4d39c834e0e9a19c443501953b487210ea88922be813bf167dfb56c6d2cc55cb3238441f58a08a6cd7511560e495f787a05a566466e9e7c5f45b06b0
7
+ data.tar.gz: 79f6d211068cf179e2fee6eea24e2e0ae60385be826db2254dcb6ce3d3aab345d68c743f529d430b242f8ea0cc3fdda7425e6cf880cf984abf1b7aeec145d74c
@@ -23,6 +23,16 @@ module Ea
23
23
  set_stored_ray_origins(model, source, target, geom)
24
24
  end
25
25
 
26
+ def mark_default_square_ray(model, link_row, geom, context)
27
+ style = model.style.to_h
28
+ return model if context || !link_row.path.to_s.empty? || model.waypoints.size != 2
29
+ return model unless style[:mode] == "3" && style[:tree] == "OS" && style[:lwidth] == "2"
30
+ return model unless geom[:sx].nil? && geom[:sy].nil? && [geom[:ex], geom[:ey]] == [0, 0]
31
+
32
+ model.style = style.merge(default_square_ray: true)
33
+ model
34
+ end
35
+
26
36
  # A Mode 2 Generalization whose stored Path is a horizontal jog
27
37
  # between vertically separated boxes that overlap in x is EA's
28
38
  # auto-route: one vertical ray at the overlap midpoint plus SX.
@@ -100,30 +110,39 @@ module Ea
100
110
  return model unless stored_ray_candidate?(model, link_row, geom)
101
111
 
102
112
  source, target = stored_ray_boxes(link_row, diagram_row)
103
- return model unless stored_ray_gap?(source, target, model.style[:mode], geom[:edge])
113
+ allowed = if normalized_port_anchors?(model)
114
+ normalized_level_ray?(model, source, target, geom)
115
+ else
116
+ stored_ray_gap?(source, target, model.style[:mode], geom[:edge])
117
+ end
118
+ return model unless allowed
104
119
 
105
120
  set_stored_ray_origins(model, source, target, geom)
106
121
  end
107
122
 
108
- # An empty-Path Mode 3 Association with explicit offsets on both
109
- # ends is the straight line between the two stored ray origins.
110
- # Origins outside their own box are not a stored ray.
123
+ # An empty-Path Mode 3 Association follows the line between its
124
+ # stored endpoint origins; an omitted offset defaults to the center.
125
+ # The target origin must lie inside its own stroked outline.
111
126
  def association_stored_ray(model, link_row, diagram_row, geom, context)
112
127
  return model if context
113
128
  return model unless association_ray_candidate?(model, link_row)
114
129
 
115
130
  source, target = stored_ray_boxes(link_row, diagram_row)
116
- origins = association_ray_origins(source, target, geom, model.style.to_h[:lwidth])
131
+ width = association_outline_width(model, link_row, diagram_row)
132
+ origins = association_ray_origins(source, target, geom, width)
117
133
  return model unless origins
118
134
 
119
- model.waypoints = port_waypoints(model)
135
+ model.waypoints = port_waypoints(model, origins)
120
136
  model.source_anchor, model.target_anchor = origins
121
137
  model.style = model.style.to_h.merge(stored_association_ray: true, association_ray_outline: true)
138
+ if geom[:edge] == 1 && !inside_box?(origins.first, grown_bounds(source, width == "2" ? 1 : 0))
139
+ model.style[:stored_top_dock] = true
140
+ end
122
141
  model
123
142
  end
124
143
 
125
- def port_waypoints(model)
126
- [model.source_port, model.target_port].map do |point|
144
+ def port_waypoints(model, origins)
145
+ [model.source_port || origins.first, model.target_port || origins.last].map do |point|
127
146
  Ea::Model::Waypoint.new(position: point)
128
147
  end
129
148
  end
@@ -135,12 +154,41 @@ module Ea
135
154
  origins = [stored_ray_origin(source, geom[:sx], geom[:sy]),
136
155
  stored_ray_origin(target, geom[:ex], geom[:ey])]
137
156
  boxes = [source, target].map { |box| grown_bounds(box, outline) }
138
- inside = origins.zip(boxes).all? { |o, b| inside_box?(o, b) }
139
- return unless inside && origins.first != origins.last
157
+ return unless origins.first != origins.last && association_origin_pair?(origins, boxes, width, geom[:edge])
140
158
 
141
159
  origins if width == "2" || sub_pixel_safe_origins?(origins, [source, target] + boxes)
142
160
  end
143
161
 
162
+ # An external source origin is a virtual point on the saved ray.
163
+ # It is usable only when that segment actually crosses the source.
164
+ def association_origin_pair?(origins, boxes, width, edge)
165
+ return false unless inside_box?(origins.last, boxes.last)
166
+ return true if inside_box?(origins.first, boxes.first)
167
+
168
+ return segment_crosses_box?(origins.first, origins.last, boxes.first) if width == "2"
169
+ return false unless edge == 1
170
+
171
+ source = boxes.first
172
+ top = Ea::Model::Bounds.new(x: source.x, y: source.y, width: source.width, height: 0)
173
+ segment_crosses_box?(origins.first, origins.last, top)
174
+ end
175
+
176
+ def segment_crosses_box?(first, last, box)
177
+ interval = [0.0, 1.0]
178
+ axes = [[first.x, last.x, box.x, box.x + box.width],
179
+ [first.y, last.y, box.y, box.y + box.height]]
180
+ axes.all? do |origin, finish, low, high|
181
+ if origin == finish
182
+ origin.between?(low, high)
183
+ else
184
+ times = [(low - origin).to_f / (finish - origin),
185
+ (high - origin).to_f / (finish - origin)].minmax
186
+ interval = [[interval.first, times.first].max, [interval.last, times.last].min]
187
+ interval.first <= interval.last
188
+ end
189
+ end
190
+ end
191
+
144
192
  def grown_bounds(bounds, amount)
145
193
  Ea::Model::Bounds.new(x: bounds.x - amount, y: bounds.y - amount,
146
194
  width: bounds.width + 2 * amount,
@@ -149,8 +197,21 @@ module Ea
149
197
 
150
198
  def association_ray_candidate?(model, link_row)
151
199
  model.connector_type == "Association" && link_row.path.to_s.empty? &&
152
- model.has_geometry_offsets && model.source_port && model.target_port &&
153
- association_ray_style?(model.style.to_h)
200
+ model.source_anchor && model.target_anchor &&
201
+ (model.source_port || model.target_port) && association_ray_style?(model.style.to_h)
202
+ end
203
+
204
+ # A thin connector still meets the outer stroke of explicitly
205
+ # bordered elements. Ambiguous placements must agree on that border.
206
+ def association_outline_width(model, link_row, diagram_row)
207
+ width = model.style[:lwidth]
208
+ return width if width == "2"
209
+
210
+ connector = database.find_connector(link_row.connectorid)
211
+ ids = [connector.start_object_id, connector.end_object_id]
212
+ placements = database.diagram_objects_for(diagram_row.diagram_id)
213
+ borders = placements.select { |placement| ids.include?(placement.ea_object_id) }
214
+ borders.all? { |placement| DiagramStyleParser.parse(placement.objectstyle)[:lwth] == "2" } ? "2" : width
154
215
  end
155
216
 
156
217
  def association_ray_style?(style)
@@ -263,7 +324,21 @@ module Ea
263
324
  def stored_ray_candidate?(model, link_row, geom)
264
325
  %w[Generalization Aggregation].include?(model.connector_type) &&
265
326
  model.waypoints.size == 2 && stored_ray_style?(model.style, link_row) &&
266
- stored_ray_geometry?(model, geom) && !normalized_port_anchors?(model)
327
+ stored_ray_geometry?(model, geom)
328
+ end
329
+
330
+ # Boundary-port rounding can move a computed jog off a level saved
331
+ # ray. Equal stored origin rows specify the line before clipping.
332
+ def normalized_level_ray?(model, source, target, geom)
333
+ style = model.style.to_h
334
+ return false unless style[:mode] == "3" && style[:lwidth] == "2" &&
335
+ horizontal_ray_gap?(source, target)
336
+
337
+ origins = [stored_ray_origin(source, geom[:sx], geom[:sy]),
338
+ stored_ray_origin(target, geom[:ex], geom[:ey])]
339
+ boxes = [source, target].map { |box| grown_bounds(box, 1) }
340
+ origins.first.y == origins.last.y &&
341
+ origins.zip(boxes).all? { |origin, box| inside_box?(origin, box) }
267
342
  end
268
343
 
269
344
  def stored_ray_style?(style, link_row)
@@ -436,7 +436,8 @@ module Ea
436
436
  model = mode2_overlap_ray(model, link_row, diagram_row, geom, context)
437
437
  model = association_stored_ray(model, link_row, diagram_row, geom, context)
438
438
  model = correct_stored_ray_origins(model, link_row, diagram_row, geom, context)
439
- overlap_ray_origins(model, link_row, diagram_row, geom, context)
439
+ model = overlap_ray_origins(model, link_row, diagram_row, geom, context)
440
+ mark_default_square_ray(model, link_row, geom, context)
440
441
  end
441
442
 
442
443
  # Ray origin for one connector end: the element's center
@@ -46,7 +46,8 @@ module Ea
46
46
  original = clipped_pairs(bounds, anchors)
47
47
  unless stroke_outline?(connector, bounds) || overlap_ray?(connector) ||
48
48
  bordered_outline?(connector, diagram, [source_ref, target_ref], bounds)
49
- return original
49
+ pairs = default_square_pairs(connector, points, source_bounds, target_bounds, original)
50
+ return stored_top_pairs(connector, source_bounds, pairs)
50
51
  end
51
52
 
52
53
  # EA docks these markers on the outer edge of the 2px box stroke.
@@ -55,9 +56,48 @@ module Ea
55
56
  width: box.width + 2, height: box.height + 2)
56
57
  end
57
58
  candidate = clipped_pairs(outline, anchors)
58
- forward_route?(original, candidate) ? candidate : original
59
+ expanded = forward_route?(original, candidate)
60
+ pairs = expanded ? candidate : original
61
+ target = expanded ? outline.last : target_bounds
62
+ pairs = default_square_pairs(connector, points, source_bounds, target, pairs)
63
+ stored_top_pairs(connector, expanded ? outline.first : source_bounds, pairs)
59
64
  end
60
65
 
66
+ # EDGE=1 explicitly seats an external source ray on the top edge;
67
+ # its first contact can instead be the bottom when its origin is below.
68
+ def stored_top_pairs(connector, source, pairs)
69
+ return pairs unless connector.connector_type == "Association" &&
70
+ connector.style.to_h[:stored_top_dock] == true
71
+
72
+ first = connector.source_anchor
73
+ last = connector.target_anchor
74
+ return pairs unless first && last && first.y != last.y
75
+
76
+ ratio = (source.y - first.y).to_f / (last.y - first.y)
77
+ x_position = first.x + ratio * (last.x - first.x)
78
+ return pairs unless ratio.between?(0, 1) &&
79
+ x_position.between?(source.x, source.x + source.width)
80
+
81
+ proposed = [[x_position.floor, source.y], pairs.last]
82
+ forward_route?(pairs, proposed) ? proposed : pairs
83
+ end
84
+ private_class_method :stored_top_pairs
85
+
86
+ # A square ray with an omitted source offset keeps its chosen side
87
+ # edge and projects that end to the clipped target's horizontal row.
88
+ def default_square_pairs(connector, points, source, target, pairs)
89
+ return pairs unless connector.style.to_h[:default_square_ray] == true
90
+ return pairs unless points.first[1] == points.last[1] &&
91
+ on_side_edge?(points.first, source) && on_side_edge?(pairs.last, target)
92
+
93
+ first = [points.first[0], pairs.last[1]]
94
+ return pairs unless on_side_edge?(first, source)
95
+
96
+ proposed = [first, pairs.last]
97
+ forward_route?(points, proposed) ? proposed : pairs
98
+ end
99
+ private_class_method :default_square_pairs
100
+
61
101
  # A horizontal lateral tree the QEA builder derived as one level
62
102
  # line (internal style flag) keeps it between the facing side edges.
63
103
  def flush_lateral_line?(connector, points, source, target)
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.101"
7
+ VERSION = "0.6.102"
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.101
4
+ version: 0.6.102
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.