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.
21 22 23 24 25 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 21 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.
19 20 21 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 19 def element_height @element_height end |
#element_width ⇒ Object (readonly)
Returns the value of attribute element_width.
19 20 21 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 19 def element_width @element_width end |
#spacing ⇒ Object (readonly)
Returns the value of attribute spacing.
19 20 21 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 19 def spacing @spacing end |
Instance Method Details
#apply_layout(elements, connectors = []) ⇒ Array
Apply automatic layout to elements without positions
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 72 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
57 58 59 60 61 62 63 64 65 66 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 57 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
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 30 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
158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 158 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
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 93 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)
117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 117 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)
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/lutaml/ea/diagram/layout_engine.rb', line 133 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 |