ea 0.6.14 → 0.6.16

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: 80dad306c8a1e15bbabb2a01af81700b3ab4ca4010f1fd9c4d9f0e2136ccf834
4
- data.tar.gz: 07a78eefd9c86ef3b025621a6b89013d1f988529a2bcd835b465cd43e0b98fa3
3
+ metadata.gz: a8c5868e2f18d7a28583d8e862f15f7f3f46703012ba58914d273a12f390b357
4
+ data.tar.gz: 61024b7e2ddb30066b9daafcad0ba9b324451e70d2344db82ab2106fc16e53e5
5
5
  SHA512:
6
- metadata.gz: 274a1276005d3863049b801c9d044dd7aa4952749041b7ad6ed63b691771bb73779c0a1567a6068c436033c758c7beb59c14e39d7e95cc467058fd6b89f28065
7
- data.tar.gz: 6600d5ecef1586cd5a566236237b0a6fb1692357ec41a7a7f0b887d294e8ed757611a9473b920807b75c499feb0e107ae953447d01ff5e9c0843482e7a942ede
6
+ metadata.gz: 76574a19dbc1a01c1add8adca00fad58dc18b436ba28869564e5bab050609fc701b5cc0874d6693d9553e670120bd19582d91477a41981e7a2ed8812dd2b45a5
7
+ data.tar.gz: cfe049c8be3f068b1dedc0f65dff2fb2b856b0eee9eafa63efd2fdd088ab0a71bd7894ce86693cb06d574baae6c686a7aa5afca2f507f1c60ff0e8e8d36e6b5c
@@ -15,6 +15,8 @@ module Ea
15
15
  attribute :package_id, :string # containing package
16
16
  attribute :package_name, :string # containing package name
17
17
  attribute :is_abstract, :boolean, default: false
18
+ attribute :is_root, :boolean, default: false
19
+ attribute :is_leaf, :boolean, default: false
18
20
  attribute :visibility, :string
19
21
  attribute :model_kind, :string, default: -> { "classifier" }
20
22
  attribute :properties, Property, collection: true, initialize_empty: true
@@ -31,6 +33,8 @@ module Ea
31
33
  map "packageId", to: :package_id
32
34
  map "packageName", to: :package_name
33
35
  map "isAbstract", to: :is_abstract, render_default: true
36
+ map "isRoot", to: :is_root, render_default: true
37
+ map "isLeaf", to: :is_leaf, render_default: true
34
38
  map "visibility", to: :visibility
35
39
  map "modelKind", to: :model_kind, render_default: true
36
40
  map "properties", to: :properties, render_empty: true
@@ -94,6 +94,8 @@ module Ea
94
94
  package_name: package_name_for(object),
95
95
  qualified_name: qualified_name_for(object),
96
96
  is_abstract: abstract?(object),
97
+ is_root: object.isroot.to_i == 1,
98
+ is_leaf: object.isleaf.to_i == 1,
97
99
  visibility: visibility_from_scope(object.scope),
98
100
  properties: properties_for(object),
99
101
  operations: OperationBuilder.new(database).build_all_for(object),
@@ -377,15 +377,31 @@ module Ea
377
377
  element_edge_point(target_placement, :target, edge_out)
378
378
 
379
379
  intermediate = intermediate_waypoints(link_row)
380
- if edge_out == 2 && source_port && target_port
381
- # EA computes tree routes at DRAW TIME from current
382
- # element positions — the stored Path is stale for them
383
- # (hand-verified: SY 8542 stored corner (271,-42) vs EA
384
- # drawn (298,70)). Route: source port -> corner at
385
- # (target dock x, source exit y) -> target port.
380
+ if edge_out == 2 && source_port && target_port &&
381
+ !valid_tree_path?(intermediate, source_placement)
382
+ # EA recomputes tree routes at DRAW TIME from current
383
+ # element positions when the stored Path no longer lands
384
+ # on the source's facing edge (hand-verified: SY 8542's
385
+ # stale Path vs EA-drawn route; CityObjectGroup's current
386
+ # Path renders byte-exact and is kept). Computed route:
387
+ # source port -> corner at (target dock x, source exit
388
+ # y) -> target port.
386
389
  points = [[source_point[0], source_point[1]],
387
390
  [target_point[0], source_point[1]],
388
391
  [target_point[0], target_point[1]]]
