ea 0.6.98 → 0.6.99
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b9e7581bab2de7d7c5766a5f141c99885bb59cbb7ad974c313c26e86bfb96a2
|
|
4
|
+
data.tar.gz: 8933e36fb2422953eddb1d60069664e39177547beaf2ec17ed17c5b9f3aa4eac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ab6882700d57207a5fd2c1b27fa0befe5f287bb61f13387e99dc8f4429dbede75320d9fb6124a126094455d38d4dff7a41dbfc258633609c6695c5dcd216b36d
|
|
7
|
+
data.tar.gz: 82cd636abfe8c20a03dc8f6c5410192c65e674093eaaabc081e8fc84b7ec6544a87f3c87ff255f90fcaa1bc0f7704fdf1b2e46f344cf9288cd51ec4a7e8cfa97
|
|
@@ -115,6 +115,85 @@ module Ea
|
|
|
115
115
|
y: bounds.y + bounds.height / 2 - y_offset.to_i)
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
+
# A vertical tree stores its trunk bend twice when the parent sits
|
|
119
|
+
# directly above; EA draws that as one bend.
|
|
120
|
+
def drop_zero_length_tree_segments(points, link_row)
|
|
121
|
+
return points unless points.size == 4 && points[1] == points[2] &&
|
|
122
|
+
DiagramStyleParser.parse(link_row.style)[:tree] == "V"
|
|
123
|
+
|
|
124
|
+
points.values_at(0, 1, 3)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# A stored vertical tree trunk docks both ends on the box edges that
|
|
128
|
+
# face its bends, at the adjacent bend's x: Aggregations without
|
|
129
|
+
# geometry offsets whose child sits below, and flush Associations
|
|
130
|
+
# whose parent sits above. Other shapes keep their stored docks.
|
|
131
|
+
def redock_vertical_tree_trunk(points, link_row, connector, source_placement, target_placement)
|
|
132
|
+
style = DiagramStyleParser.parse(link_row.style)
|
|
133
|
+
return unless points.size == 4 && style[:tree] == "V"
|
|
134
|
+
|
|
135
|
+
case connector.connector_type
|
|
136
|
+
when "Aggregation"
|
|
137
|
+
return if geometry_has_offsets?(link_row.geometry)
|
|
138
|
+
|
|
139
|
+
redock_child_below(points, bounds_from_rect(source_placement), bounds_from_rect(target_placement))
|
|
140
|
+
when "Association"
|
|
141
|
+
return unless style[:lwidth] == "0"
|
|
142
|
+
|
|
143
|
+
redock_parent_above(points, link_row, source_placement, target_placement, strict: true)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def redock_child_below(points, src_b, tgt_b)
|
|
148
|
+
first_bend, last_bend = points.values_at(1, -2)
|
|
149
|
+
source_y = src_b.y + src_b.height
|
|
150
|
+
return points unless source_y <= tgt_b.y && first_bend[1] > source_y && last_bend[1] < tgt_b.y
|
|
151
|
+
|
|
152
|
+
points[0] = [clamp_to_span(first_bend[0], src_b), source_y]
|
|
153
|
+
points[-1] = [clamp_to_span(last_bend[0], tgt_b), tgt_b.y]
|
|
154
|
+
points
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def clamp_to_span(position, bounds)
|
|
158
|
+
position.clamp(bounds.x, bounds.x + bounds.width)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# A stored-Path Mode 3 width-2 end whose adjacent bend lies level
|
|
162
|
+
# with its box (inside the box's span on one axis, outside on the
|
|
163
|
+
# other) enters the box orthogonally on the outer outline at that
|
|
164
|
+
# bend's coordinate. Bends that are not level keep the port and
|
|
165
|
+
# every authored corner stays.
|
|
166
|
+
def level_entry_route(points, link_row, connector, src, tgt)
|
|
167
|
+
return points unless points.size >= 3 && level_entry_style?(link_row, connector)
|
|
168
|
+
|
|
169
|
+
route = points.dup
|
|
170
|
+
first = level_entry_point(route[1], src)
|
|
171
|
+
last = level_entry_point(route[-2], tgt)
|
|
172
|
+
route[0] = first if first
|
|
173
|
+
route[-1] = last if last
|
|
174
|
+
route
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def level_entry_style?(link_row, connector)
|
|
178
|
+
style = DiagramStyleParser.parse(link_row.style)
|
|
179
|
+
%w[Generalization Aggregation].include?(connector.connector_type) &&
|
|
180
|
+
style[:mode] == "3" && style[:lwidth] == "2" && !style.key?(:tree)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def level_entry_point(bend, box)
|
|
184
|
+
level_y = bend[1] > box.y && bend[1] < box.y + box.height
|
|
185
|
+
level_x = bend[0] > box.x && bend[0] < box.x + box.width
|
|
186
|
+
if level_y && outside_span?(bend[0], box.x, box.width)
|
|
187
|
+
[bend[0] < box.x ? box.x - 1 : box.x + box.width + 1, bend[1]]
|
|
188
|
+
elsif level_x && outside_span?(bend[1], box.y, box.height)
|
|
189
|
+
[bend[0], bend[1] < box.y ? box.y - 1 : box.y + box.height + 1]
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def outside_span?(value, start, length)
|
|
194
|
+
value < start || value > start + length
|
|
195
|
+
end
|
|
196
|
+
|
|
118
197
|
# Square routing keeps the stored orthogonal trunk, even when a
|
|
119
198
|
# shorter facing-edge route exists between the same boxes.
|
|
120
199
|
def stored_square_route(link_row, connector, source, target)
|
|
@@ -164,19 +243,29 @@ module Ea
|
|
|
164
243
|
|
|
165
244
|
# A valid lateral corner joins one vertical leg and one horizontal
|
|
166
245
|
# leg. Keep it even when the boxes also admit a shorter direct route.
|
|
167
|
-
|
|
246
|
+
# ported: both ends carry explicit SX/SY and EX/EY offsets. An
|
|
247
|
+
# untyped corner without them may be an authored diagonal.
|
|
248
|
+
def stored_lateral_route(link_row, connector, source, target, ported: false)
|
|
168
249
|
return nil unless %w[Aggregation Association].include?(connector.connector_type)
|
|
169
250
|
|
|
170
251
|
style = DiagramStyleParser.parse(link_row.style)
|
|
171
|
-
return nil unless stored_lateral_style?(style)
|
|
252
|
+
return nil unless stored_lateral_style?(style, connector, ported)
|
|
172
253
|
|
|
173
254
|
corner = stored_lateral_corner(link_row)
|
|
174
|
-
|
|
255
|
+
corner && lateral_corner_route(style, corner, source, target)
|
|
256
|
+
end
|
|
175
257
|
|
|
176
|
-
|
|
177
|
-
|
|
258
|
+
# An untyped Mode 3 Association corner takes whichever leg
|
|
259
|
+
# orientation its position fits; width-2 docks sit on the 1px
|
|
260
|
+
# outer outline.
|
|
261
|
+
def lateral_corner_route(style, corner, source, target)
|
|
262
|
+
case style[:tree]
|
|
263
|
+
when "LH" then horizontal_corner_route(corner, source, target)
|
|
264
|
+
when "LV" then vertical_corner_route(corner, source, target)
|
|
178
265
|
else
|
|
179
|
-
|
|
266
|
+
outline = style[:lwidth] == "2" ? 1 : 0
|
|
267
|
+
horizontal_corner_route(corner, source, target, outline) ||
|
|
268
|
+
vertical_corner_route(corner, source, target, outline)
|
|
180
269
|
end
|
|
181
270
|
end
|
|
182
271
|
|
|
@@ -186,8 +275,11 @@ module Ea
|
|
|
186
275
|
intermediate_waypoints(link_row).first
|
|
187
276
|
end
|
|
188
277
|
|
|
189
|
-
def stored_lateral_style?(style)
|
|
190
|
-
style[:mode] == "3" && %w[0 2].include?(style[:lwidth])
|
|
278
|
+
def stored_lateral_style?(style, connector, ported)
|
|
279
|
+
return false unless style[:mode] == "3" && %w[0 2].include?(style[:lwidth])
|
|
280
|
+
|
|
281
|
+
%w[LH LV].include?(style[:tree]) ||
|
|
282
|
+
(style[:tree].nil? && ported && connector.connector_type == "Association")
|
|
191
283
|
end
|
|
192
284
|
|
|
193
285
|
def horizontal_corner?(corner, source, target)
|
|
@@ -195,13 +287,13 @@ module Ea
|
|
|
195
287
|
!within_height?(corner[1], source) && !within_horizontal_span?(corner, target)
|
|
196
288
|
end
|
|
197
289
|
|
|
198
|
-
def horizontal_corner_route(corner, source, target)
|
|
290
|
+
def horizontal_corner_route(corner, source, target, outline = 0)
|
|
199
291
|
return nil unless horizontal_corner?(corner, source, target)
|
|
200
292
|
|
|
201
293
|
x, y = corner
|
|
202
294
|
|
|
203
|
-
source_y = y < source.y ? source.y : source.y + source.height
|
|
204
|
-
target_x = x < target.x ? target.x : target.x + target.width
|
|
295
|
+
source_y = y < source.y ? source.y - outline : source.y + source.height + outline
|
|
296
|
+
target_x = x < target.x ? target.x - outline : target.x + target.width + outline
|
|
205
297
|
[[x, source_y], corner, [target_x, y]]
|
|
206
298
|
end
|
|
207
299
|
|
|
@@ -210,13 +302,13 @@ module Ea
|
|
|
210
302
|
!within_horizontal_span?(corner, source) && !within_height?(corner[1], target)
|
|
211
303
|
end
|
|
212
304
|
|
|
213
|
-
def vertical_corner_route(corner, source, target)
|
|
305
|
+
def vertical_corner_route(corner, source, target, outline = 0)
|
|
214
306
|
return nil unless vertical_corner?(corner, source, target)
|
|
215
307
|
|
|
216
308
|
x, y = corner
|
|
217
309
|
|
|
218
|
-
source_x = x < source.x ? source.x : source.x + source.width
|
|
219
|
-
target_y = y < target.y ? target.y : target.y + target.height
|
|
310
|
+
source_x = x < source.x ? source.x - outline : source.x + source.width + outline
|
|
311
|
+
target_y = y < target.y ? target.y - outline : target.y + target.height + outline
|
|
220
312
|
[[source_x, y], corner, [x, target_y]]
|
|
221
313
|
end
|
|
222
314
|
|
|
@@ -662,7 +662,8 @@ module Ea
|
|
|
662
662
|
src = bounds_from_rect(source_placement)
|
|
663
663
|
tgt = bounds_from_rect(target_placement)
|
|
664
664
|
tree_route = stored_square_route(link_row, connector, src, tgt) ||
|
|
665
|
-
stored_lateral_route(link_row, connector, src, tgt
|
|
665
|
+
stored_lateral_route(link_row, connector, src, tgt,
|
|
666
|
+
ported: source_port && target_port) ||
|
|
666
667
|
horizontal_tree_route(points, link_row, connector, src, tgt)
|
|
667
668
|
if tree_route
|
|
668
669
|
points = tree_route
|
|
@@ -681,8 +682,15 @@ module Ea
|
|
|
681
682
|
redock_parent_above(points, link_row, source_placement,
|
|
682
683
|
target_placement, strict: true)
|
|
683
684
|
end
|
|
685
|
+
if intermediate.any?
|
|
686
|
+
redock_vertical_tree_trunk(points, link_row, connector, source_placement, target_placement)
|
|
687
|
+
end
|
|
684
688
|
end
|
|
685
689
|
|
|
690
|
+
if intermediate.any? && !tree_route
|
|
691
|
+
points = level_entry_route(points, link_row, connector, src, tgt)
|
|
692
|
+
end
|
|
693
|
+
points = drop_zero_length_tree_segments(points, link_row)
|
|
686
694
|
points.map do |x, y|
|
|
687
695
|
Ea::Model::Waypoint.new(position: Ea::Model::Point.new(x: x, y: y))
|
|
688
696
|
end
|
|
@@ -22,6 +22,10 @@ module Ea
|
|
|
22
22
|
SEAT_FONT_SIZE = 7
|
|
23
23
|
SEAT_X_INSET = 3
|
|
24
24
|
SEAT_STEREOTYPE_GAP = 12
|
|
25
|
+
# EA keeps an upward property seat's left edge at diagram x >= 3;
|
|
26
|
+
# the stereotype line moves with the role. Measured only for the
|
|
27
|
+
# default Cambria 7pt boxed cluster.
|
|
28
|
+
MIN_ROLE_X = 3
|
|
25
29
|
|
|
26
30
|
attr_reader :canvas, :model_index, :document, :theme,
|
|
27
31
|
:font_family, :font_size, :font_unit
|
|
@@ -93,7 +97,8 @@ module Ea
|
|
|
93
97
|
mult_pos = directional_multiplicity(connector, direction, anchor, mult_box, mult) ||
|
|
94
98
|
mult_seat(direction, anchor, mult_box) || mult_pos
|
|
95
99
|
elsif end_kind == :target && !(tree && zero_offset?(text_box, mult_box))
|
|
96
|
-
text_pos =
|
|
100
|
+
text_pos = target_vertical_role(connector, heading, anchor, text_box, role) ||
|
|
101
|
+
target_rightward_role(heading, anchor, text_box, role) || text_pos
|
|
97
102
|
mult_pos = target_multiplicity_seat(heading, anchor, mult_box) || mult_pos
|
|
98
103
|
end
|
|
99
104
|
|
|
@@ -187,7 +192,9 @@ module Ea
|
|
|
187
192
|
|
|
188
193
|
x, y = seat_corner(anchor, box)
|
|
189
194
|
inset, baseline = property_role_offsets(direction)
|
|
190
|
-
|
|
195
|
+
left = x - inset - width
|
|
196
|
+
left = [left, MIN_ROLE_X].max if direction == :up
|
|
197
|
+
[left, y + baseline]
|
|
191
198
|
end
|
|
192
199
|
|
|
193
200
|
def plain_upward_seat(connector, direction, anchor, box, role)
|
|
@@ -203,34 +210,59 @@ module Ea
|
|
|
203
210
|
[x - SEAT_X_INSET - width, y - 6]
|
|
204
211
|
end
|
|
205
212
|
|
|
206
|
-
def
|
|
207
|
-
return nil unless heading && heading[0].zero? && heading[1].
|
|
213
|
+
def target_vertical_role(connector, heading, anchor, box, role)
|
|
214
|
+
return nil unless heading && heading[0].zero? && !heading[1].zero?
|
|
215
|
+
|
|
216
|
+
direction = heading[1].negative? ? :target_up : :target_down
|
|
217
|
+
plain_vertical_role(connector, direction, anchor, box, role)
|
|
218
|
+
end
|
|
208
219
|
|
|
209
|
-
|
|
220
|
+
# A target whose leg arrives from the right seats its role like a
|
|
221
|
+
# source whose leg leaves to the right.
|
|
222
|
+
def target_rightward_role(heading, anchor, box, role)
|
|
223
|
+
return nil unless heading && heading[1].zero? && heading[0].positive?
|
|
224
|
+
return nil unless seat_font? && cambria_box?(box) && !role_empty?(role)
|
|
225
|
+
|
|
226
|
+
x, y = seat_corner(anchor, box)
|
|
227
|
+
[x + SEAT_X_INSET, y - 7]
|
|
210
228
|
end
|
|
211
229
|
|
|
212
230
|
def plain_vertical_role(connector, direction, anchor, box, role)
|
|
213
|
-
return nil unless %i[down target_up].include?(direction) &&
|
|
231
|
+
return nil unless %i[down target_up target_down].include?(direction) &&
|
|
232
|
+
plain_vertical_box?(box)
|
|
214
233
|
|
|
215
234
|
end_kind = direction == :down ? :source : :target
|
|
216
|
-
return nil unless plain_vertical_connector?(connector, end_kind)
|
|
235
|
+
return nil unless plain_vertical_connector?(connector, end_kind, direction)
|
|
217
236
|
|
|
218
237
|
width = Ea::Fonts::Metrics.text_length(role, font_size, family: font_family)
|
|
219
238
|
return nil unless width
|
|
220
239
|
|
|
221
240
|
x, y = seat_corner(anchor, box)
|
|
222
|
-
[x - width - 8, y + (
|
|
241
|
+
[x - width - 8, y + (direction == :target_up ? -7 : 15)]
|
|
223
242
|
end
|
|
224
243
|
|
|
225
244
|
def plain_vertical_box?(box)
|
|
226
245
|
seat_font? && cambria_box?(box) && [0, 1].include?(box["alignment"])
|
|
227
246
|
end
|
|
228
247
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
248
|
+
# A target leg that runs down seats only on the "LV" tree route
|
|
249
|
+
# with line width 2; every other vertical seat needs the plain
|
|
250
|
+
# width-0 route.
|
|
251
|
+
def plain_vertical_connector?(connector, end_kind, direction)
|
|
252
|
+
return false unless connector.connector_type == "Association" &&
|
|
253
|
+
connector_source_dest_stereotype(connector, end_kind).empty?
|
|
254
|
+
|
|
255
|
+
plain_vertical_style?(connector.style.to_h, direction)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def plain_vertical_style?(style, direction)
|
|
259
|
+
return false unless style[:mode] == "3"
|
|
260
|
+
|
|
261
|
+
if direction == :target_down
|
|
262
|
+
style[:lwidth] == "2" && style[:tree] == "LV"
|
|
263
|
+
else
|
|
264
|
+
style[:lwidth] == "0" && !style[:tree]
|
|
265
|
+
end
|
|
234
266
|
end
|
|
235
267
|
|
|
236
268
|
def plain_role_route?(style)
|
|
@@ -261,7 +293,7 @@ module Ea
|
|
|
261
293
|
case direction
|
|
262
294
|
when :left then left_property_route?(style)
|
|
263
295
|
when :down then downward_property_route?(style)
|
|
264
|
-
else style[:lwidth]
|
|
296
|
+
else %w[0 2].include?(style[:lwidth]) && style[:tree] != "OS"
|
|
265
297
|
end
|
|
266
298
|
end
|
|
267
299
|
|
|
@@ -97,7 +97,7 @@ module Ea
|
|
|
97
97
|
mult_box: tgt_mb,
|
|
98
98
|
anchor: target_pt, connector: connector,
|
|
99
99
|
end_kind: :target, tree: tree && tree[:target],
|
|
100
|
-
heading: stored_tgt &&
|
|
100
|
+
heading: stored_tgt && target_heading(connector, points),
|
|
101
101
|
stored_labels: stored_labels).each { |t| texts << t }
|
|
102
102
|
texts
|
|
103
103
|
end
|
|
@@ -126,6 +126,19 @@ module Ea
|
|
|
126
126
|
stored_upward_heading(connector, heading)
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
+
# A target dock that clips an axis-aligned stored leg into a slight
|
|
130
|
+
# diagonal keeps the stored leg's direction.
|
|
131
|
+
def target_heading(connector, points)
|
|
132
|
+
heading = leg_heading(points.reverse)
|
|
133
|
+
return heading unless points.size == 2 && heading.none?(&:zero?)
|
|
134
|
+
|
|
135
|
+
stored = waypoint_pairs(connector)
|
|
136
|
+
return heading unless stored.size == 2
|
|
137
|
+
|
|
138
|
+
original = leg_heading(stored.reverse)
|
|
139
|
+
original.any?(&:zero?) ? original : heading
|
|
140
|
+
end
|
|
141
|
+
|
|
129
142
|
def stored_upward_heading(connector, heading)
|
|
130
143
|
return heading unless heading[1].negative?
|
|
131
144
|
|
data/lib/ea/version.rb
CHANGED