Power System Platform  2026w34a-beta
Loading...
Searching...
No Matches
Element.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 "Element.h"
19#ifdef USING_WX_3_0_X
20#include "../utils/DegreesAndRadians.h"
21#endif
22#include "../utils/Path.h"
23#include "../utils/PropertiesData.h"
24
25#include <wx/pen.h>
26#include <wx/brush.h>
27
29{
30 //m_selectionColour.Set(0, 128, 255, 128);
31}
32
33void Element::SetPosition(const wxPoint2DDouble position)
34{
35 m_position = position;
36 m_rect =
37 wxRect2DDouble(m_position.m_x - m_width / 2.0 - m_borderSize, m_position.m_y - m_height / 2.0 - m_borderSize,
38 m_width + 2.0 * m_borderSize, m_height + 2.0 * m_borderSize);
39}
40
41void Element::DrawDCRectangle(wxPoint2DDouble position, double width, double height, double angle, wxDC& dc) const
42{
43 if (angle == 0.0) {
44 wxPoint poly[4] = {
45 wxPoint((int)(position.m_x - width / 2.0), (int)(position.m_y - height / 2.0)),
46 wxPoint((int)(position.m_x + width / 2.0), (int)(position.m_y - height / 2.0)),
47 wxPoint((int)(position.m_x + width / 2.0), (int)(position.m_y + height / 2.0)),
48 wxPoint((int)(position.m_x - width / 2.0), (int)(position.m_y + height / 2.0))
49 };
50 dc.DrawPolygon(4, poly);
51 return;
52 }
53
54 double hw = width / 2.0;
55 double hh = height / 2.0;
56 double angleRad = wxDegToRad(angle);
57
58 // Retângulo centrado na origem
59 wxPoint2DDouble pts[4] = {
60 {-hw, -hh},
61 { hw, -hh},
62 { hw, hh},
63 {-hw, hh}
64 };
65
66 wxPoint poly[4];
67
68 for (int i = 0; i < 4; ++i)
69 {
70 double x = pts[i].m_x;
71 double y = pts[i].m_y;
72
73 // Rotação
74 double xr = x * cos(angleRad) - y * sin(angleRad);
75 double yr = x * sin(angleRad) + y * cos(angleRad);
76
77 // Translação para centro
78 poly[i].x = (int)(xr + position.m_x);
79 poly[i].y = (int)(yr + position.m_y);
80 }
81
82 dc.DrawPolygon(4, poly);
83
84}
85
86void Element::DrawDCRoundedRectRotated(wxDC& dc, const wxPoint2DDouble& center, double width, double height, double radius, double angleDeg, int arcSegments) const
87{
88 radius = std::min(radius, std::min(width, height) / 2.0);
89
90 double hw = width / 2.0;
91 double hh = height / 2.0;
92
93 double rad = wxDegToRad(angleDeg);
94 double c = std::cos(rad);
95 double s = std::sin(rad);
96
97 auto rotate = [&](double x, double y)
98 {
99 double xr = c * x - s * y + center.m_x;
100 double yr = s * x + c * y + center.m_y;
101 return wxPoint2DDouble(xr, yr);
102 };
103
104 auto drawArc = [&](double cx, double cy,
105 double startDeg)
106 {
107 wxPoint2DDouble arcCenterLocal(cx, cy);
108 wxPoint2DDouble arcCenter = rotate(arcCenterLocal.m_x,
109 arcCenterLocal.m_y);
110
111 double start = startDeg + angleDeg;
112 double end = start + 90.0;
113
114 dc.DrawEllipticArc(
115 wxRound(arcCenter.m_x - radius),
116 wxRound(arcCenter.m_y - radius),
117 wxRound(radius * 2),
118 wxRound(radius * 2),
119 start,
120 end
121 );
122 };
123
124 auto line = [&](double x1, double y1,
125 double x2, double y2,
126 wxPoint& p1, wxPoint& p2)
127 {
128 wxPoint2DDouble p1d = rotate(x1, y1);
129 wxPoint2DDouble p2d = rotate(x2, y2);
130 p1 = wxPoint(wxRound(p1d.m_x), wxRound(p1d.m_y));
131 p2 = wxPoint(wxRound(p2d.m_x), wxRound(p2d.m_y));
132
133 //dc.DrawLine(p1.x, p1.y, p2.x, p2.y);
134 };
135
136 wxPen pen = dc.GetPen();
137
138 wxPoint pts[4];
139 line(-hw + radius, -hh,
140 hw - radius, -hh,
141 pts[0], pts[1]);
142
143 line(hw - radius, hh,
144 -hw + radius, hh,
145 pts[2], pts[3]);
146
147 dc.SetPen(*wxTRANSPARENT_PEN);
148 dc.DrawPolygon(4, pts);
149 dc.SetPen(pen);
150 dc.DrawLine(pts[0].x, pts[0].y, pts[1].x, pts[1].y);
151 dc.DrawLine(pts[2].x, pts[2].y, pts[3].x, pts[3].y);
152
153 line(hw, -hh + radius,
154 hw, hh - radius,
155 pts[0], pts[1]);
156
157 line(-hw, hh - radius,
158 -hw, -hh + radius,
159 pts[2], pts[3]);
160
161 dc.SetPen(*wxTRANSPARENT_PEN);
162 dc.DrawPolygon(4, pts);
163 dc.SetPen(pen);
164 dc.DrawLine(pts[0].x, pts[0].y, pts[1].x, pts[1].y);
165 dc.DrawLine(pts[2].x, pts[2].y, pts[3].x, pts[3].y);
166
167 drawArc(hw - radius, -hh + radius, 0 - angleDeg * 2);
168 drawArc(hw - radius, hh - radius, 270 - angleDeg * 2);
169 drawArc(-hw + radius, hh - radius, 180 - angleDeg * 2);
170 drawArc(-hw + radius, -hh + radius, 90 - angleDeg * 2);
171}
172
173void Element::DrawDCCircle(wxPoint2DDouble position, double radius, int numSegments, wxGraphicsContext* gc) const
174{
175 wxPoint2DDouble* pts = new wxPoint2DDouble[numSegments + 1];
176 for (int i = 0; i < numSegments; i++) {
177 double theta = 2.0 * 3.1415926 * double(i) / double(numSegments);
178 pts[i] = wxPoint2DDouble(radius * std::cos(theta) + position.m_x, radius * std::sin(theta) + position.m_y);
179 }
180 pts[numSegments] = pts[0];
181 gc->DrawLines(numSegments + 1, pts);
182 delete[] pts;
183}
184
185void Element::DrawDCCircle(wxPoint2DDouble position, double radius, wxDC& dc) const
186{
187 dc.DrawCircle(wxRound(position.m_x), wxRound(position.m_y), wxRound(radius));
188}
189
190void Element::DrawDCArc(wxPoint2DDouble position, double radius, double initAngle, double finalAngle, int numSegments, wxGraphicsContext* gc) const
191{
192 double initAngRad = wxDegToRad(initAngle);
193 double finalAngRad = wxDegToRad(finalAngle);
194 wxPoint2DDouble* points = new wxPoint2DDouble[numSegments + 2];
195 for (int i = 0; i <= numSegments; i++) {
196 double theta = initAngRad + (finalAngRad - initAngRad) * double(i) / double(numSegments);
197 points[i] = wxPoint2DDouble(radius * std::cos(theta) + position.m_x, radius * std::sin(theta) + position.m_y);
198 }
199
200 gc->StrokeLines(numSegments + 1, points);
201 delete[] points;
202}
203
204void Element::DrawDCArc(wxPoint2DDouble position, double radius, double initAngle, double finalAngle, wxDC& dc) const
205{
206 dc.DrawEllipticArc(wxRound(position.m_x - radius), wxRound(position.m_y - radius), wxRound(2 * radius), wxRound(2 * radius), initAngle, finalAngle);
207}
208
209void Element::DrawDCTriangle(std::vector<wxPoint2DDouble> points, wxGraphicsContext* gc) const
210{
211 points.emplace_back(points[0]);
212 gc->DrawLines(4, &points[0]);
213}
214
215void Element::DrawDCTriangle(std::vector<wxPoint> points, wxDC& dc) const
216{
217 points.emplace_back(points[0]);
218 dc.DrawPolygon(4, &points[0]);
219}
220
221void Element::DrawStabilityEventGC(wxGraphicsContext* gc, wxPoint2DDouble translation, double scale, GUIColour* guiColour, bool rotateAnchor, wxPoint2DDouble iconPosition) const
222{
223 if (!gc)
224 return;
225
226 // Fixed size in screen pixels.
227 constexpr double radius = 10.0;
228 constexpr int handWidth = 3;
229 constexpr int crownWidth = 7;
230 constexpr int crownHeight = 3;
231 constexpr int crownStem = 3;
232 constexpr double margin = 4.0;
233
234 wxGraphicsMatrix identityMatrix = gc->GetTransform();
235 identityMatrix.Set();
236
237 gc->PushState();
238 gc->SetTransform(identityMatrix);
239
240 wxPoint2DDouble screenPt;
241
242 // Use the specified position when provided.
243 if (iconPosition.m_x != -1.0 || iconPosition.m_y != -1.0) {
244 screenPt = WorldToScreen(iconPosition, translation, scale, 0.0, 0.0);
245 }
246 else {
247 // Get the element angle in a canonical range [-90°, 90°].
248 double anchorAngle = std::fmod(m_angle, 180.0);
249
250 if (anchorAngle >= 90.0)
251 anchorAngle -= 180.0;
252 else if (anchorAngle < -90.0)
253 anchorAngle += 180.0;
254
255 // Get the right edge of the element.
256 wxPoint2DDouble anchor(m_width / 2.0, 0.0);
257
258 if (rotateAnchor)
259 anchor = RotateLocal(anchor, anchorAngle);
260
261 // Convert the anchor to screen coordinates.
262 screenPt = WorldToScreen(translation, scale, anchor.m_x, anchor.m_y);
263 }
264
265 // Keep the indicator at a fixed screen-space offset from the element.
266 screenPt.m_x += radius + margin * scale;
267 screenPt.m_y -= radius + crownStem + crownHeight + margin * scale;
268
269 // Draw stopwatch body.
270 gc->SetPen(*wxTRANSPARENT_PEN);
271 gc->SetBrush(wxBrush(guiColour->bus));
272 gc->DrawEllipse(screenPt.m_x - radius, screenPt.m_y - radius, 2.0 * radius, 2.0 * radius);
273
274 // Draw stopwatch stem.
275 gc->SetPen(wxPen(guiColour->bus, crownHeight));
276 gc->StrokeLine(screenPt.m_x, screenPt.m_y - radius, screenPt.m_x, screenPt.m_y - radius - crownStem);
277
278 // Draw stopwatch crown.
279 gc->StrokeLine(screenPt.m_x - crownWidth / 2.0, screenPt.m_y - radius - crownStem, screenPt.m_x + crownWidth / 2.0, screenPt.m_y - radius - crownStem);
280
281 // Draw clock hands.
282 wxPen handPen(guiColour->eventElement, handWidth);
283 handPen.SetCap(wxCAP_ROUND);
284 gc->SetPen(handPen);
285 gc->StrokeLine(screenPt.m_x, screenPt.m_y, screenPt.m_x, screenPt.m_y - 7.0);
286 gc->StrokeLine(screenPt.m_x, screenPt.m_y, screenPt.m_x + 5.0, screenPt.m_y + 3.0);
287
288 gc->PopState();
289}
290
291void Element::DrawDCPickbox(wxPoint2DDouble position, wxGraphicsContext* gc) const
292{
293 gc->SetPen(wxPen(wxColour(0, 0, 0, 255)));
294 gc->SetBrush(wxBrush(wxColour(255, 255, 255, 204)));
295 gc->DrawRectangle(position.m_x, position.m_y, 8, 8);
296}
297
298wxPoint2DDouble Element::RotateAtPosition(wxPoint2DDouble pointToRotate, double angle, bool degrees) const
299{
300 double radAngle = angle;
301 if (degrees) radAngle = wxDegToRad(angle);
302 return wxPoint2DDouble(std::cos(radAngle) * (pointToRotate.m_x - m_position.m_x) -
303 std::sin(radAngle) * (pointToRotate.m_y - m_position.m_y) + m_position.m_x,
304 std::sin(radAngle) * (pointToRotate.m_x - m_position.m_x) +
305 std::cos(radAngle) * (pointToRotate.m_y - m_position.m_y) + m_position.m_y);
306}
307
308wxPoint2DDouble Element::RotateLocal(wxPoint2DDouble local, double angleDeg) const
309{
310 double rad = wxDegToRad(angleDeg);
311 double c = std::cos(rad);
312 double s = std::sin(rad);
313
314 return wxPoint2DDouble(
315 c * local.m_x - s * local.m_y,
316 s * local.m_x + c * local.m_y
317 );
318}
319
320wxPoint Element::RotateAround(const wxPoint2DDouble& p, const wxPoint2DDouble& center, double angleDeg) const
321{
322 double rad = wxDegToRad(angleDeg);
323 double c = std::cos(rad);
324 double s = std::sin(rad);
325
326 double dx = p.m_x - center.m_x;
327 double dy = p.m_y - center.m_y;
328
329 double xr = c * dx - s * dy + center.m_x;
330 double yr = s * dx + c * dy + center.m_y;
331
332 return wxPoint(wxRound(xr), wxRound(yr));
333}
334
335void Element::StartMove(wxPoint2DDouble position)
336{
337 this->m_moveStartPt = position;
338 this->m_movePos = m_position;
339}
340
341void Element::Move(wxPoint2DDouble position) { SetPosition(m_movePos + position - m_moveStartPt); }
342void Element::AlignToGrid(double gridSize)
343{
344 if (gridSize <= 0.0) gridSize = 20.0;
345 wxPoint2DDouble oldPos = m_position;
346 wxPoint2DDouble newPos(std::round(oldPos.m_x / gridSize) * gridSize,
347 std::round(oldPos.m_y / gridSize) * gridSize);
348 wxPoint2DDouble delta = newPos - oldPos;
349 SetPosition(newPos);
350 for (auto& pt : m_pointList) {
351 pt += delta;
352 }
353}
354wxPoint2DDouble Element::WorldToScreen(wxPoint2DDouble translation, double scale, double offsetX, double offsetY) const
355{
356 return wxPoint2DDouble(m_position.m_x + offsetX + translation.m_x, m_position.m_y + offsetY + translation.m_y) *
357 scale;
358}
359
360wxPoint2DDouble Element::WorldToScreen(wxPoint2DDouble position,
361 wxPoint2DDouble translation,
362 double scale,
363 double offsetX,
364 double offsetY) const
365{
366 return wxPoint2DDouble(position.m_x + offsetX + translation.m_x, position.m_y + offsetY + translation.m_y) * scale;
367}
368
369//void Element::DrawPoint(wxPoint2DDouble position, double size) const
370//{
371// glPointSize(size);
372// glBegin(GL_POINTS);
373// glVertex2d(position.m_x, position.m_y);
374// glEnd();
375//}
376
377bool Element::RotatedRectanglesIntersects(wxRect2DDouble rect1,
378 wxRect2DDouble rect2,
379 double angle1,
380 double angle2) const
381{
382 wxPoint2DDouble rect1Corners[4] = { rect1.GetLeftTop(), rect1.GetLeftBottom(), rect1.GetRightBottom(),
383 rect1.GetRightTop() };
384 wxPoint2DDouble rect2Corners[4] = { rect2.GetLeftTop(), rect2.GetLeftBottom(), rect2.GetRightBottom(),
385 rect2.GetRightTop() };
386 wxPoint2DDouble rect1Center(rect1.m_x + rect1.m_width / 2.0, rect1.m_y + rect1.m_height / 2.0);
387 wxPoint2DDouble rect2Center(rect2.m_x + rect2.m_width / 2.0, rect2.m_y + rect2.m_height / 2.0);
388
389 // Rotate the corners.
390 double radAngle1 = wxDegToRad(angle1);
391 double radAngle2 = wxDegToRad(angle2);
392
393 for (int i = 0; i < 4; i++) {
394 rect1Corners[i] =
395 wxPoint2DDouble(std::cos(radAngle1) * (rect1Corners[i].m_x - rect1Center.m_x) -
396 std::sin(radAngle1) * (rect1Corners[i].m_y - rect1Center.m_y) + rect1Center.m_x,
397 std::sin(radAngle1) * (rect1Corners[i].m_x - rect1Center.m_x) +
398 std::cos(radAngle1) * (rect1Corners[i].m_y - rect1Center.m_y) + rect1Center.m_y);
399
400 rect2Corners[i] =
401 wxPoint2DDouble(std::cos(radAngle2) * (rect2Corners[i].m_x - rect2Center.m_x) -
402 std::sin(radAngle2) * (rect2Corners[i].m_y - rect2Center.m_y) + rect2Center.m_x,
403 std::sin(radAngle2) * (rect2Corners[i].m_x - rect2Center.m_x) +
404 std::cos(radAngle2) * (rect2Corners[i].m_y - rect2Center.m_y) + rect2Center.m_y);
405 }
406
407 //[Ref] http://www.gamedev.net/page/resources/_/technical/game-programming/2d-rotated-rectangle-collision-r2604
408
409 // Find the rectangles axis to project
410 wxPoint2DDouble axis[4] = { rect1Corners[3] - rect1Corners[0], rect1Corners[3] - rect1Corners[2],
411 rect2Corners[3] - rect2Corners[0], rect2Corners[3] - rect2Corners[2] };
412
413 // Calculate the projected points to each axis
414 wxPoint2DDouble rect1ProjPts[4][4]; // [axis][corner]
415 wxPoint2DDouble rect2ProjPts[4][4]; // [axis][corner]
416 for (int i = 0; i < 4; i++) {
417 double den = axis[i].m_x * axis[i].m_x + axis[i].m_y * axis[i].m_y;
418 for (int j = 0; j < 4; j++) {
419 double m_rectProj = (rect1Corners[j].m_x * axis[i].m_x + rect1Corners[j].m_y * axis[i].m_y) / den;
420 double rectProj = (rect2Corners[j].m_x * axis[i].m_x + rect2Corners[j].m_y * axis[i].m_y) / den;
421
422 rect1ProjPts[i][j] = wxPoint2DDouble(m_rectProj * axis[i].m_x, m_rectProj * axis[i].m_y);
423 rect2ProjPts[i][j] = wxPoint2DDouble(rectProj * axis[i].m_x, rectProj * axis[i].m_y);
424 }
425 }
426
427 // Calculate the scalar value to identify the max and min values on projections
428 double rect1Scalar[4][4]; //[axis][corner]
429 double rect2Scalar[4][4]; //[axis][corner]
430 for (int i = 0; i < 4; i++) {
431 for (int j = 0; j < 4; j++) {
432 rect1Scalar[i][j] = rect1ProjPts[i][j].m_x * axis[i].m_x + rect1ProjPts[i][j].m_y * axis[i].m_y;
433 rect2Scalar[i][j] = rect2ProjPts[i][j].m_x * axis[i].m_x + rect2ProjPts[i][j].m_y * axis[i].m_y;
434 }
435 }
436 // Identify the max and min scalar values
437 double rect1Min[4];
438 double rect1Max[4];
439 double rect2Min[4];
440 double rect2Max[4];
441
442 for (int i = 0; i < 4; i++) {
443 rect1Max[i] = rect1Scalar[i][0];
444 rect2Max[i] = rect2Scalar[i][0];
445 rect1Min[i] = rect1Scalar[i][0];
446 rect2Min[i] = rect2Scalar[i][0];
447
448 for (int j = 1; j < 4; j++) {
449 if (rect1Max[i] < rect1Scalar[i][j]) rect1Max[i] = rect1Scalar[i][j];
450 if (rect2Max[i] < rect2Scalar[i][j]) rect2Max[i] = rect2Scalar[i][j];
451
452 if (rect1Min[i] > rect1Scalar[i][j]) rect1Min[i] = rect1Scalar[i][j];
453 if (rect2Min[i] > rect2Scalar[i][j]) rect2Min[i] = rect2Scalar[i][j];
454 }
455 }
456
457 // Check if any segment don't overlap
458 for (int i = 0; i < 4; i++) {
459 if (!(rect2Min[i] <= rect1Max[i] && rect2Max[i] >= rect1Min[i])) return false;
460 }
461
462 return true;
463}
464
465bool Element::SetOnline(bool online)
466{
467 // Check if any parent is null.
468 for (auto it = m_parentList.begin(); it != m_parentList.end(); it++) {
469 if (!(*it)) return false;
470 }
471 m_online = online;
472 return true;
473}
474
476{
477 wxFileName exeFileName(wxStandardPaths::Get().GetExecutablePath());
478 //wxString exePath = exeFileName.GetPath();
479
480 wxMenuItem* clockItem = new wxMenuItem(&menu, ID_ROTATE_CLOCK, _("Rotate clockwise"));
481 clockItem->SetBitmap(wxImage(Paths::GetDataPath() + "/images/menu/rotateClock16.png"));
482 menu.Append(clockItem);
483
484 wxMenuItem* counterClockItem = new wxMenuItem(&menu, ID_ROTATE_COUNTERCLOCK, _("Rotate counter-clockwise"));
485 counterClockItem->SetBitmap(wxImage(Paths::GetDataPath() + "/images/menu/rotateCounterClock16.png"));
486 menu.Append(counterClockItem);
487
488 wxMenuItem* deleteItem = new wxMenuItem(&menu, ID_DELETE, _("Delete"));
489 deleteItem->SetBitmap(wxImage(Paths::GetDataPath() + "/images/menu/delete16.png"));
490 menu.Append(deleteItem);
491}
492
493void Element::CalculateBoundaries(wxPoint2DDouble& leftUp, wxPoint2DDouble& rightBottom) const
494{
495 // Check rect corners boundaries.
496
497 // Get rectangle corners
498 wxPoint2DDouble rectCorner[4] = { m_rect.GetLeftTop(), m_rect.GetLeftBottom(), m_rect.GetRightBottom(),
499 m_rect.GetRightTop() };
500 // Rotate corners.
501 for (int i = 0; i < 4; ++i) { rectCorner[i] = RotateAtPosition(rectCorner[i], m_angle); }
502 leftUp = rectCorner[0];
503 rightBottom = rectCorner[0];
504 for (int i = 1; i < 4; ++i) {
505 if (rectCorner[i].m_x < leftUp.m_x) leftUp.m_x = rectCorner[i].m_x;
506 if (rectCorner[i].m_y < leftUp.m_y) leftUp.m_y = rectCorner[i].m_y;
507 if (rectCorner[i].m_x > rightBottom.m_x) rightBottom.m_x = rectCorner[i].m_x;
508 if (rectCorner[i].m_y > rightBottom.m_y) rightBottom.m_y = rectCorner[i].m_y;
509 }
510
511 // Check points list boundaries.
512 for (int i = 0; i < (int)m_pointList.size(); i++) {
513 if (m_pointList[i].m_x < leftUp.m_x) leftUp.m_x = m_pointList[i].m_x;
514 if (m_pointList[i].m_y < leftUp.m_y) leftUp.m_y = m_pointList[i].m_y;
515 if (m_pointList[i].m_x > rightBottom.m_x) rightBottom.m_x = m_pointList[i].m_x;
516 if (m_pointList[i].m_y > rightBottom.m_y) rightBottom.m_y = m_pointList[i].m_y;
517 }
518}
519
520bool Element::DoubleFromString(wxWindow* parent, wxString strValue, double& value, wxString errorMsg)
521{
522 double dValue = 0.0;
523
524 if (!strValue.ToDouble(&dValue)) {
525 wxMessageDialog msgDialog(parent, errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
526 msgDialog.ShowModal();
527 return false;
528 }
529
530 value = dValue;
531 return true;
532}
533
534bool Element::IntFromString(wxWindow* parent, wxString strValue, int& value, wxString errorMsg)
535{
536 long int iValue = 0;
537
538 if (!strValue.ToLong(&iValue)) {
539 wxMessageDialog msgDialog(parent, errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
540 msgDialog.ShowModal();
541 return false;
542 }
543
544 value = iValue;
545 return true;
546}
547
548wxString Element::StringFromDouble(double value, int minDecimal, int maxDecimals)
549{
550 wxString str = wxString::FromCDouble(value, maxDecimals);
551 int cutNumber = 0;
552 int numDecimal = 0;
553 bool foundCut = false;
554 for (int i = (int)str.length() - 1; i >= 0; i--) {
555 if (str[i] != '0' && !foundCut) {
556 cutNumber = i;
557 foundCut = true;
558 }
559 if (str[i] == '.') {
560 numDecimal = i;
561 break;
562 }
563 }
564
565 wxString formatedStr = "";
566 if (cutNumber - numDecimal > minDecimal)
567 formatedStr = wxString::FromDouble(value, cutNumber - numDecimal);
568 else
569 formatedStr = wxString::FromDouble(value, minDecimal);
570
571 return formatedStr;
572}
573
574void Element::ReplaceParent(Element* oldParent, Element* newParent)
575{
576 for (int i = 0; i < (int)m_parentList.size(); i++) {
577 if (m_parentList[i] == oldParent) m_parentList[i] = newParent;
578 }
579}
580
581void Element::AddChild(Element* child) { m_childList.push_back(child); }
583{
584 for (auto it = m_childList.begin(); it != m_childList.end();) {
585 if (*it == child) it = m_childList.erase(it);
586 else ++it;
587 }
588}
589
590void Element::ReplaceChild(Element* oldChild, Element* newChild)
591{
592 for (int i = 0; i < (int)m_childList.size(); i++) {
593 if (m_childList[i] == oldChild) m_childList[i] = newChild;
594 }
595}
596
597//void OpenGLColour::SetRGBA(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha)
598//{
599// rgba[0] = red;
600// rgba[1] = green;
601// rgba[2] = blue;
602// rgba[3] = alpha;
603//}
604//
605//wxColour OpenGLColour::GetDcRGBA() const
606//{
607// return wxColour(rgba[0] * 255, rgba[1] * 255, rgba[2] * 255, rgba[3] * 255);
608//}
609//
610//OpenGLColour::OpenGLColour() { SetRGBA(1.0, 1.0, 1.0, 1.0); }
611//OpenGLColour::OpenGLColour(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha)
612//{
613// SetRGBA(red, green, blue, alpha);
614//}
615
616double Element::PointToLineDistance(wxPoint2DDouble point, int* segmentNumber) const
617{
618 //[Ref] http://geomalgorithms.com/a02-_lines.html
619 double distance = 100.0; // Big initial distance.
620 wxPoint2DDouble p0 = point;
621
622 for (int i = 1; i < (int)m_pointList.size() - 2; i++) {
623 double d = 0.0;
624
625 wxPoint2DDouble p1 = m_pointList[i];
626 wxPoint2DDouble p2 = m_pointList[i + 1];
627
628 wxPoint2DDouble v = p2 - p1;
629 wxPoint2DDouble w = p0 - p1;
630
631 double c1 = w.m_x * v.m_x + w.m_y * v.m_y;
632 double c2 = v.m_x * v.m_x + v.m_y * v.m_y;
633
634 if (c1 <= 0.0) {
635 d = std::sqrt(std::pow(p0.m_y - p1.m_y, 2) + std::pow(p0.m_x - p1.m_x, 2));
636 }
637 else if (c2 <= c1) {
638 d = std::sqrt(std::pow(p0.m_y - p2.m_y, 2) + std::pow(p0.m_x - p2.m_x, 2));
639 }
640 else {
641 d = std::abs((p2.m_y - p1.m_y) * p0.m_x - (p2.m_x - p1.m_x) * p0.m_y + p2.m_x * p1.m_y - p2.m_y * p1.m_x) /
642 std::sqrt(std::pow(p2.m_y - p1.m_y, 2) + std::pow(p2.m_x - p1.m_x, 2));
643 }
644 if (d < distance) {
645 distance = d;
646 if (segmentNumber) *segmentNumber = i;
647 }
648 }
649
650 return distance;
651}
652
653void Element::SaveCADProperties(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementNode)
654{
655 auto cadProp = XMLParser::AppendNode(doc, elementNode, "CADProperties");
656 auto position = XMLParser::AppendNode(doc, cadProp, "Position");
657 auto posX = XMLParser::AppendNode(doc, position, "X");
658 XMLParser::SetNodeValue(doc, posX, m_position.m_x);
659 auto posY = XMLParser::AppendNode(doc, position, "Y");
660 XMLParser::SetNodeValue(doc, posY, m_position.m_y);
661 auto size = XMLParser::AppendNode(doc, cadProp, "Size");
662 auto width = XMLParser::AppendNode(doc, size, "Width");
663 XMLParser::SetNodeValue(doc, width, m_width);
664 auto height = XMLParser::AppendNode(doc, size, "Height");
665 XMLParser::SetNodeValue(doc, height, m_height);
666 auto angle = XMLParser::AppendNode(doc, cadProp, "Angle");
667 XMLParser::SetNodeValue(doc, angle, m_angle);
668}
669
670bool Element::OpenCADProperties(rapidxml::xml_node<>* elementNode)
671{
672 auto cadPropNode = elementNode->first_node("CADProperties");
673 if (!cadPropNode) return false;
674
675 auto position = cadPropNode->first_node("Position");
676 double posX = XMLParser::GetNodeValueDouble(position, "X");
677 double posY = XMLParser::GetNodeValueDouble(position, "Y");
678 auto size = cadPropNode->first_node("Size");
679 m_width = XMLParser::GetNodeValueDouble(size, "Width");
680 m_height = XMLParser::GetNodeValueDouble(size, "Height");
681 m_angle = XMLParser::GetNodeValueDouble(cadPropNode, "Angle");
682 SetPosition(wxPoint2DDouble(posX, posY));
683 return true;
684}
@ ID_DELETE
Definition Element.h:82
@ ID_ROTATE_CLOCK
Definition Element.h:80
@ ID_ROTATE_COUNTERCLOCK
Definition Element.h:81
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:115
virtual bool RotatedRectanglesIntersects(wxRect2DDouble rect1, wxRect2DDouble rect2, double angle1, double angle2) const
Check if two roteted rectangles intersect.
Definition Element.cpp:377
virtual void GeneralMenuItens(wxMenu &menu)
Insert general itens to context menu.
Definition Element.cpp:475
static bool IntFromString(wxWindow *parent, wxString strValue, int &value, wxString errorMsg)
Convert a string to int. Show a error message if the conversion fail.
Definition Element.cpp:534
virtual double PointToLineDistance(wxPoint2DDouble point, int *segmentNumber=nullptr) const
Calculate the distance between a line (formed by point list) and a point.
Definition Element.cpp:616
virtual void CalculateBoundaries(wxPoint2DDouble &leftUp, wxPoint2DDouble &rightBottom) const
Calculate the element boundaries.
Definition Element.cpp:493
virtual void RemoveChild(Element *child)
Remove a child from the list.
Definition Element.cpp:582
virtual void ReplaceParent(Element *oldParent, Element *newParent)
Replace a parent.
Definition Element.cpp:574
virtual void DrawDCRectangle(wxPoint2DDouble position, double width, double height, double angle, wxDC &dc) const
Draw a circle.
Definition Element.cpp:41
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
Definition Element.cpp:335
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
Definition Element.cpp:33
virtual void DrawDCTriangle(std::vector< wxPoint2DDouble > points, wxGraphicsContext *gc) const
Draw rectangle.
Definition Element.cpp:209
virtual wxPoint2DDouble RotateAtPosition(wxPoint2DDouble pointToRotate, double angle, bool degrees=true) const
Rotate a point as element position being the origin.
Definition Element.cpp:298
virtual void DrawDCPickbox(wxPoint2DDouble position, wxGraphicsContext *gc) const
Draw a point.
Definition Element.cpp:291
Element()
Constructor.
Definition Element.cpp:28
virtual void Move(wxPoint2DDouble position)
Move the element other position.
Definition Element.cpp:341
virtual wxPoint2DDouble WorldToScreen(wxPoint2DDouble translation, double scale, double offsetX=0.0, double offsetY=0.0) const
Convert the element position to screen position.
Definition Element.cpp:354
virtual void AddChild(Element *child)
Add a child to the child list.
Definition Element.cpp:581
static bool DoubleFromString(wxWindow *parent, wxString strValue, double &value, wxString errorMsg)
Get a double value from a string. Show a error message if the conversion fail.
Definition Element.cpp:520
static wxString StringFromDouble(double value, int minDecimal=1, int maxDecimals=13)
Convert a double value to string.
Definition Element.cpp:548
virtual void ReplaceChild(Element *oldChild, Element *newChild)
Replace a child from the list.
Definition Element.cpp:590
bool SetOnline(bool online=true)
Set if the element is online or offline.
Definition Element.cpp:465
virtual void DrawDCCircle(wxPoint2DDouble position, double radius, int numSegments, wxGraphicsContext *gc) const
Draw a circle using device context.
Definition Element.cpp:173