| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #include <algorithm> |
| | #include <cmath> |
| |
|
| | #include "lc_linemath.h" |
| | #include "rs.h" |
| | #include "rs_information.h" |
| | #include "rs_line.h" |
| | #include "rs_math.h" |
| | #include "rs_vector.h" |
| |
|
| | namespace { |
| |
|
| | |
| | |
| | bool compareCoordinates(const RS_Vector& p0, const RS_Vector& p1) |
| | { |
| | return p0.distanceTo(p1) >= RS_TOLERANCE && ( |
| | p0.x < p1.x || (p0.x <= p1.x && p0.y < p1.y)); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | bool isCounterClockwise (const RS_Vector& v1, const RS_Vector& v2, const RS_Vector& v3) |
| | { |
| | double res = RS_Vector::crossP((v2 - v1).normalized(), (v3 - v1).normalized()).z; |
| |
|
| | return res >= RS_TOLERANCE; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Vector LC_LineMath::getEndOfLineSegment(const RS_Vector &startPoint, double angleValueDegree, double distance){ |
| | double angle = RS_Math::deg2rad(angleValueDegree); |
| | RS_Vector line = RS_Vector::polar(distance, angle); |
| | RS_Vector result = startPoint + line; |
| | return result; |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Vector LC_LineMath::relativePoint(const RS_Vector &startPoint, double distance, double angleValueRad){ |
| | RS_Vector offsetVector = RS_Vector::polar(distance, angleValueRad); |
| | RS_Vector result = startPoint + offsetVector; |
| | return result; |
| | } |
| |
|
| | RS_Vector LC_LineMath::calculateAngleSegment(const RS_Vector& startPoint, const RS_Vector& previousLineStart, |
| | const RS_Vector& previousLineEnd, double angleValueDegree, |
| | bool angleRelative, double distance) { |
| | double angle = RS_Math::deg2rad(angleValueDegree); |
| | double realAngle = defineActualSegmentAngle(angle, angleRelative, previousLineStart, previousLineEnd); |
| | RS_Vector line = RS_Vector::polar(distance, realAngle); |
| | RS_Vector result = startPoint + line; |
| | return result; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | double LC_LineMath::defineActualSegmentAngle(double angle, bool angleIsRelative, const RS_Vector& previousLineStart, |
| | const RS_Vector& previousLineEnd) { |
| | double result = angle; |
| | if (angleIsRelative) { |
| | RS_Vector line = previousLineEnd - previousLineStart; |
| | double previousSegmentAngle = line.angle(); |
| | result = angle + previousSegmentAngle; |
| | } |
| | return result; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Vector LC_LineMath::getNearestPointOnInfiniteLine(const RS_Vector& coord, const RS_Vector& lineStartPoint, |
| | const RS_Vector& lineEndPoint) { |
| | RS_Vector ae = lineEndPoint - lineStartPoint; |
| | RS_Vector ea = lineStartPoint - lineEndPoint; |
| | RS_Vector ap = coord - lineStartPoint; |
| |
|
| | double magnitude = ae.magnitude(); |
| | if (magnitude < RS_TOLERANCE || ea.magnitude() < RS_TOLERANCE) { |
| | return RS_Vector(false); |
| | } |
| |
|
| | |
| | RS_Vector ba = ae * RS_Vector::dotP(ae, ap) / (magnitude * magnitude); |
| |
|
| | return lineStartPoint + ba; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Vector LC_LineMath::calculateEndpointForAngleDirection(double wcsAngleRad, const RS_Vector& startPoint, |
| | const RS_Vector& toSnapPoint) { |
| | RS_Vector possibleEndPoint; |
| |
|
| | RS_Vector infiniteTickStartPoint = startPoint; |
| | RS_Vector infiniteTickEndPoint = infiniteTickStartPoint.relative(10.0, wcsAngleRad); |
| | RS_Vector pointOnInfiniteTick = getNearestPointOnInfiniteLine(toSnapPoint, infiniteTickStartPoint, |
| | infiniteTickEndPoint); |
| |
|
| | possibleEndPoint = pointOnInfiniteTick; |
| | return possibleEndPoint; |
| | } |
| |
|
| | RS_Vector LC_LineMath::calculateEndpointForAngleDirection(double angleValueDegree, bool angleIsRelative, |
| | const RS_Vector& fromPoint, const RS_Vector& toSnapPoint, |
| | const RS_Vector& previousLineStart, |
| | const RS_Vector& previousLineEnd) { |
| | double angle = RS_Math::deg2rad(angleValueDegree); |
| | RS_Vector infiniteTickStartPoint = fromPoint; |
| | double realAngle = defineActualSegmentAngle(angle, angleIsRelative, previousLineStart, previousLineEnd); |
| |
|
| | RS_Vector infiniteTickVector = RS_Vector::polar(10.0, realAngle); |
| | RS_Vector infiniteTickEndPoint = infiniteTickStartPoint + infiniteTickVector; |
| | RS_Vector pointOnInfiniteTick = getNearestPointOnInfiniteLine(toSnapPoint, infiniteTickStartPoint, |
| | infiniteTickEndPoint); |
| |
|
| | return pointOnInfiniteTick; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Vector LC_LineMath::getNearestPointOnLine(const RS_Line* line, const RS_Vector& coord, bool infiniteLine){ |
| | |
| | bool onEntity = ! infiniteLine; |
| | return line != nullptr ? line->getNearestPointOnEntity(coord, onEntity, nullptr) : RS_Vector{false}; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Vector LC_LineMath::findPointOnCircle(double radius, double arcAngle, const RS_Vector& centerCircle){ |
| | RS_Vector radiusVector = RS_Vector::polar(radius, arcAngle); |
| | RS_Vector pointPos = centerCircle + radiusVector; |
| | return pointPos; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | int LC_LineMath::getPointPosition(const RS_Vector &startPos, const RS_Vector &endPos, const RS_Vector &point){ |
| | RS_Vector a = endPos - startPos; |
| | RS_Vector b = point - startPos; |
| | double sa = a. x * b.y - b.x * a.y; |
| | if (sa > 0.0) { |
| | return LEFT; |
| | } |
| | if (sa < 0.0) { |
| | return RIGHT; |
| | } |
| | if ((a.x * b.x < 0.0) || (a.y * b.y < 0.0)) { |
| | return BEHIND; |
| | } |
| | if (a.magnitude() < b.magnitude()) { |
| | return BEYOND; |
| | } |
| | if (startPos == point) { |
| | return ORIGIN; |
| | } |
| | if (endPos == point) { |
| | return DESTINATION; |
| | } |
| | return BETWEEN; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | bool LC_LineMath::areLinesOnSameRay(const RS_Vector& line1Start, const RS_Vector& line1End, const RS_Vector& line2Start, |
| | const RS_Vector& line2End) { |
| | |
| | |
| | |
| | |
| | |
| | double angle1 = line1Start.angleTo(line1End); |
| | double angle2 = line1Start.angleTo(line2End); |
| | double angle3 = line1Start.angleTo(line2Start); |
| |
|
| | |
| |
|
| | angle1 = RS_Math::correctAngle0ToPi(angle1); |
| | angle2 = RS_Math::correctAngle0ToPi(angle2); |
| | angle3 = RS_Math::correctAngle0ToPi(angle3); |
| |
|
| | bool sameLine = false; |
| | if (std::abs(angle1 - angle2) < RS_TOLERANCE_ANGLE && std::abs(angle1 - angle3) < RS_TOLERANCE_ANGLE) { |
| | sameLine = true; |
| | } |
| | return sameLine; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | bool LC_LineMath::isNonZeroLineLength(const RS_Vector &startPoint, const RS_Vector &endPoint){ |
| | return (endPoint - startPoint).squared() > RS_TOLERANCE; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | double LC_LineMath::getMeaningful(double candidate, double replacementValue){ |
| | return (std::abs(candidate) < RS_TOLERANCE) ? replacementValue : candidate; |
| | } |
| |
|
| |
|
| | double LC_LineMath::getMeaningfulPositive(double candidate, double replacementValue){ |
| | return (candidate < RS_TOLERANCE) ? replacementValue : candidate; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | double LC_LineMath::getMeaningfulAngle(double candidate, double replacementValue){ |
| | return (std::abs(candidate) < RS_TOLERANCE_ANGLE) ? replacementValue : candidate; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | bool LC_LineMath::isMeaningful(double value){ |
| | return std::abs(value) >= RS_TOLERANCE; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | bool LC_LineMath::isNotMeaningful(double value){ |
| | return std::abs(value) < RS_TOLERANCE; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | bool LC_LineMath::isMeaningfulAngle(double value){ |
| | return std::abs(value) >= RS_TOLERANCE_ANGLE; |
| | } |
| |
|
| | bool LC_LineMath::isSameAngle(double angle1, double angle2) { |
| | return std::abs(angle1 - angle2) < RS_TOLERANCE_ANGLE; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | bool LC_LineMath::isMeaningfulDistance(const RS_Vector &v1, const RS_Vector &v2){ |
| | double distance = v1.distanceTo(v2); |
| | return distance >= RS_TOLERANCE; |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | bool LC_LineMath::isNotMeaningfulDistance(const RS_Vector &v1, const RS_Vector &v2){ |
| | double distance = v1.distanceTo(v2); |
| | return distance < RS_TOLERANCE; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_LineData LC_LineMath::createParallel(const RS_Vector &start, const RS_Vector &end, double distance){ |
| | RS_Line line{nullptr, {start, end}}; |
| | double ang = line.getDirection1() + M_PI_2; |
| | |
| | line.move(RS_Vector::polar(distance, ang)); |
| | return line.getData(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Vector LC_LineMath::getIntersectionLineLine(const RS_Vector& s1, const RS_Vector& e1, const RS_Vector& s2, |
| | const RS_Vector& e2) { |
| | RS_Line line1{nullptr, {s1, e1}}; |
| | RS_Line line2{nullptr, {s2, e2}}; |
| | RS_VectorSolutions sol = RS_Information::getIntersectionLineLine(&line1, &line2); |
| | return sol.empty() ? RS_Vector{false} : sol.at(0); |
| | } |
| |
|
| | RS_Vector LC_LineMath::getIntersectionLineLineFast(const RS_Vector& s1, const RS_Vector& e1, const RS_Vector& s2, |
| | const RS_Vector& e2) { |
| | RS_Vector ret; |
| |
|
| | double num = ((e2.x - s2.x) * (s1.y - s2.y) - (e2.y - s2.y) * (s1.x - s2.x)); |
| | double div = ((e2.y - s2.y) * (e1.x - s1.x) - (e2.x - s2.x) * (e1.y - s1.y)); |
| |
|
| | double angle1 = s1.angleTo(e1); |
| | double angle2 = s2.angleTo(e2); |
| |
|
| | if (fabs(div)>RS_TOLERANCE && |
| | fabs(remainder(angle1-angle2, M_PI))>=RS_TOLERANCE*10.) { |
| | double u = num / div; |
| |
|
| | double xs = s1.x + u * (e1.x - s1.x); |
| | double ys = s1.y + u * (e1.y - s1.y); |
| | ret = RS_Vector(xs, ys); |
| | } |
| | else { |
| | |
| | ret = RS_Vector(false); |
| | } |
| | return ret; |
| | } |
| |
|
| | RS_Vector LC_LineMath::getIntersectionInfiniteLineLineFast(const RS_Vector& s1, const RS_Vector& e1, const RS_Vector& s2, const RS_Vector& e2, double offsetX, double offsetY) { |
| |
|
| | RS_Vector ret; |
| |
|
| | double num = ((e2.x - s2.x) * (s1.y - s2.y) - (e2.y - s2.y) * (s1.x - s2.x)); |
| | double div = ((e2.y - s2.y) * (e1.x - s1.x) - (e2.x - s2.x) * (e1.y - s1.y)); |
| |
|
| | double angle1 = s1.angleTo(e1); |
| | double angle2 = s2.angleTo(e2); |
| |
|
| | if (fabs(div)>RS_TOLERANCE && |
| | fabs(remainder(angle1-angle2, M_PI))>=RS_TOLERANCE*10.) { |
| | double u = num / div; |
| |
|
| | double xs = s1.x + u * (e1.x - s1.x); |
| | double ys = s1.y + u * (e1.y - s1.y); |
| | |
| | if ((xs >= (s2.x-offsetX)) && (xs <= (e2.x+offsetX)) && (ys >= (s2.y-offsetY)) && (ys <= (e2.y+offsetY))){ |
| | ret = RS_Vector(xs, ys); |
| | } |
| | else { |
| | ret = RS_Vector(false); |
| | } |
| | } |
| | else { |
| | |
| | ret = RS_Vector(false); |
| | } |
| | return ret; |
| | } |
| |
|
| | bool LC_LineMath::hasIntersectionLineRect(const RS_Vector& lineStart, const RS_Vector& lineEnd, const RS_Vector& rectMin, const RS_Vector& rectMax) { |
| | RS_Vector direction = lineEnd - lineStart; |
| | |
| | |
| |
|
| | if (hasLineIntersection(lineStart, direction, rectMin, rectMax)){ |
| | return true; |
| | } |
| | else{ |
| | return hasLineIntersection(lineStart, direction, {rectMin.x, rectMax.y}, {rectMax.x, rectMin.y}); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | bool LC_LineMath::hasLineIntersection(RS_Vector p0, RS_Vector direction, RS_Vector p2, RS_Vector p3) |
| | { |
| | RS_Vector P = p2; |
| | RS_Vector R = p3 - p2; |
| | RS_Vector Q = p0; |
| | RS_Vector S = direction; |
| |
|
| | RS_Vector N = RS_Vector(S.y, -S.x); |
| | |
| |
|
| | RS_Vector tmp = Q-P; |
| | double t = tmp.dotP(N) / R.dotP(N); |
| |
|
| | if (t >= 0.0 && t <= 1.0){ |
| | return true; |
| | |
| | } |
| | return false; |
| | |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | RS_VectorSolutions LC_LineMath::convexHull(const RS_VectorSolutions& points){ |
| | RS_VectorSolutions sol = points; |
| | |
| | auto it = std::remove_if(sol.begin(), sol.end(), [](const RS_Vector& p) { |
| | return ! p.valid;}); |
| | sol = {{sol.begin(), it}}; |
| |
|
| | if (sol.size() <= 1) { |
| | return sol; |
| | } |
| |
|
| | |
| | std::sort(sol.begin(), sol.end(), compareCoordinates); |
| |
|
| | |
| | RS_VectorSolutions hull{{sol.at(0)}}; |
| | for(size_t i = 1; i < sol.size(); ++i) { |
| | if (hull.back().distanceTo(sol.at(i)) > RS_TOLERANCE) { |
| | hull.push_back(sol.at(i)); |
| | } |
| | } |
| |
|
| | if (hull.size() <= 2) { |
| | return hull; |
| | } |
| |
|
| | |
| | std::sort(hull.begin() + 1, hull.end(), |
| | [lowerLeft=hull.at(0)](const RS_Vector& lhs, const RS_Vector& rhs) { |
| | return lowerLeft.angleTo(lhs) < lowerLeft.angleTo(rhs); |
| | }); |
| |
|
| | |
| | sol = {hull.at(0), hull.at(1)}; |
| | for (size_t i = 2; i < hull.size(); ++i) { |
| | const double angle0 = sol.at(0).angleTo(sol.back()); |
| | const double angle1 = sol.at(0).angleTo(hull.at(i)); |
| | if (RS_Math::equal(angle0, angle1, RS_TOLERANCE)) { |
| | if (sol.at(0).distanceTo(hull.at(i)) < sol.at(0).distanceTo(sol.back())) { |
| | sol.back() = hull.at(i); |
| | } |
| | } else { |
| | sol.push_back(hull.at(i)); |
| | } |
| | } |
| |
|
| | |
| | hull = {sol.at(0), sol.at(1)}; |
| | for (size_t i = 2; i < sol.size(); ++i) { |
| | const size_t j = hull.size() - 1; |
| | if (isCounterClockwise(hull.at(j-1), hull.at(j), sol.at(i))) { |
| | hull.push_back(sol.at(i)); |
| | } |
| | else { |
| | hull.at(j) = sol.at(i); |
| | } |
| | } |
| | return hull; |
| | } |
| |
|
| | double LC_LineMath::angleFor3Points(const RS_Vector& edgePoint1, const RS_Vector& intersection, const RS_Vector& edgePoint2) { |
| | double angle1 = intersection.angleTo(edgePoint1); |
| | double angle2 = intersection.angleTo(edgePoint2); |
| | double angle = RS_Math::getAngleDifference(angle1, angle2, false); |
| | angle = RS_Math::correctAngle0ToPi(angle); |
| | |
| | return angle; |
| | } |
| |
|