20#include "../utils/DegreesAndRadians.h"
29 m_position = position;
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);
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))
54 dc.DrawPolygon(4, poly);
58 double hw = width / 2.0;
59 double hh = height / 2.0;
60 double angleRad = wxDegToRad(angle);
63 wxPoint2DDouble pts[4] = {
72 for (
int i = 0; i < 4; ++i)
74 double x = pts[i].m_x;
75 double y = pts[i].m_y;
78 double xr = x * cos(angleRad) - y * sin(angleRad);
79 double yr = x * sin(angleRad) + y * cos(angleRad);
82 poly[i].x = (int)(xr + position.m_x);
83 poly[i].y = (int)(yr + position.m_y);
86 dc.DrawPolygon(4, poly);
90void Element::DrawDCRoundedRectRotated(wxDC& dc,
const wxPoint2DDouble& center,
double width,
double height,
double radius,
double angleDeg,
int arcSegments)
const
92 radius = std::min(radius, std::min(width, height) / 2.0);
94 double hw = width / 2.0;
95 double hh = height / 2.0;
97 double rad = wxDegToRad(angleDeg);
98 double c = std::cos(rad);
99 double s = std::sin(rad);
101 auto rotate = [&](
double x,
double y)
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);
108 auto drawArc = [&](
double cx,
double cy,
111 wxPoint2DDouble arcCenterLocal(cx, cy);
112 wxPoint2DDouble arcCenter = rotate(arcCenterLocal.m_x,
115 double start = startDeg + angleDeg;
116 double end = start + 90.0;
119 wxRound(arcCenter.m_x - radius),
120 wxRound(arcCenter.m_y - radius),
128 auto line = [&](
double x1,
double y1,
129 double x2,
double y2,
130 wxPoint& p1, wxPoint& p2)
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));
140 wxPen pen = dc.GetPen();
143 line(-hw + radius, -hh,
147 line(hw - radius, hh,
151 dc.SetPen(*wxTRANSPARENT_PEN);
152 dc.DrawPolygon(4, pts);
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);
157 line(hw, -hh + radius,
161 line(-hw, hh - radius,
165 dc.SetPen(*wxTRANSPARENT_PEN);
166 dc.DrawPolygon(4, pts);
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);
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);
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);
184 pts[numSegments] = pts[0];
185 gc->DrawLines(numSegments + 1, pts);
191 dc.DrawCircle(wxRound(position.m_x), wxRound(position.m_y), wxRound(radius));
211void Element::DrawDCArc(wxPoint2DDouble position,
double radius,
double initAngle,
double finalAngle,
int numSegments, wxGraphicsContext* gc)
const
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);
221 gc->StrokeLines(numSegments + 1, points);
225void Element::DrawDCArc(wxPoint2DDouble position,
double radius,
double initAngle,
double finalAngle, wxDC& dc)
const
227 dc.DrawEllipticArc(wxRound(position.m_x - radius), wxRound(position.m_y - radius), wxRound(2 * radius), wxRound(2 * radius), initAngle, finalAngle);
239 points.emplace_back(points[0]);
240 gc->DrawLines(4, &points[0]);
245 points.emplace_back(points[0]);
246 dc.DrawPolygon(4, &points[0]);
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);
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);
302wxPoint2DDouble Element::RotateLocal(wxPoint2DDouble local,
double angleDeg)
const
304 double rad = wxDegToRad(angleDeg);
305 double c = std::cos(rad);
306 double s = std::sin(rad);
308 return wxPoint2DDouble(
309 c * local.m_x - s * local.m_y,
310 s * local.m_x + c * local.m_y
314wxPoint Element::RotateAround(
const wxPoint2DDouble& p,
const wxPoint2DDouble& center,
double angleDeg)
const
316 double rad = wxDegToRad(angleDeg);
317 double c = std::cos(rad);
318 double s = std::sin(rad);
320 double dx = p.m_x - center.m_x;
321 double dy = p.m_y - center.m_y;
323 double xr = c * dx - s * dy + center.m_x;
324 double yr = s * dx + c * dy + center.m_y;
326 return wxPoint(wxRound(xr), wxRound(yr));
331 this->m_moveStartPt = position;
332 this->m_movePos = m_position;
338 return wxPoint2DDouble(m_position.m_x + offsetX + translation.m_x, m_position.m_y + offsetY + translation.m_y) *
343 wxPoint2DDouble translation,
346 double offsetY)
const
348 return wxPoint2DDouble(position.m_x + offsetX + translation.m_x, position.m_y + offsetY + translation.m_y) * scale;
360 wxRect2DDouble rect2,
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);
372 double radAngle1 = wxDegToRad(angle1);
373 double radAngle2 = wxDegToRad(angle2);
375 for (
int i = 0; i < 4; 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);
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);
392 wxPoint2DDouble axis[4] = { rect1Corners[3] - rect1Corners[0], rect1Corners[3] - rect1Corners[2],
393 rect2Corners[3] - rect2Corners[0], rect2Corners[3] - rect2Corners[2] };
396 wxPoint2DDouble rect1ProjPts[4][4];
397 wxPoint2DDouble rect2ProjPts[4][4];
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;
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);
410 double rect1Scalar[4][4];
411 double rect2Scalar[4][4];
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;
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];
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];
434 if (rect1Min[i] > rect1Scalar[i][j]) rect1Min[i] = rect1Scalar[i][j];
435 if (rect2Min[i] > rect2Scalar[i][j]) rect2Min[i] = rect2Scalar[i][j];
440 for (
int i = 0; i < 4; i++) {
441 if (!(rect2Min[i] <= rect1Max[i] && rect2Max[i] >= rect1Min[i]))
return false;
450 for (
auto it = m_parentList.begin(); it != m_parentList.end(); it++) {
451 if (!(*it))
return false;
459 wxFileName exeFileName(wxStandardPaths::Get().GetExecutablePath());
460 wxString exePath = exeFileName.GetPath();
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);
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);
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);
483 wxPoint2DDouble rectCorner[4] = { m_rect.GetLeftTop(), m_rect.GetLeftBottom(), m_rect.GetRightBottom(),
484 m_rect.GetRightTop() };
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;
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;
509 if (!strValue.ToDouble(&dValue)) {
510 wxMessageDialog msgDialog(parent, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
511 msgDialog.ShowModal();
523 if (!strValue.ToLong(&iValue)) {
524 wxMessageDialog msgDialog(parent, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
525 msgDialog.ShowModal();
535 wxString str = wxString::FromCDouble(value, maxDecimals);
538 bool foundCut =
false;
539 for (
int i = (
int)str.length() - 1; i >= 0; i--) {
540 if (str[i] !=
'0' && !foundCut) {
550 wxString formatedStr =
"";
551 if (cutNumber - numDecimal > minDecimal)
552 formatedStr = wxString::FromDouble(value, cutNumber - numDecimal);
554 formatedStr = wxString::FromDouble(value, minDecimal);
561 for (
int i = 0; i < (int)m_parentList.size(); i++) {
562 if (m_parentList[i] == oldParent) m_parentList[i] = newParent;
569 for (
auto it = m_childList.begin(); it != m_childList.end();) {
570 if (*it == child) it = m_childList.erase(it);
577 for (
int i = 0; i < (int)m_childList.size(); i++) {
578 if (m_childList[i] == oldChild) m_childList[i] = newChild;
604 double distance = 100.0;
605 wxPoint2DDouble p0 = point;
607 for (
int i = 1; i < (int)m_pointList.size() - 2; i++) {
610 wxPoint2DDouble p1 = m_pointList[i];
611 wxPoint2DDouble p2 = m_pointList[i + 1];
613 wxPoint2DDouble v = p2 - p1;
614 wxPoint2DDouble w = p0 - p1;
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;
620 d = std::sqrt(std::pow(p0.m_y - p1.m_y, 2) + std::pow(p0.m_x - p1.m_x, 2));
623 d = std::sqrt(std::pow(p0.m_y - p2.m_y, 2) + std::pow(p0.m_x - p2.m_x, 2));
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));
631 if (segmentNumber) *segmentNumber = i;
638void Element::SaveCADProperties(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementNode)
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);
655bool Element::OpenCADProperties(rapidxml::xml_node<>* elementNode)
657 auto cadPropNode = elementNode->first_node(
"CADProperties");
658 if (!cadPropNode)
return false;
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");
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.