ea 0.6.103 → 0.6.105

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: 82b98433dd845c2048541c65f723faa1d9a82c38bd9045a440948722e85f7ab3
4
- data.tar.gz: aa1bcc2c3d2b8876a21f200e1c638fddabe43a5f80ad694b801e9277d38fbb7d
3
+ metadata.gz: 430568b3607d439af39bac818b48ee27caa207875c7f2015e3a6f5689419680b
4
+ data.tar.gz: dfb835e2208d50a51caa01acb96a30e2561f5ccbdf6e83a6a1c55ec368c4b5b8
5
5
  SHA512:
6
- metadata.gz: 571d077f47d8c101f6285b88b351cfcdc965337bdaec200fb40cf6eb7b00531992b4f850d03faeb403e4b4fb13039d8eafdf83552b0e0c1f930ad826657bb401
7
- data.tar.gz: 8f67bc90328fce18c5918b0bf471937d56a4e8730b9df67b0c6bcc5422902b5fd527c585b0fa89d83aea8e1b3dcbc48f5687b5995640b78eb4910824499f85ec
6
+ metadata.gz: ce35bed8f6b13d1f799d8c99b550ad70d19453e2f2827888c1dce54b246dc9a938baa52cfe2d24f9fb9f093ecce1a86b52c4dbecbb60b397777c592cc61ea654
7
+ data.tar.gz: 22747dc14774fbfaa984efba1c88078619d8ab6bc74b4d06d5276074a93957dccc071963574aad3ff272377d043e51e613723708d0fa48318acf6558ec920de1
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ class DiagramBuilder
7
+ # Stored mathematical docking for auto-routed associations.
8
+ module AutoRouting
9
+ private
10
+
11
+ # Nonuniform horizontal offsets seat the route at the source row.
12
+ # Uniform offsets retain the shared-overlap routing contract.
13
+ def source_row_association_ray(model, link_row, diagram_row, geom, context)
14
+ return model if context || !source_row_association?(model, link_row, geom)
15
+
16
+ origins = source_row_origins(*stored_ray_boxes(link_row, diagram_row), geom)
17
+ return model unless origins
18
+
19
+ model.source_anchor, model.target_anchor = origins
20
+ model.waypoints = origins.map { |point| Ea::Model::Waypoint.new(position: point) }
21
+ model.style[:overlap_ray] = true
22
+ model
23
+ end
24
+
25
+ # When the shared row misses the target, retain the physical ports
26
+ # and let the emitter clip their ray before inserting the elbow.
27
+ def auto_port_elbow(model, link_row, diagram_row, geom, context)
28
+ return model if context || !auto_port_elbow_candidate?(model, link_row, geom)
29
+
30
+ source, target = stored_ray_boxes(link_row, diagram_row)
31
+ origins = [stored_ray_origin(source, geom[:sx], geom[:sy]),
32
+ stored_ray_origin(target, geom[:ex], geom[:ey])]
33
+ return model unless auto_port_elbow_origins?(source, target, origins, geom)
34
+
35
+ apply_auto_port_elbow(model, origins)
36
+ end
37
+
38
+ def apply_auto_port_elbow(model, origins)
39
+ model.source_anchor, model.target_anchor = origins
40
+ model.waypoints = origins.map { |point| Ea::Model::Waypoint.new(position: point) }
41
+ model.style = model.style.to_h.merge(association_ray_outline: true, auto_port_elbow: true)
42
+ model
43
+ end
44
+
45
+ def auto_port_elbow_candidate?(model, link_row, geom)
46
+ auto_port_elbow_model?(model) && link_row.path.to_s.empty? &&
47
+ source_row_style?(model.style.to_h) && uniform_overlap_offsets?(geom) && geom[:edge] == 4
48
+ end
49
+
50
+ def auto_port_elbow_model?(model)
51
+ model.connector_type == "Association" && model.has_geometry_offsets &&
52
+ model.source_anchor && model.target_anchor && model.waypoints.size == 2
53
+ end
54
+
55
+ def auto_port_elbow_origins?(source, target, origins, geom)
56
+ return false if overlap_origins(source, target, geom)
57
+ return false unless source_row_faces_target?(source, target, geom[:edge])
58
+ return false unless origins.first.x == source.x - 1
59
+ return false if within_height?(origins.first.y, target)
60
+ return false unless crosses_target_horizontal_edge?(origins, target)
61
+
62
+ [source, target].zip(origins).all? { |box, origin| inside_box?(origin, grown_bounds(box, 1)) }
63
+ end
64
+
65
+ def crosses_target_horizontal_edge?(origins, target)
66
+ outline = grown_bounds(target, 1)
67
+ edge = Ea::Model::Bounds.new(x: outline.x, width: outline.width, height: 0,
68
+ y: origins.first.y < outline.y ? outline.y : outline.y + outline.height)
69
+ segment_crosses_box?(*origins, edge)
70
+ end
71
+
72
+ def source_row_origins(source, target, geom)
73
+ return nil unless source_row_faces_target?(source, target, geom[:edge])
74
+
75
+ row = source_row_y(source, geom)
76
+ return nil unless [source, target].all? { |box| within_height?(row, box) }
77
+
78
+ [source, target].map { |box| Ea::Model::Point.new(x: box.x + box.width / 2.0, y: row) }
79
+ end
80
+
81
+ def source_row_y(source, geom)
82
+ (source.y + source.height / 2.0 - geom[:sy]).floor
83
+ end
84
+
85
+ def source_row_faces_target?(source, target, edge)
86
+ if edge == 2
87
+ source.x + source.width < target.x
88
+ else
89
+ target.x + target.width < source.x
90
+ end
91
+ end
92
+
93
+ def source_row_association?(model, link_row, geom)
94
+ model.connector_type == "Association" && model.has_geometry_offsets &&
95
+ model.source_anchor && model.target_anchor &&
96
+ link_row.path.to_s.empty? && source_row_style?(model.style.to_h) &&
97
+ source_row_offsets?(geom)
98
+ end
99
+
100
+ def source_row_style?(style)
101
+ style[:mode] == "2" && style[:lwidth] == "2" && !style[:tree]
102
+ end
103
+
104
+ def source_row_offsets?(geom)
105
+ geom.values_at(:sx, :sy, :ex, :ey).all? &&
106
+ [2, 4].include?(geom[:edge]) && geom[:sy] != geom[:ey]
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ class DiagramBuilder
7
+ # Restore a saved square axis when its target remains on the outline
8
+ # but the source has moved beyond the saved endpoint into the gap.
9
+ module StoredSquarePorts
10
+ private
11
+
12
+ def restore_stored_square_ports(model, link_row, diagram_row, geom, context)
13
+ return model if context || !stored_square_port_style?(model, link_row, geom)
14
+
15
+ connector = database.find_connector(link_row.connectorid)
16
+ points = stored_square_port_coordinates(connector)
17
+ boxes = stored_square_port_boxes(connector, diagram_row)
18
+ return model unless points && boxes
19
+
20
+ ends = reclip_stored_square_source(points, *boxes)
21
+ return model unless ends
22
+
23
+ model.source_anchor, model.target_anchor = ends
24
+ model.waypoints = ends.map { |point| Ea::Model::Waypoint.new(position: point) }
25
+ model
26
+ end
27
+
28
+ def stored_square_port_style?(model, link_row, geom)
29
+ style = model.style.to_h
30
+ model.connector_type == "Association" && link_row.path.to_s.empty? &&
31
+ style.values_at(:mode, :tree, :lwidth) == %w[3 OS 2] &&
32
+ geom.values_at(:edge, :sx, :sy, :ex, :ey) == [1, 0, 0, 0, 0]
33
+ end
34
+
35
+ def stored_square_port_coordinates(connector)
36
+ return unless connector
37
+
38
+ values = [connector.ptstartx, connector.ptstarty, connector.ptendx, connector.ptendy]
39
+ return if values.any?(&:nil?)
40
+
41
+ [[values[0], -values[1]], [values[2], -values[3]]]
42
+ end
43
+
44
+ def stored_square_port_boxes(connector, diagram_row)
45
+ return unless connector
46
+
47
+ placements = [source_placement_for(connector, diagram_row),
48
+ target_placement_for(connector, diagram_row)]
49
+ placements.map { |placement| bounds_from_rect(placement) } if placements.all?
50
+ end
51
+
52
+ def reclip_stored_square_source(points, source, target)
53
+ first, last = points
54
+ return unless first[0] == last[0] && square_axis_inside_boxes?(first[0], source, target)
55
+
56
+ return unless stored_square_source_in_gap?(points, source, target)
57
+
58
+ [Ea::Model::Point.new(x: first[0], y: source.y),
59
+ Ea::Model::Point.new(x: last[0], y: last[1])]
60
+ end
61
+
62
+ def stored_square_source_in_gap?(points, source, target)
63
+ first, last = points
64
+ target_edge = target.y + target.height + 1
65
+ last[1] == target_edge && target_edge < first[1] && first[1] < source.y
66
+ end
67
+
68
+ def square_axis_inside_boxes?(axis, source, target)
69
+ [source, target].all? do |box|
70
+ box.width.positive? && box.height.positive? && axis.between?(box.x, box.x + box.width)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -11,8 +11,13 @@ module Ea
11
11
  # styles).
