/************************************************************************ ** ** @file vfoldline.cpp ** @author Roman Telezhynskyi ** @date 20 12, 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 ** 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 . ** *************************************************************************/ #include "vfoldline.h" #include "../ifc/exception/vexception.h" #include "../vformat/vsinglelineoutlinechar.h" #include "../vgeometry/vgeometrydef.h" #include "../vmisc/compatibility.h" #include "../vmisc/def.h" #include "../vmisc/svgfont/svgdef.h" #include "../vmisc/svgfont/vsvgfont.h" #include "../vmisc/svgfont/vsvgfontdatabase.h" #include "../vmisc/vabstractapplication.h" #include "../vmisc/vcommonsettings.h" #include "../vmisc/vtranslator.h" #include "../vwidgets/vpiecegrainline.h" #include #include #include #include #include #include namespace { constexpr qreal defLabelMargin = CmToPixel(0.5); constexpr qreal minArrowsHeight = CmToPixel(0.5); constexpr qreal defArrowsHeight = CmToPixel(1.); } // namespace //--------------------------------------------------------------------------------------------------------------------- VFoldLine::VFoldLine(const QLineF &line, FoldLineType type) : m_foldLine(line), m_type(type) { } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetHeight(qreal newHeight) { m_height = qAbs(newHeight); } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetWidth(qreal newWidth) { m_width = qAbs(newWidth); } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetCenterPosition(qreal newCenter) { m_center = qBound(0., qAbs(newCenter), 1.); } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetLabel(const QString &newLabel) { m_label = newLabel; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetLabelSvgFontSize(unsigned int newFontSize) { m_svgFontSize = qMax(newFontSize, static_cast(VCommonSettings::MinPieceLabelFontPointSize())); } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetLabelFontItalic(bool newItalic) { m_italic = newItalic; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetLabelFontBold(bool newBold) { m_bold = newBold; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetLabelAlignment(int alignment) { m_alignment = alignment; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetMatrix(const QTransform &matrix) { m_matrix = matrix; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetVerticallyFlipped(bool value) { m_verticallyFlipped = value; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetHorizontallyFlipped(bool value) { m_horizontallyFlipped = value; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetXScale(qreal xs) { m_xScale = xs; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetYScale(qreal ys) { m_yScale = ys; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetOutlineFont(const QFont &font) { m_outlineFont = font; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::SetSvgFont(const QString &font) { m_svgFont = font; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::FoldLinePath() const -> QVector { if (m_foldLine.isNull()) { return {}; } switch (m_type) { case FoldLineType::TwoArrows: return FoldLineTwoArrowsPath(); case FoldLineType::TwoArrowsTextAbove: return FoldLineTwoArrowsTextAbovePath(); case FoldLineType::TwoArrowsTextUnder: return FoldLineTwoArrowsTextUnderPath(); case FoldLineType::Text: return FoldLineTextPath(); case FoldLineType::ThreeDots: return FoldLineThreeDotsPath(); case FoldLineType::ThreeX: return FoldLineThreeXPath(); case FoldLineType::LAST_ONE_DO_NOT_USE: Q_UNREACHABLE(); break; case FoldLineType::None: default: return {}; }; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::FoldLineMarkPoints() const -> QVector> { if (m_foldLine.isNull()) { return {}; } switch (m_type) { case FoldLineType::TwoArrows: { ArrowsTextPosData const data = TwoArrowsData(); return TwoArrowsPoints(data.arrowsWidth, data.arrowsHeight); } case FoldLineType::TwoArrowsTextAbove: { ArrowsTextPosData const data = TwoArrowsTextAboveData(); return TwoArrowsPoints(data.arrowsWidth, data.arrowsHeight); } case FoldLineType::TwoArrowsTextUnder: { ArrowsTextPosData const data = TwoArrowsTextUnderData(); return TwoArrowsPoints(data.arrowsWidth, data.arrowsHeight); } case FoldLineType::ThreeDots: return ThreeDotsPoints(); case FoldLineType::ThreeX: return ThreeXPoints(); case FoldLineType::LAST_ONE_DO_NOT_USE: Q_UNREACHABLE(); break; case FoldLineType::Text: case FoldLineType::None: default: return {}; }; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::LabelPosition(bool &ok) const -> FoldLabelPosData { FoldLabelPosData posData; TextPosData data; if (m_type == FoldLineType::Text) { data = TextData(); } else if (m_type == FoldLineType::TwoArrowsTextAbove) { data = TwoArrowsTextAboveData(); } else { data = TwoArrowsTextUnderData(); } if (data.base.isNull()) { ok = false; return {}; } qreal const height = data.labelHeight + (qFuzzyIsNull(m_height) ? defLabelMargin : m_height); qreal const margin = qMax(0., height - data.labelHeight); QPointF const center = TrueCenter(data.base, data.labelWidth); QLineF baseLine(center, data.base.p1()); baseLine.setLength(data.labelWidth / 2); Swap(baseLine); baseLine.setLength(baseLine.length() * 2); baseLine = SimpleParallelLine(baseLine.p1(), baseLine.p2(), -(margin + data.labelHeight)); posData.font = LabelOutlineFont(); QFontMetrics const fm(posData.font); posData.label = fm.elidedText(FoldLineLabel(), Qt::ElideRight, qFloor(data.labelWidth)); if (m_alignment & Qt::AlignHCenter) // NOLINT(readability-implicit-bool-conversion) { qreal const shift = (data.labelWidth - fm.horizontalAdvance(posData.label)) / 2; baseLine.setLength(baseLine.length() - shift); } else if (m_alignment & Qt::AlignRight) // NOLINT(readability-implicit-bool-conversion) { qreal const shift = data.labelWidth - fm.horizontalAdvance(posData.label); baseLine.setLength(baseLine.length() - shift); } posData.pos = baseLine.p2(); posData.angle = QLineF(center, data.base.p1()).angle(); ok = true; return posData; } //--------------------------------------------------------------------------------------------------------------------- void VFoldLine::UpdateFoldLineLabel(QGraphicsSimpleTextItem *item) const { if (item == nullptr) { return; } if (m_type == FoldLineType::TwoArrows || m_type == FoldLineType::ThreeDots || m_type == FoldLineType::ThreeX || m_type == FoldLineType::None) { item->setVisible(false); return; } item->setVisible(true); bool ok = false; FoldLabelPosData const labelPos = LabelPosition(ok); if (not ok) { item->setVisible(false); return; } item->setFont(labelPos.font); item->setText(labelPos.label); QRectF const rect = item->boundingRect(); QTransform matrix; if (m_verticallyFlipped && !m_horizontallyFlipped) { QFontMetrics const fm(labelPos.font); matrix.scale(-1, 1); matrix.translate(-rect.width(), -rect.height() - fm.ascent()); matrix.translate(-labelPos.pos.x(), labelPos.pos.y()); matrix.rotate(labelPos.angle); matrix.translate(labelPos.pos.x(), -labelPos.pos.y()); matrix.translate(-labelPos.pos.x(), labelPos.pos.y()); } else if (!m_verticallyFlipped && m_horizontallyFlipped) { matrix.scale(1, -1); matrix.translate(0, -rect.height()); matrix.translate(labelPos.pos.x(), -labelPos.pos.y()); matrix.rotate(labelPos.angle + 180); matrix.translate(-labelPos.pos.x(), labelPos.pos.y()); matrix.translate(-rect.width(), -rect.height()); matrix.translate(labelPos.pos.x(), -labelPos.pos.y()); } else { matrix.translate(labelPos.pos.x(), labelPos.pos.y()); matrix.rotate(-labelPos.angle); matrix.translate(-labelPos.pos.x(), -labelPos.pos.y()); matrix.translate(labelPos.pos.x(), labelPos.pos.y()); } matrix *= m_matrix; item->setTransform(matrix); } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::ThreeDotsRadius() const -> qreal { return ThreeDotsData().radius * qMin(m_xScale, m_yScale); } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::DefaultFoldLineLabel() -> QString { QSharedPointer const phTr = VAbstractApplication::VApp()->GetPlaceholderTranslator(); return phTr->translate("FoldLine", "Cut On Fold"); } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::FoldLineTwoArrowsPath() const -> QVector { if (m_foldLine.isNull()) { return {}; } ArrowsTextPosData const data = TwoArrowsData(); return {TwoArrowsPath(data.arrowsWidth, data.arrowsHeight)}; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::FoldLineTwoArrowsTextAbovePath() const -> QVector { if (m_foldLine.isNull()) { return {}; } ArrowsTextPosData const data = TwoArrowsTextAboveData(); QVector shape; shape.append(TwoArrowsPath(data.arrowsWidth, data.arrowsHeight)); VCommonSettings *settings = VAbstractApplication::VApp()->Settings(); if (settings->GetSingleLineFonts()) { shape.append(SVGFontLabel(data.base, data.labelWidth, data.labelHeight)); } else { shape.append(OutlineFontLabel(data.base, data.labelWidth, data.labelHeight)); } return shape; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::FoldLineTwoArrowsTextUnderPath() const -> QVector { if (m_foldLine.isNull()) { return {}; } ArrowsTextPosData const data = TwoArrowsTextUnderData(); QVector shape; shape.append(TwoArrowsPath(data.arrowsWidth, data.arrowsHeight)); VCommonSettings *settings = VAbstractApplication::VApp()->Settings(); if (settings->GetSingleLineFonts()) { shape.append(SVGFontLabel(data.base, data.labelWidth, data.labelHeight)); } else { shape.append(OutlineFontLabel(data.base, data.labelWidth, data.labelHeight)); } return shape; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::FoldLineTextPath() const -> QVector { if (m_foldLine.isNull()) { return {}; } TextPosData const data = TextData(); VCommonSettings *settings = VAbstractApplication::VApp()->Settings(); if (settings->GetSingleLineFonts()) { return {SVGFontLabel(data.base, data.labelWidth, data.labelHeight)}; } return {OutlineFontLabel(data.base, data.labelWidth, data.labelHeight)}; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::FoldLineThreeDotsPath() const -> QVector { QVector> const shape = ThreeDotsPoints(); if (shape.isEmpty() || shape.constFirst().size() != 3) { return {}; } ThreeDotsPosData const data = ThreeDotsData(); QPainterPath dots; auto DrawCircle = [&dots, data](const QPointF ¢er) { qreal const diameter = 2 * data.radius; qreal const x = center.x() - data.radius; qreal const y = center.y() - data.radius; dots.addEllipse(x, y, diameter, diameter); }; const QVector ¢ers = shape.constFirst(); DrawCircle(centers.at(0)); DrawCircle(centers.at(1)); DrawCircle(centers.at(2)); return {dots}; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::FoldLineThreeXPath() const -> QVector { QVector> const fullShape = ThreeXPoints(); if (fullShape.isEmpty()) { return {}; } const QVector &shape = fullShape.constFirst(); QPainterPath x3; for (int i = 0; i < shape.size() - 1; i += 2) { x3.moveTo(shape.at(i)); x3.lineTo(shape.at(i + 1)); } return {x3}; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::TrueCenter(const QLineF &base, qreal width) const -> QPointF { if (base.isNull()) { return {}; } width = qMin(width, base.length()); if (VFuzzyComparePossibleNulls(base.length(), width) || qFuzzyIsNull(width)) { return base.center(); } QLineF seg1 = base; seg1.setLength(width / 2.); qreal const seg1T = seg1.length() / base.length(); if (m_center >= 0 && m_center < seg1T) { return seg1.p2(); } QLineF seg2(base.p2(), base.p1()); seg2.setLength(width / 2.); qreal const seg2T = seg2.length() / base.length(); if (m_center >= (1 - seg2T) && m_center <= 1) { return seg2.p2(); } return base.pointAt(m_center); } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::FoldLineLabel() const -> QString { if (!m_label.isEmpty()) { return m_label; } return DefaultFoldLineLabel(); } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::OutlineFontLabel(const QLineF &base, qreal width, qreal textHeight) const -> QPainterPath { if (base.isNull()) { return {}; } qreal const height = textHeight + (qFuzzyIsNull(m_height) ? defLabelMargin : m_height); qreal const margin = qMax(0., height - textHeight); QPointF const center = TrueCenter(base, width); QLineF baseLine(center, base.p1()); baseLine.setAngle(baseLine.angle() + 90); baseLine.setLength(margin); Swap(baseLine); baseLine.setAngle(baseLine.angle() - 90 - QLineF(base.p2(), base.p1()).angle()); baseLine.setLength(width / 2); QPointF const rotationCenter = baseLine.p1(); Swap(baseLine); baseLine.setLength(width); Swap(baseLine); QFont const font = LabelOutlineFont(); QFontMetrics const fm(font); QString const label = fm.elidedText(FoldLineLabel(), Qt::ElideRight, qFloor(width)); if (m_alignment & Qt::AlignHCenter) // NOLINT(readability-implicit-bool-conversion) { qreal const shift = (width - fm.horizontalAdvance(label)) / 2; baseLine.setLength(baseLine.length() - shift); } else if (m_alignment & Qt::AlignRight) // NOLINT(readability-implicit-bool-conversion) { qreal const shift = width - fm.horizontalAdvance(label); baseLine.setLength(baseLine.length() - shift); } QPainterPath labelPath; VCommonSettings *settings = VAbstractApplication::VApp()->Settings(); if (settings->GetSingleStrokeOutlineFont()) { VSingleLineOutlineChar const corrector(font); if (!corrector.IsPopulated()) { corrector.LoadCorrections(settings->GetPathFontCorrections()); } int w = 0; for (auto c : qAsConst(label)) { labelPath.addPath(corrector.DrawChar(w, static_cast(fm.ascent()), c)); w += fm.horizontalAdvance(c); } QTransform matrix; if (m_verticallyFlipped && !m_horizontallyFlipped) { matrix.scale(-1, 1); matrix.translate(-labelPath.boundingRect().width(), -labelPath.boundingRect().height() - fm.ascent()); matrix.translate(-rotationCenter.x(), rotationCenter.y()); matrix.rotate(QLineF(center, base.p1()).angle()); matrix.translate(rotationCenter.x(), -rotationCenter.y()); matrix.translate(-baseLine.p2().x(), baseLine.p2().y() - textHeight); } else if (!m_verticallyFlipped && m_horizontallyFlipped) { matrix.scale(1, -1); matrix.translate(0, -fm.height()); matrix.translate(rotationCenter.x(), -rotationCenter.y()); matrix.rotate(QLineF(center, base.p1()).angle() + 180); matrix.translate(-rotationCenter.x(), rotationCenter.y()); matrix.translate(0, -labelPath.boundingRect().height() - textHeight * 2); matrix.translate(baseLine.p2().x(), -baseLine.p2().y() + textHeight); } else { matrix.translate(rotationCenter.x(), rotationCenter.y()); matrix.rotate(-QLineF(center, base.p1()).angle()); matrix.translate(-rotationCenter.x(), -rotationCenter.y()); matrix.translate(baseLine.p2().x(), baseLine.p2().y() - textHeight); } matrix *= m_matrix; labelPath = matrix.map(labelPath); } else { Swap(baseLine); baseLine.setAngle(baseLine.angle() + 90); baseLine.setLength(fm.descent() + CmToPixel(0.2)); labelPath.addText(QPointF(), font, label); QTransform matrix; if (m_verticallyFlipped && !m_horizontallyFlipped) { matrix.scale(-1, 1); matrix.translate(-labelPath.boundingRect().width(), -labelPath.boundingRect().height() - fm.ascent()); matrix.translate(-rotationCenter.x(), rotationCenter.y()); matrix.rotate(QLineF(center, base.p1()).angle()); matrix.translate(rotationCenter.x(), -rotationCenter.y()); matrix.translate(-baseLine.p2().x(), baseLine.p2().y()); } else if (!m_verticallyFlipped && m_horizontallyFlipped) { matrix.scale(1, -1); matrix.translate(0, -fm.height()); matrix.translate(rotationCenter.x(), -rotationCenter.y()); matrix.rotate(QLineF(center, base.p1()).angle() + 180); matrix.translate(-rotationCenter.x(), rotationCenter.y()); matrix.translate(0, -labelPath.boundingRect().height() - fm.height() - CmToPixel(0.1)); matrix.translate(baseLine.p2().x(), -baseLine.p2().y()); } else { matrix.translate(rotationCenter.x(), rotationCenter.y()); matrix.rotate(-QLineF(center, base.p1()).angle()); matrix.translate(-rotationCenter.x(), -rotationCenter.y()); matrix.translate(baseLine.p2().x(), baseLine.p2().y()); } matrix *= m_matrix; labelPath = matrix.map(labelPath); } return labelPath; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::SVGFontLabel(const QLineF &base, qreal width, qreal textHeight) const -> QPainterPath { if (base.isNull()) { return {}; } VSvgFontEngine engine; if (!LabelSVGFontEngine(engine)) { return OutlineFontLabel(base, width, textHeight); } qreal const height = textHeight + (qFuzzyIsNull(m_height) ? defLabelMargin : m_height); qreal const margin = qMax(0., height - textHeight); QPointF const center = TrueCenter(base, width); QLineF baseLine(center, base.p1()); baseLine.setAngle(baseLine.angle() + 90); baseLine.setLength(margin); Swap(baseLine); baseLine.setAngle(baseLine.angle() - 90 - QLineF(base.p2(), base.p1()).angle()); baseLine.setLength(width / 2); QPointF const rotationCenter = baseLine.p1(); Swap(baseLine); baseLine.setLength(width); Swap(baseLine); QString const label = engine.ElidedText(FoldLineLabel(), SVGTextElideMode::ElideRight, qFloor(width)); if (m_alignment & Qt::AlignHCenter) // NOLINT(readability-implicit-bool-conversion) { qreal const shift = (width - engine.TextWidth(label)) / 2; baseLine.setLength(baseLine.length() - shift); } else if (m_alignment & Qt::AlignRight) // NOLINT(readability-implicit-bool-conversion) { qreal const shift = width - engine.TextWidth(label); baseLine.setLength(baseLine.length() - shift); } QPainterPath labelPath = engine.DrawPath(QPointF(), label); QTransform matrix; if (m_verticallyFlipped && !m_horizontallyFlipped) { matrix.scale(-1, 1); matrix.translate(-labelPath.boundingRect().width(), -labelPath.boundingRect().height()); matrix.translate(-rotationCenter.x(), rotationCenter.y()); matrix.rotate(QLineF(center, base.p1()).angle()); matrix.translate(rotationCenter.x(), -rotationCenter.y()); matrix.translate(-baseLine.p2().x(), baseLine.p2().y() - textHeight); } else if (!m_verticallyFlipped && m_horizontallyFlipped) { matrix.scale(1, -1); matrix.translate(0, -labelPath.boundingRect().height()); matrix.translate(rotationCenter.x(), -rotationCenter.y()); matrix.rotate(QLineF(center, base.p1()).angle() + 180); matrix.translate(-rotationCenter.x(), rotationCenter.y()); matrix.translate(0, -labelPath.boundingRect().height() - textHeight * 2); matrix.translate(baseLine.p2().x(), -baseLine.p2().y() + textHeight); } else { matrix.translate(rotationCenter.x(), rotationCenter.y()); matrix.rotate(-QLineF(center, base.p1()).angle()); matrix.translate(-rotationCenter.x(), -rotationCenter.y()); matrix.translate(baseLine.p2().x(), baseLine.p2().y() - textHeight); } matrix *= m_matrix; labelPath = matrix.map(labelPath); return labelPath; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::LabelTextHeight() const -> qreal { VCommonSettings *settings = VAbstractApplication::VApp()->Settings(); if (settings->GetSingleLineFonts()) { VSvgFontEngine engine; if (LabelSVGFontEngine(engine)) { return engine.FontHeight(); } } return QFontMetrics(LabelOutlineFont()).height(); } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::LabelSVGFontEngine(VSvgFontEngine &engine) const -> bool { VSvgFontDatabase *db = VAbstractApplication::VApp()->SVGFontDatabase(); VSvgFontEngine const svgEngine = db->FontEngine(m_svgFont, SVGFontStyle::Normal, SVGFontWeight::Normal, static_cast(m_svgFontSize)); VSvgFont svgFont = svgEngine.Font(); if (!svgFont.IsValid()) { QString const errorMsg = tr("Invalid SVG font '%1'. Fallback to outline font.").arg(svgFont.Name()); VAbstractApplication::VApp()->IsPedantic() ? throw VException(errorMsg) : qWarning() << VAbstractApplication::warningMessageSignature + errorMsg; return false; } svgFont.SetBold(m_bold); svgFont.SetItalic(m_italic); engine = db->FontEngine(svgFont); return true; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::LabelOutlineFont() const -> QFont { QFont font = m_outlineFont; font.setItalic(m_italic); font.setBold(m_bold); return font; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::TwoArrowsPath(qreal width, qreal height) const -> QPainterPath { QVector> const shape = TwoArrowsPoints(width, height); if (shape.size() != 3) { return {}; } QPainterPath path; path.addPolygon(QPolygonF(shape.constFirst())); if (shape.at(1).size() == 2) { path.moveTo(shape.at(1).constFirst()); path.lineTo(shape.at(1).constLast()); } path.addPolygon(QPolygonF(shape.constLast())); path.setFillRule(Qt::OddEvenFill); return path; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::TwoArrowsPoints(qreal width, qreal height) const -> QVector> { if (m_foldLine.isNull()) { return {}; } QPointF const center = TrueCenter(m_foldLine, width); QLineF base1(center, m_foldLine.p1()); base1.setLength(width / 2.); Swap(base1); base1.setAngle(base1.angle() - 90); base1.setLength(height); QLineF base2(center, m_foldLine.p2()); base2.setLength(width / 2.); Swap(base2); base2.setAngle(base2.angle() + 90); base2.setLength(height); VPieceGrainline const arrow1(base1, GrainlineArrowDirection::oneWayDown); VPieceGrainline const arrow2(base2, GrainlineArrowDirection::oneWayDown); QVector arrow1Shape = arrow1.Shape().constFirst(); QVector arrow2Shape = arrow2.Shape().constFirst(); QLineF line(arrow1Shape.constLast(), arrow2Shape.constLast()); std::reverse(arrow2Shape.begin(), arrow2Shape.end()); std::transform(arrow1Shape.begin(), arrow1Shape.end(), arrow1Shape.begin(), [this](const QPointF &point) { return m_matrix.map(point); }); line = m_matrix.map(line); std::transform(arrow2Shape.begin(), arrow2Shape.end(), arrow2Shape.begin(), [this](const QPointF &point) { return m_matrix.map(point); }); return {arrow1Shape, {line.p1(), line.p2()}, arrow2Shape}; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::ThreeDotsPoints() const -> QVector> { if (m_foldLine.isNull()) { return {}; } ThreeDotsPosData const data = ThreeDotsData(); QPointF const center = TrueCenter(m_foldLine, data.width); QLineF r1Line(center, m_foldLine.p1()); r1Line.setLength(data.radius * 2); Swap(r1Line); r1Line.setAngle(r1Line.angle() - 90); r1Line.setLength(data.radius + data.margin); QLineF r2Line(center, m_foldLine.p2()); r2Line.setLength(data.radius * 2); Swap(r2Line); r2Line.setAngle(r2Line.angle() + 90); r2Line.setLength(data.radius + data.margin); QLineF r3Line(center, m_foldLine.p1()); r3Line.setAngle(r3Line.angle() + 90); r3Line.setLength(data.radius * 4 + data.margin); QPointF const c1 = m_matrix.map(r1Line.p2()); QPointF const c2 = m_matrix.map(r2Line.p2()); QPointF const c3 = m_matrix.map(r3Line.p2()); return {{c1, c2, c3}}; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::ThreeXPoints() const -> QVector> { qreal const width = qFuzzyIsNull(m_width) ? m_foldLine.length() * 0.9 : qMin(m_width, m_foldLine.length()); constexpr qreal defXHeight = CmToPixel(1.5); constexpr qreal defXWidth = CmToPixel(1.1); qreal xHeight = defXHeight; qreal xWidth = defXWidth; if (defXHeight * 3 >= width * 0.8) { xHeight = width * 0.8 / 3.; xWidth = defXWidth * (xHeight / defXHeight); } qreal const height = defXWidth + (qFuzzyIsNull(m_height) ? CmToPixel(0.5) : m_height); qreal const margin = qMax(0., height - defXWidth); QPointF const center = TrueCenter(m_foldLine, width); QLineF x1Line(center, m_foldLine.p1()); x1Line.setLength(width / 2); Swap(x1Line); x1Line.setLength(xHeight / 2); Swap(x1Line); x1Line.setAngle(x1Line.angle() + 90); x1Line.setLength(xWidth / 2 + margin); Swap(x1Line); x1Line.setAngle(x1Line.angle() + 90); QLineF x2Line(center, m_foldLine.p1()); x2Line.setAngle(x2Line.angle() + 90); x2Line.setLength(xWidth / 2 + margin); Swap(x2Line); x2Line.setAngle(x2Line.angle() + 90); QLineF x3Line(center, m_foldLine.p2()); x3Line.setLength(width / 2); Swap(x3Line); x3Line.setLength(xHeight / 2); Swap(x3Line); x3Line.setAngle(x3Line.angle() - 90); x3Line.setLength(xWidth / 2 + margin); Swap(x3Line); x3Line.setAngle(x3Line.angle() - 90); QVector shape; shape.reserve(12); auto XShape = [&shape, xHeight, xWidth](const QLineF &baseLine) { qreal const height = xHeight / 2; qreal const width = xWidth / 2; qreal const c = qSqrt(height * height + width * width); qreal const angle = qRadiansToDegrees(qAcos(height / c)); QLineF line = baseLine; line.setLength(c); line.setAngle(line.angle() + angle); shape.append(line.p2()); line.setAngle(line.angle() + 180); shape.append(line.p2()); line = baseLine; line.setLength(c); line.setAngle(line.angle() + 180 - angle); shape.append(line.p2()); line.setAngle(line.angle() + 180); shape.append(line.p2()); }; XShape(x1Line); XShape(x2Line); XShape(x3Line); std::transform(shape.begin(), shape.end(), shape.begin(), [this](const QPointF &point) { return m_matrix.map(point); }); if (m_verticallyFlipped || m_horizontallyFlipped) { std::reverse(shape.begin(), shape.end()); } return {shape}; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::TwoArrowsData() const -> ArrowsTextPosData { ArrowsTextPosData data; data.arrowsWidth = qFuzzyIsNull(m_width) ? m_foldLine.length() * 0.9 : qMin(m_width, m_foldLine.length()); data.arrowsHeight = qFuzzyIsNull(m_height) ? defArrowsHeight : m_height; return data; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::TwoArrowsTextAboveData() const -> ArrowsTextPosData { const qreal defLabelHeight = LabelTextHeight(); ArrowsTextPosData data; data.arrowsWidth = qFuzzyIsNull(m_width) ? m_foldLine.length() * 0.9 : qMin(m_width, m_foldLine.length()); data.labelWidth = data.arrowsWidth; data.arrowsHeight = qFuzzyIsNull(m_height) ? defArrowsHeight : qMax(m_height - (defLabelHeight + CmToPixel(0.2)), minArrowsHeight); data.labelHeight = qFuzzyIsNull(m_height) ? defLabelHeight : (defLabelHeight + CmToPixel(0.2)) - m_height; data.base = SimpleParallelLine(m_foldLine.p1(), m_foldLine.p2(), -data.arrowsHeight); return data; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::TwoArrowsTextUnderData() const -> ArrowsTextPosData { const qreal defLabelHeight = LabelTextHeight(); ArrowsTextPosData data; data.arrowsWidth = qFuzzyIsNull(m_width) ? m_foldLine.length() * 0.9 : qMin(m_width, m_foldLine.length()); data.labelWidth = data.arrowsWidth * 0.9; data.labelHeight = qFuzzyIsNull(m_height) ? defLabelHeight : (defLabelHeight + CmToPixel(0.2)) - m_height; data.arrowsHeight = defLabelHeight + (qFuzzyIsNull(m_height) ? defLabelMargin : m_height) + CmToPixel(0.2); data.arrowsHeight = qFuzzyIsNull(m_height) ? qMax(defLabelHeight, defArrowsHeight) : data.arrowsHeight; data.base = m_foldLine; return data; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::TextData() const -> TextPosData { const qreal defLabelHeight = LabelTextHeight(); TextPosData data; data.labelWidth = qFuzzyIsNull(m_width) ? m_foldLine.length() * 0.9 : qMin(m_width, m_foldLine.length()); data.labelHeight = qFuzzyIsNull(m_height) ? defLabelHeight : (defLabelHeight + CmToPixel(0.2)) - m_height; data.base = m_foldLine; return data; } //--------------------------------------------------------------------------------------------------------------------- auto VFoldLine::ThreeDotsData() const -> ThreeDotsPosData { ThreeDotsPosData data; data.width = qFuzzyIsNull(m_width) ? CmToPixel(2.) : qMin(m_width, m_foldLine.length()); data.defHeight = data.width * qSin(qDegreesToRadians(60.0)); data.height = data.defHeight + (qFuzzyIsNull(m_height) ? CmToPixel(0.5) : m_height); data.radius = data.width / 3 / 2; data.margin = qMax(0., data.height - data.defHeight); return data; }