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