Power System Platform  2026w11a-beta
Loading...
Searching...
No Matches
ControlEditor.cpp
1/*
2 * Copyright (C) 2017 Thales Lima Oliveira <thales@ufu.br>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "ControlEditor.h"
19
20#include "../elements/controlElement/ConnectionLine.h"
21#include "../elements/controlElement/Constant.h"
22#include "../elements/controlElement/ControlElement.h"
23#include "../elements/controlElement/Divider.h"
24#include "../elements/controlElement/Exponential.h"
25#include "../elements/controlElement/Gain.h"
26#include "../elements/controlElement/Limiter.h"
27#include "../elements/controlElement/MathExpression.h"
28#include "../elements/controlElement/MathOperation.h"
29#include "../elements/controlElement/Multiplier.h"
30#include "../elements/controlElement/RateLimiter.h"
31#include "../elements/controlElement/Sum.h"
32#include "../elements/controlElement/TransferFunction.h"
33#include "../elements/controlElement/ControlElementContainer.h"
34#include "../elements/controlElement/ControlElementSolver.h"
35
36#ifdef USING_WX_3_0_X
37#include "utils/DegreesAndRadians.h"
38#endif
39#include "../utils/Camera.h"
40#include "../utils/FileHanding.h"
41#include "../utils/Path.h"
42
43#include "ChartView.h"
44#include "../utils/ElementPlotData.h"
45
46ControlElementButton::ControlElementButton(wxWindow* parent, wxString label, wxImage image, wxWindowID id)
47 : wxWindow(parent, id)
48{
49 SetBackgroundColour(*wxWHITE);
50 m_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
51 m_label = label;
52 m_image = image;
53 m_imageSize = wxSize(image.GetWidth(), image.GetHeight());
54
55 // Calculate label size.
56 wxScreenDC dc;
57 dc.SetFont(m_font);
58 wxSize textSize = dc.GetTextExtent(label);
59
60 int buttonWidth = 0;
61 if (textSize.GetWidth() > m_imageSize.GetWidth()) {
62 buttonWidth = textSize.GetWidth();
63 m_imagePosition = wxPoint((buttonWidth - m_imageSize.GetWidth()) / 2 + m_borderSize, m_borderSize);
64 m_labelPosition = wxPoint(m_borderSize, m_imageSize.GetHeight() + m_borderSize);
65 }
66 else {
67 buttonWidth = m_imageSize.GetWidth();
68 m_imagePosition = wxPoint(m_borderSize, m_borderSize);
69 m_labelPosition =
70 wxPoint((buttonWidth - textSize.GetWidth()) / 2 + m_borderSize, m_imageSize.GetHeight() + m_borderSize);
71 }
72 m_buttonSize =
73 wxSize(buttonWidth + 2 * m_borderSize, textSize.GetHeight() + m_imageSize.GetHeight() + 2 * m_borderSize);
74 SetMinSize(m_buttonSize + wxSize(m_borderSize, m_borderSize));
75
76 // Events.
77 Bind(wxEVT_PAINT, &ControlElementButton::OnPaint, this);
78 Bind(wxEVT_ENTER_WINDOW, &ControlElementButton::OnMouseEnter, this);
79 Bind(wxEVT_LEAVE_WINDOW, &ControlElementButton::OnMouseLeave, this);
80 Bind(wxEVT_LEFT_DOWN, &ControlElementButton::OnLeftClickDown, this);
81 Bind(wxEVT_LEFT_UP, &ControlElementButton::OnLeftClickUp, this);
82}
83
84ControlElementButton::~ControlElementButton() {}
85void ControlElementButton::OnPaint(wxPaintEvent& event)
86{
87 wxPaintDC dc(this);
88 wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
89 if (gc) {
90 if (m_mouseAbove) {
91 if (m_selected) {
92 gc->SetPen(wxPen(wxColour(0, 125, 255, 255), m_borderSize - 1));
93 gc->SetBrush(wxBrush(wxColour(0, 125, 255, 100)));
94 }
95 else {
96 gc->SetPen(*wxTRANSPARENT_PEN);
97 gc->SetBrush(wxBrush(wxColour(0, 125, 255, 70)));
98 }
99 gc->DrawRectangle(m_borderSize / 2, m_borderSize / 2, m_buttonSize.GetWidth(), m_buttonSize.GetHeight());
100 }
101 gc->DrawBitmap(gc->CreateBitmapFromImage(m_image), m_imagePosition.x, m_imagePosition.y, m_imageSize.GetWidth(),
102 m_imageSize.GetHeight());
103 gc->SetFont(m_font, *wxBLACK);
104 gc->DrawText(m_label, m_labelPosition.x, m_labelPosition.y);
105 delete gc;
106 }
107}
108
109void ControlElementButton::OnMouseEnter(wxMouseEvent& event)
110{
111 m_mouseAbove = true;
112 Refresh();
113 event.Skip();
114}
115
116void ControlElementButton::OnMouseLeave(wxMouseEvent& event)
117{
118 m_mouseAbove = false;
119 Refresh();
120 event.Skip();
121}
122
123void ControlElementButton::OnLeftClickDown(wxMouseEvent& event)
124{
125 m_selected = true;
126 Refresh();
127 event.Skip();
128}
129
130void ControlElementButton::OnLeftClickUp(wxMouseEvent& event)
131{
132 m_selected = false;
133 Refresh();
134 event.Skip();
135}
136
137ControlEditor::ControlEditor(wxWindow* parent, int ioflags) : ControlEditorBase(parent)
138{
139 BuildControlElementPanel();
140 //m_glContext = new wxGLContext(m_glCanvas, sharedGLContext);
141 //m_glContext->SetCurrent(*m_glCanvas);
142 m_camera = new Camera();
143 m_selectionRect = wxRect2DDouble(0, 0, 0, 0);
144 m_cePanel->SetBackgroundColour(wxColour(255, 255, 255));
145 m_cePanel->SetBackgroundStyle(wxBG_STYLE_PAINT); // To allow wxBufferedPaintDC works properly.
146 // m_camera->SetScale(1.2);
147 m_ioFlags = ioflags;
148
149 BuildColourList();
150}
151ControlEditor::~ControlEditor()
152{
153 // m_tfButton->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(ControlEditor::LeftClickDown), m_tfButton, this);
154}
155
156void ControlEditor::BuildControlElementPanel()
157{
158 m_panelControlElements->SetDoubleBuffered(true);
159 wxWrapSizer* wrapSizer = new wxWrapSizer();
160 m_panelControlElements->SetSizer(wrapSizer);
161
162 wxFileName exeFileName(wxStandardPaths::Get().GetExecutablePath());
163 //wxString exePath = exeFileName.GetPath();
164
166 m_panelControlElements, _("In/Out"),
167 wxImage(Paths::GetDataPath() + "/images/control/io.png"),
168 static_cast<int>(ControlElementButtonID::ID_IO));
169 wrapSizer->Add(ioButton, 0, wxALL, 5);
170 ioButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
171
173 m_panelControlElements, _("Transfer fcn"),
174 wxImage(Paths::GetDataPath() + "/images/control/transferFunc.png"),
175 static_cast<int>(ControlElementButtonID::ID_TF));
176 wrapSizer->Add(tfButton, 0, wxALL, 5);
177 tfButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
178
180 m_panelControlElements, _("Sum"),
181 wxImage(Paths::GetDataPath() + "/images/control/sum.png"),
182 static_cast<int>(ControlElementButtonID::ID_SUM));
183 wrapSizer->Add(sumButton, 0, wxALL, 5);
184 sumButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
185
187 m_panelControlElements, _("Constant"),
188 wxImage(Paths::GetDataPath() + "/images/control/value.png"),
189 static_cast<int>(ControlElementButtonID::ID_CONST));
190 wrapSizer->Add(constButton, 0, wxALL, 5);
191 constButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
192
194 m_panelControlElements, _("Gain"),
195 wxImage(Paths::GetDataPath() + "/images/control/gain.png"),
196 static_cast<int>(ControlElementButtonID::ID_GAIN));
197 wrapSizer->Add(gainButton, 0, wxALL, 5);
198 gainButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
199
201 m_panelControlElements, _("Limiter"),
202 wxImage(Paths::GetDataPath() + "/images/control/limiter.png"),
203 static_cast<int>(ControlElementButtonID::ID_LIMITER));
204 wrapSizer->Add(limButton, 0, wxALL, 5);
205 limButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
206
207 ControlElementButton* rateLimButton = new ControlElementButton(
208 m_panelControlElements, _("Rate limiter"),
209 wxImage(Paths::GetDataPath() + "/images/control/rateLimiter.png"),
210 static_cast<int>(ControlElementButtonID::ID_RATELIM));
211 wrapSizer->Add(rateLimButton, 0, wxALL, 5);
212 rateLimButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
213
215 m_panelControlElements, _("Multiplier"),
216 wxImage(Paths::GetDataPath() + "/images/control/mult.png"),
217 static_cast<int>(ControlElementButtonID::ID_MULT));
218 wrapSizer->Add(multButton, 0, wxALL, 5);
219 multButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
220
222 m_panelControlElements, _("Divider"),
223 wxImage(Paths::GetDataPath() + "/images/control/div.png"),
224 static_cast<int>(ControlElementButtonID::ID_MATH_DIV));
225 wrapSizer->Add(divButton, 0, wxALL, 5);
226 divButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
227
228 ControlElementButton* mathExprButton = new ControlElementButton(
229 m_panelControlElements, _("Math Expression"),
230 wxImage(Paths::GetDataPath() + "/images/control/mathExpr.png"),
231 static_cast<int>(ControlElementButtonID::ID_MATH_EXPR));
232 wrapSizer->Add(mathExprButton, 0, wxALL, 5);
233 mathExprButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
234
236 m_panelControlElements, _("Exponential"),
237 wxImage(Paths::GetDataPath() + "/images/control/sat.png"),
238 static_cast<int>(ControlElementButtonID::ID_EXP));
239 wrapSizer->Add(satButton, 0, wxALL, 5);
240 satButton->Bind(wxEVT_LEFT_DOWN, &ControlEditor::LeftClickDown, this);
241}
242
243void ControlEditor::LeftClickDown(wxMouseEvent& event)
244{
245 AddElement(static_cast<ControlElementButtonID>(event.GetId()));
246 event.Skip();
247}
248
249//void ControlEditor::SetViewport()
250//{
251// glClearColor(1.0, 1.0, 1.0, 1.0); // White background.
252// glClear(GL_COLOR_BUFFER_BIT);
253// glDisable(GL_DEPTH_TEST);
254// glDisable(GL_TEXTURE_2D);
255// glEnable(GL_COLOR_MATERIAL);
256// glEnable(GL_BLEND);
257// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
258// glEnable(GL_LINE_SMOOTH);
259//
260// double width = static_cast<double>(m_glCanvas->GetSize().x) - 1;
261// double height = static_cast<double>(m_glCanvas->GetSize().y) - 1;
262//
263// // Viewport fit the screen.
264// glViewport(0, 0, width, height);
265//
266// glMatrixMode(GL_PROJECTION);
267// glLoadIdentity();
268// gluOrtho2D(0.0, width, height, 0.0);
269//
270// glMatrixMode(GL_MODELVIEW);
271// glLoadIdentity();
272//}
273
274void ControlEditor::AddElement(ControlElementButtonID id)
275{
276 switch (id) {
277 case ControlElementButtonID::ID_IO: {
278 m_mode = ControlEditorMode::MODE_INSERT;
279 auto io = std::make_shared<IOControl>(m_ioFlags, GetNextID());
280 m_elementList.push_back(io);
281 } break;
282 case ControlElementButtonID::ID_TF: {
283 m_mode = ControlEditorMode::MODE_INSERT;
284 auto tf = std::make_shared<TransferFunction>(GetNextID());
285 m_elementList.push_back(tf);
286 } break;
287 case ControlElementButtonID::ID_SUM: {
288 m_mode = ControlEditorMode::MODE_INSERT;
289 auto sum = std::make_shared<Sum>(GetNextID());
290 m_elementList.push_back(sum);
291 } break;
292 case ControlElementButtonID::ID_CONST: {
293 m_mode = ControlEditorMode::MODE_INSERT;
294 auto constant = std::make_shared<Constant>(GetNextID());
295 m_elementList.push_back(constant);
296 } break;
297 case ControlElementButtonID::ID_LIMITER: {
298 m_mode = ControlEditorMode::MODE_INSERT;
299 auto limiter = std::make_shared<Limiter>(GetNextID());
300 m_elementList.push_back(limiter);
301 } break;
302 case ControlElementButtonID::ID_GAIN: {
303 m_mode = ControlEditorMode::MODE_INSERT;
304 auto gain = std::make_shared<Gain>(GetNextID());
305 m_elementList.push_back(gain);
306 } break;
307 case ControlElementButtonID::ID_MULT: {
308 m_mode = ControlEditorMode::MODE_INSERT;
309 auto mult = std::make_shared<Multiplier>(GetNextID());
310 m_elementList.push_back(mult);
311 } break;
312 case ControlElementButtonID::ID_EXP: {
313 m_mode = ControlEditorMode::MODE_INSERT;
314 auto exp = std::make_shared<Exponential>(GetNextID());
315 m_elementList.push_back(exp);
316 } break;
317 case ControlElementButtonID::ID_RATELIM: {
318 m_mode = ControlEditorMode::MODE_INSERT;
319 auto rateLim = std::make_shared<RateLimiter>(GetNextID());
320 m_elementList.push_back(rateLim);
321 } break;
322 case ControlElementButtonID::ID_MATH_DIV: {
323 m_mode = ControlEditorMode::MODE_INSERT;
324 auto divider = std::make_shared<Divider>(GetNextID());
325 m_elementList.push_back(divider);
326 } break;
327 case ControlElementButtonID::ID_MATH_EXPR: {
328 m_mode = ControlEditorMode::MODE_INSERT;
329 auto mathExpr = std::make_shared<MathExpression>(GetNextID());
330 m_elementList.push_back(mathExpr);
331 } break;
332 }
333}
334
335void ControlEditor::OnPaint(wxPaintEvent& event)
336{
337 //wxPaintDC dc(m_glCanvas);
339 //SetViewport();
340 //
342 //glScaled(m_camera->GetScale(), m_camera->GetScale(), 0.0); // Scale
343 //glTranslated(m_camera->GetTranslation().m_x, m_camera->GetTranslation().m_y, 0.0); // Translation
344 //
345 //for(auto it = m_connectionList.begin(), itEnd = m_connectionList.end(); it != itEnd; ++it) {
346 // ConnectionLine* line = *it;
347 // line->Draw(m_camera->GetTranslation(), m_camera->GetScale());
348 //}
349 //
350 //for(auto it = m_elementList.begin(), itEnd = m_elementList.end(); it != itEnd; ++it) {
351 // Element* element = *it;
352 // element->Draw(m_camera->GetTranslation(), m_camera->GetScale());
353 //}
354 //
356 //glLineWidth(1.0);
357 //glColor4d(0.0, 0.5, 1.0, 1.0);
358 //glBegin(GL_LINE_LOOP);
359 //glVertex2d(m_selectionRect.m_x, m_selectionRect.m_y);
360 //glVertex2d(m_selectionRect.m_x, m_selectionRect.m_y + m_selectionRect.m_height);
361 //glVertex2d(m_selectionRect.m_x + m_selectionRect.m_width, m_selectionRect.m_y + m_selectionRect.m_height);
362 //glVertex2d(m_selectionRect.m_x + m_selectionRect.m_width, m_selectionRect.m_y);
363 //glEnd();
364 //glColor4d(0.0, 0.5, 1.0, 0.3);
365 //glBegin(GL_QUADS);
366 //glVertex2d(m_selectionRect.m_x, m_selectionRect.m_y);
367 //glVertex2d(m_selectionRect.m_x, m_selectionRect.m_y + m_selectionRect.m_height);
368 //glVertex2d(m_selectionRect.m_x + m_selectionRect.m_width, m_selectionRect.m_y + m_selectionRect.m_height);
369 //glVertex2d(m_selectionRect.m_x + m_selectionRect.m_width, m_selectionRect.m_y);
370 //glEnd();
371 //
372 //glFlush(); // Sends all pending information directly to the GPU.
373 //m_glCanvas->SwapBuffers();
374 //event.Skip();
375 wxBufferedPaintDC dc(m_cePanel);
376 dc.Clear();
377 wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
378
379 // Draw
380 if (gc) {
381
382 gc->Scale(m_camera->GetScale(), m_camera->GetScale());
383 gc->Translate(m_camera->GetTranslation().m_x, m_camera->GetTranslation().m_y);
384
385 for (auto line : m_connectionList) {
386 //ConnectionLine* line = *it;
387 line->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), gc);
388 }
389
390 for (auto element : m_elementList) {
391 element->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), gc);
392 }
393
394 // Selection rectangle
395 gc->SetPen(wxPen(wxColour(0, 125, 255, 255)));
396 gc->SetBrush(wxBrush(wxColour(0, 125, 255, 125)));
397 gc->DrawRectangle(m_selectionRect.m_x, m_selectionRect.m_y, m_selectionRect.m_width, m_selectionRect.m_height);
398
399 delete gc;
400 }
401 event.Skip();
402}
403
404void ControlEditor::OnDoubleClick(wxMouseEvent& event)
405{
406 wxPoint2DDouble clickPoint = event.GetPosition();
407 bool redraw = false;
408
409 if (m_mode == ControlEditor::ControlEditorMode::MODE_EDIT) {
410 for (auto& element : m_elementList) {
411 if (element->Contains(m_camera->ScreenToWorld(clickPoint))) {
412 element->ShowForm(this, element.get());
413 CheckConnections();
414 auto childList = element->GetChildList();
415 for (auto itC = childList.begin(), itEndC = childList.end(); itC != itEndC; ++itC) {
416 ConnectionLine* line = static_cast<ConnectionLine*>(*itC);
417 line->UpdatePoints();
418 }
419 redraw = true;
420 }
421 }
422 }
423
424 if (redraw) Redraw();
425}
426
427void ControlEditor::OnLeftClickDown(wxMouseEvent& event)
428{
429 wxPoint2DDouble clickPoint = event.GetPosition();
430 bool foundElement = false;
431
432 if (m_mode == ControlEditorMode::MODE_INSERT) {
433 m_mode = ControlEditor::ControlEditorMode::MODE_EDIT;
434 }
435 else {
436 for (auto& element : m_elementList) {
437 bool foundNode = false;
438 auto nodeList = element->GetNodeList();
439 for (auto itN = nodeList.begin(), itNEnd = nodeList.end(); itN != itNEnd; ++itN) {
440 Node* node = *itN;
441 if (node->Contains(m_camera->ScreenToWorld(clickPoint))) {
442 m_mode = ControlEditorMode::MODE_INSERT_LINE;
443 auto line = std::make_shared<ConnectionLine>(node, GetNextID());
444 m_connectionList.push_back(line);
445 element->AddChild(line.get());
446 line->AddParent(element.get());
447 foundElement = true;
448 foundNode = true;
449 }
450 }
451
452 if (!foundNode) {
453 // Set movement initial position (not necessarily will be moved).
454 element->StartMove(m_camera->ScreenToWorld(clickPoint));
455
456 // Click in an element.
457 if (element->Contains(m_camera->ScreenToWorld(clickPoint))) {
458 if (!foundElement) {
459 element->SetSelected();
460 foundElement = true;
461 }
462 m_mode = ControlEditor::ControlEditorMode::MODE_MOVE_ELEMENT;
463 }
464 }
465 }
466 if (m_mode != ControlEditorMode::MODE_INSERT_LINE) {
467 for (auto& line : m_connectionList) {
468 line->StartMove(m_camera->ScreenToWorld(clickPoint));
469 if (line->Contains(m_camera->ScreenToWorld(clickPoint))) {
470 line->SetSelected();
471 foundElement = true;
472 m_mode = ControlEditorMode::MODE_MOVE_LINE;
473 }
474 }
475 }
476 }
477
478 if (!foundElement) {
479 m_mode = ControlEditorMode::MODE_SELECTION_RECT;
480 m_startSelRect = m_camera->ScreenToWorld(clickPoint);
481 }
482
483 Redraw();
484 event.Skip();
485}
486
487void ControlEditor::OnLeftClickUp(wxMouseEvent& event)
488{
489 bool foundNode = false;
490 for (auto& element : m_elementList) {
491 if (m_mode == ControlEditorMode::MODE_INSERT_LINE) {
492 auto nodeList = element->GetNodeList();
493 for (auto node : nodeList) {
494 if (node->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
495 //ConnectionLine* line = *(m_connectionList.end() - 1);
496 auto line = m_connectionList.back();
497 if (line->AppendNode(node, element.get())) {
498 line->AddParent(element.get());
499 element->AddChild(line.get());
500 line->UpdatePoints();
501 m_mode = ControlEditor::ControlEditorMode::MODE_EDIT;
502 foundNode = true;
503 }
504 }
505 }
506 }
507 else if (m_mode == ControlEditorMode::MODE_SELECTION_RECT) {
508 if (element->Intersects(m_selectionRect)) {
509 element->SetSelected();
510 }
511 else if (!event.ControlDown()) {
512 element->SetSelected(false);
513 }
514 }
515 else if (!event.ControlDown()) {
516 if (!element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) { element->SetSelected(false); }
517 }
518 }
519
520 auto& lastLine = m_connectionList.back();
521 for (auto& cLine : m_connectionList) {
522 if (m_mode == ControlEditorMode::MODE_INSERT_LINE && !foundNode && cLine.get() != lastLine.get()) {
523 if (cLine->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
524 //ConnectionLine* iLine = *(m_connectionList.end() - 1);
525 auto iLine = m_connectionList.back();
526 if (iLine->SetParentLine(cLine.get())) {
527 cLine->AddChild(iLine.get());
528 iLine->UpdatePoints();
529 m_mode = ControlEditor::ControlEditorMode::MODE_EDIT;
530 foundNode = true;
531 }
532 }
533 }
534 else if (m_mode == ControlEditorMode::MODE_SELECTION_RECT) {
535 if (cLine->Intersects(m_selectionRect)) {
536 cLine->SetSelected();
537 }
538 else if (!event.ControlDown()) {
539 cLine->SetSelected(false);
540 }
541 }
542 else if (!event.ControlDown()) {
543 if (!cLine->Contains(m_camera->ScreenToWorld(event.GetPosition()))) { cLine->SetSelected(false); }
544 }
545 }
546
547 m_selectionRect = wxRect2DDouble(0, 0, 0, 0);
548
549 if (m_mode == ControlEditorMode::MODE_INSERT_LINE && !foundNode) {
550 //ConnectionLine* cLine = *(m_connectionList.end() - 1);
551 auto cLine = m_connectionList.back();
552 // Free nodes
553 auto nodeList = cLine->GetNodeList();
554 for (auto itN = nodeList.begin(), itEndN = nodeList.end(); itN != itEndN; ++itN) {
555 Node* node = *itN;
556 node->SetConnected(false);
557 }
558 // Remove the associated child from parents.
559 auto parentList = cLine->GetParentList();
560 for (auto it = parentList.begin(), itEnd = parentList.end(); it != itEnd; ++it) {
561 Element* element = *it;
562 element->RemoveChild(cLine.get());
563 }
564 m_connectionList.pop_back();
565 //if (cLine) delete cLine;
566 m_mode = ControlEditor::ControlEditorMode::MODE_EDIT;
567 }
568 else if (m_mode != ControlEditorMode::MODE_INSERT) {
569 m_mode = ControlEditor::ControlEditorMode::MODE_EDIT;
570 }
571
572 Redraw();
573 event.Skip();
574}
575
576void ControlEditor::OnMiddleDown(wxMouseEvent& event)
577{
578 // Set to drag mode.
579 switch (m_mode) {
580 case ControlEditorMode::MODE_INSERT: {
581 m_mode = ControlEditorMode::MODE_DRAG_INSERT;
582 } break;
583 case ControlEditorMode::MODE_PASTE: {
584 m_mode = ControlEditorMode::MODE_DRAG_PASTE;
585 } break;
586 default: {
587 m_mode = ControlEditorMode::MODE_DRAG;
588 } break;
589 }
590 m_camera->StartTranslation(m_camera->ScreenToWorld(event.GetPosition()));
591}
592
593void ControlEditor::OnMiddleUp(wxMouseEvent& event)
594{
595 switch (m_mode) {
596 case ControlEditorMode::MODE_DRAG_INSERT: {
597 m_mode = ControlEditorMode::MODE_INSERT;
598 } break;
599 case ControlEditorMode::MODE_DRAG_PASTE: {
600 m_mode = ControlEditorMode::MODE_PASTE;
601 } break;
602 case ControlEditorMode::MODE_INSERT:
603 case ControlEditorMode::MODE_PASTE: {
604 // Does nothing.
605 } break;
606 default: {
607 m_mode = ControlEditor::ControlEditorMode::MODE_EDIT;
608 } break;
609 }
610}
611
612void ControlEditor::OnMouseMotion(wxMouseEvent& event)
613{
614 wxPoint2DDouble clickPoint = event.GetPosition();
615 bool redraw = false;
616
617 switch (m_mode) {
618 case ControlEditorMode::MODE_INSERT: {
619 //Element* newElement = *(m_elementList.end() - 1); // Get the last element in the list.
620 auto newElement = m_elementList.back();
621 newElement->Move(m_camera->ScreenToWorld(clickPoint));
622 redraw = true;
623 } break;
624 case ControlEditorMode::MODE_INSERT_LINE: {
625 //ConnectionLine* line = *(m_connectionList.end() - 1);
626 auto line = m_connectionList.back();
627 line->SetTemporarySecondPoint(m_camera->ScreenToWorld(clickPoint));
628 line->UpdatePoints();
629 redraw = true;
630 } break;
631 case ControlEditorMode::MODE_DRAG:
632 case ControlEditorMode::MODE_DRAG_INSERT:
633 case ControlEditorMode::MODE_DRAG_PASTE: {
634 m_camera->SetTranslation(clickPoint);
635 redraw = true;
636 } break;
637 case ControlEditor::ControlEditorMode::MODE_MOVE_ELEMENT: {
638 for (auto& element : m_elementList) {
639 if (element->IsSelected()) {
640 element->Move(m_camera->ScreenToWorld(clickPoint));
641 auto childList = element->GetChildList();
642 for (auto itC = childList.begin(), itEndC = childList.end(); itC != itEndC; itC++) {
643 ConnectionLine* line = static_cast<ConnectionLine*>(*itC);
644 line->UpdatePoints();
645 }
646 redraw = true;
647 }
648 }
649 } break;
650 case ControlEditorMode::MODE_MOVE_LINE: {
651 for (auto& line : m_connectionList) {
652 if (line->IsSelected()) {
653 line->Move(m_camera->ScreenToWorld(clickPoint));
654 redraw = true;
655 }
656 }
657 } break;
658 case ControlEditorMode::MODE_SELECTION_RECT: {
659 wxPoint2DDouble currentPos = m_camera->ScreenToWorld(clickPoint);
660 double x, y, w, h;
661 if (currentPos.m_x < m_startSelRect.m_x) {
662 x = currentPos.m_x;
663 w = m_startSelRect.m_x - currentPos.m_x;
664 }
665 else {
666 x = m_startSelRect.m_x;
667 w = currentPos.m_x - m_startSelRect.m_x;
668 }
669 if (currentPos.m_y < m_startSelRect.m_y) {
670 y = currentPos.m_y;
671 h = m_startSelRect.m_y - currentPos.m_y;
672 }
673 else {
674 y = m_startSelRect.m_y;
675 h = currentPos.m_y - m_startSelRect.m_y;
676 }
677
678 m_selectionRect = wxRect2DDouble(x, y, w, h);
679 redraw = true;
680 } break;
681 default:
682 break;
683 }
684
685 if (redraw) Redraw();
686 event.Skip();
687}
688
689void ControlEditor::OnScroll(wxMouseEvent& event)
690{
691 if (event.GetWheelRotation() > 0)
692 m_camera->SetScale(event.GetPosition(), +0.05);
693 else
694 m_camera->SetScale(event.GetPosition(), -0.05);
695
696 Redraw();
697}
698
699void ControlEditor::OnIdle(wxIdleEvent& event)
700{
701 //if(m_justOpened) {
702 // this->Raise();
703 //
704 // // Update all text elements
705 // m_justOpened = false;
706 // for(auto it = m_elementList.begin(), itEnd = m_elementList.end(); it != itEnd; ++it) {
707 // ControlElement* element = *it;
708 // if(!element->UpdateText()) m_justOpened = true;
709 // }
710 // Redraw();
711 //}
712}
713void ControlEditor::OnKeyDown(wxKeyEvent& event)
714{
715 char key = event.GetUnicodeKey();
716 if (key != WXK_NONE) {
717 switch (key) {
718 case WXK_DELETE: // Delete selected elements.
719 {
720 DeleteSelectedElements();
721 } break;
722 case 'R': // Rotate the selected elements.
723 {
724 RotateSelectedElements(event.GetModifiers() != wxMOD_SHIFT);
725 } break;
726 case 'L': {
727 // tests
728 } break;
729 }
730 }
731}
732
733void ControlEditor::RotateSelectedElements(bool clockwise)
734{
735 for (auto& element : m_elementList) {
736 if (element->IsSelected()) {
737 element->Rotate(clockwise);
738 auto childList = element->GetChildList();
739 for (auto itC = childList.begin(), itEndC = childList.end(); itC != itEndC; itC++) {
740 ConnectionLine* line = static_cast<ConnectionLine*>(*itC);
741 line->UpdatePoints();
742 }
743 }
744 }
745 Redraw();
746}
747
748void ControlEditor::DeleteSelectedElements()
749{
750 for (auto it = m_elementList.begin(); it != m_elementList.end();) {
751 Element* element = it->get();
752 if (element->IsSelected()) {
753 // Remove child/parent.
754 auto childList = element->GetChildList();
755 for (auto child : childList) {
756 // The child is always a connection line, but check it.
757 //ConnectionLine* child = static_cast<ConnectionLine*>(*itC);
758 if (auto childLine = dynamic_cast<ConnectionLine*>(child)) {
759 // Delete the connection line.
760 for (auto itCo = m_connectionList.begin(); itCo != m_connectionList.end(); ) {
761 if (itCo->get() == childLine)
762 itCo = DeleteLineFromList(itCo);
763 else
764 ++itCo;
765 }
766 }
767
768 }
769 m_elementList.erase(it);
770 //if (element) delete element;
771 }
772 else {
773 ++it;
774 }
775 }
776
777 for (auto it = m_connectionList.begin(); it != m_connectionList.end(); ) {
778 ConnectionLine* line = it->get();
779 if (line->IsSelected())
780 it = DeleteLineFromList(it);
781 else
782 ++it;
783 }
784 Redraw();
785}
786
787std::vector< std::shared_ptr<ConnectionLine> >::iterator ControlEditor::DeleteLineFromList(std::vector< std::shared_ptr<ConnectionLine> >::iterator& it)
788{
789 ConnectionLine* cLine = it->get();
790
791 // Delete children recursively
792 auto childList = cLine->GetLineChildList();
793 for (auto child : childList) {
794 for (auto itL = m_connectionList.begin(); itL != m_connectionList.end(); ) {
795 if (itL->get() == child)
796 itL = DeleteLineFromList(itL);
797 else
798 ++itL;
799 }
800 }
801 // Remove parents
802 auto parentList = cLine->GetParentList();
803 for (auto parent : parentList) {
804 if (parent) parent->RemoveChild(cLine);
805 }
806 if (cLine->GetParentLine()) cLine->GetParentLine()->RemoveChild(cLine);
807
808 // Free nodes
809 auto nodeList = cLine->GetNodeList();
810 for (auto node : nodeList)
811 node->SetConnected(false);
812
813 return m_connectionList.erase(it);
814}
815
816void ControlEditor::CheckConnections()
817{
818 for (auto it = m_connectionList.begin(); it != m_connectionList.end(); ++it) {
819 ConnectionLine* cLine = it->get();
820 if (cLine->GetType() == ConnectionLine::ConnectionLineType::ELEMENT_ELEMENT) {
821 if (cLine->GetParentList().size() < 2) { it = DeleteLineFromList(it); }
822 }
823 else if (cLine->GetParentList().size() < 1) {
824 it = DeleteLineFromList(it);
825 }
826 }
827}
828
829void ControlEditor::OnExportClick(wxCommandEvent& event)
830{
831 FileHanding fileHandling(this);
832
833 wxFileDialog saveFileDialog(this, _("Save CTL file"), "", "", "CTL files (*.ctl)|*.ctl",
834 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
835 if (saveFileDialog.ShowModal() == wxID_CANCEL) return;
836
837 fileHandling.SaveControl(saveFileDialog.GetPath());
838 wxFileName fileName(saveFileDialog.GetPath());
839 event.Skip();
840}
841
842void ControlEditor::OnImportClick(wxCommandEvent& event)
843{
844 wxFileDialog openFileDialog(this, _("Open CTL file"), "", "", "CTL files (*.ctl)|*.ctl",
845 wxFD_OPEN | wxFD_FILE_MUST_EXIST);
846 if (openFileDialog.ShowModal() == wxID_CANCEL) return;
847
848 wxFileName fileName(openFileDialog.GetPath());
849
850 FileHanding fileHandling(this);
851 if (!fileHandling.OpenControl(fileName, m_elementList, m_connectionList)) {
852 wxMessageDialog msgDialog(this, _("It was not possible to open the selected file."), _("Error"),
853 wxOK | wxCENTRE | wxICON_ERROR);
854 msgDialog.ShowModal();
855 }
856 Redraw();
857 event.Skip();
858}
859
860void ControlEditor::OnTestClick(wxCommandEvent& event)
861{
862 // Reset colour list position for the test plot.
863 m_itColourList = --m_colourList.end();
864
865 std::vector<IOControl*> ioList;
866
867 for (auto& element : m_elementList) {
868 if (auto io = dynamic_cast<IOControl*>(element.get())) {
869 ioList.push_back(io);
870 }
871 }
872
873 ControlSystemTest csTest(this, ioList, &m_inputType, &m_startTime, &m_slope, &m_timeStep, &m_simTime);
874 if (csTest.ShowModal() == wxID_OK) {
875 double printStep = 1e-3;
876 double pdbStep = 1e-1;
877
878 struct InputData {
879 wxString name;
880 std::vector<double> values;
881 };
882
883 std::vector<InputData> inputList;
884
885 // Store real flags for restoring after the test and set initial value.
886 std::vector<IOControl::IOFlags> realFlagValue;
887 for (auto* io : ioList) {
888 realFlagValue.push_back(io->GetValue());
889 if (io->GetType() == Node::NodeType::NODE_OUT) {
890 SimTestData testData = io->GetSimTestData();
891 io->SetValue(IOControl::IN_TEST);
892 io->SetTestValue(testData.initialValue);
893 inputList.push_back({ io->GetName(), {} });
894 }
895 }
896
897 wxProgressDialog pbd(_("Test"), _("Initializing..."), 100, this,
898 wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_SMOOTH);
899 ControlElementSolver solver(this, m_timeStep, 1e-5);
900 solver.InitializeValues(false);
901 if (solver.IsOK()) {
902 bool simStopped = false;
903 double currentTime = 0.0;
904 double printTime = 0.0;
905 double pdbTime = 0.0;
906 std::vector<double> time;
907 std::vector<double> solution;
908
909 while (currentTime <= m_simTime) {
910 for (auto* io : ioList) {
911 SimTestData testData = io->GetSimTestData();
912 if (currentTime >= testData.startTime) {
913 switch (testData.type) {
914 case 0: {
915 io->SetTestValue(testData.slope);
916 } break;
917 case 1: {
918 io->SetTestValue(testData.slope * (currentTime - testData.startTime));
919 } break;
920 case 2: {
921 io->SetTestValue(testData.slope * std::pow(currentTime - testData.startTime, 2));
922 } break;
923 default: {
924 io->SetTestValue(0.0);
925 break;
926 }
927 }
928 }
929 }
930
931 solver.SetCurrentTime(currentTime);
932 solver.SolveNextStep();
933 if (!solver.IsOK()) {
934 wxString msg = wxString::Format(_("Failed to solve the control system.\n%s"), solver.GetErrorMessage());
935 wxMessageDialog msgDialog(this, msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
936 msgDialog.ShowModal();
937 simStopped = true;
938 currentTime = m_simTime;
939 }
940
941 if (printTime >= printStep) {
942 time.push_back(currentTime);
943 solution.push_back(solver.GetLastSolution());
944 //inputV.push_back(input);
945 int i = 0;
946 for (auto* io : ioList) {
947 if (io->GetType() == Node::NodeType::NODE_OUT) {
948 inputList[i].values.push_back(io->GetTestValue());
949 i++;
950 }
951 }
952 printTime = 0.0;
953 }
954
955 if (pdbTime > pdbStep) {
956 if (!pbd.Update((currentTime / m_simTime) * 100, wxString::Format("Time = %.2fs", currentTime))) {
957 pbd.Update(100);
958 simStopped = true;
959 currentTime = m_simTime;
960 }
961 pdbTime = 0.0;
962 }
963
964 printTime += m_timeStep;
965 currentTime += m_timeStep;
966 pdbTime += m_timeStep;
967 }
968
969 if (!simStopped) {
970 std::vector<ElementPlotData> epdList;
971 ElementPlotData curveData(_("I/O"), ElementPlotData::CurveType::CT_TEST);
972 int i = 0;
973 for (const auto& input : inputList) {
974 curveData.AddData(input.values, input.name);
975 curveData.SetPlot(i);
976 curveData.SetColour(i, GetNextColour());
977 i++;
978 }
979 //curveData.AddData(inputV, _("Input"));
980 curveData.AddData(solution, _("Output"));
981 curveData.SetPlot(i);
982 curveData.SetColour(i, GetNextColour());
983
984 //curveData.SetPlot(0);
985 //curveData.SetColour(0, *wxRED);
986 //curveData.SetPlot(1);
987 //curveData.SetColour(1, *wxBLUE);
988
989 epdList.push_back(curveData);
990
991 ChartView* cView = new ChartView(this, epdList, time, static_cast<PlotLib>(m_plotLib));
992 cView->Show();
993 cView->UpdatePlot();
994 }
995 }
996 else {
997 wxMessageDialog msgDialog(
998 this,
999 wxString::Format(_("Failed to solve the control system.\n%s"), solver.GetErrorMessage()),
1000 _("Error"),
1001 wxOK | wxCENTRE | wxICON_ERROR
1002 );
1003
1004 msgDialog.ShowModal();
1005 }
1006
1007 // Restore real flags.
1008 for (size_t i = 0; i < ioList.size(); i++) {
1009 if (ioList[i]->GetType() == Node::NodeType::NODE_OUT)
1010 ioList[i]->SetValue(realFlagValue[i]);
1011 }
1012 }
1013}
1014
1015void ControlEditor::OnClose(wxCloseEvent& event)
1016{
1017 if (m_ctrlContainer) { m_ctrlContainer->FillContainer(this); }
1018 event.Skip();
1019}
1020
1021int ControlEditor::GetNextID()
1022{
1023 int id = 0;
1024 for (auto& element : m_elementList) {
1025 if (element->GetID() > id) id = element->GetID();
1026 }
1027 for (auto& line : m_connectionList) {
1028 if (line->GetID() > id) id = line->GetID();
1029 }
1030 id++;
1031 return id;
1032}
1033
1034void ControlEditor::BuildColourList()
1035{
1036 m_colourList.push_back(wxColour(255, 30, 0));
1037 m_colourList.push_back(wxColour(0, 30, 255));
1038 m_colourList.push_back(wxColour(0, 128, 0));
1039 m_colourList.push_back(wxColour(100, 100, 100));
1040 m_colourList.push_back(wxColour(255, 128, 0));
1041 m_colourList.push_back(wxColour(128, 0, 255));
1042 m_colourList.push_back(wxColour(0, 255, 128));
1043 m_colourList.push_back(wxColour(255, 255, 0));
1044 m_colourList.push_back(wxColour(255, 0, 255));
1045 m_colourList.push_back(wxColour(0, 255, 255));
1046 m_colourList.push_back(wxColour(128, 255, 0));
1047 m_colourList.push_back(wxColour(255, 0, 128));
1048 m_colourList.push_back(wxColour(0, 128, 255));
1049 m_colourList.push_back(wxColour(128, 128, 128));
1050 m_colourList.push_back(*wxBLACK);
1051 m_itColourList = --m_colourList.end();
1052}
1053
1054wxColour ControlEditor::GetNextColour()
1055{
1056 if (*m_itColourList == *wxBLACK)
1057 m_itColourList = m_colourList.begin();
1058 else
1059 ++m_itColourList;
1060
1061 return *m_itColourList;
1062}
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
Connection between two control elements or other connection line and an element.
This class is responsible to handle the user interaction with control elements.
Solves in the time the control system. Can solve the control system directly from a ControlEditor or ...
Form to edit properties to test the control system created.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:112
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 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:495
virtual void Move(wxPoint2DDouble position)
Move the element other position.
Definition Element.cpp:266
bool IsSelected() const
Checks if the element is selected.
Definition Element.h:201
virtual void Rotate(bool clockwise=true)
Rotate the element.
Definition Element.h:316
Save and opens the projects created on disk.
Definition FileHanding.h:43
Provides the communication with the power element.
Definition IOControl.h:43
Node of a control element. This class manages the user interaction with the connection and control el...