12
12
  class DiagramBuilder
13
13
  autoload :Routing, "ea/sources/qea/diagram_builder/routing"
14
+ autoload :AutoRouting, "ea/sources/qea/diagram_builder/auto_routing"
15
+ autoload :StoredSquarePorts, "ea/sources/qea/diagram_builder/stored_square_ports"
14
16
  include Routing
17
+ include AutoRouting
18
+ include StoredSquarePorts
15
19
  private_constant :Routing
20
+ private_constant :AutoRouting, :StoredSquarePorts
16
21
 
17
22
  attr_reader :database
18
23
 
@@ -436,8 +441,11 @@ module Ea
436
441
  model = mode2_overlap_ray(model, link_row, diagram_row, geom, context)
437
442
  model = association_stored_ray(model, link_row, diagram_row, geom, context)
438
443
  model = correct_stored_ray_origins(model, link_row, diagram_row, geom, context)
444
+ model = source_row_association_ray(model, link_row, diagram_row, geom, context)
439
445
  model = overlap_ray_origins(model, link_row, diagram_row, geom, context)
440
- mark_default_square_ray(model, link_row, geom, context)
446
+ model = auto_port_elbow(model, link_row, diagram_row, geom, context)
447
+ model = mark_default_square_ray(model, link_row, geom, context)
448
+ restore_stored_square_ports(model, link_row, diagram_row, geom, context)
441
449
  end
