20#include "../utils/DegreesAndRadians.h"
22#include "../utils/Path.h"
23#include "../utils/PropertiesData.h"
35 m_position = position;
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);
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))
50 dc.DrawPolygon(4, poly);
54 double hw = width / 2.0;
55 double hh = height / 2.0;
56 double angleRad = wxDegToRad(angle);
59 wxPoint2DDouble pts[4] = {
68 for (
int i = 0; i < 4; ++i)
70 double x = pts[i].m_x;
71 double y = pts[i].m_y;
74 double xr = x * cos(angleRad) - y * sin(angleRad);
75 double yr = x * sin(angleRad) + y * cos(angleRad);
78 poly[i].x = (int)(xr + position.m_x);
79 poly[i].y = (int)(yr + position.m_y);
82 dc.DrawPolygon(4, poly);
86void Element::DrawDCRoundedRectRotated(wxDC& dc,
const wxPoint2DDouble& center,
double width,
double height,
double radius,
double angleDeg,
int arcSegments)
const
88 radius = std::min(radius, std::min(width, height) / 2.0);
90 double hw = width / 2.0;
91 double hh = height / 2.0;
93 double rad = wxDegToRad(angleDeg);
94 double c = std::cos(rad);
95 double s = std::sin(rad);
97 auto rotate = [&](
double x,
double y)
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);
104 auto drawArc = [&](
double cx,
double cy,
107 wxPoint2DDouble arcCenterLocal(cx, cy);
108 wxPoint2DDouble arcCenter = rotate(arcCenterLocal.m_x,
111 double start = startDeg + angleDeg;
112 double end = start + 90.0;
115 wxRound(arcCenter.m_x - radius),
116 wxRound(arcCenter.m_y - radius),
124 auto line = [&](
double x1,
double y1,
125 double x2,
double y2,
126 wxPoint& p1, wxPoint& p2)
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));
136 wxPen pen = dc.GetPen();
139 line(-hw + radius, -hh,
143 line(hw - radius, hh,
147 dc.SetPen(*wxTRANSPARENT_PEN);
148 dc.DrawPolygon(4, pts);
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);
153 line(hw, -hh + radius,
157 line(-hw, hh - radius,
161 dc.SetPen(*wxTRANSPARENT_PEN);
162 dc.DrawPolygon(4, pts);
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);
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);
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);
180 pts[numSegments] = pts[0];
181 gc->DrawLines(numSegments + 1, pts);
187 dc.DrawCircle(wxRound(position.m_x), wxRound(position.m_y), wxRound(radius));
190void Element::DrawDCArc(wxPoint2DDouble position,
double radius,
double initAngle,
double finalAngle,
int numSegments, wxGraphicsContext* gc)
const
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);
200 gc->StrokeLines(numSegments + 1, points);
204void Element::DrawDCArc(wxPoint2DDouble position,
double radius,
double initAngle,
double finalAngle, wxDC& dc)
const
206 dc.DrawEllipticArc(wxRound(position.m_x - radius), wxRound(position.m_y - radius), wxRound(2 * radius), wxRound(2 * radius), initAngle, finalAngle);
211 points.emplace_back(points[0]);
212 gc->DrawLines(4, &points[0]);
217 points.emplace_back(points[0]);
218 dc.DrawPolygon(4, &points[0]);
221void Element::DrawStabilityEventGC(wxGraphicsContext* gc, wxPoint2DDouble translation,
double scale,
GUIColour* guiColour,
bool rotateAnchor, wxPoint2DDouble iconPosition)
const
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;
234 wxGraphicsMatrix identityMatrix = gc->GetTransform();
235 identityMatrix.Set();
238 gc->SetTransform(identityMatrix);
240 wxPoint2DDouble screenPt;
243 if (iconPosition.m_x != -1.0 || iconPosition.m_y != -1.0) {
244 screenPt =
WorldToScreen(iconPosition, translation, scale, 0.0, 0.0);
248 double anchorAngle = std::fmod(m_angle, 180.0);
250 if (anchorAngle >= 90.0)
251 anchorAngle -= 180.0;
252 else if (anchorAngle < -90.0)
253 anchorAngle += 180.0;
256 wxPoint2DDouble anchor(m_width / 2.0, 0.0);
259 anchor = RotateLocal(anchor, anchorAngle);
262 screenPt =
WorldToScreen(translation, scale, anchor.m_x, anchor.m_y);
266 screenPt.m_x += radius + margin * scale;
267 screenPt.m_y -= radius + crownStem + crownHeight + margin * scale;
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);
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);
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);
282 wxPen handPen(guiColour->eventElement, handWidth);
283 handPen.SetCap(wxCAP_ROUND);
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);
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);
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);
308wxPoint2DDouble Element::RotateLocal(wxPoint2DDouble local,
double angleDeg)
const
310 double rad = wxDegToRad(angleDeg);
311 double c = std::cos(rad);
312 double s = std::sin(rad);
314 return wxPoint2DDouble(
315 c * local.m_x - s * local.m_y,
316 s * local.m_x + c * local.m_y
320wxPoint Element::RotateAround(
const wxPoint2DDouble& p,
const wxPoint2DDouble& center,
double angleDeg)
const
322 double rad = wxDegToRad(angleDeg);
323 double c = std::cos(rad);
324 double s = std::sin(rad);
326 double dx = p.m_x - center.m_x;
327 double dy = p.m_y - center.m_y;
329 double xr = c * dx - s * dy + center.m_x;
330 double yr = s * dx + c * dy + center.m_y;
332 return wxPoint(wxRound(xr), wxRound(yr));
337 this->m_moveStartPt = position;
338 this->m_movePos = m_position;
342void Element::AlignToGrid(
double gridSize)
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;
350 for (
auto& pt : m_pointList) {
356 return wxPoint2DDouble(m_position.m_x + offsetX + translation.m_x, m_position.m_y + offsetY + translation.m_y) *
361 wxPoint2DDouble translation,
364 double offsetY)
const
366 return wxPoint2DDouble(position.m_x + offsetX + translation.m_x, position.m_y + offsetY + translation.m_y) * scale;
378 wxRect2DDouble rect2,
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);
390 double radAngle1 = wxDegToRad(angle1);
391 double radAngle2 = wxDegToRad(angle2);
393 for (
int i = 0; i < 4; 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);
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);
410 wxPoint2DDouble axis[4] = { rect1Corners[3] - rect1Corners[0], rect1Corners[3] - rect1Corners[2],
411 rect2Corners[3] - rect2Corners[0], rect2Corners[3] - rect2Corners[2] };
414 wxPoint2DDouble rect1ProjPts[4][4];
415 wxPoint2DDouble rect2ProjPts[4][4];
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;
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);
428 double rect1Scalar[4][4];
429 double rect2Scalar[4][4];
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;
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];
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];
452 if (rect1Min[i] > rect1Scalar[i][j]) rect1Min[i] = rect1Scalar[i][j];
453 if (rect2Min[i] > rect2Scalar[i][j]) rect2Min[i] = rect2Scalar[i][j];
458 for (
int i = 0; i < 4; i++) {
459 if (!(rect2Min[i] <= rect1Max[i] && rect2Max[i] >= rect1Min[i]))
return false;
468 for (
auto it = m_parentList.begin(); it != m_parentList.end(); it++) {
469 if (!(*it))
return false;
477 wxFileName exeFileName(wxStandardPaths::Get().GetExecutablePath());
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);
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);
488 wxMenuItem* deleteItem =
new wxMenuItem(&menu,
ID_DELETE, _(
"Delete"));
489 deleteItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/delete16.png"));
490 menu.Append(deleteItem);
498 wxPoint2DDouble rectCorner[4] = { m_rect.GetLeftTop(), m_rect.GetLeftBottom(), m_rect.GetRightBottom(),
499 m_rect.GetRightTop() };
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;
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;
524 if (!strValue.ToDouble(&dValue)) {
525 wxMessageDialog msgDialog(parent, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
526 msgDialog.ShowModal();
538 if (!strValue.ToLong(&iValue)) {
539 wxMessageDialog msgDialog(parent, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
540 msgDialog.ShowModal();
550 wxString str = wxString::FromCDouble(value, maxDecimals);
553 bool foundCut =
false;
554 for (
int i = (
int)str.length() - 1; i >= 0; i--) {
555 if (str[i] !=
'0' && !foundCut) {
565 wxString formatedStr =
"";
566 if (cutNumber - numDecimal > minDecimal)
567 formatedStr = wxString::FromDouble(value, cutNumber - numDecimal);
569 formatedStr = wxString::FromDouble(value, minDecimal);
576 for (
int i = 0; i < (int)m_parentList.size(); i++) {
577 if (m_parentList[i] == oldParent) m_parentList[i] = newParent;
584 for (
auto it = m_childList.begin(); it != m_childList.end();) {
585 if (*it == child) it = m_childList.erase(it);
592 for (
int i = 0; i < (int)m_childList.size(); i++) {
593 if (m_childList[i] == oldChild) m_childList[i] = newChild;
619 double distance = 100.0;
620 wxPoint2DDouble p0 = point;
622 for (
int i = 1; i < (int)m_pointList.size() - 2; i++) {
625 wxPoint2DDouble p1 = m_pointList[i];
626 wxPoint2DDouble p2 = m_pointList[i + 1];
628 wxPoint2DDouble v = p2 - p1;
629 wxPoint2DDouble w = p0 - p1;
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;
635 d = std::sqrt(std::pow(p0.m_y - p1.m_y, 2) + std::pow(p0.m_x - p1.m_x, 2));
638 d = std::sqrt(std::pow(p0.m_y - p2.m_y, 2) + std::pow(p0.m_x - p2.m_x, 2));
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));
646 if (segmentNumber) *segmentNumber = i;
653void Element::SaveCADProperties(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementNode)
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);
670bool Element::OpenCADProperties(rapidxml::xml_node<>* elementNode)
672 auto cadPropNode = elementNode->first_node(
"CADProperties");
673 if (!cadPropNode)
return false;
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");
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
virtual bool RotatedRectanglesIntersects(wxRect2DDouble rect1, wxRect2DDouble rect2, double angle1, double angle2) const
Check if two roteted rectangles intersect.
virtual void GeneralMenuItens(wxMenu &menu)
Insert general itens to context menu.
static bool IntFromString(wxWindow *parent, wxString strValue, int &value, wxString errorMsg)
Convert a string to int. Show a error message if the conversion fail.
virtual double PointToLineDistance(wxPoint2DDouble point, int *segmentNumber=nullptr) const
Calculate the distance between a line (formed by point list) and a point.
virtual void CalculateBoundaries(wxPoint2DDouble &leftUp, wxPoint2DDouble &rightBottom) const
Calculate the element boundaries.
virtual void RemoveChild(Element *child)
Remove a child from the list.
virtual void ReplaceParent(Element *oldParent, Element *newParent)
Replace a parent.
virtual void DrawDCRectangle(wxPoint2DDouble position, double width, double height, double angle, wxDC &dc) const
Draw a circle.
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
virtual void DrawDCTriangle(std::vector< wxPoint2DDouble > points, wxGraphicsContext *gc) const
Draw rectangle.
virtual wxPoint2DDouble RotateAtPosition(wxPoint2DDouble pointToRotate, double angle, bool degrees=true) const
Rotate a point as element position being the origin.
virtual void DrawDCPickbox(wxPoint2DDouble position, wxGraphicsContext *gc) const
Draw a point.
virtual void Move(wxPoint2DDouble position)
Move the element other position.
virtual wxPoint2DDouble WorldToScreen(wxPoint2DDouble translation, double scale, double offsetX=0.0, double offsetY=0.0) const
Convert the element position to screen position.
virtual void AddChild(Element *child)
Add a child to the child list.
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.
static wxString StringFromDouble(double value, int minDecimal=1, int maxDecimals=13)
Convert a double value to string.
virtual void ReplaceChild(Element *oldChild, Element *newChild)
Replace a child from the list.
bool SetOnline(bool online=true)
Set if the element is online or offline.
virtual void DrawDCCircle(wxPoint2DDouble position, double radius, int numSegments, wxGraphicsContext *gc) const
Draw a circle using device context.