ea 0.6.120 → 0.6.122
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 +4 -4
- data/config/themes/119.yml +2 -4
- data/lib/ea/fonts/carlito_metrics.json +1 -1
- data/lib/ea/fonts/metrics.rb +12 -2
- data/lib/ea/model/diagram_element.rb +3 -0
- data/lib/ea/sources/qea/diagram_builder.rb +3 -2
- data/lib/ea/svg/ea_emitter/color_resolver.rb +1 -1
- data/lib/ea/svg/ea_emitter/compartment/note_body.rb +13 -12
- data/lib/ea/svg/ea_emitter/compartment/shape.rb +12 -1
- data/lib/ea/svg/ea_emitter/connector_redock.rb +3 -2
- data/lib/ea/svg/ea_emitter/connectors.rb +51 -9
- data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +9 -6
- data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +4 -44
- data/lib/ea/svg/ea_emitter/elements.rb +34 -14
- data/lib/ea/svg/ea_emitter/font_resolver.rb +18 -3
- data/lib/ea/svg/ea_emitter/label/end_label.rb +69 -12
- data/lib/ea/svg/ea_emitter/labels.rb +24 -6
- data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +12 -1
- data/lib/ea/svg/ea_emitter/marker/diamond.rb +48 -3
- data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +1 -1
- data/lib/ea/svg/ea_emitter/marker/package_import.rb +6 -19
- data/lib/ea/svg/ea_emitter/marker/plus.rb +9 -17
- data/lib/ea/svg/ea_emitter/markers.rb +64 -73
- data/lib/ea/version.rb +1 -1
- metadata +1 -1
|
@@ -125,7 +125,7 @@ module Ea
|
|
|
125
125
|
# Unknown faces retain the stored-upward fallback.
|
|
126
126
|
def source_heading(connector, points)
|
|
127
127
|
heading = leg_heading(points)
|
|
128
|
-
return heading unless
|
|
128
|
+
return heading unless heading.none?(&:zero?) && source_face_route?(connector, points)
|
|
129
129
|
|
|
130
130
|
box = source_dock_bounds(connector)
|
|
131
131
|
face = box && source_dock_heading(connector, points.first, box)
|
|
@@ -134,6 +134,15 @@ module Ea
|
|
|
134
134
|
stored_upward_heading(connector, heading)
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
+
# Explicit source boxes preserve the heading of a manual bend. An
|
|
138
|
+
# omitted source box uses the same dock seat as a straight route.
|
|
139
|
+
def source_face_route?(connector, points)
|
|
140
|
+
return true if points.size == 2
|
|
141
|
+
|
|
142
|
+
boxes = connector.label_boxes || {}
|
|
143
|
+
end_boxes(boxes, :llt, :llb).nil? && !end_boxes(boxes, :lrt, :lrb).nil?
|
|
144
|
+
end
|
|
145
|
+
|
|
137
146
|
# A target dock that clips an axis-aligned stored leg into a slight
|
|
138
147
|
# diagonal keeps the stored leg's direction.
|
|
139
148
|
def target_heading(connector, points)
|
|
@@ -150,15 +159,24 @@ module Ea
|
|
|
150
159
|
# A saved external ray enters the source through the face it first
|
|
151
160
|
# meets, even when the clipped leg then runs back across the box.
|
|
152
161
|
def face_leads_leg?(connector, face, heading, box)
|
|
153
|
-
|
|
154
|
-
if face == [0, 1] && anchor && anchor.y > box.y + box.height &&
|
|
155
|
-
connector.style.to_h[:stored_association_ray] == true
|
|
156
|
-
return true
|
|
157
|
-
end
|
|
162
|
+
return true if saved_ray_beyond_face?(connector, face, box)
|
|
158
163
|
|
|
159
164
|
face.zip(heading).any? { |axis, delta| (axis * delta).positive? }
|
|
160
165
|
end
|
|
161
166
|
|
|
167
|
+
# A saved ray from beyond the bottom or right face meets that face
|
|
168
|
+
# first. The top face is excluded: its clipped leg keeps the anchor seat.
|
|
169
|
+
def saved_ray_beyond_face?(connector, face, box)
|
|
170
|
+
anchor = connector.source_anchor
|
|
171
|
+
return false unless anchor && connector.style.to_h[:stored_association_ray] == true
|
|
172
|
+
|
|
173
|
+
case face
|
|
174
|
+
when [0, 1] then anchor.y > box.y + box.height
|
|
175
|
+
when [1, 0] then anchor.x > box.x + box.width
|
|
176
|
+
else false
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
162
180
|
def stored_upward_heading(connector, heading)
|
|
163
181
|
return heading unless heading[1].negative?
|
|
164
182
|
|
|
@@ -4,7 +4,9 @@ module Ea
|
|
|
4
4
|
module Svg
|
|
5
5
|
module EaEmitter
|
|
6
6
|
module Marker
|
|
7
|
-
# Navigability arrow at the target end of an association
|
|
7
|
+
# Navigability arrow at the target end of an association, plus
|
|
8
|
+
# the diamond of an association whose end carries an aggregate
|
|
9
|
+
# flag (SourceIsAggregate/DestIsAggregate).
|
|
8
10
|
# Rendered as a `<path>` (not polygon) sharing style with the
|
|
9
11
|
# connector line. When both ends are navigable
|
|
10
12
|
# (bidirectional association), EA renders a separate arrow at
|
|
@@ -16,6 +18,14 @@ module Ea
|
|
|
16
18
|
|
|
17
19
|
def self.specs_for(connector, source, target, before_target,
|
|
18
20
|
after_source, relationship: nil)
|
|
21
|
+
Diamond.flagged_end_specs(relationship, source, target,
|
|
22
|
+
before_target, after_source) +
|
|
23
|
+
arrow_specs(connector, source, target, before_target,
|
|
24
|
+
after_source)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.arrow_specs(connector, source, target, before_target,
|
|
28
|
+
after_source)
|
|
19
29
|
# EA draws the navigability arrow on directed
|
|
20
30
|
# associations AND aggregations (333EFCCF: 6 aggregations
|
|
21
31
|
# render diamond + path arrow); Direction
|
|
@@ -33,6 +43,7 @@ module Ea
|
|
|
33
43
|
base = whole_end_at_source ? before_target : after_source
|
|
34
44
|
[Registry::Spec.new(shape: :arrow, anchor: anchor, base: base)]
|
|
35
45
|
end
|
|
46
|
+
private_class_method :arrow_specs
|
|
36
47
|
|
|
37
48
|
# EA marks truly bidirectional associations with
|
|
38
49
|
# direction="Bi-Directional" on t_connector. These get a
|
|
@@ -4,8 +4,9 @@ module Ea
|
|
|
4
4
|
module Svg
|
|
5
5
|
module EaEmitter
|
|
6
6
|
module Marker
|
|
7
|
-
# EA endpoint fields select the diamond end
|
|
8
|
-
# the arrow end
|
|
7
|
+
# EA endpoint fields select the diamond end and its fill (shared
|
|
8
|
+
# is hollow, composite is solid); direction selects the arrow end
|
|
9
|
+
# for target-owned aggregations.
|
|
9
10
|
class Diamond < Kind
|
|
10
11
|
def self.handles?(effective_type)
|
|
11
12
|
%w[Aggregation Composition].include?(effective_type)
|
|
@@ -15,15 +16,51 @@ module Ea
|
|
|
15
16
|
whole_at_target = whole_at_target?(connector, relationship)
|
|
16
17
|
whole_anchor = whole_at_target ? target : source
|
|
17
18
|
whole_base = whole_at_target ? before_target : after_source
|
|
18
|
-
specs = [Registry::Spec.new(shape:
|
|
19
|
+
specs = [Registry::Spec.new(shape: diamond_shape(connector, relationship, whole_at_target),
|
|
19
20
|
anchor: whole_anchor,
|
|
20
21
|
base: whole_base)]
|
|
21
22
|
return specs unless whole_at_target
|
|
23
|
+
return specs if arrow_on_target_diamond?(connector, relationship)
|
|
22
24
|
|
|
23
25
|
specs.concat(direction_specs(connector, source, target,
|
|
24
26
|
before_target, after_source))
|
|
25
27
|
end
|
|
26
28
|
|
|
29
|
+
# Diamonds for the ends of a plain association that carry an
|
|
30
|
+
# aggregate flag.
|
|
31
|
+
def self.flagged_end_specs(relationship, source, target,
|
|
32
|
+
before_target, after_source)
|
|
33
|
+
return [] unless relationship
|
|
34
|
+
|
|
35
|
+
{ target_aggregation: [target, before_target],
|
|
36
|
+
source_aggregation: [source, after_source] }.filter_map do |reader, (anchor, base)|
|
|
37
|
+
shape = flagged_shape(relationship.public_send(reader))
|
|
38
|
+
Registry::Spec.new(shape: shape, anchor: anchor, base: base) if shape
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.flagged_shape(kind)
|
|
43
|
+
case kind.to_s
|
|
44
|
+
when "shared" then :diamond_hollow
|
|
45
|
+
when "composite" then :diamond
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
private_class_method :flagged_shape
|
|
49
|
+
|
|
50
|
+
def self.diamond_shape(connector, relationship, whole_at_target)
|
|
51
|
+
kind = if relationship
|
|
52
|
+
whole_at_target ? relationship.target_aggregation : relationship.source_aggregation
|
|
53
|
+
end
|
|
54
|
+
kind = type_default_kind(connector) if kind.to_s.empty? || kind.to_s == "none"
|
|
55
|
+
kind.to_s == "shared" ? :diamond_hollow : :diamond
|
|
56
|
+
end
|
|
57
|
+
private_class_method :diamond_shape
|
|
58
|
+
|
|
59
|
+
def self.type_default_kind(connector)
|
|
60
|
+
connector.connector_type == "Composition" ? "composite" : "shared"
|
|
61
|
+
end
|
|
62
|
+
private_class_method :type_default_kind
|
|
63
|
+
|
|
27
64
|
def self.whole_at_target?(connector, relationship)
|
|
28
65
|
return true if dest_flagged?(relationship)
|
|
29
66
|
return false if source_flagged?(relationship)
|
|
@@ -31,6 +68,14 @@ module Ea
|
|
|
31
68
|
!whole_end_at_source?(connector)
|
|
32
69
|
end
|
|
33
70
|
|
|
71
|
+
# EA draws no arrow where a directed, dest-flagged aggregation
|
|
72
|
+
# would put it on its own target diamond.
|
|
73
|
+
def self.arrow_on_target_diamond?(connector, relationship)
|
|
74
|
+
connector.direction == "Source -> Destination" &&
|
|
75
|
+
dest_flagged?(relationship)
|
|
76
|
+
end
|
|
77
|
+
private_class_method :arrow_on_target_diamond?
|
|
78
|
+
|
|
34
79
|
def self.dest_flagged?(relationship)
|
|
35
80
|
relationship &&
|
|
36
81
|
%w[shared composite].include?(relationship.target_aggregation.to_s)
|
|
@@ -15,7 +15,7 @@ module Ea
|
|
|
15
15
|
class OpenTriangle < Kind
|
|
16
16
|
def self.handles?(effective_type)
|
|
17
17
|
%w[Generalization Realization Realisation Dependency
|
|
18
|
-
InformationFlow
|
|
18
|
+
InformationFlow].include?(effective_type)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def self.specs_for(connector, source, target, before_target, after_source, relationship: nil)
|
|
@@ -5,30 +5,17 @@ module Ea
|
|
|
5
5
|
module EaEmitter
|
|
6
6
|
module Marker
|
|
7
7
|
# PackageImport marker for «import» connectors between
|
|
8
|
-
# packages. EA draws
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# connector's geometry string carries explicit SX/SY offset
|
|
12
|
-
# fields. Auto-routed connectors (no SX/SY) render as a
|
|
13
|
-
# plain solid line with no package_anchor marker.
|
|
14
|
-
#
|
|
15
|
-
# Verified against both QEAs (output by the same EA run):
|
|
16
|
-
# - basic.qea Package Imports: SX=0;SY=0 present → marker
|
|
17
|
-
# - plateau Urban Planning ADE2: SX/SY absent → no marker
|
|
8
|
+
# packages. EA draws the dependency-style open arrowhead at the
|
|
9
|
+
# target (imported) end, whether or not the diagram link
|
|
10
|
+
# carries explicit SX/SY offsets.
|
|
18
11
|
class PackageImport < Kind
|
|
19
12
|
def self.handles?(effective_type)
|
|
20
13
|
effective_type == "Package"
|
|
21
14
|
end
|
|
22
15
|
|
|
23
|
-
def self.specs_for(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
[
|
|
27
|
-
Registry::Spec.new(shape: :package_anchor,
|
|
28
|
-
anchor: source, base: after_source),
|
|
29
|
-
Registry::Spec.new(shape: :arrow, anchor: target,
|
|
30
|
-
base: before_target)
|
|
31
|
-
]
|
|
16
|
+
def self.specs_for(_connector, _source, target, before_target, _after_source, **)
|
|
17
|
+
[Registry::Spec.new(shape: :dependency_arrow,
|
|
18
|
+
anchor: target, base: before_target)]
|
|
32
19
|
end
|
|
33
20
|
end
|
|
34
21
|
end
|
|
@@ -4,16 +4,12 @@ module Ea
|
|
|
4
4
|
module Svg
|
|
5
5
|
module EaEmitter
|
|
6
6
|
module Marker
|
|
7
|
-
#
|
|
8
|
-
# connector. EA
|
|
9
|
-
#
|
|
7
|
+
# Circled-plus marker at the container end of a Nesting
|
|
8
|
+
# connector. EA draws a white circle of radius ARM_LENGTH just
|
|
9
|
+
# outside the container edge with a plus (two crossed arms)
|
|
10
|
+
# through its centre; the connector starts at the nested
|
|
11
|
+
# element and ends at the container.
|
|
10
12
|
#
|
|
11
|
-
# Path encoding (two sub-paths in one <path>):
|
|
12
|
-
#
|
|
13
|
-
# M cx cy-8 L cx cy+8 (vertical arm, 16 tall)
|
|
14
|
-
# M cx+8 cy L cx-8 cy (horizontal arm, 16 wide)
|
|
15
|
-
#
|
|
16
|
-
# cx, cy = plus center, anchored at the source-end waypoint.
|
|
17
13
|
class Plus < Kind
|
|
18
14
|
ARM_LENGTH = 8
|
|
19
15
|
|
|
@@ -22,14 +18,10 @@ module Ea
|
|
|
22
18
|
end
|
|
23
19
|
|
|
24
20
|
def self.specs_for(connector, source, target, before_target, after_source, relationship: nil)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
whole_end_at_source = whole_end_at_source?(connector)
|
|
30
|
-
anchor = whole_end_at_source ? source : target
|
|
31
|
-
base = whole_end_at_source ? after_source : before_target
|
|
32
|
-
[Registry::Spec.new(shape: :plus, anchor: anchor, base: base)]
|
|
21
|
+
container_at_target = whole_end_at_source?(connector)
|
|
22
|
+
anchor = container_at_target ? target : source
|
|
23
|
+
base = container_at_target ? before_target : after_source
|
|
24
|
+
[Registry::Spec.new(shape: :nesting, anchor: anchor, base: base)]
|
|
33
25
|
end
|
|
34
26
|
end
|
|
35
27
|
end
|
|
@@ -9,7 +9,9 @@ module Ea
|
|
|
9
9
|
#
|
|
10
10
|
# Layer style keys:
|
|
11
11
|
# - :diamond_filled → 4-pt <polygon>, black fill
|
|
12
|
+
# - :diamond_hollow → 4-pt <polygon>, white fill
|
|
12
13
|
# - :triangle_open → 3-pt <polygon>, white fill
|
|
14
|
+
# - :nesting_circle → <ellipse> + plus <path>, white fill
|
|
13
15
|
# - :arrow → 3-pt <path>, no fill (shares style with
|
|
14
16
|
# connector lines — same stroke, no fill)
|
|
15
17
|
#
|
|
@@ -20,7 +22,7 @@ module Ea
|
|
|
20
22
|
DIAMOND_HALF_W = 5
|
|
21
23
|
DIAMOND_HALF_H = 10
|
|
22
24
|
TRI_HALF_BASE = 6
|
|
23
|
-
TRI_HEIGHT =
|
|
25
|
+
TRI_HEIGHT = 16
|
|
24
26
|
# EA's path arrowheads are height 15 corpus-wide, with
|
|
25
27
|
# distinct bases per connector kind: association
|
|
26
28
|
# navigability arrows base 12, dependency arrows base 10
|
|
@@ -48,22 +50,19 @@ module Ea
|
|
|
48
50
|
|
|
49
51
|
def layers
|
|
50
52
|
entries = visible_connectors.flat_map { |c| entries_for(c) }
|
|
51
|
-
return [] if entries.empty?
|
|
52
53
|
|
|
53
54
|
if grouped
|
|
54
|
-
entries.group_by
|
|
55
|
+
entries.group_by { |e| [e.style_key, e.stroke_width] }.map do |(key, width), grouped_entries|
|
|
55
56
|
# EA emits ONE marker per connector regardless of
|
|
56
57
|
# anchor overlap. Verified against plateau Bridge
|
|
57
58
|
# diagram: 9 generalizations fanning from the same
|
|
58
59
|
# parent anchor produce 9 distinct triangle polygons
|
|
59
60
|
# at the same coordinates. The earlier "dedup by
|
|
60
61
|
# anchor" hypothesis is disproven by reference SVGs.
|
|
61
|
-
|
|
62
|
-
style: style_for(key),
|
|
63
|
-
body: grouped_entries.map(&:body).join("\n "))
|
|
62
|
+
marker_layer(key, width, grouped_entries.map(&:body).join("\n "))
|
|
64
63
|
end
|
|
65
64
|
else
|
|
66
|
-
entries.map { |e|
|
|
65
|
+
entries.map { |e| marker_layer(e.style_key, e.stroke_width, e.body) }
|
|
67
66
|
end
|
|
68
67
|
end
|
|
69
68
|
|
|
@@ -86,7 +85,7 @@ module Ea
|
|
|
86
85
|
# for future use (e.g. overlap diagnostics) but no longer
|
|
87
86
|
# drives dedup — EA emits one marker per connector.
|
|
88
87
|
Entry = Struct.new(:style_key, :body, :anchor,
|
|
89
|
-
:connector_waypoint_count,
|
|
88
|
+
:connector_waypoint_count, :stroke_width,
|
|
90
89
|
keyword_init: true) do
|
|
91
90
|
def hash
|
|
92
91
|
anchor.hash
|
|
@@ -109,13 +108,17 @@ module Ea
|
|
|
109
108
|
style_key: :diamond_filled,
|
|
110
109
|
render: ->(m, anchor, base) { m.diamond_polygon(anchor, base) }
|
|
111
110
|
},
|
|
111
|
+
diamond_hollow: {
|
|
112
|
+
style_key: :diamond_hollow,
|
|
113
|
+
render: ->(m, anchor, base) { m.diamond_polygon(anchor, base) }
|
|
114
|
+
},
|
|
112
115
|
triangle: {
|
|
113
116
|
style_key: :triangle_open,
|
|
114
117
|
render: ->(m, anchor, base) { m.triangle_polygon(anchor, base) }
|
|
115
118
|
},
|
|
116
|
-
|
|
117
|
-
style_key: :
|
|
118
|
-
render: ->(m, anchor,
|
|
119
|
+
nesting: {
|
|
120
|
+
style_key: :nesting_circle,
|
|
121
|
+
render: ->(m, anchor, base) { m.nesting_marker(anchor, base) }
|
|
119
122
|
},
|
|
120
123
|
arrow: {
|
|
121
124
|
style_key: :connector_line,
|
|
@@ -124,10 +127,6 @@ module Ea
|
|
|
124
127
|
dependency_arrow: {
|
|
125
128
|
style_key: :connector_line,
|
|
126
129
|
render: ->(m, anchor, base) { m.dependency_arrow_path(anchor, base) }
|
|
127
|
-
},
|
|
128
|
-
package_anchor: {
|
|
129
|
-
style_key: :connector_line,
|
|
130
|
-
render: ->(m, anchor, base) { m.package_anchor_path(anchor, base) }
|
|
131
130
|
}
|
|
132
131
|
}.freeze
|
|
133
132
|
|
|
@@ -135,15 +134,22 @@ module Ea
|
|
|
135
134
|
# is shared, so only the fill and opacity vary.
|
|
136
135
|
STYLE_MAP = {
|
|
137
136
|
diamond_filled: { fill: "#000000", opacity: "1.00" },
|
|
137
|
+
diamond_hollow: { fill: "#FFFFFF", opacity: "1.00" },
|
|
138
138
|
triangle_open: { fill: "#FFFFFF", opacity: "1.00" },
|
|
139
|
+
nesting_circle: { fill: "#FFFFFF", opacity: "1.00" },
|
|
139
140
|
connector_line: { fill: "#000000", opacity: "0.00" }
|
|
140
141
|
}.freeze
|
|
141
142
|
|
|
142
|
-
def
|
|
143
|
+
def marker_layer(key, width, body)
|
|
144
|
+
Layer.new(style_key: Connectors.layer_key(key, width, stroke_width),
|
|
145
|
+
style: style_for(key, width), body: body)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def style_for(key, width = stroke_width)
|
|
143
149
|
spec = STYLE_MAP[key]
|
|
144
150
|
return nil unless spec
|
|
145
151
|
|
|
146
|
-
"stroke-width:#{
|
|
152
|
+
"stroke-width:#{width};stroke-linecap:round;stroke-linejoin:bevel; " \
|
|
147
153
|
"fill:#{spec[:fill]};fill-opacity:#{spec[:opacity]}; " \
|
|
148
154
|
"stroke:#000000; stroke-opacity:1.00"
|
|
149
155
|
end
|
|
@@ -164,7 +170,8 @@ module Ea
|
|
|
164
170
|
Entry.new(style_key: style_key_for(spec),
|
|
165
171
|
body: body,
|
|
166
172
|
anchor: anchor_for(spec),
|
|
167
|
-
connector_waypoint_count: waypoint_count
|
|
173
|
+
connector_waypoint_count: waypoint_count,
|
|
174
|
+
stroke_width: Connectors.stroke_width_for(connector, stroke_width))
|
|
168
175
|
end
|
|
169
176
|
end
|
|
170
177
|
|
|
@@ -279,22 +286,36 @@ module Ea
|
|
|
279
286
|
entry[:render].call(self, spec.anchor, spec.base)
|
|
280
287
|
end
|
|
281
288
|
|
|
282
|
-
#
|
|
283
|
-
#
|
|
284
|
-
#
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
289
|
+
# Nesting marker: a white circle tangent to the container edge
|
|
290
|
+
# at the anchor, with a plus through its centre. The plus is an
|
|
291
|
+
# unfilled path, so it stays visible on the circle's fill.
|
|
292
|
+
def nesting_marker(tip, base)
|
|
293
|
+
tx, ty = translate_point(tip)
|
|
294
|
+
bx, by = translate_point(base)
|
|
295
|
+
ux, uy = unit_vector(tx, ty, bx, by)
|
|
296
|
+
return nil unless ux
|
|
297
|
+
|
|
298
|
+
radius = Marker::Plus::ARM_LENGTH
|
|
299
|
+
cx_val = tx - ux * radius
|
|
300
|
+
cy_val = ty - uy * radius
|
|
301
|
+
circle = %(<ellipse cx="#{fixed(cx_val)}" cy="#{fixed(cy_val)}" ) +
|
|
302
|
+
%(rx="#{radius}.00" ry="#{radius}.00" shape-rendering="auto" />)
|
|
303
|
+
[circle, plus_path(cx_val, cy_val)].join("\n ")
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def plus_path(center_x, center_y)
|
|
307
|
+
arm = Marker::Plus::ARM_LENGTH
|
|
308
|
+
d = "M #{Canvas.coord(center_x)} #{Canvas.coord(center_y - arm)} " \
|
|
309
|
+
"L #{Canvas.coord(center_x)} #{Canvas.coord(center_y + arm)} " \
|
|
310
|
+
"M #{Canvas.coord(center_x + arm)} #{Canvas.coord(center_y)} " \
|
|
311
|
+
"L #{Canvas.coord(center_x - arm)} #{Canvas.coord(center_y)}"
|
|
295
312
|
%(<path d="#{d}" shape-rendering="auto"/>)
|
|
296
313
|
end
|
|
297
314
|
|
|
315
|
+
def fixed(value)
|
|
316
|
+
format("%<value>.2f", value: value)
|
|
317
|
+
end
|
|
318
|
+
|
|
298
319
|
def specs_for(connector)
|
|
299
320
|
points = waypoint_pairs(connector)
|
|
300
321
|
return [] if points.size < 2
|
|
@@ -343,10 +364,10 @@ module Ea
|
|
|
343
364
|
left_x = tx - ux * DIAMOND_HALF_H - (-uy) * DIAMOND_HALF_W
|
|
344
365
|
left_y = ty - uy * DIAMOND_HALF_H - ux * DIAMOND_HALF_W
|
|
345
366
|
pts = [
|
|
346
|
-
Canvas.coord(tx), Canvas.coord(ty),
|
|
347
|
-
Canvas.coord(right_x), Canvas.coord(right_y),
|
|
348
|
-
Canvas.coord(back_x), Canvas.coord(back_y),
|
|
349
|
-
Canvas.coord(left_x), Canvas.coord(left_y)
|
|
367
|
+
Canvas.coord(tx.round), Canvas.coord(ty.round),
|
|
368
|
+
Canvas.coord(right_x.round), Canvas.coord(right_y.round),
|
|
369
|
+
Canvas.coord(back_x.round), Canvas.coord(back_y.round),
|
|
370
|
+
Canvas.coord(left_x.round), Canvas.coord(left_y.round)
|
|
350
371
|
].join(" ")
|
|
351
372
|
%(<polygon points="#{pts}" shape-rendering="auto" style="fill-rule:evenodd;"/>)
|
|
352
373
|
end
|
|
@@ -364,9 +385,9 @@ module Ea
|
|
|
364
385
|
w2_x = back_x - (-uy) * TRI_HALF_BASE
|
|
365
386
|
w2_y = back_y - ux * TRI_HALF_BASE
|
|
366
387
|
pts = [
|
|
367
|
-
Canvas.coord(tx), Canvas.coord(ty),
|
|
368
|
-
Canvas.coord(w1_x), Canvas.coord(w1_y),
|
|
369
|
-
Canvas.coord(w2_x), Canvas.coord(w2_y)
|
|
388
|
+
Canvas.coord(tx.round), Canvas.coord(ty.round),
|
|
389
|
+
Canvas.coord(w1_x.round), Canvas.coord(w1_y.round),
|
|
390
|
+
Canvas.coord(w2_x.round), Canvas.coord(w2_y.round)
|
|
370
391
|
].join(" ")
|
|
371
392
|
%(<polygon points="#{pts}" shape-rendering="auto" style="fill-rule:evenodd;"/>)
|
|
372
393
|
end
|
|
@@ -383,39 +404,9 @@ module Ea
|
|
|
383
404
|
w1_y = back_y + ux * ARROW_HALF_BASE
|
|
384
405
|
w2_x = back_x - (-uy) * ARROW_HALF_BASE
|
|
385
406
|
w2_y = back_y - ux * ARROW_HALF_BASE
|
|
386
|
-
d = "M #{Canvas.coord(w1_x)} #{Canvas.coord(w1_y
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
# «import» source-end marker: a small closed trapezoid that
|
|
391
|
-
# visually represents the imported package. Shape is a 4-point
|
|
392
|
-
# polygon: top edge shorter than bottom edge, centered on the
|
|
393
|
-
# anchor point.
|
|
394
|
-
PACKAGE_ANCHOR_W = 5
|
|
395
|
-
PACKAGE_ANCHOR_H_TOP = 3
|
|
396
|
-
PACKAGE_ANCHOR_H_BOTTOM = 4
|
|
397
|
-
|
|
398
|
-
def package_anchor_path(tip, base)
|
|
399
|
-
tx, ty = translate_point(tip)
|
|
400
|
-
bx, by = translate_point(base)
|
|
401
|
-
ux, uy = unit_vector(tx, ty, bx, by)
|
|
402
|
-
return nil unless ux
|
|
403
|
-
|
|
404
|
-
back_x = tx - ux * PACKAGE_ANCHOR_H_BOTTOM
|
|
405
|
-
back_y = ty - uy * PACKAGE_ANCHOR_H_BOTTOM
|
|
406
|
-
tl_x = tx - ux * PACKAGE_ANCHOR_H_TOP + (-uy) * PACKAGE_ANCHOR_W
|
|
407
|
-
tl_y = ty - uy * PACKAGE_ANCHOR_H_TOP + ux * PACKAGE_ANCHOR_W
|
|
408
|
-
tr_x = tx - ux * PACKAGE_ANCHOR_H_TOP - (-uy) * PACKAGE_ANCHOR_W
|
|
409
|
-
tr_y = ty - uy * PACKAGE_ANCHOR_H_TOP - ux * PACKAGE_ANCHOR_W
|
|
410
|
-
bl_x = back_x + (-uy) * (PACKAGE_ANCHOR_W + 1)
|
|
411
|
-
bl_y = back_y + ux * (PACKAGE_ANCHOR_W + 1)
|
|
412
|
-
br_x = back_x - (-uy) * (PACKAGE_ANCHOR_W + 1)
|
|
413
|
-
br_y = back_y - ux * (PACKAGE_ANCHOR_W + 1)
|
|
414
|
-
d = "M #{Canvas.coord(tl_x)} #{Canvas.coord(tl_y)} " \
|
|
415
|
-
"L #{Canvas.coord(tr_x)} #{Canvas.coord(tr_y)} " \
|
|
416
|
-
"L #{Canvas.coord(br_x)} #{Canvas.coord(br_y)} " \
|
|
417
|
-
"L #{Canvas.coord(bl_x)} #{Canvas.coord(bl_y)} " \
|
|
418
|
-
"L #{Canvas.coord(tl_x)} #{Canvas.coord(tl_y)} Z"
|
|
407
|
+
d = "M #{Canvas.coord(w1_x.round)} #{Canvas.coord(w1_y.round)} " \
|
|
408
|
+
"L #{Canvas.coord(tx.round)} #{Canvas.coord(ty.round)} " \
|
|
409
|
+
"L #{Canvas.coord(w2_x.round)} #{Canvas.coord(w2_y.round)}"
|
|
419
410
|
%(<path d="#{d}" shape-rendering="auto"/>)
|
|
420
411
|
end
|
|
421
412
|
|
|
@@ -449,8 +440,8 @@ module Ea
|
|
|
449
440
|
|
|
450
441
|
[dx / len, dy / len]
|
|
451
442
|
end
|
|
452
|
-
public :diamond_polygon, :triangle_polygon, :
|
|
453
|
-
:arrow_path, :dependency_arrow_path
|
|
443
|
+
public :diamond_polygon, :triangle_polygon, :nesting_marker,
|
|
444
|
+
:arrow_path, :dependency_arrow_path
|
|
454
445
|
|
|
455
446
|
def translate_point(p)
|
|
456
447
|
return p unless @canvas
|
data/lib/ea/version.rb
CHANGED