Class: Ea::Diagram::LayoutEngine
- Inherits:
-
Object
- Object
- Ea::Diagram::LayoutEngine
- Includes:
- Util
- Defined in:
- lib/ea/diagram/layout_engine.rb
Overview
Layout engine for positioning diagram elements
Constant Summary collapse
- DEFAULT_SPACING =
50- DEFAULT_PADDING =
20- ELEMENT_WIDTH =
120- ELEMENT_HEIGHT =
80
Instance Attribute Summary collapse
-
#element_height ⇒ Object
readonly
Returns the value of attribute element_height.
-
#element_width ⇒ Object
readonly
Returns the value of attribute element_width.
-
#spacing ⇒ Object
readonly
Returns the value of attribute spacing.
Instance Method Summary collapse
-
#apply_force_directed_layout(elements, _connectors, fixed_elements) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics:MethodLength.
-
#apply_layout(elements, connectors = []) ⇒ Object
rubocop:disable Metrics/MethodLength.
-
#apply_padding_to_bounds(bounds) ⇒ Object
rubocop:disable Metrics/AbcSize.
-
#calculate_bounds(diagram_data) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity.
-
#calculate_connector_bounds(connectors) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity.
-
#calculate_element_position(element, related_elements = []) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity.
-
#connector_endpoints(conn) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity.
- #element_height_for(element) ⇒ Object
- #element_width_for(element) ⇒ Object
-
#initialize(options = {}) ⇒ LayoutEngine
constructor
A new instance of LayoutEngine.
Methods included from Util
#parse_ea_geometry, #parse_geometry_offsets, #style_to_css
Constructor Details
#initialize(options = {}) ⇒ LayoutEngine
Returns a new instance of LayoutEngine.
16 17 18 19 20 |
# File 'lib/ea/diagram/layout_engine.rb', line 16 def initialize( = {}) @spacing = [:spacing] || DEFAULT_SPACING @element_width = [:element_width] || ELEMENT_WIDTH @element_height = [:element_height] || ELEMENT_HEIGHT end |
Instance Attribute Details
#element_height ⇒ Object (readonly)
Returns the value of attribute element_height.
14 15 16 |
# File 'lib/ea/diagram/layout_engine.rb', line 14 def element_height @element_height end |
#element_width ⇒ Object (readonly)
Returns the value of attribute element_width.
14 15 16 |
# File 'lib/ea/diagram/layout_engine.rb', line 14 def element_width @element_width end |
#spacing ⇒ Object (readonly)
Returns the value of attribute spacing.
14 15 16 |
# File 'lib/ea/diagram/layout_engine.rb', line 14 def spacing @spacing end |
Instance Method Details
#apply_force_directed_layout(elements, _connectors, fixed_elements) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics:MethodLength
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/ea/diagram/layout_engine.rb', line 147 def apply_force_directed_layout(elements, _connectors, fixed_elements) # rubocop:disable Metrics/AbcSize,Metrics:MethodLength positioned = [] elements.each_with_index do |element, index| cols = Math.sqrt(elements.size).ceil row = index / cols col = index % cols x = col * (ELEMENT_WIDTH + spacing) y = row * (ELEMENT_HEIGHT + spacing) if fixed_elements.any? x += fixed_elements.map do |e| (e[:x] || 0) + element_width_for(e) end.max + (spacing * 2) end positioned << element.merge(x: x, y: y) end positioned end |
#apply_layout(elements, connectors = []) ⇒ Object
rubocop:disable Metrics/MethodLength
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/ea/diagram/layout_engine.rb', line 56 def apply_layout(elements, connectors = []) # rubocop:disable Metrics/MethodLength positioned_elements, unpositioned_elements = elements.partition do |e| e[:x] && e[:y] end if unpositioned_elements.any? positioned_elements += apply_force_directed_layout( unpositioned_elements, connectors, positioned_elements, ) end positioned_elements end |
#apply_padding_to_bounds(bounds) ⇒ Object
rubocop:disable Metrics/AbcSize
45 46 47 48 49 50 51 52 53 54 |
# File 'lib/ea/diagram/layout_engine.rb', line 45 def apply_padding_to_bounds(bounds) # rubocop:disable Metrics/AbcSize padding_x = [bounds[:width] * 0.05, DEFAULT_PADDING].max padding_y = [bounds[:height] * 0.05, DEFAULT_PADDING].max { x: bounds[:x] - padding_x, y: bounds[:y] - padding_y, width: bounds[:width] + (padding_x * 2), height: bounds[:height] + (padding_y * 2), } end |
#calculate_bounds(diagram_data) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/ea/diagram/layout_engine.rb', line 22 def calculate_bounds(diagram_data) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity elements = diagram_data[:elements] || [] return { x: 0, y: 0, width: 400, height: 300 } if elements.empty? min_x = elements.map { |e| e[:x] || 0 }.min min_y = elements.map { |e| e[:y] || 0 }.min max_x = elements.map do |e| (e[:x] || 0) + element_width_for(e) end.max max_y = elements.map do |e| (e[:y] || 0) + element_height_for(e) end.max apply_padding_to_bounds( { x: min_x, y: min_y, width: max_x - min_x, height: max_y - min_y, }, ) end |
#calculate_connector_bounds(connectors) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/ea/diagram/layout_engine.rb', line 89 def calculate_connector_bounds(connectors) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity return nil if connectors.empty? valid = connectors.select do |c| c[:source_element] && c[:target_element] && c[:geometry] end return nil if valid.empty? points = valid.flat_map { |conn| connector_endpoints(conn) } xs = points.map(&:first) ys = points.map(&:last) { min_x: xs.min, max_x: xs.max, min_y: ys.min, max_y: ys.max } end |
#calculate_element_position(element, related_elements = []) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/ea/diagram/layout_engine.rb', line 72 def calculate_element_position(element, = []) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity return element if element[:x] && element[:y] if .any? max_x = .map do |e| (e[:x] || 0) + element_width_for(e) end.max element[:x] = max_x + spacing element[:y] = .first[:y] || 0 else element[:x] = 0 element[:y] = 0 end element end |
#connector_endpoints(conn) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/ea/diagram/layout_engine.rb', line 104 def connector_endpoints(conn) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity src = conn[:source_element] tgt = conn[:target_element] sx, sy, ex, ey = parse_geometry_offsets(conn[:geometry]) src_point = [(src[:x] || 0) + (src[:width] || 120) + sx, (src[:y] || 0) + ((src[:height] || 80) / 2) + sy] tgt_point = [(tgt[:x] || 0) + ex, (tgt[:y] || 0) + ((tgt[:height] || 80) / 2) + ey] [src_point, tgt_point] end |
#element_height_for(element) ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/ea/diagram/layout_engine.rb', line 132 def element_height_for(element) if element[:height] return element[:height].zero? ? ELEMENT_HEIGHT : element[:height] end case element[:type] when "class" (element[:operations]&.size.to_i * 15) + ELEMENT_HEIGHT when "package" ELEMENT_HEIGHT - 10 else ELEMENT_HEIGHT end end |
#element_width_for(element) ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/ea/diagram/layout_engine.rb', line 117 def element_width_for(element) if element[:width] return element[:width].zero? ? ELEMENT_WIDTH : element[:width] end case element[:type] when "class" (element[:attributes]&.size.to_i * 10) + ELEMENT_WIDTH when "package" ELEMENT_WIDTH + 20 else ELEMENT_WIDTH end end |