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
diff --git a/svx/source/dialog/framelink.cxx b/svx/source/dialog/framelink.cxx
index e5b6abb..c746e88 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));
// 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;
for (size_t a(0); a < rOtherOffsets.size(); a++)
{
const basegfx::B2DPoint aOtherPos(rOrigin + (rOtherUnifiedPerpendicular * rOtherOffsets[a]));
basegfx::tools::findCut(
aOtherPosLeft,
rOtherVector,
aMyPosLeft,
rMyVector,
CutFlagValue::LINE,
&aNewCuts.mfLeftLeft);
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);
basegfx::tools::findCut(
aOtherPosLeft,
rOtherVector,
aMyPosRight,
rMyVector,
CutFlagValue::LINE,
&aNewCuts.mfLeftRight);
rOtherCuts[a].push_back(fCut);
}
}
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())
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);
if (pResult)
{
return 0.0;
return (pResult->mfLeftRight + pResult->mfRightRight) * 0.5 * (bEdgeStart ? -fLength : fLength);
}
if (1 == rVector.size())
{
return rVector[0];
}
double fRetval(rVector[0]);
for (size_t a(1); a < rVector.size(); a++)
{
fRetval = bMin ? std::min(fRetval, rVector[a]) : std::max(fRetval, rVector[a]);
}
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())
{
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;
// get offsets for my style (one or two)
std::vector< OffsetPair > myOffsets;
getOffsetPairsFromStyle(rBorder, myOffsets);
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;
if (1 == myOffsets.size())
{
// 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);
}
// 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);
mfExtendLeftStart = ((nMinCutsS[0] + nMinCutsS[1]) * 0.5) * -1.0 * fLength;
// 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);
std::vector< std::vector< double >> myCutsE(myOffsetsA.size());
std::vector< double > nMinCutsE;
compareToStyle(rOrigin, rX, aPerpendX, myOffsetsA, rRFromT, rY, myCutsE);
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 20c362b..407ac44 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<double>(aStyles[1].GetWidth(),
aStyles[3].GetWidth()) / 2.0;
double const offsetEnd = (isBelow)
? aStyles[0].GetWidth() / 2.0
: std::max<double>(aStyles[4].GetWidth(),
aStyles[6].GetWidth()) / 2.0;
if (mrTabFrame.IsVertical())
{
aPaintStart.X() -= static_cast<long>(offsetStart + 0.5);
aPaintEnd.X() -= static_cast<long>(offsetEnd + 0.5);
}
else
{
aPaintStart.Y() += static_cast<long>(offsetStart + 0.5);
aPaintEnd.Y() += static_cast<long>(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<double>(aStyles[1].GetWidth(),
// aStyles[3].GetWidth()) / 2.0;
// double const offsetEnd = (isBelow)
// ? aStyles[0].GetWidth() / 2.0
// : std::max<double>(aStyles[4].GetWidth(),
// aStyles[6].GetWidth()) / 2.0;
// if (mrTabFrame.IsVertical())
// {
// aPaintStart.X() -= static_cast<long>(offsetStart + 0.5);
// aPaintEnd.X() -= static_cast<long>(offsetEnd + 0.5);
// }
// else
// {
// aPaintStart.Y() += static_cast<long>(offsetStart + 0.5);
// aPaintEnd.Y() += static_cast<long>(offsetEnd + 0.5);
// }
if (bHori)
{