// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cubicsegment.h" #include #include 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 &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 CubicSegment::attributes() const { return d->attributes; } double CubicSegment::percent() const { return d->percent; } QList CubicSegment::controlPoints() const { QList 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::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