/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// to have the correct curve segments in the crossover checks, // it is necessary to keep the original next vectors, too. Else, // it may happen to use an already switched next vector which // would interpolate the wrong comparison point
B2DVector maOriginalNext;
};
struct SN
{ public:
PN* mpPN;
// For this to be a strict weak ordering, the assumption is that none of the involved // maPoint coordinates are NaN: booloperator<(const SN& rComp) const
{ return std::tie(mpPN->maPoint, mpPN->mnI)
< std::tie(rComp.mpPN->maPoint, rComp.mpPN->mnI);
}
};
staticbool impLeftOfEdges(const B2DVector& rVecA, const B2DVector& rVecB, constB2DVector& rTest)
{ // tests if rTest is left of both directed line segments along the line -rVecA, rVecB. Test is // with border. if(rVecA.cross(rVecB) > 0.0)
{ // b is left turn seen from a, test if Test is left of both and so inside (left is seen as inside) constbool bBoolA(rVecA.cross(rTest) >= 0.0); constbool bBoolB(rVecB.cross(rTest) <= 0.0);
return (bBoolA && bBoolB);
} else
{ // b is right turn seen from a, test if Test is right of both and so outside (left is seen as inside) constbool bBoolA(rVecA.cross(rTest) <= 0.0); constbool bBoolB(rVecB.cross(rTest) >= 0.0);
if(aNextB.equal(aPrevB))
{ // deadend on B (identical edge) return;
}
if(aPrevA.equal(aPrevB))
{ // common edge in same direction return;
} elseif(aPrevA.equal(aNextB))
{ // common edge in opposite direction if(aNextA.equal(aPrevB))
{ // common edge in opposite direction continues return;
} else
{ // common edge in opposite direction leave
impSwitchNext(rPNa, rPNb);
}
} elseif(aNextA.equal(aNextB))
{ // common edge in same direction enter // search leave edge
PN* pPNa2 = &maPNV[rPNa.mnIN];
PN* pPNb2 = &maPNV[rPNb.mnIN]; bool bOnEdge(true);
do
{ const B2DCubicBezier aNextA2(createSegment(*pPNa2, false)); const B2DCubicBezier aNextB2(createSegment(*pPNb2, false));
if(rNextB.equal(rPrevB))
{ // deadend on B return;
}
if(rPrevA.equal(rPrevB))
{ // common edge in same direction return;
} elseif(rPrevA.equal(rNextB))
{ // common edge in opposite direction if(rNextA.equal(rPrevB))
{ // common edge in opposite direction continues return;
} else
{ // common edge in opposite direction leave
impSwitchNext(rPNa, rPNb);
}
} elseif(rNextA.equal(rNextB))
{ // common edge in same direction enter // search leave edge
PN* pPNa2 = &maPNV[rPNa.mnIN];
PN* pPNb2 = &maPNV[rPNb.mnIN]; bool bOnEdge(true);
do
{ const B2DPoint& rNextA2(maPNV[pPNa2->mnIN].maPoint); const B2DPoint& rNextB2(maPNV[pPNb2->mnIN].maPoint);
for (sal_uInt32 a = 0; a < nNodeCount - 1; a++)
{ // test a before using it, not after. Also use nPointCount instead of aSortNodes.size()
PN& rPNb = *(maSNV[a].mpPN);
// If it's not a bezier polygon, at least four points are needed to create // a self-intersection. If it's a bezier polygon, the minimum point number // is two, since with a single point You get a curve, but no self-intersection if(!(nPointCount > 3 || (nPointCount > 1 && mbIsCurve))) return;
// reserve space in point, control and sort vector.
maSNV.reserve(nPointCount);
maPNV.reserve(nPointCount);
maVNV.reserve(mbIsCurve ? nPointCount : 0);
// If it's not a bezier curve, at least three points would be needed to have a // topological relevant (not empty) polygon. Since it's not known here if trivial // edges (dead ends) will be kept or sorted out, add non-bezier polygons with // more than one point. // For bezier curves, the minimum for defining an area is also one.
sal_uInt32 nPointCount = std::accumulate( aGeometry.begin(), aGeometry.end(), sal_uInt32(0),
[](sal_uInt32 a, const basegfx::B2DPolygon& b){return a + b.count();});
if(!nPointCount) return;
// reserve space in point, control and sort vector.
maSNV.reserve(nPointCount);
maPNV.reserve(nPointCount);
maVNV.reserve(mbIsCurve ? nPointCount : 0);
// use same condition as above, the data vector is // pre-allocated if(nCandCount)
{
impAddPolygon(nInsertIndex, rCandidate);
nInsertIndex += nCandCount;
}
}
B2DPolyPolygon createNonzeroConform(const B2DPolyPolygon& rCandidate)
{ if (rCandidate.count() > 1000)
{
SAL_WARN("basegfx", "this poly is too large, " << rCandidate.count() << " elements, to be able to process timeously, falling back to ignoring the winding rule, which is likely to cause rendering artifacts"); return rCandidate;
}
B2DPolyPolygon aCandidate;
// remove all self-intersections and intersections if(rCandidate.count() == 1)
{
aCandidate = basegfx::utils::solveCrossovers(rCandidate.getB2DPolygon(0));
} else
{
aCandidate = basegfx::utils::solveCrossovers(rCandidate);
}
for(a = 0; a < nCount; a++)
{ const StripHelper& rHelper = aHelpers[a]; // for contained unequal oriented polygons sum will be 0 // for contained equal it will be >=2 or <=-2 // for free polygons (not contained) it will be 1 or -1 // -> accept all which are >=-1 && <= 1 bool bAcceptEntry(rHelper.mnDepth >= -1 && rHelper.mnDepth <= 1);
if(bAInB && bBInA)
{ // congruent if(rHelperA.meOrinetation == rHelperB.meOrinetation)
{ // two polys or two holes. Lower one of them to get one of them out of the way. // Since each will be contained in the other one, both will be increased, too. // So, for lowering, increase only one of them
rHelperA.mnDepth++;
} else
{ // poly and hole. They neutralize, so get rid of both. Move securely below zero.
rHelperA.mnDepth = - static_cast<sal_Int32>(nCount);
rHelperB.mnDepth = - static_cast<sal_Int32>(nCount);
}
} else
{ if(bAInB)
{ if(rHelperB.meOrinetation == B2VectorOrientation::Negative)
{
rHelperA.mnDepth--;
} else
{
rHelperA.mnDepth++;
}
} elseif(bBInA)
{ if(rHelperA.meOrinetation == B2VectorOrientation::Negative)
{
rHelperB.mnDepth--;
} else
{
rHelperB.mnDepth++;
}
}
}
}
}
B2DPolyPolygon solvePolygonOperationXor(const B2DPolyPolygon& rCandidateA, constB2DPolyPolygon& rCandidateB)
{ if(!rCandidateA.count())
{ return rCandidateB;
} elseif(!rCandidateB.count())
{ return rCandidateA;
} else
{ // XOR is pretty simple: By definition it is the simple concatenation of // the single polygons since we imply XOR fill rule. Make it intersection-free // and correct orientations
B2DPolyPolygon aRetval(rCandidateA);
B2DPolyPolygon solvePolygonOperationAnd(const B2DPolyPolygon& rCandidateA, constB2DPolyPolygon& rCandidateB)
{ if(!rCandidateA.count())
{ return B2DPolyPolygon();
} elseif(!rCandidateB.count())
{ return B2DPolyPolygon();
} else
{ // tdf#130150 shortcut & precision: If both are simple ranges, // solve based on ranges if(basegfx::utils::isRectangle(rCandidateA) && basegfx::utils::isRectangle(rCandidateB))
{ // *if* both are ranges, AND always can be solved const basegfx::B2DRange aRangeA(rCandidateA.getB2DRange()); const basegfx::B2DRange aRangeB(rCandidateB.getB2DRange());
if(aRangeA.isInside(aRangeB))
{ // 2nd completely inside 1st -> 2nd is result of AND return rCandidateB;
}
if(aRangeB.isInside(aRangeA))
{ // 2nd completely inside 1st -> 2nd is result of AND return rCandidateA;
}
// solve by intersection
basegfx::B2DRange aIntersect(aRangeA);
aIntersect.intersect(aRangeB);
if(aIntersect.isEmpty())
{ // no overlap -> empty polygon as result of AND return B2DPolyPolygon();
}
// create polygon result return B2DPolyPolygon(
basegfx::utils::createPolygonFromRect(
aIntersect));
}
// concatenate polygons, solve crossovers and throw away all sub-polygons // with a depth of < 1. This means to keep all polygons where at least two // polygons do overlap.
B2DPolyPolygon aRetval(rCandidateA);
B2DPolyPolygon solvePolygonOperationDiff(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
{ if(!rCandidateA.count())
{ return B2DPolyPolygon();
} elseif(!rCandidateB.count())
{ return rCandidateA;
} else
{ // Make B topologically to holes and append to A
B2DPolyPolygon aRetval(rCandidateB);
aRetval.flip();
aRetval.append(rCandidateA);
// solve crossovers and throw away all sub-polygons which have a // depth other than 0.
aRetval = basegfx::utils::solveCrossovers(aRetval);
aRetval = basegfx::utils::stripNeutralPolygons(aRetval);
// first step: prepareForPolygonOperation and simple merge of non-overlapping // PolyPolygons for speedup; this is possible for the wanted OR-operation
B2DPolyPolygonVector aResult;
aResult.reserve(rInput.size());
for(const basegfx::B2DPolyPolygon & a : rInput)
{ const basegfx::B2DPolyPolygon aCandidate(prepareForPolygonOperation(a));
// second step: melt pairwise to a single PolyPolygon while(aResult.size() > 1)
{
B2DPolyPolygonVector aResult2;
aResult2.reserve((aResult.size() / 2) + 1);
for(size_t a(0); a < aResult.size(); a += 2)
{ if(a + 1 < aResult.size())
{ // a pair for processing
aResult2.push_back(solvePolygonOperationOr(aResult[a], aResult[a + 1]));
} else
{ // last single PolyPolygon; copy to target to not lose it
aResult2.push_back(aResult[a]);
}
}
aResult = std::move(aResult2);
}
// third step: get result if(aResult.size() == 1)
{ return aResult[0];
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.