1*cdf0e10cSrcweir /************************************************************************* 2*cdf0e10cSrcweir * 3*cdf0e10cSrcweir * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4*cdf0e10cSrcweir * 5*cdf0e10cSrcweir * Copyright 2000, 2010 Oracle and/or its affiliates. 6*cdf0e10cSrcweir * 7*cdf0e10cSrcweir * OpenOffice.org - a multi-platform office productivity suite 8*cdf0e10cSrcweir * 9*cdf0e10cSrcweir * This file is part of OpenOffice.org. 10*cdf0e10cSrcweir * 11*cdf0e10cSrcweir * OpenOffice.org is free software: you can redistribute it and/or modify 12*cdf0e10cSrcweir * it under the terms of the GNU Lesser General Public License version 3 13*cdf0e10cSrcweir * only, as published by the Free Software Foundation. 14*cdf0e10cSrcweir * 15*cdf0e10cSrcweir * OpenOffice.org is distributed in the hope that it will be useful, 16*cdf0e10cSrcweir * but WITHOUT ANY WARRANTY; without even the implied warranty of 17*cdf0e10cSrcweir * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18*cdf0e10cSrcweir * GNU Lesser General Public License version 3 for more details 19*cdf0e10cSrcweir * (a copy is included in the LICENSE file that accompanied this code). 20*cdf0e10cSrcweir * 21*cdf0e10cSrcweir * You should have received a copy of the GNU Lesser General Public License 22*cdf0e10cSrcweir * version 3 along with OpenOffice.org. If not, see 23*cdf0e10cSrcweir * <http://www.openoffice.org/license.html> 24*cdf0e10cSrcweir * for a copy of the LGPLv3 License. 25*cdf0e10cSrcweir * 26*cdf0e10cSrcweir ************************************************************************/ 27*cdf0e10cSrcweir 28*cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove 29*cdf0e10cSrcweir #include "precompiled_basegfx.hxx" 30*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygonclipper.hxx> 31*cdf0e10cSrcweir #include <osl/diagnose.h> 32*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx> 33*cdf0e10cSrcweir #include <basegfx/numeric/ftools.hxx> 34*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx> 35*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 36*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygoncutandtouch.hxx> 37*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygontools.hxx> 38*cdf0e10cSrcweir #include <basegfx/curve/b2dcubicbezier.hxx> 39*cdf0e10cSrcweir #include <basegfx/tools/rectcliptools.hxx> 40*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrixtools.hxx> 41*cdf0e10cSrcweir 42*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 43*cdf0e10cSrcweir 44*cdf0e10cSrcweir namespace basegfx 45*cdf0e10cSrcweir { 46*cdf0e10cSrcweir namespace tools 47*cdf0e10cSrcweir { 48*cdf0e10cSrcweir B2DPolyPolygon clipPolygonOnParallelAxis(const B2DPolygon& rCandidate, bool bParallelToXAxis, bool bAboveAxis, double fValueOnOtherAxis, bool bStroke) 49*cdf0e10cSrcweir { 50*cdf0e10cSrcweir B2DPolyPolygon aRetval; 51*cdf0e10cSrcweir 52*cdf0e10cSrcweir if(rCandidate.count()) 53*cdf0e10cSrcweir { 54*cdf0e10cSrcweir const B2DRange aCandidateRange(getRange(rCandidate)); 55*cdf0e10cSrcweir 56*cdf0e10cSrcweir if(bParallelToXAxis && fTools::moreOrEqual(aCandidateRange.getMinY(), fValueOnOtherAxis)) 57*cdf0e10cSrcweir { 58*cdf0e10cSrcweir // completely above and on the clip line. also true for curves. 59*cdf0e10cSrcweir if(bAboveAxis) 60*cdf0e10cSrcweir { 61*cdf0e10cSrcweir // add completely 62*cdf0e10cSrcweir aRetval.append(rCandidate); 63*cdf0e10cSrcweir } 64*cdf0e10cSrcweir } 65*cdf0e10cSrcweir else if(bParallelToXAxis && fTools::lessOrEqual(aCandidateRange.getMaxY(), fValueOnOtherAxis)) 66*cdf0e10cSrcweir { 67*cdf0e10cSrcweir // completely below and on the clip line. also true for curves. 68*cdf0e10cSrcweir if(!bAboveAxis) 69*cdf0e10cSrcweir { 70*cdf0e10cSrcweir // add completely 71*cdf0e10cSrcweir aRetval.append(rCandidate); 72*cdf0e10cSrcweir } 73*cdf0e10cSrcweir } 74*cdf0e10cSrcweir else if(!bParallelToXAxis && fTools::moreOrEqual(aCandidateRange.getMinX(), fValueOnOtherAxis)) 75*cdf0e10cSrcweir { 76*cdf0e10cSrcweir // completely right of and on the clip line. also true for curves. 77*cdf0e10cSrcweir if(bAboveAxis) 78*cdf0e10cSrcweir { 79*cdf0e10cSrcweir // add completely 80*cdf0e10cSrcweir aRetval.append(rCandidate); 81*cdf0e10cSrcweir } 82*cdf0e10cSrcweir } 83*cdf0e10cSrcweir else if(!bParallelToXAxis && fTools::lessOrEqual(aCandidateRange.getMaxX(), fValueOnOtherAxis)) 84*cdf0e10cSrcweir { 85*cdf0e10cSrcweir // completely left of and on the clip line. also true for curves. 86*cdf0e10cSrcweir if(!bAboveAxis) 87*cdf0e10cSrcweir { 88*cdf0e10cSrcweir // add completely 89*cdf0e10cSrcweir aRetval.append(rCandidate); 90*cdf0e10cSrcweir } 91*cdf0e10cSrcweir } 92*cdf0e10cSrcweir else 93*cdf0e10cSrcweir { 94*cdf0e10cSrcweir // add cuts with axis to polygon, including bezier segments 95*cdf0e10cSrcweir // Build edge to cut with. Make it a little big longer than needed for 96*cdf0e10cSrcweir // numerical stability. We want to cut against the edge seen as endless 97*cdf0e10cSrcweir // ray here, but addPointsAtCuts() will limit itself to the 98*cdf0e10cSrcweir // edge's range ]0.0 .. 1.0[. 99*cdf0e10cSrcweir const double fSmallExtension((aCandidateRange.getWidth() + aCandidateRange.getHeight()) * (0.5 * 0.1)); 100*cdf0e10cSrcweir const B2DPoint aStart( 101*cdf0e10cSrcweir bParallelToXAxis ? aCandidateRange.getMinX() - fSmallExtension : fValueOnOtherAxis, 102*cdf0e10cSrcweir bParallelToXAxis ? fValueOnOtherAxis : aCandidateRange.getMinY() - fSmallExtension); 103*cdf0e10cSrcweir const B2DPoint aEnd( 104*cdf0e10cSrcweir bParallelToXAxis ? aCandidateRange.getMaxX() + fSmallExtension : fValueOnOtherAxis, 105*cdf0e10cSrcweir bParallelToXAxis ? fValueOnOtherAxis : aCandidateRange.getMaxY() + fSmallExtension); 106*cdf0e10cSrcweir const B2DPolygon aCandidate(addPointsAtCuts(rCandidate, aStart, aEnd)); 107*cdf0e10cSrcweir const sal_uInt32 nPointCount(aCandidate.count()); 108*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L); 109*cdf0e10cSrcweir B2DCubicBezier aEdge; 110*cdf0e10cSrcweir B2DPolygon aRun; 111*cdf0e10cSrcweir 112*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nEdgeCount; a++) 113*cdf0e10cSrcweir { 114*cdf0e10cSrcweir aCandidate.getBezierSegment(a, aEdge); 115*cdf0e10cSrcweir const B2DPoint aTestPoint(aEdge.interpolatePoint(0.5)); 116*cdf0e10cSrcweir const bool bInside(bParallelToXAxis ? 117*cdf0e10cSrcweir fTools::moreOrEqual(aTestPoint.getY(), fValueOnOtherAxis) == bAboveAxis : 118*cdf0e10cSrcweir fTools::moreOrEqual(aTestPoint.getX(), fValueOnOtherAxis) == bAboveAxis); 119*cdf0e10cSrcweir 120*cdf0e10cSrcweir if(bInside) 121*cdf0e10cSrcweir { 122*cdf0e10cSrcweir if(!aRun.count() || !aRun.getB2DPoint(aRun.count() - 1).equal(aEdge.getStartPoint())) 123*cdf0e10cSrcweir { 124*cdf0e10cSrcweir aRun.append(aEdge.getStartPoint()); 125*cdf0e10cSrcweir } 126*cdf0e10cSrcweir 127*cdf0e10cSrcweir if(aEdge.isBezier()) 128*cdf0e10cSrcweir { 129*cdf0e10cSrcweir aRun.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint()); 130*cdf0e10cSrcweir } 131*cdf0e10cSrcweir else 132*cdf0e10cSrcweir { 133*cdf0e10cSrcweir aRun.append(aEdge.getEndPoint()); 134*cdf0e10cSrcweir } 135*cdf0e10cSrcweir } 136*cdf0e10cSrcweir else 137*cdf0e10cSrcweir { 138*cdf0e10cSrcweir if(bStroke && aRun.count()) 139*cdf0e10cSrcweir { 140*cdf0e10cSrcweir aRetval.append(aRun); 141*cdf0e10cSrcweir aRun.clear(); 142*cdf0e10cSrcweir } 143*cdf0e10cSrcweir } 144*cdf0e10cSrcweir } 145*cdf0e10cSrcweir 146*cdf0e10cSrcweir if(aRun.count()) 147*cdf0e10cSrcweir { 148*cdf0e10cSrcweir if(bStroke) 149*cdf0e10cSrcweir { 150*cdf0e10cSrcweir // try to merge this last and first polygon; they may have been 151*cdf0e10cSrcweir // the former polygon's start/end point 152*cdf0e10cSrcweir if(aRetval.count()) 153*cdf0e10cSrcweir { 154*cdf0e10cSrcweir const B2DPolygon aStartPolygon(aRetval.getB2DPolygon(0)); 155*cdf0e10cSrcweir 156*cdf0e10cSrcweir if(aStartPolygon.count() && aStartPolygon.getB2DPoint(0).equal(aRun.getB2DPoint(aRun.count() - 1))) 157*cdf0e10cSrcweir { 158*cdf0e10cSrcweir // append start polygon to aRun, remove from result set 159*cdf0e10cSrcweir aRun.append(aStartPolygon); aRun.removeDoublePoints(); 160*cdf0e10cSrcweir aRetval.remove(0); 161*cdf0e10cSrcweir } 162*cdf0e10cSrcweir } 163*cdf0e10cSrcweir 164*cdf0e10cSrcweir aRetval.append(aRun); 165*cdf0e10cSrcweir } 166*cdf0e10cSrcweir else 167*cdf0e10cSrcweir { 168*cdf0e10cSrcweir // set closed flag and correct last point (which is added double now). 169*cdf0e10cSrcweir closeWithGeometryChange(aRun); 170*cdf0e10cSrcweir aRetval.append(aRun); 171*cdf0e10cSrcweir } 172*cdf0e10cSrcweir } 173*cdf0e10cSrcweir } 174*cdf0e10cSrcweir } 175*cdf0e10cSrcweir 176*cdf0e10cSrcweir return aRetval; 177*cdf0e10cSrcweir } 178*cdf0e10cSrcweir 179*cdf0e10cSrcweir B2DPolyPolygon clipPolyPolygonOnParallelAxis(const B2DPolyPolygon& rCandidate, bool bParallelToXAxis, bool bAboveAxis, double fValueOnOtherAxis, bool bStroke) 180*cdf0e10cSrcweir { 181*cdf0e10cSrcweir const sal_uInt32 nPolygonCount(rCandidate.count()); 182*cdf0e10cSrcweir B2DPolyPolygon aRetval; 183*cdf0e10cSrcweir 184*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nPolygonCount; a++) 185*cdf0e10cSrcweir { 186*cdf0e10cSrcweir const B2DPolyPolygon aClippedPolyPolygon(clipPolygonOnParallelAxis(rCandidate.getB2DPolygon(a), bParallelToXAxis, bAboveAxis, fValueOnOtherAxis, bStroke)); 187*cdf0e10cSrcweir 188*cdf0e10cSrcweir if(aClippedPolyPolygon.count()) 189*cdf0e10cSrcweir { 190*cdf0e10cSrcweir aRetval.append(aClippedPolyPolygon); 191*cdf0e10cSrcweir } 192*cdf0e10cSrcweir } 193*cdf0e10cSrcweir 194*cdf0e10cSrcweir return aRetval; 195*cdf0e10cSrcweir } 196*cdf0e10cSrcweir 197*cdf0e10cSrcweir B2DPolyPolygon clipPolygonOnRange(const B2DPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke) 198*cdf0e10cSrcweir { 199*cdf0e10cSrcweir const sal_uInt32 nCount(rCandidate.count()); 200*cdf0e10cSrcweir B2DPolyPolygon aRetval; 201*cdf0e10cSrcweir 202*cdf0e10cSrcweir if(!nCount) 203*cdf0e10cSrcweir { 204*cdf0e10cSrcweir // source is empty 205*cdf0e10cSrcweir return aRetval; 206*cdf0e10cSrcweir } 207*cdf0e10cSrcweir 208*cdf0e10cSrcweir if(rRange.isEmpty()) 209*cdf0e10cSrcweir { 210*cdf0e10cSrcweir if(bInside) 211*cdf0e10cSrcweir { 212*cdf0e10cSrcweir // nothing is inside an empty range 213*cdf0e10cSrcweir return aRetval; 214*cdf0e10cSrcweir } 215*cdf0e10cSrcweir else 216*cdf0e10cSrcweir { 217*cdf0e10cSrcweir // everything is outside an empty range 218*cdf0e10cSrcweir return B2DPolyPolygon(rCandidate); 219*cdf0e10cSrcweir } 220*cdf0e10cSrcweir } 221*cdf0e10cSrcweir 222*cdf0e10cSrcweir const B2DRange aCandidateRange(getRange(rCandidate)); 223*cdf0e10cSrcweir 224*cdf0e10cSrcweir if(rRange.isInside(aCandidateRange)) 225*cdf0e10cSrcweir { 226*cdf0e10cSrcweir // candidate is completely inside given range 227*cdf0e10cSrcweir if(bInside) 228*cdf0e10cSrcweir { 229*cdf0e10cSrcweir // nothing to do 230*cdf0e10cSrcweir return B2DPolyPolygon(rCandidate); 231*cdf0e10cSrcweir } 232*cdf0e10cSrcweir else 233*cdf0e10cSrcweir { 234*cdf0e10cSrcweir // nothing is outside, then 235*cdf0e10cSrcweir return aRetval; 236*cdf0e10cSrcweir } 237*cdf0e10cSrcweir } 238*cdf0e10cSrcweir 239*cdf0e10cSrcweir if(!bInside) 240*cdf0e10cSrcweir { 241*cdf0e10cSrcweir // cutting off the outer parts of filled polygons at parallell 242*cdf0e10cSrcweir // lines to the axes is only possible for the inner part, not for 243*cdf0e10cSrcweir // the outer part which means cutting a hole into the original polygon. 244*cdf0e10cSrcweir // This is because the inner part is a logical AND-operation of 245*cdf0e10cSrcweir // the four implied half-planes, but the outer part is not. 246*cdf0e10cSrcweir // It is possible for strokes, but with creating unnecessary extra 247*cdf0e10cSrcweir // cuts, so using clipPolygonOnPolyPolygon is better there, too. 248*cdf0e10cSrcweir // This needs to be done with the topology knowlegde and is unfurtunately 249*cdf0e10cSrcweir // more expensive, too. 250*cdf0e10cSrcweir const B2DPolygon aClip(createPolygonFromRect(rRange)); 251*cdf0e10cSrcweir 252*cdf0e10cSrcweir return clipPolygonOnPolyPolygon(rCandidate, B2DPolyPolygon(aClip), bInside, bStroke); 253*cdf0e10cSrcweir } 254*cdf0e10cSrcweir 255*cdf0e10cSrcweir // clip against the four axes of the range 256*cdf0e10cSrcweir // against X-Axis, lower value 257*cdf0e10cSrcweir aRetval = clipPolygonOnParallelAxis(rCandidate, true, bInside, rRange.getMinY(), bStroke); 258*cdf0e10cSrcweir 259*cdf0e10cSrcweir if(aRetval.count()) 260*cdf0e10cSrcweir { 261*cdf0e10cSrcweir // against Y-Axis, lower value 262*cdf0e10cSrcweir if(1L == aRetval.count()) 263*cdf0e10cSrcweir { 264*cdf0e10cSrcweir aRetval = clipPolygonOnParallelAxis(aRetval.getB2DPolygon(0L), false, bInside, rRange.getMinX(), bStroke); 265*cdf0e10cSrcweir } 266*cdf0e10cSrcweir else 267*cdf0e10cSrcweir { 268*cdf0e10cSrcweir aRetval = clipPolyPolygonOnParallelAxis(aRetval, false, bInside, rRange.getMinX(), bStroke); 269*cdf0e10cSrcweir } 270*cdf0e10cSrcweir 271*cdf0e10cSrcweir if(aRetval.count()) 272*cdf0e10cSrcweir { 273*cdf0e10cSrcweir // against X-Axis, higher value 274*cdf0e10cSrcweir if(1L == aRetval.count()) 275*cdf0e10cSrcweir { 276*cdf0e10cSrcweir aRetval = clipPolygonOnParallelAxis(aRetval.getB2DPolygon(0L), true, !bInside, rRange.getMaxY(), bStroke); 277*cdf0e10cSrcweir } 278*cdf0e10cSrcweir else 279*cdf0e10cSrcweir { 280*cdf0e10cSrcweir aRetval = clipPolyPolygonOnParallelAxis(aRetval, true, !bInside, rRange.getMaxY(), bStroke); 281*cdf0e10cSrcweir } 282*cdf0e10cSrcweir 283*cdf0e10cSrcweir if(aRetval.count()) 284*cdf0e10cSrcweir { 285*cdf0e10cSrcweir // against Y-Axis, higher value 286*cdf0e10cSrcweir if(1L == aRetval.count()) 287*cdf0e10cSrcweir { 288*cdf0e10cSrcweir aRetval = clipPolygonOnParallelAxis(aRetval.getB2DPolygon(0L), false, !bInside, rRange.getMaxX(), bStroke); 289*cdf0e10cSrcweir } 290*cdf0e10cSrcweir else 291*cdf0e10cSrcweir { 292*cdf0e10cSrcweir aRetval = clipPolyPolygonOnParallelAxis(aRetval, false, !bInside, rRange.getMaxX(), bStroke); 293*cdf0e10cSrcweir } 294*cdf0e10cSrcweir } 295*cdf0e10cSrcweir } 296*cdf0e10cSrcweir } 297*cdf0e10cSrcweir 298*cdf0e10cSrcweir return aRetval; 299*cdf0e10cSrcweir } 300*cdf0e10cSrcweir 301*cdf0e10cSrcweir B2DPolyPolygon clipPolyPolygonOnRange(const B2DPolyPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke) 302*cdf0e10cSrcweir { 303*cdf0e10cSrcweir const sal_uInt32 nPolygonCount(rCandidate.count()); 304*cdf0e10cSrcweir B2DPolyPolygon aRetval; 305*cdf0e10cSrcweir 306*cdf0e10cSrcweir if(!nPolygonCount) 307*cdf0e10cSrcweir { 308*cdf0e10cSrcweir // source is empty 309*cdf0e10cSrcweir return aRetval; 310*cdf0e10cSrcweir } 311*cdf0e10cSrcweir 312*cdf0e10cSrcweir if(rRange.isEmpty()) 313*cdf0e10cSrcweir { 314*cdf0e10cSrcweir if(bInside) 315*cdf0e10cSrcweir { 316*cdf0e10cSrcweir // nothing is inside an empty range 317*cdf0e10cSrcweir return aRetval; 318*cdf0e10cSrcweir } 319*cdf0e10cSrcweir else 320*cdf0e10cSrcweir { 321*cdf0e10cSrcweir // everything is outside an empty range 322*cdf0e10cSrcweir return rCandidate; 323*cdf0e10cSrcweir } 324*cdf0e10cSrcweir } 325*cdf0e10cSrcweir 326*cdf0e10cSrcweir if(bInside) 327*cdf0e10cSrcweir { 328*cdf0e10cSrcweir for(sal_uInt32 a(0L); a < nPolygonCount; a++) 329*cdf0e10cSrcweir { 330*cdf0e10cSrcweir const B2DPolyPolygon aClippedPolyPolygon(clipPolygonOnRange(rCandidate.getB2DPolygon(a), rRange, bInside, bStroke)); 331*cdf0e10cSrcweir 332*cdf0e10cSrcweir if(aClippedPolyPolygon.count()) 333*cdf0e10cSrcweir { 334*cdf0e10cSrcweir aRetval.append(aClippedPolyPolygon); 335*cdf0e10cSrcweir } 336*cdf0e10cSrcweir } 337*cdf0e10cSrcweir } 338*cdf0e10cSrcweir else 339*cdf0e10cSrcweir { 340*cdf0e10cSrcweir // for details, see comment in clipPolygonOnRange for the "cutting off 341*cdf0e10cSrcweir // the outer parts of filled polygons at parallell lines" explanations 342*cdf0e10cSrcweir const B2DPolygon aClip(createPolygonFromRect(rRange)); 343*cdf0e10cSrcweir 344*cdf0e10cSrcweir return clipPolyPolygonOnPolyPolygon(rCandidate, B2DPolyPolygon(aClip), bInside, bStroke); 345*cdf0e10cSrcweir } 346*cdf0e10cSrcweir 347*cdf0e10cSrcweir return aRetval; 348*cdf0e10cSrcweir } 349*cdf0e10cSrcweir 350*cdf0e10cSrcweir B2DPolyPolygon clipPolygonOnEdge(const B2DPolygon& rCandidate, const B2DPoint& rPointA, const B2DPoint& rPointB, bool bAbove, bool bStroke) 351*cdf0e10cSrcweir { 352*cdf0e10cSrcweir B2DPolyPolygon aRetval; 353*cdf0e10cSrcweir 354*cdf0e10cSrcweir if(rPointA.equal(rPointB)) 355*cdf0e10cSrcweir { 356*cdf0e10cSrcweir // edge has no length, return polygon 357*cdf0e10cSrcweir aRetval.append(rCandidate); 358*cdf0e10cSrcweir } 359*cdf0e10cSrcweir else if(rCandidate.count()) 360*cdf0e10cSrcweir { 361*cdf0e10cSrcweir const B2DVector aEdge(rPointB - rPointA); 362*cdf0e10cSrcweir B2DPolygon aCandidate(rCandidate); 363*cdf0e10cSrcweir 364*cdf0e10cSrcweir // translate and rotate polygon so that given edge is on x axis 365*cdf0e10cSrcweir B2DHomMatrix aMatrixTransform(basegfx::tools::createTranslateB2DHomMatrix(-rPointA.getX(), -rPointA.getY())); 366*cdf0e10cSrcweir aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX())); 367*cdf0e10cSrcweir aCandidate.transform(aMatrixTransform); 368*cdf0e10cSrcweir 369*cdf0e10cSrcweir // call clip method on X-Axis 370*cdf0e10cSrcweir aRetval = clipPolygonOnParallelAxis(aCandidate, true, bAbove, 0.0, bStroke); 371*cdf0e10cSrcweir 372*cdf0e10cSrcweir if(aRetval.count()) 373*cdf0e10cSrcweir { 374*cdf0e10cSrcweir // if there is a result, it needs to be transformed back 375*cdf0e10cSrcweir aMatrixTransform.invert(); 376*cdf0e10cSrcweir aRetval.transform(aMatrixTransform); 377*cdf0e10cSrcweir } 378*cdf0e10cSrcweir } 379*cdf0e10cSrcweir 380*cdf0e10cSrcweir return aRetval; 381*cdf0e10cSrcweir } 382*cdf0e10cSrcweir 383*cdf0e10cSrcweir B2DPolyPolygon clipPolyPolygonOnEdge(const B2DPolyPolygon& rCandidate, const B2DPoint& rPointA, const B2DPoint& rPointB, bool bAbove, bool bStroke) 384*cdf0e10cSrcweir { 385*cdf0e10cSrcweir B2DPolyPolygon aRetval; 386*cdf0e10cSrcweir 387*cdf0e10cSrcweir if(rPointA.equal(rPointB)) 388*cdf0e10cSrcweir { 389*cdf0e10cSrcweir // edge has no length, return polygon 390*cdf0e10cSrcweir aRetval = rCandidate; 391*cdf0e10cSrcweir } 392*cdf0e10cSrcweir else if(rCandidate.count()) 393*cdf0e10cSrcweir { 394*cdf0e10cSrcweir const B2DVector aEdge(rPointB - rPointA); 395*cdf0e10cSrcweir B2DPolyPolygon aCandidate(rCandidate); 396*cdf0e10cSrcweir 397*cdf0e10cSrcweir // translate and rotate polygon so that given edge is on x axis 398*cdf0e10cSrcweir B2DHomMatrix aMatrixTransform(basegfx::tools::createTranslateB2DHomMatrix(-rPointA.getX(), -rPointA.getY())); 399*cdf0e10cSrcweir aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX())); 400*cdf0e10cSrcweir aCandidate.transform(aMatrixTransform); 401*cdf0e10cSrcweir 402*cdf0e10cSrcweir // call clip method on X-Axis 403*cdf0e10cSrcweir aRetval = clipPolyPolygonOnParallelAxis(aCandidate, true, bAbove, 0.0, bStroke); 404*cdf0e10cSrcweir 405*cdf0e10cSrcweir if(aRetval.count()) 406*cdf0e10cSrcweir { 407*cdf0e10cSrcweir // if there is a result, it needs to be transformed back 408*cdf0e10cSrcweir aMatrixTransform.invert(); 409*cdf0e10cSrcweir aRetval.transform(aMatrixTransform); 410*cdf0e10cSrcweir } 411*cdf0e10cSrcweir } 412*cdf0e10cSrcweir 413*cdf0e10cSrcweir return aRetval; 414*cdf0e10cSrcweir } 415*cdf0e10cSrcweir 416*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 417*cdf0e10cSrcweir 418*cdf0e10cSrcweir B2DPolyPolygon clipPolyPolygonOnPolyPolygon(const B2DPolyPolygon& rCandidate, const B2DPolyPolygon& rClip, bool bInside, bool bStroke) 419*cdf0e10cSrcweir { 420*cdf0e10cSrcweir B2DPolyPolygon aRetval; 421*cdf0e10cSrcweir 422*cdf0e10cSrcweir if(rCandidate.count() && rClip.count()) 423*cdf0e10cSrcweir { 424*cdf0e10cSrcweir if(bStroke) 425*cdf0e10cSrcweir { 426*cdf0e10cSrcweir // line clipping, create line snippets by first adding all cut points and 427*cdf0e10cSrcweir // then marching along the edges and detecting if they are inside or outside 428*cdf0e10cSrcweir // the clip polygon 429*cdf0e10cSrcweir for(sal_uInt32 a(0); a < rCandidate.count(); a++) 430*cdf0e10cSrcweir { 431*cdf0e10cSrcweir // add cuts with clip to polygon, including bezier segments 432*cdf0e10cSrcweir const B2DPolygon aCandidate(addPointsAtCuts(rCandidate.getB2DPolygon(a), rClip)); 433*cdf0e10cSrcweir const sal_uInt32 nPointCount(aCandidate.count()); 434*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L); 435*cdf0e10cSrcweir B2DCubicBezier aEdge; 436*cdf0e10cSrcweir B2DPolygon aRun; 437*cdf0e10cSrcweir 438*cdf0e10cSrcweir for(sal_uInt32 b(0); b < nEdgeCount; b++) 439*cdf0e10cSrcweir { 440*cdf0e10cSrcweir aCandidate.getBezierSegment(b, aEdge); 441*cdf0e10cSrcweir const B2DPoint aTestPoint(aEdge.interpolatePoint(0.5)); 442*cdf0e10cSrcweir const bool bIsInside(tools::isInside(rClip, aTestPoint) == bInside); 443*cdf0e10cSrcweir 444*cdf0e10cSrcweir if(bIsInside) 445*cdf0e10cSrcweir { 446*cdf0e10cSrcweir if(!aRun.count()) 447*cdf0e10cSrcweir { 448*cdf0e10cSrcweir aRun.append(aEdge.getStartPoint()); 449*cdf0e10cSrcweir } 450*cdf0e10cSrcweir 451*cdf0e10cSrcweir if(aEdge.isBezier()) 452*cdf0e10cSrcweir { 453*cdf0e10cSrcweir aRun.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint()); 454*cdf0e10cSrcweir } 455*cdf0e10cSrcweir else 456*cdf0e10cSrcweir { 457*cdf0e10cSrcweir aRun.append(aEdge.getEndPoint()); 458*cdf0e10cSrcweir } 459*cdf0e10cSrcweir } 460*cdf0e10cSrcweir else 461*cdf0e10cSrcweir { 462*cdf0e10cSrcweir if(aRun.count()) 463*cdf0e10cSrcweir { 464*cdf0e10cSrcweir aRetval.append(aRun); 465*cdf0e10cSrcweir aRun.clear(); 466*cdf0e10cSrcweir } 467*cdf0e10cSrcweir } 468*cdf0e10cSrcweir } 469*cdf0e10cSrcweir 470*cdf0e10cSrcweir if(aRun.count()) 471*cdf0e10cSrcweir { 472*cdf0e10cSrcweir // try to merge this last and first polygon; they may have been 473*cdf0e10cSrcweir // the former polygon's start/end point 474*cdf0e10cSrcweir if(aRetval.count()) 475*cdf0e10cSrcweir { 476*cdf0e10cSrcweir const B2DPolygon aStartPolygon(aRetval.getB2DPolygon(0)); 477*cdf0e10cSrcweir 478*cdf0e10cSrcweir if(aStartPolygon.count() && aStartPolygon.getB2DPoint(0).equal(aRun.getB2DPoint(aRun.count() - 1))) 479*cdf0e10cSrcweir { 480*cdf0e10cSrcweir // append start polygon to aRun, remove from result set 481*cdf0e10cSrcweir aRun.append(aStartPolygon); aRun.removeDoublePoints(); 482*cdf0e10cSrcweir aRetval.remove(0); 483*cdf0e10cSrcweir } 484*cdf0e10cSrcweir } 485*cdf0e10cSrcweir 486*cdf0e10cSrcweir aRetval.append(aRun); 487*cdf0e10cSrcweir } 488*cdf0e10cSrcweir } 489*cdf0e10cSrcweir } 490*cdf0e10cSrcweir else 491*cdf0e10cSrcweir { 492*cdf0e10cSrcweir // area clipping 493*cdf0e10cSrcweir B2DPolyPolygon aMergePolyPolygonA(rClip); 494*cdf0e10cSrcweir 495*cdf0e10cSrcweir // First solve all polygon-self and polygon-polygon intersections. 496*cdf0e10cSrcweir // Also get rid of some not-needed polygons (neutral, no area -> when 497*cdf0e10cSrcweir // no intersections, these are tubes). 498*cdf0e10cSrcweir // Now it is possible to correct the orientations in the cut-free 499*cdf0e10cSrcweir // polygons to values corresponding to painting the PolyPolygon with 500*cdf0e10cSrcweir // a XOR-WindingRule. 501*cdf0e10cSrcweir aMergePolyPolygonA = solveCrossovers(aMergePolyPolygonA); 502*cdf0e10cSrcweir aMergePolyPolygonA = stripNeutralPolygons(aMergePolyPolygonA); 503*cdf0e10cSrcweir aMergePolyPolygonA = correctOrientations(aMergePolyPolygonA); 504*cdf0e10cSrcweir 505*cdf0e10cSrcweir if(!bInside) 506*cdf0e10cSrcweir { 507*cdf0e10cSrcweir // if we want to get the outside of the clip polygon, make 508*cdf0e10cSrcweir // it a 'Hole' in topological sense 509*cdf0e10cSrcweir aMergePolyPolygonA.flip(); 510*cdf0e10cSrcweir } 511*cdf0e10cSrcweir 512*cdf0e10cSrcweir B2DPolyPolygon aMergePolyPolygonB(rCandidate); 513*cdf0e10cSrcweir 514*cdf0e10cSrcweir // prepare 2nd source polygon in same way 515*cdf0e10cSrcweir aMergePolyPolygonB = solveCrossovers(aMergePolyPolygonB); 516*cdf0e10cSrcweir aMergePolyPolygonB = stripNeutralPolygons(aMergePolyPolygonB); 517*cdf0e10cSrcweir aMergePolyPolygonB = correctOrientations(aMergePolyPolygonB); 518*cdf0e10cSrcweir 519*cdf0e10cSrcweir // to clip against each other, concatenate and solve all 520*cdf0e10cSrcweir // polygon-polygon crossovers. polygon-self do not need to 521*cdf0e10cSrcweir // be solved again, they were solved in the preparation. 522*cdf0e10cSrcweir aRetval.append(aMergePolyPolygonA); 523*cdf0e10cSrcweir aRetval.append(aMergePolyPolygonB); 524*cdf0e10cSrcweir aRetval = solveCrossovers(aRetval); 525*cdf0e10cSrcweir 526*cdf0e10cSrcweir // now remove neutral polygons (closed, but no area). In a last 527*cdf0e10cSrcweir // step throw away all polygons which have a depth of less than 1 528*cdf0e10cSrcweir // which means there was no logical AND at their position. For the 529*cdf0e10cSrcweir // not-inside solution, the clip was flipped to define it as 'Hole', 530*cdf0e10cSrcweir // so the removal rule is different here; remove all with a depth 531*cdf0e10cSrcweir // of less than 0 (aka holes). 532*cdf0e10cSrcweir aRetval = stripNeutralPolygons(aRetval); 533*cdf0e10cSrcweir aRetval = stripDispensablePolygons(aRetval, bInside); 534*cdf0e10cSrcweir } 535*cdf0e10cSrcweir } 536*cdf0e10cSrcweir 537*cdf0e10cSrcweir return aRetval; 538*cdf0e10cSrcweir } 539*cdf0e10cSrcweir 540*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 541*cdf0e10cSrcweir 542*cdf0e10cSrcweir B2DPolyPolygon clipPolygonOnPolyPolygon(const B2DPolygon& rCandidate, const B2DPolyPolygon& rClip, bool bInside, bool bStroke) 543*cdf0e10cSrcweir { 544*cdf0e10cSrcweir B2DPolyPolygon aRetval; 545*cdf0e10cSrcweir 546*cdf0e10cSrcweir if(rCandidate.count() && rClip.count()) 547*cdf0e10cSrcweir { 548*cdf0e10cSrcweir aRetval = clipPolyPolygonOnPolyPolygon(B2DPolyPolygon(rCandidate), rClip, bInside, bStroke); 549*cdf0e10cSrcweir } 550*cdf0e10cSrcweir 551*cdf0e10cSrcweir return aRetval; 552*cdf0e10cSrcweir } 553*cdf0e10cSrcweir 554*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 555*cdf0e10cSrcweir 556*cdf0e10cSrcweir /* 557*cdf0e10cSrcweir * let a plane be defined as 558*cdf0e10cSrcweir * 559*cdf0e10cSrcweir * v.n+d=0 560*cdf0e10cSrcweir * 561*cdf0e10cSrcweir * and a ray be defined as 562*cdf0e10cSrcweir * 563*cdf0e10cSrcweir * a+(b-a)*t=0 564*cdf0e10cSrcweir * 565*cdf0e10cSrcweir * substitute and rearranging yields 566*cdf0e10cSrcweir * 567*cdf0e10cSrcweir * t = -(a.n+d)/(n.(b-a)) 568*cdf0e10cSrcweir * 569*cdf0e10cSrcweir * if the denominator is zero, the line is either 570*cdf0e10cSrcweir * contained in the plane or parallel to the plane. 571*cdf0e10cSrcweir * in either case, there is no intersection. 572*cdf0e10cSrcweir * if numerator and denominator are both zero, the 573*cdf0e10cSrcweir * ray is contained in the plane. 574*cdf0e10cSrcweir * 575*cdf0e10cSrcweir */ 576*cdf0e10cSrcweir struct scissor_plane { 577*cdf0e10cSrcweir double nx,ny; // plane normal 578*cdf0e10cSrcweir double d; // [-] minimum distance from origin 579*cdf0e10cSrcweir sal_uInt32 clipmask; // clipping mask, e.g. 1000 1000 580*cdf0e10cSrcweir }; 581*cdf0e10cSrcweir 582*cdf0e10cSrcweir /* 583*cdf0e10cSrcweir * 584*cdf0e10cSrcweir * polygon clipping rules (straight out of Foley and Van Dam) 585*cdf0e10cSrcweir * =========================================================== 586*cdf0e10cSrcweir * current |next |emit 587*cdf0e10cSrcweir * ____________________________________ 588*cdf0e10cSrcweir * inside |inside |next 589*cdf0e10cSrcweir * inside |outside |intersect with clip plane 590*cdf0e10cSrcweir * outside |outside |nothing 591*cdf0e10cSrcweir * outside |inside |intersect with clip plane follwed by next 592*cdf0e10cSrcweir * 593*cdf0e10cSrcweir */ 594*cdf0e10cSrcweir sal_uInt32 scissorLineSegment( ::basegfx::B2DPoint *in_vertex, // input buffer 595*cdf0e10cSrcweir sal_uInt32 in_count, // number of verts in input buffer 596*cdf0e10cSrcweir ::basegfx::B2DPoint *out_vertex, // output buffer 597*cdf0e10cSrcweir scissor_plane *pPlane, // scissoring plane 598*cdf0e10cSrcweir const ::basegfx::B2DRectangle &rR ) // clipping rectangle 599*cdf0e10cSrcweir { 600*cdf0e10cSrcweir ::basegfx::B2DPoint *curr; 601*cdf0e10cSrcweir ::basegfx::B2DPoint *next; 602*cdf0e10cSrcweir 603*cdf0e10cSrcweir sal_uInt32 out_count=0; 604*cdf0e10cSrcweir 605*cdf0e10cSrcweir // process all the verts 606*cdf0e10cSrcweir for(sal_uInt32 i=0; i<in_count; i++) { 607*cdf0e10cSrcweir 608*cdf0e10cSrcweir // vertices are relative to the coordinate 609*cdf0e10cSrcweir // system defined by the rectangle. 610*cdf0e10cSrcweir curr = &in_vertex[i]; 611*cdf0e10cSrcweir next = &in_vertex[(i+1)%in_count]; 612*cdf0e10cSrcweir 613*cdf0e10cSrcweir // perform clipping judgement & mask against current plane. 614*cdf0e10cSrcweir sal_uInt32 clip = pPlane->clipmask & ((getCohenSutherlandClipFlags(*curr,rR)<<4)|getCohenSutherlandClipFlags(*next,rR)); 615*cdf0e10cSrcweir 616*cdf0e10cSrcweir if(clip==0) { // both verts are inside 617*cdf0e10cSrcweir out_vertex[out_count++] = *next; 618*cdf0e10cSrcweir } 619*cdf0e10cSrcweir else if((clip&0x0f) && (clip&0xf0)) { // both verts are outside 620*cdf0e10cSrcweir } 621*cdf0e10cSrcweir else if((clip&0x0f) && (clip&0xf0)==0) { // curr is inside, next is outside 622*cdf0e10cSrcweir 623*cdf0e10cSrcweir // direction vector from 'current' to 'next', *not* normalized 624*cdf0e10cSrcweir // to bring 't' into the [0<=x<=1] intervall. 625*cdf0e10cSrcweir ::basegfx::B2DPoint dir((*next)-(*curr)); 626*cdf0e10cSrcweir 627*cdf0e10cSrcweir double denominator = ( pPlane->nx*dir.getX() + 628*cdf0e10cSrcweir pPlane->ny*dir.getY() ); 629*cdf0e10cSrcweir double numerator = ( pPlane->nx*curr->getX() + 630*cdf0e10cSrcweir pPlane->ny*curr->getY() + 631*cdf0e10cSrcweir pPlane->d ); 632*cdf0e10cSrcweir double t = -numerator/denominator; 633*cdf0e10cSrcweir 634*cdf0e10cSrcweir // calculate the actual point of intersection 635*cdf0e10cSrcweir ::basegfx::B2DPoint intersection( curr->getX()+t*dir.getX(), 636*cdf0e10cSrcweir curr->getY()+t*dir.getY() ); 637*cdf0e10cSrcweir 638*cdf0e10cSrcweir out_vertex[out_count++] = intersection; 639*cdf0e10cSrcweir } 640*cdf0e10cSrcweir else if((clip&0x0f)==0 && (clip&0xf0)) { // curr is outside, next is inside 641*cdf0e10cSrcweir 642*cdf0e10cSrcweir // direction vector from 'current' to 'next', *not* normalized 643*cdf0e10cSrcweir // to bring 't' into the [0<=x<=1] intervall. 644*cdf0e10cSrcweir ::basegfx::B2DPoint dir((*next)-(*curr)); 645*cdf0e10cSrcweir 646*cdf0e10cSrcweir double denominator = ( pPlane->nx*dir.getX() + 647*cdf0e10cSrcweir pPlane->ny*dir.getY() ); 648*cdf0e10cSrcweir double numerator = ( pPlane->nx*curr->getX() + 649*cdf0e10cSrcweir pPlane->ny*curr->getY() + 650*cdf0e10cSrcweir pPlane->d ); 651*cdf0e10cSrcweir double t = -numerator/denominator; 652*cdf0e10cSrcweir 653*cdf0e10cSrcweir // calculate the actual point of intersection 654*cdf0e10cSrcweir ::basegfx::B2DPoint intersection( curr->getX()+t*dir.getX(), 655*cdf0e10cSrcweir curr->getY()+t*dir.getY() ); 656*cdf0e10cSrcweir 657*cdf0e10cSrcweir out_vertex[out_count++] = intersection; 658*cdf0e10cSrcweir out_vertex[out_count++] = *next; 659*cdf0e10cSrcweir } 660*cdf0e10cSrcweir } 661*cdf0e10cSrcweir 662*cdf0e10cSrcweir return out_count; 663*cdf0e10cSrcweir } 664*cdf0e10cSrcweir 665*cdf0e10cSrcweir B2DPolygon clipTriangleListOnRange( const B2DPolygon& rCandidate, 666*cdf0e10cSrcweir const B2DRange& rRange ) 667*cdf0e10cSrcweir { 668*cdf0e10cSrcweir B2DPolygon aResult; 669*cdf0e10cSrcweir 670*cdf0e10cSrcweir if( !(rCandidate.count()%3) ) 671*cdf0e10cSrcweir { 672*cdf0e10cSrcweir const int scissor_plane_count = 4; 673*cdf0e10cSrcweir 674*cdf0e10cSrcweir scissor_plane sp[scissor_plane_count]; 675*cdf0e10cSrcweir 676*cdf0e10cSrcweir sp[0].nx = +1.0; 677*cdf0e10cSrcweir sp[0].ny = +0.0; 678*cdf0e10cSrcweir sp[0].d = -(rRange.getMinX()); 679*cdf0e10cSrcweir sp[0].clipmask = (RectClipFlags::LEFT << 4) | RectClipFlags::LEFT; // 0001 0001 680*cdf0e10cSrcweir sp[1].nx = -1.0; 681*cdf0e10cSrcweir sp[1].ny = +0.0; 682*cdf0e10cSrcweir sp[1].d = +(rRange.getMaxX()); 683*cdf0e10cSrcweir sp[1].clipmask = (RectClipFlags::RIGHT << 4) | RectClipFlags::RIGHT; // 0010 0010 684*cdf0e10cSrcweir sp[2].nx = +0.0; 685*cdf0e10cSrcweir sp[2].ny = +1.0; 686*cdf0e10cSrcweir sp[2].d = -(rRange.getMinY()); 687*cdf0e10cSrcweir sp[2].clipmask = (RectClipFlags::TOP << 4) | RectClipFlags::TOP; // 0100 0100 688*cdf0e10cSrcweir sp[3].nx = +0.0; 689*cdf0e10cSrcweir sp[3].ny = -1.0; 690*cdf0e10cSrcweir sp[3].d = +(rRange.getMaxY()); 691*cdf0e10cSrcweir sp[3].clipmask = (RectClipFlags::BOTTOM << 4) | RectClipFlags::BOTTOM; // 1000 1000 692*cdf0e10cSrcweir 693*cdf0e10cSrcweir // retrieve the number of vertices of the triangulated polygon 694*cdf0e10cSrcweir const sal_uInt32 nVertexCount = rCandidate.count(); 695*cdf0e10cSrcweir 696*cdf0e10cSrcweir if(nVertexCount) 697*cdf0e10cSrcweir { 698*cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////// 699*cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////// 700*cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////// 701*cdf0e10cSrcweir // 702*cdf0e10cSrcweir // Upper bound for the maximal number of vertices when intersecting an 703*cdf0e10cSrcweir // axis-aligned rectangle with a triangle in E2 704*cdf0e10cSrcweir // 705*cdf0e10cSrcweir // The rectangle and the triangle are in general position, and have 4 and 3 706*cdf0e10cSrcweir // vertices, respectively. 707*cdf0e10cSrcweir // 708*cdf0e10cSrcweir // Lemma: Since the rectangle is a convex polygon ( see 709*cdf0e10cSrcweir // http://mathworld.wolfram.com/ConvexPolygon.html for a definition), and 710*cdf0e10cSrcweir // has no holes, it follows that any straight line will intersect the 711*cdf0e10cSrcweir // rectangle's border line at utmost two times (with the usual 712*cdf0e10cSrcweir // tie-breaking rule, if the intersection exactly hits an already existing 713*cdf0e10cSrcweir // rectangle vertex, that this intersection is only attributed to one of 714*cdf0e10cSrcweir // the adjoining edges). Thus, having a rectangle intersected with 715*cdf0e10cSrcweir // a half-plane (one side of a straight line denotes 'inside', the 716*cdf0e10cSrcweir // other 'outside') will at utmost add _one_ vertex to the resulting 717*cdf0e10cSrcweir // intersection polygon (adding two intersection vertices, and removing at 718*cdf0e10cSrcweir // least one rectangle vertex): 719*cdf0e10cSrcweir // 720*cdf0e10cSrcweir // * 721*cdf0e10cSrcweir // +--+-----------------+ 722*cdf0e10cSrcweir // | * | 723*cdf0e10cSrcweir // |* | 724*cdf0e10cSrcweir // + | 725*cdf0e10cSrcweir // *| | 726*cdf0e10cSrcweir // * | | 727*cdf0e10cSrcweir // +--------------------+ 728*cdf0e10cSrcweir // 729*cdf0e10cSrcweir // Proof: If the straight line intersects the rectangle two 730*cdf0e10cSrcweir // times, it does so for distinct edges, i.e. the intersection has 731*cdf0e10cSrcweir // minimally one of the rectangle's vertices on either side of the straight 732*cdf0e10cSrcweir // line (but maybe more). Thus, the intersection with a half-plane has 733*cdf0e10cSrcweir // minimally _one_ rectangle vertex removed from the resulting clip 734*cdf0e10cSrcweir // polygon, and therefore, a clip against a half-plane has the net effect 735*cdf0e10cSrcweir // of adding at utmost _one_ vertex to the resulting clip polygon. 736*cdf0e10cSrcweir // 737*cdf0e10cSrcweir // Theorem: The intersection of a rectangle and a triangle results in a 738*cdf0e10cSrcweir // polygon with at utmost 7 vertices. 739*cdf0e10cSrcweir // 740*cdf0e10cSrcweir // Proof: The inside of the triangle can be described as the consecutive 741*cdf0e10cSrcweir // intersection with three half-planes. Together with the lemma above, this 742*cdf0e10cSrcweir // results in at utmost 3 additional vertices added to the already existing 4 743*cdf0e10cSrcweir // rectangle vertices. 744*cdf0e10cSrcweir // 745*cdf0e10cSrcweir // This upper bound is attained with the following example configuration: 746*cdf0e10cSrcweir // 747*cdf0e10cSrcweir // * 748*cdf0e10cSrcweir // *** 749*cdf0e10cSrcweir // ** * 750*cdf0e10cSrcweir // ** * 751*cdf0e10cSrcweir // ** * 752*cdf0e10cSrcweir // ** * 753*cdf0e10cSrcweir // ** * 754*cdf0e10cSrcweir // ** * 755*cdf0e10cSrcweir // ** * 756*cdf0e10cSrcweir // ** * 757*cdf0e10cSrcweir // ** * 758*cdf0e10cSrcweir // ----*2--------3 * 759*cdf0e10cSrcweir // | ** |* 760*cdf0e10cSrcweir // 1* 4 761*cdf0e10cSrcweir // **| *| 762*cdf0e10cSrcweir // ** | * | 763*cdf0e10cSrcweir // **| * | 764*cdf0e10cSrcweir // 7* * | 765*cdf0e10cSrcweir // --*6-----5----- 766*cdf0e10cSrcweir // ** * 767*cdf0e10cSrcweir // ** 768*cdf0e10cSrcweir // 769*cdf0e10cSrcweir // As we need to scissor all triangles against the 770*cdf0e10cSrcweir // output rectangle we employ an output buffer for the 771*cdf0e10cSrcweir // resulting vertices. the question is how large this 772*cdf0e10cSrcweir // buffer needs to be compared to the number of 773*cdf0e10cSrcweir // incoming vertices. this buffer needs to hold at 774*cdf0e10cSrcweir // most the number of original vertices times '7'. see 775*cdf0e10cSrcweir // figure above for an example. scissoring triangles 776*cdf0e10cSrcweir // with the cohen-sutherland line clipping algorithm 777*cdf0e10cSrcweir // as implemented here will result in a triangle fan 778*cdf0e10cSrcweir // which will be rendered as separate triangles to 779*cdf0e10cSrcweir // avoid pipeline stalls for each scissored 780*cdf0e10cSrcweir // triangle. creating separate triangles from a 781*cdf0e10cSrcweir // triangle fan produces (n-2)*3 vertices where n is 782*cdf0e10cSrcweir // the number of vertices of the original triangle 783*cdf0e10cSrcweir // fan. for the maximum number of 7 vertices of 784*cdf0e10cSrcweir // resulting triangle fans we therefore need 15 times 785*cdf0e10cSrcweir // the number of original vertices. 786*cdf0e10cSrcweir // 787*cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////// 788*cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////// 789*cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////// 790*cdf0e10cSrcweir 791*cdf0e10cSrcweir //const size_t nBufferSize = sizeof(vertex)*(nVertexCount*16); 792*cdf0e10cSrcweir //vertex *pVertices = (vertex*)alloca(nBufferSize); 793*cdf0e10cSrcweir //sal_uInt32 nNumOutput = 0; 794*cdf0e10cSrcweir 795*cdf0e10cSrcweir // we need to clip this triangle against the output rectangle 796*cdf0e10cSrcweir // to ensure that the resulting texture coordinates are in 797*cdf0e10cSrcweir // the valid range from [0<=st<=1]. under normal circustances 798*cdf0e10cSrcweir // we could use the BORDERCOLOR renderstate but some cards 799*cdf0e10cSrcweir // seem to ignore this feature. 800*cdf0e10cSrcweir ::basegfx::B2DPoint stack[3]; 801*cdf0e10cSrcweir unsigned int clipflag = 0; 802*cdf0e10cSrcweir 803*cdf0e10cSrcweir for(sal_uInt32 nIndex=0; nIndex<nVertexCount; ++nIndex) 804*cdf0e10cSrcweir { 805*cdf0e10cSrcweir // rotate stack 806*cdf0e10cSrcweir stack[0] = stack[1]; 807*cdf0e10cSrcweir stack[1] = stack[2]; 808*cdf0e10cSrcweir stack[2] = rCandidate.getB2DPoint(nIndex); 809*cdf0e10cSrcweir 810*cdf0e10cSrcweir // clipping judgement 811*cdf0e10cSrcweir clipflag |= !(rRange.isInside(stack[2])); 812*cdf0e10cSrcweir 813*cdf0e10cSrcweir if(nIndex > 1) 814*cdf0e10cSrcweir { 815*cdf0e10cSrcweir // consume vertices until a single seperate triangle has been visited. 816*cdf0e10cSrcweir if(!((nIndex+1)%3)) 817*cdf0e10cSrcweir { 818*cdf0e10cSrcweir // if any of the last three vertices was outside 819*cdf0e10cSrcweir // we need to scissor against the destination rectangle 820*cdf0e10cSrcweir if(clipflag & 7) 821*cdf0e10cSrcweir { 822*cdf0e10cSrcweir ::basegfx::B2DPoint buf0[16]; 823*cdf0e10cSrcweir ::basegfx::B2DPoint buf1[16]; 824*cdf0e10cSrcweir 825*cdf0e10cSrcweir sal_uInt32 vertex_count = 3; 826*cdf0e10cSrcweir 827*cdf0e10cSrcweir // clip against all 4 planes passing the result of 828*cdf0e10cSrcweir // each plane as the input to the next using a double buffer 829*cdf0e10cSrcweir vertex_count = scissorLineSegment(stack,vertex_count,buf1,&sp[0],rRange); 830*cdf0e10cSrcweir vertex_count = scissorLineSegment(buf1,vertex_count,buf0,&sp[1],rRange); 831*cdf0e10cSrcweir vertex_count = scissorLineSegment(buf0,vertex_count,buf1,&sp[2],rRange); 832*cdf0e10cSrcweir vertex_count = scissorLineSegment(buf1,vertex_count,buf0,&sp[3],rRange); 833*cdf0e10cSrcweir 834*cdf0e10cSrcweir if(vertex_count >= 3) 835*cdf0e10cSrcweir { 836*cdf0e10cSrcweir // convert triangle fan back to triangle list. 837*cdf0e10cSrcweir ::basegfx::B2DPoint v0(buf0[0]); 838*cdf0e10cSrcweir ::basegfx::B2DPoint v1(buf0[1]); 839*cdf0e10cSrcweir for(sal_uInt32 i=2; i<vertex_count; ++i) 840*cdf0e10cSrcweir { 841*cdf0e10cSrcweir ::basegfx::B2DPoint v2(buf0[i]); 842*cdf0e10cSrcweir aResult.append(v0); 843*cdf0e10cSrcweir aResult.append(v1); 844*cdf0e10cSrcweir aResult.append(v2); 845*cdf0e10cSrcweir v1 = v2; 846*cdf0e10cSrcweir } 847*cdf0e10cSrcweir } 848*cdf0e10cSrcweir } 849*cdf0e10cSrcweir else 850*cdf0e10cSrcweir { 851*cdf0e10cSrcweir // the last triangle has not been altered, simply copy to result 852*cdf0e10cSrcweir for(sal_uInt32 i=0; i<3; ++i) 853*cdf0e10cSrcweir aResult.append(stack[i]); 854*cdf0e10cSrcweir } 855*cdf0e10cSrcweir } 856*cdf0e10cSrcweir } 857*cdf0e10cSrcweir 858*cdf0e10cSrcweir clipflag <<= 1; 859*cdf0e10cSrcweir } 860*cdf0e10cSrcweir } 861*cdf0e10cSrcweir } 862*cdf0e10cSrcweir 863*cdf0e10cSrcweir return aResult; 864*cdf0e10cSrcweir } 865*cdf0e10cSrcweir 866*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 867*cdf0e10cSrcweir 868*cdf0e10cSrcweir } // end of namespace tools 869*cdf0e10cSrcweir } // end of namespace basegfx 870*cdf0e10cSrcweir 871*cdf0e10cSrcweir ////////////////////////////////////////////////////////////////////////////// 872*cdf0e10cSrcweir 873*cdf0e10cSrcweir // eof 874