392
+ elsif edge_out == 2 && intermediate.size == 1
393
+ # Current tree Path (bend on the source's facing edge):
394
+ # EA's draw-time route shape — source right edge at the
395
+ # bend's y, corner at the bend, target facing edge at the
396
+ # bend's x (byte-exact on CityObjectGroup).
397
+ src = bounds_from_rect(source_placement)
398
+ tgt = bounds_from_rect(target_placement)
399
+ bend = intermediate.first
400
+ going_up = tgt.y + tgt.height <= src.y
401
+ target_y = going_up ? tgt.y + tgt.height : tgt.y
402
+ points = [[src.x + src.width, bend[1]],
403
+ [bend[0], bend[1]],
404
+ [bend[0], target_y]]
389
405
  elsif intermediate.any?
390
406
  points = [source_point, *intermediate, target_point]
391
407
  elsif source_port || target_port
@@ -401,6 +417,19 @@ module Ea
401
417
  end
402
418
  end
403
419
 
420
+ # A tree Path is current when its bend height still falls
421
+ # within the source element's y-range (the exit height EA
422
+ # records); a bend outside the element is stale and EA
423
+ # re-derives the route at draw time. The bend's x may sit
424
+ # arbitrarily far right (trunk position).
425
+ def valid_tree_path?(intermediate, source_placement)
426
+ return false if intermediate.empty?
427
+
428
+ bend = intermediate.first
429
+ b = bounds_from_rect(source_placement)
430
+ bend[1].between?(b.y - 5, b.y + b.height + 5)
431
+ end
432
+
404
433
  # Absolute connection point EA docks the connector's visible
405
434
  # endpoint at, derived from the Geometry SX/SY (source) and
406
435
  # EX/EY (target) offsets. The offsets are measured inward from
@@ -20,6 +20,8 @@ module Ea
20
20
  attr_reader :bounds, :size, :header_lines_count,
21
21
  :attr_lines_count, :op_lines_count,
22
22
  :tagged_values_count,
23
+ :constraints_count,
24
+ :enum_literals_count,
23
25
  :header_top_padding, :header_line_offset,
24
26
  :divider_offset, :attr_line_offset,
25
27
  :attr_first_offset
@@ -27,6 +29,8 @@ module Ea
27
29
  def initialize(bounds:, size:, header_lines_count:,
28
30
  attr_lines_count:, op_lines_count:,
29
31
  tagged_values_count:,
32
+ constraints_count: 0,
33
+ enum_literals_count: 0,
30
34
  header_top_padding: 12,
31
35
  header_line_offset: 6,
32
36
  divider_offset: 8,
@@ -38,6 +42,8 @@ module Ea
38
42
  @attr_lines_count = attr_lines_count
39
43
  @op_lines_count = op_lines_count
40
44
  @tagged_values_count = tagged_values_count
45
+ @constraints_count = constraints_count
46
+ @enum_literals_count = enum_literals_count
41
47
  @header_top_padding = header_top_padding
42
48
  @header_line_offset = header_line_offset
43
49
  @divider_offset = divider_offset
@@ -102,11 +108,24 @@ module Ea
102
108
  # present). EA does not emit a separate divider — the
103
109
  # italic "tags" header marks the compartment.
104
110
  def tagged_value_first_y
105
- return nil unless tagged_values_count&.positive?
111
+ return nil unless tagged_values_count.to_i.positive? || constraints_count.to_i.positive?
106
112
 
107
- base = attr_bottom_y
108
- base = op_bottom_y if op_lines_count&.positive?
109
- base + size + 5
113
+ content_bottom_y + size + 5
114
+ end
115
+
116
+ # The lowest content edge among attributes, enum literals,
117
+ # and operations — the anchor for trailing compartments.
118
+ def content_bottom_y
119
+ bottoms = [attr_bottom_y]
120
+ bottoms << enum_literals_bottom_y if enum_literals_count.to_i.positive?
121
+ bottoms << op_bottom_y if op_lines_count.to_i.positive?
122
+ bottoms.compact.max
123
+ end
124
+
125
+ def enum_literals_bottom_y
126
+ return nil unless enum_literal_first_y
127
+
128
+ enum_literal_first_y + ([enum_literals_count, 1].max - 1) * (size + 6)
110
129
  end
111
130
  end
112
131
  end
@@ -82,7 +82,9 @@ module Ea
82
82
  op_lines = (is_classifier && show_operations?) ? Element::OperationRenderer.lines_for(classifier) : []
83
83
  geometry = compartment_geometry(bounds, size, attr_lines, op_lines,
84
84
  tagged_values_for(classifier).size,
85
- header_lines)
85
+ header_lines,
86
+ constraints_count: constraints_for(classifier).size,
87
+ enum_literals_count: enum_literals_for(classifier).size)
86
88
  # EA suppresses the whole attribute compartment when it does