442
450
 
443
451
  # Ray origin for one connector end: the element's center
@@ -60,9 +60,26 @@ module Ea
60
60
  pairs = expanded ? candidate : original
61
61
  target = expanded ? outline.last : target_bounds
62
62
  pairs = default_square_pairs(connector, points, source_bounds, target, pairs)
63
- stored_top_pairs(connector, expanded ? outline.first : source_bounds, pairs)
63
+ source = expanded ? outline.first : source_bounds
64
+ pairs = stored_top_pairs(connector, source, pairs)
65
+ auto_port_elbow_pairs(connector, source, target, pairs)
64
66
  end
65
67
 
68
+ # Auto routing turns at the clipped target column when the source
69
+ # leaves a side edge and the target enters through its top or bottom.
70
+ def auto_port_elbow_pairs(connector, source, target, pairs)
71
+ return pairs unless connector.style.to_h[:auto_port_elbow] == true
72
+
73
+ first, last = pairs
74
+ return pairs unless on_side_edge?(first, source) &&
75
+ [target.y, target.y + target.height].include?(last[1]) &&
76
+ last[0].between?(target.x, target.x + target.width)
77
+ return pairs if first[0] == last[0] || first[1] == last[1]
78
+
79
+ [first, [last[0], first[1]], last]
80
+ end
81
+ private_class_method :auto_port_elbow_pairs
82
+
66
83
  # EDGE=1 explicitly seats an external source ray on the top edge;
67
84
  # its first contact can instead be the bottom when its origin is below.
68
85
  def stored_top_pairs(connector, source, pairs)
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.103"
7
+ VERSION = "0.6.105"
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.103
4
+ version: 0.6.105
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -817,7 +817,9 @@ files:
817
817
  - lib/ea/sources/qea/connector_relationship_map.rb
818
818
  - lib/ea/sources/qea/context_diagram_support.rb
819
819
  - lib/ea/sources/qea/diagram_builder.rb
820
+ - lib/ea/sources/qea/diagram_builder/auto_routing.rb
820
821
  - lib/ea/sources/qea/diagram_builder/routing.rb
822
+ - lib/ea/sources/qea/diagram_builder/stored_square_ports.rb
821
823
  - lib/ea/sources/qea/diagram_style_parser.rb
822
824
  - lib/ea/sources/qea/id_normalizer.rb
823
825
  - lib/ea/sources/qea/instance_builder.rb