From 7202cfa0d3bae430f8fcb8508ed389583a14539b Mon Sep 17 00:00:00 2001 From: Armin Le Grand Date: Wed, 26 Jul 2017 16:34:52 +0200 Subject: borderline: Unified primitive creation Overhauled the edge extension calculations to better use all existing cuts, for cases with single and double lines. Also a preparation for more complex edge cuts. Changed BorderLine paint in Writer, it was setting the svx::frame::RefMode attribute at the Style *and* was manually moving the geometry which lead to a double applying of the offsets Change-Id: I446d04632050272849f43accb8dcbde574d10cf4 --- svx/source/dialog/framelink.cxx | 371 +++++++++++++++++++------------------ sw/source/core/layout/paintfrm.cxx | 45 +++-- 2 files changed, 217 insertions(+), 199 deletions(-) diff --git a/svx/source/dialog/framelink.cxx b/svx/source/dialog/framelink.cxx index e5b6abb8f5f8..c746e881679c 100644 --- a/svx/source/dialog/framelink.cxx +++ b/svx/source/dialog/framelink.cxx @@ -345,7 +345,53 @@ double lcl_GetExtent( return nCut; } -void getOffsetsFromStyle(const Style& rStyle, std::vector< double >& offsets) +struct OffsetPair +{ + double mfLeft; + double mfRight; + + OffsetPair(double a, double b) : mfLeft(a), mfRight(b) {} +}; + +struct OffsetCutSet +{ + double mfLeftLeft; + double mfRightLeft; + double mfLeftRight; + double mfRightRight; +}; + +const OffsetCutSet* getMinMaxCutSet(bool bMin, const std::vector< OffsetCutSet >& myCutSets) +{ + if (myCutSets.empty()) + { + return nullptr; + } + + if (1 == myCutSets.size()) + { + return &myCutSets[0]; + } + + const OffsetCutSet* pRetval = &myCutSets[0]; + double fRetval(pRetval->mfLeftLeft + pRetval->mfLeftRight + pRetval->mfRightLeft + pRetval->mfRightRight); + + for (size_t a(1); a < myCutSets.size(); a++) + { + const OffsetCutSet* pCandidate = &myCutSets[a]; + const double fCandidate(pCandidate->mfLeftLeft + pCandidate->mfLeftRight + pCandidate->mfRightLeft + pCandidate->mfRightRight); + + if ((bMin && fCandidate < fRetval) || (!bMin && fCandidate > fRetval)) + { + pRetval = pCandidate; + fRetval = fCandidate; + } + } + + return pRetval; +} + +void getOffsetPairsFromStyle(const Style& rStyle, std::vector< OffsetPair >& offsets) { if (rStyle.Prim()) { @@ -357,25 +403,21 @@ void getOffsetsFromStyle(const Style& rStyle, std::vector< double >& offsets) case RefMode::Centered: { const double fHalfFullWidth(rStyle.GetWidth() * 0.5); - offsets.push_back(-fHalfFullWidth); - offsets.push_back(rStyle.Prim() - fHalfFullWidth); - offsets.push_back((rStyle.Prim() + rStyle.Dist()) - fHalfFullWidth); - offsets.push_back(fHalfFullWidth); + offsets.push_back(OffsetPair(-fHalfFullWidth, rStyle.Prim() - fHalfFullWidth)); + offsets.push_back(OffsetPair((rStyle.Prim() + rStyle.Dist()) - fHalfFullWidth, fHalfFullWidth)); break; } case RefMode::Begin: - offsets.push_back(0.0); - offsets.push_back(rStyle.Prim()); - offsets.push_back(rStyle.Prim() + rStyle.Dist()); - offsets.push_back(rStyle.GetWidth()); + { + offsets.push_back(OffsetPair(0.0, rStyle.Prim())); + offsets.push_back(OffsetPair(rStyle.Prim() + rStyle.Dist(), rStyle.GetWidth())); break; + } default: // case RefMode::End: { const double fFullWidth(rStyle.GetWidth()); - offsets.push_back(-fFullWidth); - offsets.push_back(rStyle.Prim() - fFullWidth); - offsets.push_back((rStyle.Prim() + rStyle.Dist()) - fFullWidth); - offsets.push_back(0.0); + offsets.push_back(OffsetPair(-fFullWidth, rStyle.Prim() - fFullWidth)); + offsets.push_back(OffsetPair((rStyle.Prim() + rStyle.Dist()) - fFullWidth, 0.0)); break; } } @@ -386,110 +428,132 @@ void getOffsetsFromStyle(const Style& rStyle, std::vector< double >& offsets) switch (rStyle.GetRefMode()) { case RefMode::Centered: - offsets.push_back(rStyle.Prim() * -0.5); - offsets.push_back(rStyle.Prim() * 0.5); + offsets.push_back(OffsetPair(rStyle.Prim() * -0.5, rStyle.Prim() * 0.5)); break; case RefMode::Begin: - offsets.push_back(0.0); - offsets.push_back(rStyle.Prim()); + offsets.push_back(OffsetPair(0.0, rStyle.Prim())); break; default: // case RefMode::End: - offsets.push_back(-rStyle.Prim()); - offsets.push_back(0.0); + offsets.push_back(OffsetPair(-rStyle.Prim(), 0.0)); break; } } } } -void compareToStyle( +void createCutsWithStyle( const basegfx::B2DPoint& rOrigin, const basegfx::B2DVector& rOtherVector, const basegfx::B2DVector& rOtherUnifiedPerpendicular, - const std::vector< double >& rOtherOffsets, + const OffsetPair& rOtherOffsets, const Style& rStyle, const basegfx::B2DVector& rMyVector, - std::vector< std::vector< double >>& rOtherCuts) + std::vector< OffsetCutSet>& rOtherCuts) { if (rStyle.Prim()) { - std::vector< double > myOffsets; + // get values dependent on source vector + const basegfx::B2DVector aMyUnifiedPerpendicular(basegfx::getNormalizedPerpendicular(rMyVector)); + const basegfx::B2DPoint aOtherPosLeft(rOrigin + (rOtherUnifiedPerpendicular * rOtherOffsets.mfLeft)); + const basegfx::B2DPoint aOtherPosRight(rOrigin + (rOtherUnifiedPerpendicular * rOtherOffsets.mfRight)); + std::vector< OffsetPair > myOffsets; - // get offsets from outer to inner (two or four, depending on style) - getOffsetsFromStyle(rStyle, myOffsets); + // get offsets from outer to inner from target style (one or two) + getOffsetPairsFromStyle(rStyle, myOffsets); - if (!myOffsets.empty()) + for (const auto& myOffset : myOffsets) { - const basegfx::B2DVector aMyUnifiedPerpendicular(basegfx::getNormalizedPerpendicular(rMyVector)); - - for (size_t a(0); a < rOtherOffsets.size(); a++) - { - const basegfx::B2DPoint aOtherPos(rOrigin + (rOtherUnifiedPerpendicular * rOtherOffsets[a])); - - for (size_t b(0); b < myOffsets.size(); b++) - { - const basegfx::B2DPoint aMyPos(rOrigin + (aMyUnifiedPerpendicular * myOffsets[b])); - double fCut(0.0); - basegfx::tools::findCut( - aOtherPos, - rOtherVector, - aMyPos, - rMyVector, - CutFlagValue::LINE, - &fCut); - - rOtherCuts[a].push_back(fCut); - } - } + // get values for new vectors and create all four cuts + const basegfx::B2DPoint aMyPosLeft(rOrigin + (aMyUnifiedPerpendicular * myOffset.mfLeft)); + const basegfx::B2DPoint aMyPosRight(rOrigin + (aMyUnifiedPerpendicular * myOffset.mfRight)); + OffsetCutSet aNewCuts; + + basegfx::tools::findCut( + aOtherPosLeft, + rOtherVector, + aMyPosLeft, + rMyVector, + CutFlagValue::LINE, + &aNewCuts.mfLeftLeft); + + basegfx::tools::findCut( + aOtherPosLeft, + rOtherVector, + aMyPosRight, + rMyVector, + CutFlagValue::LINE, + &aNewCuts.mfLeftRight); + + basegfx::tools::findCut( + aOtherPosRight, + rOtherVector, + aMyPosLeft, + rMyVector, + CutFlagValue::LINE, + &aNewCuts.mfRightLeft); + + basegfx::tools::findCut( + aOtherPosRight, + rOtherVector, + aMyPosRight, + rMyVector, + CutFlagValue::LINE, + &aNewCuts.mfRightRight); + + rOtherCuts.push_back(aNewCuts); } } } -double getMinMaxCut(bool bMin, const std::vector< double >& rVector) +double getSimpleExtendedLineValues( + const basegfx::B2DPoint& rOrigin, + const basegfx::B2DVector& rX, + const basegfx::B2DVector& rY, + const basegfx::B2DVector& rPerpendX, + const OffsetPair& myOffset, + const Style& rFirst, + const Style& rSecond, + bool bEdgeStart, + double fLength) { - if (rVector.empty()) - { - return 0.0; - } - - if (1 == rVector.size()) - { - return rVector[0]; - } - - double fRetval(rVector[0]); + std::vector< OffsetCutSet > myCutSets; + createCutsWithStyle(rOrigin, rX, rPerpendX, myOffset, rFirst, rY, myCutSets); + createCutsWithStyle(rOrigin, rX, rPerpendX, myOffset, rSecond, rY, myCutSets); + const OffsetCutSet* pResult = getMinMaxCutSet(bEdgeStart, myCutSets); - for (size_t a(1); a < rVector.size(); a++) + if (pResult) { - fRetval = bMin ? std::min(fRetval, rVector[a]) : std::max(fRetval, rVector[a]); + return (pResult->mfLeftRight + pResult->mfRightRight) * 0.5 * (bEdgeStart ? -fLength : fLength); } - return fRetval; + return 0.0; } -std::vector< double > getMinMaxCuts(bool bMin, const std::vector< std::vector< double >>& rCuts) +double getComplexExtendedLineValues( + const basegfx::B2DPoint& rOrigin, + const basegfx::B2DVector& rX, + const basegfx::B2DVector& rY, + const basegfx::B2DVector& rPerpendX, + const OffsetPair& myOffset, + const Style& rFirst, + const Style& rSecond, + bool bEdgeStart, + double fLength) { - std::vector< double > aRetval(rCuts.size()); + std::vector< OffsetCutSet > myCutSets; + createCutsWithStyle(rOrigin, rX, rPerpendX, myOffset, rFirst, rY, myCutSets); + const OffsetCutSet* pResult = getMinMaxCutSet(!bEdgeStart, myCutSets); - for (size_t a(0); a < rCuts.size(); a++) + if (!pResult) { - aRetval[a] = getMinMaxCut(bMin, rCuts[a]); + createCutsWithStyle(rOrigin, rX, rPerpendX, myOffset, rSecond, rY, myCutSets); + pResult = getMinMaxCutSet(bEdgeStart, myCutSets); } - return aRetval; -} - -bool areCutsEmpty(std::vector< std::vector< double >>& rCuts) -{ - for (const auto& rVec : rCuts) + if (pResult) { - if (!rVec.empty()) - { - return false; - } + return (pResult->mfLeftRight + pResult->mfRightRight) * 0.5 * (bEdgeStart ? -fLength : fLength); } - - return true; } void CreateBorderPrimitives( @@ -512,12 +576,6 @@ void CreateBorderPrimitives( { if (rBorder.Prim()) { - double mfExtendLeftStart(0.0); - double mfExtendLeftEnd(0.0); - double mfExtendRightStart(0.0); - double mfExtendRightEnd(0.0); - std::vector< double > myOffsets; - getOffsetsFromStyle(rBorder, myOffsets); const basegfx::B2DVector aPerpendX(basegfx::getNormalizedPerpendicular(rX)); const double fLength(rX.getLength()); @@ -526,7 +584,6 @@ void CreateBorderPrimitives( if (RefMode::Centered != rBorder.GetRefMode()) { - const basegfx::B2DVector aPerpendX(basegfx::getNormalizedPerpendicular(rX)); const double fHalfWidth(rBorder.GetWidth() * 0.5); if (RefMode::Begin == rBorder.GetRefMode()) @@ -541,23 +598,27 @@ void CreateBorderPrimitives( } } - // create start/end for RefMode::Centered + // create start/end (use RefMode) const basegfx::B2DPoint aStart(rOrigin + aRefModeOffset); const basegfx::B2DPoint aEnd(aStart + rX); - if (2 == myOffsets.size()) + // get offsets for my style (one or two) + std::vector< OffsetPair > myOffsets; + getOffsetPairsFromStyle(rBorder, myOffsets); + + if (1 == myOffsets.size()) { - std::vector< std::vector< double >> myCutsS(myOffsets.size()); - compareToStyle(rOrigin, rX, aPerpendX, myOffsets, rLFromT, rY, myCutsS); - compareToStyle(rOrigin, rX, aPerpendX, myOffsets, rLFromB, rY, myCutsS); - std::vector< double > nMinCutsS(getMinMaxCuts(true, myCutsS)); - mfExtendLeftStart = ((nMinCutsS[0] + nMinCutsS[1]) * 0.5) * -1.0 * fLength; - - std::vector< std::vector< double >> myCutsE(myOffsets.size()); - compareToStyle(rOrigin, rX, aPerpendX, myOffsets, rRFromT, rY, myCutsE); - compareToStyle(rOrigin, rX, aPerpendX, myOffsets, rRFromB, rY, myCutsE); - std::vector< double > nMinCutsE(getMinMaxCuts(false, myCutsE)); - mfExtendLeftEnd = ((nMinCutsE[0] + nMinCutsE[1]) * 0.5) * fLength; + // we are a single edge, calculate cuts with edges coming from above/below + // to detect the line start/end extensions + const OffsetPair& myOffset(myOffsets[0]); + double mfExtendStart(0.0); + double mfExtendEnd(0.0); + + // for start: get cuts with all left target styles and use the minimum + mfExtendStart = getSimpleExtendedLineValues(rOrigin, rX, rY, aPerpendX, myOffset, rLFromT, rLFromB, true, fLength); + + // for end: get cuts with all right target styles and use the maximum + mfExtendEnd = getSimpleExtendedLineValues(rOrigin, rX, rY, aPerpendX, myOffset, rRFromT, rRFromB, false, fLength); rTarget.append( drawinglayer::primitive2d::Primitive2DReference( @@ -567,88 +628,35 @@ void CreateBorderPrimitives( drawinglayer::primitive2d::BorderLine( rBorder.Prim(), (pForceColor ? *pForceColor : rBorder.GetColorPrim()).getBColor(), - mfExtendLeftStart, - mfExtendLeftEnd), + mfExtendStart, + mfExtendEnd), rBorder.Type(), rBorder.PatternScale()))); } - else if (4 == myOffsets.size()) + else if (2 == myOffsets.size()) { - { - std::vector< double > myOffsetsA; - myOffsetsA.push_back(myOffsets[0]); - myOffsetsA.push_back(myOffsets[1]); + // we are a double edge, calculate cuts with edges coming from above/below + // for both edges to detect the line start/end extensions + double mfExtendLeftStart(0.0); + double mfExtendLeftEnd(0.0); + double mfExtendRightStart(0.0); + double mfExtendRightEnd(0.0); - std::vector< std::vector< double >> myCutsS(myOffsetsA.size()); - std::vector< double > nMinCutsS; - compareToStyle(rOrigin, rX, aPerpendX, myOffsetsA, rLFromT, rY, myCutsS); + // for start of first edge, get cuts with left targets. Start with upper and take maximum when + // cut exists. Else use lower and take minimum when cut exists + mfExtendLeftStart = getComplexExtendedLineValues(rOrigin, rX, rY, aPerpendX, myOffsets[0], rLFromT, rLFromB, true, fLength); - if (!areCutsEmpty(myCutsS)) - { - nMinCutsS = getMinMaxCuts(false, myCutsS); - } - else - { - compareToStyle(rOrigin, rX, aPerpendX, myOffsetsA, rLFromB, rY, myCutsS); - nMinCutsS = getMinMaxCuts(true, myCutsS); - } - - mfExtendLeftStart = ((nMinCutsS[0] + nMinCutsS[1]) * 0.5) * -1.0 * fLength; + // for end of first edge, get cuts with right targets. Start with upper and take minimum when + // cut exists. Else use lower and take maximum when cut exists + mfExtendLeftEnd = getComplexExtendedLineValues(rOrigin, rX, rY, aPerpendX, myOffsets[0], rRFromT, rRFromB, false, fLength); - std::vector< std::vector< double >> myCutsE(myOffsetsA.size()); - std::vector< double > nMinCutsE; - compareToStyle(rOrigin, rX, aPerpendX, myOffsetsA, rRFromT, rY, myCutsE); + // for start of second edge, get cuts with left targets. Start with lower and take maximum when + // cut exists. Else use upper and take minimum when cut exists + mfExtendRightStart = getComplexExtendedLineValues(rOrigin, rX, rY, aPerpendX, myOffsets[1], rLFromB, rLFromT, true, fLength); - if (!areCutsEmpty(myCutsE)) - { - nMinCutsE = getMinMaxCuts(true, myCutsE); - } - else - { - compareToStyle(rOrigin, rX, aPerpendX, myOffsetsA, rRFromB, rY, myCutsE); - nMinCutsE = getMinMaxCuts(false, myCutsE); - } - - mfExtendLeftEnd = ((nMinCutsE[0] + nMinCutsE[1]) * 0.5) * fLength; - } - - { - std::vector< double > myOffsetsB; - myOffsetsB.push_back(myOffsets[2]); - myOffsetsB.push_back(myOffsets[3]); - - std::vector< std::vector< double >> myCutsS(myOffsetsB.size()); - std::vector< double > nMinCutsS; - compareToStyle(rOrigin, rX, aPerpendX, myOffsetsB, rLFromB, rY, myCutsS); - - if (!areCutsEmpty(myCutsS)) - { - nMinCutsS = getMinMaxCuts(false, myCutsS); - } - else - { - compareToStyle(rOrigin, rX, aPerpendX, myOffsetsB, rLFromT, rY, myCutsS); - nMinCutsS = getMinMaxCuts(true, myCutsS); - } - - mfExtendRightStart = ((nMinCutsS[0] + nMinCutsS[1]) * 0.5) * -1.0 * fLength; - - std::vector< std::vector< double >> myCutsE(myOffsetsB.size()); - std::vector< double > nMinCutsE; - compareToStyle(rOrigin, rX, aPerpendX, myOffsetsB, rRFromB, rY, myCutsE); - - if (!areCutsEmpty(myCutsE)) - { - nMinCutsE = getMinMaxCuts(true, myCutsE); - } - else - { - compareToStyle(rOrigin, rX, aPerpendX, myOffsetsB, rRFromT, rY, myCutsE); - nMinCutsE = getMinMaxCuts(false, myCutsE); - } - - mfExtendRightEnd = ((nMinCutsE[0] + nMinCutsE[1]) * 0.5) * fLength; - } + // for end of second edge, get cuts with right targets. Start with lower and take minimum when + // cut exists. Else use upper and take maximum when cut exists + mfExtendRightEnd = getComplexExtendedLineValues(rOrigin, rX, rY, aPerpendX, myOffsets[1], rRFromB, rRFromT, false, fLength); rTarget.append( drawinglayer::primitive2d::Primitive2DReference( @@ -720,16 +728,19 @@ void CreateDiagFrameBorderPrimitives( const basegfx::B2DVector& rYAxis, const Style& rTLBR, const Style& rBLTR, - const Style& rTLFromB, - const Style& rTLFromR, - const Style& rBRFromT, - const Style& rBRFromL, - const Style& rBLFromT, - const Style& rBLFromR, - const Style& rTRFromB, - const Style& rTRFromL, + const Style& /*rTLFromB*/, + const Style& /*rTLFromR*/, + const Style& /*rBRFromT*/, + const Style& /*rBRFromL*/, + const Style& /*rBLFromT*/, + const Style& /*rBLFromR*/, + const Style& /*rTRFromB*/, + const Style& /*rTRFromL*/, const Color* pForceColor) { + // currently the diagonal edges are just added as-is without cutting them against the incoming + // edges. This needs to be improved in the future, so please do *not* remove the currently unused + // parameters from above if (rTLBR.Prim()) { // top-left to bottom-right diff --git a/sw/source/core/layout/paintfrm.cxx b/sw/source/core/layout/paintfrm.cxx index 20c362b0f8e4..407ac441ecf7 100644 --- a/sw/source/core/layout/paintfrm.cxx +++ b/sw/source/core/layout/paintfrm.cxx @@ -2771,25 +2771,32 @@ void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) cons // logically vertical lines are painted centered on the line, // logically horizontal lines are painted "below" the line - bool const isBelow((mrTabFrame.IsVertical()) ? !bHori : bHori); - double const offsetStart = (isBelow) - ? aStyles[0].GetWidth() / 2.0 - : std::max(aStyles[1].GetWidth(), - aStyles[3].GetWidth()) / 2.0; - double const offsetEnd = (isBelow) - ? aStyles[0].GetWidth() / 2.0 - : std::max(aStyles[4].GetWidth(), - aStyles[6].GetWidth()) / 2.0; - if (mrTabFrame.IsVertical()) - { - aPaintStart.X() -= static_cast(offsetStart + 0.5); - aPaintEnd.X() -= static_cast(offsetEnd + 0.5); - } - else - { - aPaintStart.Y() += static_cast(offsetStart + 0.5); - aPaintEnd.Y() += static_cast(offsetEnd + 0.5); - } + // + // This does not need to be done here, it is set in SwTabFramePainter::Insert + // already using SetRefMode(...) as property of the BorderLine Style, see there. + // When additionally adding the offset here manually, it will be applied + // double and will be rendered wrong. This did not happen before because + // the setting of the svx::frame::RefMode at svx::frame::Style was ignored there. + // + // bool const isBelow((mrTabFrame.IsVertical()) ? !bHori : bHori); + // double const offsetStart = (isBelow) + // ? aStyles[0].GetWidth() / 2.0 + // : std::max(aStyles[1].GetWidth(), + // aStyles[3].GetWidth()) / 2.0; + // double const offsetEnd = (isBelow) + // ? aStyles[0].GetWidth() / 2.0 + // : std::max(aStyles[4].GetWidth(), + // aStyles[6].GetWidth()) / 2.0; + // if (mrTabFrame.IsVertical()) + // { + // aPaintStart.X() -= static_cast(offsetStart + 0.5); + // aPaintEnd.X() -= static_cast(offsetEnd + 0.5); + // } + // else + // { + // aPaintStart.Y() += static_cast(offsetStart + 0.5); + // aPaintEnd.Y() += static_cast(offsetEnd + 0.5); + // } if (bHori) { -- cgit