Power System Platform  2026w34a-beta
Loading...
Searching...
No Matches
Transformer.cpp
1/*
2 * Copyright (C) 2017 Thales Lima Oliveira <thales@ufu.br>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "Transformer.h"
19#include "../../forms/TransformerForm.h"
20
21Transformer::Transformer() : Branch()
22{
23 m_elementType = TYPE_TRANSFORMER;
24 for (int i = 0; i < 2; i++) {
25 for (int j = 0; j < 3; j++) { m_electricalData.faultCurrent[i][j] = std::complex<double>(0.0, 0.0); }
26 }
27}
28Transformer::Transformer(wxString name) : Branch()
29{
30 m_elementType = TYPE_TRANSFORMER;
31 for (int i = 0; i < 2; i++) {
32 for (int j = 0; j < 3; j++) { m_electricalData.faultCurrent[i][j] = std::complex<double>(0.0, 0.0); }
33 }
34 m_electricalData.name = name;
35}
36Transformer::~Transformer() {}
37
38bool Transformer::AddParent(Element* parent, wxPoint2DDouble position, bool isOpening)
39{
40 if (parent) {
41 // First bus.
42 if (m_parentList.size() == 0) {
43 m_position = position;
44 m_parentList.push_back(parent);
45 parent->AddChild(this);
46
47 wxPoint2DDouble parentPt = parent->RotateAtPosition(position, -parent->GetAngle()); // Rotate click to horizontal position
48 parentPt.m_y = parent->GetPosition().m_y; // Centralize on bus.
49 parentPt = parent->RotateAtPosition(parentPt, parent->GetAngle()); // Rotate back.
50
51 m_pointList.push_back(parentPt); // First point
52 m_pointList.push_back(GetSwitchPoint(parent, parentPt, m_position));
53
54 wxRect2DDouble genRect(0, 0, 0, 0);
55 m_switchRect.push_back(genRect);
56
57 return false;
58 }
59
60 // Second bus.
61 else if (parent != m_parentList[0]) {
62 m_parentList.push_back(parent);
63 parent->AddChild(this);
64
65 wxPoint2DDouble parentPt = parent->RotateAtPosition(position, -parent->GetAngle()); // Rotate click to horizontal position.
66 parentPt.m_y = parent->GetPosition().m_y; // Centralize on bus.
67
68 parentPt = parent->RotateAtPosition(parentPt, parent->GetAngle()); // Rotate back.
69
70 if (isOpening) {
71 m_width = 70.0;
72 m_height = 40.0;
73
74 m_position = wxPoint2DDouble(
75 (m_pointList[0].m_x + parentPt.m_x) / 2.0,
76 (m_pointList[0].m_y + parentPt.m_y) / 2.0);
77
78 SetPosition(m_position);
79
80 wxPoint2DDouble term1 =
81 m_position +
82 RotateLocal(wxPoint2DDouble(-40.0, 0.0), m_angle);
83
84 wxPoint2DDouble term2 =
85 m_position +
86 RotateLocal(wxPoint2DDouble(40.0, 0.0), m_angle);
87
88 m_pointList.push_back(term1);
89 m_pointList.push_back(term2);
90
91 m_pointList[1] =
93 m_parentList[0],
94 m_pointList[0],
95 term1);
96
97 m_pointList.push_back(
99 parent,
100 parentPt,
101 term2));
102
103 m_pointList.push_back(parentPt);
104 m_inserted = true;
105
106 wxRect2DDouble genRect(0, 0, 0, 0);
107 m_switchRect.push_back(genRect);
108
110 UpdatePowerFlowArrowsPosition();
111
112 return true;
113 }
114
115 // Normal insertion: determine position and rotation automatically.
116 wxPoint2DDouble p1 = m_pointList[0];
117 wxPoint2DDouble p2 = parentPt;
118
119 double dx = p2.m_x - p1.m_x;
120 double dy = p2.m_y - p1.m_y;
121
122 // Determine orientation based on bus separation.
123 if (std::abs(dy) > std::abs(dx)) {
124 m_angle = (dy >= 0.0) ? 90.0 : 270.0;
125 }
126 else {
127 m_angle = (dx >= 0.0) ? 0.0 : 180.0;
128 }
129
130 // Get the midpoint between the two bus points.
131 m_position = wxPoint2DDouble((p1.m_x + p2.m_x) / 2.0, (p1.m_y + p2.m_y) / 2.0);
132
133 // Snap position to grid alignment.
134 if (std::abs(dx) < 1.0) {
135 m_position.m_x = p1.m_x;
136 m_position.m_y = std::round(m_position.m_y / 20.0) * 20.0;
137 }
138 else if (std::abs(dy) < 1.0) {
139 m_position.m_y = p1.m_y;
140 m_position.m_x = std::round(m_position.m_x / 20.0) * 20.0;
141 }
142 else {
143 m_position.m_x = std::round(m_position.m_x / 20.0) * 20.0;
144 m_position.m_y = std::round(m_position.m_y / 20.0) * 20.0;
145 }
146
147 // Check whether a point belongs to the rotated bus rectangle.
148 //
149 // The point is first transformed into the local coordinate
150 // system of the bus. In that system:
151 // X -> bus width
152 // Y -> bus height
153 //
154 // This makes the test independent of the bus rotation.
155 auto IsPointInsideBus = [](Element* bus, const wxPoint2DDouble& point) -> bool {
156 wxPoint2DDouble delta(
157 point.m_x - bus->GetPosition().m_x,
158 point.m_y - bus->GetPosition().m_y);
159
160 wxPoint2DDouble local =
161 bus->RotateLocal(delta, -bus->GetAngle());
162
163 double halfWidth = bus->GetWidth() / 2.0 + 2.0;
164 double halfHeight = bus->GetHeight() / 2.0 + 2.0;
165
166 return std::abs(local.m_x) <= halfWidth &&
167 std::abs(local.m_y) <= halfHeight;
168 };
169
170 if (std::abs(dy) > std::abs(dx)) {
171 // Transformer is vertically oriented.
172 // Keep the connection points aligned in X.
173 wxPoint2DDouble candidate1(m_position.m_x, m_parentList[0]->GetPosition().m_y);
174
175 wxPoint2DDouble candidate2(m_position.m_x, parent->GetPosition().m_y);
176
177 if (IsPointInsideBus(m_parentList[0], candidate1) && IsPointInsideBus(parent, candidate2)) {
178 m_pointList[0].m_x = m_position.m_x;
179 parentPt.m_x = m_position.m_x;
180 }
181 }
182 else {
183 // Transformer is horizontally oriented.
184 // Keep the connection points aligned in Y.
185 wxPoint2DDouble candidate1(m_parentList[0]->GetPosition().m_x, m_position.m_y);
186
187 wxPoint2DDouble candidate2(parent->GetPosition().m_x, m_position.m_y);
188
189 if (IsPointInsideBus(m_parentList[0], candidate1) &&
190 IsPointInsideBus(parent, candidate2)) {
191
192 m_pointList[0].m_y = m_position.m_y;
193 parentPt.m_y = m_position.m_y;
194 }
195 }
196
197 // Set the transformer rectangle.
198 m_width = 70.0;
199 m_height = 40.0;
200
201 SetPosition(m_position); // This method calculates the rectangle properly.
202
203 // Set the terminals at 40.0 units offset (2 grid cells),
204 // rotated by m_angle.
205 wxPoint2DDouble term1 = m_position + RotateLocal(wxPoint2DDouble(-40.0, 0.0), m_angle);
206
207 wxPoint2DDouble term2 = m_position + RotateLocal(wxPoint2DDouble(40.0, 0.0), m_angle);
208
209 term1.m_x = std::round(term1.m_x);
210 term1.m_y = std::round(term1.m_y);
211
212 term2.m_x = std::round(term2.m_x);
213 term2.m_y = std::round(term2.m_y);
214
215 m_pointList.push_back(term1);
216 m_pointList.push_back(term2);
217
218 // Set first switch point.
219 m_pointList[1] = GetSwitchPoint(m_parentList[0], m_pointList[0], term1);
220
221 // Set the second switch point.
222 m_pointList.push_back(GetSwitchPoint(parent, parentPt, term2));
223
224 m_pointList.push_back(parentPt); // Last point.
225 m_inserted = true;
226
227 wxRect2DDouble genRect(0, 0, 0, 0);
228 m_switchRect.push_back(genRect);
229
231 UpdatePowerFlowArrowsPosition();
232
233 return true;
234 }
235 }
236
237 return false;
238}
239
240bool Transformer::Contains(wxPoint2DDouble position) const
241{
242 wxPoint2DDouble ptR = RotateAtPosition(position, -m_angle);
243 return m_rect.Contains(ptR);
244}
245
246//void Transformer::Draw(wxPoint2DDouble translation, double scale) const
247//{
248// OpenGLColour elementColour;
249// if (m_online) {
250// if (m_dynEvent)
251// elementColour = m_dynamicEventColour;
252// else
253// elementColour = m_onlineElementColour;
254// }
255// else
256// elementColour = m_offlineElementColour;
257//
258// if (m_inserted) {
259// // Draw selection (layer 1).
260// if (m_selected) {
261// // Push the current matrix on stack.
262// glLineWidth(1.5 + m_borderSize * 2.0);
263// glColor4dv(m_selectionColour.GetRGBA());
264// DrawLine(m_pointList);
265// glPushMatrix();
266// // Rotate the matrix around the object position.
267// glTranslated(m_position.m_x, m_position.m_y, 0.0);
268// glRotated(m_angle, 0.0, 0.0, 1.0);
269// glTranslated(-m_position.m_x, -m_position.m_y, 0.0);
270//
271// DrawCircle(m_rect.GetPosition() + wxPoint2DDouble(20.0, 20.0), 20 + (m_borderSize + 1.5) / scale, 20,
272// GL_POLYGON);
273// DrawCircle(m_rect.GetPosition() + wxPoint2DDouble(50.0, 20.0), 20 + (m_borderSize + 1.5) / scale, 20,
274// GL_POLYGON);
275//
276// glPopMatrix();
277//
278// // Draw nodes selection.
279// if (m_pointList.size() > 0) {
280// DrawCircle(m_pointList[0], 5.0 + m_borderSize / scale, 10, GL_POLYGON);
281// if (m_inserted) {
282// DrawCircle(m_pointList[m_pointList.size() - 1], 5.0 + m_borderSize / scale, 10, GL_POLYGON);
283// }
284// }
285// }
286//
287// // Draw transformer (layer 2).
288// // Transformer line
289// glLineWidth(1.5);
290// glColor4dv(elementColour.GetRGBA());
291// DrawLine(m_pointList);
292//
293// // Draw nodes.
294// if (m_pointList.size() > 0) {
295// glColor4dv(elementColour.GetRGBA());
296// DrawCircle(m_pointList[0], 5.0, 10, GL_POLYGON);
297// if (m_inserted) { DrawCircle(m_pointList[m_pointList.size() - 1], 5.0, 10, GL_POLYGON); }
298// }
299//
300// DrawSwitches();
301// DrawPowerFlowPts();
302//
303// // Push the current matrix on stack.
304// glPushMatrix();
305// // Rotate the matrix around the object position.
306// glTranslated(m_position.m_x, m_position.m_y, 0.0);
307// glRotated(m_angle, 0.0, 0.0, 1.0);
308// glTranslated(-m_position.m_x, -m_position.m_y, 0.0);
309//
310// glColor4d(1.0, 1.0, 1.0, 1.0);
311// DrawCircle(m_rect.GetPosition() + wxPoint2DDouble(20.0, 20.0), 20, 20, GL_POLYGON);
312// DrawCircle(m_rect.GetPosition() + wxPoint2DDouble(50.0, 20.0), 20, 20, GL_POLYGON);
313//
314// glColor4dv(elementColour.GetRGBA());
315// DrawCircle(m_rect.GetPosition() + wxPoint2DDouble(20.0, 20.0), 20, 20);
316// DrawCircle(m_rect.GetPosition() + wxPoint2DDouble(50.0, 20.0), 20, 20);
317//
318// DrawPoint(m_rect.GetPosition(), 8.0 * scale);
319//
320// glPopMatrix();
321// }
322//}
323
324void Transformer::DrawDC(GUIColour* guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext* gc) const
325{
326 wxColour elementColour;
327 if (m_online) {
328 //if (m_dynEvent)
329 // elementColour = guiColour->eventElement;
330 //else
331 elementColour = guiColour->enabled;
332 }
333 else
334 elementColour = guiColour->disable;
335
336 if (m_inserted) {
337 // Draw selection (layer 1).
338 if (m_selected) {
339 gc->SetPen(wxPen(guiColour->selection, 2 + m_borderSize * 2.0));
340 gc->SetBrush(*wxTRANSPARENT_BRUSH);
341 gc->StrokeLines(m_pointList.size(), &m_pointList[0]);
342
343 // Push the current matrix on stack.
344 gc->PushState();
345 // Rotate the matrix around the object position.
346 gc->Translate(m_position.m_x, m_position.m_y);
347 gc->Rotate(wxDegToRad(m_angle));
348 gc->Translate(-m_position.m_x, -m_position.m_y);
349
350 gc->SetPen(*wxTRANSPARENT_PEN);
351 gc->SetBrush(wxBrush(guiColour->selection));
352 DrawDCCircle(m_position + wxPoint2DDouble(-15.0, 0.0), 20 + (m_borderSize + 1.5) / scale, 20, gc);
353 DrawDCCircle(m_position + wxPoint2DDouble(15.0, 0.0), 20 + (m_borderSize + 1.5) / scale, 20, gc);
354
355 gc->PopState();
356
357 // Draw nodes selection.
358 gc->SetPen(*wxTRANSPARENT_PEN);
359 gc->SetBrush(wxBrush(guiColour->selection));
360 if (m_pointList.size() > 0) {
361 DrawDCCircle(m_pointList[0], 5.0 + m_borderSize / scale, 10, gc);
362 if (m_inserted) { DrawDCCircle(m_pointList[m_pointList.size() - 1], 5.0 + m_borderSize / scale, 10, gc); }
363 }
364 }
365
366 // Draw transformer (layer 2).
367 // Transformer line
368 gc->SetPen(wxPen(elementColour, 2));
369 gc->SetBrush(*wxTRANSPARENT_BRUSH);
370 gc->StrokeLines(m_pointList.size(), &m_pointList[0]);
371
372 // Draw nodes.
373 gc->SetPen(*wxTRANSPARENT_PEN);
374 gc->SetBrush(wxBrush(elementColour));
375 if (m_pointList.size() > 0) {
376 DrawDCCircle(m_pointList[0], 5.0, 10, gc);
377 if (m_inserted) { DrawDCCircle(m_pointList[m_pointList.size() - 1], 5.0, 10, gc); }
378 }
379
380 DrawDCSwitches(guiColour, gc);
381 DrawDCPowerFlowPts(guiColour, gc);
382
383 // Push the current matrix on stack.
384 gc->PushState();
385 // Rotate the matrix around the object position.
386 gc->Translate(m_position.m_x, m_position.m_y);
387 gc->Rotate(wxDegToRad(m_angle));
388 gc->Translate(-m_position.m_x, -m_position.m_y);
389
390 //glColor4d(1.0, 1.0, 1.0, 1.0);
391 gc->SetPen(*wxTRANSPARENT_PEN);
392 gc->SetBrush(wxBrush(guiColour->background));
393 DrawDCCircle(m_position + wxPoint2DDouble(-15.0, 0.0), 20, 20, gc);
394 DrawDCCircle(m_position + wxPoint2DDouble(15.0, 0.0), 20, 20, gc);
395
396 gc->SetPen(wxPen(elementColour, 2));
397 gc->SetBrush(*wxTRANSPARENT_BRUSH);
398 DrawDCCircle(m_position + wxPoint2DDouble(-15.0, 0.0), 20, 20, gc);
399 DrawDCCircle(m_position + wxPoint2DDouble(15.0, 0.0), 20, 20, gc);
400
401 // Point
402 gc->SetPen(*wxTRANSPARENT_PEN);
403 gc->SetBrush(wxBrush(elementColour));
404 DrawDCCircle(m_rect.GetPosition(), 4, 10, gc);
405
406 gc->PopState();
407
408 if (m_dynEvent) {
409 DrawStabilityEventGC(gc, translation, scale, guiColour);
410 }
411 }
412}
413
414void Transformer::DrawDC(GUIColour* guiColour, wxPoint2DDouble translation, double scale, wxDC& dc) const
415{
416 wxColour elementColour;
417 if (m_online) {
418 if (m_dynEvent)
419 elementColour = guiColour->eventElement;
420 else
421 elementColour = guiColour->enabled;
422 }
423 else
424 elementColour = guiColour->disable;
425
426 std::vector<wxPoint> pointList;
427 for (auto& pt : m_pointList) { pointList.emplace_back(static_cast<int>(pt.m_x), static_cast<int>(pt.m_y)); }
428
429 if (m_inserted) {
430 // Draw selection (layer 1).
431 if (m_selected) {
432 dc.SetPen(wxPen(wxColour(guiColour->selection), 2 + m_borderSize * 2.0));
433 dc.SetBrush(*wxTRANSPARENT_BRUSH);
434 dc.DrawLines(pointList.size(), &pointList[0]);
435
436 dc.SetPen(*wxTRANSPARENT_PEN);
437 dc.SetBrush(wxBrush(guiColour->selection));
438 //DrawDCCircle(m_rect.GetPosition() + wxPoint2DDouble(20.0, 20.0), 20 + (m_borderSize + 1.5) / scale, dc);
439 //DrawDCCircle(m_rect.GetPosition() + wxPoint2DDouble(50.0, 20.0), 20 + (m_borderSize + 1.5) / scale, dc);
440 DrawDCCircle(m_position + RotateLocal(wxPoint2DDouble(15.0, 0.0), m_angle), 20 + (m_borderSize + 1.5) / scale, dc);
441 DrawDCCircle(m_position + RotateLocal(wxPoint2DDouble(-15.0, 0.0), m_angle), 20 + (m_borderSize + 1.5) / scale, dc);
442
443 // Draw nodes selection.
444 dc.SetPen(*wxTRANSPARENT_PEN);
445 dc.SetBrush(wxBrush(guiColour->selection));
446 if (pointList.size() > 0) {
447 DrawDCCircle(pointList[0], 5.0 + m_borderSize / scale, dc);
448 if (m_inserted) { DrawDCCircle(pointList[pointList.size() - 1], 5.0 + m_borderSize / scale, dc); }
449 }
450 }
451
452 // Draw transformer (layer 2).
453 // Transformer line
454 dc.SetPen(wxPen(elementColour, 2));
455 dc.SetBrush(*wxTRANSPARENT_BRUSH);
456 dc.DrawLines(pointList.size(), &pointList[0]);
457
458 // Draw nodes.
459 dc.SetPen(*wxTRANSPARENT_PEN);
460 dc.SetBrush(wxBrush(elementColour));
461 if (pointList.size() > 0) {
462 DrawDCCircle(pointList[0], 5.0, dc);
463 if (m_inserted) { DrawDCCircle(pointList[pointList.size() - 1], 5.0, dc); }
464 }
465
466 DrawDCSwitches(guiColour, dc);
467 DrawDCPowerFlowPts(guiColour, dc);
468
469 dc.SetPen(*wxTRANSPARENT_PEN);
470 dc.SetBrush(wxBrush(guiColour->background));
471 DrawDCCircle(m_position + RotateLocal(wxPoint2DDouble(15.0, 0.0), m_angle), 20, dc);
472 DrawDCCircle(m_position + RotateLocal(wxPoint2DDouble(-15.0, 0.0), m_angle), 20, dc);
473
474 dc.SetPen(wxPen(elementColour, 2));
475 dc.SetBrush(*wxTRANSPARENT_BRUSH);
476 DrawDCCircle(m_position + RotateLocal(wxPoint2DDouble(15.0, 0.0), m_angle), 20, dc);
477 DrawDCCircle(m_position + RotateLocal(wxPoint2DDouble(-15.0, 0.0), m_angle), 20, dc);
478
479 // Point
480 dc.SetPen(*wxTRANSPARENT_PEN);
481 dc.SetBrush(wxBrush(elementColour));
482 DrawDCCircle(m_position + RotateLocal(wxPoint2DDouble(-35, -20), m_angle), 4, dc);
483 }
484}
485
486bool Transformer::Intersects(wxRect2DDouble rect) const
487{
488 if (m_angle == 0.0 || m_angle == 180.0) return m_rect.Intersects(rect);
489 return RotatedRectanglesIntersects(m_rect, rect, m_angle, 0.0);
490}
491
492void Transformer::Rotate(bool clockwise)
493{
494 double rotAngle = m_rotationAngle;
495 if (!clockwise) rotAngle = -m_rotationAngle;
496
497 m_angle += rotAngle;
498 while (m_angle >= 360.0) m_angle -= 360.0;
499 while (m_angle < 0.0) m_angle += 360.0;
500
501 // Clean up floating point precision for cardinal angles
502 if (std::abs(m_angle - 90.0) < 1e-4) m_angle = 90.0;
503 else if (std::abs(m_angle - 180.0) < 1e-4) m_angle = 180.0;
504 else if (std::abs(m_angle - 270.0) < 1e-4) m_angle = 270.0;
505 else if (std::abs(m_angle - 0.0) < 1e-4 || std::abs(m_angle - 360.0) < 1e-4) m_angle = 0.0;
506
507 // Update terminals rigidly attached to transformer body at 40.0 units offset
508 if (m_pointList.size() >= 4) {
509 wxPoint2DDouble t1 = m_position + RotateLocal(wxPoint2DDouble(-40.0, 0.0), m_angle);
510 wxPoint2DDouble t2 = m_position + RotateLocal(wxPoint2DDouble(40.0, 0.0), m_angle);
511 m_pointList[2] = wxPoint2DDouble(std::round(t1.m_x), std::round(t1.m_y));
512 m_pointList[3] = wxPoint2DDouble(std::round(t2.m_x), std::round(t2.m_y));
513 }
514
515 UpdateSwitchesPosition();
516 UpdatePowerFlowArrowsPosition();
517}
518
519void Transformer::Move(wxPoint2DDouble position)
520{
521 SetPosition(m_movePos + position - m_moveStartPt);
522
523 // Update terminals rigidly attached to transformer body at 40.0 units offset
524 if (m_pointList.size() >= 4) {
525 wxPoint2DDouble t1 = m_position + RotateLocal(wxPoint2DDouble(-40.0, 0.0), m_angle);
526 wxPoint2DDouble t2 = m_position + RotateLocal(wxPoint2DDouble(40.0, 0.0), m_angle);
527 m_pointList[2] = wxPoint2DDouble(std::round(t1.m_x), std::round(t1.m_y));
528 m_pointList[3] = wxPoint2DDouble(std::round(t2.m_x), std::round(t2.m_y));
529 }
530
531 if (!m_parentList[0]) { m_pointList[0] = m_movePts[0] + position - m_moveStartPt; }
532 if (!m_parentList[1]) {
533 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
534 }
535
536 UpdateSwitchesPosition();
537 UpdatePowerFlowArrowsPosition();
538}
539
540void Transformer::AlignToGrid(double gridSize)
541{
542 if (gridSize <= 0.0)
543 gridSize = 20.0;
544
545 // 1. Snap center position to grid.
546 m_position.m_x = std::round(m_position.m_x / gridSize) * gridSize;
547 m_position.m_y = std::round(m_position.m_y / gridSize) * gridSize;
548
549 SetPosition(m_position);
550
551 // 2. Rigidly attach terminals at 40.0 units offset.
552 if (m_pointList.size() >= 4) {
553 wxPoint2DDouble t1 = m_position + RotateLocal(wxPoint2DDouble(-40.0, 0.0), m_angle);
554 wxPoint2DDouble t2 = m_position + RotateLocal(wxPoint2DDouble(40.0, 0.0), m_angle);
555 m_pointList[2] = wxPoint2DDouble(std::round(t1.m_x), std::round(t1.m_y));
556 m_pointList[3] = wxPoint2DDouble(std::round(t2.m_x), std::round(t2.m_y));
557 }
558
559 // Transformer orientation.
560 bool isVertical = (std::abs(m_angle - 90.0) < 1.0 || std::abs(m_angle - 270.0) < 1.0);
561
562 // Check whether a point belongs to the rotated bus rectangle.
563 auto IsPointInsideBus =
564 [](Element* bus, const wxPoint2DDouble& point) -> bool
565 {
566 wxPoint2DDouble delta(point.m_x - bus->GetPosition().m_x, point.m_y - bus->GetPosition().m_y);
567
568 // Transform the point into the local coordinate
569 // system of the bus.
570 wxPoint2DDouble local = bus->RotateLocal(delta, -bus->GetAngle());
571
572 double halfWidth = bus->GetWidth() / 2.0 + 2.0;
573 double halfHeight = bus->GetHeight() / 2.0 + 2.0;
574
575 return std::abs(local.m_x) <= halfWidth && std::abs(local.m_y) <= halfHeight;
576 };
577
578 // 3. Align first bus contact point.
579 if (m_parentList.size() > 0 && m_parentList[0] && !m_pointList.empty()) {
580
581 Element* bus1 = m_parentList[0];
582
583 if (isVertical) {
584 // Candidate point obtained by aligning X with the
585 // transformer center.
586 wxPoint2DDouble candidate(m_position.m_x, bus1->GetPosition().m_y);
587
588 if (IsPointInsideBus(bus1, candidate)) {
589 m_pointList[0].m_x = m_position.m_x;
590 }
591
592 // Snap the longitudinal coordinate to the grid.
593 m_pointList[0].m_y = std::round(m_pointList[0].m_y / gridSize) * gridSize;
594 }
595 else {
596 // Candidate point obtained by aligning Y with the
597 // transformer center.
598 wxPoint2DDouble candidate(bus1->GetPosition().m_x, m_position.m_y);
599
600 if (IsPointInsideBus(bus1, candidate)) {
601 m_pointList[0].m_y = m_position.m_y;
602 }
603
604 // Snap the longitudinal coordinate to the grid.
605 m_pointList[0].m_x = std::round(m_pointList[0].m_x / gridSize) * gridSize;
606 }
607 }
608 else if (!m_pointList.empty()) {
609 m_pointList[0].m_x = std::round(m_pointList[0].m_x / gridSize) * gridSize;
610
611 m_pointList[0].m_y = std::round(m_pointList[0].m_y / gridSize) * gridSize;
612 }
613
614 // 4. Align second bus contact point.
615 if (m_parentList.size() > 1 &&
616 m_parentList[1] &&
617 !m_pointList.empty()) {
618
619 Element* bus2 = m_parentList[1];
620
621 if (isVertical) {
622 // Candidate point obtained by aligning X with the
623 // transformer center.
624 wxPoint2DDouble candidate(m_position.m_x, bus2->GetPosition().m_y);
625
626 if (IsPointInsideBus(bus2, candidate)) {
627 m_pointList.back().m_x = m_position.m_x;
628 }
629
630 // Snap the longitudinal coordinate to the grid.
631 m_pointList.back().m_y = std::round(m_pointList.back().m_y / gridSize) * gridSize;
632 }
633 else {
634 // Candidate point obtained by aligning Y with the
635 // transformer center.
636 wxPoint2DDouble candidate(bus2->GetPosition().m_x, m_position.m_y);
637
638 if (IsPointInsideBus(bus2, candidate)) {
639 m_pointList.back().m_y = m_position.m_y;
640 }
641
642 // Snap the longitudinal coordinate to the grid.
643 m_pointList.back().m_x = std::round(m_pointList.back().m_x / gridSize) * gridSize;
644 }
645 }
646 else if (m_pointList.size() > 1) {
647 m_pointList.back().m_x = std::round(m_pointList.back().m_x / gridSize) * gridSize;
648
649 m_pointList.back().m_y = std::round(m_pointList.back().m_y / gridSize) * gridSize;
650 }
651
652 UpdateSwitchesPosition();
653 UpdatePowerFlowArrowsPosition();
654}
655
656void Transformer::MoveNode(Element* parent, wxPoint2DDouble position)
657{
658 if (parent) {
659 // First bus.
660 if (parent == m_parentList[0]) {
661 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
662 }
663 // Second bus.
664 else if (parent == m_parentList[1]) {
665 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
666 }
667 }
668 else {
669 if (m_activeNodeID == 1) {
670 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
671 if (m_parentList[0]) {
672 m_parentList[0]->RemoveChild(this);
673 m_parentList[0] = nullptr;
674 m_online = false;
675 }
676 }
677 else if (m_activeNodeID == 2) {
678 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
679 if (m_parentList[1]) {
680 m_parentList[1]->RemoveChild(this);
681 m_parentList[1] = nullptr;
682 m_online = false;
683 }
684 }
685 }
686
687 // Recalculate switches positions
688 UpdateSwitchesPosition();
689 UpdatePowerFlowArrowsPosition();
690}
691
692void Transformer::StartMove(wxPoint2DDouble position)
693{
694 m_moveStartPt = position;
695 m_movePts = m_pointList;
696 m_movePos = m_position;
697}
698
700{
701 menu.Append(ID_EDIT_ELEMENT, _("Edit tranformer"));
702
703 wxString busName[2] = { "?", "?" };
704 if (m_parentList.size() == 2) {
705 int i = 0;
706 for (Element* element : m_parentList) {
707 if (element) {
708 Bus* bus = static_cast<Bus*>(element);
709 busName[i] = bus->GetElectricalData().name;
710 }
711 i++;
712 }
713 }
714
715 wxMenu* textMenu = new wxMenu();
716
717 textMenu->Append(ID_TXT_NAME, _("Name"));
718 textMenu->Append(ID_TXT_BRANCH_ACTIVE_POWER_1_2, _("Active power (") + busName[0] + _(" to ") + busName[1] + wxT(")"));
719 textMenu->Append(ID_TXT_BRANCH_ACTIVE_POWER_2_1, _("Active power (") + busName[1] + _(" to ") + busName[0] + wxT(")"));
720 textMenu->Append(ID_TXT_BRANCH_REACTIVE_POWER_1_2, _("Reactive power (") + busName[0] + _(" to ") + busName[1] + wxT(")"));
721 textMenu->Append(ID_TXT_BRANCH_REACTIVE_POWER_2_1, _("Reactive power (") + busName[1] + _(" to ") + busName[0] + wxT(")"));
722 textMenu->Append(ID_TXT_BRANCH_LOSSES, _("Losses"));
723 textMenu->Append(ID_TXT_BRANCH_CURRENT_1_2, _("Current (") + busName[0] + _(" to ") + busName[1] + wxT(")"));
724 textMenu->Append(ID_TXT_BRANCH_CURRENT_2_1, _("Current (") + busName[1] + _(" to ") + busName[0] + wxT(")"));
725 textMenu->Append(ID_TXT_BRANCH_FAULT_CURRENT_1_2, _("Fault current (") + busName[0] + _(" to ") + busName[1] + wxT(")"));
726 textMenu->Append(ID_TXT_BRANCH_FAULT_CURRENT_2_1, _("Fault current (") + busName[1] + _(" to ") + busName[0] + wxT(")"));
727 textMenu->Append(ID_TXT_TAP, _("Tap"));
728 textMenu->SetClientData(menu.GetClientData());
729 menu.AppendSubMenu(textMenu, _("Add text"));
730
731 GeneralMenuItens(menu);
732 return true;
733}
734
735bool Transformer::ShowForm(wxWindow* parent, Element* element, wxWindow* workspace)
736{
737 TransformerForm transfForm(parent, this);
738 transfForm.CenterOnParent();
739 if (transfForm.ShowModal() == wxID_OK) {
740 return true;
741 }
742 return false;
743}
744
745void Transformer::SetNominalVoltage(std::vector<double> nominalVoltage, std::vector<ElectricalUnit> nominalVoltageUnit)
746{
747 if (nominalVoltage.size() == 1) {
748 m_electricalData.primaryNominalVoltage = nominalVoltage[0];
749 m_electricalData.primaryNominalVoltageUnit = nominalVoltageUnit[0];
750 }
751 else if (nominalVoltage.size() == 2) {
752 m_electricalData.primaryNominalVoltage = nominalVoltage[0];
753 m_electricalData.primaryNominalVoltageUnit = nominalVoltageUnit[0];
754 m_electricalData.secondaryNominalVoltage = nominalVoltage[1];
755 m_electricalData.secondaryNominalVoltageUnit = nominalVoltageUnit[1];
756 }
757}
758
759void Transformer::UpdatePowerFlowArrowsPosition()
760{
761 std::vector<wxPoint2DDouble> edges;
762 switch (m_pfDirection) {
764 m_powerFlowArrow.clear();
765 } break;
767 for (int i = 1; i < (int)m_pointList.size() - 1; i++) { edges.push_back(m_pointList[i]); }
768 } break;
770 for (int i = (int)m_pointList.size() - 2; i > 0; i--) { edges.push_back(m_pointList[i]); }
771 } break;
772 default:
773 break;
774 }
776}
777
778void Transformer::RotateNode(Element* parent, bool clockwise)
779{
780 double rotAngle = m_rotationAngle;
781 if (!clockwise) rotAngle = -m_rotationAngle;
782
783 if (parent == m_parentList[0]) {
784 m_pointList[0] = parent->RotateAtPosition(m_pointList[0], rotAngle);
785 }
786 else if (parent == m_parentList[1]) {
787 m_pointList[m_pointList.size() - 1] = parent->RotateAtPosition(m_pointList[m_pointList.size() - 1], rotAngle);
788 }
789 UpdateSwitchesPosition();
790 UpdatePowerFlowArrowsPosition();
791}
792
794{
795 if (m_activeNodeID == 1 && parent == m_parentList[0]) return false;
796 if (m_activeNodeID == 2 && parent == m_parentList[1]) return false;
797
798 if (parent && m_activeNodeID != 0) {
799 wxRect2DDouble nodeRect(0, 0, 0, 0);
800 if (m_activeNodeID == 1) {
801 nodeRect = wxRect2DDouble(m_pointList[0].m_x - 5.0 - m_borderSize, m_pointList[0].m_y - 5.0 - m_borderSize,
802 10 + 2.0 * m_borderSize, 10 + 2.0 * m_borderSize);
803 }
804 if (m_activeNodeID == 2) {
805 nodeRect = wxRect2DDouble(m_pointList[m_pointList.size() - 1].m_x - 5.0 - m_borderSize,
806 m_pointList[m_pointList.size() - 1].m_y - 5.0 - m_borderSize,
807 10 + 2.0 * m_borderSize, 10 + 2.0 * m_borderSize);
808 }
809
810 if (parent->Intersects(nodeRect)) {
811 if (m_activeNodeID == 1) {
812 // Check if the user is trying to connect the same bus.
813 if (m_parentList[1] == parent) {
814 m_activeNodeID = 0;
815 return false;
816 }
817
818 m_parentList[0] = parent;
819
820 // Centralize the node on bus.
821 wxPoint2DDouble parentPt = parent->RotateAtPosition(
822 m_pointList[0], -parent->GetAngle()); // Rotate click to horizontal position.
823 parentPt.m_y = parent->GetPosition().m_y; // Centralize on bus.
824 parentPt = parent->RotateAtPosition(parentPt, parent->GetAngle());
825 m_pointList[0] = parentPt;
826
827 UpdateSwitchesPosition();
828 UpdatePowerFlowArrowsPosition();
829 return true;
830 }
831 if (m_activeNodeID == 2) {
832 if (m_parentList[0] == parent) {
833 m_activeNodeID = 0;
834 return false;
835 }
836
837 m_parentList[1] = parent;
838
839 wxPoint2DDouble parentPt =
840 parent->RotateAtPosition(m_pointList[m_pointList.size() - 1], -parent->GetAngle());
841 parentPt.m_y = parent->GetPosition().m_y;
842 parentPt = parent->RotateAtPosition(parentPt, parent->GetAngle());
843 m_pointList[m_pointList.size() - 1] = parentPt;
844
845 UpdateSwitchesPosition();
846 UpdatePowerFlowArrowsPosition();
847 return true;
848 }
849 }
850 else {
851 if (m_activeNodeID == 1) m_parentList[0] = nullptr;
852 if (m_activeNodeID == 2) m_parentList[1] = nullptr;
853 }
854 }
855 return false;
856}
857
859{
860 m_pfDirection = pfDirection;
861 UpdatePowerFlowArrowsPosition();
862}
863
865{
866 Transformer* copy = new Transformer();
867 *copy = *this;
868 return copy;
869}
870
872{
873 wxString tipText = m_electricalData.name;
874 wxString primVoltage = StringFromDouble(m_electricalData.primaryNominalVoltage);
875 switch (m_electricalData.primaryNominalVoltageUnit) {
876 case ElectricalUnit::UNIT_V: {
877 primVoltage += _(" V");
878 } break;
879 case ElectricalUnit::UNIT_kV: {
880 primVoltage += _(" kV");
881 } break;
882 default:
883 break;
884 }
885 wxString secVoltage = StringFromDouble(m_electricalData.secondaryNominalVoltage);
886 switch (m_electricalData.secondaryNominalVoltageUnit) {
887 case ElectricalUnit::UNIT_V: {
888 secVoltage += _(" V");
889 } break;
890 case ElectricalUnit::UNIT_kV: {
891 secVoltage += _(" kV");
892 } break;
893 default:
894 break;
895 }
896
897 tipText += "\n" + primVoltage + " / " + secVoltage;
898
899 if (m_online) {
900 tipText += "\n";
901 int busNumber[2];
902 busNumber[0] = static_cast<Bus*>(m_parentList[0])->GetElectricalData().number + 1;
903 busNumber[1] = static_cast<Bus*>(m_parentList[1])->GetElectricalData().number + 1;
904
905 tipText += _("\nP") + wxString::Format("(%d-%d) = ", busNumber[0], busNumber[1]) +
906 wxString::FromDouble(m_electricalData.powerFlow[0].real(), 5) + _(" p.u.");
907 tipText += _("\nQ") + wxString::Format("(%d-%d) = ", busNumber[0], busNumber[1]) +
908 wxString::FromDouble(m_electricalData.powerFlow[0].imag(), 5) + _(" p.u.");
909 tipText += _("\nP") + wxString::Format("(%d-%d) = ", busNumber[1], busNumber[0]) +
910 wxString::FromDouble(m_electricalData.powerFlow[1].real(), 5) + _(" p.u.");
911 tipText += _("\nQ") + wxString::Format("(%d-%d) = ", busNumber[1], busNumber[0]) +
912 wxString::FromDouble(m_electricalData.powerFlow[1].imag(), 5) + _(" p.u.");
913
914 if (m_electricalData.hasTapChanger) {
915 tipText += _("\nOLTC: Enabled (Vset = ") +
916 wxString::FromDouble(m_electricalData.oltcTargetVoltage, 3) +
917 _(" p.u., Tap = ") +
918 wxString::FromDouble(m_electricalData.turnsRatio, 4) +
919 _(" p.u.)");
920 }
921
922 if (!m_electricalData.harmonicOrder.empty()) {
923 tipText += _("\n\nHarmonic currents:");
924 int i = 0;
925 for (auto& hCurrent1 : m_electricalData.harmonicCurrent[0]) {
926 auto& hCurrent2 = m_electricalData.harmonicCurrent[1][i];
927 wxString i1, i2;
928 i1.Printf(_("\nIh(%d)(%d-%d) = %.5e%s%.2f%s p.u."), m_electricalData.harmonicOrder[i], busNumber[0], busNumber[1], std::abs(hCurrent1), wxString(L'\u2220'), wxRadToDeg(std::arg(hCurrent1)), wxString(L'\u00B0'));
929 i2.Printf(_("\nIh(%d)(%d-%d) = %.5e%s%.2f%s p.u."), m_electricalData.harmonicOrder[i], busNumber[1], busNumber[0], std::abs(hCurrent2), wxString(L'\u2220'), wxRadToDeg(std::arg(hCurrent2)), wxString(L'\u00B0'));
930
931 tipText += i1 + i2;
932 i++;
933 }
934 }
935 }
936
937 return tipText;
938}
939
940TransformerElectricalData Transformer::GetPUElectricalData(double systemBasePower) const
941{
943
944 data.name = m_electricalData.name;
945
946 data.nominalPower = m_electricalData.nominalPower;
947 data.nominalPowerUnit = m_electricalData.nominalPowerUnit;
948
949 data.primaryNominalVoltage = m_electricalData.primaryNominalVoltage;
950 data.primaryNominalVoltageUnit = m_electricalData.primaryNominalVoltageUnit;
951
952 data.secondaryNominalVoltage = m_electricalData.secondaryNominalVoltage;
953 data.secondaryNominalVoltageUnit = m_electricalData.secondaryNominalVoltageUnit;
954
955 data.useTransformerPower = m_electricalData.useTransformerPower;
956
957 data.baseVoltage = m_electricalData.baseVoltage;
958
959 data.resistance = m_electricalData.resistance;
960 data.resistanceUnit = m_electricalData.resistanceUnit;
961
962 data.indReactance = m_electricalData.indReactance;
963 data.indReactanceUnit = m_electricalData.indReactanceUnit;
964
965 data.connection = m_electricalData.connection;
966 data.turnsRatio = m_electricalData.turnsRatio;
967 data.phaseShift = m_electricalData.phaseShift;
968
969 data.hasTapChanger = m_electricalData.hasTapChanger;
970 data.nominalTurnsRatio = m_electricalData.nominalTurnsRatio;
971 data.oltcControlledBus = m_electricalData.oltcControlledBus;
972 data.oltcTargetVoltage = m_electricalData.oltcTargetVoltage;
973 data.oltcVoltageDeadband = m_electricalData.oltcVoltageDeadband;
974 data.oltcMinTap = m_electricalData.oltcMinTap;
975 data.oltcMaxTap = m_electricalData.oltcMaxTap;
976 data.oltcTapStep = m_electricalData.oltcTapStep;
977 data.oltcIsDiscrete = m_electricalData.oltcIsDiscrete;
978
979 data.zeroResistance = m_electricalData.zeroResistance;
980 data.zeroIndReactance = m_electricalData.zeroIndReactance;
981
982 data.primaryGrndResistance = m_electricalData.primaryGrndResistance;
983 data.primaryGrndReactance = m_electricalData.primaryGrndReactance;
984
985 data.secondaryGrndResistance = m_electricalData.secondaryGrndResistance;
986 data.secondaryGrndReactance = m_electricalData.secondaryGrndReactance;
987
988 data.powerFlow[0] = m_electricalData.powerFlow[0];
989 data.powerFlow[1] = m_electricalData.powerFlow[1];
990
991 data.faultCurrent[0][0] = m_electricalData.faultCurrent[0][0];
992 data.faultCurrent[0][1] = m_electricalData.faultCurrent[0][1];
993 data.faultCurrent[0][2] = m_electricalData.faultCurrent[0][2];
994 data.faultCurrent[1][0] = m_electricalData.faultCurrent[1][0];
995 data.faultCurrent[1][1] = m_electricalData.faultCurrent[1][1];
996 data.faultCurrent[1][2] = m_electricalData.faultCurrent[1][2];
997
998
999 data.harmonicOrder = m_electricalData.harmonicOrder;
1000 data.harmonicCurrent[0] = m_electricalData.harmonicCurrent[0];
1001 data.harmonicCurrent[1] = m_electricalData.harmonicCurrent[1];
1002
1003 double transformerBasePower = GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
1004 double baseVoltage = 0.0;
1005 if (data.baseVoltage == 0) {
1006 baseVoltage = GetValueFromUnit(data.primaryNominalVoltage, data.primaryNominalVoltageUnit);
1007 }
1008 else {
1009 baseVoltage = GetValueFromUnit(data.secondaryNominalVoltage, data.secondaryNominalVoltageUnit);
1010 }
1011 double systemBaseImpedance = (baseVoltage * baseVoltage) / systemBasePower;
1012 double transformerBaseImpedance = (baseVoltage * baseVoltage) / transformerBasePower;
1013
1014 // Resistance
1015 double r = data.resistance;
1016 if (data.resistanceUnit == ElectricalUnit::UNIT_PU) {
1017 if (data.useTransformerPower) data.resistance = (r * transformerBaseImpedance) / systemBaseImpedance;
1018 }
1019 else {
1020 data.resistance = r / systemBaseImpedance;
1021 }
1022 data.resistanceUnit = ElectricalUnit::UNIT_PU;
1023
1024 // Indutive reactance
1025 double x = data.indReactance;
1026 if (data.indReactanceUnit == ElectricalUnit::UNIT_PU) {
1027 if (data.useTransformerPower) data.indReactance = (x * transformerBaseImpedance) / systemBaseImpedance;
1028 }
1029 else {
1030 data.indReactance = x / systemBaseImpedance;
1031 }
1032 data.indReactanceUnit = ElectricalUnit::UNIT_PU;
1033
1034 // Fault
1035
1036 // Zero seq. resistance
1037 double r0 = data.zeroResistance;
1038 if (data.useTransformerPower) data.zeroResistance = (r0 * transformerBaseImpedance) / systemBaseImpedance;
1039
1040 // Zero seq. ind. reactance
1041 double x0 = data.zeroIndReactance;
1042 if (data.useTransformerPower) data.zeroIndReactance = (x0 * transformerBaseImpedance) / systemBaseImpedance;
1043
1044 // Primary ground resistance
1045 double rgp = data.primaryGrndResistance;
1046 if (data.useTransformerPower) data.primaryGrndResistance = (rgp * transformerBaseImpedance) / systemBaseImpedance;
1047
1048 // Primary ground ind reactance
1049 double xgp = data.primaryGrndReactance;
1050 if (data.useTransformerPower) data.primaryGrndReactance = (xgp * transformerBaseImpedance) / systemBaseImpedance;
1051
1052 // Secondary ground resistance
1053 double rgs = data.secondaryGrndResistance;
1054 if (data.useTransformerPower) data.secondaryGrndResistance = (rgs * transformerBaseImpedance) / systemBaseImpedance;
1055
1056 // Secondary ground ind reactance
1057 double xgs = data.secondaryGrndReactance;
1058 if (data.useTransformerPower) data.secondaryGrndReactance = (xgs * transformerBaseImpedance) / systemBaseImpedance;
1059
1060 if (!m_online) {
1061 data.powerFlow[0] = std::complex<double>(0, 0);
1062 data.powerFlow[1] = std::complex<double>(0, 0);
1063 data.faultCurrent[0][0] = std::complex<double>(0, 0);
1064 data.faultCurrent[0][1] = std::complex<double>(0, 0);
1065 data.faultCurrent[0][2] = std::complex<double>(0, 0);
1066 data.faultCurrent[1][0] = std::complex<double>(0, 0);
1067 data.faultCurrent[1][1] = std::complex<double>(0, 0);
1068 data.faultCurrent[1][2] = std::complex<double>(0, 0);
1069 }
1070
1071 return data;
1072}
1073
1074rapidxml::xml_node<>* Transformer::SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode)
1075{
1076 auto elementNode = XMLParser::AppendNode(doc, elementListNode, "Transfomer");
1077 XMLParser::SetNodeAttribute(doc, elementNode, "ID", m_elementID);
1078 auto cadProp = XMLParser::AppendNode(doc, elementNode, "CADProperties");
1079 auto position = XMLParser::AppendNode(doc, cadProp, "Position");
1080 auto posX = XMLParser::AppendNode(doc, position, "X");
1081 XMLParser::SetNodeValue(doc, posX, m_position.m_x);
1082 auto posY = XMLParser::AppendNode(doc, position, "Y");
1083 XMLParser::SetNodeValue(doc, posY, m_position.m_y);
1084 auto size = XMLParser::AppendNode(doc, cadProp, "Size");
1085 auto width = XMLParser::AppendNode(doc, size, "Width");
1086 XMLParser::SetNodeValue(doc, width, m_width);
1087 auto height = XMLParser::AppendNode(doc, size, "Height");
1088 XMLParser::SetNodeValue(doc, height, m_height);
1089 auto angle = XMLParser::AppendNode(doc, cadProp, "Angle");
1090 XMLParser::SetNodeValue(doc, angle, m_angle);
1091 auto nodeList = XMLParser::AppendNode(doc, cadProp, "NodeList");
1092 auto nodePos1 = XMLParser::AppendNode(doc, nodeList, "Node");
1093 XMLParser::SetNodeAttribute(doc, nodePos1, "ID", 0);
1094 auto nodePosX1 = XMLParser::AppendNode(doc, nodePos1, "X");
1095 XMLParser::SetNodeValue(doc, nodePosX1, m_pointList[0].m_x);
1096 auto nodePosY1 = XMLParser::AppendNode(doc, nodePos1, "Y");
1097 XMLParser::SetNodeValue(doc, nodePosY1, m_pointList[0].m_y);
1098 auto nodePos2 = XMLParser::AppendNode(doc, nodeList, "Node");
1099 XMLParser::SetNodeAttribute(doc, nodePos2, "ID", 1);
1100 auto nodePosX2 = XMLParser::AppendNode(doc, nodePos2, "X");
1101 XMLParser::SetNodeValue(doc, nodePosX2, m_pointList[m_pointList.size() - 1].m_x);
1102 auto nodePosY2 = XMLParser::AppendNode(doc, nodePos2, "Y");
1103 XMLParser::SetNodeValue(doc, nodePosY2, m_pointList[m_pointList.size() - 1].m_y);
1104
1105 auto parentIDList = XMLParser::AppendNode(doc, cadProp, "ParentIDList");
1106 for (unsigned int i = 0; i < m_parentList.size(); i++) {
1107 Element* parent = m_parentList[i];
1108 if (parent) {
1109 auto parentID = XMLParser::AppendNode(doc, parentIDList, "ParentID");
1110 XMLParser::SetNodeAttribute(doc, parentID, "ID", static_cast<int>(i));
1111 XMLParser::SetNodeValue(doc, parentID, parent->GetID());
1112 }
1113 }
1114
1115 auto electricalProp = XMLParser::AppendNode(doc, elementNode, "ElectricalProperties");
1116 auto isOnline = XMLParser::AppendNode(doc, electricalProp, "IsOnline");
1117 XMLParser::SetNodeValue(doc, isOnline, m_online);
1118 auto name = XMLParser::AppendNode(doc, electricalProp, "Name");
1119 XMLParser::SetNodeValue(doc, name, m_electricalData.name);
1120 auto primaryNominalVoltage = XMLParser::AppendNode(doc, electricalProp, "PrimaryNominalVoltage");
1121 XMLParser::SetNodeValue(doc, primaryNominalVoltage, m_electricalData.primaryNominalVoltage);
1122 XMLParser::SetNodeAttribute(doc, primaryNominalVoltage, "UnitID", static_cast<int>(m_electricalData.primaryNominalVoltageUnit));
1123 auto secondaryNominalVoltage = XMLParser::AppendNode(doc, electricalProp, "SecondaryNominalVoltage");
1124 XMLParser::SetNodeValue(doc, secondaryNominalVoltage, m_electricalData.secondaryNominalVoltage);
1125 XMLParser::SetNodeAttribute(doc, secondaryNominalVoltage, "UnitID", static_cast<int>(m_electricalData.secondaryNominalVoltageUnit));
1126 auto nominalPower = XMLParser::AppendNode(doc, electricalProp, "NominalPower");
1127 XMLParser::SetNodeValue(doc, nominalPower, m_electricalData.nominalPower);
1128 XMLParser::SetNodeAttribute(doc, nominalPower, "UnitID", static_cast<int>(m_electricalData.nominalPowerUnit));
1129 auto resistance = XMLParser::AppendNode(doc, electricalProp, "Resistance");
1130 XMLParser::SetNodeValue(doc, resistance, m_electricalData.resistance);
1131 XMLParser::SetNodeAttribute(doc, resistance, "UnitID", static_cast<int>(m_electricalData.resistanceUnit));
1132 auto indReactance = XMLParser::AppendNode(doc, electricalProp, "IndReactance");
1133 XMLParser::SetNodeValue(doc, indReactance, m_electricalData.indReactance);
1134 XMLParser::SetNodeAttribute(doc, indReactance, "UnitID", static_cast<int>(m_electricalData.indReactanceUnit));
1135 auto connection = XMLParser::AppendNode(doc, electricalProp, "Connection");
1136 XMLParser::SetNodeValue(doc, connection, m_electricalData.connection);
1137 auto turnsRatio = XMLParser::AppendNode(doc, electricalProp, "TurnsRatio");
1138 XMLParser::SetNodeValue(doc, turnsRatio, m_electricalData.turnsRatio);
1139 auto phaseShift = XMLParser::AppendNode(doc, electricalProp, "PhaseShift");
1140 XMLParser::SetNodeValue(doc, phaseShift, m_electricalData.phaseShift);
1141 auto useTransformerPower = XMLParser::AppendNode(doc, electricalProp, "UseTransfomerPower");
1142 XMLParser::SetNodeValue(doc, useTransformerPower, m_electricalData.useTransformerPower);
1143
1144 auto oltcNode = XMLParser::AppendNode(doc, electricalProp, "TapChanger");
1145 auto hasTapChanger = XMLParser::AppendNode(doc, oltcNode, "Enabled");
1146 XMLParser::SetNodeValue(doc, hasTapChanger, m_electricalData.hasTapChanger ? 1 : 0);
1147 auto nominalTurnsRatio = XMLParser::AppendNode(doc, oltcNode, "NominalTurnsRatio");
1148 XMLParser::SetNodeValue(doc, nominalTurnsRatio, m_electricalData.nominalTurnsRatio);
1149 auto oltcControlledBus = XMLParser::AppendNode(doc, oltcNode, "ControlledBus");
1150 XMLParser::SetNodeValue(doc, oltcControlledBus, m_electricalData.oltcControlledBus);
1151 auto oltcTargetVoltage = XMLParser::AppendNode(doc, oltcNode, "TargetVoltage");
1152 XMLParser::SetNodeValue(doc, oltcTargetVoltage, m_electricalData.oltcTargetVoltage);
1153 auto oltcVoltageDeadband = XMLParser::AppendNode(doc, oltcNode, "VoltageDeadband");
1154 XMLParser::SetNodeValue(doc, oltcVoltageDeadband, m_electricalData.oltcVoltageDeadband);
1155 auto oltcMinTap = XMLParser::AppendNode(doc, oltcNode, "MinTap");
1156 XMLParser::SetNodeValue(doc, oltcMinTap, m_electricalData.oltcMinTap);
1157 auto oltcMaxTap = XMLParser::AppendNode(doc, oltcNode, "MaxTap");
1158 XMLParser::SetNodeValue(doc, oltcMaxTap, m_electricalData.oltcMaxTap);
1159 auto oltcTapStep = XMLParser::AppendNode(doc, oltcNode, "TapStep");
1160 XMLParser::SetNodeValue(doc, oltcTapStep, m_electricalData.oltcTapStep);
1161 auto oltcIsDiscrete = XMLParser::AppendNode(doc, oltcNode, "IsDiscrete");
1162 XMLParser::SetNodeValue(doc, oltcIsDiscrete, m_electricalData.oltcIsDiscrete ? 1 : 0);
1163
1164 auto fault = XMLParser::AppendNode(doc, electricalProp, "Fault");
1165 auto zeroResistance = XMLParser::AppendNode(doc, fault, "ZeroResistance");
1166 XMLParser::SetNodeValue(doc, zeroResistance, m_electricalData.zeroResistance);
1167 auto zeroIndReactance = XMLParser::AppendNode(doc, fault, "ZeroIndReactance");
1168 XMLParser::SetNodeValue(doc, zeroIndReactance, m_electricalData.zeroIndReactance);
1169 auto primaryGrndResistance = XMLParser::AppendNode(doc, fault, "PrimaryGrndResistance");
1170 XMLParser::SetNodeValue(doc, primaryGrndResistance, m_electricalData.primaryGrndResistance);
1171 auto primaryGrndReactance = XMLParser::AppendNode(doc, fault, "PrimaryGrndReactance");
1172 XMLParser::SetNodeValue(doc, primaryGrndReactance, m_electricalData.primaryGrndReactance);
1173 auto secondaryGrndResistance = XMLParser::AppendNode(doc, fault, "SecondaryGrndResistance");
1174 XMLParser::SetNodeValue(doc, secondaryGrndResistance, m_electricalData.secondaryGrndResistance);
1175 auto secondaryGrndReactance = XMLParser::AppendNode(doc, fault, "SecondaryGrndReactance");
1176 XMLParser::SetNodeValue(doc, secondaryGrndReactance, m_electricalData.secondaryGrndReactance);
1177
1178 SaveSwitchingData(doc, electricalProp);
1179
1180 return elementNode;
1181}
1182
1183bool Transformer::OpenElement(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList)
1184{
1185 auto cadPropNode = elementNode->first_node("CADProperties");
1186 if (!cadPropNode) return false;
1187
1188 auto position = cadPropNode->first_node("Position");
1189 double posX = XMLParser::GetNodeValueDouble(position, "X");
1190 double posY = XMLParser::GetNodeValueDouble(position, "Y");
1191 auto size = cadPropNode->first_node("Size");
1192 m_width = XMLParser::GetNodeValueDouble(size, "Width");
1193 m_height = XMLParser::GetNodeValueDouble(size, "Height");
1194 m_angle = XMLParser::GetNodeValueDouble(cadPropNode, "Angle");
1195
1196 // Get nodes points
1197 std::vector<wxPoint2DDouble> ptsList;
1198 auto nodePosList = cadPropNode->first_node("NodeList");
1199 if (!nodePosList) return false;
1200 auto nodePos = nodePosList->first_node("Node");
1201 while (nodePos) {
1202 double nodePosX = XMLParser::GetNodeValueDouble(nodePos, "X");
1203 double nodePosY = XMLParser::GetNodeValueDouble(nodePos, "Y");
1204 ptsList.push_back(wxPoint2DDouble(nodePosX, nodePosY));
1205 nodePos = nodePos->next_sibling("Node");
1206 }
1207
1208 // Get parents IDs
1209 auto parentIDList = cadPropNode->first_node("ParentIDList");
1210 if (!parentIDList) return false;
1211 auto parentNode = parentIDList->first_node("ParentID");
1212 long parentID[2] = { -1, -1 };
1213 while (parentNode) {
1214 long index = 0;
1215 wxString(parentNode->first_attribute("ID")->value()).ToLong(&index);
1216 wxString(parentNode->value()).ToCLong(&parentID[index]);
1217 parentNode = parentNode->next_sibling("ParentID");
1218 }
1219
1220 std::vector<wxPoint2DDouble> nodePtsList; // List of node points
1221 nodePtsList.push_back(ptsList[0]); // First point on the list
1222 nodePtsList.push_back(ptsList[ptsList.size() - 1]); // Last point on the list
1223
1224 // List of dummy buses to set not connected nodes properly
1225 std::vector<Bus*> dummyBusList;
1226 // Set parents (if have)
1227 for (unsigned int i = 0; i < 2; ++i) {
1228 if (parentID[i] == -1) // No parent connected
1229 {
1230 Bus* dummyBus = new Bus(nodePtsList[i]);
1231 dummyBusList.push_back(dummyBus);
1232 AddParent(dummyBus, nodePtsList[i], true);
1233 }
1234 else { // Parent connected (necessarily a bus, get from bus list)
1235 AddParent(parentList[parentID[i]], nodePtsList[i], true);
1236 }
1237 }
1238
1239 StartMove(m_position);
1240 Move(wxPoint2DDouble(posX, posY));
1241
1242 // Remove dummy buses
1243 for (auto it = dummyBusList.begin(), itEnd = dummyBusList.end(); it != itEnd; ++it) {
1244 RemoveParent(*it);
1245 delete* it;
1246 }
1247 dummyBusList.clear();
1248
1249 // Set rotation properly.
1250 //int numRot = angle / GetRotationAngle();
1251 //bool clockwise = true;
1252 //if (numRot < 0) {
1253 // numRot = std::abs(numRot);
1254 // clockwise = false;
1255 //}
1256 //for (int i = 0; i < numRot; i++) Rotate(clockwise);
1257
1258 auto electricalProp = elementNode->first_node("ElectricalProperties");
1259 if (!electricalProp) return false;
1260
1261 SetOnline(XMLParser::GetNodeValueInt(electricalProp, "IsOnline"));
1262 m_electricalData.name = electricalProp->first_node("Name")->value();
1263 m_electricalData.primaryNominalVoltage = XMLParser::GetNodeValueDouble(electricalProp, "PrimaryNominalVoltage");
1264 m_electricalData.primaryNominalVoltageUnit =
1265 static_cast<ElectricalUnit>(XMLParser::GetAttributeValueInt(electricalProp, "PrimaryNominalVoltage", "UnitID"));
1266 m_electricalData.secondaryNominalVoltage = XMLParser::GetNodeValueDouble(electricalProp, "SecondaryNominalVoltage");
1267 m_electricalData.secondaryNominalVoltageUnit = static_cast<ElectricalUnit>(
1268 XMLParser::GetAttributeValueInt(electricalProp, "SecondaryNominalVoltage", "UnitID"));
1269 m_electricalData.nominalPower = XMLParser::GetNodeValueDouble(electricalProp, "NominalPower");
1270 m_electricalData.nominalPowerUnit =
1271 static_cast<ElectricalUnit>(XMLParser::GetAttributeValueInt(electricalProp, "NominalPower", "UnitID"));
1272 m_electricalData.resistance = XMLParser::GetNodeValueDouble(electricalProp, "Resistance");
1273 m_electricalData.resistanceUnit =
1274 static_cast<ElectricalUnit>(XMLParser::GetAttributeValueInt(electricalProp, "Resistance", "UnitID"));
1275 m_electricalData.indReactance = XMLParser::GetNodeValueDouble(electricalProp, "IndReactance");
1276 m_electricalData.indReactanceUnit =
1277 static_cast<ElectricalUnit>(XMLParser::GetAttributeValueInt(electricalProp, "IndReactance", "UnitID"));
1278 m_electricalData.connection = (TransformerConnection)XMLParser::GetNodeValueInt(electricalProp, "Connection");
1279 m_electricalData.turnsRatio = XMLParser::GetNodeValueDouble(electricalProp, "TurnsRatio");
1280 m_electricalData.phaseShift = XMLParser::GetNodeValueDouble(electricalProp, "PhaseShift");
1281 m_electricalData.useTransformerPower = XMLParser::GetNodeValueInt(electricalProp, "UseTransfomerPower");
1282
1283 auto oltcNode = electricalProp->first_node("TapChanger");
1284 if (oltcNode) {
1285 m_electricalData.hasTapChanger = XMLParser::GetNodeValueInt(oltcNode, "Enabled") == 1;
1286 auto nomNode = oltcNode->first_node("NominalTurnsRatio");
1287 if (nomNode) wxString(nomNode->value()).ToCDouble(&m_electricalData.nominalTurnsRatio);
1288 else m_electricalData.nominalTurnsRatio = m_electricalData.turnsRatio;
1289 auto ctrlBusNode = oltcNode->first_node("ControlledBus");
1290 if (ctrlBusNode) {
1291 long cb = 1;
1292 wxString(ctrlBusNode->value()).ToCLong(&cb);
1293 m_electricalData.oltcControlledBus = (int)cb;
1294 }
1295 auto targetVNode = oltcNode->first_node("TargetVoltage");
1296 if (targetVNode) wxString(targetVNode->value()).ToCDouble(&m_electricalData.oltcTargetVoltage);
1297 auto deadbandNode = oltcNode->first_node("VoltageDeadband");
1298 if (deadbandNode) wxString(deadbandNode->value()).ToCDouble(&m_electricalData.oltcVoltageDeadband);
1299 auto minTapNode = oltcNode->first_node("MinTap");
1300 if (minTapNode) wxString(minTapNode->value()).ToCDouble(&m_electricalData.oltcMinTap);
1301 auto maxTapNode = oltcNode->first_node("MaxTap");
1302 if (maxTapNode) wxString(maxTapNode->value()).ToCDouble(&m_electricalData.oltcMaxTap);
1303 auto tapStepNode = oltcNode->first_node("TapStep");
1304 if (tapStepNode) wxString(tapStepNode->value()).ToCDouble(&m_electricalData.oltcTapStep);
1305 m_electricalData.oltcIsDiscrete = XMLParser::GetNodeValueInt(oltcNode, "IsDiscrete") == 1;
1306 }
1307 else {
1308 m_electricalData.hasTapChanger = false;
1309 m_electricalData.nominalTurnsRatio = m_electricalData.turnsRatio;
1310 m_electricalData.oltcControlledBus = 1;
1311 m_electricalData.oltcTargetVoltage = 1.0;
1312 m_electricalData.oltcVoltageDeadband = 0.005;
1313 m_electricalData.oltcMinTap = 0.90;
1314 m_electricalData.oltcMaxTap = 1.10;
1315 m_electricalData.oltcTapStep = 0.00625;
1316 m_electricalData.oltcIsDiscrete = false;
1317 }
1318
1319 auto fault = electricalProp->first_node("Fault");
1320 m_electricalData.zeroResistance = XMLParser::GetNodeValueDouble(fault, "ZeroResistance");
1321 m_electricalData.zeroIndReactance = XMLParser::GetNodeValueDouble(fault, "ZeroIndReactance");
1322 m_electricalData.primaryGrndResistance = XMLParser::GetNodeValueDouble(fault, "PrimaryGrndResistance");
1323 m_electricalData.primaryGrndReactance = XMLParser::GetNodeValueDouble(fault, "PrimaryGrndReactance");
1324 m_electricalData.secondaryGrndResistance = XMLParser::GetNodeValueDouble(fault, "SecondaryGrndResistance");
1325 m_electricalData.secondaryGrndReactance = XMLParser::GetNodeValueDouble(fault, "SecondaryGrndReactance");
1326
1327 if (!OpenSwitchingData(electricalProp)) return false;
1328 if (m_swData.swTime.size() != 0) SetDynamicEvent(true);
1329
1330 return true;
1331}
1332
1333void Transformer::SetBestPositionAndRotation()
1334{
1335 wxPoint2DDouble p1 = m_pointList[0];
1336 wxPoint2DDouble p2 = m_pointList[m_pointList.size() - 1];
1337 double dx = p2.m_x - p1.m_x;
1338 double dy = p2.m_y - p1.m_y;
1339
1340 if (std::abs(dy) > std::abs(dx)) {
1341 m_angle = (dy >= 0.0) ? 90.0 : 270.0;
1342 }
1343 else {
1344 m_angle = (dx >= 0.0) ? 0.0 : 180.0;
1345 }
1346
1347 wxPoint2DDouble mid = (p1 + p2) / 2.0;
1348 if (std::abs(dx) < 1.0) {
1349 mid.m_x = p1.m_x;
1350 mid.m_y = std::round(mid.m_y / 20.0) * 20.0;
1351 }
1352 else if (std::abs(dy) < 1.0) {
1353 mid.m_y = p1.m_y;
1354 mid.m_x = std::round(mid.m_x / 20.0) * 20.0;
1355 }
1356 else {
1357 mid.m_x = std::round(mid.m_x / 20.0) * 20.0;
1358 mid.m_y = std::round(mid.m_y / 20.0) * 20.0;
1359 }
1360
1361 SetPosition(mid);
1362
1363 if (m_parentList.size() > 0 && m_parentList[0] && m_parentList.size() > 1 && m_parentList[1]) {
1364 Element* bus1 = m_parentList[0];
1365 Element* bus2 = m_parentList[1];
1366 if (std::abs(dy) > std::abs(dx)) {
1367 wxPoint2DDouble loc1 = bus1->RotateAtPosition(wxPoint2DDouble(m_position.m_x, bus1->GetPosition().m_y), -bus1->GetAngle());
1368 wxPoint2DDouble loc2 = bus2->RotateAtPosition(wxPoint2DDouble(m_position.m_x, bus2->GetPosition().m_y), -bus2->GetAngle());
1369 double halfW1 = bus1->GetWidth() / 2.0 + 2.0;
1370 double halfW2 = bus2->GetWidth() / 2.0 + 2.0;
1371 if (std::abs(loc1.m_x - bus1->GetPosition().m_x) <= halfW1 &&
1372 std::abs(loc2.m_x - bus2->GetPosition().m_x) <= halfW2) {
1373 m_pointList[0].m_x = m_position.m_x;
1374 m_pointList.back().m_x = m_position.m_x;
1375 }
1376 }
1377 else {
1378 wxPoint2DDouble loc1 = bus1->RotateAtPosition(wxPoint2DDouble(bus1->GetPosition().m_x, m_position.m_y), -bus1->GetAngle());
1379 wxPoint2DDouble loc2 = bus2->RotateAtPosition(wxPoint2DDouble(bus2->GetPosition().m_x, m_position.m_y), -bus2->GetAngle());
1380 double halfW1 = bus1->GetWidth() / 2.0 + 2.0;
1381 double halfW2 = bus2->GetWidth() / 2.0 + 2.0;
1382 if (std::abs(loc1.m_x - bus1->GetPosition().m_x) <= halfW1 &&
1383 std::abs(loc2.m_x - bus2->GetPosition().m_x) <= halfW2) {
1384 m_pointList[0].m_y = m_position.m_y;
1385 m_pointList.back().m_y = m_position.m_y;
1386 }
1387 }
1388 }
1389
1390 if (m_pointList.size() >= 4) {
1391 wxPoint2DDouble t1 = m_position + RotateLocal(wxPoint2DDouble(-40.0, 0.0), m_angle);
1392 wxPoint2DDouble t2 = m_position + RotateLocal(wxPoint2DDouble(40.0, 0.0), m_angle);
1393 m_pointList[2] = wxPoint2DDouble(std::round(t1.m_x), std::round(t1.m_y));
1394 m_pointList[3] = wxPoint2DDouble(std::round(t2.m_x), std::round(t2.m_y));
1395 }
1396 UpdateSwitchesPosition();
1397 UpdatePowerFlowArrowsPosition();
1398}
@ ID_EDIT_ELEMENT
Definition Element.h:77
PowerFlowDirection
Direction of power flow arrows.
Abstract class for branch power elements.
Definition Branch.h:32
virtual void UpdateSwitches()
Update the switch position.
Definition Branch.cpp:179
virtual void RemoveParent(Element *parent)
Remove a parent.
Definition Branch.cpp:105
Node for power elements. All others power elements are connected through this.
Definition Bus.h:87
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:115
virtual bool RotatedRectanglesIntersects(wxRect2DDouble rect1, wxRect2DDouble rect2, double angle1, double angle2) const
Check if two roteted rectangles intersect.
Definition Element.cpp:377
virtual bool Intersects(wxRect2DDouble rect) const =0
Check if the element's rect intersects other rect.
virtual int GetID() const
Get the element ID.
Definition Element.h:275
double GetWidth() const
Get the element width.
Definition Element.h:209
virtual void GeneralMenuItens(wxMenu &menu)
Insert general itens to context menu.
Definition Element.cpp:475
wxPoint2DDouble GetPosition() const
Get the element position.
Definition Element.h:189
double GetAngle() const
Get the element angle.
Definition Element.h:214
double GetHeight() const
Get the element height.
Definition Element.h:199
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
Definition Element.cpp:33
virtual wxPoint2DDouble RotateAtPosition(wxPoint2DDouble pointToRotate, double angle, bool degrees=true) const
Rotate a point as element position being the origin.
Definition Element.cpp:298
virtual void AddChild(Element *child)
Add a child to the child list.
Definition Element.cpp:581
static wxString StringFromDouble(double value, int minDecimal=1, int maxDecimals=13)
Convert a double value to string.
Definition Element.cpp:548
bool SetOnline(bool online=true)
Set if the element is online or offline.
Definition Element.cpp:465
virtual void DrawDCCircle(wxPoint2DDouble position, double radius, int numSegments, wxGraphicsContext *gc) const
Draw a circle using device context.
Definition Element.cpp:173
virtual void SetDynamicEvent(bool dynEvent=true)
Set if the power element have dynamic event.
virtual void CalculatePowerFlowPts(std::vector< wxPoint2DDouble > edges)
Calculate the points of the power flow arrows.
virtual void DrawDCPowerFlowPts(GUIColour *guiColour, wxGraphicsContext *gc) const
Draw power flow arrows.
virtual void DrawDCSwitches(GUIColour *guiColour, wxGraphicsContext *gc) const
Draw switch.
virtual wxPoint2DDouble GetSwitchPoint(Element *parent, wxPoint2DDouble parentPoint, wxPoint2DDouble secondPoint) const
Get the correct switch position.
Form to edit the transformer power data and OLTC tap changer.
Two-winding transformer power element with OLTC support.
Definition Transformer.h:96
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
virtual wxString GetTipText() const
Get the tip text.
virtual void DrawDC(GUIColour *guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext *gc) const
Draw the element using GDI+.
virtual Element * GetCopy()
Get a the element copy.
virtual bool Contains(wxPoint2DDouble position) const
Checks if the element contains a position.
virtual void SetPowerFlowDirection(PowerFlowDirection pfDirection)
Set the direction of the power flow.
virtual bool Intersects(wxRect2DDouble rect) const
Check if the element's rect intersects other rect.
virtual bool SetNodeParent(Element *parent)
Set a perent to the node. If all conditions are met, a new parent are added to the element and the po...
virtual void Move(wxPoint2DDouble position)
Move the element other position.
virtual void SetNominalVoltage(std::vector< double > nominalVoltage, std::vector< ElectricalUnit > nominalVoltageUnit)
Set nominal voltage of the element.
virtual void MoveNode(Element *parent, wxPoint2DDouble position)
Move a node. StartMove(wxPoint2DDouble position) before start moving.
virtual bool ShowForm(wxWindow *parent, Element *element, wxWindow *workspace=nullptr)
Show element data form.
virtual void Rotate(bool clockwise=true)
Rotate the element.
virtual bool AddParent(Element *parent, wxPoint2DDouble position, bool isOpening=false)
Add a parent to the element. This method must be used on power elements that connect to a bus,...
virtual void RotateNode(Element *parent, bool clockwise)
Rotate a node.
virtual bool GetContextMenu(wxMenu &menu)
Get the element contex menu.
std::vector< double > swTime