2016-01-15 14:58:39 +01:00
|
|
|
/****************************************************************************
|
2015-08-16 13:11:15 +02:00
|
|
|
**
|
2016-01-15 14:58:39 +01:00
|
|
|
** Copyright (C) 2016 Jochen Becher
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2015-08-16 13:11:15 +02:00
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2016-01-15 14:58:39 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2015-08-16 13:11:15 +02:00
|
|
|
**
|
2016-01-15 14:58:39 +01:00
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
2015-08-16 13:11:15 +02:00
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "geometryutilities.h"
|
|
|
|
|
|
|
|
|
|
#include "qmt/infrastructure/qmtassert.h"
|
|
|
|
|
|
|
|
|
|
#include <QPolygonF>
|
|
|
|
|
#include <QLineF>
|
|
|
|
|
#include <QPointF>
|
|
|
|
|
#include <QPair>
|
|
|
|
|
#include <QList>
|
|
|
|
|
#include <QVector2D>
|
|
|
|
|
#include <qmath.h>
|
|
|
|
|
#include <qdebug.h>
|
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
inline int sgn(T val)
|
|
|
|
|
{
|
|
|
|
|
return (T(0) < val) - (val < T(0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace qmt {
|
|
|
|
|
|
2015-11-04 07:52:44 +01:00
|
|
|
QLineF GeometryUtilities::stretch(const QLineF &line, double p1Extension, double p2Extension)
|
2015-08-16 13:11:15 +02:00
|
|
|
{
|
|
|
|
|
QLineF direction = line.unitVector();
|
2015-11-04 07:52:44 +01:00
|
|
|
QPointF stretchedP1 = line.p1() - (direction.p2() - direction.p1()) * p1Extension;
|
|
|
|
|
QPointF stretchedP2 = line.p2() + (direction.p2() - direction.p1()) * p2Extension;
|
|
|
|
|
return QLineF(stretchedP1, stretchedP2);
|
2015-08-16 13:11:15 +02:00
|
|
|
}
|
|
|
|
|
|
2015-11-15 19:11:16 +01:00
|
|
|
bool GeometryUtilities::intersect(const QPolygonF &polygon, const QLineF &line,
|
|
|
|
|
QPointF *intersectionPoint, QLineF *intersectionLine)
|
2015-08-16 13:11:15 +02:00
|
|
|
{
|
|
|
|
|
for (int i = 0; i <= polygon.size() - 2; ++i) {
|
2015-11-04 07:52:44 +01:00
|
|
|
QLineF polygonLine(polygon.at(i), polygon.at(i+1));
|
|
|
|
|
QLineF::IntersectType intersectionType = polygonLine.intersect(line, intersectionPoint);
|
|
|
|
|
if (intersectionType == QLineF::BoundedIntersection) {
|
2015-11-14 16:59:30 +01:00
|
|
|
if (intersectionLine)
|
2015-11-04 07:52:44 +01:00
|
|
|
*intersectionLine = polygonLine;
|
2015-08-16 13:11:15 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2015-11-09 23:09:23 +01:00
|
|
|
class Candidate
|
|
|
|
|
{
|
|
|
|
|
public:
|
2015-11-24 20:00:24 +01:00
|
|
|
Candidate() = default;
|
2015-08-16 13:11:15 +02:00
|
|
|
Candidate(const QVector2D &f, const QPointF &s, GeometryUtilities::Side t) : first(f), second(s), third(t) { }
|
|
|
|
|
|
|
|
|
|
QVector2D first;
|
|
|
|
|
QPointF second;
|
2015-11-23 22:48:45 +01:00
|
|
|
GeometryUtilities::Side third = GeometryUtilities::SideUnspecified;
|
2015-08-16 13:11:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-15 19:11:16 +01:00
|
|
|
bool GeometryUtilities::placeRectAtLine(const QRectF &rect, const QLineF &line, double lineOffset, double distance,
|
|
|
|
|
const QLineF &intersectionLine, QPointF *placement, Side *horizontalAlignedSide)
|
2015-08-16 13:11:15 +02:00
|
|
|
{
|
2017-07-09 11:49:13 +02:00
|
|
|
QMT_ASSERT(placement, return false);
|
2015-08-16 13:11:15 +02:00
|
|
|
|
|
|
|
|
QList<Candidate> candidates;
|
2015-11-04 23:34:44 +01:00
|
|
|
candidates << Candidate(QVector2D(rect.topRight() - rect.topLeft()), QPointF(0.0, 0.0), SideTop)
|
|
|
|
|
<< Candidate(QVector2D(rect.topLeft() - rect.topRight()), rect.topRight() - rect.topLeft(), SideTop)
|
|
|
|
|
<< Candidate(QVector2D(rect.bottomLeft() - rect.topLeft()), QPointF(0.0, 0.0), SideLeft)
|
|
|
|
|
<< Candidate(QVector2D(rect.topLeft() - rect.bottomLeft()), rect.bottomLeft() - rect.topLeft(), SideLeft)
|
|
|
|
|
<< Candidate(QVector2D(rect.bottomRight() - rect.bottomLeft()), rect.bottomLeft() - rect.topLeft(), SideBottom)
|
|
|
|
|
<< Candidate(QVector2D(rect.bottomLeft() - rect.bottomRight()), rect.bottomRight() - rect.topLeft(), SideBottom)
|
|
|
|
|
<< Candidate(QVector2D(rect.bottomRight() - rect.topRight()), rect.topRight() - rect.topLeft(), SideRight)
|
|
|
|
|
<< Candidate(QVector2D(rect.topRight() - rect.bottomRight()), rect.bottomRight() - rect.topLeft(), SideRight);
|
2015-08-16 13:11:15 +02:00
|
|
|
|
2015-11-04 07:52:44 +01:00
|
|
|
QVector<QVector2D> rectEdgeVectors;
|
|
|
|
|
rectEdgeVectors << QVector2D(rect.topLeft() - rect.topLeft())
|
2015-11-15 19:11:16 +01:00
|
|
|
<< QVector2D(rect.topRight() - rect.topLeft())
|
|
|
|
|
<< QVector2D(rect.bottomLeft() - rect.topLeft())
|
|
|
|
|
<< QVector2D(rect.bottomRight() -rect.topLeft());
|
2015-08-16 13:11:15 +02:00
|
|
|
|
2015-11-04 07:52:44 +01:00
|
|
|
QVector2D directionVector(line.p2() - line.p1());
|
|
|
|
|
directionVector.normalize();
|
2015-08-16 13:11:15 +02:00
|
|
|
|
2015-11-04 07:52:44 +01:00
|
|
|
QVector2D sideVector(directionVector.y(), -directionVector.x());
|
2015-08-16 13:11:15 +02:00
|
|
|
|
2015-11-04 07:52:44 +01:00
|
|
|
QVector2D intersectionVector(intersectionLine.p2() - intersectionLine.p1());
|
|
|
|
|
intersectionVector.normalize();
|
2015-08-16 13:11:15 +02:00
|
|
|
|
2015-11-04 07:52:44 +01:00
|
|
|
QVector2D outsideVector = QVector2D(intersectionVector.y(), -intersectionVector.x());
|
|
|
|
|
double p = QVector2D::dotProduct(directionVector, outsideVector);
|
2015-11-14 16:59:30 +01:00
|
|
|
if (p < 0.0)
|
2015-11-04 07:52:44 +01:00
|
|
|
outsideVector = outsideVector * -1.0;
|
2015-08-16 13:11:15 +02:00
|
|
|
|
2015-11-04 07:52:44 +01:00
|
|
|
double smallestA = -1.0;
|
|
|
|
|
QPointF rectTranslation;
|
2015-11-04 23:34:44 +01:00
|
|
|
Side side = SideUnspecified;
|
2015-11-04 07:52:44 +01:00
|
|
|
int bestSign = 0;
|
2015-08-16 13:11:15 +02:00
|
|
|
|
|
|
|
|
foreach (const Candidate &candidate, candidates) {
|
2015-11-04 07:52:44 +01:00
|
|
|
// solve equation a * directionVector + candidate.first = b * intersectionVector to find smallest a
|
|
|
|
|
double r = directionVector.x() * intersectionVector.y() - directionVector.y() * intersectionVector.x();
|
2015-08-16 13:11:15 +02:00
|
|
|
if (r <= -1e-5 || r >= 1e-5) {
|
2015-11-15 19:11:16 +01:00
|
|
|
double a = (candidate.first.y() * intersectionVector.x()
|
|
|
|
|
- candidate.first.x() * intersectionVector.y()) / r;
|
2015-11-04 07:52:44 +01:00
|
|
|
if (a >= 0.0 && (smallestA < 0.0 || a < smallestA)) {
|
2015-08-16 13:11:15 +02:00
|
|
|
// verify that all rectangle edges lay outside of shape (by checking for positiv projection to intersection)
|
|
|
|
|
bool ok = true;
|
|
|
|
|
int sign = 0;
|
2015-11-04 07:52:44 +01:00
|
|
|
QVector2D rectOriginVector = directionVector * a - QVector2D(candidate.second);
|
|
|
|
|
foreach (const QVector2D &rectEdgeVector, rectEdgeVectors) {
|
|
|
|
|
QVector2D edgeVector = rectOriginVector + rectEdgeVector;
|
|
|
|
|
double aa = QVector2D::dotProduct(outsideVector, edgeVector);
|
2015-08-16 13:11:15 +02:00
|
|
|
if (aa < 0.0) {
|
|
|
|
|
ok = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-11-04 07:52:44 +01:00
|
|
|
int s = sgn(QVector2D::dotProduct(sideVector, edgeVector));
|
2015-08-16 13:11:15 +02:00
|
|
|
if (s) {
|
|
|
|
|
if (sign) {
|
|
|
|
|
if (s != sign) {
|
|
|
|
|
ok = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
sign = s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ok) {
|
2015-11-04 07:52:44 +01:00
|
|
|
smallestA = a;
|
|
|
|
|
rectTranslation = candidate.second;
|
2015-08-16 13:11:15 +02:00
|
|
|
side = candidate.third;
|
2015-11-04 07:52:44 +01:00
|
|
|
bestSign = sign;
|
2015-08-16 13:11:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-04 07:52:44 +01:00
|
|
|
if (horizontalAlignedSide) {
|
2015-08-16 13:11:15 +02:00
|
|
|
// convert side into a horizontal side depending on placement relative to direction vector
|
|
|
|
|
switch (side) {
|
2015-11-04 23:34:44 +01:00
|
|
|
case SideTop:
|
|
|
|
|
side = bestSign == -1 ? SideRight : SideLeft;
|
2015-08-16 13:11:15 +02:00
|
|
|
break;
|
2015-11-04 23:34:44 +01:00
|
|
|
case SideBottom:
|
|
|
|
|
side = bestSign == -1 ? SideLeft : SideRight;
|
2015-08-16 13:11:15 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-11-04 07:52:44 +01:00
|
|
|
*horizontalAlignedSide = side;
|
2015-08-16 13:11:15 +02:00
|
|
|
}
|
2015-11-14 16:59:30 +01:00
|
|
|
if (smallestA < 0.0)
|
2015-08-16 13:11:15 +02:00
|
|
|
return false;
|
2015-11-15 19:11:16 +01:00
|
|
|
*placement = line.p1() + (directionVector * (smallestA + lineOffset)).toPointF()
|
|
|
|
|
+ (sideVector * (bestSign * distance)).toPointF() - rectTranslation;
|
2015-08-16 13:11:15 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double GeometryUtilities::calcAngle(const QLineF &line)
|
|
|
|
|
{
|
2015-11-04 07:52:44 +01:00
|
|
|
QVector2D directionVector(line.p2() - line.p1());
|
|
|
|
|
directionVector.normalize();
|
|
|
|
|
double angle = qAcos(directionVector.x()) * 180.0 / 3.1415926535;
|
2015-11-14 16:59:30 +01:00
|
|
|
if (directionVector.y() > 0.0)
|
2015-08-16 13:11:15 +02:00
|
|
|
angle = -angle;
|
|
|
|
|
return angle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
// scalar product
|
|
|
|
|
static qreal operator&(const QVector2D &lhs, const QVector2D &rhs) {
|
|
|
|
|
return lhs.x() * rhs.x() + lhs.y() * rhs.y();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double GeometryUtilities::calcDistancePointToLine(const QPointF &point, const QLineF &line)
|
|
|
|
|
{
|
|
|
|
|
QVector2D p(point);
|
|
|
|
|
QVector2D a(line.p1());
|
2015-11-04 07:52:44 +01:00
|
|
|
QVector2D directionVector(line.p2() - line.p1());
|
|
|
|
|
qreal r = -((a - p) & directionVector) / directionVector.lengthSquared();
|
2015-11-14 16:59:30 +01:00
|
|
|
if (r < 0.0 || r > 1.0)
|
2015-08-16 13:11:15 +02:00
|
|
|
return std::numeric_limits<float>::quiet_NaN();
|
2015-11-04 07:52:44 +01:00
|
|
|
qreal d = (a + r * directionVector - p).length();
|
2015-08-16 13:11:15 +02:00
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPointF GeometryUtilities::calcProjection(const QLineF &line, const QPointF &point)
|
|
|
|
|
{
|
|
|
|
|
QVector2D p(point);
|
|
|
|
|
QVector2D a(line.p1());
|
2015-11-04 07:52:44 +01:00
|
|
|
QVector2D directionVector(line.p2() - line.p1());
|
|
|
|
|
qreal r = -((a - p) & directionVector) / directionVector.lengthSquared();
|
|
|
|
|
return (a + r * directionVector).toPointF();
|
2015-08-16 13:11:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPointF GeometryUtilities::calcPrimaryAxisDirection(const QLineF &line)
|
|
|
|
|
{
|
|
|
|
|
qreal xAbs = qAbs(line.dx());
|
|
|
|
|
qreal yAbs = qAbs(line.dy());
|
|
|
|
|
if (yAbs > xAbs) {
|
2015-11-14 16:59:30 +01:00
|
|
|
if (line.dy() >= 0.0)
|
2015-08-16 13:11:15 +02:00
|
|
|
return QPointF(0.0, 1.0);
|
2015-11-14 16:59:30 +01:00
|
|
|
else
|
2015-08-16 13:11:15 +02:00
|
|
|
return QPointF(0.0, -1.0);
|
|
|
|
|
} else {
|
2015-11-14 16:59:30 +01:00
|
|
|
if (line.dx() >= 0.0)
|
2015-08-16 13:11:15 +02:00
|
|
|
return QPointF(1.0, 0.0);
|
2015-11-14 16:59:30 +01:00
|
|
|
else
|
2015-08-16 13:11:15 +02:00
|
|
|
return QPointF(-1.0, 0.0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPointF GeometryUtilities::calcSecondaryAxisDirection(const QLineF &line)
|
|
|
|
|
{
|
|
|
|
|
qreal xAbs = qAbs(line.dx());
|
|
|
|
|
qreal yAbs = qAbs(line.dy());
|
|
|
|
|
if (yAbs > xAbs) {
|
2015-11-14 16:59:30 +01:00
|
|
|
if (line.dx() >= 0.0)
|
2015-08-16 13:11:15 +02:00
|
|
|
return QPointF(1.0, 0.0);
|
2015-11-14 16:59:30 +01:00
|
|
|
else
|
2015-08-16 13:11:15 +02:00
|
|
|
return QPointF(-1.0, 0.0);
|
|
|
|
|
} else {
|
2015-11-14 16:59:30 +01:00
|
|
|
if (line.dy() >= 0.0)
|
2015-08-16 13:11:15 +02:00
|
|
|
return QPointF(0.0, 1.0);
|
2015-11-14 16:59:30 +01:00
|
|
|
else
|
2015-08-16 13:11:15 +02:00
|
|
|
return QPointF(0.0, -1.0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-15 19:11:16 +01:00
|
|
|
void GeometryUtilities::adjustPosAndRect(QPointF *pos, QRectF *rect, const QPointF &topLeftDelta,
|
|
|
|
|
const QPointF &bottomRightDelta, const QPointF &relativeAlignment)
|
2015-08-16 13:11:15 +02:00
|
|
|
{
|
2015-11-04 07:52:44 +01:00
|
|
|
*pos += QPointF(topLeftDelta.x() * (1.0 - relativeAlignment.x()) + bottomRightDelta.x() * relativeAlignment.x(),
|
|
|
|
|
topLeftDelta.y() * (1.0 - relativeAlignment.y()) + bottomRightDelta.y() * relativeAlignment.y());
|
|
|
|
|
rect->adjust(topLeftDelta.x() * relativeAlignment.x() - bottomRightDelta.x() * relativeAlignment.x(),
|
|
|
|
|
topLeftDelta.y() * relativeAlignment.y() - bottomRightDelta.y() * relativeAlignment.y(),
|
|
|
|
|
bottomRightDelta.x() * (1.0 - relativeAlignment.x()) - topLeftDelta.x() * (1.0 - relativeAlignment.x()),
|
|
|
|
|
bottomRightDelta.y() * (1.0 - relativeAlignment.y()) - topLeftDelta.y() * (1.0 - relativeAlignment.y()));
|
2015-08-16 13:11:15 +02:00
|
|
|
}
|
|
|
|
|
|
2015-11-04 07:52:44 +01:00
|
|
|
QSizeF GeometryUtilities::ensureMinimumRasterSize(const QSizeF &size, double rasterWidth, double rasterHeight)
|
2015-08-16 13:11:15 +02:00
|
|
|
{
|
2015-11-04 07:52:44 +01:00
|
|
|
double width = int(size.width() / rasterWidth + 0.99999) * rasterWidth;
|
|
|
|
|
double height = int(size.height() / rasterHeight + 0.99999) * rasterHeight;
|
2015-08-16 13:11:15 +02:00
|
|
|
return QSizeF(width, height);
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-14 16:59:30 +01:00
|
|
|
} // namespace qmt
|
2015-08-16 13:11:15 +02:00
|
|
|
|