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