Power System Platform  2026w10a-beta
Loading...
Searching...
No Matches
GraphAutoLayout.cpp
1#include "GraphAutoLayout.h"
2//#include "ImportForm.h"
3
4GraphAutoLayout::GraphAutoLayout() {}
5
6GraphAutoLayout::~GraphAutoLayout() {}
7
8GraphAutoLayout::GraphAutoLayout(std::vector<ParseMatpower::BusData*> busData,
9 std::vector<ParseMatpower::BranchData*> branchData)
10{
11 m_busData = busData;
12 m_branchData = branchData;
13 for(auto it = m_busData.begin(); it != m_busData.end(); ++it) {
14 GraphLayoutNode node;
15 m_nodes.push_back(node);
16 }
17 for(auto it = m_branchData.begin(); it != m_branchData.end(); ++it) {
18 ParseMatpower::BranchData* branch = *it;
19 float weight = 1.0;
20 if(branch->tap > 1e-3) weight = 2.0;
21 AddLink(branch->busConnections.first - 1, branch->busConnections.second - 1, weight);
22 }
23}
24
25void GraphAutoLayout::AddLink(size_t index1, size_t index2, float weight)
26{
27 // If the two indices are the same, or if the weight is zero, do nothing (no link)
28 if(index1 == index2 || weight == 0.f) { return; }
29 // If the number of nodes is lesser than one of the indices, extend the nodes vector
30 size_t maxIndex = std::max(index1, index2);
31 size_t nodesMaxIndex = m_nodes.size() - 1;
32 for(size_t i = nodesMaxIndex; i < maxIndex; i++) {
33 GraphLayoutNode node;
34 m_nodes.push_back(node);
35 }
36 // Add an edge
37 m_edges.push_back({.node1 = m_nodes[index1], .node2 = m_nodes[index2], .weight = weight});
38}
39
40void GraphAutoLayout::Compute(size_t iterations)
41{
42 wxProgressDialog pbd(_("Importing..."), _("Initializing..."), iterations, nullptr,
43 wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_SMOOTH);
44
45 float nodesCount = m_nodes.size();
46
47 // Initialize nodes positions on a circle
48 float a = 0.f;
49 float da = 2.f * M_PI / nodesCount;
50 for(auto node = m_nodes.begin(); node != m_nodes.end(); node++) {
51 node->position.x = nodesCount * std::cos(a);
52 node->position.y = nodesCount * std::sin(a);
53 a += da;
54 }
55
56 // Initial parameters; other values can be chosen for area;
57 float area = nodesCount;
58 float k2 = area / nodesCount;
59 float k = sqrt(k2);
60
61 for(size_t i = 0; i < iterations; i++) {
62 // Temperature cools down; starts at 1, ends at 0
63 // (other formulas can be used for the cooling)
64 float temperature = 1.f - i / (float)iterations;
65 temperature *= temperature;
66
67 // Calculate repulsive forces
68 for(auto node1 = m_nodes.begin(); node1 != m_nodes.end(); node1++) {
69 node1->displacement = {0.f, 0.f};
70 for(auto node2 = m_nodes.begin(); node2 != m_nodes.end(); node2++) {
71 float dx = node1->position.x - node2->position.x;
72 float dy = node1->position.y - node2->position.y;
73 if(dx && dy) {
74 float d2 = dx * dx + dy * dy;
75 float coefficient = k2 / d2;
76 node1->displacement.x += coefficient * dx;
77 node1->displacement.y += coefficient * dy;
78 }
79 }
80 }
81
82 // Calculate attractive forces
83 for(auto edge = m_edges.begin(); edge != m_edges.end(); edge++) {
84 float dx = edge->node1.position.x - edge->node2.position.x;
85 float dy = edge->node1.position.y - edge->node2.position.y;
86 float d2 = dx * dx + dy * dy;
87 float coefficient = sqrt(d2) / k * edge->weight;
88 edge->node1.displacement.x -= dx * coefficient;
89 edge->node1.displacement.y -= dy * coefficient;
90 edge->node2.displacement.x += dx * coefficient;
91 edge->node2.displacement.y += dy * coefficient;
92 }
93
94 // Calculate positions
95 float sum = 0.f;
96 for(auto node = m_nodes.begin(); node != m_nodes.end(); node++) {
97 float d2 = node->displacement.x * node->displacement.x + node->displacement.y * node->displacement.y;
98 float d = sqrt(d2);
99 if(d > temperature) {
100 float coefficient = temperature / d;
101 node->displacement.x *= coefficient;
102 node->displacement.y *= coefficient;
103 sum += temperature;
104 } else {
105 sum += d;
106 }
107 node->position.x += node->displacement.x;
108 node->position.y += node->displacement.y;
109 }
110
111 if(!pbd.Update(i, wxString::Format("Iteration = %d", i))) {
112 pbd.Update(iterations);
113 i = iterations;
114 }
115 }
116}
117
118void GraphAutoLayout::CalculatePositions(int iterations, double scale)
119{
120 Compute(iterations);
121 int index = 0;
122 for(auto it = m_busData.begin(); it != m_busData.end(); ++it) {
123 (*it)->busPosition = wxPoint2DDouble(m_nodes[index].position.x * scale, m_nodes[index].position.y * scale);
124 index++;
125 }
126}
std::pair< int, int > busConnections
Definition ImportForm.h:243