Class: Ea::Diagram::PathBuilder
- Inherits:
-
Object
- Object
- Ea::Diagram::PathBuilder
- Includes:
- Util
- Defined in:
- lib/ea/diagram/path_builder.rb
Overview
Path builder for connector rendering
This class calculates SVG path data for connectors between diagram elements, supporting various connector types and routing algorithms.
Instance Attribute Summary collapse
-
#connector ⇒ Object
readonly
Returns the value of attribute connector.
-
#source_element ⇒ Object
readonly
Returns the value of attribute source_element.
-
#target_element ⇒ Object
readonly
Returns the value of attribute target_element.
Instance Method Summary collapse
- #apply_offset(point, offsets, type) ⇒ Object
- #bezier_path ⇒ Object
-
#build_path ⇒ String
Build SVG path data for the connector.
-
#calculate_element_connection_point(element, type) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity.
-
#calculate_orthogonal_points ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/MethodLength.
- #geometry_has_waypoints? ⇒ Boolean
-
#initialize(connector, source_element = nil, target_element = nil) ⇒ PathBuilder
constructor
A new instance of PathBuilder.
-
#manhattan_path ⇒ Object
rubocop:disable Metrics/MethodLength.
- #orthogonal_path ⇒ Object
- #path_from_points(points) ⇒ Object
- #simple_connector? ⇒ Boolean
- #source_point ⇒ Object
- #straight_path ⇒ Object
- #target_point ⇒ Object
- #waypoint_path ⇒ Object
Methods included from Util
#parse_ea_geometry, #parse_geometry_offsets, #style_to_css
Constructor Details
#initialize(connector, source_element = nil, target_element = nil) ⇒ PathBuilder
Returns a new instance of PathBuilder.
15 16 17 18 19 |
# File 'lib/ea/diagram/path_builder.rb', line 15 def initialize(connector, source_element = nil, target_element = nil) @connector = connector @source_element = source_element @target_element = target_element end |
Instance Attribute Details
#connector ⇒ Object (readonly)
Returns the value of attribute connector.
13 14 15 |
# File 'lib/ea/diagram/path_builder.rb', line 13 def connector @connector end |
#source_element ⇒ Object (readonly)
Returns the value of attribute source_element.
13 14 15 |
# File 'lib/ea/diagram/path_builder.rb', line 13 def source_element @source_element end |
#target_element ⇒ Object (readonly)
Returns the value of attribute target_element.
13 14 15 |
# File 'lib/ea/diagram/path_builder.rb', line 13 def target_element @target_element end |
Instance Method Details
#apply_offset(point, offsets, type) ⇒ Object
188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/ea/diagram/path_builder.rb', line 188 def apply_offset(point, offsets, type) offset_x, offset_y = case type when :source [offsets[0], offsets[1]] when :target [offsets[2], offsets[3]] else [0, 0] end [point[0] + offset_x, point[1] + offset_y] end |
#bezier_path ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/ea/diagram/path_builder.rb', line 75 def bezier_path # Smooth curved path using Bezier curves x1, y1 = source_point x2, y2 = target_point # Control points for smooth curve cp1x = x1 + ((x2 - x1) * 0.3) cp1y = y1 cp2x = x2 - ((x2 - x1) * 0.3) cp2y = y2 "M #{x1},#{y1} C #{cp1x},#{cp1y} #{cp2x},#{cp2y} #{x2},#{y2}" end |
#build_path ⇒ String
Build SVG path data for the connector
23 24 25 26 27 28 29 30 31 32 |
# File 'lib/ea/diagram/path_builder.rb', line 23 def build_path return straight_path if simple_connector? return waypoint_path if geometry_has_waypoints? case connector[:routing_type] when "orthogonal" then orthogonal_path when "bezier" then bezier_path else manhattan_path end end |
#calculate_element_connection_point(element, type) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/ea/diagram/path_builder.rb', line 160 def calculate_element_connection_point(element, type) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity return [0, 0] unless element # Calculate connection point based on element bounds and # connector type x = element[:x] || 0 y = element[:y] || 0 width = element[:width] || 120 height = element[:height] || 80 point = case type when :source # Connect from right side for outgoing connectors [x + width, y + (height / 2)] when :target # Connect to left side for incoming connectors [x, y + (height / 2)] else [x + (width / 2), y + (height / 2)] end return point unless connector[:geometry] # Apply relative offsets if specified offsets = parse_geometry_offsets(connector[:geometry]) apply_offset(point, offsets, type) end |
#calculate_orthogonal_points ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/MethodLength
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/ea/diagram/path_builder.rb', line 89 def calculate_orthogonal_points # rubocop:disable Metrics/AbcSize,Metrics/MethodLength x1, y1 = source_point x2, y2 = target_point points = [[x1, y1]] # Determine direction based on relative positions if (x2 - x1).abs > (y2 - y1).abs # Horizontal first, then vertical points << [x1 + ((x2 - x1) / 2), y1] points << [x1 + ((x2 - x1) / 2), y2] else # Vertical first, then horizontal points << [x1, y1 + ((y2 - y1) / 2)] points << [x2, y1 + ((y2 - y1) / 2)] end points << [x2, y2] points end |
#geometry_has_waypoints? ⇒ Boolean
120 121 122 123 124 125 |
# File 'lib/ea/diagram/path_builder.rb', line 120 def geometry_has_waypoints? return false unless connector[:geometry] geometry_data = parse_ea_geometry(connector[:geometry]) geometry_data&.dig(:waypoints)&.any? end |
#manhattan_path ⇒ Object
rubocop:disable Metrics/MethodLength
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/ea/diagram/path_builder.rb', line 56 def manhattan_path # rubocop:disable Metrics/MethodLength # Manhattan distance routing with one bend x1, y1 = source_point x2, y2 = target_point # Calculate bend point (midpoint) bend_x = (x1 + x2) / 2 bend_y = (y1 + y2) / 2 # Choose bend direction to avoid elements if (x2 - x1).abs > (y2 - y1).abs # Horizontal bend "M #{x1},#{y1} L #{bend_x},#{y1} L #{bend_x},#{y2} L #{x2},#{y2}" else # Vertical bend "M #{x1},#{y1} L #{x1},#{bend_y} L #{x2},#{bend_y} L #{x2},#{y2}" end end |
#orthogonal_path ⇒ Object
50 51 52 53 54 |
# File 'lib/ea/diagram/path_builder.rb', line 50 def orthogonal_path # Right-angle routing points = calculate_orthogonal_points path_from_points(points) end |
#path_from_points(points) ⇒ Object
110 111 112 113 114 115 116 117 118 |
# File 'lib/ea/diagram/path_builder.rb', line 110 def path_from_points(points) return "" if points.empty? path = "M #{points[0][0]},#{points[0][1]}" points[1..].each do |point| path += " L #{point[0]},#{point[1]}" end path end |
#simple_connector? ⇒ Boolean
35 36 37 38 39 |
# File 'lib/ea/diagram/path_builder.rb', line 35 def simple_connector? # Use straight line if both elements have direct coordinates connector[:source_x] && connector[:source_y] && connector[:target_x] && connector[:target_y] end |
#source_point ⇒ Object
144 145 146 147 148 149 150 |
# File 'lib/ea/diagram/path_builder.rb', line 144 def source_point if connector[:source_x] && connector[:source_y] [connector[:source_x], connector[:source_y]] else calculate_element_connection_point(source_element, :source) end end |
#straight_path ⇒ Object
41 42 43 44 45 46 47 48 |
# File 'lib/ea/diagram/path_builder.rb', line 41 def straight_path x1 = connector[:source_x] || 0 y1 = connector[:source_y] || 0 x2 = connector[:target_x] || 100 y2 = connector[:target_y] || 100 "M #{x1},#{y1} L #{x2},#{y2}" end |
#target_point ⇒ Object
152 153 154 155 156 157 158 |
# File 'lib/ea/diagram/path_builder.rb', line 152 def target_point if connector[:target_x] && connector[:target_y] [connector[:target_x], connector[:target_y]] else calculate_element_connection_point(target_element, :target) end end |
#waypoint_path ⇒ Object
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/ea/diagram/path_builder.rb', line 127 def waypoint_path geometry_data = parse_ea_geometry(connector[:geometry]) points = [] sp = source_point points << sp if sp geometry_data[:waypoints].each do |wp| points << [wp[:x], wp[:y]] end tp = target_point points << tp if tp path_from_points(points) end |