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