/* Copyright (c) 2008-2022 Jan W. Krieger () This software is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License (LGPL) for more details. You should have received a copy of the GNU Lesser General Public License (LGPL) along with this program. If not, see . */ #include "jkqtcommon/jkqtpgeometrytools.h" #include #include #include QPolygonF jkqtpRotateRect(QRectF r, double angle) { QPolygonF p; QTransform m; m.rotate(angle); p.append(m.map(r.bottomLeft())); p.append(m.map(r.bottomRight())); p.append(m.map(r.topRight())); p.append(m.map(r.topLeft())); return p; } QVector JKQTPSplitEllipseIntoPoints(double x, double y, double a, double b, double angle_start, double angle_end, double alpha, int controlPoints, QPointF* x_start, QPointF* x_end) { QVector result; const double start=angle_start*JKQTPSTATISTICS_PI/180.0; const double stop=angle_end*JKQTPSTATISTICS_PI/180.0; double step=(stop-start)/static_cast(controlPoints); while (fabs(stop-start)/step<10) step=step/2.0; const double sina=sin(1.0*alpha/180.0*JKQTPSTATISTICS_PI); const double cosa=cos(1.0*alpha/180.0*JKQTPSTATISTICS_PI); QPointF xp(x+a*cos(start)*cosa-b*sin(start)*sina, y+a*cos(start)*sina+b*sin(start)*cosa); result.append(xp); if (x_start) *x_start = xp; double t=start+step; for (int i=1; i JKQTPSplitEllipseIntoPoints(std::function fTransform, double x, double y, double a, double b, double angle_start, double angle_end, double alpha, QPointF *x_start, QPointF *x_end, QPointF *x_start_notrafo, QPointF *x_end_notrafo) { const double sina=sin(1.0*alpha/180.0*JKQTPSTATISTICS_PI); const double cosa=cos(1.0*alpha/180.0*JKQTPSTATISTICS_PI); std::function fell=[&](double t)->QPointF { return QPointF(x+a*cos(t)*cosa-b*sin(t)*sina, y+a*cos(t)*sina+b*sin(t)*cosa); }; std::function fxy = [&](double t) ->QPointF { return fTransform(fell(t)); }; JKQTPAdaptiveFunctionGraphEvaluator eval(fxy); const QVector points=eval.evaluate(angle_start*JKQTPSTATISTICS_PI/180.0, angle_end*JKQTPSTATISTICS_PI/180.0); if (points.size()>0) { if (x_start) *x_start=points.first(); if (x_end) *x_end=points.last(); if (x_start_notrafo) *x_start_notrafo=fell(angle_start*JKQTPSTATISTICS_PI/180.0); if (x_end_notrafo) *x_end_notrafo=fell(angle_end*JKQTPSTATISTICS_PI/180.0); } return points; } QVector JKQTPUnifyLinesToPolygons(const QVector &lines, double distanceThreshold, int searchMaxSurroundingElements) { #ifdef JKQTBP_AUTOTIMER JKQTPAutoOutputTimer jkaat(QString("JKQTPUnifyLinesToPolygons(%1, %2, %3)").arg(lines.size()).arg(distanceThreshold).arg(searchMaxSurroundingElements)); #endif QList res; res.reserve(lines.size()); // first simply convert all lines to polygons for (const QLineF& l: lines) { QPolygonF p; p< polygons start "<=0; k--) { res[i].prepend(res[j].at(k)); } res.removeAt(j); } else if (jkqtp_distance(res[i].last(),res[j].first())<=distanceThreshold) { found=true; for (int k=1; k=0; k--) { res[i].append(res[j].at(k)); } res.removeAt(j); } else { j++; } } res[i]=JKQTPCleanPolygon(res[i], distanceThreshold); i++; } //qDebug()<<" iter "< polygons left "< inbetween; int i=1; while (i0) { for (const QPointF& pi: inbetween) { pmean=QPointF(pmean.x()+pi.x()/static_cast(inbetween.size()), pmean.y()+pi.y()/static_cast(inbetween.size())); } } else { pmean=poly[i]; } p<0) { for (const QPointF& pi: inbetween) { pmean=QPointF(pmean.x()+pi.x()/static_cast(inbetween.size()), pmean.y()+pi.y()/static_cast(inbetween.size())); } } else { pmean=p0; } if (jkqtp_distance(pmean, poly.last())>distanceThreshold) { p< JKQTPSplitLineIntoPoints(const QLineF &line, int controlPoints) { QVector result; result.reserve(controlPoints); result.push_back(line.p1()); for (int i=1; i(i)/static_cast(controlPoints)*(line.p2()-line.p1())); } result.push_back(line.p2()); return result; } QVector JKQTPSimplyfyLineSegemnts(const QVector &points, double maxConsecutiveAngleDegree) { QVector result; if (points.size()>2) { result.push_back(points[0]); for (int i=1; imaxConsecutiveAngleDegree && l1.length()>0 ) { result.push_back(points[i]); } } if (result.last()!=points.last()) result.push_back(points.last()); return result; } else { return points; } } JKQTPAdaptiveFunctionGraphEvaluator::JKQTPAdaptiveFunctionGraphEvaluator(const std::function &fx_, const std::function &fy_, unsigned int minSamples_, unsigned int maxRefinementDegree_, double slopeTolerance_, double minPixelPerSample_): fx(fx_), fy(fy_), fxy([&](double t)->QPointF { return QPointF(fx(t), fy(t)); }), minSamples(minSamples_), maxRefinementDegree(maxRefinementDegree_), slopeTolerance(slopeTolerance_), minPixelPerSample(minPixelPerSample_) { } JKQTPAdaptiveFunctionGraphEvaluator::JKQTPAdaptiveFunctionGraphEvaluator(const std::function &fxy_, unsigned int minSamples_, unsigned int maxRefinementDegree_, double slopeTolerance_, double minPixelPerSample_): fxy(fxy_), minSamples(minSamples_), maxRefinementDegree(maxRefinementDegree_), slopeTolerance(slopeTolerance_), minPixelPerSample(minPixelPerSample_) { } QVector JKQTPAdaptiveFunctionGraphEvaluator::evaluate(double tmin, double tmax) const { InternalList intData; double delta_t0=(tmax-tmin)/static_cast(minSamples); intData.push_front(std::pair(tmin, fxy(tmin))); InternalList::iterator a=intData.begin(); //qDebug()<<"**************************************************"; for (double t=tmin+delta_t0; t(treal, fxy(treal))); InternalList::iterator b=a; ++b; //qDebug()<<"t="< ps.size()="< parallel line"; line=QLineF(); return false; } if (p1 != 0) { const qreal r1 = q1 / p1; const qreal r2 = q2 / p2; if (p1 < 0) { negarr.push_back(r1); // for negative p1, add it to negative array posarr.push_back(r2); // and add p2 to positive array } else { negarr.push_back(r2); posarr.push_back(r1); } } if (p3 != 0) { const qreal r3 = q3 / p3; const qreal r4 = q4 / p4; if (p3 < 0) { negarr.push_back(r3); posarr.push_back(r4); } else { negarr.push_back(r4); posarr.push_back(r3); } } const qreal rn1 = *std::max_element(negarr.begin(), negarr.end()); // maximum of negative array const qreal rn2 = *std::min_element(posarr.begin(), posarr.end()); // minimum of positive array //qDebug()<<" rn1="< rejected line"; line=QLineF(); return false; } const qreal xn1 = line.x1() + p2 * rn1; const qreal yn1 = line.y1() + p4 * rn1; // computing new points const qreal xn2 = line.x1() + p2 * rn2; const qreal yn2 = line.y1() + p4 * rn2; line=QLineF(xn1, yn1, xn2, yn2); // the drawing the new line //qDebug()<<" --> clipped line: "<