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