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]);
223 gc->SetPen(wxPen(wxColour(0, 0, 0, 255)));
224 gc->SetBrush(wxBrush(wxColour(255, 255, 255, 204)));
225 gc->DrawRectangle(position.m_x, position.m_y, 8, 8);
230 double radAngle = angle;
231 if (degrees) radAngle = wxDegToRad(angle);
232 return wxPoint2DDouble(std::cos(radAngle) * (pointToRotate.m_x - m_position.m_x) -
233 std::sin(radAngle) * (pointToRotate.m_y - m_position.m_y) + m_position.m_x,
234 std::sin(radAngle) * (pointToRotate.m_x - m_position.m_x) +
235 std::cos(radAngle) * (pointToRotate.m_y - m_position.m_y) + m_position.m_y);
238wxPoint2DDouble Element::RotateLocal(wxPoint2DDouble local,
double angleDeg)
const
240 double rad = wxDegToRad(angleDeg);
241 double c = std::cos(rad);
242 double s = std::sin(rad);
244 return wxPoint2DDouble(
245 c * local.m_x - s * local.m_y,
246 s * local.m_x + c * local.m_y
250wxPoint Element::RotateAround(
const wxPoint2DDouble& p,
const wxPoint2DDouble& center,
double angleDeg)
const
252 double rad = wxDegToRad(angleDeg);
253 double c = std::cos(rad);
254 double s = std::sin(rad);
256 double dx = p.m_x - center.m_x;
257 double dy = p.m_y - center.m_y;
259 double xr = c * dx - s * dy + center.m_x;
260 double yr = s * dx + c * dy + center.m_y;
262 return wxPoint(wxRound(xr), wxRound(yr));
267 this->m_moveStartPt = position;
268 this->m_movePos = m_position;
274 return wxPoint2DDouble(m_position.m_x + offsetX + translation.m_x, m_position.m_y + offsetY + translation.m_y) *
279 wxPoint2DDouble translation,
282 double offsetY)
const
284 return wxPoint2DDouble(position.m_x + offsetX + translation.m_x, position.m_y + offsetY + translation.m_y) * scale;
296 wxRect2DDouble rect2,
300 wxPoint2DDouble rect1Corners[4] = { rect1.GetLeftTop(), rect1.GetLeftBottom(), rect1.GetRightBottom(),
301 rect1.GetRightTop() };
302 wxPoint2DDouble rect2Corners[4] = { rect2.GetLeftTop(), rect2.GetLeftBottom(), rect2.GetRightBottom(),
303 rect2.GetRightTop() };
304 wxPoint2DDouble rect1Center(rect1.m_x + rect1.m_width / 2.0, rect1.m_y + rect1.m_height / 2.0);
305 wxPoint2DDouble rect2Center(rect2.m_x + rect2.m_width / 2.0, rect2.m_y + rect2.m_height / 2.0);
308 double radAngle1 = wxDegToRad(angle1);
309 double radAngle2 = wxDegToRad(angle2);
311 for (
int i = 0; i < 4; i++) {
313 wxPoint2DDouble(std::cos(radAngle1) * (rect1Corners[i].m_x - rect1Center.m_x) -
314 std::sin(radAngle1) * (rect1Corners[i].m_y - rect1Center.m_y) + rect1Center.m_x,
315 std::sin(radAngle1) * (rect1Corners[i].m_x - rect1Center.m_x) +
316 std::cos(radAngle1) * (rect1Corners[i].m_y - rect1Center.m_y) + rect1Center.m_y);
319 wxPoint2DDouble(std::cos(radAngle2) * (rect2Corners[i].m_x - rect2Center.m_x) -
320 std::sin(radAngle2) * (rect2Corners[i].m_y - rect2Center.m_y) + rect2Center.m_x,
321 std::sin(radAngle2) * (rect2Corners[i].m_x - rect2Center.m_x) +
322 std::cos(radAngle2) * (rect2Corners[i].m_y - rect2Center.m_y) + rect2Center.m_y);
328 wxPoint2DDouble axis[4] = { rect1Corners[3] - rect1Corners[0], rect1Corners[3] - rect1Corners[2],
329 rect2Corners[3] - rect2Corners[0], rect2Corners[3] - rect2Corners[2] };
332 wxPoint2DDouble rect1ProjPts[4][4];
333 wxPoint2DDouble rect2ProjPts[4][4];
334 for (
int i = 0; i < 4; i++) {
335 double den = axis[i].m_x * axis[i].m_x + axis[i].m_y * axis[i].m_y;
336 for (
int j = 0; j < 4; j++) {
337 double m_rectProj = (rect1Corners[j].m_x * axis[i].m_x + rect1Corners[j].m_y * axis[i].m_y) / den;
338 double rectProj = (rect2Corners[j].m_x * axis[i].m_x + rect2Corners[j].m_y * axis[i].m_y) / den;
340 rect1ProjPts[i][j] = wxPoint2DDouble(m_rectProj * axis[i].m_x, m_rectProj * axis[i].m_y);
341 rect2ProjPts[i][j] = wxPoint2DDouble(rectProj * axis[i].m_x, rectProj * axis[i].m_y);
346 double rect1Scalar[4][4];
347 double rect2Scalar[4][4];
348 for (
int i = 0; i < 4; i++) {
349 for (
int j = 0; j < 4; j++) {
350 rect1Scalar[i][j] = rect1ProjPts[i][j].m_x * axis[i].m_x + rect1ProjPts[i][j].m_y * axis[i].m_y;
351 rect2Scalar[i][j] = rect2ProjPts[i][j].m_x * axis[i].m_x + rect2ProjPts[i][j].m_y * axis[i].m_y;
360 for (
int i = 0; i < 4; i++) {
361 rect1Max[i] = rect1Scalar[i][0];
362 rect2Max[i] = rect2Scalar[i][0];
363 rect1Min[i] = rect1Scalar[i][0];
364 rect2Min[i] = rect2Scalar[i][0];
366 for (
int j = 1; j < 4; j++) {
367 if (rect1Max[i] < rect1Scalar[i][j]) rect1Max[i] = rect1Scalar[i][j];
368 if (rect2Max[i] < rect2Scalar[i][j]) rect2Max[i] = rect2Scalar[i][j];
370 if (rect1Min[i] > rect1Scalar[i][j]) rect1Min[i] = rect1Scalar[i][j];
371 if (rect2Min[i] > rect2Scalar[i][j]) rect2Min[i] = rect2Scalar[i][j];
376 for (
int i = 0; i < 4; i++) {
377 if (!(rect2Min[i] <= rect1Max[i] && rect2Max[i] >= rect1Min[i]))
return false;
386 for (
auto it = m_parentList.begin(); it != m_parentList.end(); it++) {
387 if (!(*it))
return false;
395 wxFileName exeFileName(wxStandardPaths::Get().GetExecutablePath());
398 wxMenuItem* clockItem =
new wxMenuItem(&menu,
ID_ROTATE_CLOCK, _(
"Rotate clockwise"));
399 clockItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/rotateClock16.png"));
400 menu.Append(clockItem);
402 wxMenuItem* counterClockItem =
new wxMenuItem(&menu,
ID_ROTATE_COUNTERCLOCK, _(
"Rotate counter-clockwise"));
403 counterClockItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/rotateCounterClock16.png"));
404 menu.Append(counterClockItem);
406 wxMenuItem* deleteItem =
new wxMenuItem(&menu,
ID_DELETE, _(
"Delete"));
407 deleteItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/delete16.png"));
408 menu.Append(deleteItem);
416 wxPoint2DDouble rectCorner[4] = { m_rect.GetLeftTop(), m_rect.GetLeftBottom(), m_rect.GetRightBottom(),
417 m_rect.GetRightTop() };
419 for (
int i = 0; i < 4; ++i) { rectCorner[i] =
RotateAtPosition(rectCorner[i], m_angle); }
420 leftUp = rectCorner[0];
421 rightBottom = rectCorner[0];
422 for (
int i = 1; i < 4; ++i) {
423 if (rectCorner[i].m_x < leftUp.m_x) leftUp.m_x = rectCorner[i].m_x;
424 if (rectCorner[i].m_y < leftUp.m_y) leftUp.m_y = rectCorner[i].m_y;
425 if (rectCorner[i].m_x > rightBottom.m_x) rightBottom.m_x = rectCorner[i].m_x;
426 if (rectCorner[i].m_y > rightBottom.m_y) rightBottom.m_y = rectCorner[i].m_y;
430 for (
int i = 0; i < (int)m_pointList.size(); i++) {
431 if (m_pointList[i].m_x < leftUp.m_x) leftUp.m_x = m_pointList[i].m_x;
432 if (m_pointList[i].m_y < leftUp.m_y) leftUp.m_y = m_pointList[i].m_y;
433 if (m_pointList[i].m_x > rightBottom.m_x) rightBottom.m_x = m_pointList[i].m_x;
434 if (m_pointList[i].m_y > rightBottom.m_y) rightBottom.m_y = m_pointList[i].m_y;
442 if (!strValue.ToDouble(&dValue)) {
443 wxMessageDialog msgDialog(parent, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
444 msgDialog.ShowModal();
456 if (!strValue.ToLong(&iValue)) {
457 wxMessageDialog msgDialog(parent, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
458 msgDialog.ShowModal();
468 wxString str = wxString::FromCDouble(value, maxDecimals);
471 bool foundCut =
false;
472 for (
int i = (
int)str.length() - 1; i >= 0; i--) {
473 if (str[i] !=
'0' && !foundCut) {
483 wxString formatedStr =
"";
484 if (cutNumber - numDecimal > minDecimal)
485 formatedStr = wxString::FromDouble(value, cutNumber - numDecimal);
487 formatedStr = wxString::FromDouble(value, minDecimal);
494 for (
int i = 0; i < (int)m_parentList.size(); i++) {
495 if (m_parentList[i] == oldParent) m_parentList[i] = newParent;
502 for (
auto it = m_childList.begin(); it != m_childList.end();) {
503 if (*it == child) it = m_childList.erase(it);
510 for (
int i = 0; i < (int)m_childList.size(); i++) {
511 if (m_childList[i] == oldChild) m_childList[i] = newChild;
537 double distance = 100.0;
538 wxPoint2DDouble p0 = point;
540 for (
int i = 1; i < (int)m_pointList.size() - 2; i++) {
543 wxPoint2DDouble p1 = m_pointList[i];
544 wxPoint2DDouble p2 = m_pointList[i + 1];
546 wxPoint2DDouble v = p2 - p1;
547 wxPoint2DDouble w = p0 - p1;
549 double c1 = w.m_x * v.m_x + w.m_y * v.m_y;
550 double c2 = v.m_x * v.m_x + v.m_y * v.m_y;
553 d = std::sqrt(std::pow(p0.m_y - p1.m_y, 2) + std::pow(p0.m_x - p1.m_x, 2));
556 d = std::sqrt(std::pow(p0.m_y - p2.m_y, 2) + std::pow(p0.m_x - p2.m_x, 2));
559 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) /
560 std::sqrt(std::pow(p2.m_y - p1.m_y, 2) + std::pow(p2.m_x - p1.m_x, 2));
564 if (segmentNumber) *segmentNumber = i;
571void Element::SaveCADProperties(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementNode)
573 auto cadProp = XMLParser::AppendNode(doc, elementNode,
"CADProperties");
574 auto position = XMLParser::AppendNode(doc, cadProp,
"Position");
575 auto posX = XMLParser::AppendNode(doc, position,
"X");
576 XMLParser::SetNodeValue(doc, posX, m_position.m_x);
577 auto posY = XMLParser::AppendNode(doc, position,
"Y");
578 XMLParser::SetNodeValue(doc, posY, m_position.m_y);
579 auto size = XMLParser::AppendNode(doc, cadProp,
"Size");
580 auto width = XMLParser::AppendNode(doc, size,
"Width");
581 XMLParser::SetNodeValue(doc, width, m_width);
582 auto height = XMLParser::AppendNode(doc, size,
"Height");
583 XMLParser::SetNodeValue(doc, height, m_height);
584 auto angle = XMLParser::AppendNode(doc, cadProp,
"Angle");
585 XMLParser::SetNodeValue(doc, angle, m_angle);
588bool Element::OpenCADProperties(rapidxml::xml_node<>* elementNode)
590 auto cadPropNode = elementNode->first_node(
"CADProperties");
591 if (!cadPropNode)
return false;
593 auto position = cadPropNode->first_node(
"Position");
594 double posX = XMLParser::GetNodeValueDouble(position,
"X");
595 double posY = XMLParser::GetNodeValueDouble(position,
"Y");
596 auto size = cadPropNode->first_node(
"Size");
597 m_width = XMLParser::GetNodeValueDouble(size,
"Width");
598 m_height = XMLParser::GetNodeValueDouble(size,
"Height");
599 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.