elkrb 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +11 -0
  4. data/Gemfile +13 -0
  5. data/README.adoc +1028 -0
  6. data/Rakefile +64 -0
  7. data/benchmarks/README.md +172 -0
  8. data/benchmarks/elkjs_benchmark.js +140 -0
  9. data/benchmarks/elkrb_benchmark.rb +145 -0
  10. data/benchmarks/fixtures/graphs.json +10777 -0
  11. data/benchmarks/generate_report.rb +241 -0
  12. data/benchmarks/generate_test_graphs.rb +154 -0
  13. data/benchmarks/results/elkrb_results.json +280 -0
  14. data/benchmarks/results/elkrb_summary.json +285 -0
  15. data/elkrb.gemspec +39 -0
  16. data/examples/dot_export_demo.rb +133 -0
  17. data/examples/hierarchical_graph.rb +19 -0
  18. data/examples/layout_constraints_demo.rb +272 -0
  19. data/examples/port_constraints_demo.rb +291 -0
  20. data/examples/self_loop_demo.rb +391 -0
  21. data/examples/simple_graph.rb +50 -0
  22. data/examples/spline_routing_demo.rb +235 -0
  23. data/exe/elkrb +8 -0
  24. data/lib/elkrb/cli.rb +224 -0
  25. data/lib/elkrb/commands/batch_command.rb +66 -0
  26. data/lib/elkrb/commands/convert_command.rb +130 -0
  27. data/lib/elkrb/commands/diagram_command.rb +208 -0
  28. data/lib/elkrb/commands/render_command.rb +52 -0
  29. data/lib/elkrb/commands/validate_command.rb +241 -0
  30. data/lib/elkrb/errors.rb +30 -0
  31. data/lib/elkrb/geometry/bezier.rb +163 -0
  32. data/lib/elkrb/geometry/dimension.rb +32 -0
  33. data/lib/elkrb/geometry/point.rb +68 -0
  34. data/lib/elkrb/geometry/rectangle.rb +86 -0
  35. data/lib/elkrb/geometry/vector.rb +67 -0
  36. data/lib/elkrb/graph/edge.rb +95 -0
  37. data/lib/elkrb/graph/graph.rb +90 -0
  38. data/lib/elkrb/graph/label.rb +45 -0
  39. data/lib/elkrb/graph/layout_options.rb +247 -0
  40. data/lib/elkrb/graph/node.rb +79 -0
  41. data/lib/elkrb/graph/node_constraints.rb +107 -0
  42. data/lib/elkrb/graph/port.rb +104 -0
  43. data/lib/elkrb/graphviz_wrapper.rb +133 -0
  44. data/lib/elkrb/layout/algorithm_registry.rb +57 -0
  45. data/lib/elkrb/layout/algorithms/base_algorithm.rb +208 -0
  46. data/lib/elkrb/layout/algorithms/box.rb +47 -0
  47. data/lib/elkrb/layout/algorithms/disco.rb +206 -0
  48. data/lib/elkrb/layout/algorithms/fixed.rb +32 -0
  49. data/lib/elkrb/layout/algorithms/force.rb +165 -0
  50. data/lib/elkrb/layout/algorithms/layered/cycle_breaker.rb +86 -0
  51. data/lib/elkrb/layout/algorithms/layered/layer_assigner.rb +96 -0
  52. data/lib/elkrb/layout/algorithms/layered/node_placer.rb +77 -0
  53. data/lib/elkrb/layout/algorithms/layered.rb +49 -0
  54. data/lib/elkrb/layout/algorithms/libavoid.rb +389 -0
  55. data/lib/elkrb/layout/algorithms/mrtree.rb +144 -0
  56. data/lib/elkrb/layout/algorithms/radial.rb +64 -0
  57. data/lib/elkrb/layout/algorithms/random.rb +43 -0
  58. data/lib/elkrb/layout/algorithms/rectpacking.rb +93 -0
  59. data/lib/elkrb/layout/algorithms/spore_compaction.rb +139 -0
  60. data/lib/elkrb/layout/algorithms/spore_overlap.rb +117 -0
  61. data/lib/elkrb/layout/algorithms/stress.rb +176 -0
  62. data/lib/elkrb/layout/algorithms/topdown_packing.rb +183 -0
  63. data/lib/elkrb/layout/algorithms/vertiflex.rb +174 -0
  64. data/lib/elkrb/layout/constraints/alignment_constraint.rb +150 -0
  65. data/lib/elkrb/layout/constraints/base_constraint.rb +72 -0
  66. data/lib/elkrb/layout/constraints/constraint_processor.rb +134 -0
  67. data/lib/elkrb/layout/constraints/fixed_position_constraint.rb +87 -0
  68. data/lib/elkrb/layout/constraints/layer_constraint.rb +71 -0
  69. data/lib/elkrb/layout/constraints/relative_position_constraint.rb +110 -0
  70. data/lib/elkrb/layout/edge_router.rb +935 -0
  71. data/lib/elkrb/layout/hierarchical_processor.rb +299 -0
  72. data/lib/elkrb/layout/label_placer.rb +338 -0
  73. data/lib/elkrb/layout/layout_engine.rb +170 -0
  74. data/lib/elkrb/layout/port_constraint_processor.rb +173 -0
  75. data/lib/elkrb/options/elk_padding.rb +94 -0
  76. data/lib/elkrb/options/k_vector.rb +100 -0
  77. data/lib/elkrb/options/k_vector_chain.rb +135 -0
  78. data/lib/elkrb/parsers/elkt_parser.rb +248 -0
  79. data/lib/elkrb/serializers/dot_serializer.rb +339 -0
  80. data/lib/elkrb/serializers/elkt_serializer.rb +236 -0
  81. data/lib/elkrb/version.rb +5 -0
  82. data/lib/elkrb.rb +509 -0
  83. data/sig/elkrb/constraints.rbs +114 -0
  84. data/sig/elkrb/geometry.rbs +61 -0
  85. data/sig/elkrb/graph.rbs +112 -0
  86. data/sig/elkrb/layout.rbs +107 -0
  87. data/sig/elkrb/options.rbs +81 -0
  88. data/sig/elkrb.rbs +32 -0
  89. metadata +179 -0
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_constraint"
4
+
5
+ module Elkrb
6
+ module Layout
7
+ module Constraints
8
+ # Relative position constraint
9
+ #
10
+ # Positions a node relative to another node with a specified offset.
11
+ # The constrained node will be positioned at:
12
+ # x = reference_node.x + offset.x
13
+ # y = reference_node.y + offset.y
14
+ #
15
+ # @example
16
+ # api_node.constraints = NodeConstraints.new(
17
+ # relative_to: "backend_service",
18
+ # relative_offset: RelativeOffset.new(x: 150, y: 0)
19
+ # )
20
+ # # api_node will be 150px to the right of backend_service
21
+ class RelativePositionConstraint < BaseConstraint
22
+ # Apply relative position constraint
23
+ #
24
+ # Positions nodes relative to their reference nodes.
25
+ # Processes in order of position_priority to handle chains.
26
+ #
27
+ # @param graph [Graph::Graph] The graph
28
+ # @return [Graph::Graph] The modified graph
29
+ def apply(graph)
30
+ nodes_with_relative = all_nodes(graph).select do |node|
31
+ node.constraints&.relative_to &&
32
+ node.constraints.relative_offset
33
+ end
34
+
35
+ # Sort by priority (higher priority processed first)
36
+ nodes_with_relative.sort_by! do |node|
37
+ -(node.constraints.position_priority || 0)
38
+ end
39
+
40
+ # Apply relative positioning
41
+ nodes_with_relative.each do |node|
42
+ apply_relative_position(node, graph)
43
+ end
44
+
45
+ graph
46
+ end
47
+
48
+ # Validate relative position constraints
49
+ #
50
+ # Checks that reference nodes exist and positions are correct.
51
+ #
52
+ # @param graph [Graph::Graph] The graph to validate
53
+ # @return [Array<String>] List of validation errors
54
+ def validate(graph)
55
+ errors = []
56
+
57
+ all_nodes(graph).each do |node|
58
+ next unless node.constraints&.relative_to
59
+
60
+ ref_id = node.constraints.relative_to
61
+ ref_node = find_node(graph, ref_id)
62
+
63
+ if ref_node.nil?
64
+ errors << "Node '#{node.id}' has relative_to constraint " \
65
+ "referencing '#{ref_id}' which doesn't exist"
66
+ next
67
+ end
68
+
69
+ # Validate position if offset is specified
70
+ if node.constraints.relative_offset
71
+ expected_x = ref_node.x + node.constraints.relative_offset.x
72
+ expected_y = ref_node.y + node.constraints.relative_offset.y
73
+
74
+ tolerance = 0.01
75
+ if (node.x - expected_x).abs > tolerance ||
76
+ (node.y - expected_y).abs > tolerance
77
+ errors << "Node '#{node.id}' relative position incorrect. " \
78
+ "Expected (#{expected_x}, #{expected_y}), " \
79
+ "got (#{node.x}, #{node.y})"
80
+ end
81
+ end
82
+ end
83
+
84
+ errors
85
+ end
86
+
87
+ private
88
+
89
+ # Apply relative position to a single node
90
+ def apply_relative_position(node, graph)
91
+ ref_id = node.constraints.relative_to
92
+ ref_node = find_node(graph, ref_id)
93
+
94
+ unless ref_node
95
+ warn "Warning: Node '#{node.id}' references non-existent " \
96
+ "node '#{ref_id}' for relative positioning"
97
+ return
98
+ end
99
+
100
+ offset = node.constraints.relative_offset
101
+ return unless offset
102
+
103
+ # Calculate and set new position
104
+ node.x = ref_node.x + offset.x
105
+ node.y = ref_node.y + offset.y
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end