Class: Lutaml::Ea::Diagram::LayoutEngine
- Inherits:
-
Object
- Object
- Lutaml::Ea::Diagram::LayoutEngine
- Includes:
- Util
- Defined in:
- lib/lutaml/ea/diagram/layout_engine.rb
Overview
Layout engine for positioning diagram elements
This engine calculates optimal positions for diagram elements based on their relationships and EA diagram data. It handles automatic layout for elements that don’t have explicit positions.
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_layout(elements, connectors = []) ⇒ Array
Apply automatic layout to elements without positions.
-
#apply_padding_to_bounds(bounds) ⇒ Hash
Apply padding to bounds.
-
#calculate_bounds(diagram_data) ⇒ Hash
Calculate bounds for the entire diagram.
-
#calculate_connector_bounds(connectors) ⇒ Hash?
Calculate min/max bounds from connector endpoints.
-
#calculate_element_position(element, related_elements = []) ⇒ Hash
Calculate optimal position for a single element.
-
#convert_ea_coordinates(diagram_object) ⇒ Object
deprecated
Deprecated.
Use DiagramPresenter coordinate handling instead
-
#initialize(options = {}) ⇒ LayoutEngine
constructor
A new instance of LayoutEngine.
-
#normalize_coordinates(elements) ⇒ Object
deprecated
Deprecated.
No longer needed in current architecture
Methods included from Util
#parse_ea_geometry, #parse_geometry_offsets
Constructor Details
#initialize(options = {}) ⇒ LayoutEngine
Returns a new instance of LayoutEngine.
23 24 25 26 27 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 23 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.
21 22 23 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 21 def element_height @element_height end |
#element_width ⇒ Object (readonly)
Returns the value of attribute element_width.
21 22 23 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 21 def element_width @element_width end |
#spacing ⇒ Object (readonly)
Returns the value of attribute spacing.
21 22 23 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 21 def spacing @spacing end |
Instance Method Details
#apply_layout(elements, connectors = []) ⇒ Array
Apply automatic layout to elements without positions
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 74 def apply_layout(elements, connectors = []) # rubocop:disable Metrics/MethodLength positioned_elements, unpositioned_elements = elements.partition do |e| e[:x] && e[:y] end # Apply force-directed layout for unpositioned elements if unpositioned_elements.any? positioned_elements += apply_force_directed_layout( unpositioned_elements, connectors, positioned_elements, ) end positioned_elements end |
#apply_padding_to_bounds(bounds) ⇒ Hash
Apply padding to bounds
59 60 61 62 63 64 65 66 67 68 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 59 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) ⇒ Hash
Calculate bounds for the entire diagram
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 32 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? # Find min/max coordinates 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) ⇒ Hash?
Calculate min/max bounds from connector endpoints
160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 160 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 = []) ⇒ Hash
Calculate optimal position for a single element
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 95 def calculate_element_position(element, = []) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity return element if element[:x] && element[:y] # Simple positioning: place to the right of related elements 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 |
#convert_ea_coordinates(diagram_object) ⇒ Object
Use DiagramPresenter coordinate handling instead
Convert EA coordinates (deprecated - now handled by DiagramPresenter)
119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 119 def convert_ea_coordinates(diagram_object) left = diagram_object.left || 0 top = diagram_object.top || 0 right = diagram_object.right || 100 bottom = diagram_object.bottom || 100 { x: left, y: top, width: right - left, height: bottom - top, } end |
#normalize_coordinates(elements) ⇒ Object
No longer needed in current architecture
Normalize coordinates (deprecated)
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 135 def normalize_coordinates(elements) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity return elements if elements.empty? largest_negative_x = elements.map { |e| e[:x] || 0 }.min largest_negative_y = elements.map { |e| e[:y] || 0 }.min offset_x = largest_negative_x.negative? ? -largest_negative_x : 0 offset_y = largest_negative_y.negative? ? -largest_negative_y : 0 return elements if offset_x.zero? && offset_y.zero? elements.each do |e| e[:x] = 0 if e[:x].nil? e[:y] = 0 if e[:y].nil? e[:width] = 0 if e[:width].nil? e[:height] = 0 if e[:height].nil? e[:x] = e[:x].to_i + offset_x e[:y] = e[:y].to_i + offset_y e[:width] = -e[:width] if e[:width].negative? e[:height] = -e[:height] if e[:height].negative? end end |