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