20#include "../utils/DegreesAndRadians.h"
22#include "../utils/Path.h"
30 m_position = position;
32 wxRect2DDouble(m_position.m_x - m_width / 2.0 - m_borderSize, m_position.m_y - m_height / 2.0 - m_borderSize,
33 m_width + 2.0 * m_borderSize, m_height + 2.0 * m_borderSize);
40 wxPoint((
int)(position.m_x - width / 2.0), (
int)(position.m_y - height / 2.0)),
41 wxPoint((
int)(position.m_x + width / 2.0), (
int)(position.m_y - height / 2.0)),
42 wxPoint((
int)(position.m_x + width / 2.0), (
int)(position.m_y + height / 2.0)),
43 wxPoint((
int)(position.m_x - width / 2.0), (
int)(position.m_y + height / 2.0))
45 dc.DrawPolygon(4, poly);
49 double hw = width / 2.0;
50 double hh = height / 2.0;
51 double angleRad = wxDegToRad(angle);
54 wxPoint2DDouble pts[4] = {
63 for (
int i = 0; i < 4; ++i)
65 double x = pts[i].m_x;
66 double y = pts[i].m_y;
69 double xr = x * cos(angleRad) - y * sin(angleRad);
70 double yr = x * sin(angleRad) + y * cos(angleRad);
73 poly[i].x = (int)(xr + position.m_x);
74 poly[i].y = (int)(yr + position.m_y);
77 dc.DrawPolygon(4, poly);
81void Element::DrawDCRoundedRectRotated(wxDC& dc,
const wxPoint2DDouble& center,
double width,
double height,
double radius,
double angleDeg,
int arcSegments)
const
83 radius = std::min(radius, std::min(width, height) / 2.0);
85 double hw = width / 2.0;
86 double hh = height / 2.0;
88 double rad = wxDegToRad(angleDeg);
89 double c = std::cos(rad);
90 double s = std::sin(rad);
92 auto rotate = [&](
double x,
double y)
94 double xr = c * x - s * y + center.m_x;
95 double yr = s * x + c * y + center.m_y;
96 return wxPoint2DDouble(xr, yr);
99 auto drawArc = [&](
double cx,
double cy,
102 wxPoint2DDouble arcCenterLocal(cx, cy);
103 wxPoint2DDouble arcCenter = rotate(arcCenterLocal.m_x,
106 double start = startDeg + angleDeg;
107 double end = start + 90.0;
110 wxRound(arcCenter.m_x - radius),
111 wxRound(arcCenter.m_y - radius),
119 auto line = [&](
double x1,
double y1,
120 double x2,
double y2,
121 wxPoint& p1, wxPoint& p2)
123 wxPoint2DDouble p1d = rotate(x1, y1);
124 wxPoint2DDouble p2d = rotate(x2, y2);
125 p1 = wxPoint(wxRound(p1d.m_x), wxRound(p1d.m_y));
126 p2 = wxPoint(wxRound(p2d.m_x), wxRound(p2d.m_y));
131 wxPen pen = dc.GetPen();
134 line(-hw + radius, -hh,
138 line(hw - radius, hh,
142 dc.SetPen(*wxTRANSPARENT_PEN);
143 dc.DrawPolygon(4, pts);
145 dc.DrawLine(pts[0].x, pts[0].y, pts[1].x, pts[1].y);
146 dc.DrawLine(pts[2].x, pts[2].y, pts[3].x, pts[3].y);
148 line(hw, -hh + radius,
152 line(-hw, hh - radius,
156 dc.SetPen(*wxTRANSPARENT_PEN);
157 dc.DrawPolygon(4, pts);
159 dc.DrawLine(pts[0].x, pts[0].y, pts[1].x, pts[1].y);
160 dc.DrawLine(pts[2].x, pts[2].y, pts[3].x, pts[3].y);
162 drawArc(hw - radius, -hh + radius, 0 - angleDeg * 2);
163 drawArc(hw - radius, hh - radius, 270 - angleDeg * 2);
164 drawArc(-hw + radius, hh - radius, 180 - angleDeg * 2);
165 drawArc(-hw + radius, -hh + radius, 90 - angleDeg * 2);
170 wxPoint2DDouble* pts =
new wxPoint2DDouble[numSegments + 1];
171 for (
int i = 0; i < numSegments; i++) {
172 double theta = 2.0 * 3.1415926 * double(i) / double(numSegments);
173 pts[i] = wxPoint2DDouble(radius * std::cos(theta) + position.m_x, radius * std::sin(theta) + position.m_y);
175 pts[numSegments] = pts[0];
176 gc->DrawLines(numSegments + 1, pts);
182 dc.DrawCircle(wxRound(position.m_x), wxRound(position.m_y), wxRound(radius));
185void Element::DrawDCArc(wxPoint2DDouble position,
double radius,
double initAngle,
double finalAngle,
int numSegments, wxGraphicsContext* gc)
const
187 double initAngRad = wxDegToRad(initAngle);
188 double finalAngRad = wxDegToRad(finalAngle);
189 wxPoint2DDouble* points =
new wxPoint2DDouble[numSegments + 2];
190 for (
int i = 0; i <= numSegments; i++) {
191 double theta = initAngRad + (finalAngRad - initAngRad) *
double(i) / double(numSegments);
192 points[i] = wxPoint2DDouble(radius * std::cos(theta) + position.m_x, radius * std::sin(theta) + position.m_y);
195 gc->StrokeLines(numSegments + 1, points);
199void Element::DrawDCArc(wxPoint2DDouble position,
double radius,
double initAngle,
double finalAngle, wxDC& dc)
const
201 dc.DrawEllipticArc(wxRound(position.m_x - radius), wxRound(position.m_y - radius), wxRound(2 * radius), wxRound(2 * radius), initAngle, finalAngle);
206 points.emplace_back(points[0]);
207 gc->DrawLines(4, &points[0]);
212 points.emplace_back(points[0]);
213 dc.DrawPolygon(4, &points[0]);
218 gc->SetPen(wxPen(wxColour(0, 0, 0, 255)));
219 gc->SetBrush(wxBrush(wxColour(255, 255, 255, 204)));
220 gc->DrawRectangle(position.m_x, position.m_y, 8, 8);
225 double radAngle = angle;
226 if (degrees) radAngle = wxDegToRad(angle);
227 return wxPoint2DDouble(std::cos(radAngle) * (pointToRotate.m_x - m_position.m_x) -
228 std::sin(radAngle) * (pointToRotate.m_y - m_position.m_y) + m_position.m_x,
229 std::sin(radAngle) * (pointToRotate.m_x - m_position.m_x) +
230 std::cos(radAngle) * (pointToRotate.m_y - m_position.m_y) + m_position.m_y);
233wxPoint2DDouble Element::RotateLocal(wxPoint2DDouble local,
double angleDeg)
const
235 double rad = wxDegToRad(angleDeg);
236 double c = std::cos(rad);
237 double s = std::sin(rad);
239 return wxPoint2DDouble(
240 c * local.m_x - s * local.m_y,
241 s * local.m_x + c * local.m_y
245wxPoint Element::RotateAround(
const wxPoint2DDouble& p,
const wxPoint2DDouble& center,
double angleDeg)
const
247 double rad = wxDegToRad(angleDeg);
248 double c = std::cos(rad);
249 double s = std::sin(rad);
251 double dx = p.m_x - center.m_x;
252 double dy = p.m_y - center.m_y;
254 double xr = c * dx - s * dy + center.m_x;
255 double yr = s * dx + c * dy + center.m_y;
257 return wxPoint(wxRound(xr), wxRound(yr));
262 this->m_moveStartPt = position;
263 this->m_movePos = m_position;
269 return wxPoint2DDouble(m_position.m_x + offsetX + translation.m_x, m_position.m_y + offsetY + translation.m_y) *
274 wxPoint2DDouble translation,
277 double offsetY)
const
279 return wxPoint2DDouble(position.m_x + offsetX + translation.m_x, position.m_y + offsetY + translation.m_y) * scale;
291 wxRect2DDouble rect2,
295 wxPoint2DDouble rect1Corners[4] = { rect1.GetLeftTop(), rect1.GetLeftBottom(), rect1.GetRightBottom(),
296 rect1.GetRightTop() };
297 wxPoint2DDouble rect2Corners[4] = { rect2.GetLeftTop(), rect2.GetLeftBottom(), rect2.GetRightBottom(),
298 rect2.GetRightTop() };
299 wxPoint2DDouble rect1Center(rect1.m_x + rect1.m_width / 2.0, rect1.m_y + rect1.m_height / 2.0);
300 wxPoint2DDouble rect2Center(rect2.m_x + rect2.m_width / 2.0, rect2.m_y + rect2.m_height / 2.0);
303 double radAngle1 = wxDegToRad(angle1);
304 double radAngle2 = wxDegToRad(angle2);
306 for (
int i = 0; i < 4; i++) {
308 wxPoint2DDouble(std::cos(radAngle1) * (rect1Corners[i].m_x - rect1Center.m_x) -
309 std::sin(radAngle1) * (rect1Corners[i].m_y - rect1Center.m_y) + rect1Center.m_x,
310 std::sin(radAngle1) * (rect1Corners[i].m_x - rect1Center.m_x) +
311 std::cos(radAngle1) * (rect1Corners[i].m_y - rect1Center.m_y) + rect1Center.m_y);
314 wxPoint2DDouble(std::cos(radAngle2) * (rect2Corners[i].m_x - rect2Center.m_x) -
315 std::sin(radAngle2) * (rect2Corners[i].m_y - rect2Center.m_y) + rect2Center.m_x,
316 std::sin(radAngle2) * (rect2Corners[i].m_x - rect2Center.m_x) +
317 std::cos(radAngle2) * (rect2Corners[i].m_y - rect2Center.m_y) + rect2Center.m_y);
323 wxPoint2DDouble axis[4] = { rect1Corners[3] - rect1Corners[0], rect1Corners[3] - rect1Corners[2],
324 rect2Corners[3] - rect2Corners[0], rect2Corners[3] - rect2Corners[2] };
327 wxPoint2DDouble rect1ProjPts[4][4];
328 wxPoint2DDouble rect2ProjPts[4][4];
329 for (
int i = 0; i < 4; i++) {
330 double den = axis[i].m_x * axis[i].m_x + axis[i].m_y * axis[i].m_y;
331 for (
int j = 0; j < 4; j++) {
332 double m_rectProj = (rect1Corners[j].m_x * axis[i].m_x + rect1Corners[j].m_y * axis[i].m_y) / den;
333 double rectProj = (rect2Corners[j].m_x * axis[i].m_x + rect2Corners[j].m_y * axis[i].m_y) / den;
335 rect1ProjPts[i][j] = wxPoint2DDouble(m_rectProj * axis[i].m_x, m_rectProj * axis[i].m_y);
336 rect2ProjPts[i][j] = wxPoint2DDouble(rectProj * axis[i].m_x, rectProj * axis[i].m_y);
341 double rect1Scalar[4][4];
342 double rect2Scalar[4][4];
343 for (
int i = 0; i < 4; i++) {
344 for (
int j = 0; j < 4; j++) {
345 rect1Scalar[i][j] = rect1ProjPts[i][j].m_x * axis[i].m_x + rect1ProjPts[i][j].m_y * axis[i].m_y;
346 rect2Scalar[i][j] = rect2ProjPts[i][j].m_x * axis[i].m_x + rect2ProjPts[i][j].m_y * axis[i].m_y;
355 for (
int i = 0; i < 4; i++) {
356 rect1Max[i] = rect1Scalar[i][0];
357 rect2Max[i] = rect2Scalar[i][0];
358 rect1Min[i] = rect1Scalar[i][0];
359 rect2Min[i] = rect2Scalar[i][0];
361 for (
int j = 1; j < 4; j++) {
362 if (rect1Max[i] < rect1Scalar[i][j]) rect1Max[i] = rect1Scalar[i][j];
363 if (rect2Max[i] < rect2Scalar[i][j]) rect2Max[i] = rect2Scalar[i][j];
365 if (rect1Min[i] > rect1Scalar[i][j]) rect1Min[i] = rect1Scalar[i][j];
366 if (rect2Min[i] > rect2Scalar[i][j]) rect2Min[i] = rect2Scalar[i][j];
371 for (
int i = 0; i < 4; i++) {
372 if (!(rect2Min[i] <= rect1Max[i] && rect2Max[i] >= rect1Min[i]))
return false;
381 for (
auto it = m_parentList.begin(); it != m_parentList.end(); it++) {
382 if (!(*it))
return false;
390 wxFileName exeFileName(wxStandardPaths::Get().GetExecutablePath());
393 wxMenuItem* clockItem =
new wxMenuItem(&menu,
ID_ROTATE_CLOCK, _(
"Rotate clockwise"));
394 clockItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/rotateClock16.png"));
395 menu.Append(clockItem);
397 wxMenuItem* counterClockItem =
new wxMenuItem(&menu,
ID_ROTATE_COUNTERCLOCK, _(
"Rotate counter-clockwise"));
398 counterClockItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/rotateCounterClock16.png"));
399 menu.Append(counterClockItem);
401 wxMenuItem* deleteItem =
new wxMenuItem(&menu,
ID_DELETE, _(
"Delete"));
402 deleteItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/delete16.png"));
403 menu.Append(deleteItem);
411 wxPoint2DDouble rectCorner[4] = { m_rect.GetLeftTop(), m_rect.GetLeftBottom(), m_rect.GetRightBottom(),
412 m_rect.GetRightTop() };
414 for (
int i = 0; i < 4; ++i) { rectCorner[i] =
RotateAtPosition(rectCorner[i], m_angle); }
415 leftUp = rectCorner[0];
416 rightBottom = rectCorner[0];
417 for (
int i = 1; i < 4; ++i) {
418 if (rectCorner[i].m_x < leftUp.m_x) leftUp.m_x = rectCorner[i].m_x;
419 if (rectCorner[i].m_y < leftUp.m_y) leftUp.m_y = rectCorner[i].m_y;
420 if (rectCorner[i].m_x > rightBottom.m_x) rightBottom.m_x = rectCorner[i].m_x;
421 if (rectCorner[i].m_y > rightBottom.m_y) rightBottom.m_y = rectCorner[i].m_y;
425 for (
int i = 0; i < (int)m_pointList.size(); i++) {
426 if (m_pointList[i].m_x < leftUp.m_x) leftUp.m_x = m_pointList[i].m_x;
427 if (m_pointList[i].m_y < leftUp.m_y) leftUp.m_y = m_pointList[i].m_y;
428 if (m_pointList[i].m_x > rightBottom.m_x) rightBottom.m_x = m_pointList[i].m_x;
429 if (m_pointList[i].m_y > rightBottom.m_y) rightBottom.m_y = m_pointList[i].m_y;
437 if (!strValue.ToDouble(&dValue)) {
438 wxMessageDialog msgDialog(parent, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
439 msgDialog.ShowModal();
451 if (!strValue.ToLong(&iValue)) {
452 wxMessageDialog msgDialog(parent, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
453 msgDialog.ShowModal();
463 wxString str = wxString::FromCDouble(value, maxDecimals);
466 bool foundCut =
false;
467 for (
int i = (
int)str.length() - 1; i >= 0; i--) {
468 if (str[i] !=
'0' && !foundCut) {
478 wxString formatedStr =
"";
479 if (cutNumber - numDecimal > minDecimal)
480 formatedStr = wxString::FromDouble(value, cutNumber - numDecimal);
482 formatedStr = wxString::FromDouble(value, minDecimal);
489 for (
int i = 0; i < (int)m_parentList.size(); i++) {
490 if (m_parentList[i] == oldParent) m_parentList[i] = newParent;
497 for (
auto it = m_childList.begin(); it != m_childList.end();) {
498 if (*it == child) it = m_childList.erase(it);
505 for (
int i = 0; i < (int)m_childList.size(); i++) {
506 if (m_childList[i] == oldChild) m_childList[i] = newChild;
532 double distance = 100.0;
533 wxPoint2DDouble p0 = point;
535 for (
int i = 1; i < (int)m_pointList.size() - 2; i++) {
538 wxPoint2DDouble p1 = m_pointList[i];
539 wxPoint2DDouble p2 = m_pointList[i + 1];
541 wxPoint2DDouble v = p2 - p1;
542 wxPoint2DDouble w = p0 - p1;
544 double c1 = w.m_x * v.m_x + w.m_y * v.m_y;
545 double c2 = v.m_x * v.m_x + v.m_y * v.m_y;
548 d = std::sqrt(std::pow(p0.m_y - p1.m_y, 2) + std::pow(p0.m_x - p1.m_x, 2));
551 d = std::sqrt(std::pow(p0.m_y - p2.m_y, 2) + std::pow(p0.m_x - p2.m_x, 2));
554 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) /
555 std::sqrt(std::pow(p2.m_y - p1.m_y, 2) + std::pow(p2.m_x - p1.m_x, 2));
559 if (segmentNumber) *segmentNumber = i;
566void Element::SaveCADProperties(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementNode)
568 auto cadProp = XMLParser::AppendNode(doc, elementNode,
"CADProperties");
569 auto position = XMLParser::AppendNode(doc, cadProp,
"Position");
570 auto posX = XMLParser::AppendNode(doc, position,
"X");
571 XMLParser::SetNodeValue(doc, posX, m_position.m_x);
572 auto posY = XMLParser::AppendNode(doc, position,
"Y");
573 XMLParser::SetNodeValue(doc, posY, m_position.m_y);
574 auto size = XMLParser::AppendNode(doc, cadProp,
"Size");
575 auto width = XMLParser::AppendNode(doc, size,
"Width");
576 XMLParser::SetNodeValue(doc, width, m_width);
577 auto height = XMLParser::AppendNode(doc, size,
"Height");
578 XMLParser::SetNodeValue(doc, height, m_height);
579 auto angle = XMLParser::AppendNode(doc, cadProp,
"Angle");
580 XMLParser::SetNodeValue(doc, angle, m_angle);
583bool Element::OpenCADProperties(rapidxml::xml_node<>* elementNode)
585 auto cadPropNode = elementNode->first_node(
"CADProperties");
586 if (!cadPropNode)
return false;
588 auto position = cadPropNode->first_node(
"Position");
589 double posX = XMLParser::GetNodeValueDouble(position,
"X");
590 double posY = XMLParser::GetNodeValueDouble(position,
"Y");
591 auto size = cadPropNode->first_node(
"Size");
592 m_width = XMLParser::GetNodeValueDouble(size,
"Width");
593 m_height = XMLParser::GetNodeValueDouble(size,
"Height");
594 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.