ea 0.6.53 → 0.6.55

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: '007348c214ad1a2ed4226d46e55a528dafcb12d9e4b5eae9e4d3a3e7d13b5c8c'
4
- data.tar.gz: 7b6af86fe7a76f8661c993b2559345d1538ef5fda6f4fa912f6925be4e4a79f2
3
+ metadata.gz: 7d4c834fa91906a0866bf29561214b7c9d140cb4b2a7106eb332f4ec029e56f1
4
+ data.tar.gz: e99cb2ff135675ec69d47397c102bd67f07925d8e59565bbabf6b96fb70cb60d
5
5
  SHA512:
6
- metadata.gz: 4b75f8f3f76d3c4433f147c7f9afba023c84f7961e5c901465c6d237a8775817d130596b4c75714d0bc367bc7605e5db1b356bc8bff2d23a45b723e77deaadf3
7
- data.tar.gz: e6d556ef5bdc04dad2fe196d26114672d7f7b0c9c5e580588279697f3ec5c45538d0fd0729a5847716dd01b98acb64a529aa1d19c38363a99fc307168e5554c6
6
+ metadata.gz: 302544eb3d7bc17747114aac54f1aacb3b91c6f87340c699d4c35bb8eace2f76c9a4766fd675eb1d58d0c5faa039624124ee5d7dca75d36b7a2e6e109d9aef2c
7
+ data.tar.gz: e5a86d18db02e980a690a2653529f17cd87b431ba7d7aee03683abe6f1fc3de9d4881347817735365a53575e234a91e0cca36364552a39d24e9b725a06932017
@@ -44,6 +44,13 @@ module Ea
44
44
  # when the geometry string carries no offsets (auto-routed).
45
45
  attribute :source_port, Ea::Model::Point
46
46
  attribute :target_port, Ea::Model::Point
47
+ # Ray origins for the drawn line: element center translated by
48
+ # the stored Geometry SX/SY / EX/EY offsets. EA clips the
49
+ # segment between the two anchors at the DRAWN outlines
50
+ # (F851A657: anchors center+(-29,0)/center+(-29,0) clip to EA's
51
+ # (297,788)/(433,514) within 1.5px).
52
+ attribute :source_anchor, Ea::Model::Point
53
+ attribute :target_anchor, Ea::Model::Point
47
54
 
48
55
  json do
49
56
  map "id", to: :id
@@ -57,6 +64,8 @@ module Ea
57
64
  map "targetEdge", to: :target_edge
58
65
  map "sourcePort", to: :source_port
59
66
  map "targetPort", to: :target_port
67
+ map "sourceAnchor", to: :source_anchor
68
+ map "targetAnchor", to: :target_anchor
60
69
  map "connectorType", to: :connector_type
61
70
  map "direction", to: :direction
62
71
  map "sourceStereotype", to: :source_stereotype
@@ -111,26 +111,27 @@ module Ea
111
111
  value.nil? || value.to_s.empty? ? nil : value
112
112
  end
113
113
 
114
- # EA stores the element font size in tenths of a point in the
115
- # objectstyle string, SHIFTED one point up from what it
116
- # renders: explicit fontsz 100 renders 9pt (0D31AED1: every
117
- # element stores 100, all 321 texts publish at 9pt), 70
118
- # renders 6pt, 140 renders 13pt. 0 means "use default", and
119
- # 80/90 (8pt / 9pt) are EA's classic defaults the dialogs
120
- # write back when the user never customized — such elements
121
- # render at the theme size. Only genuinely custom sizes are
122
- # surfaced as explicit element fonts.
123
- USE_DEFAULT_FONTSZ = [0, 80, 90].freeze
114
+ # EA stores the element font size in tenths of a point and
115
+ # renders it through GDI's double rounding: pixel height =
116
+ # floor(tenths * 96 / 720), then point size =
117
+ # floor(pixels * 72 / 96). 70 -> 9px -> 6pt (F3660305 Text),
118
+ # 80 -> 10px -> 7pt, 90 -> 12px -> 9pt (4EBDE645's PI_*
119
+ # classes), 100 -> 13px -> 9pt (0D31AED1's all-100 elements
120
+ # publish 321 texts at 9pt), 120 -> 16px -> 12pt
121
+ # (412BC89E's Boundary title), 140 -> 18px -> 13pt.
122
+ # fontsz=0 ("use default", written back by the font dialog)
123
+ # renders at the 7pt theme size.
124
+ USE_DEFAULT_FONTSZ = [0].freeze
125
+ GDI_DPI = 96
124
126
 
125
127
  def font_size_from(style_hash)
126
128
  raw = style_hash[:fontsz]
127
129
  return nil if raw.nil? || raw.to_s.empty?
128
130
 
129
- return nil if raw.to_i.zero?
130
-
131
131
  return nil if USE_DEFAULT_FONTSZ.include?(raw.to_i)
132
132
 
133
- (raw.to_f / 10).round - 1
133
+ pixels = (raw.to_f * GDI_DPI / 720).floor
134
+ (pixels * 72.0 / GDI_DPI).floor
134
135
  end
135
136
 
136
137
  def build_connectors(diagram_row)
@@ -372,6 +373,10 @@ module Ea
372
373
  target_stereotype: connector&.deststereotype,
373
374
  waypoints: context ? context_waypoints_for(connector, diagram_row) :
374
375
  waypoints_for_link(link_row, diagram_row),
376
+ source_anchor: port_anchor_for(connector, diagram_row,
377
+ geom, :source),
378
+ target_anchor: port_anchor_for(connector, diagram_row,
379
+ geom, :target),
375
380
  source_port: connection_port(diagram_row, connector, :source, geom),
