diff options
author | Mathias Bauer <mba@openoffice.org> | 2009-09-09 11:45:13 +0200 |
---|---|---|
committer | Mathias Bauer <mba@openoffice.org> | 2009-09-09 11:45:13 +0200 |
commit | 0cb550725d7e128e72e67739aff5338865c28abc (patch) | |
tree | 0ffabe03dd8b83e273539f26f633273d50c37397 /basegfx | |
parent | aa611d78df11fcc1e35ead5fa093143bf17e4cf6 (diff) | |
parent | 1f0839c836781bc41f8c301b6262eabc978c90f8 (diff) |
merge commit to DEV300_m57
Diffstat (limited to 'basegfx')
-rw-r--r-- | basegfx/inc/basegfx/polygon/b2dpolypolygoncutter.hxx | 9 | ||||
-rw-r--r-- | basegfx/inc/basegfx/polygon/b2dpolypolygontools.hxx | 14 | ||||
-rw-r--r-- | basegfx/source/curve/b2dcubicbezier.cxx | 36 | ||||
-rw-r--r-- | basegfx/source/polygon/b2dlinegeometry.cxx | 105 | ||||
-rw-r--r-- | basegfx/source/polygon/b2dpolygoncutandtouch.cxx | 9 | ||||
-rw-r--r-- | basegfx/source/polygon/b2dpolypolygoncutter.cxx | 79 | ||||
-rw-r--r-- | basegfx/source/polygon/b2dpolypolygontools.cxx | 12 |
7 files changed, 213 insertions, 51 deletions
diff --git a/basegfx/inc/basegfx/polygon/b2dpolypolygoncutter.hxx b/basegfx/inc/basegfx/polygon/b2dpolypolygoncutter.hxx index 9474e51e3904..12532ff078f3 100644 --- a/basegfx/inc/basegfx/polygon/b2dpolypolygoncutter.hxx +++ b/basegfx/inc/basegfx/polygon/b2dpolypolygoncutter.hxx @@ -104,6 +104,15 @@ namespace basegfx // DIFF: Return all areas where CandidateA is not covered by CandidateB (cut B out of A) B2DPolyPolygon solvePolygonOperationDiff(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB); + /** merge all single PolyPolygons to a single, OR-ed PolyPolygon + + @param rInput + The source PolyPolygons + + @return A single PolyPolygon containing the Or-merged result + */ + B2DPolyPolygon mergeToSinglePolyPolygon(const std::vector< basegfx::B2DPolyPolygon >& rInput); + } // end of namespace tools } // end of namespace basegfx diff --git a/basegfx/inc/basegfx/polygon/b2dpolypolygontools.hxx b/basegfx/inc/basegfx/polygon/b2dpolypolygontools.hxx index 5c75edd7f262..c4687b3cfc5f 100644 --- a/basegfx/inc/basegfx/polygon/b2dpolypolygontools.hxx +++ b/basegfx/inc/basegfx/polygon/b2dpolypolygontools.hxx @@ -262,6 +262,20 @@ namespace basegfx bool equal(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB, const double& rfSmallValue); bool equal(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB); + /** snap some polygon coordinates to discrete coordinates + + This method allows to snap some polygon points to discrete (integer) values + which equals e.g. a snap to discrete coordinates. It will snap points of + horizontal and vertical edges + + @param rCandidate + The source polygon + + @return + The modified version of the source polygon + */ + B2DPolyPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolyPolygon& rCandidate); + } // end of namespace tools } // end of namespace basegfx diff --git a/basegfx/source/curve/b2dcubicbezier.cxx b/basegfx/source/curve/b2dcubicbezier.cxx index 76d1b74ddbca..e7247a95333b 100644 --- a/basegfx/source/curve/b2dcubicbezier.cxx +++ b/basegfx/source/curve/b2dcubicbezier.cxx @@ -443,21 +443,32 @@ namespace basegfx bool bAIsTrivial(aVecA.equalZero()); bool bBIsTrivial(aVecB.equalZero()); + // #i102241# prepare inverse edge length to normalize cross values; + // else the small compare value used in fTools::equalZero + // will be length dependent and this detection will work as less + // precise as longer the edge is. In principle, the length of the control + // vector would need to be used too, but to be trivial it is assumed to + // be of roughly equal length to the edge, so edge length can be used + // for both. Only needed when one of both is not trivial per se. + const double fInverseEdgeLength(bAIsTrivial && bBIsTrivial + ? 1.0 + : 1.0 / aEdge.getLength()); + // if A is not zero, check if it could be if(!bAIsTrivial) { - // parallel to edge? Check aVecA, aEdge - // B2DVector::areParallel is too correct, uses differences in the e15 region, - // thus do own test here - const double fValA(aVecA.getX() * aEdge.getY()); - const double fValB(aVecA.getY() * aEdge.getX()); + // #i102241# parallel to edge? Check aVecA, aEdge. Use cross() which does what + // we need here with the precision we need + const double fCross(aVecA.cross(aEdge) * fInverseEdgeLength); - if(fTools::equalZero(fabs(fValA) - fabs(fValB))) + if(fTools::equalZero(fCross)) { // get scale to edge. Use bigger distance for numeric quality - const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY()) ? aVecA.getX() / aEdge.getX() : aVecA.getY() / aEdge.getY()); + const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY()) + ? aVecA.getX() / aEdge.getX() + : aVecA.getY() / aEdge.getY()); - // end point of vector in edge range? + // relative end point of vector in edge range? if(fTools::moreOrEqual(fScale, 0.0) && fTools::lessOrEqual(fScale, 1.0)) { bAIsTrivial = true; @@ -470,13 +481,14 @@ namespace basegfx if(bAIsTrivial && !bBIsTrivial) { // parallel to edge? Check aVecB, aEdge - const double fValA(aVecB.getX() * aEdge.getY()); - const double fValB(aVecB.getY() * aEdge.getX()); + const double fCross(aVecB.cross(aEdge) * fInverseEdgeLength); - if(fTools::equalZero(fabs(fValA) - fabs(fValB))) + if(fTools::equalZero(fCross)) { // get scale to edge. Use bigger distance for numeric quality - const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY()) ? aVecB.getX() / aEdge.getX() : aVecB.getY() / aEdge.getY()); + const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY()) + ? aVecB.getX() / aEdge.getX() + : aVecB.getY() / aEdge.getY()); // end point of vector in edge range? Caution: controlB is directed AGAINST edge if(fTools::lessOrEqual(fScale, 0.0) && fTools::moreOrEqual(fScale, -1.0)) diff --git a/basegfx/source/polygon/b2dlinegeometry.cxx b/basegfx/source/polygon/b2dlinegeometry.cxx index 0cda1036e13b..1a9264ab769e 100644 --- a/basegfx/source/polygon/b2dlinegeometry.cxx +++ b/basegfx/source/polygon/b2dlinegeometry.cxx @@ -352,13 +352,12 @@ namespace basegfx // Unfortunately, while it would be geometrically correct to not add // the in-between points EdgeEnd and EdgeStart, it leads to rounding // errors when converting to integer polygon coordinates for painting - const B2DVector aEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint()); - if(rEdge.isBezier()) { // prepare target and data common for upper and lower B2DPolygon aBezierPolygon; - const double fEdgeLength(aEdgeVector.getLength()); + const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint()); + const double fEdgeLength(aPureEdgeVector.getLength()); const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength)); const B2DVector aTangentA(rEdge.getTangent(0.0)); const B2DVector aTangentB(rEdge.getTangent(1.0)); @@ -441,7 +440,11 @@ namespace basegfx } else { - const B2DVector aPerpendEdgeVector(getNormalizedPerpendicular(aEdgeVector) * fHalfLineWidth); + // #i101491# emulate rEdge.getTangent call which applies a factor of 0.3 to the + // full-length edge vector to have numerically exactly the same results as in the + // createAreaGeometryForJoin implementation + const B2DVector aEdgeTangent((rEdge.getEndPoint() - rEdge.getStartPoint()) * 0.3); + const B2DVector aPerpendEdgeVector(getNormalizedPerpendicular(aEdgeTangent) * fHalfLineWidth); B2DPolygon aEdgePolygon; // create upper edge @@ -495,45 +498,75 @@ namespace basegfx } } - // create first polygon part for edge - aEdgePolygon.append(aEndPoint); - aEdgePolygon.append(rPoint); - aEdgePolygon.append(aStartPoint); - - if(B2DLINEJOIN_MITER == eJoin) + switch(eJoin) { - // Look for the cut point between start point along rTangentPrev and - // end point along rTangentEdge. -rTangentEdge should be used, but since - // the cut value is used for interpolating along the first edge, the negation - // is not needed since the same fCut will be found on the first edge. - // If it exists, insert it to complete the mitered fill polygon. - double fCutPos(0.0); - tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos); - - if(0.0 != fCutPos) + case B2DLINEJOIN_MITER : { - const B2DPoint aCutPoint(interpolate(aStartPoint, aStartPoint + rTangentPrev, fCutPos)); - aEdgePolygon.append(aCutPoint); - } - } - else if(B2DLINEJOIN_ROUND == eJoin) - { - // use tooling to add needed EllipseSegment - double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX())); - double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX())); + aEdgePolygon.append(aEndPoint); + aEdgePolygon.append(rPoint); + aEdgePolygon.append(aStartPoint); + + // Look for the cut point between start point along rTangentPrev and + // end point along rTangentEdge. -rTangentEdge should be used, but since + // the cut value is used for interpolating along the first edge, the negation + // is not needed since the same fCut will be found on the first edge. + // If it exists, insert it to complete the mitered fill polygon. + double fCutPos(0.0); + tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos); + + if(0.0 != fCutPos) + { + const B2DPoint aCutPoint(interpolate(aStartPoint, aStartPoint + rTangentPrev, fCutPos)); + aEdgePolygon.append(aCutPoint); + } - // atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI] - if(fAngleStart < 0.0) - { - fAngleStart += F_2PI; + break; } - - if(fAngleEnd < 0.0) + case B2DLINEJOIN_ROUND : { - fAngleEnd += F_2PI; + // use tooling to add needed EllipseSegment + double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX())); + double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX())); + + // atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI] + if(fAngleStart < 0.0) + { + fAngleStart += F_2PI; + } + + if(fAngleEnd < 0.0) + { + fAngleEnd += F_2PI; + } + + const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd)); + + if(aBow.count() > 1) + { + // #i101491# + // use the original start/end positions; the ones from bow creation may be numerically + // different due to their different creation. To guarantee good merging quality with edges + // and edge roundings (and to reduce point count) + aEdgePolygon = aBow; + aEdgePolygon.setB2DPoint(0, aStartPoint); + aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint); + aEdgePolygon.append(rPoint); + + break; + } + else + { + // wanted fall-through to default + } } + default: // B2DLINEJOIN_BEVEL + { + aEdgePolygon.append(aEndPoint); + aEdgePolygon.append(rPoint); + aEdgePolygon.append(aStartPoint); - aEdgePolygon.append(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd)); + break; + } } // create last polygon part for edge diff --git a/basegfx/source/polygon/b2dpolygoncutandtouch.cxx b/basegfx/source/polygon/b2dpolygoncutandtouch.cxx index a1b7a69775ad..26016942717d 100644 --- a/basegfx/source/polygon/b2dpolygoncutandtouch.cxx +++ b/basegfx/source/polygon/b2dpolygoncutandtouch.cxx @@ -110,7 +110,10 @@ namespace basegfx { // #i76891# mergeTemporaryPointsAndPolygon redesigned to be able to correctly handle // single edges with/without control points - if(rTempPoints.size()) + // #i101491# added counter for non-changing element count + const sal_uInt32 nTempPointCount(rTempPoints.size()); + + if(nTempPointCount) { B2DPolygon aRetval; const sal_uInt32 nCount(rCandidate.count()); @@ -138,7 +141,7 @@ namespace basegfx double fLeftStart(0.0); // now add all points targeted to be at this index - while(nNewInd < rTempPoints.size() && rTempPoints[nNewInd].getIndex() == a) + while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a) { const temporaryPoint& rTempPoint = rTempPoints[nNewInd++]; @@ -160,7 +163,7 @@ namespace basegfx else { // add all points targeted to be at this index - while(nNewInd < rTempPoints.size() && rTempPoints[nNewInd].getIndex() == a) + while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a) { const temporaryPoint& rTempPoint = rTempPoints[nNewInd++]; const B2DPoint aNewPoint(rTempPoint.getPoint()); diff --git a/basegfx/source/polygon/b2dpolypolygoncutter.cxx b/basegfx/source/polygon/b2dpolypolygoncutter.cxx index b06e6fbafff7..0674bfe3953e 100644 --- a/basegfx/source/polygon/b2dpolypolygoncutter.cxx +++ b/basegfx/source/polygon/b2dpolypolygoncutter.cxx @@ -929,6 +929,85 @@ namespace basegfx } } + B2DPolyPolygon mergeToSinglePolyPolygon(const std::vector< basegfx::B2DPolyPolygon >& rInput) + { + std::vector< basegfx::B2DPolyPolygon > aInput(rInput); + + // first step: prepareForPolygonOperation and simple merge of non-overlapping + // PolyPolygons for speedup; this is possible for the wanted OR-operation + if(aInput.size()) + { + std::vector< basegfx::B2DPolyPolygon > aResult; + aResult.reserve(aInput.size()); + + for(sal_uInt32 a(0); a < aInput.size(); a++) + { + const basegfx::B2DPolyPolygon aCandidate(prepareForPolygonOperation(aInput[a])); + + if(aResult.size()) + { + const B2DRange aCandidateRange(aCandidate.getB2DRange()); + bool bCouldMergeSimple(false); + + for(sal_uInt32 b(0); !bCouldMergeSimple && b < aResult.size(); b++) + { + basegfx::B2DPolyPolygon aTarget(aResult[b]); + const B2DRange aTargetRange(aTarget.getB2DRange()); + + if(!aCandidateRange.overlaps(aTargetRange)) + { + aTarget.append(aCandidate); + aResult[b] = aTarget; + bCouldMergeSimple = true; + } + } + + if(!bCouldMergeSimple) + { + aResult.push_back(aCandidate); + } + } + else + { + aResult.push_back(aCandidate); + } + } + + aInput = aResult; + } + + // second step: melt pairwise to a single PolyPolygon + while(aInput.size() > 1) + { + std::vector< basegfx::B2DPolyPolygon > aResult; + aResult.reserve((aInput.size() / 2) + 1); + + for(sal_uInt32 a(0); a < aInput.size(); a += 2) + { + if(a + 1 < aInput.size()) + { + // a pair for processing + aResult.push_back(solvePolygonOperationOr(aInput[a], aInput[a + 1])); + } + else + { + // last single PolyPolygon; copy to target to not lose it + aResult.push_back(aInput[a]); + } + } + + aInput = aResult; + } + + // third step: get result + if(1 == aInput.size()) + { + return aInput[0]; + } + + return B2DPolyPolygon(); + } + ////////////////////////////////////////////////////////////////////////////// } // end of namespace tools diff --git a/basegfx/source/polygon/b2dpolypolygontools.cxx b/basegfx/source/polygon/b2dpolypolygontools.cxx index c92f2f29147b..e6b3a448530d 100644 --- a/basegfx/source/polygon/b2dpolypolygontools.cxx +++ b/basegfx/source/polygon/b2dpolypolygontools.cxx @@ -569,6 +569,18 @@ namespace basegfx return equal(rCandidateA, rCandidateB, fSmallValue); } + B2DPolyPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolyPolygon& rCandidate) + { + B2DPolyPolygon aRetval; + + for(sal_uInt32 a(0L); a < rCandidate.count(); a++) + { + aRetval.append(snapPointsOfHorizontalOrVerticalEdges(rCandidate.getB2DPolygon(a))); + } + + return aRetval; + } + } // end of namespace tools } // end of namespace basegfx |