Class: Archimate::Svg::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/archimate/svg/connection.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection) ⇒ Connection

Returns a new instance of Connection.



9
10
11
12
# File 'lib/archimate/svg/connection.rb', line 9

def initialize(connection)
  @connection = connection
  @css_style = CssStyle.new(connection.style)
end

Instance Attribute Details

#connectionObject (readonly)

Returns the value of attribute connection.



6
7
8
# File 'lib/archimate/svg/connection.rb', line 6

def connection
  @connection
end

#css_styleObject (readonly)

Returns the value of attribute css_style.



7
8
9
# File 'lib/archimate/svg/connection.rb', line 7

def css_style
  @css_style
end

Instance Method Details

#calc_points(a, b) ⇒ Object

a: Bounds b: Bounds



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/archimate/svg/connection.rb', line 141

def calc_points(a, b)
  ax_range = a.x_range
  bx_range = b.x_range

  overlap_x_center = ranges_overlap(ax_range, bx_range)

  if overlap_x_center
    ax = bx = overlap_x_center
  elsif b.is_right_of?(a)
    ax = a.right
    bx = b.left
  else
    ax = a.left
    bx = b.right
  end

  ay_range = a.y_range
  by_range = b.y_range

  overlap_y_center = ranges_overlap(ay_range, by_range)

  if overlap_y_center
    ay = by = overlap_y_center
  elsif b.is_above?(a)
    ay = a.top
    by = b.bottom
  else
    ay = a.bottom
    by = b.top
  end

  [Point.new(ax, ay), Point.new(bx, by)]
end

#css_classify(str) ⇒ Object

TODO: StringRefinements refinement isn’t working in this class, so added this method here. Investigate.



85
86
87
88
89
90
# File 'lib/archimate/svg/connection.rb', line 85

def css_classify(str)
  str.gsub(/::/, '/')
     .gsub(/([A-Z]+)([A-Z][a-z])/, '\1-\2')
     .gsub(/([a-z\d])([A-Z])/, '\1-\2')
     .downcase
end

#idObject



72
73
74
# File 'lib/archimate/svg/connection.rb', line 72

def id
  connection.relationship_element&.id || connection.id
end

#line_styleObject



41
42
43
44
45
46
47
48
49
50
# File 'lib/archimate/svg/connection.rb', line 41

def line_style
  style = connection.style
  return "" if style.nil?
  {
    "stroke": style.line_color&.to_rgba,
    "stroke-width": style.line_width
  }.delete_if { |_key, value| value.nil? }
    .map { |key, value| "#{key}:#{value};" }
    .join("")
end

#line_to(point) ⇒ Object



179
180
181
# File 'lib/archimate/svg/connection.rb', line 179

def line_to(point)
  "L #{point.x} #{point.y}"
end

#move_to(point) ⇒ Object



175
176
177
# File 'lib/archimate/svg/connection.rb', line 175

def move_to(point)
  "M #{point.x} #{point.y}"
end

#path_attrsObject



63
64
65
66
67
68
69
70
# File 'lib/archimate/svg/connection.rb', line 63

def path_attrs
  {
    id: id,
    class: path_class,
    d: path_d,
    style: line_style
  }
end

#path_classObject

Look at the type (if any of the path and set the class appropriately)



77
78
79
80
81
82
# File 'lib/archimate/svg/connection.rb', line 77

def path_class
  [
    "archimate",
    css_classify(connection&.relationship_element&.type || "default")
  ].join("-") + " archimate-relationship"
end

#path_dObject

builds the line coordinates for the path rough drawing is the point at center of first element, point of each bendpoint, and center of end element First naive implementation if left/right range of both overlap, use centerpoint of overlap range as x val if top/bottom range of both overlap, use centerpoint of overlap range as y val



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/archimate/svg/connection.rb', line 104

def path_d
  source_bounds = connection.source_element&.absolute_position || DataModel::Bounds.zero
  target_bounds = connection.target_element&.absolute_position || DataModel::Bounds.zero

  start_point = DataModel::Bounds.new(
    x: source_bounds.left + source_bounds.width / 2.0,
    y: source_bounds.top + source_bounds.height / 2.0,
    width: 0,
    height: 0
  )
  bp_bounds = connection.bendpoints.map do |bp|
    DataModel::Bounds.new(
      x: start_point.x + (bp.x || 0),
      y: start_point.y + (bp.y || 0),
      width: 0,
      height: 0
    )
  end
  bp_bounds = bp_bounds.reject do |bounds|
    bounds.inside?(source_bounds) || bounds.inside?(target_bounds)
  end

  bounds = [source_bounds].concat(bp_bounds) << target_bounds

  points = []
  a = bounds.shift
  until bounds.empty?
    b = bounds.shift
    points.concat(calc_points(a, b))
    a = b
  end
  points.uniq!
  [move_to(points.shift)].concat(points.map { |pt| line_to(pt) }).join(" ")
end

#ranges_overlap(r1, r2) ⇒ Object



92
93
94
95
96
97
# File 'lib/archimate/svg/connection.rb', line 92

def ranges_overlap(r1, r2)
  begin_max = [r1, r2].map(&:begin).max
  end_min = [r1, r2].map(&:end).min
  return nil if begin_max > end_min
  (begin_max + end_min) / 2.0
end

#render(svg) ⇒ Object



14
15
16
17
18
19
# File 'lib/archimate/svg/connection.rb', line 14

def render(svg)
  Nokogiri::XML::Builder.with(svg) do |xml|
    to_svg(xml)
  end
  svg
end

#text_positionObject



52
53
54
55
56
57
58
59
60
61
# File 'lib/archimate/svg/connection.rb', line 52

def text_position
  case connection.style
  when 0
    "10%"
  when 1
    "90%"
  else
    "50%"
  end
end

#to_svg(xml) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/archimate/svg/connection.rb', line 21

def to_svg(xml)
  return if connection.source_element.nodes.include?(connection.target_element)
  xml.path(path_attrs) do
    xml.title @connection.description
  end

  name = connection&.relationship_element&.name&.strip
  return if name.nil? || name.empty?
  xml.text_(
    class: "archimate-relationship-name",
    dy: -2,
    "text-anchor" => "middle",
    style: css_style.text
  ) do
    xml.textPath(startOffset: text_position, "xlink:href" => "##{id}") do
      xml.text name
    end
  end
end