/* -*- 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 .
*/
class temporaryPoint
{
B2DPoint maPoint; // the new point
sal_uInt32 mnIndex; // index after which to insert double mfCut; // parametric cut description [0.0 .. 1.0]
B2DPolygon mergeTemporaryPointsAndPolygon(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints)
{ // #i76891# mergeTemporaryPointsAndPolygon redesigned to be able to correctly handle // single edges with/without control points // #i101491# added counter for non-changing element count const sal_uInt32 nTempPointCount(rTempPoints.size());
// add start point
aRetval.append(rCandidate.getB2DPoint(0));
for(sal_uInt32 a(0); a < nCount; a++)
{ // get edge
rCandidate.getBezierSegment(a, aEdge);
if(aEdge.isBezier())
{ // control vectors involved for this edge double fLeftStart(0.0);
// now add all points targeted to be at this index while (nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a && fLeftStart < 1.0)
{ const temporaryPoint& rTempPoint = rTempPoints[nNewInd++];
// split curve segment. Splits need to come sorted and need to be < 1.0. Also, // since original segment is consumed from left to right, the cut values need // to be scaled to the remaining part
B2DCubicBezier aLeftPart; constdouble fRelativeSplitPoint((rTempPoint.getCut() - fLeftStart) / (1.0 - fLeftStart));
aEdge.split(fRelativeSplitPoint, &aLeftPart, &aEdge);
fLeftStart = rTempPoint.getCut();
// add left bow
aRetval.appendBezierSegment(aLeftPart.getControlPointA(), aLeftPart.getControlPointB(), rTempPoint.getPoint());
}
// add remaining bow
aRetval.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint());
} else
{ // add all points targeted to be at this index while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a)
{ const temporaryPoint& rTempPoint = rTempPoints[nNewInd++]; const B2DPoint& aNewPoint(rTempPoint.getPoint());
// do not add points double if(!aRetval.getB2DPoint(aRetval.count() - 1).equal(aNewPoint))
{
aRetval.append(aNewPoint);
}
}
// add edge end point
aRetval.append(aEdge.getEndPoint());
}
}
}
if(rCandidate.isClosed())
{ // set closed flag and correct last point (which is added double now).
utils::closeWithGeometryChange(aRetval);
}
return aRetval;
} else
{ return rCandidate;
}
}
void adaptAndTransferCutsWithBezierSegment( const temporaryPointVector& rPointVector, const B2DPolygon& rPolygon,
sal_uInt32 nInd, temporaryPointVector& rTempPoints)
{ // assuming that the subdivision to create rPolygon used equidistant pieces // (as in adaptiveSubdivideByCount) it is now possible to calculate back the // cut positions in the polygon to relative cut positions on the original bezier // segment. const sal_uInt32 nEdgeCount(rPolygon.count() ? rPolygon.count() - 1 : 0);
// no common start/end points, this can be no cuts if(rCurrB.equal(rCurrA) || rCurrB.equal(rNextA) || rNextB.equal(rCurrA) || rNextB.equal(rNextA)) return;
if (fTools::betweenOrEqualEither(fCut2, fZero, fOne))
{ // cut is in range, add point. Two edges can have only one cut, but // add a cut point to each list. The lists may be the same for // self intersections. const B2DPoint aCutPoint(interpolate(rCurrA, rNextA, fCut));
rTempPointsA.emplace_back(aCutPoint, nIndA, fCut);
rTempPointsB.emplace_back(aCutPoint, nIndB, fCut2);
}
}
void findCutsAndTouchesAndCommonForBezier(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
{ // #i76891# // This new method is necessary since in findEdgeCutsBezierAndEdge and in findEdgeCutsTwoBeziers // it is not sufficient to use findCuts() recursively. This will indeed find the cuts between the // segments of the two temporarily adaptive subdivided bezier segments, but not the touches or // equal points of them. // It would be possible to find the touches using findTouches(), but at last with common points // the adding of cut points (temporary points) would fail. But for these temporarily adaptive // subdivided bezier segments, common points may be not very likely, but the bug shows that it // happens. // Touch points are a little bit more likely than common points. All in all it is best to use // a specialized method here which can profit from knowing that it is working on a special // family of B2DPolygons: no curve segments included and not closed.
OSL_ENSURE(!rCandidateA.areControlPointsUsed() && !rCandidateB.areControlPointsUsed(), "findCutsAndTouchesAndCommonForBezier only works with subdivided polygons (!)");
OSL_ENSURE(!rCandidateA.isClosed() && !rCandidateB.isClosed(), "findCutsAndTouchesAndCommonForBezier only works with opened polygons (!)"); const sal_uInt32 nPointCountA(rCandidateA.count()); const sal_uInt32 nPointCountB(rCandidateB.count());
// use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered // as 0.0 cut. The 1.0 cut will be registered in the next loop step if(fTools::moreOrEqual(fCutA, fZero) && fTools::less(fCutA, fOne))
{ // it's a candidate, but also need to test parameter value of cut on line 2 double fCutB;
// use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered // as 0.0 cut. The 1.0 cut will be registered in the next loop step if(fTools::moreOrEqual(fCutB, fZero) && fTools::less(fCutB, fOne))
{ // cut is in both ranges. Add points for A and B // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy if(fTools::equal(fCutA, fZero))
{ // ignore for start point in first edge; this is handled // by outer methods and would just produce a double point if(a)
{
rTempPointsA.emplace_back(aCurrA, a, 0.0);
}
} else
{ const B2DPoint aCutPoint(interpolate(aCurrA, aNextA, fCutA));
rTempPointsA.emplace_back(aCutPoint, a, fCutA);
}
// #i111715# use fTools::equal instead of fTools::equalZero for better accuracy if(fTools::equal(fCutB, fZero))
{ // ignore for start point in first edge; this is handled // by outer methods and would just produce a double point if(b)
{
rTempPointsB.emplace_back(aCurrB, b, 0.0);
}
} else
{ const B2DPoint aCutPoint(interpolate(aCurrB, aNextB, fCutB));
rTempPointsB.emplace_back(aCutPoint, b, fCutB);
}
}
}
}
}
}
// prepare next step
aCurrB = aNextB;
}
// prepare next step
aCurrA = aNextA;
}
}
void findEdgeCutsBezierAndEdge( const B2DCubicBezier& rCubicA, const B2DPoint& rCurrB, const B2DPoint& rNextB,
sal_uInt32 nIndA, sal_uInt32 nIndB,
temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
{ // find all cuts between given bezier segment and edge. Add an entry to the tempPoints // for each common point with the cut value describing the relative position on given // bezier segment and edge.
B2DPolygon aTempPolygonA;
B2DPolygon aTempPolygonEdge;
temporaryPointVector aTempPointVectorA;
temporaryPointVector aTempPointVectorEdge;
// create subdivided polygons and find cuts between them // Keep adaptiveSubdivideByCount due to needed quality
aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
aTempPolygonA.append(rCubicA.getStartPoint());
rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT);
aTempPolygonEdge.append(rCurrB);
aTempPolygonEdge.append(rNextB);
// #i76891# using findCuts recursively is not sufficient here
findCutsAndTouchesAndCommonForBezier(aTempPolygonA, aTempPolygonEdge, aTempPointVectorA, aTempPointVectorEdge);
// append remapped tempVector entries for edge to tempPoints for edge for(const temporaryPoint & rTempPoint : aTempPointVectorEdge)
{
rTempPointsB.emplace_back(rTempPoint.getPoint(), nIndB, rTempPoint.getCut());
}
}
void findEdgeCutsTwoBeziers( const B2DCubicBezier& rCubicA, const B2DCubicBezier& rCubicB,
sal_uInt32 nIndA, sal_uInt32 nIndB,
temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
{ // find all cuts between the two given bezier segments. Add an entry to the tempPoints // for each common point with the cut value describing the relative position on given // bezier segments.
B2DPolygon aTempPolygonA;
B2DPolygon aTempPolygonB;
temporaryPointVector aTempPointVectorA;
temporaryPointVector aTempPointVectorB;
// create subdivided polygons and find cuts between them // Keep adaptiveSubdivideByCount due to needed quality
aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
aTempPolygonA.append(rCubicA.getStartPoint());
rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT);
aTempPolygonB.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
aTempPolygonB.append(rCubicB.getStartPoint());
rCubicB.adaptiveSubdivideByCount(aTempPolygonB, SUBDIVIDE_FOR_CUT_TEST_COUNT);
// #i76891# using findCuts recursively is not sufficient here
findCutsAndTouchesAndCommonForBezier(aTempPolygonA, aTempPolygonB, aTempPointVectorA, aTempPointVectorB);
void findEdgeCutsOneBezier( const B2DCubicBezier& rCubicA,
sal_uInt32 nInd, temporaryPointVector& rTempPoints)
{ // avoid expensive part of this method if possible // TODO: use hasAnyExtremum() method instead when it becomes available double fDummy; constbool bHasAnyExtremum = rCubicA.getMinimumExtremumPosition( fDummy ); if( !bHasAnyExtremum ) return;
// find all self-intersections on the given bezier segment. Add an entry to the tempPoints // for each self intersection point with the cut value describing the relative position on given // bezier segment.
B2DPolygon aTempPolygon;
temporaryPointVector aTempPointVector;
// create subdivided polygon and find cuts on it // Keep adaptiveSubdivideByCount due to needed quality
aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
aTempPolygon.append(rCubicA.getStartPoint());
rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT);
findCuts(aTempPolygon, aTempPointVector);
void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints, size_t* pPointLimit)
{ // find out if there are edges with intersections (self-cuts). If yes, add // entries to rTempPoints accordingly const sal_uInt32 nPointCount(rCandidate.count());
if (pPointLimit && rTempPoints.size() > *pPointLimit) break;
// prepare next step
aCurrB = aNextB;
}
// prepare next step
aCurrA = aNextA;
}
}
if (pPointLimit)
{ if (rTempPoints.size() > *pPointLimit)
*pPointLimit = 0; else
*pPointLimit -= rTempPoints.size();
}
}
} // end of anonymous namespace
} // end of namespace basegfx
namespace basegfx
{ namespace
{
void findTouchesOnEdge( const B2DPoint& rCurr, const B2DPoint& rNext, const B2DPolygon& rPointPolygon,
sal_uInt32 nInd, temporaryPointVector& rTempPoints)
{ // find out if points from rPointPolygon are positioned on given edge. If Yes, add // points there to represent touches (which may be enter or leave nodes later). const sal_uInt32 nPointCount(rPointPolygon.count());
void findTouchesOnCurve( const B2DCubicBezier& rCubicA, const B2DPolygon& rPointPolygon,
sal_uInt32 nInd, temporaryPointVector& rTempPoints)
{ // find all points from rPointPolygon which touch the given bezier segment. Add an entry // for each touch to the given pointVector. The cut for that entry is the relative position on // the given bezier segment.
B2DPolygon aTempPolygon;
temporaryPointVector aTempPointVector;
// create subdivided polygon and find cuts on it // Keep adaptiveSubdivideByCount due to needed quality
aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
aTempPolygon.append(rCubicA.getStartPoint());
rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT);
findTouches(aTempPolygon, rPointPolygon, aTempPointVector);
if(bHandleAsSimpleEdge)
{
findTouchesOnEdge(aCurr, aNext, rPointPolygon, a, rTempPoints);
}
}
// next step
aCurr = aNext;
}
}
} // end of anonymous namespace
} // end of namespace basegfx
namespace basegfx
{ namespace
{
void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
{ // find out if edges from both polygons cut. If so, add entries to rTempPoints which // should be added to the polygons accordingly const sal_uInt32 nPointCountA(rCandidateA.count()); const sal_uInt32 nPointCountB(rCandidateB.count());
if(nCount == 1)
{ // remove self intersections
aRetval.append(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(0), pPointLimit));
} else
{ // first solve self cuts and self touches for all contained single polygons
std::unique_ptr<temporaryPolygonData[]> pTempData(new temporaryPolygonData[nCount]);
sal_uInt32 a, b;
for(a = 0; a < nCount; a++)
{ // use polygons with solved self intersections
pTempData[a].setPolygon(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(a), pPointLimit));
}
if (pPointLimit && !*pPointLimit)
{
SAL_WARN("basegfx", "addPointsAtCutsAndTouches hit point limit"); return rCandidate;
}
// now cuts and touches between the polygons for(a = 0; a < nCount; a++)
{ for(b = 0; b < nCount; b++)
{ if(a != b)
{ // look for touches, compare each edge polygon to all other points if(pTempData[a].getRange().overlaps(pTempData[b].getRange()))
{
findTouches(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector());
}
}
if(a < b)
{ // look for cuts, compare each edge polygon to following ones if(pTempData[a].getRange().overlaps(pTempData[b].getRange()))
{
findCuts(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector(), pTempData[b].getTemporaryPointVector());
}
}
}
}
// consolidate the result for(a = 0; a < nCount; a++)
{
aRetval.append(mergeTemporaryPointsAndPolygon(pTempData[a].getPolygon(), pTempData[a].getTemporaryPointVector()));
}
}
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.