376
381
  target_port: connection_port(diagram_row, connector, :target, geom),
377
382
  label_boxes: context ? {} : (geom[:label_boxes] || {}),
@@ -383,6 +388,26 @@ module Ea
383
388
  )
384
389
  end
385
390
 
391
+ # Ray origin for one connector end: the element's center
392
+ # translated by the stored SX/SY (source) or EX/EY (target)
393
+ # offset. EA clips the anchor-to-anchor segment at the drawn
394
+ # outlines.
395
+ def port_anchor_for(connector, diagram_row, geom, end_kind)
396
+ return nil unless connector
397
+
398
+ object_id = end_kind == :source ? connector.start_object_id :
399
+ connector.end_object_id
400
+ placement = diagram_object_placement(diagram_row.diagram_id,
401
+ object_id)
402
+ return nil unless placement
403
+
404
+ bounds = bounds_from_rect(placement)
405
+ dx = (end_kind == :source ? geom[:sx] : geom[:ex]).to_i
406
+ dy = (end_kind == :source ? geom[:sy] : geom[:ey]).to_i
407
+ Ea::Model::Point.new(x: bounds.x + bounds.width / 2 + dx,
408
+ y: bounds.y + bounds.height / 2 + dy)
409
+ end
410
+
386
411
  # Context diagrams re-route every drawn connector as a
387
412
  # direct line at draw time: the center-to-center ray clipped
388
413
  # at both box outlines. The stored SX/SY/EDGE geometry is
@@ -19,11 +19,12 @@ module Ea
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)
22
+ return nil unless direct?(connector) || two_point?(connector)
23
23
 
24
24
  points = waypoint_pairs(connector)
25
25
  return nil unless points.size == 2
26
26
 
27
+
27
28
  rel = relationship_for(connector, model_index)
28
29
  return nil unless rel
29
30
 
@@ -35,8 +36,45 @@ module Ea
35
36
  return nil unless source_bounds && target_bounds
36
37
  return nil if source_ref == target_ref
37
38
 
38
- [edge_point(source_bounds, target_bounds),
39
- edge_point(target_bounds, source_bounds)]
39
+ sa = connector.source_anchor
40
+ ta = connector.target_anchor
41
+ if sa && ta
42
+ [anchor_exit(sa, source_bounds, ta),
43
+ anchor_exit(ta, target_bounds, sa)]
44
+ else
45
+ [edge_point(source_bounds, target_bounds),
46
+ edge_point(target_bounds, source_bounds)]
47
+ end
48
+ end
49
+
50
+ # Exit point of the anchor-to-anchor segment through a box's
51
+ # outline: smallest positive t whose crossing lies on the
52
+ # rectangle perimeter.
53
+ def anchor_exit(anchor, bounds, other)
54
+ ax, ay = anchor.x.to_f, anchor.y.to_f
55
+ ox, oy = other.x.to_f, other.y.to_f
56
+ dx, dy = ox - ax, oy - ay
57
+ candidates = []
58
+ if dx != 0
59
+ [bounds.x, bounds.x + bounds.width].each do |x|
60
+ t = (x - ax) / dx
61
+ candidates << [t, x, ay + t * dy] if t.positive?
62
+ end
63
+ end
64
+ if dy != 0
65
+ [bounds.y, bounds.y + bounds.height].each do |y|
66
+ t = (y - ay) / dy
67
+ candidates << [t, ax + t * dx, y] if t.positive?
68
+ end
69
+ end
70
+ on_perimeter = candidates.select do |_t, x, y|
71
+ x >= bounds.x - 0.01 && x <= bounds.x + bounds.width + 0.01 &&
72
+ y >= bounds.y - 0.01 && y <= bounds.y + bounds.height + 0.01
73
+ end
74
+ best = on_perimeter.min_by(&:first)
75
+ # EA floors the dock coordinates (F851A657: 297.75 -> 297,
76
+ # 433.7 -> 433) - a rounded-up dock is a visible miss.
77
+ best ? [best[1].floor, best[2].floor] : [ax.floor, ay.floor]
40
78
  end
41
79
 
42
80
  # EA never docks a horizontal connector line at the header
@@ -85,6 +123,12 @@ module Ea
85
123
  %w[direct regenerated].any? { |k| style.key?(k) }
86
124
  end
87
125
 
126
+ # EXPERIMENT: every auto-laid 2-waypoint connector re-docks
127
+ # as the center-to-center ray clipped at the drawn outlines.
128
+ def two_point?(connector)
129
+ (connector.waypoints || []).size == 2
130
+ end
131
+
88
132
  def waypoint_pairs(connector)
89
133
  (connector.waypoints || []).filter_map do |wp|
90
134
  next unless wp.position
@@ -126,11 +170,11 @@ module Ea
126
170
  if dx.abs * bounds.height > dy.abs * bounds.width
127
171
  x = dx.positive? ? bounds.x + bounds.width : bounds.x
128
172
  y = safe_y(cx, cy, dx, dy, x)
129
- [x, y]
173
+ [x, y.floor]
130
174
  else
131
175
  y = dy.positive? ? bounds.y + bounds.height : bounds.y
132
176
  x = safe_x(cx, cy, dx, dy, y)
133
- [x, y]
177
+ [x.floor, y]
134
178
  end
135
179
  end
136
180
 
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.53"
7
+ VERSION = "0.6.55"
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.53
4
+ version: 0.6.55
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.