diff options
Diffstat (limited to 'src/plugins/qmldesigner/components/pathtool/cubicsegment.cpp')
-rw-r--r-- | src/plugins/qmldesigner/components/pathtool/cubicsegment.cpp | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/src/plugins/qmldesigner/components/pathtool/cubicsegment.cpp b/src/plugins/qmldesigner/components/pathtool/cubicsegment.cpp new file mode 100644 index 0000000000..0005514339 --- /dev/null +++ b/src/plugins/qmldesigner/components/pathtool/cubicsegment.cpp @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 +** 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. +** +** 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. +** +****************************************************************************/ + +#include "cubicsegment.h" + +#include <qmath.h> +#include <QtDebug> + + +namespace QmlDesigner { + +CubicSegment::CubicSegment() = default; + +CubicSegment CubicSegment::create() +{ + CubicSegment cubicSegment; + cubicSegment.d = new CubicSegmentData; + + return cubicSegment; +} + +void CubicSegment::setModelNode(const ModelNode &modelNode) +{ + d->modelNode = modelNode; +} + +ModelNode CubicSegment::modelNode() const +{ + return d->modelNode; +} + +void CubicSegment::setFirstControlPoint(const ControlPoint &firstControlPoint) +{ + d->firstControllPoint = firstControlPoint; +} + +void CubicSegment::setFirstControlPoint(double x, double y) +{ + d->firstControllPoint.setX(x); + d->firstControllPoint.setY(y); +} + +void CubicSegment::setFirstControlPoint(const QPointF &coordiante) +{ + d->firstControllPoint.setCoordinate(coordiante); +} + +void CubicSegment::setSecondControlPoint(const ControlPoint &secondControlPoint) +{ + d->secondControllPoint = secondControlPoint; + d->secondControllPoint.setPathElementModelNode(d->modelNode); + d->secondControllPoint.setPointType(FirstControlPoint); +} + +void CubicSegment::setSecondControlPoint(double x, double y) +{ + d->secondControllPoint.setX(x); + d->secondControllPoint.setY(y); + d->secondControllPoint.setPathElementModelNode(d->modelNode); + d->secondControllPoint.setPointType(FirstControlPoint); +} + +void CubicSegment::setSecondControlPoint(const QPointF &coordiante) +{ + d->secondControllPoint.setCoordinate(coordiante); + d->secondControllPoint.setPathElementModelNode(d->modelNode); + d->secondControllPoint.setPointType(FirstControlPoint); +} + +void CubicSegment::setThirdControlPoint(const ControlPoint &thirdControlPoint) +{ + d->thirdControllPoint = thirdControlPoint; + d->thirdControllPoint.setPathElementModelNode(d->modelNode); + d->thirdControllPoint.setPointType(SecondControlPoint); +} + +void CubicSegment::setThirdControlPoint(double x, double y) +{ + d->thirdControllPoint.setX(x); + d->thirdControllPoint.setY(y); + d->thirdControllPoint.setPathElementModelNode(d->modelNode); + d->thirdControllPoint.setPointType(SecondControlPoint); +} + +void CubicSegment::setThirdControlPoint(const QPointF &coordiante) +{ + d->thirdControllPoint.setCoordinate(coordiante); + d->thirdControllPoint.setPathElementModelNode(d->modelNode); + d->thirdControllPoint.setPointType(SecondControlPoint); +} + +void CubicSegment::setFourthControlPoint(const ControlPoint &fourthControlPoint) +{ + d->fourthControllPoint = fourthControlPoint; + d->fourthControllPoint.setPathElementModelNode(d->modelNode); + d->fourthControllPoint.setPointType(EndPoint); +} + +void CubicSegment::setFourthControlPoint(double x, double y) +{ + d->fourthControllPoint.setX(x); + d->fourthControllPoint.setY(y); + d->fourthControllPoint.setPathElementModelNode(d->modelNode); + d->fourthControllPoint.setPointType(EndPoint); +} + +void CubicSegment::setFourthControlPoint(const QPointF &coordiante) +{ + d->fourthControllPoint.setCoordinate(coordiante); + d->fourthControllPoint.setPathElementModelNode(d->modelNode); + d->fourthControllPoint.setPointType(EndPoint); +} + +void CubicSegment::setAttributes(const QMap<QString, QVariant> &attributes) +{ + d->attributes = attributes; +} + +void CubicSegment::setPercent(double percent) +{ + d->percent = percent; +} + +ControlPoint CubicSegment::firstControlPoint() const +{ + return d->firstControllPoint; +} + +ControlPoint CubicSegment::secondControlPoint() const +{ + return d->secondControllPoint; +} + +ControlPoint CubicSegment::thirdControlPoint() const +{ + return d->thirdControllPoint; +} + +ControlPoint CubicSegment::fourthControlPoint() const +{ + return d->fourthControllPoint; +} + +const QMap<QString, QVariant> CubicSegment::attributes() const +{ + return d->attributes; +} + +double CubicSegment::percent() const +{ + return d->percent; +} + +QList<ControlPoint> CubicSegment::controlPoints() const +{ + QList<ControlPoint> controlPointList; + + controlPointList.reserve(4); + + controlPointList.append(firstControlPoint()); + controlPointList.append(secondControlPoint()); + controlPointList.append(thirdControlPoint()); + controlPointList.append(fourthControlPoint()); + + return controlPointList; +} + +double CubicSegment::firstControlX() const +{ + return firstControlPoint().coordinate().x(); +} + +double CubicSegment::firstControlY() const +{ + return firstControlPoint().coordinate().y(); +} + +double CubicSegment::secondControlX() const +{ + return secondControlPoint().coordinate().x(); +} + +double CubicSegment::secondControlY() const +{ + return secondControlPoint().coordinate().y(); +} + +double CubicSegment::thirdControlX() const +{ + return thirdControlPoint().coordinate().x(); +} + +double CubicSegment::thirdControlY() const +{ + return thirdControlPoint().coordinate().y(); +} + +double CubicSegment::fourthControlX() const +{ + return fourthControlPoint().coordinate().x(); +} + +double CubicSegment::fourthControlY() const +{ + return fourthControlPoint().coordinate().y(); +} + +double CubicSegment::quadraticControlX() const +{ + return -0.25 * firstControlX() + 0.75 * secondControlX() + 0.75 * thirdControlX() - 0.25 * fourthControlX(); +} + +double CubicSegment::quadraticControlY() const +{ + return -0.25 * firstControlY() + 0.75 * secondControlY() + 0.75 * thirdControlY() - 0.25 * fourthControlY(); +} + +bool CubicSegment::isValid() const +{ + return d.data(); +} + +bool CubicSegment::canBeConvertedToLine() const +{ + return canBeConvertedToQuad() + && qFuzzyIsNull(((3. * d->firstControllPoint.coordinate()) + - (6. * d->secondControllPoint.coordinate()) + + (3. * d->thirdControllPoint.coordinate())).manhattanLength());; +} + +bool CubicSegment::canBeConvertedToQuad() const +{ + return qFuzzyIsNull(((3. * d->secondControllPoint.coordinate()) + - (3 * d->thirdControllPoint.coordinate()) + + d->fourthControllPoint.coordinate() + - d->firstControllPoint.coordinate()).manhattanLength()); +} + +QPointF CubicSegment::sample(double t) const +{ + return qPow(1.-t, 3.) * firstControlPoint().coordinate() + + 3 * qPow(1.-t, 2.) * t * secondControlPoint().coordinate() + + 3 * qPow(t, 2.) * (1. - t) * thirdControlPoint().coordinate() + + qPow(t, 3.) * fourthControlPoint().coordinate(); +} + +double CubicSegment::minimumDistance(const QPointF &pickPoint, double &tReturnValue) const +{ + double actualMinimumDistance = 10000000.; + for (double t = 0.0; t <= 1.0; t += 0.1) { + QPointF samplePoint = sample(t); + QPointF distanceVector = pickPoint - samplePoint; + if (distanceVector.manhattanLength() < actualMinimumDistance) { + actualMinimumDistance = distanceVector.manhattanLength(); + tReturnValue = t; + } + } + + return actualMinimumDistance; +} + +static QPointF interpolatedPoint(double t, const QPointF &firstPoint, const QPointF &secondPoint) +{ + return (secondPoint - firstPoint) * t + firstPoint; +} + +QPair<CubicSegment, CubicSegment> CubicSegment::split(double t) +{ + // first pass + QPointF secondPointFirstSegment = interpolatedPoint(t, firstControlPoint().coordinate(), secondControlPoint().coordinate()); + QPointF firstIntermediatPoint = interpolatedPoint(t, secondControlPoint().coordinate(), thirdControlPoint().coordinate()); + QPointF thirdPointSecondSegment = interpolatedPoint(t, thirdControlPoint().coordinate(), fourthControlPoint().coordinate()); + + // second pass + QPointF thirdPointFirstSegment = interpolatedPoint(t, secondPointFirstSegment, firstIntermediatPoint); + QPointF secondPointSecondSegment = interpolatedPoint(t, firstIntermediatPoint, thirdPointSecondSegment); + + // third pass + QPointF midPoint = interpolatedPoint(t, thirdPointFirstSegment, secondPointSecondSegment); + ControlPoint midControlPoint(midPoint); + + + CubicSegment firstCubicSegment = CubicSegment::create(); + firstCubicSegment.setFirstControlPoint(firstControlPoint().coordinate()); + firstCubicSegment.setSecondControlPoint(secondPointFirstSegment); + firstCubicSegment.setThirdControlPoint(thirdPointFirstSegment); + firstCubicSegment.setFourthControlPoint(midControlPoint); + + CubicSegment secondCubicSegment = CubicSegment::create(); + secondCubicSegment.setFirstControlPoint(midControlPoint); + secondCubicSegment.setSecondControlPoint(secondPointSecondSegment); + secondCubicSegment.setThirdControlPoint(thirdPointSecondSegment); + secondCubicSegment.setFourthControlPoint(fourthControlPoint().coordinate()); + + qDebug() << firstCubicSegment << secondCubicSegment; + + return {firstCubicSegment, secondCubicSegment}; +} + +void CubicSegment::makeStraightLine() +{ + QPointF lineVector = fourthControlPoint().coordinate() - firstControlPoint().coordinate(); + QPointF newSecondControlPoint = firstControlPoint().coordinate() + (lineVector * 0.3); + QPointF newThirdControlPoint = fourthControlPoint().coordinate() - (lineVector * 0.3); + setSecondControlPoint(newSecondControlPoint); + setThirdControlPoint(newThirdControlPoint); +} + +void CubicSegment::updateModelNode() +{ + firstControlPoint().updateModelNode(); + secondControlPoint().updateModelNode(); + thirdControlPoint().updateModelNode(); + fourthControlPoint().updateModelNode(); +} + +CubicSegmentData::CubicSegmentData() + : firstControllPoint(0., 0.), + secondControllPoint(0., 0.), + thirdControllPoint(0., 0.), + fourthControllPoint(0., 0.), + percent(-1.0) +{ +} + +bool operator ==(const CubicSegment& firstCubicSegment, const CubicSegment& secondCubicSegment) +{ + return firstCubicSegment.d.data() == secondCubicSegment.d.data(); +} + +QDebug operator<<(QDebug debug, const CubicSegment &cubicSegment) +{ + if (cubicSegment.isValid()) { + debug.nospace() << "CubicSegment(" + << cubicSegment.firstControlPoint() << ", " + << cubicSegment.secondControlPoint() << ", " + << cubicSegment.thirdControlPoint() << ", " + << cubicSegment.fourthControlPoint() << ')'; + } else { + debug.nospace() << "CubicSegment(invalid)"; + } + + return debug.space(); +} +} // namespace QmlDesigner |