465 lines
17 KiB
C++
465 lines
17 KiB
C++
/************************************************************************
|
|
**
|
|
** @file vgrainline.cpp
|
|
** @author Roman Telezhynskyi <dismine(at)gmail.com>
|
|
** @date 27 4, 2023
|
|
**
|
|
** @brief
|
|
** @copyright
|
|
** This source code is part of the Valentina project, a pattern making
|
|
** program, whose allow create and modeling patterns of clothing.
|
|
** Copyright (C) 2023 Valentina project
|
|
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
|
|
**
|
|
** Valentina is free software: you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation, either version 3 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** Valentina 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 General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
|
|
**
|
|
*************************************************************************/
|
|
#include "vpiecegrainline.h"
|
|
#include "../vgeometry/vabstractcurve.h"
|
|
#include "compatibility.h"
|
|
#include "qmath.h"
|
|
#include "vpiecegrainline_p.h"
|
|
|
|
#include <QPolygonF>
|
|
#include <QTransform>
|
|
#include <algorithm>
|
|
|
|
namespace
|
|
{
|
|
constexpr qreal arrowAngle = M_PI / 9;
|
|
constexpr int arrowLength = 15;
|
|
} // namespace
|
|
|
|
// VPieceGrainlinePrivate
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainlinePrivate::MainLine(const QPointF &p1, qreal length, qreal angle) -> QLineF
|
|
{
|
|
QPointF const pt2(p1.x() + length * cos(angle), p1.y() - length * sin(angle));
|
|
return {p1, pt2};
|
|
}
|
|
|
|
// VPieceGrainline
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
QT_WARNING_PUSH
|
|
QT_WARNING_DISABLE_GCC("-Wnoexcept")
|
|
|
|
VPieceGrainline::VPieceGrainline()
|
|
: d(new VPieceGrainlinePrivate)
|
|
{
|
|
}
|
|
|
|
QT_WARNING_POP
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
VPieceGrainline::~VPieceGrainline() = default;
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
VPieceGrainline::VPieceGrainline(const QLineF &mainLine, GrainlineArrowDirection arrowType)
|
|
: d(new VPieceGrainlinePrivate(mainLine, arrowType))
|
|
{
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
VPieceGrainline::VPieceGrainline(const QPointF &p1, qreal length, qreal angle, GrainlineArrowDirection arrowType)
|
|
: d(new VPieceGrainlinePrivate(VPieceGrainlinePrivate::MainLine(p1, length, angle), arrowType))
|
|
{
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
COPY_CONSTRUCTOR_IMPL(VPieceGrainline)
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::operator=(const VPieceGrainline &grainline) -> VPieceGrainline &
|
|
{
|
|
if (&grainline == this)
|
|
{
|
|
return *this;
|
|
}
|
|
d = grainline.d;
|
|
return *this;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
VPieceGrainline::VPieceGrainline(VPieceGrainline &&grainline) noexcept
|
|
: d(std::move(grainline.d))
|
|
{
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::operator=(VPieceGrainline &&grainline) noexcept -> VPieceGrainline &
|
|
{
|
|
std::swap(d, grainline.d);
|
|
return *this;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::GetMainLine() const -> QLineF
|
|
{
|
|
return d->m_mainLine;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
void VPieceGrainline::SetMainLine(const QLineF &mainLine)
|
|
{
|
|
d->m_mainLine = mainLine;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::GetArrowType() const -> GrainlineArrowDirection
|
|
{
|
|
return d->m_arrowType;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
void VPieceGrainline::SetArrowType(GrainlineArrowDirection arrowType)
|
|
{
|
|
d->m_arrowType = arrowType;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::IsEnabled() const -> bool
|
|
{
|
|
return d->m_enabled;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
void VPieceGrainline::SetEnabled(bool enabled)
|
|
{
|
|
d->m_enabled = enabled;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::IsVisible() const -> bool
|
|
{
|
|
return d->m_visible;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
void VPieceGrainline::SetVisible(bool visible)
|
|
{
|
|
d->m_visible = visible;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::SecondaryLine() const -> QLineF
|
|
{
|
|
const QLineF mainLine = GetMainLine();
|
|
const QPointF center = mainLine.center();
|
|
QTransform t;
|
|
t.translate(center.x(), center.y());
|
|
t.rotate(90);
|
|
t.translate(-center.x(), -center.y());
|
|
return t.map(mainLine);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::IsFourWays() const -> bool
|
|
{
|
|
return d->m_arrowType == GrainlineArrowDirection::fourWays ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysUpLeft ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysUpRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysDownLeft ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysDownRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpDownLeft ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpDownRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpLeftRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysDownLeftRight;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::IsArrowUpEnabled() const -> bool
|
|
{
|
|
return d->m_arrowType == GrainlineArrowDirection::oneWayUp ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysUpDown ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysUpLeft ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysUpRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpDownLeft ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpDownRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpLeftRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::fourWays;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::IsArrowDownEnabled() const -> bool
|
|
{
|
|
return d->m_arrowType == GrainlineArrowDirection::oneWayDown ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysUpDown ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysDownLeft ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysDownRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpDownLeft ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpDownRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysDownLeftRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::fourWays;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::IsArrowLeftEnabled() const -> bool
|
|
{
|
|
return d->m_arrowType == GrainlineArrowDirection::fourWays ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysUpLeft ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysDownLeft ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpDownLeft ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpLeftRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysDownLeftRight;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::IsArrowRightEnabled() const -> bool
|
|
{
|
|
return d->m_arrowType == GrainlineArrowDirection::fourWays ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysUpRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::twoWaysDownRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpDownRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysUpLeftRight ||
|
|
d->m_arrowType == GrainlineArrowDirection::threeWaysDownLeftRight;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::ArrowUp() const -> QPolygonF
|
|
{
|
|
const QLineF mainLine = GetMainLine();
|
|
const qreal rotation = M_PI + qDegreesToRadians(mainLine.angle());
|
|
const QPointF pt = mainLine.p2();
|
|
|
|
return {
|
|
{pt,
|
|
QPointF(pt.x() + arrowLength * cos(rotation + arrowAngle), pt.y() - arrowLength * sin(rotation + arrowAngle)),
|
|
QPointF(pt.x() + arrowLength * cos(rotation - arrowAngle), pt.y() - arrowLength * sin(rotation - arrowAngle)),
|
|
pt}};
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::ArrowDown() const -> QPolygonF
|
|
{
|
|
const QLineF mainLine = GetMainLine();
|
|
const qreal rotation = qDegreesToRadians(mainLine.angle());
|
|
const QPointF pt = mainLine.p1();
|
|
|
|
return {
|
|
{pt,
|
|
QPointF(pt.x() + arrowLength * cos(rotation + arrowAngle), pt.y() - arrowLength * sin(rotation + arrowAngle)),
|
|
QPointF(pt.x() + arrowLength * cos(rotation - arrowAngle), pt.y() - arrowLength * sin(rotation - arrowAngle)),
|
|
pt}};
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::ArrowLeft() const -> QPolygonF
|
|
{
|
|
const qreal rotation = 3 * M_PI / 2 + qDegreesToRadians(GetMainLine().angle());
|
|
const QPointF pt = SecondaryLine().p1();
|
|
|
|
return {
|
|
{pt,
|
|
QPointF(pt.x() + arrowLength * cos(rotation - arrowAngle), pt.y() - arrowLength * sin(rotation - arrowAngle)),
|
|
QPointF(pt.x() + arrowLength * cos(rotation + arrowAngle), pt.y() - arrowLength * sin(rotation + arrowAngle)),
|
|
pt}};
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::ArrowRight() const -> QPolygonF
|
|
{
|
|
const qreal rotation = M_PI / 2 + qDegreesToRadians(GetMainLine().angle());
|
|
const QPointF pt = SecondaryLine().p2();
|
|
|
|
return {
|
|
{pt,
|
|
QPointF(pt.x() + arrowLength * cos(rotation + arrowAngle), pt.y() - arrowLength * sin(rotation + arrowAngle)),
|
|
QPointF(pt.x() + arrowLength * cos(rotation - arrowAngle), pt.y() - arrowLength * sin(rotation - arrowAngle)),
|
|
pt}};
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::Shape() const -> GrainlineShape
|
|
{
|
|
const QLineF mainLine = GetMainLine();
|
|
if (mainLine.isNull())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
// main arrow
|
|
QVector<QPointF> arrow1;
|
|
|
|
if (IsArrowDownEnabled())
|
|
{
|
|
arrow1 << ArrowDown();
|
|
}
|
|
else
|
|
{
|
|
arrow1 << mainLine.p1();
|
|
}
|
|
|
|
if (IsArrowUpEnabled())
|
|
{
|
|
arrow1 << ArrowUp();
|
|
}
|
|
else
|
|
{
|
|
arrow1 << mainLine.p2();
|
|
}
|
|
|
|
if (IsFourWays())
|
|
{
|
|
// secondary arrow
|
|
QVector<QPointF> arrow2;
|
|
const QLineF secondaryLine = SecondaryLine();
|
|
|
|
if (IsArrowLeftEnabled())
|
|
{
|
|
arrow2 << ArrowLeft();
|
|
}
|
|
else
|
|
{
|
|
arrow2 << secondaryLine.p1();
|
|
}
|
|
|
|
if (IsArrowRightEnabled())
|
|
{
|
|
arrow2 << ArrowRight();
|
|
}
|
|
else
|
|
{
|
|
arrow2 << secondaryLine.p2();
|
|
}
|
|
|
|
return {arrow1, arrow2};
|
|
}
|
|
|
|
return {arrow1};
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
/**
|
|
* @brief VPieceGrainline::IsContained checks, if all ends of the grainline, starting at pt, are contained in
|
|
* parent widget.
|
|
* @param boundingRect bounding rect of piece
|
|
* @param dX horizontal translation needed to put the arrow inside parent item
|
|
* @param dY vertical translation needed to put the arrow inside parent item
|
|
* @return true, if all ends of the grainline, starting at pt, are contained in the bounding rect of piece and
|
|
* false otherwise.
|
|
*/
|
|
auto VPieceGrainline::IsContained(const QRectF &boundingRect, qreal &dX, qreal &dY) const -> bool
|
|
{
|
|
dX = 0;
|
|
dY = 0;
|
|
|
|
const QLineF mainLine = GetMainLine();
|
|
QVector<QPointF> apt = {mainLine.p1(), mainLine.p2()};
|
|
if (IsFourWays())
|
|
{
|
|
const QLineF secondaryLine = SecondaryLine();
|
|
apt.append(secondaryLine.p1());
|
|
apt.append(secondaryLine.p2());
|
|
}
|
|
|
|
// single point differences
|
|
qreal dPtX;
|
|
qreal dPtY;
|
|
bool bInside = true;
|
|
|
|
for (auto item : apt)
|
|
{
|
|
dPtX = 0;
|
|
dPtY = 0;
|
|
if (boundingRect.contains(item))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (item.x() < boundingRect.left())
|
|
{
|
|
dPtX = boundingRect.left() - item.x();
|
|
}
|
|
else if (item.x() > boundingRect.right())
|
|
{
|
|
dPtX = boundingRect.right() - item.x();
|
|
}
|
|
if (item.y() < boundingRect.top())
|
|
{
|
|
dPtY = boundingRect.top() - item.y();
|
|
}
|
|
else if (item.y() > boundingRect.bottom())
|
|
{
|
|
dPtY = boundingRect.bottom() - item.y();
|
|
}
|
|
|
|
if (fabs(dPtX) > fabs(dX))
|
|
{
|
|
dX = dPtX;
|
|
}
|
|
if (fabs(dPtY) > fabs(dY))
|
|
{
|
|
dY = dPtY;
|
|
}
|
|
|
|
bInside = false;
|
|
}
|
|
return bInside;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::IsPositionValid(const QVector<QPointF> &contourPoints) const -> bool
|
|
{
|
|
QVector<QLineF> grainLine;
|
|
QLineF const mainLine = GetMainLine();
|
|
if (IsFourWays())
|
|
{
|
|
grainLine = {mainLine, SecondaryLine()};
|
|
}
|
|
|
|
grainLine = {mainLine};
|
|
|
|
for (auto line : qAsConst(grainLine))
|
|
{
|
|
QVector<QPointF> const points = VAbstractCurve::CurveIntersectLine(contourPoints, line);
|
|
for (const auto &point : points)
|
|
{
|
|
if (not VFuzzyComparePoints(line.p1(), point) && not VFuzzyComparePoints(line.p2(), point))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
QPainterPath grainLinePath;
|
|
for (auto line : qAsConst(grainLine))
|
|
{
|
|
grainLinePath.addPath(VGObject::PainterPath(QVector<QPointF>{line.p1(), line.p2()}));
|
|
}
|
|
const QPainterPath contourPath = VGObject::PainterPath(contourPoints);
|
|
return contourPath.contains(grainLinePath);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto VPieceGrainline::IsShapeValid() const -> bool
|
|
{
|
|
GrainlineShape const shape = Shape();
|
|
return std::all_of(shape.cbegin(), shape.cend(), [](const auto &subShape) { return not subShape.isEmpty(); });
|
|
}
|
|
|
|
// Friend functions
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto operator<<(QDataStream &dataStream, const VPieceGrainline &grainline) -> QDataStream &
|
|
{
|
|
dataStream << *grainline.d;
|
|
return dataStream;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
auto operator>>(QDataStream &dataStream, VPieceGrainline &grainline) -> QDataStream &
|
|
{
|
|
dataStream >> *grainline.d;
|
|
return dataStream;
|
|
}
|