Power System Platform  2026w23a-beta
Loading...
Searching...
No Matches
Line.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 "Line.h"
19#include "../../utils/Path.h"
20
21Line::Line() : Branch()
22{
23 m_elementType = TYPE_LINE;
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}
28
29Line::Line(wxString name) : Branch()
30{
31 m_elementType = TYPE_LINE;
32 for (int i = 0; i < 2; i++) {
33 for (int j = 0; j < 3; j++) { m_electricalData.faultCurrent[i][j] = std::complex<double>(0.0, 0.0); }
34 }
35 m_electricalData.name = name;
36}
37Line::~Line() {}
38bool Line::Contains(wxPoint2DDouble position) const
39{
40 if (PointToLineDistance(position) < 5.0) { return true; }
41 return false;
42}
43
44//void Line::Draw(wxPoint2DDouble translation, double scale) const
45//{
46// OpenGLColour elementColour;
47// if (m_online) {
48// if (m_dynEvent)
49// elementColour = m_dynamicEventColour;
50// else
51// elementColour = m_onlineElementColour;
52//
53// }
54// else
55// elementColour = m_offlineElementColour;
56//
57// std::vector<wxPoint2DDouble> pointList = m_pointList;
58// if (!m_inserted && pointList.size() > 0) {
59// wxPoint2DDouble secondPoint = m_position;
60// if (pointList.size() > 2) { secondPoint = pointList[2]; }
61// pointList[1] = GetSwitchPoint(m_parentList[0], pointList[0], secondPoint);
62// pointList.push_back(m_position);
63// }
64//
65// // Line selected (Layer 1).
66// if (m_selected) {
67// glLineWidth(1.5 + m_borderSize * 2.0);
68// glColor4dv(m_selectionColour.GetRGBA());
69// DrawLine(pointList);
70//
71// // Draw nodes selection.
72// if (pointList.size() > 0) {
73// DrawCircle(pointList[0], 5.0 + m_borderSize / scale, 10, GL_POLYGON);
74// if (m_inserted) { DrawCircle(pointList[pointList.size() - 1], 5.0 + m_borderSize / scale, 10, GL_POLYGON); }
75// }
76// }
77//
78// // Draw line (Layer 2)
79// glLineWidth(1.5);
80// glColor4dv(elementColour.GetRGBA());
81// DrawLine(pointList);
82//
83// if (m_inserted) {
84// DrawSwitches();
85// DrawPowerFlowPts();
86// }
87//
88// // Draw nodes.
89// if (pointList.size() > 0) {
90// glColor4dv(elementColour.GetRGBA());
91// DrawCircle(pointList[0], 5.0, 10, GL_POLYGON);
92// if (m_inserted) { DrawCircle(pointList[pointList.size() - 1], 5.0, 10, GL_POLYGON); }
93// }
94//
95// // Draw pickboxes (Layer 3).
96// if (m_showPickbox) {
97// glPushMatrix();
98// glLoadIdentity();
99//
100// for (int i = 2; i < (int)m_pointList.size() - 2; i++) {
101// DrawPickbox(WorldToScreen(m_pointList[i], translation, scale));
102// }
103//
104// glPopMatrix();
105// }
106//}
107
108void Line::DrawDC(GUIColour* guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext* gc) const
109{
110 //gc->SetBrush(*wxTRANSPARENT_BRUSH);
111
112 wxGraphicsMatrix identityMatrix = gc->GetTransform();
113 identityMatrix.Set(); // Set to identity
114
115 wxColour elementColour;
116 if (m_online) {
117 if (m_dynEvent)
118 elementColour = guiColour->eventElement;
119 else
120 elementColour = guiColour->enabled;
121 }
122 else
123 elementColour = guiColour->disable;
124
125 std::vector<wxPoint2DDouble> pointList = m_pointList;
126 if (!m_inserted && pointList.size() > 0) {
127 wxPoint2DDouble secondPoint = m_position;
128 if (pointList.size() > 2) { secondPoint = pointList[2]; }
129 pointList[1] = GetSwitchPoint(m_parentList[0], pointList[0], secondPoint);
130 pointList.push_back(m_position);
131 }
132
133 // Line selected (Layer 1).
134 if (m_selected) {
135 gc->SetPen(wxPen(guiColour->selection, 2 + m_borderSize * 2.0));
136 gc->SetBrush(*wxTRANSPARENT_BRUSH);
137 if (pointList.size() > 0)
138 gc->StrokeLines(pointList.size(), &pointList[0]);
139
140 // Draw nodes selection.
141 gc->SetPen(*wxTRANSPARENT_PEN);
142 gc->SetBrush(wxBrush(guiColour->selection));
143 if (pointList.size() > 0) {
144 DrawDCCircle(pointList[0], 5.0 + m_borderSize / scale, 10, gc);
145 if (m_inserted) { DrawDCCircle(pointList[pointList.size() - 1], 5.0 + m_borderSize / scale, 10, gc); }
146 }
147 }
148
149 // Draw line (Layer 2)
150 gc->SetPen(wxPen(elementColour, 2));
151 gc->SetBrush(*wxTRANSPARENT_BRUSH);
152 if (pointList.size() > 0)
153 gc->StrokeLines(pointList.size(), &pointList[0]);
154
155 if (m_inserted) {
156 DrawDCSwitches(guiColour, gc);
157 DrawDCPowerFlowPts(guiColour, gc);
158 }
159
160 // Draw nodes.
161 gc->SetPen(*wxTRANSPARENT_PEN);
162 gc->SetBrush(wxBrush(elementColour));
163 if (pointList.size() > 0) {
164 DrawDCCircle(pointList[0], 5.0, 10, gc);
165 if (m_inserted) { DrawDCCircle(pointList[pointList.size() - 1], 5.0, 10, gc); }
166 }
167
168 // Draw pickboxes (Layer 3).
169 if (m_showPickbox) {
170 gc->PushState();
171 gc->SetTransform(identityMatrix);
172
173 for (int i = 2; i < (int)m_pointList.size() - 2; i++) {
174 DrawDCPickbox(WorldToScreen(m_pointList[i], translation - wxPoint2DDouble(4 / scale, 4 / scale), scale), gc);
175 }
176
177 gc->PopState();
178 }
179}
180
181void Line::DrawDC(GUIColour* guiColour, wxPoint2DDouble translation, double scale, wxDC& dc) const
182{
183
184 wxColour elementColour;
185 if (m_online) {
186 if (m_dynEvent)
187 elementColour = guiColour->eventElement;
188 else
189 elementColour = guiColour->enabled;
190 }
191 else
192 elementColour = guiColour->disable;
193
194 std::vector<wxPoint> pointList;
195 for (auto pt : m_pointList) {
196 pointList.emplace_back(pt.m_x, pt.m_y);
197 }
198
199 // Line selected (Layer 1).
200 if (m_selected) {
201 dc.SetPen(wxPen(guiColour->selection, 2 + m_borderSize * 2.0));
202 dc.SetBrush(*wxTRANSPARENT_BRUSH);
203 if (pointList.size() > 0)
204 dc.DrawLines(pointList.size(), &pointList[0]);
205
206 // Draw nodes selection.
207 dc.SetPen(*wxTRANSPARENT_PEN);
208 dc.SetBrush(wxBrush(guiColour->selection));
209 if (pointList.size() > 0) {
210 DrawDCCircle(pointList[0], 5.0 + m_borderSize / scale, dc);
211 if (m_inserted) { DrawDCCircle(pointList[pointList.size() - 1], 5.0 + m_borderSize / scale, dc); }
212 }
213 }
214
215 // Draw line (Layer 2)
216 dc.SetPen(wxPen(elementColour, 2));
217 dc.SetBrush(*wxTRANSPARENT_BRUSH);
218 if (pointList.size() > 0)
219 dc.DrawLines(pointList.size(), &pointList[0]);
220
221 if (m_inserted) {
222 DrawDCSwitches(guiColour, dc);
223 DrawDCPowerFlowPts(guiColour, dc);
224 }
225
226 // Draw nodes.
227 dc.SetPen(*wxTRANSPARENT_PEN);
228 dc.SetBrush(wxBrush(elementColour));
229 if (pointList.size() > 0) {
230 DrawDCCircle(pointList[0], 5.0, dc);
231 if (m_inserted) { DrawDCCircle(pointList[pointList.size() - 1], 5.0, dc); }
232 }
233}
234
235void Line::Move(wxPoint2DDouble position)
236{
237 if (!m_parentList[0]) {
238 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
239 UpdateSwitchesPosition();
240 UpdatePowerFlowArrowsPosition();
241 }
242 if (!m_parentList[1]) {
243 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
244 UpdateSwitchesPosition();
245 UpdatePowerFlowArrowsPosition();
246 }
247
248 if (!m_parentList[0] && !m_parentList[1]) {
249 for (int i = 2; i < (int)m_pointList.size() - 2; i++) {
250 m_pointList[i] = m_movePts[i] + position - m_moveStartPt;
251 }
252 }
253}
254
255bool Line::AddParent(Element* parent, wxPoint2DDouble position)
256{
257 if (parent) {
258 // First bus.
259 if (m_parentList.size() == 0) {
260 m_position = position;
261 m_parentList.push_back(parent);
262 parent->AddChild(this);
263 wxPoint2DDouble parentPt =
264 parent->RotateAtPosition(position, -parent->GetAngle()); // Rotate click to horizontal position.
265 parentPt.m_y = parent->GetPosition().m_y; // Centralize on bus.
266 parentPt = parent->RotateAtPosition(parentPt, parent->GetAngle()); // Rotate back.
267 m_pointList.push_back(parentPt); // First point
268 m_pointList.push_back(GetSwitchPoint(parent, parentPt, m_position));
269
270 wxRect2DDouble genRect(0, 0, 0, 0);
271 m_switchRect.push_back(genRect);
273
274 Bus* parentBus = static_cast<Bus*>(parent);
275 m_electricalData.nominalVoltage = parentBus->GetElectricalData().nominalVoltage;
276 m_electricalData.nominalVoltageUnit = parentBus->GetElectricalData().nominalVoltageUnit;
277
278 return false;
279 }
280 // Second bus.
281 else if (parent != m_parentList[0]) {
282 Bus* parentBus = static_cast<Bus*>(parent);
283 if (m_electricalData.nominalVoltage != parentBus->GetElectricalData().nominalVoltage ||
284 m_electricalData.nominalVoltageUnit != parentBus->GetElectricalData().nominalVoltageUnit) {
285 wxMessageDialog msgDialog(nullptr,
286 _("Unable to connect two buses with different nominal voltages.\n"
287 "Use a transformer or edit the bus properties."),
288 _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
289 msgDialog.ShowModal();
290 return false;
291 }
292
293 m_parentList.push_back(parent);
294 parent->AddChild(this);
295 wxPoint2DDouble parentPt =
296 parent->RotateAtPosition(position, -parent->GetAngle()); // Rotate click to horizontal position.
297 parentPt.m_y = parent->GetPosition().m_y; // Centralize on bus.
298 parentPt = parent->RotateAtPosition(parentPt, parent->GetAngle()); // Rotate back.
299
300 // Set first switch point.
301 wxPoint2DDouble secondPoint = parentPt;
302 if (m_pointList.size() > 2) { secondPoint = m_pointList[2]; }
303 m_pointList[1] = GetSwitchPoint(m_parentList[0], m_pointList[0], secondPoint);
304
305 // Set the second switch point.
306 m_pointList.push_back(GetSwitchPoint(parent, parentPt, m_pointList[m_pointList.size() - 1]));
307
308 m_pointList.push_back(parentPt); // Last point.
309
310 wxRect2DDouble genRect(0, 0, 0, 0);
311 m_switchRect.push_back(genRect);
313
314 SetInserted();
315 UpdatePowerFlowArrowsPosition();
316 return true;
317 }
318 }
319 return false;
320}
321bool Line::Intersects(wxRect2DDouble rect) const
322{
323 for (auto it = m_pointList.begin(); it != m_pointList.end(); ++it) {
324 if (rect.Contains(*it)) return true;
325 }
326 return false;
327}
328void Line::MovePickbox(wxPoint2DDouble position)
329{
330 if (m_activePickboxID == ID_PB_NONE) return;
331
332 for (int i = 2; i < (int)m_pointList.size() - 2; i++) {
333 if (m_activePickboxID == i) {
334 m_pointList[i] = m_movePts[i] + position - m_moveStartPt;
335 UpdateSwitchesPosition();
336 UpdatePowerFlowArrowsPosition();
337 }
338 }
339}
340bool Line::PickboxContains(wxPoint2DDouble position)
341{
342 for (int i = 2; i < (int)m_pointList.size() - 2; i++) {
343 wxRect2DDouble rect(m_pointList[i].m_x - 5.0, m_pointList[i].m_y - 5.0, 10.0, 10.0);
344 if (rect.Contains(position)) {
345 m_activePickboxID = i;
346 return true;
347 }
348 }
349 return false;
350}
351
352void Line::AddPoint(wxPoint2DDouble point)
353{
354 if (m_parentList.size() != 0) { m_pointList.push_back(point); }
355}
356
357void Line::StartMove(wxPoint2DDouble position)
358{
359 m_moveStartPt = position;
360 m_movePts = m_pointList;
361}
362
363void Line::MoveNode(Element* parent, wxPoint2DDouble position)
364{
365 if (parent) {
366 // First bus.
367 if (parent == m_parentList[0]) {
368 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
369 }
370 // Second bus.
371 else if (parent == m_parentList[1]) {
372 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
373 }
374
375 // If the line is selected, move all the points, except the switches and buses points.
376 if (m_selected) {
377 for (int i = 2; i < (int)m_pointList.size() - 1; i++) {
378 m_pointList[i] = m_movePts[i] + position - m_moveStartPt;
379 }
380 }
381 }
382 else {
383 // If parent is setted to nullptr for the firts time, remove the parent child
384 if (m_activeNodeID == 1) {
385 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
386 if (m_parentList[0]) {
387 m_parentList[0]->RemoveChild(this);
388 m_parentList[0] = nullptr;
389 m_online = false;
390 }
391 }
392 else if (m_activeNodeID == 2) {
393 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
394 if (m_parentList[1]) {
395 m_parentList[1]->RemoveChild(this);
396 m_parentList[1] = nullptr;
397 m_online = false;
398 }
399 }
400 }
401
402 // Recalculate switches positions
403 UpdateSwitchesPosition();
404 UpdatePowerFlowArrowsPosition();
405}
406
407bool Line::GetContextMenu(wxMenu& menu)
408{
409 //wxFileName exeFileName(wxStandardPaths::Get().GetExecutablePath());
410 //wxString exePath = exeFileName.GetPath();
411
412 menu.Append(ID_EDIT_ELEMENT, _("Edit line"));
413
414 wxString busName[2] = { "?", "?" };
415 if (m_parentList.size() == 2) {
416 int i = 0;
417 for (Element* element : m_parentList) {
418 if (element) {
419 Bus* bus = static_cast<Bus*>(element);
420 busName[i] = bus->GetElectricalData().name;
421 }
422 i++;
423 }
424 }
425
426 wxMenu* textMenu = new wxMenu();
427
428 textMenu->Append(ID_TXT_NAME, _("Name"));
429 textMenu->Append(ID_TXT_BRANCH_ACTIVE_POWER_1_2, _("Active power (") + busName[0] + _(" to ") + busName[1] + wxT(")"));
430 textMenu->Append(ID_TXT_BRANCH_ACTIVE_POWER_2_1, _("Active power (") + busName[1] + _(" to ") + busName[0] + wxT(")"));
431 textMenu->Append(ID_TXT_BRANCH_REACTIVE_POWER_1_2, _("Reactive power (") + busName[0] + _(" to ") + busName[1] + wxT(")"));
432 textMenu->Append(ID_TXT_BRANCH_REACTIVE_POWER_2_1, _("Reactive power (") + busName[1] + _(" to ") + busName[0] + wxT(")"));
433 textMenu->Append(ID_TXT_BRANCH_LOSSES, _("Losses"));
434 textMenu->Append(ID_TXT_BRANCH_CURRENT_1_2, _("Current (") + busName[0] + _(" to ") + busName[1] + wxT(")"));
435 textMenu->Append(ID_TXT_BRANCH_CURRENT_2_1, _("Current (") + busName[1] + _(" to ") + busName[0] + wxT(")"));
436 textMenu->Append(ID_TXT_BRANCH_FAULT_CURRENT_1_2, _("Fault current (") + busName[0] + _(" to ") + busName[1] + wxT(")"));
437 textMenu->Append(ID_TXT_BRANCH_FAULT_CURRENT_2_1, _("Fault current (") + busName[1] + _(" to ") + busName[0] + wxT(")"));
438 textMenu->SetClientData(menu.GetClientData());
439 menu.AppendSubMenu(textMenu, _("Add text"));
440
441 if (m_activePickboxID == ID_PB_NONE) {
442 wxMenuItem* addNodeItem = new wxMenuItem(&menu, ID_LINE_ADD_NODE, _("Insert node"));
443 addNodeItem->SetBitmap(wxImage(Paths::GetDataPath() + "/images/menu/addNode16.png"));
444 menu.Append(addNodeItem);
445 }
446 else {
447 wxMenuItem* addNodeItem = new wxMenuItem(&menu, ID_LINE_REMOVE_NODE, _("Remove node"));
448 addNodeItem->SetBitmap(wxImage(Paths::GetDataPath() + "/images/menu/removeNode16.png"));
449 menu.Append(addNodeItem);
450 }
451 wxMenuItem* deleteItem = new wxMenuItem(&menu, ID_DELETE, _("Delete"));
452 deleteItem->SetBitmap(wxImage(Paths::GetDataPath() + "/images/menu/delete16.png"));
453 menu.Append(deleteItem);
454 return true;
455}
456
457void Line::RemoveNode(wxPoint2DDouble point)
458{
459 if (PickboxContains(point)) {
460 for (int i = 2; i < (int)m_pointList.size() - 2; i++) {
461 if (m_activePickboxID == i) {
462 m_pointList.erase(m_pointList.begin() + i);
463 break;
464 }
465 }
466 }
467 UpdateSwitchesPosition();
468 UpdatePowerFlowArrowsPosition();
469}
470
471void Line::AddNode(wxPoint2DDouble point)
472{
473 int segmentNumber = 0;
474 PointToLineDistance(point, &segmentNumber);
475 if (segmentNumber > 0 && segmentNumber < (int)m_pointList.size() - 2) {
476 m_pointList.insert(m_pointList.begin() + segmentNumber + 1, point);
477 }
478 UpdateSwitchesPosition();
479 UpdatePowerFlowArrowsPosition();
480}
481
482void Line::CalculateBoundaries(wxPoint2DDouble& leftUp, wxPoint2DDouble& rightBottom) const
483{
484 if (m_pointList.size() > 0) {
485 // Check points list boundaries.
486 leftUp = m_pointList[0];
487 rightBottom = m_pointList[0];
488 for (int i = 1; i < (int)m_pointList.size(); i++) {
489 if (m_pointList[i].m_x < leftUp.m_x) leftUp.m_x = m_pointList[i].m_x;
490 if (m_pointList[i].m_y < leftUp.m_y) leftUp.m_y = m_pointList[i].m_y;
491 if (m_pointList[i].m_x > rightBottom.m_x) rightBottom.m_x = m_pointList[i].m_x;
492 if (m_pointList[i].m_y > rightBottom.m_y) rightBottom.m_y = m_pointList[i].m_y;
493 }
494 }
495}
496
497bool Line::ShowForm(wxWindow* parent, Element* element, wxWindow* workspace)
498{
499 LineForm lineForm(parent, this);
500 lineForm.CenterOnParent();
501 if (lineForm.ShowModal() == wxID_OK) {
502 return true;
503 }
504 return false;
505}
506
507void Line::SetNominalVoltage(std::vector<double> nominalVoltage, std::vector<ElectricalUnit> nominalVoltageUnit)
508{
509 if (nominalVoltage.size() > 0) {
510 m_electricalData.nominalVoltage = nominalVoltage[0];
511 m_electricalData.nominalVoltageUnit = nominalVoltageUnit[0];
512 }
513}
514
516{
517 if (m_activeNodeID == 1 && parent == m_parentList[0]) return false;
518 if (m_activeNodeID == 2 && parent == m_parentList[1]) return false;
519
520 if (parent && m_activeNodeID != 0) {
521 wxRect2DDouble nodeRect(0, 0, 0, 0);
522 if (m_activeNodeID == 1) {
523 nodeRect = wxRect2DDouble(m_pointList[0].m_x - 5.0 - m_borderSize, m_pointList[0].m_y - 5.0 - m_borderSize,
524 10 + 2.0 * m_borderSize, 10 + 2.0 * m_borderSize);
525 }
526 if (m_activeNodeID == 2) {
527 nodeRect = wxRect2DDouble(m_pointList[m_pointList.size() - 1].m_x - 5.0 - m_borderSize,
528 m_pointList[m_pointList.size() - 1].m_y - 5.0 - m_borderSize,
529 10 + 2.0 * m_borderSize, 10 + 2.0 * m_borderSize);
530 }
531
532 if (parent->Intersects(nodeRect)) {
533 // If the line has no parents set the new rated voltage, otherwise check if it's not connecting
534 // two different voltages buses
535 Bus* parentBus = static_cast<Bus*>(parent);
536 if (!m_parentList[0] && !m_parentList[1]) {
537 m_electricalData.nominalVoltage = parentBus->GetElectricalData().nominalVoltage;
538 m_electricalData.nominalVoltageUnit = parentBus->GetElectricalData().nominalVoltageUnit;
539 }
540 else if (m_electricalData.nominalVoltage != parentBus->GetElectricalData().nominalVoltage ||
541 m_electricalData.nominalVoltageUnit != parentBus->GetElectricalData().nominalVoltageUnit) {
542 wxMessageDialog msgDialog(nullptr,
543 _("Unable to connect two buses with different nominal voltages.\n"
544 "Use a transformer or edit the bus properties."),
545 _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
546 msgDialog.ShowModal();
547 m_activeNodeID = 0;
548 return false;
549 }
550
551 if (m_activeNodeID == 1) {
552 // Check if the user is trying to connect the same bus.
553 if (m_parentList[1] == parent) {
554 m_activeNodeID = 0;
555 return false;
556 }
557
558 m_parentList[0] = parent;
559
560 // Centralize the node on bus.
561 wxPoint2DDouble parentPt = parent->RotateAtPosition(
562 m_pointList[0], -parent->GetAngle()); // Rotate click to horizontal position.
563 parentPt.m_y = parent->GetPosition().m_y; // Centralize on bus.
564 parentPt = parent->RotateAtPosition(parentPt, parent->GetAngle());
565 m_pointList[0] = parentPt;
566
567 UpdateSwitchesPosition();
568 UpdatePowerFlowArrowsPosition();
569 return true;
570 }
571 if (m_activeNodeID == 2) {
572 if (m_parentList[0] == parent) {
573 m_activeNodeID = 0;
574 return false;
575 }
576
577 m_parentList[1] = parent;
578
579 wxPoint2DDouble parentPt =
580 parent->RotateAtPosition(m_pointList[m_pointList.size() - 1], -parent->GetAngle());
581 parentPt.m_y = parent->GetPosition().m_y;
582 parentPt = parent->RotateAtPosition(parentPt, parent->GetAngle());
583 m_pointList[m_pointList.size() - 1] = parentPt;
584
585 UpdateSwitchesPosition();
586 UpdatePowerFlowArrowsPosition();
587 return true;
588 }
589 }
590 else {
591 if (m_activeNodeID == 1) m_parentList[0] = nullptr;
592 if (m_activeNodeID == 2) m_parentList[1] = nullptr;
593 }
594 }
595 return false;
596}
597
599{
600 m_pfDirection = pfDirection;
601 UpdatePowerFlowArrowsPosition();
602}
603
604void Line::UpdatePowerFlowArrowsPosition()
605{
606 std::vector<wxPoint2DDouble> edges;
607 switch (m_pfDirection) {
609 m_powerFlowArrow.clear();
610 } break;
612 for (int i = 1; i < (int)m_pointList.size() - 1; i++) { edges.push_back(m_pointList[i]); }
613 } break;
615 for (int i = (int)m_pointList.size() - 2; i > 0; i--) { edges.push_back(m_pointList[i]); }
616 } break;
617 default:
618 break;
619 }
621}
622
623void Line::RotateNode(Element* parent, bool clockwise)
624{
625 double rotAngle = m_rotationAngle;
626 if (!clockwise) rotAngle = -m_rotationAngle;
627
628 if (parent == m_parentList[0]) {
629 m_pointList[0] = parent->RotateAtPosition(m_pointList[0], rotAngle);
630 }
631 else if (parent == m_parentList[1]) {
632 m_pointList[m_pointList.size() - 1] = parent->RotateAtPosition(m_pointList[m_pointList.size() - 1], rotAngle);
633 }
634 UpdateSwitchesPosition();
635 UpdatePowerFlowArrowsPosition();
636}
637
638void Line::SetPointList(std::vector<wxPoint2DDouble> pointList)
639{
640 m_pointList = pointList;
641 UpdateSwitchesPosition();
642 UpdatePowerFlowArrowsPosition();
643}
644
646{
647 Line* copy = new Line();
648 *copy = *this;
649 return copy;
650}
651
652wxString Line::GetTipText() const
653{
654 wxString tipText = m_electricalData.name;
655
656 if (m_online) {
657 tipText += "\n";
658 int busNumber[2];
659 busNumber[0] = static_cast<Bus*>(m_parentList[0])->GetElectricalData().number + 1;
660 busNumber[1] = static_cast<Bus*>(m_parentList[1])->GetElectricalData().number + 1;
661
662 tipText += _("\nP") + wxString::Format("(%d-%d) = ", busNumber[0], busNumber[1]) +
663 wxString::FromDouble(m_electricalData.powerFlow[0].real(), 5) + _(" p.u.");
664 tipText += _("\nQ") + wxString::Format("(%d-%d) = ", busNumber[0], busNumber[1]) +
665 wxString::FromDouble(m_electricalData.powerFlow[0].imag(), 5) + _(" p.u.");
666 tipText += _("\nP") + wxString::Format("(%d-%d) = ", busNumber[1], busNumber[0]) +
667 wxString::FromDouble(m_electricalData.powerFlow[1].real(), 5) + _(" p.u.");
668 tipText += _("\nQ") + wxString::Format("(%d-%d) = ", busNumber[1], busNumber[0]) +
669 wxString::FromDouble(m_electricalData.powerFlow[1].imag(), 5) + _(" p.u.");
670
671 if (!m_electricalData.harmonicOrder.empty()) {
672 tipText += _("\n\nHarmonic currents:");
673 int i = 0;
674 for (auto& hCurrent1 : m_electricalData.harmonicCurrent[0]) {
675 auto& hCurrent2 = m_electricalData.harmonicCurrent[1][i];
676 wxString i1, i2;
677 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'));
678 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'));
679
680 tipText += i1 + i2;
681 i++;
682 }
683 }
684 }
685
686 return tipText;
687}
688
689LineElectricalData Line::GetPUElectricalData(double systemBasePower) const
690{
692
693 data.name = m_electricalData.name;
694
695 data.nominalPower = m_electricalData.nominalPower;
696 data.nominalPowerUnit = m_electricalData.nominalPowerUnit;
697
698 data.nominalVoltage = m_electricalData.nominalVoltage;
699 data.nominalVoltageUnit = m_electricalData.nominalVoltageUnit;
700
701 data.useLinePower = m_electricalData.useLinePower;
702
703 data.lineSize = m_electricalData.lineSize;
704
705 data.resistance = m_electricalData.resistance;
706 data.resistanceUnit = m_electricalData.resistanceUnit;
707
708 data.indReactance = m_electricalData.indReactance;
709 data.indReactanceUnit = m_electricalData.indReactanceUnit;
710
711 data.capSusceptance = m_electricalData.capSusceptance;
712 data.capSusceptanceUnit = m_electricalData.capSusceptanceUnit;
713
714 data.zeroResistance = m_electricalData.zeroResistance;
715 data.zeroIndReactance = m_electricalData.zeroIndReactance;
716 data.zeroCapSusceptance = m_electricalData.zeroCapSusceptance;
717
718 data.powerFlow[0] = m_electricalData.powerFlow[0];
719 data.powerFlow[1] = m_electricalData.powerFlow[1];
720
721 data.harmonicOrder = m_electricalData.harmonicOrder;
722 data.harmonicCurrent[0] = m_electricalData.harmonicCurrent[0];
723 data.harmonicCurrent[1] = m_electricalData.harmonicCurrent[1];
724
725
726 double lineBasePower = GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
727 double baseVoltage = GetValueFromUnit(data.nominalVoltage, data.nominalVoltageUnit);
728 double systemBaseImpedance = (baseVoltage * baseVoltage) / systemBasePower;
729 double lineBaseImpedance = (baseVoltage * baseVoltage) / lineBasePower;
730
731 // Resistance
732 double r = data.resistance;
733 if (data.resistanceUnit == ElectricalUnit::UNIT_OHM_km) data.resistance = r * data.lineSize / systemBaseImpedance;
734 else if (data.resistanceUnit == ElectricalUnit::UNIT_PU) {
735 if (data.useLinePower) data.resistance = (r * lineBaseImpedance) / systemBaseImpedance;
736 }
737 else { // Ohm
738 data.resistance = r / systemBaseImpedance;
739 }
740 data.resistanceUnit = ElectricalUnit::UNIT_PU;
741
742 // Inductive reactance
743 double x = data.indReactance;
744 if (data.indReactanceUnit == ElectricalUnit::UNIT_OHM_km) data.indReactance = x * data.lineSize / systemBaseImpedance;
745 else if (data.indReactanceUnit == ElectricalUnit::UNIT_PU) {
746 if (data.useLinePower) data.indReactance = (x * lineBaseImpedance) / systemBaseImpedance;
747 }
748 else { // Ohm
749 data.indReactance = x / systemBaseImpedance;
750 }
751 data.indReactanceUnit = ElectricalUnit::UNIT_PU;
752
753 // Capacitive susceptance
754 double b = data.capSusceptance;
755 if (data.capSusceptanceUnit == ElectricalUnit::UNIT_S_km) data.capSusceptance = b * data.lineSize * systemBaseImpedance;
756 else if (data.capSusceptanceUnit == ElectricalUnit::UNIT_PU) {
757 if (data.useLinePower) data.capSusceptance = (b / lineBaseImpedance) * systemBaseImpedance;
758 }
759 else { // S
760 data.capSusceptance = b * systemBaseImpedance;
761 }
762 data.capSusceptanceUnit = ElectricalUnit::UNIT_PU;
763
764 // Fault
765
766 // Zero seq. resistance
767 double r0 = data.zeroResistance;
768 if (data.useLinePower) data.zeroResistance = (r0 * lineBaseImpedance) / systemBaseImpedance;
769
770 // Zero seq. ind. reactance
771 double x0 = data.zeroIndReactance;
772 if (data.useLinePower) data.zeroIndReactance = (x0 * lineBaseImpedance) / systemBaseImpedance;
773
774 // Zero seq. cap. susceptance
775 double b0 = data.zeroCapSusceptance;
776 if (data.useLinePower) data.zeroCapSusceptance = (b0 / lineBaseImpedance) * systemBaseImpedance;
777
778 if (!m_online) {
779 data.powerFlow[0] = std::complex<double>(0, 0);
780 data.powerFlow[1] = std::complex<double>(0, 0);
781 data.faultCurrent[0][0] = std::complex<double>(0, 0);
782 data.faultCurrent[0][1] = std::complex<double>(0, 0);
783 data.faultCurrent[0][2] = std::complex<double>(0, 0);
784 data.faultCurrent[1][0] = std::complex<double>(0, 0);
785 data.faultCurrent[1][1] = std::complex<double>(0, 0);
786 data.faultCurrent[1][2] = std::complex<double>(0, 0);
787 }
788
789 return data;
790}
791
792rapidxml::xml_node<>* Line::SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode)
793{
794 auto elementNode = XMLParser::AppendNode(doc, elementListNode, "Line");
795 XMLParser::SetNodeAttribute(doc, elementNode, "ID", m_elementID);
796 auto cadProp = XMLParser::AppendNode(doc, elementNode, "CADProperties");
797 auto nodeList = XMLParser::AppendNode(doc, cadProp, "NodeList");
798 int nodeID = 0;
799 // Parse all the points.
800 for (unsigned int i = 0; i < m_pointList.size(); i++) {
801 // Don't save switch points, the method UpdateSwitchesPosition() calculate these points properly after open the
802 // element
803 if ((i != 1) && (i != m_pointList.size() - 2)) {
804 auto nodePos = XMLParser::AppendNode(doc, nodeList, "Node");
805 XMLParser::SetNodeAttribute(doc, nodePos, "ID", nodeID);
806 auto nodePosX = XMLParser::AppendNode(doc, nodePos, "X");
807 XMLParser::SetNodeValue(doc, nodePosX, m_pointList[i].m_x);
808 auto nodePosY = XMLParser::AppendNode(doc, nodePos, "Y");
809 XMLParser::SetNodeValue(doc, nodePosY, m_pointList[i].m_y);
810 nodeID++;
811 }
812 }
813
814 auto parentIDList = XMLParser::AppendNode(doc, cadProp, "ParentIDList");
815 for (unsigned int i = 0; i < m_parentList.size(); i++) {
816 if (m_parentList[i]) {
817 auto parentID = XMLParser::AppendNode(doc, parentIDList, "ParentID");
818 XMLParser::SetNodeAttribute(doc, parentID, "ID", static_cast<int>(i));
819 XMLParser::SetNodeValue(doc, parentID, m_parentList[i]->GetID());
820 }
821 }
822
823 auto electricalProp = XMLParser::AppendNode(doc, elementNode, "ElectricalProperties");
824 auto isOnline = XMLParser::AppendNode(doc, electricalProp, "IsOnline");
825 XMLParser::SetNodeValue(doc, isOnline, m_online);
826 auto name = XMLParser::AppendNode(doc, electricalProp, "Name");
827 XMLParser::SetNodeValue(doc, name, m_electricalData.name);
828 auto nominalVoltage = XMLParser::AppendNode(doc, electricalProp, "NominalVoltage");
829 XMLParser::SetNodeValue(doc, nominalVoltage, m_electricalData.nominalVoltage);
830 XMLParser::SetNodeAttribute(doc, nominalVoltage, "UnitID", static_cast<int>(m_electricalData.nominalVoltageUnit));
831 auto nominalPower = XMLParser::AppendNode(doc, electricalProp, "NominalPower");
832 XMLParser::SetNodeValue(doc, nominalPower, m_electricalData.nominalPower);
833 XMLParser::SetNodeAttribute(doc, nominalPower, "UnitID", static_cast<int>(m_electricalData.nominalPowerUnit));
834 auto resistance = XMLParser::AppendNode(doc, electricalProp, "Resistance");
835 XMLParser::SetNodeValue(doc, resistance, m_electricalData.resistance);
836 XMLParser::SetNodeAttribute(doc, resistance, "UnitID", static_cast<int>(m_electricalData.resistanceUnit));
837 auto indReactance = XMLParser::AppendNode(doc, electricalProp, "IndReactance");
838 XMLParser::SetNodeValue(doc, indReactance, m_electricalData.indReactance);
839 XMLParser::SetNodeAttribute(doc, indReactance, "UnitID", static_cast<int>(m_electricalData.indReactanceUnit));
840 auto capSusceptance = XMLParser::AppendNode(doc, electricalProp, "CapSusceptance");
841 XMLParser::SetNodeValue(doc, capSusceptance, m_electricalData.capSusceptance);
842 XMLParser::SetNodeAttribute(doc, capSusceptance, "UnitID", static_cast<int>(m_electricalData.capSusceptanceUnit));
843 auto lineSize = XMLParser::AppendNode(doc, electricalProp, "LineSize");
844 XMLParser::SetNodeValue(doc, lineSize, m_electricalData.lineSize);
845 auto useLinePower = XMLParser::AppendNode(doc, electricalProp, "UseLinePower");
846 XMLParser::SetNodeValue(doc, useLinePower, m_electricalData.useLinePower);
847
848 auto fault = XMLParser::AppendNode(doc, electricalProp, "Fault");
849 auto zeroResistance = XMLParser::AppendNode(doc, fault, "ZeroResistance");
850 XMLParser::SetNodeValue(doc, zeroResistance, m_electricalData.zeroResistance);
851 auto zeroIndReactance = XMLParser::AppendNode(doc, fault, "ZeroIndReactance");
852 XMLParser::SetNodeValue(doc, zeroIndReactance, m_electricalData.zeroIndReactance);
853 auto zeroCapSusceptance = XMLParser::AppendNode(doc, fault, "ZeroCapSusceptance");
854 XMLParser::SetNodeValue(doc, zeroCapSusceptance, m_electricalData.zeroCapSusceptance);
855
856 SaveSwitchingData(doc, electricalProp);
857
858 return elementNode;
859}
860
861bool Line::OpenElement(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList)
862{
863 auto cadPropNode = elementNode->first_node("CADProperties");
864 if (!cadPropNode) return false;
865
866 // Get nodes points
867 std::vector<wxPoint2DDouble> ptsList;
868 auto nodePosList = cadPropNode->first_node("NodeList");
869 if (!nodePosList) return false;
870 auto nodePos = nodePosList->first_node("Node");
871 while (nodePos) {
872 double nodePosX = XMLParser::GetNodeValueDouble(nodePos, "X");
873 double nodePosY = XMLParser::GetNodeValueDouble(nodePos, "Y");
874 ptsList.push_back(wxPoint2DDouble(nodePosX, nodePosY));
875 nodePos = nodePos->next_sibling("Node");
876 }
877
878 // Get parents IDs
879 auto parentIDList = cadPropNode->first_node("ParentIDList");
880 if (!parentIDList) return false;
881 auto parentNode = parentIDList->first_node("ParentID");
882 long parentID[2] = { -1, -1 };
883 while (parentNode) {
884 long index = 0;
885 wxString(parentNode->first_attribute("ID")->value()).ToCLong(&index);
886 wxString(parentNode->value()).ToCLong(&parentID[index]);
887 parentNode = parentNode->next_sibling("ParentID");
888 }
889
890 std::vector<wxPoint2DDouble> nodePtsList; // List of node points
891 nodePtsList.push_back(ptsList[0]); // First point on the list
892 nodePtsList.push_back(ptsList[ptsList.size() - 1]); // Last point on the list
893
894 // List of dummy buses to set not connected nodes properly
895 std::vector<Bus*> dummyBusList;
896 for (unsigned int i = 0; i < nodePtsList.size(); ++i) {
897 if (parentID[i] == -1) // No parent connected
898 {
899 Bus* dummyBus = new Bus(nodePtsList[i]);
900 dummyBusList.push_back(dummyBus);
901 AddParent(dummyBus, nodePtsList[i]);
902 }
903 else { // Parent connected (necessarily a bus, get from bus list)
904 AddParent(parentList[parentID[i]], nodePtsList[i]);
905 }
906 }
907
908 // Add the others nodes (if exists)
909 std::vector<wxPoint2DDouble> midPts;
910 for (unsigned int i = 1; i < ptsList.size() - 1; i++) midPts.push_back(ptsList[i]);
911 m_pointList.insert(m_pointList.begin() + 2, midPts.begin(), midPts.end());
912 SetPointList(m_pointList);
913
914 // Remove dummy buses
915 for (auto it = dummyBusList.begin(), itEnd = dummyBusList.end(); it != itEnd; ++it) {
916 RemoveParent(*it);
917 delete* it;
918 }
919 dummyBusList.clear();
920
921 auto electricalProp = elementNode->first_node("ElectricalProperties");
922 if (!electricalProp) return false;
923
924 SetOnline(XMLParser::GetNodeValueInt(electricalProp, "IsOnline"));
925 m_electricalData.name = electricalProp->first_node("Name")->value();
926 m_electricalData.nominalVoltage = XMLParser::GetNodeValueDouble(electricalProp, "NominalVoltage");
927 m_electricalData.nominalVoltageUnit =
928 static_cast<ElectricalUnit>(XMLParser::GetAttributeValueInt(electricalProp, "NominalVoltage", "UnitID"));
929 m_electricalData.nominalPower = XMLParser::GetNodeValueDouble(electricalProp, "NominalPower");
930 m_electricalData.nominalPowerUnit =
931 static_cast<ElectricalUnit>(XMLParser::GetAttributeValueInt(electricalProp, "NominalPower", "UnitID"));
932 m_electricalData.resistance = XMLParser::GetNodeValueDouble(electricalProp, "Resistance");
933 m_electricalData.resistanceUnit =
934 static_cast<ElectricalUnit>(XMLParser::GetAttributeValueInt(electricalProp, "Resistance", "UnitID"));
935 m_electricalData.indReactance = XMLParser::GetNodeValueDouble(electricalProp, "IndReactance");
936 m_electricalData.indReactanceUnit =
937 static_cast<ElectricalUnit>(XMLParser::GetAttributeValueInt(electricalProp, "IndReactance", "UnitID"));
938 m_electricalData.capSusceptance = XMLParser::GetNodeValueDouble(electricalProp, "CapSusceptance");
939 m_electricalData.capSusceptanceUnit =
940 static_cast<ElectricalUnit>(XMLParser::GetAttributeValueInt(electricalProp, "CapSusceptance", "UnitID"));
941 m_electricalData.lineSize = XMLParser::GetNodeValueDouble(electricalProp, "LineSize");
942 m_electricalData.useLinePower = XMLParser::GetNodeValueInt(electricalProp, "UseLinePower");
943
944 auto fault = electricalProp->first_node("Fault");
945 m_electricalData.zeroResistance = XMLParser::GetNodeValueDouble(fault, "ZeroResistance");
946 m_electricalData.zeroIndReactance = XMLParser::GetNodeValueDouble(fault, "ZeroIndReactance");
947 m_electricalData.zeroCapSusceptance = XMLParser::GetNodeValueDouble(fault, "ZeroCapSusceptance");
948
949 if (!OpenSwitchingData(electricalProp)) return false;
950 if (m_swData.swTime.size() != 0) SetDynamicEvent(true);
951 return true;
952}
@ ID_LINE_REMOVE_NODE
Definition Element.h:79
@ ID_DELETE
Definition Element.h:82
@ ID_EDIT_ELEMENT
Definition Element.h:77
@ ID_LINE_ADD_NODE
Definition Element.h:78
@ ID_PB_NONE
Definition Element.h:63
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:86
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:114
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:273
virtual double PointToLineDistance(wxPoint2DDouble point, int *segmentNumber=nullptr) const
Calculate the distance between a line (formed by point list) and a point.
Definition Element.cpp:534
wxPoint2DDouble GetPosition() const
Get the element position.
Definition Element.h:188
double GetAngle() const
Get the element angle.
Definition Element.h:213
virtual wxPoint2DDouble RotateAtPosition(wxPoint2DDouble pointToRotate, double angle, bool degrees=true) const
Rotate a point as element position being the origin.
Definition Element.cpp:228
virtual void DrawDCPickbox(wxPoint2DDouble position, wxGraphicsContext *gc) const
Draw a point.
Definition Element.cpp:221
virtual wxPoint2DDouble WorldToScreen(wxPoint2DDouble translation, double scale, double offsetX=0.0, double offsetY=0.0) const
Convert the element position to screen position.
Definition Element.cpp:272
virtual void AddChild(Element *child)
Add a child to the child list.
Definition Element.cpp:499
bool SetOnline(bool online=true)
Set if the element is online or offline.
Definition Element.cpp:383
virtual void DrawDCCircle(wxPoint2DDouble position, double radius, int numSegments, wxGraphicsContext *gc) const
Draw a circle using device context.
Definition Element.cpp:173
void SetInserted(bool inserted=true)
Set if the element is properly inserted in the workspace.
Definition Element.h:634
Form to edit the line power data.
Definition LineForm.h:33
Power line element.
Definition Line.h:64
virtual void AddPoint(wxPoint2DDouble point)
Add point to the list of points that connect the element to the bus.
Definition Line.cpp:352
virtual bool Contains(wxPoint2DDouble position) const
Checks if the element contains a position.
Definition Line.cpp:38
virtual bool Intersects(wxRect2DDouble rect) const
Check if the element's rect intersects other rect.
Definition Line.cpp:321
virtual void SetNominalVoltage(std::vector< double > nominalVoltage, std::vector< ElectricalUnit > nominalVoltageUnit)
Set nominal voltage of the element.
Definition Line.cpp:507
virtual bool AddParent(Element *parent, wxPoint2DDouble position)
Add a parent to the element. This method must be used on power elements that connect to a bus,...
Definition Line.cpp:255
virtual void Move(wxPoint2DDouble position)
Move the element other position.
Definition Line.cpp:235
virtual void SetPowerFlowDirection(PowerFlowDirection pfDirection)
Set the direction of the power flow.
Definition Line.cpp:598
virtual wxString GetTipText() const
Get the tip text.
Definition Line.cpp:652
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...
Definition Line.cpp:515
virtual void DrawDC(GUIColour *guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext *gc) const
Draw the element using GDI+.
Definition Line.cpp:108
virtual bool ShowForm(wxWindow *parent, Element *element, wxWindow *workspace=nullptr)
Show element data form.
Definition Line.cpp:497
virtual bool GetContextMenu(wxMenu &menu)
Get the element contex menu.
Definition Line.cpp:407
virtual void CalculateBoundaries(wxPoint2DDouble &leftUp, wxPoint2DDouble &rightBottom) const
Calculate the element boundaries.
Definition Line.cpp:482
virtual void RotateNode(Element *parent, bool clockwise=true)
Rotate a node.
Definition Line.cpp:623
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
Definition Line.cpp:357
virtual void MoveNode(Element *parent, wxPoint2DDouble position)
Move a node. StartMove(wxPoint2DDouble position) before start moving.
Definition Line.cpp:363
virtual bool PickboxContains(wxPoint2DDouble position)
Check if a pickbox contains a point. If contains the attributes related to pickbox movement will be c...
Definition Line.cpp:340
virtual void SetPointList(std::vector< wxPoint2DDouble > pointList)
Set the list of points that connect the element to the bus.
Definition Line.cpp:638
virtual void MovePickbox(wxPoint2DDouble position)
Move the pickbox.
Definition Line.cpp:328
virtual Element * GetCopy()
Get a the element copy.
Definition Line.cpp:645
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.
std::vector< double > swTime