Power System Platform  2026w10a-beta
Loading...
Searching...
No Matches
Workspace.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 //#define SHOW_DEBUG_PANEL;
19 //#define SHOW_SIMULATION_PARAMETERS;
20
21#include "Workspace.h"
22
23#include "ChartView.h"
24
25#include "../elements/Element.h"
26#include "../elements/ElementDataObject.h"
27#include "../elements/Text.h"
28#include "../elements/powerElement/Capacitor.h"
29#include "../elements/powerElement/HarmCurrent.h"
30#include "../elements/powerElement/IndMotor.h"
31#include "../elements/powerElement/Inductor.h"
32#include "../elements/powerElement/Line.h"
33#include "../elements/powerElement/Load.h"
34#include "../elements/powerElement/SyncGenerator.h"
35#include "../elements/powerElement/SyncMotor.h"
36#include "../elements/powerElement/Transformer.h"
37#include "../elements/powerElement/EMTElement.h"
38
39#include "../simulation/Electromechanical.h"
40#include "../simulation/Fault.h"
41#include "../simulation/PowerFlow.h"
42#include "../simulation/PowerQuality.h"
43
44#include "../utils/ElementPlotData.h"
45
46#include "../forms/FrequencyResponseForm.h"
47
48#include "../utils/Camera.h"
49#include "../utils/PropertiesData.h"
50#include "../utils/HMPlane.h"
51#include "../utils/FileHanding.h"
52
53#include <wx/busyinfo.h>
54#include <wx/dcsvg.h>
55
56// Workspace
57Workspace::Workspace() : WorkspaceBase(nullptr)
58{
59#ifdef _DEBUG
60#ifdef SHOW_DEBUG_PANEL
61 m_debugFrame = new DebugMainFrame(this);
62 m_debugFrame->SetTitle(_("Debug window: ") + m_name);
63 m_debugFrame->Show();
64 //m_debugFrame->SetSize(m_debugFrame->GetBestVirtualSize());
65#endif
66#endif
67 SetBackgroundColour(wxColour(255, 255, 255));
68 SetBackgroundStyle(wxBG_STYLE_PAINT); // To allow wxBufferedPaintDC works properly.
69}
70
71Workspace::Workspace(wxWindow* parent, wxString name, wxStatusBar* statusBar, wxAuiNotebook* auiNotebook)
72 : WorkspaceBase(parent)
73{
74#ifdef _DEBUG
75#ifdef SHOW_DEBUG_PANEL
76 m_debugFrame = new DebugMainFrame(this);
77 m_debugFrame->SetTitle(_("Debug window: ") + m_name);
78 m_debugFrame->Show();
79 //m_debugFrame->SetSize(m_debugFrame->GetBestVirtualSize());
80#endif
81#endif
82
83 m_timer->Start();
84 m_name = name;
85 m_statusBar = statusBar;
86 m_auiNotebook = auiNotebook;
87 //m_glContext = new wxGLContext(m_glCanvas, sharedGLContext);
88 m_camera = new Camera();
89 m_selectionRect = wxRect2DDouble(0, 0, 0, 0);
90
91 for (int i = 0; i < NUM_ELEMENTS; ++i) { m_elementNumber[i] = 1; }
92
93 const int widths[4] = { -3, -1, 100, 100 };
94 m_statusBar->SetStatusWidths(4, widths);
95
96 m_properties = new PropertiesData();
97
98 //m_glCanvas->SetBackgroundStyle(wxBG_STYLE_CUSTOM);
99 m_workspacePanel->SetBackgroundColour(wxColour(255, 255, 255));
100 m_workspacePanel->SetBackgroundStyle(wxBG_STYLE_PAINT); // To allow wxBufferedPaintDC works properly.
101
102 //m_width = static_cast<float>(m_glCanvas->GetSize().x) - 1.0;
103 //m_height = static_cast<float>(m_glCanvas->GetSize().y) - 1.0;
104 m_width = static_cast<float>(m_workspacePanel->GetSize().x) - 1.0;
105 m_height = static_cast<float>(m_workspacePanel->GetSize().y) - 1.0;
106 //m_renderer = new Renderer();
107
108 SaveCurrentState();
109}
110
111Workspace::~Workspace()
112{
113 //for (auto it = m_elementList.begin(), itEnd = m_elementList.end(); it != itEnd; ++it) {
114 // if (*it) delete* it;
115 //}
116 //for (auto it = m_textList.begin(), itEnd = m_textList.end(); it != itEnd; ++it) {
117 // if (*it) delete* it;
118 //}
119
120 if (m_hmPlane) delete m_hmPlane;
121
122 if (m_camera) delete m_camera;
123 //if (m_isThisContextShared) {
124 //delete m_glContext;
125 //m_glContext = nullptr;
126 //}
127 if (m_tipWindow) delete m_tipWindow;
128 if (m_properties) delete m_properties;
129}
130
131void Workspace::OnPaint(wxPaintEvent& event)
132{
133 wxBufferedPaintDC dc(m_workspacePanel);
134 dc.Clear();
135 wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
136
137 // Draw
138 DrawScene(gc);
139 delete gc;
140
141 event.Skip();
142}
143
144void Workspace::DrawScene(wxGraphicsContext* gc)
145{
146 // Draw
147 if (gc) {
148
149 // HMPlane
150 if (m_hmPlane && m_showHM) {
151 m_hmPlane->DrawDC(gc);
152 }
153
154 gc->Scale(m_camera->GetScale(), m_camera->GetScale());
155 gc->Translate(m_camera->GetTranslation().m_x, m_camera->GetTranslation().m_y);
156
157 // Elements
158 for (auto& element : m_elementList) {
159 element->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), gc);
160 }
161
162 // Dummy Text to set correct context
163 // TODO: Find a better way to do this.
164 Text* text = new Text(wxPoint2DDouble(0.0, 0.0), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
165 text->SetText("");
166 text->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), gc);
167 delete text;
168
169 // Texts
170 for (auto& text : m_textList) {
171 text->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), gc);
172 }
173
174 // Selection rectangle
175 gc->SetPen(wxPen(wxColour(0, 125, 255, 255)));
176 gc->SetBrush(wxBrush(wxColour(0, 125, 255, 125)));
177 gc->DrawRectangle(m_selectionRect.m_x, m_selectionRect.m_y, m_selectionRect.m_width, m_selectionRect.m_height);
178
179 if (m_hmPlane && m_showHM) {
180 m_hmPlane->DrawLabelDC(gc);
181 }
182 }
183}
184
185void Workspace::DrawScene(wxDC& dc)
186{
187 // HMPlane
188 if (m_hmPlane && m_showHM) {
189 m_hmPlane->DrawDC(dc);
190 }
191
192 dc.SetUserScale(m_camera->GetScale(), m_camera->GetScale());
193 dc.SetDeviceOrigin(m_camera->GetTranslation().m_x * m_camera->GetScale(), m_camera->GetTranslation().m_y * m_camera->GetScale());
194 //dc.SetLogicalOrigin(-m_camera->GetTranslation().m_x, -m_camera->GetTranslation().m_y);
195
196 // Elements
197 for (auto& element : m_elementList) {
198 element->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), dc);
199 }
200
201 // Dummy Text to set correct context
202 // TODO: Find a better way to do this.
203 Text* text = new Text(wxPoint2DDouble(0.0, 0.0), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
204 text->SetText("");
205 text->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), dc);
206 delete text;
207
208 // Texts
209 for (auto& text : m_textList) {
210 text->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), dc);
211 }
212
213 if (m_hmPlane && m_showHM) {
214 m_hmPlane->DrawLabelDC(dc);
215 }
216}
217
218void Workspace::CopyToClipboard()
219{
220 wxSize size = GetClientSize();
221
222 int scale = 2;
223
224 wxBitmap bitmap(size.x * scale, size.y * scale);
225 wxMemoryDC memDC(bitmap);
226
227 memDC.SetBackground(*wxWHITE_BRUSH);
228 memDC.Clear();
229
230 wxGraphicsContext* gc = wxGraphicsContext::Create(memDC);
231 if (!gc) return;
232 gc->Scale(scale, scale); // Increase the scale to improve the quality of the copied image.
233
234 DrawScene(gc);
235 delete gc;
236
237 memDC.SelectObject(wxNullBitmap);
238
239 if (wxTheClipboard->Open())
240 {
241 wxTheClipboard->SetData(new wxBitmapDataObject(bitmap));
242 wxTheClipboard->Close();
243 }
244}
245
246void Workspace::ExportAsSVG(wxString path)
247{
248 wxSize size = GetClientSize();
249
250 wxSVGFileDC svgDC(path, size.x, size.y);
251
252 svgDC.SetClippingRegion(wxRect(0, 0, size.x, size.y));
253
254 DrawScene(svgDC);
255}
256
257void Workspace::OnLeftClickDown(wxMouseEvent& event)
258{
259 wxPoint clickPoint = event.GetPosition();
260 bool foundElement = false;
261 Element* newElement = nullptr;
262 bool showNewElementForm = false;
263 bool clickOnSwitch = false;
264 bool unselectAll = true;
265 std::vector<Element*> notUnselectElementList;
266 std::vector<Text*> notUnselectTextList;
267
268 if (m_mode == WorkspaceMode::MODE_INSERT_TEXT || m_mode == WorkspaceMode::MODE_PASTE || m_mode == WorkspaceMode::MODE_DRAG_PASTE) {
269 m_mode = WorkspaceMode::MODE_EDIT;
270 SaveCurrentState();
271 }
272 else if (m_mode == WorkspaceMode::MODE_INSERT || m_mode == WorkspaceMode::MODE_DRAG_INSERT || m_mode == WorkspaceMode::MODE_DRAG_INSERT_TEXT) {
273 wxPoint2DDouble clickPointWorld = m_camera->ScreenToWorld(clickPoint);
274
275 if (!m_elementList.empty()) {
276 // Get the last element inserted on the list.
277 newElement = m_elementList.back().get();
278 for (auto& element : m_elementList) {
279 // Clicked in any element.
280 if (element->Contains(clickPointWorld)) {
281 // Click at a bus.
282 if (auto bus = dynamic_cast<Bus*>(element.get())) {
283 // Select the bus.
284 bus->SetSelected();
285 foundElement = true; // Element found.
286 // Add the new element's parent. If the element being inserted returns true, back to
287 // edit mode.
288 if (newElement->AddParent(bus, clickPointWorld)) {
289 ValidateElementsVoltages();
290 m_timer->Stop();
291 showNewElementForm = true;
292 m_mode = WorkspaceMode::MODE_EDIT;
293 bus->SetInserted();
294 }
295 }
296 }
297 }
298
299 // The line element can have an undefined number of points.
300 if (!foundElement) {
301 if (auto line = dynamic_cast<Line*>(newElement)) { line->AddPoint(clickPointWorld); }
302 }
303 foundElement = true;
304 unselectAll = false;
305 }
306 }
307 else {
308
309 bool clickPickbox = false;
310
311 for (auto& element : m_elementList) {
312 element->ResetPickboxes(); // Reset pickbox state.
313
314 // Set movement initial position (not necessarily will be moved).
315 element->StartMove(m_camera->ScreenToWorld(clickPoint));
316
317 // Click in selected element node.
318 if (element->NodeContains(m_camera->ScreenToWorld(clickPoint)) != 0 && element->IsSelected()) {
319 m_mode = WorkspaceMode::MODE_MOVE_NODE;
320 m_disconnectedElement = true;
321 foundElement = true;
322 unselectAll = false;
323 if (m_hmPlane && m_showHM) {
324 m_hmPlane->Clear();
325 }
326 }
327
328 // Click in an element.
329 else if (element->Contains(m_camera->ScreenToWorld(clickPoint))) {
330 notUnselectElementList.emplace_back(element.get());
331 if (!foundElement) {
332 if (element->IsSelected()) unselectAll = false;
333 // Select and show pickbox.
334 element->SetSelected();
335 element->ShowPickbox();
336 foundElement = true;
337 // Select the associated text
338 for (auto& text : m_textList) {
339 if (text->GetElement() == element.get()) {
340 notUnselectTextList.emplace_back(text.get());
341 text->SetSelected();
342 text->SetAllowRotation(false);
343 if (unselectAll) text->SetAltSelectionColour();
344 }
345 }
346 }
347 // If pickbox contains the click, move the pickbox
348 if (element->PickboxContains(m_camera->ScreenToWorld(clickPoint))) {
349 m_mode = WorkspaceMode::MODE_MOVE_PICKBOX;
350 clickPickbox = true;
351 }
352 // If didn't found a pickbox, move the element
353 if (!clickPickbox) { m_mode = WorkspaceMode::MODE_MOVE_ELEMENT; }
354
355 if (m_hmPlane && m_showHM) {
356 m_hmPlane->Clear();
357 }
358 }
359
360 // Click in a switch.
361 else if (element->SwitchesContains(m_camera->ScreenToWorld(clickPoint))) {
362 element->SetOnline(element->IsOnline() ? false : true);
363 clickOnSwitch = true;
364 }
365 }
366
367 // Text element
368 for (auto& text : m_textList) {
369 text->StartMove(m_camera->ScreenToWorld(clickPoint));
370
371 if (text->Contains(m_camera->ScreenToWorld(clickPoint))) {
372 notUnselectTextList.emplace_back(text.get());
373 if (!foundElement) {
374 if (text->IsSelected()) unselectAll = false;
375 text->SetSelected();
376 text->SetAltSelectionColour(false);
377 text->SetAllowRotation();
378 m_mode = WorkspaceMode::MODE_MOVE_ELEMENT;
379 foundElement = true;
380 if (m_hmPlane && m_showHM) {
381 m_hmPlane->Clear();
382 }
383 }
384 }
385 }
386 }
387
388 // Unselect all elements
389 if (!event.ControlDown() && unselectAll) {
390 for (auto& element : m_elementList) {
391 bool select = false;
392 for (Element* notUnselectElement : notUnselectElementList) {
393 if (notUnselectElement == element.get()) select = true;
394 }
395 element->SetSelected(select);
396 }
397 for (auto& text : m_textList) {
398 bool select = false;
399 for (auto& notUnselectText : notUnselectTextList) {
400 if (notUnselectText == text.get()) select = true;
401 }
402 text->SetSelected(select);
403 }
404 }
405
406 if (!foundElement && !clickOnSwitch) {
407 m_mode = WorkspaceMode::MODE_SELECTION_RECT;
408 m_startSelRect = m_camera->ScreenToWorld(clickPoint);
409 if (m_hmPlane && m_showHM) {
410 m_hmPlane->Clear();
411 }
412 }
413
414 Redraw();
415 UpdateStatusBar();
416
417 if (showNewElementForm) {
418 if (newElement) {
419 newElement->ShowForm(this, newElement);
420 CheckSlackBusDuplication(newElement);
421 SaveCurrentState();
422 if (m_continuousCalc) RunStaticStudies();
423 }
424 }
425 if (clickOnSwitch && m_continuousCalc) RunStaticStudies();
426
427 event.Skip();
428}
429
430void Workspace::OnLeftDoubleClick(wxMouseEvent& event)
431{
432 bool elementEdited = false;
433 bool clickOnSwitch = false;
434 bool redraw = false;
435
436 for (auto& element : m_elementList) {
437 // Click in an element.
438 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
439 bool elementIsBus = false;
440 Bus oldBus;
441 Bus* currentBus = nullptr;
442 if ((currentBus = dynamic_cast<Bus*>(element.get()))) {
443 elementIsBus = true;
444 oldBus = *currentBus;
445 }
446 m_timer->Stop();
447 if (element->ShowForm(this, element.get())) {
448 CheckSlackBusDuplication(element.get());
449 SaveCurrentState();
450 }
451 elementEdited = true;
452 redraw = true;
453
454 // If the edited element is a bus and was changed the rated voltage, this voltage must be
455 // propagated through the lines
456 if (elementIsBus) {
457 // The voltage was changed
458 if (oldBus.GetElectricalData().nominalVoltage != currentBus->GetElectricalData().nominalVoltage ||
459 oldBus.GetElectricalData().nominalVoltageUnit !=
460 currentBus->GetElectricalData().nominalVoltageUnit) {
461 // Check if the bus has line as child.
462 std::vector<Element*> childList = element->GetChildList();
463 for (auto itc = childList.begin(), itcEnd = childList.end(); itc != itcEnd; ++itc) {
464 Element* child = *itc;
465 if (typeid(*child) == typeid(Line)) {
466 wxMessageDialog msgDialog(this, _("Do you want to change the rated voltage of the path?"),
467 _("Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING);
468 if (msgDialog.ShowModal() == wxID_YES)
469 ValidateBusesVoltages(element.get());
470 else {
471 auto data = currentBus->GetElectricalData();
472 data.nominalVoltage = oldBus.GetElectricalData().nominalVoltage;
473 data.nominalVoltageUnit = oldBus.GetElectricalData().nominalVoltageUnit;
474 currentBus->SetElectricalData(data);
475 }
476 break;
477 }
478 }
479 }
480 ValidateElementsVoltages();
481 }
482 }
483
484 // Click in a switch.
485 else if (element->SwitchesContains(m_camera->ScreenToWorld(event.GetPosition()))) {
486 element->SetOnline(element->IsOnline() ? false : true);
487 clickOnSwitch = true;
488 }
489 }
490
491 // Text element
492 for (auto& text : m_textList) {
493 if (text->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
494 if (text->ShowForm(this, GetElementList())) SaveCurrentState();
495 redraw = true;
496 }
497 }
498 if (elementEdited) {
499 UpdateTextElements();
500 if (m_continuousCalc) RunStaticStudies();
501 }
502 if (clickOnSwitch && m_continuousCalc) RunStaticStudies();
503
504 if (redraw) Redraw();
505 m_timer->Start();
506}
507
508void Workspace::OnRightClickDown(wxMouseEvent& event)
509{
510 bool redraw = false;
511 if (m_mode == WorkspaceMode::MODE_EDIT) {
512 for (auto& element : m_elementList) {
513 if (element->IsSelected()) {
514 // Show context menu.
515 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
516 element->ShowPickbox(false);
517 wxMenu menu;
518 menu.SetClientData(element.get());
519 if (element->GetContextMenu(menu)) {
520 m_timer->Stop();
521 menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &Workspace::OnPopupClick, this);
522 PopupMenu(&menu);
523 redraw = true;
524
525 if (!menu.GetClientData()) break;
526 }
527 element->ResetPickboxes();
528 }
529 }
530 }
531 }
532 if (redraw) Redraw();
533 m_timer->Start();
534}
535
536void Workspace::OnLeftClickUp(wxMouseEvent& event)
537{
538 // This event (under certain conditions) deselects the elements and back to edit mode or select the elements using
539 // the selection rectangle.
540 bool foundPickbox = false;
541 bool findNewParent = false;
542 bool updateVoltages = false;
543 bool saveCurrentState = false;
544 auto itnp = m_elementList.begin();
545
546 for (auto it = m_elementList.begin(); it != m_elementList.end(); ++it) {
547 auto& element = *it;
548 // The user was moving a pickbox.
549 if (m_mode == WorkspaceMode::MODE_MOVE_PICKBOX) {
550 // Catch only the element that have the pickbox shown.
551 if (element->IsPickboxShown()) {
552 saveCurrentState = true;
553 // If the element is a bus, check if a node is outside.
554 if (auto bus = dynamic_cast<Bus*>(element.get())) {
555 // Get all the bus children.
556 for (auto child : m_elementList) {
557 for (auto parent : child->GetParentList()) {
558 // The child have a parent that is the element (bus).
559 if (parent == bus) {
560 child->UpdateNodes();
561 m_disconnectedElement = true;
562 }
563 }
564 }
565 //for (int i = 0; i < (int)m_elementList.size(); i++) {
566 // Element* child = m_elementList[i];
567 // for (int j = 0; j < (int)child->GetParentList().size(); j++) {
568 // Element* parent = child->GetParentList()[j];
569 // // The child have a parent that is the element.
570 // if (parent == element) {
571 // child->UpdateNodes();
572 // m_disconnectedElement = true;
573 // }
574 // }
575 //}
576 }
577 }
578 }
579
580 if (m_mode == WorkspaceMode::MODE_SELECTION_RECT) {
581 if (element->Intersects(m_selectionRect)) {
582 element->SetSelected();
583 // Select the associated text
584 for (auto& text : m_textList) {
585 if (text->GetElement() == element.get()) {
586 text->SetSelected();
587 text->SetAltSelectionColour(false);
588 text->SetAllowRotation();
589 }
590 }
591 }
592 //else if (!event.ControlDown()) {
593 // element->SetSelected(false);
594 //}
595 }
596 else if (m_mode == WorkspaceMode::MODE_MOVE_NODE) {
597 if (element->IsSelected()) {
598 saveCurrentState = true;
599 for (auto parent : m_elementList) {
600 if (auto bus = dynamic_cast<Bus*>(parent.get())) {
601 if (element->SetNodeParent(bus)) {
602 parent->AddChild(element.get());
603 findNewParent = true;
604 itnp = it;
605 element->ResetNodes();
606 break;
607 }
608 }
609 }
610 // element->ResetNodes();
611 }
612 }
613 else {
614 // Deselect
615 //if (!event.ControlDown()) {
616 // if (!element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) { element->SetSelected(false); }
617 //}
618
619 if (element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) {
620 foundPickbox = true;
621 }
622 else {
623 element->ShowPickbox(false);
624 element->ResetPickboxes();
625 }
626 }
627 }
628
629 // Text element
630 for (auto& text : m_textList) {
631 if (m_mode == WorkspaceMode::MODE_SELECTION_RECT) {
632 if (text->Intersects(m_selectionRect)) {
633 text->SetSelected();
634 text->SetAltSelectionColour(false);
635 text->SetAllowRotation();
636 }
637 //else if (!event.ControlDown()) {
638 // text->SetSelected(false);
639 //}
640 }
641 //else if (!event.ControlDown()) {
642 // if (!text->Contains(m_camera->ScreenToWorld(event.GetPosition()))) { text->SetSelected(false); }
643 //}
644 }
645 if (m_mode == WorkspaceMode::MODE_MOVE_ELEMENT) saveCurrentState = true;
646
647 if (findNewParent) {
648 std::rotate(itnp, itnp + 1, m_elementList.end());
649 updateVoltages = true;
650 }
651 if (!foundPickbox) { SetCursor(wxCURSOR_ARROW); }
652
653 if (m_mode != WorkspaceMode::MODE_INSERT) { m_mode = WorkspaceMode::MODE_EDIT; }
654
655 if (updateVoltages) { ValidateElementsVoltages(); }
656
657 if (saveCurrentState) SaveCurrentState();
658
659 if (m_continuousCalc && m_disconnectedElement) {
660 m_disconnectedElement = false;
661 RunStaticStudies();
662 }
663
664 m_selectionRect = wxRect2DDouble(0, 0, 0, 0);
665
666 if (m_hmPlane && m_showHM) {
667 m_showHMTimer = true;
668 m_timerHeatMap->Start();
669 }
670
671 Redraw();
672 UpdateStatusBar();
673}
674
675void Workspace::OnMouseMotion(wxMouseEvent& event)
676{
677 bool redraw = false;
678 switch (m_mode) {
679 case WorkspaceMode::MODE_INSERT: {
680 auto& newElement = m_elementList.back(); // Get the last element in the list.
681 newElement->SetPosition(m_camera->ScreenToWorld(event.GetPosition()));
682 redraw = true;
683 } break;
684
685 case WorkspaceMode::MODE_INSERT_TEXT: {
686 auto& newText = m_textList.back();
687 newText->SetPosition(m_camera->ScreenToWorld(event.GetPosition()));
688 redraw = true;
689 } break;
690
691 case WorkspaceMode::MODE_DRAG:
692 case WorkspaceMode::MODE_DRAG_INSERT:
693 case WorkspaceMode::MODE_DRAG_INSERT_TEXT:
694 case WorkspaceMode::MODE_DRAG_PASTE: {
695 m_camera->SetTranslation(event.GetPosition());
696 redraw = true;
697 } break;
698
699 case WorkspaceMode::MODE_EDIT: {
700 bool foundPickbox = false;
701 for (auto& element : m_elementList) {
702 if (element->IsSelected()) {
703 // Show element pickbox (when it has) if the mouse is over the selected object.
704 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
705 element->ShowPickbox();
706 redraw = true;
707
708 // If the mouse is over a pickbox set correct mouse cursor.
709 if (element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) {
710 foundPickbox = true;
711 SetCursor(element->GetBestPickboxCursor());
712 }
713 else if (!foundPickbox) {
714 SetCursor(wxCURSOR_ARROW);
715 element->ResetPickboxes();
716 }
717 }
718 else if (!foundPickbox) {
719 if (element->IsPickboxShown()) redraw = true;
720
721 element->ShowPickbox(false);
722 element->ResetPickboxes();
723 SetCursor(wxCURSOR_ARROW);
724 }
725 }
726 }
727 } break;
728
729 case WorkspaceMode::MODE_MOVE_NODE: {
730 for (auto& element : m_elementList) {
731 if (element->IsSelected()) {
732 element->MoveNode(nullptr, m_camera->ScreenToWorld(event.GetPosition()));
733 redraw = true;
734 }
735 }
736 } break;
737
738 case WorkspaceMode::MODE_MOVE_PICKBOX: {
739 for (auto& element : m_elementList) {
740 if (element->IsSelected()) {
741 element->MovePickbox(m_camera->ScreenToWorld(event.GetPosition()));
742 redraw = true;
743 }
744 }
745 if (m_hmPlane && m_showHM) {
746 m_hmPlane->Clear();
747 }
748 } break;
749
750 case WorkspaceMode::MODE_MOVE_ELEMENT:
751 case WorkspaceMode::MODE_PASTE: {
752 for (auto it = m_elementList.begin(), itEnd = m_elementList.end(); it != itEnd; ++it) {
753 auto element = *it;
754 if (element->IsSelected()) {
755 element->Move(m_camera->ScreenToWorld(event.GetPosition()));
756 // Move child nodes
757 std::vector<Element*> childList = element->GetChildList();
758 for (auto it = childList.begin(), itEnd = childList.end(); it != itEnd; ++it) {
759 (*it)->MoveNode(element.get(), m_camera->ScreenToWorld(event.GetPosition()));
760 }
761 redraw = true;
762 }
763 }
764 // Text element motion
765 for (auto& text : m_textList) {
766 if (text->IsSelected()) {
767 text->Move(m_camera->ScreenToWorld(event.GetPosition()));
768 redraw = true;
769 }
770 }
771 if (m_hmPlane && m_showHM) {
772 m_hmPlane->Clear();
773 }
774 } break;
775
776 case WorkspaceMode::MODE_SELECTION_RECT: {
777 wxPoint2DDouble currentPos = m_camera->ScreenToWorld(event.GetPosition());
778 double x, y, w, h;
779 if (currentPos.m_x < m_startSelRect.m_x) {
780 x = currentPos.m_x;
781 w = m_startSelRect.m_x - currentPos.m_x;
782 }
783 else {
784 x = m_startSelRect.m_x;
785 w = currentPos.m_x - m_startSelRect.m_x;
786 }
787 if (currentPos.m_y < m_startSelRect.m_y) {
788 y = currentPos.m_y;
789 h = m_startSelRect.m_y - currentPos.m_y;
790 }
791 else {
792 y = m_startSelRect.m_y;
793 h = currentPos.m_y - m_startSelRect.m_y;
794 }
795
796 m_selectionRect = wxRect2DDouble(x, y, w, h);
797 redraw = true;
798 } break;
799 }
800
801 if (redraw) {
802 Redraw();
803 }
804 m_camera->UpdateMousePosition(event.GetPosition());
805 UpdateStatusBar();
806 m_timer->Start(); // Restart the timer.
807 event.Skip();
808}
809
810void Workspace::OnMiddleDown(wxMouseEvent& event)
811{
812 // Set to drag mode.
813 switch (m_mode) {
814 case WorkspaceMode::MODE_INSERT: {
815 m_mode = WorkspaceMode::MODE_DRAG_INSERT;
816 } break;
817 case WorkspaceMode::MODE_INSERT_TEXT: {
818 m_mode = WorkspaceMode::MODE_DRAG_INSERT_TEXT;
819 } break;
820 case WorkspaceMode::MODE_PASTE: {
821 m_mode = WorkspaceMode::MODE_DRAG_PASTE;
822 } break;
823 default: {
824 m_mode = WorkspaceMode::MODE_DRAG;
825 } break;
826 }
827 m_camera->StartTranslation(m_camera->ScreenToWorld(event.GetPosition()));
828 UpdateStatusBar();
829
830 if (m_hmPlane && m_showHM) {
831 m_hmPlane->Clear();
832 }
833
834 event.Skip();
835}
836
837void Workspace::OnMiddleUp(wxMouseEvent& event)
838{
839 switch (m_mode) {
840 case WorkspaceMode::MODE_DRAG_INSERT: {
841 m_mode = WorkspaceMode::MODE_INSERT;
842 } break;
843 case WorkspaceMode::MODE_DRAG_INSERT_TEXT: {
844 m_mode = WorkspaceMode::MODE_INSERT_TEXT;
845 } break;
846 case WorkspaceMode::MODE_DRAG_PASTE: {
847 m_mode = WorkspaceMode::MODE_PASTE;
848 } break;
849 case WorkspaceMode::MODE_INSERT:
850 case WorkspaceMode::MODE_INSERT_TEXT:
851 case WorkspaceMode::MODE_PASTE: {
852 // Does nothing.
853 } break;
854 default: {
855 m_mode = WorkspaceMode::MODE_EDIT;
856 } break;
857 }
858 UpdateStatusBar();
859
860 if (m_hmPlane && m_showHM) {
861 UpdateHeatMap();
862 Redraw();
863 }
864
865 event.Skip();
866}
867
868void Workspace::OnScroll(wxMouseEvent& event)
869{
870 if (event.GetWheelRotation() > 0)
871 m_camera->SetScale(event.GetPosition(), +0.05);
872 else
873 m_camera->SetScale(event.GetPosition(), -0.05);
874
875 if (m_hmPlane && m_showHM) {
876 m_hmPlane->Clear();
877 m_showHMTimer = true;
878 m_timerHeatMap->Start();
879 }
880
881 UpdateStatusBar();
882 Redraw();
883}
884
885void Workspace::OnKeyDown(wxKeyEvent& event)
886{
887 bool insertingElement = false;
888 if (m_mode == WorkspaceMode::MODE_INSERT || m_mode == WorkspaceMode::MODE_INSERT_TEXT) insertingElement = true;
889
890 char key = event.GetUnicodeKey();
891 if (key != WXK_NONE) {
892 switch (key) {
893 case WXK_ESCAPE: // Cancel operations.
894 {
895 if (m_mode == WorkspaceMode::MODE_INSERT) {
896 //Element* elementToDelete = m_elementList[m_elementList.size() - 1];
897 auto elementToDelete = m_elementList.back();
898
899 // Remove child element that has to be deleted (specially buses)
900 for (auto& element : m_elementList) {
901 element->RemoveChild(elementToDelete.get());
902 }
903
904 m_elementList.pop_back(); // Removes the last element being inserted.
905 m_mode = WorkspaceMode::MODE_EDIT;
906 Redraw();
907 }
908 else if (m_mode == WorkspaceMode::MODE_INSERT_TEXT) {
909 m_textList.pop_back();
910 m_mode = WorkspaceMode::MODE_EDIT;
911 Redraw();
912 }
913 } break;
914 case WXK_DELETE: // Delete selected elements
915 {
916 DeleteSelectedElements();
917 } break;
918 case 'A': {
919 if (!insertingElement) {
920 //Text* newText = new Text(m_camera->ScreenToWorld(event.GetPosition()), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
921 auto newText = std::make_shared<Text>(
922 m_camera->ScreenToWorld(event.GetPosition()),
923 m_properties->GetGeneralPropertiesData().labelFont,
924 m_properties->GetGeneralPropertiesData().labelFontSize);
925 m_textList.push_back(newText);
926 m_mode = WorkspaceMode::MODE_INSERT_TEXT;
927 m_statusBar->SetStatusText(_("Insert Text: Click to insert, ESC to cancel."));
928 if (m_hmPlane && m_showHM) {
929 m_hmPlane->Clear();
930 }
931 Redraw();
932 }
933 } break;
934 case 'F': {
935 if (event.GetModifiers() == wxMOD_SHIFT) { Fit(); }
936 } break;
937 case 'R': // Rotate the selected elements.
938 {
939 RotateSelectedElements(event.GetModifiers() != wxMOD_SHIFT);
940 } break;
941 case 'B': // Insert a bus.
942 {
943 if (!insertingElement) {
944 auto newBus = std::make_shared<Bus>(m_camera->ScreenToWorld(event.GetPosition()),
945 wxString::Format(_("Bus %d"), GetElementNumber(ID_BUS)));
946 IncrementElementNumber(ID_BUS);
947 m_elementList.push_back(newBus);
948 m_mode = WorkspaceMode::MODE_INSERT;
949 m_statusBar->SetStatusText(_("Insert Bus: Click to insert, ESC to cancel."));
950 if (m_hmPlane && m_showHM) {
951 m_hmPlane->Clear();
952 }
953 Redraw();
954 }
955 } break;
956 case 'L': {
957 if (!insertingElement) {
958 if (!event.ControlDown() && event.ShiftDown()) { // Insert a load.
959 auto newLoad = std::make_shared<Load>(wxString::Format(_("Load %d"), GetElementNumber(ID_LOAD)));
960 IncrementElementNumber(ID_LOAD);
961 m_elementList.push_back(newLoad);
962 m_mode = WorkspaceMode::MODE_INSERT;
963 m_statusBar->SetStatusText(_("Insert Load: Click on a bus, ESC to cancel."));
964 }
965 else if (!event.ControlDown() && !event.ShiftDown()) { // Insert a power line.
966 auto newLine = std::make_shared<Line>(wxString::Format(_("Line %d"), GetElementNumber(ID_LINE)));
967 IncrementElementNumber(ID_LINE);
968 m_elementList.push_back(newLine);
969 m_mode = WorkspaceMode::MODE_INSERT;
970 m_statusBar->SetStatusText(_("Insert Line: Click on two buses, ESC to cancel."));
971 }
972 if (m_hmPlane && m_showHM) {
973 m_hmPlane->Clear();
974 }
975 Redraw();
976 }
977 // Tests - Ctrl + Shift + L
978 } break;
979 case 'T': // Insert a transformer.
980 {
981 if (!insertingElement) {
982 auto newTransformer = std::make_shared<Transformer>(wxString::Format(_("Transformer %d"), GetElementNumber(ID_TRANSFORMER)));
983 IncrementElementNumber(ID_TRANSFORMER);
984 m_elementList.push_back(newTransformer);
985 m_mode = WorkspaceMode::MODE_INSERT;
986 m_statusBar->SetStatusText(_("Insert Transformer: Click on two buses, ESC to cancel."));
987 if (m_hmPlane && m_showHM) {
988 m_hmPlane->Clear();
989 }
990 Redraw();
991 }
992 } break;
993 case 'G': // Insert a generator.
994 {
995 if (!insertingElement) {
996 auto newGenerator = std::make_shared<SyncGenerator>(wxString::Format(_("Generator %d"), GetElementNumber(ID_SYNCGENERATOR)));
997 IncrementElementNumber(ID_SYNCGENERATOR);
998 m_elementList.push_back(newGenerator);
999 m_mode = WorkspaceMode::MODE_INSERT;
1000 m_statusBar->SetStatusText(_("Insert Generator: Click on a bus, ESC to cancel."));
1001 if (m_hmPlane && m_showHM) {
1002 m_hmPlane->Clear();
1003 }
1004 Redraw();
1005 }
1006 } break;
1007 case 'I': {
1008 if (!insertingElement) {
1009 if (event.GetModifiers() == wxMOD_SHIFT) { // Insert an inductor.
1010 auto newInductor = std::make_shared<Inductor>(wxString::Format(_("Inductor %d"), GetElementNumber(ID_INDUCTOR)));
1011 IncrementElementNumber(ID_INDUCTOR);
1012 m_elementList.push_back(newInductor);
1013 m_mode = WorkspaceMode::MODE_INSERT;
1014 m_statusBar->SetStatusText(_("Insert Inductor: Click on a bus, ESC to cancel."));
1015 }
1016 else // Insert an induction motor.
1017 {
1018 auto newIndMotor = std::make_shared<IndMotor>(wxString::Format(_("Induction motor %d"), GetElementNumber(ID_INDMOTOR)));
1019 IncrementElementNumber(ID_INDMOTOR);
1020 m_elementList.push_back(newIndMotor);
1021 m_mode = WorkspaceMode::MODE_INSERT;
1022 m_statusBar->SetStatusText(_("Insert Induction Motor: Click on a bus, ESC to cancel."));
1023 }
1024 if (m_hmPlane && m_showHM) {
1025 m_hmPlane->Clear();
1026 }
1027 Redraw();
1028 }
1029 } break;
1030 case 'K': // Insert a synchronous condenser.
1031 {
1032 if (!insertingElement) {
1033 auto newSyncCondenser = std::make_shared<SyncMotor>(wxString::Format(_("Synchronous condenser %d"), GetElementNumber(ID_SYNCMOTOR)));
1034 IncrementElementNumber(ID_SYNCMOTOR);
1035 m_elementList.push_back(newSyncCondenser);
1036 m_mode = WorkspaceMode::MODE_INSERT;
1037 m_statusBar->SetStatusText(_("Insert Synchronous Condenser: Click on a bus, ESC to cancel."));
1038 if (m_hmPlane && m_showHM) {
1039 m_hmPlane->Clear();
1040 }
1041 Redraw();
1042 }
1043 } break;
1044 case 'C': {
1045 if (!insertingElement) {
1046 if (event.GetModifiers() == wxMOD_SHIFT) { // Insert a capacitor.
1047 auto newCapacitor = std::make_shared<Capacitor>(wxString::Format(_("Capacitor %d"), GetElementNumber(ID_CAPACITOR)));
1048 IncrementElementNumber(ID_CAPACITOR);
1049 m_elementList.push_back(newCapacitor);
1050 m_mode = WorkspaceMode::MODE_INSERT;
1051 m_statusBar->SetStatusText(_("Insert Capacitor: Click on a bus, ESC to cancel."));
1052 if (m_hmPlane && m_showHM) {
1053 m_hmPlane->Clear();
1054 }
1055 Redraw();
1056 }
1057 else if (event.GetModifiers() == wxMOD_CONTROL) { // Copy.
1058 CopySelection();
1059 }
1060 }
1061 } break;
1062 case 'H': {
1063 if (!insertingElement) {
1064 if (event.ShiftDown() && event.ControlDown()) {
1065 if (!m_showHM) {
1066 m_showHM = true;
1067 UpdateHeatMap();
1068 }
1069 else {
1070 m_showHM = false;
1071 }
1072
1073 }
1074 else if (event.GetModifiers() == wxMOD_SHIFT) { // Insert an harmonic current source.
1075 auto newHarmCurrent = std::make_shared<HarmCurrent>(
1076 wxString::Format(_("Harmonic Current %d"), GetElementNumber(ID_HARMCURRENT)));
1077 IncrementElementNumber(ID_HARMCURRENT);
1078 m_elementList.push_back(newHarmCurrent);
1079 m_mode = WorkspaceMode::MODE_INSERT;
1080 m_statusBar->SetStatusText(
1081 _("Insert Harmonic Current Source: Click on a bus, ESC to cancel."));
1082 }
1083 if (m_hmPlane && m_showHM) {
1084 m_hmPlane->Clear();
1085 }
1086 Redraw();
1087 }
1088 } break;
1089 case 'V': {
1090 if (!insertingElement) {
1091 if (event.GetModifiers() == wxMOD_CONTROL) { Paste(); }
1092 }
1093 } break;
1094 case 'S': {
1095 if (!insertingElement) {
1096 if (event.GetModifiers() == wxMOD_CONTROL) {
1097 // Save the workspace.
1098 FileHanding fileHandling(this);
1099
1100 if (GetSavedPath().IsOk()) {
1101 fileHandling.SaveProject(GetSavedPath());
1102 }
1103 else {
1104 wxFileDialog saveFileDialog(this, _("Save PSP file"), "", "", "PSP files (*.psp)|*.psp",
1105 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
1106 if (saveFileDialog.ShowModal() == wxID_CANCEL) return;
1107
1108 fileHandling.SaveProject(saveFileDialog.GetPath());
1109 wxFileName fileName(saveFileDialog.GetPath());
1110 SetName(fileName.GetName());
1111 if (m_auiNotebook) m_auiNotebook->SetPageText(m_auiNotebook->GetPageIndex(this), GetName());
1112 SetSavedPath(fileName);
1113 }
1114 }
1115 }
1116 } break;
1117 case 'Z': {
1118 if (!insertingElement) {
1119 if (event.ControlDown() && !event.ShiftDown()) { SetPreviousState(); }
1120 if (event.ControlDown() && event.ShiftDown()) { SetNextState(); }
1121 }
1122 } break;
1123 case 'Y': {
1124 if (!insertingElement) {
1125 if (event.GetModifiers() == wxMOD_CONTROL) { SetNextState(); }
1126 }
1127 } break;
1128 case 'E': {
1129 if (!insertingElement) {
1130 if (event.GetModifiers() == wxMOD_SHIFT) {
1131
1132 if (!insertingElement) {
1133 auto newEMTElement = std::make_shared<EMTElement>(wxString::Format(_("Electromagnetic Element %d"), GetElementNumber(ID_EMTELEMENT)));
1134 IncrementElementNumber(ID_EMTELEMENT);
1135 m_elementList.push_back(newEMTElement);
1136 m_mode = WorkspaceMode::MODE_INSERT;
1137 m_statusBar->SetStatusText(_("Insert Electromagnetic Transient Element: Click on a bus, ESC to cancel."));
1138 if (m_hmPlane && m_showHM) {
1139 m_hmPlane->Clear();
1140 }
1141 Redraw();
1142 }
1143 }
1144 }
1145 } break;
1146#ifdef _DEBUG
1147#ifdef SHOW_DEBUG_PANEL
1148 case 'D': {
1149 if (event.GetModifiers() == wxMOD_CONTROL) {
1150 if (m_debugFrame) {
1151 m_debugFrame->SetFocus();
1152 }
1153 }
1154 } break;
1155#endif
1156#endif
1157 default:
1158 break;
1159 }
1160 }
1161
1162 UpdateStatusBar();
1163 event.Skip();
1164}
1165
1166void Workspace::UpdateStatusBar()
1167{
1168 switch (m_mode) {
1169 case WorkspaceMode::MODE_DRAG: {
1170 m_statusBar->SetStatusText(_("MODE: DRAG"), 1);
1171 } break;
1172
1173 case WorkspaceMode::MODE_PASTE:
1174 case WorkspaceMode::MODE_DRAG_PASTE: {
1175 m_statusBar->SetStatusText(_("MODE: PASTE"), 1);
1176 } break;
1177
1178 case WorkspaceMode::MODE_INSERT:
1179 case WorkspaceMode::MODE_INSERT_TEXT:
1180 case WorkspaceMode::MODE_DRAG_INSERT:
1181 case WorkspaceMode::MODE_DRAG_INSERT_TEXT: {
1182 m_statusBar->SetStatusText(_("MODE: INSERT"), 1);
1183 } break;
1184
1185 case WorkspaceMode::MODE_MOVE_ELEMENT:
1186 case WorkspaceMode::MODE_MOVE_PICKBOX:
1187 case WorkspaceMode::MODE_MOVE_NODE:
1188 case WorkspaceMode::MODE_SELECTION_RECT:
1189 case WorkspaceMode::MODE_EDIT: {
1190 if (m_oldStatusMode != m_mode)
1191 m_statusBar->SetStatusText(wxT(""));
1192 m_statusBar->SetStatusText(_("MODE: EDIT"), 1);
1193 } break;
1194 }
1195
1196 m_statusBar->SetStatusText(wxString::Format(_("ZOOM: %d%%"), (int)(m_camera->GetScale() * 100.0)), 2);
1197 m_statusBar->SetStatusText(
1198 wxString::Format(wxT("X: %.1f Y: %.1f"), m_camera->GetMousePosition().m_x, m_camera->GetMousePosition().m_y),
1199 3);
1200 m_oldStatusMode = m_mode;
1201}
1202
1203int Workspace::GetElementNumberFromList(Element* element)
1204{
1205 int elementNum = 0;
1206 for (auto& elementFromList : m_elementList) {
1207 if (element->GetElementType() == elementFromList->GetElementType()) {
1208 if (element == elementFromList.get()) return elementNum;
1209 elementNum++;
1210 }
1211 }
1212 return 0;
1213}
1214
1215void Workspace::GetStateListsCopy(const std::vector< std::shared_ptr<PowerElement> >& elementsList,
1216 const std::vector< std::shared_ptr<Text> >& textList,
1217 std::vector< std::shared_ptr<PowerElement> >& elementsListCopy,
1218 std::vector< std::shared_ptr<Text> >& textListCopy)
1219{
1220 // Free copy lists first
1221 //for (auto& element : elementsListCopy) delete element;
1222 //for (auto& text : textListCopy) delete text;
1223 elementsListCopy.clear();
1224 textListCopy.clear();
1225
1226 std::map<Element*, Element*> elementMap;
1227
1228 for (auto& element : elementsList) {
1229 PowerElement* copyElement = static_cast<PowerElement*>(element->GetCopy());
1230 elementsListCopy.emplace_back(copyElement);
1231 elementMap[element.get()] = copyElement;
1232 }
1233 // Correct the parent and child pointers
1234 for (auto& copyElement : elementsListCopy) {
1235 // Parent
1236 int i = 0;
1237 for (Element* parent : copyElement->GetParentList()) {
1238 auto it = elementMap.find(parent);
1239
1240 if (it != elementMap.end())
1241 copyElement->SetParent(it->second, i);
1242
1243 i++;
1244 }
1245 // Child
1246 i = 0;
1247 for (Element* child : copyElement->GetChildList()) {
1248 auto it = elementMap.find(child);
1249
1250 if (it != elementMap.end())
1251 copyElement->SetChild(it->second, i);
1252
1253 i++;
1254 }
1255 }
1256
1257 for (const auto& text : textList) {
1258 auto copyText = static_cast<Text*>(text->GetCopy());
1259 // Set text the correct element associated with the text
1260 auto it = elementMap.find(copyText->GetElement());
1261
1262 if (it != elementMap.end())
1263 copyText->SetElement(it->second);
1264 else
1265 copyText->SetElement(nullptr);
1266 textListCopy.emplace_back(copyText);
1267 }
1268}
1269
1270void Workspace::UpdateHeatMap()
1271{
1272 if (m_hmPlane && m_showHM) {
1273 m_hmPlane->Clear();
1274
1275 wxRect2DDouble screenRect(-100, -100, m_width + 200.0, m_height + 200.0);
1276
1277 // Get new voltage range
1278 std::vector<Bus*> busList;
1279 float minVoltage, maxVoltage;
1280 if (m_hmAutomaticLabel) {
1281 minVoltage = 10.0f;
1282 maxVoltage = 0.0f;
1283 }
1284 else {
1285 minVoltage = m_hmPlane->GetMinLimit();
1286 maxVoltage = m_hmPlane->GetMaxLimit();
1287 }
1288
1289 for (auto& element : m_elementList) {
1290 if (Bus* bus = dynamic_cast<Bus*>(element.get())) {
1291 if (m_hmAutomaticLabel) {
1292 const float voltage = std::abs(bus->GetElectricalData().voltage);
1293 if (minVoltage > voltage) minVoltage = voltage;
1294 if (maxVoltage < voltage) maxVoltage = voltage;
1295 }
1296 busList.push_back(bus);
1297 }
1298 }
1299 if (m_hmAutomaticLabel) {
1300 m_hmPlane->SetLabelLimits(minVoltage, maxVoltage);
1301 }
1302
1303 for (Bus* bus : busList) {
1304 const float voltage = std::abs(bus->GetElectricalData().voltage);
1305 float depth = 2.0f * (voltage - (maxVoltage + minVoltage) / 2.0f) / (maxVoltage - minVoltage);
1306 if (depth < -1.0) depth = -1.0;
1307 if (depth > 1.0) depth = 1.0;
1308
1309 wxRect2DDouble rect = bus->GetRect();
1310 rect = wxRect2DDouble(
1311 (rect.m_x - 100.0f) * m_camera->GetScale() + m_camera->GetTranslation().m_x * m_camera->GetScale(),
1312 (rect.m_y - 50.0f) * m_camera->GetScale() + m_camera->GetTranslation().m_y * m_camera->GetScale(),
1313 (rect.m_width + 200.0f) * m_camera->GetScale(),
1314 (rect.m_height + 100.0f) * m_camera->GetScale());
1315
1316 if (screenRect.Contains(rect))
1317 m_hmPlane->SetRectSlope(rect, M_PI * static_cast<float>(bus->GetAngle()) / 180.0f, depth);
1318 }
1319
1320 //m_hmPlane->UpdateCoords();
1321
1322 //int iterations = std::lround(3.3 * std::pow(m_camera->GetScale(), 0.62));
1323 int iterations = std::lround(10 * std::pow(m_camera->GetScale(), 2));
1324 //int iterations = std::lround(1 * std::pow(m_camera->GetScale(), 0.2));
1325 //int iterations = 1;
1326 if (iterations < 1) iterations = 1;
1327 m_hmPlane->SmoothPlane(iterations);
1328 }
1329}
1330
1331void Workspace::OnPopupClick(wxCommandEvent& event)
1332{
1333 bool redrawHM = false;
1334
1335 wxMenu* menu = static_cast<wxMenu*>(event.GetEventObject());
1336 Element* element = static_cast<Element*>(menu->GetClientData());
1337 int eventID = event.GetId();
1338 switch (eventID) {
1339 case ID_EDIT_ELEMENT: {
1340 if (element->ShowForm(this, element)) {
1341 CheckSlackBusDuplication(element);
1342 UpdateTextElements();
1343 SaveCurrentState();
1344 }
1345 } break;
1346 case ID_LINE_ADD_NODE: {
1347 Line* line = static_cast<Line*>(element);
1348 line->AddNode(m_camera->GetMousePosition());
1349 SaveCurrentState();
1350 Redraw();
1351 } break;
1352 case ID_LINE_REMOVE_NODE: {
1353 Line* line = static_cast<Line*>(element);
1354 line->RemoveNode(m_camera->GetMousePosition());
1355 SaveCurrentState();
1356 Redraw();
1357 } break;
1358 case ID_ROTATE_CLOCK: {
1359 element->Rotate();
1360 for (auto& iElement : m_elementList) {
1361 // Parent's element rotating...
1362 for (int i = 0; i < (int)iElement->GetParentList().size(); i++) {
1363 Element* parent = iElement->GetParentList()[i];
1364 if (parent == element) { iElement->RotateNode(parent); }
1365 }
1366 }
1367 redrawHM = true;
1368 SaveCurrentState();
1369 Redraw();
1370 } break;
1372 element->Rotate(false);
1373 for (auto& iElement : m_elementList) {
1374 // Parent's element rotating...
1375 for (int i = 0; i < (int)iElement->GetParentList().size(); i++) {
1376 Element* parent = iElement->GetParentList()[i];
1377 if (parent == element) { iElement->RotateNode(parent, false); }
1378 }
1379 }
1380 redrawHM = true;
1381 SaveCurrentState();
1382 Redraw();
1383 } break;
1384 case ID_DELETE: {
1385 //for (auto& iElement : m_elementList) {
1386 // if (element == iElement.get()) {
1387 // // Remove child/parent.
1388 // std::vector<Element*> childList = element->GetChildList();
1389 // for (auto& child : childList) {
1390 // if (child) {
1391 // child->RemoveParent(element);
1392 // element->RemoveChild(child);
1393 // }
1394 // }
1395 // std::vector<Element*> parentList = element->GetParentList();
1396 // for (auto& parent : parentList) {
1397 // if (parent) { parent->RemoveChild(element); }
1398 // }
1399 //
1400 // //for (auto itt = m_textList.begin(); itt != m_textList.end(); ++itt) {
1401 // // Text* text = *itt;
1402 // // if (text->GetElement() == element) {
1403 // // m_textList.erase(itt--);
1404 // // if (text) delete text;
1405 // // }
1406 // //}
1407 // std::erase_if(m_textList, [&](const auto& text) {
1408 // return text->GetElement() == element;
1409 // });
1410 //
1411 // std::erase_if(m_elementList, [&](const auto& delElement) {
1412 // return delElement.get() == element;
1413 // });
1414 //
1415 // //m_elementList.erase(it);
1416 // //if (element) delete element;
1417 // menu->SetClientData(nullptr);
1418 // break;
1419 // }
1420 //}
1421 //SaveCurrentState();
1422
1423 // Remove child/parent.
1424 std::vector<Element*> childList = element->GetChildList();
1425 for (auto child : childList) {
1426 if (child) {
1427 child->RemoveParent(element);
1428 element->RemoveChild(child);
1429 }
1430 }
1431
1432 std::vector<Element*> parentList = element->GetParentList();
1433 for (auto parent : parentList) {
1434 if (parent)
1435 parent->RemoveChild(element);
1436 }
1437
1438 std::erase_if(m_textList, [&](const auto& text) {
1439 return text->GetElement() == element;
1440 });
1441
1442 std::erase_if(m_elementList, [&](const auto& delElement) {
1443 return delElement.get() == element;
1444 });
1445
1446 menu->SetClientData(nullptr);
1447 SaveCurrentState();
1448 } break;
1449 default:
1450 if (InsertTextElement(eventID, element)) {
1451 UpdateTextElements();
1452 SaveCurrentState();
1453 }
1454 break;
1455 }
1456 if (redrawHM && m_hmPlane && m_showHM) {
1457 m_hmPlane->Clear();
1458 m_showHMTimer = true;
1459 m_timerHeatMap->Start();
1460 }
1461}
1462
1463void Workspace::RotateSelectedElements(bool clockwise)
1464{
1465 bool saveCurrrentState = false;
1466 for (auto& element : m_elementList) {
1467 // Parent's element rotating...
1468 for (int i = 0; i < (int)element->GetParentList().size(); i++) {
1469 Element* parent = element->GetParentList()[i];
1470 if (parent) { // Check if parent is not null
1471 if (parent->IsSelected()) {
1472 element->RotateNode(parent, clockwise);
1473 // Update the positions used on motion action, the element will not be necessarily
1474 // moved.
1475 element->StartMove(m_camera->GetMousePosition());
1476 }
1477 }
1478 }
1479 if (element->IsSelected()) {
1480 saveCurrrentState = true;
1481 element->Rotate(clockwise);
1482 element->StartMove(m_camera->GetMousePosition());
1483 }
1484 }
1485
1486 // Rotate text element
1487 for (auto& text : m_textList) {
1488 if (text->IsSelected()) {
1489 saveCurrrentState = true;
1490 text->Rotate(clockwise);
1491 text->StartMove(m_camera->GetMousePosition());
1492 }
1493 }
1494 if (saveCurrrentState) SaveCurrentState();
1495
1496 if (m_hmPlane && m_showHM) {
1497 m_hmPlane->Clear();
1498 m_showHMTimer = true;
1499 m_timerHeatMap->Start();
1500 }
1501 Redraw();
1502}
1503
1504void Workspace::DeleteSelectedElements()
1505{
1506 // Don't set the end of the list at the loop's begin.
1507 for (auto it = m_elementList.begin(); it != m_elementList.end();) {
1508 Element* element = it->get();
1509
1510 if (element->IsSelected()) {
1511 // Remove child/parent.
1512 std::vector<Element*> childList = element->GetChildList();
1513 for (auto itc = childList.begin(), itEnd = childList.end(); itc != itEnd; ++itc) {
1514 Element* child = *itc;
1515 if (child) {
1516 child->RemoveParent(element);
1517 element->RemoveChild(child);
1518 }
1519 }
1520 std::vector<Element*> parentList = element->GetParentList();
1521 for (auto itp = parentList.begin(), itEnd = parentList.end(); itp != itEnd; ++itp) {
1522 Element* parent = *itp;
1523 if (parent) { parent->RemoveChild(element); }
1524 }
1525
1526 //for (auto& text : m_textList) {
1527 // if (text->GetElement() == element) {
1528 // if (m_textList.size() == 1) {
1529 // m_textList.erase(itt);
1530 // break;
1531 // }
1532 // else m_textList.erase(itt--);
1533 // if (text) delete text;
1534 // }
1535 //}
1536 std::erase_if(m_textList, [&](const auto& text) {
1537 return text->GetElement() == element;
1538 });
1539
1540 it = m_elementList.erase(it);
1541 //if (element) delete element;
1542 }
1543 else it++;
1544 }
1545
1546 //for (auto it = m_textList.begin(); it != m_textList.end(); ++it) {
1547 // Text* text = *it;
1548 // if (text->IsSelected()) {
1549 // if (m_textList.size() == 1) {
1550 // m_textList.erase(it);
1551 // break;
1552 // }
1553 // else m_textList.erase(it--);
1554 // if (text) delete text;
1555 // }
1556 //}
1557 std::erase_if(m_textList, [](const auto& text) {
1558 return text->IsSelected();
1559 });
1560
1561 if (m_hmPlane && m_showHM) {
1562 m_hmPlane->Clear();
1563 m_showHMTimer = true;
1564 m_timerHeatMap->Start();
1565 }
1566 SaveCurrentState();
1567 Redraw();
1568}
1569
1570bool Workspace::GetElementsCorners(wxPoint2DDouble& leftUpCorner,
1571 wxPoint2DDouble& rightDownCorner,
1572 std::vector<Element*> elementList)
1573{
1574 if (elementList.size() == 0) return false;
1575
1576 elementList[0]->CalculateBoundaries(leftUpCorner, rightDownCorner);
1577
1578 for (auto it = elementList.begin() + 1, itEnd = elementList.end(); it != itEnd; it++) {
1579 Element* element = *it;
1580 wxPoint2DDouble leftUp;
1581 wxPoint2DDouble rightDown;
1582 element->CalculateBoundaries(leftUp, rightDown);
1583 if (leftUp.m_x < leftUpCorner.m_x) leftUpCorner.m_x = leftUp.m_x;
1584 if (leftUp.m_y < leftUpCorner.m_y) leftUpCorner.m_y = leftUp.m_y;
1585 if (rightDown.m_x > rightDownCorner.m_x) rightDownCorner.m_x = rightDown.m_x;
1586 if (rightDown.m_y > rightDownCorner.m_y) rightDownCorner.m_y = rightDown.m_y;
1587 }
1588 return true;
1589}
1590
1591void Workspace::Fit()
1592{
1593 wxPoint2DDouble leftUpCorner(0, 0);
1594 wxPoint2DDouble rightDownCorner(0, 0);
1595 std::vector<Element*> elementList = GetElementList();
1596 for (const auto& text : m_textList) { elementList.push_back(text.get()); }
1597
1598 if (!GetElementsCorners(leftUpCorner, rightDownCorner, elementList)) return;
1599 wxPoint2DDouble middleCoords = (leftUpCorner + rightDownCorner) / 2.0;
1600
1601 int width = 0.0;
1602 int height = 0.0;
1603 GetSize(&width, &height);
1604
1605 double scaleX = double(width) / (rightDownCorner.m_x - leftUpCorner.m_x);
1606 double scaleY = double(height) / (rightDownCorner.m_y - leftUpCorner.m_y);
1607
1608 double scale = scaleX < scaleY ? scaleX : scaleY;
1609 if (scale > m_camera->GetZoomMax()) scale = m_camera->GetZoomMax();
1610 if (scale < m_camera->GetZoomMin()) scale = m_camera->GetZoomMin();
1611
1612 m_camera->SetScale(scale);
1613
1614 m_camera->StartTranslation(middleCoords);
1615 m_camera->SetTranslation(wxPoint2DDouble(width / 2, height / 2));
1616
1617 if (m_hmPlane && m_showHM) {
1618 UpdateHeatMap();
1619 }
1620
1621 Redraw();
1622}
1623
1624bool Workspace::InsertTextElement(int textID, Element* parentElement, ElectricalUnit unit, int precision)
1625{
1626 switch (textID) {
1627 case ID_TXT_NAME: {
1628 if (FindTextElement(parentElement, DATA_NAME)) return false; // Avoid inserting more than one text element of the same type for an element.
1629 //Text* newText = new Text(parentElement->GetPosition() + wxPoint2DDouble(40, -30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1630 auto newText = std::make_shared<Text>(parentElement->GetPosition() + wxPoint2DDouble(40, -30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1631 newText->SetElement(parentElement);
1632 newText->SetDataType(DATA_NAME);
1633 newText->SetElementTypeText(parentElement->GetElementType());
1634 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1635
1636 m_textList.push_back(newText);
1637 } break;
1638 case ID_TXT_VOLTAGE: {
1639 if (FindTextElement(parentElement, DATA_VOLTAGE)) return false;
1640 //Text* newText = new Text(parentElement->GetPosition() + wxPoint2DDouble(40, 15), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1641 auto newText = std::make_shared<Text>(parentElement->GetPosition() + wxPoint2DDouble(40, 15), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1642 newText->SetElement(parentElement);
1643 newText->SetDataType(DATA_VOLTAGE);
1644 if (unit == ElectricalUnit::UNIT_NONE)
1645 newText->SetUnit(ElectricalUnit::UNIT_PU);
1646 else
1647 newText->SetUnit(unit);
1648 newText->SetDecimalPlaces(precision);
1649 newText->SetElementTypeText(parentElement->GetElementType());
1650 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1651
1652 m_textList.emplace_back(newText);
1653 } break;
1654 case ID_TXT_ANGLE: {
1655 if (FindTextElement(parentElement, DATA_ANGLE)) return false;
1656 //Text* newText = new Text(parentElement->GetPosition() + wxPoint2DDouble(40, 30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1657 auto newText = std::make_shared<Text>(parentElement->GetPosition() + wxPoint2DDouble(40, 30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1658 newText->SetElement(parentElement);
1659 newText->SetDataType(DATA_ANGLE);
1660 if (unit == ElectricalUnit::UNIT_NONE)
1661 newText->SetUnit(ElectricalUnit::UNIT_DEGREE);
1662 else
1663 newText->SetUnit(unit);
1664 newText->SetDecimalPlaces(precision);
1665 newText->SetElementTypeText(parentElement->GetElementType());
1666 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1667
1668 m_textList.emplace_back(newText);
1669 } break;
1670 case ID_TXT_FAULTCURRENT: {
1671 if (FindTextElement(parentElement, DATA_SC_CURRENT)) return false;
1672 //Text* newText = new Text(parentElement->GetPosition() + wxPoint2DDouble(-70, 30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1673 auto newText = std::make_shared<Text>(parentElement->GetPosition() + wxPoint2DDouble(-70, 30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1674 newText->SetElement(parentElement);
1675 newText->SetDataType(DATA_SC_CURRENT);
1676 if (unit == ElectricalUnit::UNIT_NONE)
1677 newText->SetUnit(ElectricalUnit::UNIT_A);
1678 else
1679 newText->SetUnit(unit);
1680 newText->SetDecimalPlaces(precision);
1681 newText->SetElementTypeText(parentElement->GetElementType());
1682 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1683
1684 m_textList.emplace_back(newText);
1685 } break;
1686 case ID_TXT_FAULTVOLTAGE: {
1687 if (FindTextElement(parentElement, DATA_SC_VOLTAGE)) return false;
1688 //Text* newText = new Text(parentElement->GetPosition() + wxPoint2DDouble(-70, 75), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1689 auto newText = std::make_shared<Text>(parentElement->GetPosition() + wxPoint2DDouble(-70, 75), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1690 newText->SetElement(parentElement);
1691 newText->SetDataType(DATA_SC_VOLTAGE);
1692 if (unit == ElectricalUnit::UNIT_NONE)
1693 newText->SetUnit(ElectricalUnit::UNIT_PU);
1694 else
1695 newText->SetUnit(unit);
1696 newText->SetDecimalPlaces(precision);
1697 newText->SetElementTypeText(parentElement->GetElementType());
1698 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1699
1700 m_textList.emplace_back(newText);
1701 } break;
1702 case ID_TXT_SCC: {
1703 if (FindTextElement(parentElement, DATA_SC_POWER)) return false;
1704 auto newText = std::make_shared<Text>(parentElement->GetPosition() + wxPoint2DDouble(-50, -30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1705 newText->SetElement(parentElement);
1706 newText->SetDataType(DATA_SC_POWER);
1707 newText->SetUnit(ElectricalUnit::UNIT_MVA);
1708 if (unit == ElectricalUnit::UNIT_NONE)
1709 newText->SetUnit(ElectricalUnit::UNIT_MVA);
1710 else
1711 newText->SetUnit(unit);
1712 newText->SetDecimalPlaces(precision);
1713 newText->SetElementTypeText(parentElement->GetElementType());
1714 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1715
1716 m_textList.emplace_back(newText);
1717 } break;
1718 case ID_TXT_THD: {
1719 if (FindTextElement(parentElement, DATA_PQ_THD)) return false;
1720 auto newText = std::make_shared<Text>(parentElement->GetPosition() + wxPoint2DDouble(-50, -15), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1721 newText->SetElement(parentElement);
1722 newText->SetDataType(DATA_PQ_THD);
1723 newText->SetDecimalPlaces(precision);
1724 newText->SetElementTypeText(parentElement->GetElementType());
1725 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1726
1727 m_textList.emplace_back(newText);
1728 } break;
1729 case ID_TXT_ACTIVE_POWER: {
1730 if (FindTextElement(parentElement, DATA_ACTIVE_POWER)) return false;
1731 auto newText = std::make_shared<Text>(parentElement->GetPosition() + wxPoint2DDouble(0, 35), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1732 newText->SetElement(parentElement);
1733 newText->SetDataType(DATA_ACTIVE_POWER);
1734 if (unit == ElectricalUnit::UNIT_NONE)
1735 newText->SetUnit(ElectricalUnit::UNIT_MW);
1736 else
1737 newText->SetUnit(unit);
1738 newText->SetDecimalPlaces(precision);
1739 newText->SetElementTypeText(parentElement->GetElementType());
1740 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1741
1742 m_textList.emplace_back(newText);
1743 } break;
1744 case ID_TXT_REACTIVE_POWER: {
1745 if (FindTextElement(parentElement, DATA_REACTIVE_POWER)) return false;
1746 auto newText = std::make_shared<Text>(parentElement->GetPosition() + wxPoint2DDouble(0, 50), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1747 newText->SetElement(parentElement);
1748 newText->SetDataType(DATA_REACTIVE_POWER);
1749 if (unit == ElectricalUnit::UNIT_NONE)
1750 newText->SetUnit(ElectricalUnit::UNIT_Mvar);
1751 else
1752 newText->SetUnit(unit);
1753 newText->SetDecimalPlaces(precision);
1754 newText->SetElementTypeText(parentElement->GetElementType());
1755 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1756
1757 m_textList.emplace_back(newText);
1758 } break;
1759 case ID_TXT_BRANCH_ACTIVE_POWER_1_2:
1760 case ID_TXT_BRANCH_ACTIVE_POWER_2_1: {
1761 if (FindTextElement(parentElement, DATA_PF_ACTIVE)) return false;
1762 wxPoint2DDouble position(0.0, -10.0);
1763 if (textID == ID_TXT_BRANCH_ACTIVE_POWER_1_2)
1764 position += 2.0 * parentElement->GetPointList()[1] - parentElement->GetPointList()[0];
1765 else
1766 position += 2.0 * parentElement->GetPointList()[parentElement->GetPointList().size() - 2] - parentElement->GetPointList()[parentElement->GetPointList().size() - 1];
1767
1768 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1769 newText->SetElement(parentElement);
1770 newText->SetDataType(DATA_PF_ACTIVE);
1771 if (unit == ElectricalUnit::UNIT_NONE)
1772 newText->SetUnit(ElectricalUnit::UNIT_MW);
1773 else
1774 newText->SetUnit(unit);
1775 newText->SetDecimalPlaces(precision);
1776 newText->SetElementTypeText(parentElement->GetElementType());
1777 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1778 if (textID == ID_TXT_BRANCH_ACTIVE_POWER_2_1)
1779 newText->SetDirection(1); // std is 0
1780
1781 m_textList.emplace_back(newText);
1782 } break;
1783 case ID_TXT_BRANCH_REACTIVE_POWER_1_2:
1784 case ID_TXT_BRANCH_REACTIVE_POWER_2_1: {
1785 if (FindTextElement(parentElement, DATA_PF_REACTIVE)) return false;
1786 wxPoint2DDouble position(0.0, 10.0);
1787 if (textID == ID_TXT_BRANCH_REACTIVE_POWER_1_2)
1788 position += 2.0 * parentElement->GetPointList()[1] - parentElement->GetPointList()[0];
1789 else
1790 position += 2.0 * parentElement->GetPointList()[parentElement->GetPointList().size() - 2] - parentElement->GetPointList()[parentElement->GetPointList().size() - 1];
1791
1792 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1793 newText->SetElement(parentElement);
1794 newText->SetDataType(DATA_PF_REACTIVE);
1795 if (unit == ElectricalUnit::UNIT_NONE)
1796 newText->SetUnit(ElectricalUnit::UNIT_Mvar);
1797 else
1798 newText->SetUnit(unit);
1799 newText->SetDecimalPlaces(precision);
1800 newText->SetElementTypeText(parentElement->GetElementType());
1801 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1802 if (textID == ID_TXT_BRANCH_REACTIVE_POWER_2_1)
1803 newText->SetDirection(1); // std is 0
1804
1805 m_textList.emplace_back(newText);
1806 } break;
1807 case ID_TXT_BRANCH_LOSSES: {
1808 if (FindTextElement(parentElement, DATA_PF_LOSSES)) return false;
1809 wxPoint2DDouble position = wxPoint2DDouble(0, 35) + (parentElement->GetPointList()[0] + parentElement->GetPointList()[parentElement->GetPointList().size() - 1]) / 2.0;
1810 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1811 newText->SetElement(parentElement);
1812 newText->SetDataType(DATA_PF_LOSSES);
1813 if (unit == ElectricalUnit::UNIT_NONE)
1814 newText->SetUnit(ElectricalUnit::UNIT_MW);
1815 else
1816 newText->SetUnit(unit);
1817 newText->SetDecimalPlaces(precision);
1818 newText->SetElementTypeText(parentElement->GetElementType());
1819 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1820
1821 m_textList.emplace_back(newText);
1822 } break;
1823 case ID_TXT_BRANCH_CURRENT_1_2:
1824 case ID_TXT_BRANCH_CURRENT_2_1: {
1825 if (FindTextElement(parentElement, DATA_PF_CURRENT)) return false;
1826 wxPoint2DDouble position(0.0, 10.0);
1827 if (textID == ID_TXT_BRANCH_CURRENT_1_2)
1828 position += 2.0 * parentElement->GetPointList()[1] - parentElement->GetPointList()[0];
1829 else
1830 position += 2.0 * parentElement->GetPointList()[parentElement->GetPointList().size() - 2] - parentElement->GetPointList()[parentElement->GetPointList().size() - 1];
1831
1832 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1833 newText->SetElement(parentElement);
1834 newText->SetDataType(DATA_PF_CURRENT);
1835 if (unit == ElectricalUnit::UNIT_NONE)
1836 newText->SetUnit(ElectricalUnit::UNIT_A);
1837 else
1838 newText->SetUnit(unit);
1839 newText->SetDecimalPlaces(precision);
1840 newText->SetElementTypeText(parentElement->GetElementType());
1841 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1842 if (textID == ID_TXT_BRANCH_CURRENT_2_1)
1843 newText->SetDirection(1); // std is 0
1844
1845 m_textList.emplace_back(newText);
1846 } break;
1847 case ID_TXT_BRANCH_FAULT_CURRENT_1_2:
1848 case ID_TXT_BRANCH_FAULT_CURRENT_2_1: {
1849 if (FindTextElement(parentElement, DATA_SC_CURRENT)) return false;
1850 wxPoint2DDouble position(0.0, 25.0);
1851 if (textID == ID_TXT_BRANCH_FAULT_CURRENT_1_2)
1852 position += 2.0 * parentElement->GetPointList()[1] - parentElement->GetPointList()[0];
1853 else
1854 position += 2.0 * parentElement->GetPointList()[parentElement->GetPointList().size() - 2] - parentElement->GetPointList()[parentElement->GetPointList().size() - 1];
1855
1856 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1857 newText->SetElement(parentElement);
1858 newText->SetDataType(DATA_SC_CURRENT);
1859 if (unit == ElectricalUnit::UNIT_NONE)
1860 newText->SetUnit(ElectricalUnit::UNIT_kA);
1861 else
1862 newText->SetUnit(unit);
1863 newText->SetDecimalPlaces(precision);
1864 newText->SetElementTypeText(parentElement->GetElementType());
1865 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1866 if (textID == ID_TXT_BRANCH_FAULT_CURRENT_2_1)
1867 newText->SetDirection(1); // std is 0
1868
1869 m_textList.emplace_back(newText);
1870 } break;
1871 default:
1872 return false;
1873 break;
1874 }
1875 return true;
1876}
1877
1878Element* Workspace::FindTextElement(Element* parentElement, int dataType)
1879{
1880 for (auto& text : m_textList) {
1881 if (text->GetElement() == parentElement && text->GetDataType() == dataType)
1882 return text.get();
1883 }
1884 return nullptr;
1885}
1886
1887void Workspace::RemoveAllTextElements()
1888{
1889 //for (auto* text : m_textList) {
1890 // if (text) delete text;
1891 //}
1892 m_textList.clear();
1893 SaveCurrentState();
1894}
1895
1896void Workspace::CheckSlackBusDuplication(Element* newSlackBus)
1897{
1898 Bus* newBus = dynamic_cast<Bus*>(newSlackBus);
1899 if (newBus) {
1900 if (!newBus->GetElectricalData().slackBus) return; // If the new bus is not set as slack bus, no need to check for duplication.
1901
1902 for (auto& element : m_elementList) {
1903 Bus* bus = dynamic_cast<Bus*>(element.get());
1904 if (!bus) continue;
1905 if (bus->GetElectricalData().slackBus && bus != newSlackBus) {
1906 wxMessageDialog msgDialog(this,
1907 wxString::Format(_("The system already has %s as the slack bus.\nDo you want to set %s as the new slack bus?"), bus->GetElectricalData().name, newBus->GetElectricalData().name),
1908 _("Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING);
1909 if (msgDialog.ShowModal() == wxID_YES) {
1910 auto data = bus->GetElectricalData();
1911 data.slackBus = false;
1912 bus->SetElectricalData(data);
1913 return;
1914 }
1915 else {
1916 auto data = newBus->GetElectricalData();
1917 data.slackBus = false;
1918 newBus->SetElectricalData(data);
1919 return;
1920 }
1921 }
1922 }
1923 }
1924}
1925
1926void Workspace::ValidateBusesVoltages(Element* initialBus)
1927{
1928 double nominalVoltage = static_cast<Bus*>(initialBus)->GetElectricalData().nominalVoltage;
1929 ElectricalUnit nominalVoltageUnit = static_cast<Bus*>(initialBus)->GetElectricalData().nominalVoltageUnit;
1930
1931 for (auto it = m_elementList.begin(); it != m_elementList.end(); it++) {
1932 Element* child = it->get();
1933
1934 if (auto line = dynamic_cast<Line*>(child)) {
1935 if (line->GetParentList()[0] && line->GetParentList()[1]) {
1936 BusElectricalData data1 = static_cast<Bus*>(line->GetParentList()[0])->GetElectricalData();
1937 BusElectricalData data2 = static_cast<Bus*>(line->GetParentList()[1])->GetElectricalData();
1938
1939 if (data1.nominalVoltage != data2.nominalVoltage ||
1940 data1.nominalVoltageUnit != data2.nominalVoltageUnit) {
1941 data1.nominalVoltage = nominalVoltage;
1942 data2.nominalVoltage = nominalVoltage;
1943 data1.nominalVoltageUnit = nominalVoltageUnit;
1944 data2.nominalVoltageUnit = nominalVoltageUnit;
1945
1946 static_cast<Bus*>(line->GetParentList()[0])->SetElectricalData(data1);
1947 static_cast<Bus*>(line->GetParentList()[1])->SetElectricalData(data2);
1948
1949 it = m_elementList.begin(); // Restart search.
1950 }
1951 }
1952 }
1953 }
1954
1955 // ValidateElementsVoltages();
1956}
1957
1958void Workspace::ValidateElementsVoltages()
1959{
1960 for (auto& child : m_elementList) {
1961 std::vector<double> nominalVoltage;
1962 std::vector<ElectricalUnit> nominalVoltageUnit;
1963 for (int i = 0; i < (int)child->GetParentList().size(); i++) {
1964 Bus* parent = static_cast<Bus*>(child->GetParentList()[i]);
1965 if (parent) {
1966 nominalVoltage.push_back(parent->GetElectricalData().nominalVoltage);
1967 nominalVoltageUnit.push_back(parent->GetElectricalData().nominalVoltageUnit);
1968 }
1969 }
1970 child->SetNominalVoltage(nominalVoltage, nominalVoltageUnit);
1971 }
1972}
1973
1974void Workspace::ResetAllVoltages()
1975{
1976 PowerFlow pf(GetElementList());
1977 pf.ResetVoltages();
1978 UpdateTextElements();
1979 Redraw();
1980}
1981
1982bool Workspace::RunPowerFlow(bool resetVoltages, bool showBusyInfo)
1983{
1984 auto simProp = m_properties->GetSimulationPropertiesData();
1985 double basePower = simProp.basePower;
1986 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
1987 basePower *= 1e6;
1988 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
1989 basePower *= 1e3;
1990
1991 // Update EMTElements
1992 for (auto& element : m_elementList) {
1993 if (auto emtElement = dynamic_cast<EMTElement*>(element.get())) {
1994 if (emtElement->IsOnline()) {
1995 emtElement->UpdateData(m_properties, true);
1996 }
1997 }
1998 }
1999 bool result = false;
2000 wxString errorMsg = "";
2001 int numIt = 0;
2002
2003 wxStopWatch sw;
2004 {
2005 wxBusyInfo* info = nullptr;
2006 if (showBusyInfo)
2007 info = new wxBusyInfo(
2008 wxBusyInfoFlags()
2009 .Parent(this)
2010 .Icon(wxIcon(wxT("..\\data\\images\\ribbon\\powerFLow32.png"), wxBITMAP_TYPE_PNG))
2011 .Title(_("<b>Calculating Power Flow</b>"))
2012 .Text(_("Please wait..."))
2013 .Foreground(*wxWHITE)
2014 .Background(*wxBLACK)
2015 .Transparency(4 * wxALPHA_OPAQUE / 5)
2016 );
2017 //wxBusyInfo info
2018 //(
2019 // wxBusyInfoFlags()
2020 // .Parent(this)
2021 // .Icon(wxIcon(wxT("..\\data\\images\\ribbon\\powerFLow32.png"), wxBITMAP_TYPE_PNG))
2022 // .Title(_("<b>Calculating Power Flow</b>"))
2023 // .Text(_("Please wait..."))
2024 // .Foreground(*wxWHITE)
2025 // .Background(*wxBLACK)
2026 // .Transparency(4 * wxALPHA_OPAQUE / 5)
2027 //);
2028
2029 PowerFlow pf(GetElementList());
2030 if (resetVoltages) pf.ResetVoltages();
2031
2032 switch (simProp.powerFlowMethod) {
2033 case GAUSS_SEIDEL: {
2034 result = pf.RunGaussSeidel(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2035 simProp.initAngle, simProp.accFator);
2036 } break;
2037 case NEWTON_RAPHSON: {
2038 result = pf.RunNewtonRaphson(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2039 simProp.initAngle, simProp.newtonInertia);
2040 } break;
2041 case GAUSS_NEWTON: {
2042 result =
2043 pf.RunGaussNewton(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2044 simProp.initAngle, simProp.accFator, simProp.gaussTolerance, simProp.newtonInertia);
2045 } break;
2046 }
2047
2048 errorMsg = pf.GetErrorMessage();
2049 numIt = pf.GetIterations();
2050
2051 if (showBusyInfo) delete info;
2052 }
2053 sw.Pause();
2054
2055 if (!result) {
2056 wxMessageDialog msgDialog(this, errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
2057 msgDialog.ShowModal();
2058 }
2059 else {
2060 m_statusBar->SetStatusText(
2061 wxString::Format(_("Power flow converge with %d iterations (%ld ms)"), numIt, sw.Time()));
2062 }
2063
2064 UpdateTextElements();
2065 UpdateHeatMap();
2066 Redraw();
2067
2068 return result;
2069}
2070
2071bool Workspace::UpdateTextElements()
2072{
2073 bool isTexturesOK = true;
2074 double basePower = m_properties->GetSimulationPropertiesData().basePower;
2075 if (m_properties->GetSimulationPropertiesData().basePowerUnit == ElectricalUnit::UNIT_kVA)
2076 basePower *= 1e3;
2077 else if (m_properties->GetSimulationPropertiesData().basePowerUnit == ElectricalUnit::UNIT_MVA)
2078 basePower *= 1e6;
2079 for (auto& text : m_textList) {
2080 text->SetFontName(m_properties->GetGeneralPropertiesData().labelFont);
2081 text->SetFontSize(m_properties->GetGeneralPropertiesData().labelFontSize);
2082 text->UpdateText(basePower);
2083 //if (!text->IsGLTextOK()) isTexturesOK = false;
2084 }
2085 return isTexturesOK;
2086}
2087
2088void Workspace::CopySelection()
2089{
2090 UpdateElementsID();
2091 std::vector<Element*> selectedElements;
2092 // The buses need to be numerated to associate the child's parents to the copies.
2093 int busNumber = 0;
2094 for (auto& element : m_elementList) {
2095 if (auto bus = dynamic_cast<Bus*>(element.get())) {
2096 auto data = bus->GetElectricalData();
2097 data.number = busNumber;
2098 bus->SetElectricalData(data);
2099 busNumber++;
2100 }
2101 if (element->IsSelected()) { selectedElements.push_back(element.get()); }
2102 }
2103 for (auto& text : m_textList) {
2104 if (text->IsSelected()) { selectedElements.push_back(text.get()); }
2105 }
2106 ElementDataObject* dataObject = new ElementDataObject(selectedElements);
2107 if (wxTheClipboard->Open()) {
2108 wxTheClipboard->SetData(dataObject);
2109 wxTheClipboard->Close();
2110 }
2111}
2112
2113bool Workspace::Paste()
2114{
2115 if (wxTheClipboard->Open()) {
2116 ElementDataObject dataObject;
2117
2118 if (wxTheClipboard->IsSupported(dataObject.GetFormat())) {
2119 if (!wxTheClipboard->GetData(dataObject)) {
2120 wxMessageDialog dialog(this, _("It was not possible to paste from clipboard."), _("Error"),
2121 wxOK | wxCENTER | wxICON_ERROR, wxDefaultPosition);
2122 dialog.ShowModal();
2123 wxTheClipboard->Close();
2124 return false;
2125 }
2126 }
2127 else {
2128 wxTheClipboard->Close();
2129 return false;
2130 }
2131 wxTheClipboard->Close();
2132
2133 UnselectAll();
2134
2135 std::vector<Element*> pastedElements;
2136 ElementsLists* elementsLists = dataObject.GetElementsLists();
2137
2138 // Paste buses (parents).
2139 auto parentList = elementsLists->parentList;
2140 std::vector<Bus*> pastedBusList; // To set new parents;
2141 for (auto it = parentList.begin(), itEnd = parentList.end(); it != itEnd; ++it) {
2142 Element* copy = (*it)->GetCopy();
2143 if (copy) {
2144 pastedElements.push_back(copy);
2145 pastedBusList.push_back(static_cast<Bus*>(copy));
2146 m_elementList.emplace_back(static_cast<PowerElement*>(copy));
2147 }
2148 }
2149
2150 // Paste other elements.
2151 auto elementLists = elementsLists->elementList;
2152 for (auto it = elementLists.begin(), itEnd = elementLists.end(); it != itEnd; ++it) {
2153 Element* copy = (*it)->GetCopy();
2154 if (copy) {
2155 // Check if is text element
2156 if (Text* text = dynamic_cast<Text*>(copy)) {
2157 // Check if element associated with the text exists.
2158 bool elementExist = false;
2159 for (auto& element : m_elementList) {
2160 if (text->GetElement() == element.get()) {
2161 elementExist = true;
2162 break;
2163 }
2164 }
2165 if (elementExist) {
2166 pastedElements.push_back(copy);
2167 m_textList.emplace_back(text);
2168 }
2169 }
2170 else {
2171 // Change the parent if copied, otherwise remove it.
2172 for (size_t j = 0; j < copy->GetParentList().size(); j++) {
2173 Bus* currentParent = static_cast<Bus*>(copy->GetParentList()[j]);
2174 if (currentParent) {
2175 int parentID = currentParent->GetID();
2176 bool parentCopied = false;
2177 for (size_t k = 0; k < pastedBusList.size(); k++) {
2178 Bus* newParent = pastedBusList[k];
2179 if (parentID == newParent->GetID()) {
2180 parentCopied = true;
2181 copy->ReplaceParent(currentParent, newParent);
2182 break;
2183 }
2184 }
2185 if (!parentCopied) copy->RemoveParent(currentParent);
2186 }
2187 }
2188
2189 pastedElements.push_back(copy);
2190 m_elementList.emplace_back(static_cast<PowerElement*>(copy));
2191 }
2192 }
2193 }
2194
2195 // Update buses childs
2196 for (auto it = pastedBusList.begin(), itEnd = pastedBusList.end(); it != itEnd; ++it) {
2197 Bus* bus = *it;
2198 std::vector<Element*> childList = bus->GetChildList();
2199 for (auto it = childList.begin(), itEnd = childList.end(); it != itEnd; ++it) {
2200 Element* currentChild = *it;
2201 int childID = currentChild->GetID();
2202 bool childCopied = false;
2203 for (int i = 0; i < (int)pastedElements.size(); i++) {
2204 Element* newChild = pastedElements[i];
2205 if (childID == newChild->GetID()) {
2206 childCopied = true;
2207 bus->ReplaceChild(currentChild, newChild);
2208 break;
2209 }
2210 }
2211 if (!childCopied) bus->RemoveChild(currentChild);
2212 }
2213 }
2214
2215 // Move elements (and nodes) to the mouse position.
2216 // The start position it's the center of the pasted objects.
2217 wxPoint2DDouble leftUpCorner, rightDownCorner;
2218 GetElementsCorners(leftUpCorner, rightDownCorner, pastedElements);
2219 wxPoint2DDouble startPosition = (leftUpCorner + rightDownCorner) / 2.0;
2220 for (auto it = pastedElements.begin(), itEnd = pastedElements.end(); it != itEnd; ++it) {
2221 Element* element = *it;
2222 element->StartMove(startPosition);
2223 element->Move(m_camera->GetMousePosition());
2224 for (int i = 0; i < (int)element->GetParentList().size(); i++) {
2225 Element* parent = element->GetParentList()[i];
2226 element->MoveNode(parent, m_camera->GetMousePosition());
2227 }
2228 }
2229 }
2230 else {
2231 wxMessageDialog dialog(this, _("It was not possible to paste from clipboard."), _("Error"),
2232 wxOK | wxCENTER | wxICON_ERROR, wxDefaultPosition);
2233 dialog.ShowModal();
2234 return false;
2235 }
2236
2237 UpdateElementsID();
2238 m_mode = WorkspaceMode::MODE_PASTE;
2239 m_statusBar->SetStatusText(_("Click to paste."));
2240 UpdateStatusBar();
2241 Redraw();
2242 return true;
2243}
2244
2245void Workspace::SaveCurrentState()
2246{
2247 // Setup current state
2248 std::vector< std::shared_ptr<PowerElement> > currentStateElementList;
2249 std::vector< std::shared_ptr<Text> > currentStateTextList;
2250
2251 GetStateListsCopy(m_elementList, m_textList, currentStateElementList, currentStateTextList);
2252
2253 // Delete all states after the current one
2254 //auto itE = m_elementListState.begin();
2255 //std::advance(itE, m_currenteState + 1);
2256 //for (; itE != m_elementListState.end(); ++itE) {
2257 // auto& elementList = *itE;
2258 // for (auto& element : elementList) delete element;
2259 // elementList.clear();
2260 //}
2261 m_elementListState.resize(m_currenteState + 1);
2262
2263 //auto itT = m_textListState.begin();
2264 //std::advance(itT, m_currenteState + 1);
2265 //for (; itT != m_textListState.end(); ++itT) {
2266 // auto& textList = *itT;
2267 // for (auto& text : textList) delete text;
2268 // textList.clear();
2269 //}
2270 m_textListState.resize(m_currenteState + 1);
2271
2272 m_currenteState++;
2273 if (m_currenteState >= m_maxStates) {
2274 m_currenteState = m_maxStates - 1;
2275 // Erase the first sate on the list
2276 //auto itE = m_elementListState.begin();
2278 //auto& elementList = *itE;
2280 //for (auto& element : elementList) delete element;
2281 //elementList.clear();
2284 //
2285 //m_elementListState.erase(itE);
2286 //m_textListState.erase(itT);
2287 m_elementListState.erase(m_elementListState.begin());
2288 m_textListState.erase(m_textListState.begin());
2289 }
2290
2291 m_elementListState.emplace_back(currentStateElementList);
2292 m_textListState.emplace_back(currentStateTextList);
2293
2294#ifdef _DEBUG
2295 wxString msg = "";
2296 wxString pointerStr;
2297 pointerStr.Printf("[%d S saved s%d] ", m_currenteState, m_elementListState.size());
2298 msg += pointerStr;
2299 for (auto& element : currentStateElementList) {
2300 pointerStr.Printf("%p ", element.get());
2301 msg += pointerStr;
2302 }
2303 msg += "\n";
2304 pointerStr.Printf("[%d S curr s%d] ", m_currenteState, m_elementListState.size());
2305 msg += pointerStr;
2306 for (auto& element : m_elementList) {
2307 pointerStr.Printf("%p ", element.get());
2308 msg += pointerStr;
2309 }
2310 msg += "\n";
2311 //m_debugFrame->AppendDebugMessage(msg);
2312#endif
2313}
2314
2315void Workspace::SetNextState()
2316{
2317 m_currenteState++;
2318 if (m_currenteState >= 0 &&
2319 static_cast<size_t>(m_currenteState) < m_elementListState.size() &&
2320 static_cast<size_t>(m_currenteState) < m_textListState.size()) {
2321 //m_elementList = m_elementListState[m_currenteState];
2322 //m_textList = m_textListState[m_currenteState];
2323 GetStateListsCopy(m_elementListState[m_currenteState], m_textListState[m_currenteState], m_elementList, m_textList);
2324
2325#ifdef _DEBUG
2326#ifdef SHOW_DEBUG_PANEL
2327 wxString msg = "";
2328 wxString pointerStr;
2329 pointerStr.Printf("[%d N curr s%d] ", m_currenteState, m_elementListState.size());
2330 msg += pointerStr;
2331 for (Element* element : m_elementList) {
2332 pointerStr.Printf("%p ", element);
2333 msg += pointerStr;
2334 }
2335 msg += "\n";
2336 //m_debugFrame->AppendDebugMessage(msg);
2337#endif
2338#endif
2339
2340 UpdateTextElements();
2341 Redraw();
2342 }
2343 else {
2344 m_currenteState--;
2345 }
2346}
2347
2348void Workspace::SetPreviousState()
2349{
2350 m_currenteState--;
2351 if (m_currenteState >= 0) {
2352 //m_elementList = m_elementListState[m_currenteState];
2353 //m_textList = m_textListState[m_currenteState];
2354 GetStateListsCopy(m_elementListState[m_currenteState], m_textListState[m_currenteState], m_elementList, m_textList);
2355
2356#ifdef _DEBUG
2357#ifdef SHOW_DEBUG_PANEL
2358 wxString msg = "";
2359 wxString pointerStr;
2360 pointerStr.Printf("[%d P curr s%d] ", m_currenteState, m_elementListState.size());
2361 msg += pointerStr;
2362 for (Element* element : m_elementListState[m_currenteState]) {
2363 pointerStr.Printf("%p ", element);
2364 msg += pointerStr;
2365 }
2366 msg += "\n";
2367 pointerStr.Printf("[%d P list s%d] ", m_currenteState, m_elementListState.size());
2368 msg += pointerStr;
2369 for (Element* element : m_elementList) {
2370 pointerStr.Printf("%p ", element);
2371 msg += pointerStr;
2372 }
2373 msg += "\n";
2374 //m_debugFrame->AppendDebugMessage(msg);
2375#endif
2376#endif
2377
2378 UpdateTextElements();
2379 Redraw();
2380 }
2381 else {
2382 m_currenteState++;
2383 }
2384}
2385
2386void Workspace::UnselectAll()
2387{
2388 for (auto& element : m_elementList) {
2389 element->SetSelected(false);
2390 }
2391 for (auto& text : m_textList) {
2392 text->SetSelected(false);
2393 }
2394}
2395
2396void Workspace::EnableHeatMap(const bool& enable)
2397{
2398 m_showHM = enable;
2399 if (m_showHM) {
2400 UpdateHeatMap();
2401 }
2402 Redraw();
2403}
2404
2405void Workspace::UpdateElementsID()
2406{
2407 int id = 0;
2408 for (auto& element : m_elementList) {
2409 element->SetID(id);
2410 id++;
2411 }
2412 for (auto& text : m_textList) {
2413 text->SetID(id);
2414 id++;
2415 }
2416}
2417void Workspace::OnTimer(wxTimerEvent& event)
2418{
2419 if (m_tipWindow) {
2420 m_tipWindow->Close();
2421 m_tipWindow = nullptr;
2422 }
2423 if (m_mode == WorkspaceMode::MODE_EDIT) {
2424 for (auto& element : m_elementList) {
2425 if (element->Contains(m_camera->GetMousePosition())) {
2426 wxString tipText = element->GetTipText();
2427 if (!tipText.IsEmpty()) {
2428 m_tipWindow = new wxTipWindow(this, tipText, 10000, &m_tipWindow);
2429 // Creates a very tiny bounding rect to remove the tip on any mouse movement.
2430 m_tipWindow->SetBoundingRect(wxRect(wxGetMousePosition(), wxSize(1, 1)));
2431 break;
2432 }
2433 }
2434 }
2435 }
2436
2437 m_timer->Stop();
2438}
2439
2440void Workspace::SetTextList(const std::vector< std::shared_ptr<Text> >& textList)
2441{
2442 //m_textList.clear();
2443 //for (auto it = textList.begin(), itEnd = textList.end(); it != itEnd; ++it) m_textList.push_back(*it);
2444 m_textList = std::move(textList);
2445
2446 UpdateTextElements();
2447}
2448
2449void Workspace::SetName(wxString name)
2450{
2451 m_name = name;
2452#ifdef _DEBUG
2453#ifdef SHOW_DEBUG_PANEL
2454 m_debugFrame->SetTitle(_("Debug window: ") + m_name);
2455#endif
2456#endif
2457}
2458
2459void Workspace::SetElementList(std::vector< std::shared_ptr<PowerElement> > elementList)
2460{
2461 m_elementList = std::move(elementList);
2462}
2463
2464void Workspace::SetElementList(std::vector<Element*> elementList)
2465{
2466 m_elementList.clear();
2467 for (auto it = elementList.begin(), itEnd = elementList.end(); it != itEnd; ++it)
2468 m_elementList.emplace_back(static_cast<PowerElement*>(*it));
2469}
2470
2471void Workspace::OnIdle(wxIdleEvent& event)
2472{
2473 /*
2474 // The OpenGL element (m_glCanvas) must be completely initialized (showed) to draw properly the textures and init glew.
2475 // TODO(?): Find other solution to text displayed wrong on opened file.
2476 if (m_justOpened) {
2477 if (UpdateTextElements()) m_justOpened = false;
2478 if (glewInit() != GLEW_OK)
2479 wxMessageBox("Erro ao iniciar glew ='(");
2480 else
2481 {
2482 // Load and create shader
2483 m_basicShader = new Shader("..//data//shaders//Basic.shader");
2484 m_hmLabelShader = new Shader("..//data//shaders//HMLabel.shader");
2485 float limits[2] = { 1.05, 0.95 };
2486 m_hmPlane = new HMPlane(m_basicShader, m_hmLabelShader, m_width, m_height, limits);
2487 }
2488
2489 Redraw();
2490 }
2491 */
2492 if (m_justOpened) {
2493 m_justOpened = false;
2494 double limits[2] = { 1.05, 0.95 };
2495 m_hmPlane = new HMPlane(m_width, m_height, limits);
2496
2497 Redraw();
2498 }
2499}
2500
2501std::vector<Element*> Workspace::GetAllElements() const
2502{
2503 std::vector<Element*> allElements;
2504
2505 for (auto& element : m_elementList) allElements.push_back(element.get());
2506 for (auto& text : m_textList) allElements.push_back(text.get());
2507
2508 return allElements;
2509}
2510
2511bool Workspace::RunFault()
2512{
2513 auto simProp = m_properties->GetSimulationPropertiesData();
2514 double basePower = simProp.basePower;
2515 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
2516 basePower *= 1e6;
2517 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
2518 basePower *= 1e3;
2519
2520 Fault fault(GetElementList());
2521 bool result = fault.RunFaultCalculation(basePower);
2522 if (!result) {
2523 wxMessageDialog msgDialog(this, fault.GetErrorMessage(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
2524 msgDialog.ShowModal();
2525 }
2526
2527 UpdateTextElements();
2528 Redraw();
2529
2530 return result;
2531}
2532
2533std::vector<Element*> Workspace::GetElementList() const
2534{
2535 std::vector<Element*> elementList;
2536 for (auto& element : m_elementList) elementList.push_back(element.get());
2537 return elementList;
2538}
2539
2540bool Workspace::RunSCPower()
2541{
2542 Fault fault(GetElementList());
2543 bool result = fault.RunSCPowerCalcutation(100e6);
2544 if (!result) {
2545 wxMessageDialog msgDialog(this, fault.GetErrorMessage(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
2546 msgDialog.ShowModal();
2547 }
2548
2549 UpdateTextElements();
2550 Redraw();
2551
2552 return result;
2553}
2554
2555bool Workspace::RunStability()
2556{
2557 // Run power flow before stability.
2558 RunPowerFlow();
2559
2560 Electromechanical stability(this, GetElementList(), m_properties->GetSimulationPropertiesData());
2561 wxStopWatch sw;
2562 bool result = stability.RunStabilityCalculation();
2563#ifdef _DEBUG
2564#ifdef SHOW_DEBUG_PANEL
2565 m_debugFrame->AppendDebugMessage(stability.GetDebugMessage());
2566#endif
2567#endif // _DEBUG
2568
2569 sw.Pause();
2570 if (!result) {
2571 wxMessageDialog msgDialog(this, stability.GetErrorMessage(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
2572 msgDialog.ShowModal();
2573 }
2574 m_stabilityTimeVector.clear();
2575 m_stabilityTimeVector = stability.GetTimeVector();
2576
2577 // Run power flow after stability.
2578 RunPowerFlow();
2579
2580 wxMessageDialog msgDialog(
2581 this,
2582 wxString::Format(_("The program took %ld ms to run this system.\nDo you wish to open the stability graphics?"),
2583 sw.Time()),
2584 _("Question"), wxYES_NO | wxCENTRE | wxICON_QUESTION);
2585 if (msgDialog.ShowModal() == wxID_YES) {
2586 std::vector<ElementPlotData> plotDataList;
2587 for (auto& element : m_elementList) {
2588 ElementPlotData plotData;
2589 if (element->GetPlotData(plotData)) plotDataList.push_back(plotData);
2590 }
2591#ifdef SHOW_SIMULATION_PARAMETERS
2592 ElementPlotData plotData;
2593 plotData.SetName(_("Simulation parameters"));
2594 plotData.SetCurveType(ElementPlotData::CurveType::CT_TEST);
2595 plotData.AddData(stability.GetIterationVector(), _("Iterations number"));
2596 plotDataList.push_back(plotData);
2597#endif
2598 ChartView* cView = new ChartView(this, plotDataList, m_stabilityTimeVector, m_properties->GetGeneralPropertiesData().plotLib);
2599 cView->Show();
2600 }
2601
2602 return result;
2603}
2604void Workspace::OnMiddleDoubleClick(wxMouseEvent& event)
2605{
2606 Fit();
2607 event.Skip();
2608}
2609
2610bool Workspace::RunStaticStudies()
2611{
2612 bool pfStatus, faultStatus, scStatus, harmStatus;
2613 pfStatus = faultStatus = scStatus = harmStatus = false;
2614
2615 bool runHarmDistortion = m_properties->GetSimulationPropertiesData().harmDistortionAfterPowerFlow;
2616
2617 pfStatus = RunPowerFlow(runHarmDistortion);
2618
2619 if (m_properties->GetSimulationPropertiesData().faultAfterPowerFlow) {
2620 if (pfStatus) faultStatus = RunFault();
2621 }
2622 else {
2623 faultStatus = true;
2624 }
2625
2626 if (m_properties->GetSimulationPropertiesData().scPowerAfterPowerFlow) {
2627 if (pfStatus) scStatus = RunSCPower();
2628 }
2629 else {
2630 scStatus = true;
2631 }
2632
2633 if (runHarmDistortion) {
2634 if (pfStatus) harmStatus = RunHarmonicDistortion(false);
2635 }
2636 else {
2637 harmStatus = true;
2638 }
2639
2640 if (pfStatus && faultStatus && scStatus && harmStatus) return true;
2641
2642 return false;
2643}
2644
2645bool Workspace::RunHarmonicDistortion(bool runPowerFlowBefore)
2646{
2647 auto simProp = m_properties->GetSimulationPropertiesData();
2648 double basePower = simProp.basePower;
2649 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
2650 basePower *= 1e6;
2651 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
2652 basePower *= 1e3;
2653 if (runPowerFlowBefore) {
2654 if (!RunPowerFlow(true)) return false;
2655 }
2656
2657 bool hasEMTElement = false;
2658 for (auto& element : m_elementList) {
2659 if (auto emtElement = dynamic_cast<EMTElement*>(element.get())) {
2660 if (emtElement->IsOnline()) hasEMTElement = true;
2661 }
2662 }
2663
2664 HarmLoadConnection loadConnection = simProp.harmLoadConnection;
2665
2666 PowerQuality pq(GetElementList());
2667 bool result = pq.CalculateDistortions(basePower, loadConnection);
2668
2669 // If has EMT element, repeat the Power Flow and Harmonics calculation untion DHT converge.
2670 if (hasEMTElement && result) {
2671 wxBusyInfo info(
2672 wxBusyInfoFlags()
2673 .Parent(this)
2674 .Icon(wxIcon(wxT("..\\data\\images\\ribbon\\harmDist32.png"), wxBITMAP_TYPE_PNG))
2675 .Title(_("<b>Calculating Harmonic Flow</b>"))
2676 .Text(_("Please wait..."))
2677 .Foreground(*wxWHITE)
2678 .Background(*wxBLACK)
2679 .Transparency(4 * wxALPHA_OPAQUE / 5)
2680 );
2681 std::vector<double> thdList;
2682 for (auto const& bus : pq.GetBusList())
2683 thdList.emplace_back(bus->GetElectricalData().thd);
2684 double error = 1e3;
2685 while (error > 1e-3) {
2686 // Run Power Flow
2687 if (!RunPowerFlow(false, false)) return false;
2688
2689 // Run Harmonic Distortion
2690 bool result = pq.CalculateDistortions(basePower, loadConnection);
2691 if (!result) break;
2692
2693 // Calculate error
2694 int i = 0;
2695 for (auto const& bus : pq.GetBusList()) {
2696 double errorBus = std::abs(bus->GetElectricalData().thd - thdList[i]);
2697 if (i == 0)
2698 error = errorBus;
2699 else if (errorBus > error)
2700 error = errorBus;
2701 thdList[i] = bus->GetElectricalData().thd;
2702 i++;
2703 }
2704 }
2705 }
2706
2707 if (!result) {
2708 wxMessageDialog msgDialog(this, pq.GetErrorMessage(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
2709 msgDialog.ShowModal();
2710 }
2711 else {
2712 UpdateTextElements();
2713 Redraw();
2714 }
2715
2716 return result;
2717}
2718
2719bool Workspace::RunFrequencyResponse()
2720{
2721 // Get bus list
2722 std::vector<Bus*> busList;
2723 for (auto& element : m_elementList) {
2724 if (Bus* bus = dynamic_cast<Bus*>(element.get())) { busList.push_back(bus); }
2725 }
2726
2727 auto data = m_properties->GetFreqRespData();
2728
2729 FrequencyResponseForm frForm(this, busList, data.injBusNumber, data.initFreq, data.finalFreq, data.stepFreq);
2730
2731 if (frForm.ShowModal() == wxID_OK) {
2732 data.initFreq = frForm.GetInitFreq();
2733 data.finalFreq = frForm.GetEndFreq();
2734 data.stepFreq = frForm.GetStepFreq();
2735 data.injBusNumber = frForm.GetInjBusNumber();
2736 m_properties->SetFreqRespData(data);
2737 }
2738 else
2739 return false;
2740
2741 auto simProp = m_properties->GetSimulationPropertiesData();
2742 double basePower = simProp.basePower;
2743 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
2744 basePower *= 1e6;
2745 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
2746 basePower *= 1e3;
2747 PowerQuality pq(GetElementList());
2748 bool result = pq.CalculateFrequencyResponse(simProp.stabilityFrequency, data.initFreq, data.finalFreq,
2749 data.stepFreq, data.injBusNumber, basePower, simProp.harmLoadConnection);
2750
2751 wxMessageDialog msgDialog(
2752 this, wxString::Format(_("Calculations done.\nDo you wish to open the frequency response graphics?")),
2753 _("Question"), wxYES_NO | wxCENTRE | wxICON_QUESTION);
2754 if (msgDialog.ShowModal() == wxID_YES) {
2755 std::vector<ElementPlotData> plotDataList;
2756 for (auto& element : m_elementList) {
2757 ElementPlotData plotData;
2758 if (element->GetPlotData(plotData, PlotStudy::FREQRESPONSE)) plotDataList.push_back(plotData);
2759 }
2760
2761 ChartView* cView = new ChartView(this, plotDataList, pq.GetFrequencies(), m_properties->GetGeneralPropertiesData().plotLib);
2762 cView->Show();
2763 }
2764
2765 UpdateTextElements();
2766 Redraw();
2767
2768 return result;
2769}
2770void Workspace::OnResize(wxSizeEvent& event)
2771{
2772 //m_width = static_cast<float>(m_glCanvas->GetSize().x) - 1.0f;
2773 //m_height = static_cast<float>(m_glCanvas->GetSize().y) - 1.0f;
2774 //
2775 //if (m_hmPlane && m_showHM) {
2776 // m_hmPlane->Resize(m_width, m_height);
2777 // m_showHMTimer = true;
2778 // m_timerHeatMap->Start();
2779 //}
2780 //
2781 //event.Skip();
2782 m_width = static_cast<float>(GetSize().x) - 1.0f;
2783 m_height = static_cast<float>(GetSize().y) - 1.0f;
2784
2785 if (m_hmPlane && m_showHM) {
2786 m_hmPlane->ResizeDC(m_width, m_height);
2787 m_showHMTimer = true;
2788 m_timerHeatMap->Start();
2789 }
2790
2791 event.Skip();
2792}
2793void Workspace::OnHeatMapTime(wxTimerEvent& event)
2794{
2795 if (m_showHMTimer) {
2796 UpdateHeatMap();
2797 Redraw();
2798 m_showHMTimer = false;
2799 }
2800}
@ ID_LINE_REMOVE_NODE
Definition Element.h:77
@ ID_DELETE
Definition Element.h:80
@ ID_ROTATE_CLOCK
Definition Element.h:78
@ ID_EDIT_ELEMENT
Definition Element.h:75
@ ID_ROTATE_COUNTERCLOCK
Definition Element.h:79
@ ID_LINE_ADD_NODE
Definition Element.h:76
ElectricalUnit
Electrical units.
Node for power elements. All others power elements are connected through this.
Definition Bus.h:86
Class responsible for the correct visualization of the elements on screen.
Definition Camera.h:31
This class is responsible to manage the charts generated in the transient electromechanical studies.
Definition ChartView.h:49
Element to connect ATP-EMTP.
Definition EMTElement.h:65
Calculates the electromechanical transient based on disturbances (e.g. system fault).
Class to store the elements in the clipboard.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:112
virtual Element * GetCopy()
Get a the element copy.
Definition Element.h:261
virtual std::vector< Element * > GetParentList() const
Get the parent list.
Definition Element.h:559
virtual int GetID() const
Get the element ID.
Definition Element.h:271
virtual void UpdateNodes()
Update the nodes according to the parents. If a parent is removed, use this method.
Definition Element.h:356
virtual std::vector< wxPoint2DDouble > GetPointList() const
Get the list of points that connect the element to bus.
Definition Element.h:231
void SetSelected(bool selected=true)
Set element selection.
Definition Element.h:144
wxPoint2DDouble GetPosition() const
Get the element position.
Definition Element.h:186
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 Element.h:239
virtual void CalculateBoundaries(wxPoint2DDouble &leftUp, wxPoint2DDouble &rightBottom) const
Calculate the element boundaries.
Definition Element.cpp:478
virtual std::vector< Element * > GetChildList() const
Get the Child list.
Definition Element.h:564
virtual void RemoveChild(Element *child)
Remove a child from the list.
Definition Element.cpp:567
virtual void ReplaceParent(Element *oldParent, Element *newParent)
Replace a parent.
Definition Element.cpp:559
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
Definition Element.cpp:329
virtual void MoveNode(Element *parent, wxPoint2DDouble position)
Move a node. StartMove(wxPoint2DDouble position) before start moving.
Definition Element.h:346
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
Definition Element.cpp:27
virtual void RemoveParent(Element *parent)
Remove a parent.
Definition Element.h:371
virtual void SetID(int id)
Set the element ID.
Definition Element.h:266
virtual wxString GetTipText() const
Get the tip text.
Definition Element.h:295
virtual void Move(wxPoint2DDouble position)
Move the element other position.
Definition Element.cpp:335
virtual bool Contains(wxPoint2DDouble position) const =0
Checks if the element contains a position.
bool IsSelected() const
Checks if the element is selected.
Definition Element.h:201
virtual void ReplaceChild(Element *oldChild, Element *newChild)
Replace a child from the list.
Definition Element.cpp:575
virtual bool ShowForm(wxWindow *parent, Element *element)
Show element data form.
Definition Element.h:587
virtual void RotateNode(Element *parent, bool clockwise=true)
Rotate a node.
Definition Element.h:384
virtual void Rotate(bool clockwise=true)
Rotate the element.
Definition Element.h:316
Calculate the fault of the system and update the elements data.
Definition Fault.h:31
Save and opens the projects created on disk.
Definition FileHanding.h:43
Power line element.
Definition Line.h:64
Abstract class of power elements.
Calculate the power flow.
Definition PowerFlow.h:36
Responsible for the power quality calculations.
General and simulation data manager.
Element that shows power element informations in workspace.
Definition Text.h:70
virtual Element * GetCopy()
Get a the element copy.
Definition Text.cpp:1146
virtual bool Contains(wxPoint2DDouble position) const
Checks if the element contains a position.
Definition Text.cpp:62
virtual bool Intersects(wxRect2DDouble rect) const
Check if the element's rect intersects other rect.
Definition Text.cpp:242
virtual void Rotate(bool clockwise=true)
Rotate the element.
Definition Text.cpp:254