87
89
  # not fit the stored box height (never truncates rows) - the
88
90
  # AOS box on CityFurniture hides its association property
@@ -92,7 +94,9 @@ module Ea
92
94
  attr_lines = []
93
95
  geometry = compartment_geometry(bounds, size, attr_lines, op_lines,
94
96
  tagged_values_for(classifier).size,
95
- header_lines)
97
+ header_lines,
98
+ constraints_count: constraints_for(classifier).size,
99
+ enum_literals_count: enum_literals_for(classifier).size)
96
100
  end
97
101
  # EA suppresses the operations compartment when its rows
98
102
  # overflow the stored box height (same rule as attributes).
@@ -101,7 +105,9 @@ module Ea
101
105
  op_lines = []
102
106
  geometry = compartment_geometry(bounds, size, attr_lines, op_lines,
103
107
  tagged_values_for(classifier).size,
104
- header_lines)
108
+ header_lines,
109
+ constraints_count: constraints_for(classifier).size,
110
+ enum_literals_count: enum_literals_for(classifier).size)
105
111
  end
106
112
  RenderContext.new(
107
113
  element: element,
@@ -130,12 +136,15 @@ module Ea
130
136
  end
131
137
 
132
138
  def compartment_geometry(bounds, size, attr_lines, op_lines,
133
- tagged_count, header_lines)
139
+ tagged_count, header_lines,
140
+ constraints_count: 0, enum_literals_count: 0)
134
141
  CompartmentGeometry.new(bounds: bounds, size: size,
135
142
  header_lines_count: header_lines.size,
136
143
  attr_lines_count: attr_lines.size,
137
144
  op_lines_count: op_lines.size,
138
145
  tagged_values_count: tagged_count,
146
+ constraints_count: constraints_count,
147
+ enum_literals_count: enum_literals_count,
139
148
  header_top_padding: header_padding_for(header_lines),
140
149
  header_line_offset: theme.compartments.header_line_offset,
141
150
  divider_offset: theme.compartments.divider_offset,
@@ -185,7 +194,24 @@ module Ea
185
194
  def constraints_for(classifier)
186
195
  return [] unless classifier.is_a?(Ea::Model::Classifier)
187
196
 
188
- classifier.constraints || []
197
+ marker_line = root_leaf_marker_for(classifier)
198
+ marker = marker_line ? [marker_constraint(marker_line)] : []
199
+ marker + (classifier.constraints || [])
200
+ end
201
+
202
+ # EA renders the element's Root/Leaf checkboxes as {root},
203
+ # {leaf}, or {root,leaf} first in the constraints compartment
204
+ # (t_object.IsRoot/IsLeaf — verified on LanguageCode boxes).
205
+ def root_leaf_marker_for(classifier)
206
+ parts = []
207
+ parts << "root" if classifier.is_root
208
+ parts << "leaf" if classifier.is_leaf
209
+ parts.empty? ? nil : "{#{parts.join(',')}}"
210
+ end
211
+
212
+ def marker_constraint(text)
213
+ name = text[1..-2]
214
+ Ea::Model::Constraint.new(name: name, kind: "Marker")
189
215
  end
190
216
 
191
217
  # EA renders the off-canvas parent classifier's name as an
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.14"
7
+ VERSION = "0.6.16"
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.14
4
+ version: 0.6.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.