| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <random> |
| |
|
| | #include "rs_information.h" |
| |
|
| | #include "lc_containertraverser.h" |
| | #include "lc_parabola.h" |
| | #include "lc_quadratic.h" |
| | #include "lc_rect.h" |
| | #include "lc_splinepoints.h" |
| | #include "rs_arc.h" |
| | #include "rs_circle.h" |
| | #include "rs_debug.h" |
| | #include "rs_ellipse.h" |
| | #include "rs_entity.h" |
| | #include "rs_entitycontainer.h" |
| | #include "rs_line.h" |
| | #include "rs_math.h" |
| | #include "rs_polyline.h" |
| |
|
| | class LC_Parabola; |
| |
|
| | namespace { |
| |
|
| | |
| | |
| | |
| | constexpr double g_tangentTolerance = 1e-7; |
| |
|
| | |
| | bool isArc(RS_Entity const* e) { |
| | if (e == nullptr) { |
| | return false; |
| | } |
| | switch (e->rtti()) { |
| | case RS2::EntityArc: |
| | case RS2::EntityCircle: |
| | return true; |
| | default: |
| | return false; |
| | } |
| | } |
| |
|
| | |
| | bool isLine(RS_Entity const* e) { |
| | return e != nullptr && e->rtti() == RS2::EntityLine; |
| | } |
| |
|
| | |
| | bool isEllipse(RS_Entity const* e) { |
| | return e != nullptr && e->rtti() == RS2::EntityEllipse; |
| | } |
| |
|
| | |
| | bool isParabola(RS_Entity const* e) { |
| | return e != nullptr && e->rtti() == RS2::EntityParabola; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | class TangentFinder { |
| | const RS_Entity* m_e1=nullptr; |
| | const RS_Entity* m_e2=nullptr; |
| | public: |
| | TangentFinder(const RS_Entity* e1, const RS_Entity* e2): |
| | m_e1{e1} |
| | , m_e2{e2} |
| | {} |
| |
|
| | |
| | RS_VectorSolutions GetTangent() const; |
| | }; |
| |
|
| | |
| | class TangentFinderAlgo { |
| | public: |
| |
|
| | virtual ~TangentFinderAlgo() = default; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | virtual std::pair<RS_VectorSolutions, bool> operator () (const RS_Entity* e1, const RS_Entity* e2) const = 0; |
| | protected: |
| |
|
| | |
| | RS_VectorSolutions FindTangent(const RS_Entity* e1, const RS_Entity* e2) const { |
| | const double tol = g_tangentTolerance * std::min(e1->getLength(), e2->getLength()); |
| |
|
| | RS_VectorSolutions ret = FindOffsetIntersections(e1, e2, tol); |
| |
|
| | if (ret.empty()) { |
| | ret = FindOffsetIntersections(e1, e2, -tol); |
| | } |
| | return ret; |
| | } |
| |
|
| | private: |
| | |
| | |
| | |
| | RS_VectorSolutions FindOffsetIntersections(const RS_Entity* e1, const RS_Entity* e2, double aOffsetValue) const { |
| | double offsetValue = aOffsetValue; |
| | std::unique_ptr<RS_Entity> offset = CreateOffset(e1, offsetValue); |
| | RS_VectorSolutions ret = LC_Quadratic::getIntersection(offset->getQuadratic(), e2->getQuadratic()); |
| | if (ret.size() <= 1) { |
| | return ret; |
| | } |
| |
|
| | |
| | double low = 0., high = offsetValue; |
| | while( ret.at(0).distanceTo(ret.at(1)) > 1e-7 && high - low > RS_TOLERANCE) { |
| | offsetValue = (low + high) * 0.5; |
| | offset = CreateOffset(e1, offsetValue); |
| | RS_VectorSolutions retMid = LC_Quadratic::getIntersection(offset->getQuadratic(), e2->getQuadratic()); |
| | switch(retMid.size()) { |
| | case 0: |
| | low = offsetValue; |
| | break; |
| | case 1: |
| | retMid.setTangent(true); |
| | return retMid; |
| | default: |
| | ret = std::move(retMid); |
| | high = offsetValue; |
| | } |
| | } |
| | RS_Vector tangent = (ret.at(0) + ret.at(1)) * 0.5; |
| | tangent = e2->getNearestPointOnEntity(tangent, false); |
| | ret = RS_VectorSolutions{tangent}; |
| | ret.setTangent(true); |
| | return ret; |
| | } |
| |
|
| | |
| | |
| | virtual std::unique_ptr<RS_Entity> CreateOffset([[maybe_unused]] const RS_Entity* e1, [[maybe_unused]] double offsetValue) const { |
| | return {}; |
| | } |
| | }; |
| |
|
| | |
| | class TangentFinderAlgoLine : public TangentFinderAlgo{ |
| | public: |
| | std::pair<RS_VectorSolutions, bool> operator () (const RS_Entity* e1, const RS_Entity* e2) const override { |
| | if (isLine(e2)) { |
| | std::swap(e1, e2); |
| | } |
| | if (!isLine(e1)) { |
| | return {}; |
| | } |
| | |
| | if (isLine(e2)) { |
| | return {}; |
| | } |
| |
|
| | RS_VectorSolutions ret = FindTangent(e1, e2); |
| | return {ret, true}; |
| | } |
| |
|
| | std::unique_ptr<RS_Entity> CreateOffset(const RS_Entity* e1, double offsetValue) const override { |
| | std::unique_ptr<RS_Entity> line{e1->clone()}; |
| | auto normal = RS_Vector{e1->getDirection1() + M_PI/2.}; |
| | line->move(normal * offsetValue); |
| | return line; |
| | } |
| | }; |
| |
|
| | |
| | class TangentFinderAlgoArc : public TangentFinderAlgo{ |
| | public: |
| | std::pair<RS_VectorSolutions, bool> operator () (const RS_Entity* e1, const RS_Entity* e2) const override { |
| | if (isArc(e2)) { |
| | std::swap(e1, e2); |
| | } |
| | if (!isArc(e1)) { |
| | return {}; |
| | } |
| | RS_VectorSolutions ret = FindTangent(e1, e2); |
| | return {ret, true}; |
| | } |
| |
|
| | std::unique_ptr<RS_Entity> CreateOffset(const RS_Entity* e1, double offsetValue) const override { |
| | std::unique_ptr<RS_Entity> circle{ e1->clone()}; |
| | circle->setRadius(e1->getRadius()*(1. + offsetValue)); |
| | return circle; |
| | } |
| | }; |
| |
|
| | |
| | class TangentFinderAlgoEllipse : public TangentFinderAlgo{ |
| | public: |
| | std::pair<RS_VectorSolutions, bool> operator () (const RS_Entity* e1, const RS_Entity* e2) const override { |
| | if (isEllipse(e2)) { |
| | std::swap(e1, e2); |
| | } |
| | if (!isEllipse(e1)) { |
| | return {}; |
| | } |
| | auto ellipse = static_cast<const RS_Ellipse*>(e1); |
| | RS_Circle c1{nullptr, {ellipse->getCenter(), ellipse->getMinorRadius() * (1.0 + g_tangentTolerance)}}; |
| | std::unique_ptr<RS_Entity> other{e2->clone()}; |
| | other->rotate(ellipse->getCenter(), -ellipse->getAngle()); |
| | other->scale(ellipse->getCenter(), {ellipse->getRatio(), 1.}); |
| |
|
| | RS_VectorSolutions ret; |
| | bool processed=false; |
| | std::tie(ret, processed) = TangentFinderAlgoArc{}(&c1, other.get()); |
| | ret.scale(ellipse->getCenter(), {1./ellipse->getRatio(), 1.}); |
| | ret.rotate(ellipse->getCenter(), ellipse->getAngle()); |
| | return {ret, true}; |
| | } |
| | }; |
| |
|
| | |
| | class TangentFinderAlgoParabola : public TangentFinderAlgo{ |
| | public: |
| | std::pair<RS_VectorSolutions, bool> operator () (const RS_Entity* e1, const RS_Entity* e2) const override { |
| | if (isParabola(e2)) { |
| | std::swap(e1, e2); |
| | } |
| | if (!isParabola(e1)) { |
| | return {}; |
| | } |
| |
|
| | RS_VectorSolutions ret = FindTangent(e1, e2); |
| | return {ret, true}; |
| | } |
| |
|
| | std::unique_ptr<RS_Entity> CreateOffset(const RS_Entity* e1, double offsetValue) const override { |
| | auto parabola = static_cast<const LC_Parabola*>(e1); |
| | return parabola->approximateOffset(offsetValue); |
| | } |
| | }; |
| |
|
| | RS_VectorSolutions TangentFinder::GetTangent() const{ |
| | |
| | std::unique_ptr<TangentFinderAlgo> algos[] = { |
| | std::make_unique<TangentFinderAlgoLine>(), |
| | std::make_unique<TangentFinderAlgoArc>(), |
| | std::make_unique<TangentFinderAlgoEllipse>(), |
| | std::make_unique<TangentFinderAlgoParabola>() |
| | }; |
| |
|
| | for(const std::unique_ptr<TangentFinderAlgo>& algo: algos) { |
| | const auto& [points, processed] = (*algo)(m_e1, m_e2); |
| | if (processed) { |
| | RS_VectorSolutions ret = points; |
| | |
| | if (points.size() == 2) { |
| | RS_Vector center = (points.at(0) + points.at(1)) * 0.5; |
| | RS_Vector projection = m_e1->getNearestPointOnEntity(center, false); |
| | projection = m_e2->getNearestPointOnEntity(projection, false); |
| | ret = {projection}; |
| | } |
| | if (!ret.empty()) |
| | ret.setTangent(true); |
| |
|
| | return ret; |
| | } |
| | } |
| | return {}; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Information::RS_Information(RS_EntityContainer& container): |
| | container(&container){ |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | bool RS_Information::isDimension(RS2::EntityType type) { |
| | return RS2::isDimensionalEntity(type); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | bool RS_Information::isTrimmable(RS_Entity *e){ |
| | if (e){ |
| | if (e->getParent()){ |
| | switch (e->getParent()->rtti()) { |
| | case RS2::EntityPolyline: |
| | case RS2::EntityContainer: |
| | case RS2::EntityGraphic: |
| | case RS2::EntityBlock: |
| | return true; |
| | default: |
| | return false; |
| | } |
| | } |
| | } |
| |
|
| | return false; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | bool RS_Information::isTrimmable(RS_Entity *e1, RS_Entity *e2){ |
| | if (e1 && e2){ |
| | if (e1->getParent() && e2->getParent()){ |
| | if (e1->getParent()->rtti() == RS2::EntityPolyline && |
| | e2->getParent()->rtti() == RS2::EntityPolyline && |
| | e1->getParent() == e2->getParent()){ |
| |
|
| | |
| | RS_Polyline* pl = static_cast<RS_Polyline *>(e1->getParent()); |
| | int idx1 = pl->findEntity(e1); |
| | int idx2 = pl->findEntity(e2); |
| | int length = pl->count(); |
| | LC_LOG<<"RS_Information::isTrimmable: " |
| | "idx1: "<<idx1<<" idx2:"<<idx2<<", length: "<<length; |
| | bool adjacentSegments = std::abs(idx1 - idx2) == 1; |
| | if (adjacentSegments || |
| | (pl->isClosed() && std::abs(idx1 - idx2) == length - 1)){ |
| | |
| | return true; |
| | } else { |
| | |
| | return false; |
| | } |
| | } else if ((e1->getParent()->rtti() == RS2::EntityContainer || |
| | e1->getParent()->rtti() == RS2::EntityGraphic || |
| | e1->getParent()->rtti() == RS2::EntityBlock) && |
| | (e2->getParent()->rtti() == RS2::EntityContainer || |
| | e2->getParent()->rtti() == RS2::EntityGraphic || |
| | e2->getParent()->rtti() == RS2::EntityBlock)){ |
| |
|
| | |
| | return true; |
| | } |
| | } else { |
| | |
| | return (e1->getParent() == e2->getParent()); |
| | } |
| | } |
| |
|
| | return false; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Vector RS_Information::getNearestEndpoint(const RS_Vector& coord,double* dist) const { |
| | return container->getNearestEndpoint(coord, dist); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Vector RS_Information::getNearestPointOnEntity(const RS_Vector& coord, |
| | bool onEntity, |
| | double* dist, |
| | RS_Entity** entity) const { |
| |
|
| | return container->getNearestPointOnEntity(coord, onEntity, dist, entity); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_Entity* RS_Information::getNearestEntity(const RS_Vector& coord, |
| | double* dist, |
| | RS2::ResolveLevel level) const { |
| |
|
| | return container->getNearestEntity(coord, dist, level); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | RS_VectorSolutions RS_Information::getIntersection(RS_Entity const* e1, |
| | RS_Entity const* e2, bool onEntities) { |
| | RS_VectorSolutions ret; |
| | const double tol = 1.0e-4; |
| |
|
| | if (!(e1 && e2) ) { |
| | RS_DEBUG->print("RS_Information::getIntersection() for nullptr entities"); |
| | return ret; |
| | } |
| | if (e1->getId() == e2->getId()) { |
| | RS_DEBUG->print("RS_Information::getIntersection() of the same entity"); |
| | return ret; |
| | } |
| |
|
| | |
| | RS2::EntityType e1Type = e1->rtti(); |
| | RS2::EntityType e2Type = e2->rtti(); |
| | if ( |
| | e1Type == RS2::EntityMText || e2Type == RS2::EntityMText || |
| | e1Type == RS2::EntityText || e2Type == RS2::EntityText || |
| | isDimension(e1Type) || isDimension(e2Type)) { |
| | return ret; |
| | } |
| |
|
| | if (onEntities && !(e1->isConstruction() || e2->isConstruction() || e1Type == RS2::EntityConstructionLine || e2Type == RS2::EntityConstructionLine)) { |
| | |
| | LC_Rect const rect1{e1->getMin(), e1->getMax()}; |
| | LC_Rect const rect2{e2->getMin(), e2->getMax()}; |
| |
|
| | if (onEntities && !rect1.intersects(rect2, RS_TOLERANCE)) { |
| | return ret; |
| | } |
| | } |
| |
|
| | auto parentOne = e1->getParent(); |
| | |
| | |
| | if ( parentOne != nullptr && parentOne == e2->getParent()) { |
| | if ( parentOne->rtti()==RS2::EntitySpline ) { |
| | |
| | if ( parentOne->areNeighborsEntities(e1,e2)) { |
| | return ret; |
| | } |
| | } |
| | } |
| |
|
| | if(e1Type == RS2::EntitySplinePoints || e2Type == RS2::EntitySplinePoints){ |
| | ret = LC_SplinePoints::getIntersection(e1, e2); |
| | } |
| | else { |
| | |
| | |
| | |
| | |
| | bool firstIsLine = e1Type == RS2::EntityLine || e1Type == RS2::EntityConstructionLine; |
| | bool secondIsLine = e2Type == RS2::EntityLine || e2Type == RS2::EntityConstructionLine; |
| | bool isLineLine = firstIsLine && secondIsLine; |
| | if (isLineLine) { |
| | ret = getIntersectionLineLine(e1, e2); |
| | } else if (isArc(e1)) { |
| | std::swap(e1, e2); |
| | if (isArc(e1)) { |
| | |
| | ret = getIntersectionArcArc(e1, e2); |
| | } else if (e1Type == RS2::EntityLine || e1Type == RS2::EntityConstructionLine) { |
| | ret = getIntersectionLineArc(static_cast<const RS_Line *>(e1), static_cast<const RS_Arc *>(e2)); |
| | } |
| | } |
| |
|
| | if (ret.empty()) { |
| | ret = LC_Quadratic::getIntersection(e1->getQuadratic(), e2->getQuadratic()); |
| | if (!isLineLine && ret.empty()) { |
| | |
| | |
| | |
| | |
| | |
| | ret = TangentFinder{e1, e2}.GetTangent(); |
| | } |
| | } |
| |
|
| | } |
| | RS_VectorSolutions ret2; |
| | for(const RS_Vector& vp: ret){ |
| | if (!vp.valid) { |
| | continue; |
| | } |
| | if (onEntities) { |
| | |
| | if (!( |
| | (e1->isConstruction(true) || e1->isPointOnEntity(vp, tol)) && |
| | (e2->isConstruction(true) || e2->isPointOnEntity(vp, tol)) |
| | ) |
| | ) { |
| | |
| | |
| | |
| | continue; |
| | } |
| | } |
| | |
| | RS_Vector direction1=e1->getTangentDirection(vp); |
| | RS_Vector direction2=e2->getTangentDirection(vp); |
| | if( direction1.valid && direction2.valid && fabs(fabs(direction1.dotP(direction2)) - sqrt(direction1.squared()*direction2.squared())) < sqrt(tol)*tol ) |
| | ret2.setTangent(true); |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | ret2.push_back(vp); |
| | } |
| |
|
| | return ret2; |
| | } |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| | RS_VectorSolutions RS_Information::getIntersectionLineLine(const RS_Entity* e1, const RS_Entity* e2){ |
| | if (e1 == nullptr || e2 == nullptr) { |
| | return {}; |
| | } |
| |
|
| | if (e1->rtti() != RS2::EntityLine || e2->rtti() != RS2::EntityLine) { |
| | return {}; |
| | } |
| |
|
| | if (!(e1 && e2)) { |
| | RS_DEBUG->print("RS_Information::getIntersectionLineLin() for nullptr entities"); |
| | return {}; |
| | } |
| |
|
| | RS_Vector p1 = e1->getStartpoint(); |
| | RS_Vector p2 = e1->getEndpoint(); |
| | RS_Vector p3 = e2->getStartpoint(); |
| | RS_Vector p4 = e2->getEndpoint(); |
| |
|
| | double num = ((p4.x-p3.x)*(p1.y-p3.y) - (p4.y-p3.y)*(p1.x-p3.x)); |
| | double div = ((p4.y-p3.y)*(p2.x-p1.x) - (p4.x-p3.x)*(p2.y-p1.y)); |
| |
|
| | |
| | const double dAngle = static_cast<const RS_Line*>(e1)->getAngle1() - static_cast<const RS_Line*>(e2)->getAngle1(); |
| | if (std::abs(div)>RS_TOLERANCE && |
| | std::abs(std::remainder(dAngle, M_PI))>=RS_TOLERANCE_ANGLE) { |
| | double u = num / div; |
| |
|
| | double xs = p1.x + u * (p2.x-p1.x); |
| | double ys = p1.y + u * (p2.y-p1.y); |
| | return {RS_Vector{xs, ys}}; |
| | } |
| |
|
| | |
| | if (e1->getLength() < e2->getLength()) { |
| | std::swap(e1, e2); |
| | } |
| | if (e1->getLength() <= RS_TOLERANCE) { |
| | |
| | if (p1.squaredTo(p3) <= RS_TOLERANCE2) { |
| | return {p1}; |
| | } |
| | return {}; |
| | } |
| | if (e2->getLength() <= RS_TOLERANCE) { |
| | |
| | RS_Vector projection = e1->getNearestPointOnEntity(e2->getStartpoint(), true); |
| | if (projection.squaredTo(e2->getStartpoint()) <= RS_TOLERANCE2) |
| | return {projection}; |
| | } |
| | |
| | return {}; |
| | } |
| |
|
| | |
| | |
| | |
| | RS_VectorSolutions RS_Information::getIntersectionLineArc(const RS_Entity* line, const RS_Entity* arc){ |
| |
|
| | if (line == nullptr || arc == nullptr) { |
| | return {}; |
| | } |
| |
|
| | if(line->rtti() != RS2::EntityLine || !isArc(arc)) { |
| | return {}; |
| | } |
| |
|
| | RS_Vector p = line->getStartpoint(); |
| | RS_Vector d = line->getEndpoint() - line->getStartpoint(); |
| | const double d2=d.squared(); |
| | RS_Vector c = arc->getCenter(); |
| | const double r = arc->getRadius(); |
| | RS_Vector delta = p - c; |
| | if (d2<RS_TOLERANCE2) { |
| | |
| | if ( std::abs(delta.squared() - r*r) < 2.*RS_TOLERANCE*r ){ |
| | return RS_VectorSolutions({line->getMiddlePoint()}); |
| | } |
| | return {}; |
| | } |
| |
|
| | |
| | double dist = 0.; |
| | RS_Vector projection = line->getNearestPointOnEntity(c, false, &dist); |
| | RS_Vector dP = projection - c; |
| | dP -= d *(d.dotP(dP)/d2); |
| | projection = c + dP; |
| |
|
| | const double dr = dP.magnitude() - r; |
| | const double tol = 1e-5 * r; |
| | if (dr > tol ) { |
| | return {}; |
| | } |
| |
|
| | if (dr < - tol) { |
| | |
| | const double dt = std::sqrt(r*r - dP.squared()); |
| | const RS_Vector dT = d*(dt/d.magnitude()); |
| | return RS_VectorSolutions({ projection + dT, projection - dT}); |
| | } |
| |
|
| | |
| | RS_VectorSolutions ret{projection}; |
| | ret.setTangent(true); |
| | |
| | return ret; |
| | } |
| |
|
| | |
| | |
| | |
| | RS_VectorSolutions RS_Information::getIntersectionArcArc(RS_Entity const* e1, RS_Entity const* e2) { |
| | if (!(e1 && e2)) { |
| | return {}; |
| | } |
| |
|
| | if(!isArc(e1) || !isArc(e2)) { |
| | return {}; |
| | } |
| |
|
| | RS_Vector c1 = e1->getCenter(); |
| | RS_Vector c2 = e2->getCenter(); |
| |
|
| | double r1 = e1->getRadius(); |
| | double r2 = e2->getRadius(); |
| |
|
| | RS_Vector u = c2 - c1; |
| |
|
| | |
| | if (u.magnitude()<1.0e-7*(r1 + r2)) { |
| | return {}; |
| | } |
| |
|
| | |
| | auto v = RS_Vector{u.y, -u.x}; |
| |
|
| | const double r12 = r1*r1; |
| | const double r22 = r2*r2; |
| | |
| | double s = 0.5 * ((r12 - r22)/u.squared() + 1.0); |
| | |
| | double term = r12/u.squared() - s*s; |
| |
|
| | |
| | if (term<- RS_TOLERANCE) { |
| | return {}; |
| | } |
| |
|
| | |
| | double t1 = std::sqrt(std::max(0., term)); |
| |
|
| | RS_Vector sol1 = c1 + u*s + v*t1; |
| | RS_Vector sol2 = c1 + u*s - v*t1; |
| |
|
| | if (sol1.distanceTo(sol2)<1.0e-5*(r1+r2)) { |
| | RS_VectorSolutions ret{sol1}; |
| | ret.setTangent(true); |
| | return ret; |
| | } |
| |
|
| | return {sol1, sol2}; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | RS_VectorSolutions RS_Information::getIntersectionEllipseEllipse(RS_Ellipse const* e1, RS_Ellipse const* e2) { |
| | RS_VectorSolutions ret; |
| |
|
| | if (!(e1 && e2) ) { |
| | return ret; |
| | } |
| | if ( |
| | (e1->getCenter() - e2 ->getCenter()).squared() < RS_TOLERANCE2 && |
| | (e1->getMajorP() - e2 ->getMajorP()).squared() < RS_TOLERANCE2 && |
| | fabs(e1->getRatio() - e2 ->getRatio()) < RS_TOLERANCE |
| | ) { |
| | return ret; |
| | } |
| | RS_Ellipse ellipse01(nullptr,e1->getData()); |
| |
|
| | RS_Ellipse *e01= & ellipse01; |
| | if( e01->getMajorRadius() < e01->getMinorRadius() ) e01->switchMajorMinor(); |
| | RS_Ellipse ellipse02(nullptr,e2->getData()); |
| | RS_Ellipse *e02= &ellipse02; |
| | if( e02->getMajorRadius() < e02->getMinorRadius() ) e02->switchMajorMinor(); |
| | |
| | RS_Vector shiftc1=- e01->getCenter(); |
| | double shifta1=-e01->getAngle(); |
| | e02->move(shiftc1); |
| | e02->rotate(shifta1); |
| | |
| | double a1=e01->getMajorRadius(); |
| | double b1=e01->getMinorRadius(); |
| | double x2=e02->getCenter().x, |
| | y2=e02->getCenter().y; |
| | double a2=e02->getMajorRadius(); |
| | double b2=e02->getMinorRadius(); |
| |
|
| | if( e01->getMinorRadius() < RS_TOLERANCE || e01 -> getRatio()< RS_TOLERANCE) { |
| | |
| | RS_Line line{e1->getParent(), {{-a1,0.}, {a1,0.}}}; |
| | ret= getIntersectionEllipseLine(&line, e02); |
| | ret.rotate(-shifta1); |
| | ret.move(-shiftc1); |
| | return ret; |
| | } |
| | if( e02->getMinorRadius() < RS_TOLERANCE || e02 -> getRatio()< RS_TOLERANCE) { |
| | |
| | RS_Line line{e1->getParent(), {{-a2,0.}, {a2,0.}}}; |
| | line.rotate({0.,0.}, e02->getAngle()); |
| | line.move(e02->getCenter()); |
| | ret = getIntersectionEllipseLine(&line, e01); |
| | ret.rotate(-shifta1); |
| | ret.move(-shiftc1); |
| | return ret; |
| | } |
| |
|
| | |
| | |
| | double t2= - e02->getAngle(); |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | double cs=cos(t2),si=sin(t2); |
| | double ucs=x2*cs,usi=x2*si, |
| | vcs=y2*cs,vsi=y2*si; |
| | double cs2=cs*cs,si2=1-cs2; |
| | double tcssi=2.*cs*si; |
| | double ia2=1./(a2*a2),ib2=1./(b2*b2); |
| | std::vector<double> m(0,0.); |
| | m.push_back( 1./(a1*a1)); |
| | m.push_back( 1./(b1*b1)); |
| | m.push_back(cs2*ia2 + si2*ib2); |
| | m.push_back(cs*si*(ib2 - ia2)); |
| | m.push_back(si2*ia2 + cs2*ib2); |
| | m.push_back(( y2*tcssi - 2.*x2*cs2)*ia2 - ( y2*tcssi+2*x2*si2)*ib2); |
| | m.push_back( ( x2*tcssi - 2.*y2*si2)*ia2 - ( x2*tcssi+2*y2*cs2)*ib2); |
| | m.push_back((ucs - vsi)*(ucs-vsi)*ia2+(usi+vcs)*(usi+vcs)*ib2 -1.); |
| | auto vs0=RS_Math::simultaneousQuadraticSolver(m); |
| | shifta1 = - shifta1; |
| | shiftc1 = - shiftc1; |
| | for(RS_Vector vp: vs0){ |
| | vp.rotate(shifta1); |
| | vp.move(shiftc1); |
| | ret.push_back(vp); |
| | } |
| | return ret; |
| | } |
| |
|
| | |
| | RS_VectorSolutions RS_Information::getIntersectionCircleEllipse(RS_Circle* c1, RS_Ellipse* e1) { |
| | RS_VectorSolutions ret; |
| | if (!(c1 && e1)) { |
| | return ret; |
| | } |
| |
|
| | RS_Ellipse const e2{c1->getParent(), |
| | {c1->getCenter(), {c1->getRadius(),0.}, |
| | 1.0, |
| | 0., 2.*M_PI, |
| | false}}; |
| | return getIntersectionEllipseEllipse(e1, &e2); |
| | } |
| |
|
| | RS_VectorSolutions RS_Information::getIntersectionArcEllipse(RS_Arc * a1, |
| | RS_Ellipse* e1) { |
| | RS_VectorSolutions ret; |
| | if (!(a1 && e1)) { |
| | return ret; |
| | } |
| | RS_Ellipse const e2{a1->getParent(), |
| | {a1->getCenter(), |
| | {a1->getRadius(), 0.}, |
| | 1.0, |
| | a1->getAngle1(), a1->getAngle2(), |
| | a1->isReversed()}}; |
| | return getIntersectionEllipseEllipse(e1, &e2); |
| | } |
| |
|
| | |
| | |
| | |
| | RS_VectorSolutions RS_Information::getIntersectionEllipseLine(RS_Line* line, |
| | RS_Ellipse* ellipse) { |
| | RS_VectorSolutions ret; |
| |
|
| | if (!(line && ellipse)) { |
| | return ret; |
| | } |
| | |
| |
|
| | double rx = ellipse->getMajorRadius(); |
| | if(rx<RS_TOLERANCE) { |
| | |
| | RS_Vector vp(line->getNearestPointOnEntity(ellipse->getCenter(), true)); |
| | if((vp-ellipse->getCenter()).squared() <RS_TOLERANCE2){ |
| | |
| | ret.push_back(vp); |
| | } |
| | return ret; |
| | } |
| | RS_Vector angleVector = ellipse->getMajorP().scale(RS_Vector(1./rx,-1./rx)); |
| | double ry = rx*ellipse->getRatio(); |
| | RS_Vector center = ellipse->getCenter(); |
| | RS_Vector a1 = line->getStartpoint().rotate(center, angleVector); |
| | RS_Vector a2 = line->getEndpoint().rotate(center, angleVector); |
| | |
| | RS_Vector dir = a2-a1; |
| | RS_Vector diff = a1 - center; |
| | RS_Vector mDir = RS_Vector(dir.x/(rx*rx), dir.y/(ry*ry)); |
| | RS_Vector mDiff = RS_Vector(diff.x/(rx*rx), diff.y/(ry*ry)); |
| |
|
| | double a = RS_Vector::dotP(dir, mDir); |
| | double b = RS_Vector::dotP(dir, mDiff); |
| | double c = RS_Vector::dotP(diff, mDiff) - 1.0; |
| | double d = b*b - a*c; |
| |
|
| | |
| | if (d < - 1.e3*RS_TOLERANCE*sqrt(RS_TOLERANCE)) { |
| | RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: outside 0"); |
| | return ret; |
| | } |
| | if( d < 0. ) { |
| | d=0.; |
| | } |
| | double root = sqrt(d); |
| | double t_a = -b/a; |
| | double t_b = root/a; |
| | |
| |
|
| | ret.push_back(a1.lerp(a2, t_a + t_b)); |
| | RS_Vector vp(a1.lerp(a2, t_a - t_b)); |
| | if ((ret.get(0) - vp).squared() > RS_TOLERANCE2) { |
| | ret.push_back(vp); |
| | } |
| | angleVector.y *= -1.; |
| | ret.rotate(center, angleVector); |
| | |
| | |
| | RS_DEBUG->print("RS_Information::getIntersectionEllipseLine(): done"); |
| | return ret; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | bool RS_Information::isPointInsideContour(const RS_Vector& point, |
| | RS_EntityContainer* contour, bool* onContour) { |
| |
|
| | if (contour == nullptr || !LC_Rect{contour->getMin(), contour->getMax()}.inArea(point)) |
| | return false; |
| |
|
| | const double onTol = 1.0e-4; |
| | for (RS_Entity* e : lc::LC_ContainerTraverser{*contour, RS2::ResolveAll}.entities()) { |
| | if (e->isPointOnEntity(point, onTol)) { |
| | if (onContour != nullptr) |
| | *onContour = true; |
| | return true; |
| | } |
| | } |
| |
|
| | double width = contour->getSize().magnitude() + 1.0; |
| | std::random_device rd; |
| | std::mt19937 gen(rd()); |
| | std::uniform_real_distribution<double> dist(0.0, 2.0 * M_PI); |
| |
|
| | auto intersections = [&]() { |
| | RS_Vector vector = RS_Vector::polar(width, dist(gen)); |
| | RS_Line ray{point, point + vector}; |
| | size_t counter=0; |
| | for (RS_Entity* en: lc::LC_ContainerTraverser{*contour, RS2::ResolveAll}.entities()) { |
| | RS_VectorSolutions sol = getIntersection(en, &ray, true); |
| | if (sol.isTangent()) |
| | counter += sol.size() + 1; |
| | else |
| | counter += sol.size(); |
| | } |
| | return counter; |
| | }; |
| |
|
| | |
| | size_t counter = std::min(intersections(), intersections()); |
| |
|
| | return counter%2 == 1; |
| | } |
| |
|
| | RS_VectorSolutions RS_Information::createQuadrilateral(const RS_EntityContainer& container){ |
| | RS_VectorSolutions ret; |
| | if (container.count() != 4) { |
| | return ret; |
| | } |
| | RS_EntityContainer c(container); |
| | std::vector<RS_Line*> lines; |
| | for (auto e : c) { |
| | if (e->rtti() != RS2::EntityLine) { |
| | return ret; |
| | } |
| | lines.push_back(static_cast<RS_Line*>(e)); |
| | } |
| | if (lines.size() != 4) { |
| | return ret; |
| | } |
| |
|
| | |
| | std::vector<RS_Vector> vertices; |
| | for (auto it = lines.begin() + 1; it != lines.end(); ++it) { |
| | for (auto jt = lines.begin(); jt != it; ++jt) { |
| | RS_VectorSolutions const& sol = RS_Information::getIntersectionLineLine(*it, *jt); |
| | if (sol.size()) { |
| | vertices.push_back(sol.at(0)); |
| | } |
| | } |
| | } |
| |
|
| | |
| |
|
| | switch (vertices.size()) { |
| | default: |
| | return ret; |
| | case 4: |
| | break; |
| | case 5: |
| | case 6: |
| | for (RS_Line* pl : lines) { |
| | const double a0 = pl->getDirection1(); |
| | std::vector<std::vector<RS_Vector>::iterator> left; |
| | std::vector<std::vector<RS_Vector>::iterator> right; |
| | for (auto it = vertices.begin(); it != vertices.end(); ++it) { |
| | RS_Vector const& dir = *it - pl->getNearestPointOnEntity(*it, false); |
| | if (dir.squared() < RS_TOLERANCE15) { |
| | continue; |
| | } |
| | |
| | if (remainder(dir.angle() - a0, 2. * M_PI) > 0.) { |
| | left.push_back(it); |
| | } |
| | else { |
| | right.push_back(it); |
| | } |
| |
|
| | if (left.size() == 2 && right.size() == 1) { |
| | vertices.erase(right[0]); |
| | break; |
| | } |
| | else if (left.size() == 1 && right.size() == 2) { |
| | vertices.erase(left[0]); |
| | break; |
| | } |
| | } |
| | if (vertices.size() == 4) { |
| | break; |
| | } |
| | } |
| | break; |
| | } |
| |
|
| | |
| | RS_Vector center{0., 0.}; |
| | for(const RS_Vector& vp: vertices) { |
| | center += vp; |
| | } |
| | center *= 0.25; |
| | std::sort(vertices.begin(), vertices.end(), [¢er](const RS_Vector& a, |
| | const RS_Vector&b)->bool{ |
| | return center.angleTo(a)<center.angleTo(b); |
| | } |
| | ); |
| | for(const RS_Vector& vp: vertices){ |
| | ret.push_back(vp); |
| | |
| | } |
| | return ret; |
| | } |
| |
|