Update ooo320-m1
[ooovba.git] / basegfx / source / polygon / b3dpolygonclipper.cxx
blobb3c5af662e0632350dd4ba037b965531fc45b447
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: b3dpolygonclipper.cxx,v $
11 * $Revision: 1.2 $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_basegfx.hxx"
35 #include <basegfx/polygon/b3dpolygonclipper.hxx>
36 #include <osl/diagnose.h>
37 #include <basegfx/polygon/b3dpolygontools.hxx>
38 #include <basegfx/numeric/ftools.hxx>
39 #include <basegfx/matrix/b3dhommatrix.hxx>
40 #include <basegfx/polygon/b3dpolygontools.hxx>
41 #include <basegfx/range/b3drange.hxx>
42 #include <basegfx/point/b2dpoint.hxx>
43 #include <basegfx/range/b2drange.hxx>
44 #include <basegfx/color/bcolor.hxx>
46 //////////////////////////////////////////////////////////////////////////////
48 namespace basegfx
50 namespace
52 inline bool impIsInside(const B3DPoint& rCandidate, double fPlaneOffset, tools::B3DOrientation ePlaneOrthogonal)
54 if(tools::B3DORIENTATION_X == ePlaneOrthogonal)
56 return fTools::moreOrEqual(rCandidate.getX(), fPlaneOffset);
58 else if(tools::B3DORIENTATION_Y == ePlaneOrthogonal)
60 return fTools::moreOrEqual(rCandidate.getY(), fPlaneOffset);
62 else
64 return fTools::moreOrEqual(rCandidate.getZ(), fPlaneOffset);
68 inline double impGetCut(const B3DPoint& rCurrent, const B3DPoint& rNext, double fPlaneOffset, tools::B3DOrientation ePlaneOrthogonal)
70 if(tools::B3DORIENTATION_X == ePlaneOrthogonal)
72 return ((fPlaneOffset - rCurrent.getX())/(rNext.getX() - rCurrent.getX()));
74 else if(tools::B3DORIENTATION_Y == ePlaneOrthogonal)
76 return ((fPlaneOffset - rCurrent.getY())/(rNext.getY() - rCurrent.getY()));
78 else
80 return ((fPlaneOffset - rCurrent.getZ())/(rNext.getZ() - rCurrent.getZ()));
84 void impAppendCopy(B3DPolygon& rDest, const B3DPolygon& rSource, sal_uInt32 nIndex)
86 rDest.append(rSource.getB3DPoint(nIndex));
88 if(rSource.areBColorsUsed())
90 rDest.setBColor(rDest.count() - 1L, rSource.getBColor(nIndex));
93 if(rSource.areNormalsUsed())
95 rDest.setNormal(rDest.count() - 1L, rSource.getNormal(nIndex));
98 if(rSource.areTextureCoordinatesUsed())
100 rDest.setTextureCoordinate(rDest.count() - 1L, rSource.getTextureCoordinate(nIndex));
104 void impAppendInterpolate(B3DPolygon& rDest, const B3DPolygon& rSource, sal_uInt32 nIndA, sal_uInt32 nIndB, double fCut)
106 const B3DPoint aCurrPoint(rSource.getB3DPoint(nIndA));
107 const B3DPoint aNextPoint(rSource.getB3DPoint(nIndB));
108 rDest.append(interpolate(aCurrPoint, aNextPoint, fCut));
110 if(rSource.areBColorsUsed())
112 const BColor aCurrBColor(rSource.getBColor(nIndA));
113 const BColor aNextBColor(rSource.getBColor(nIndB));
114 rDest.setBColor(rDest.count() - 1L, interpolate(aCurrBColor, aNextBColor, fCut));
117 if(rSource.areNormalsUsed())
119 const B3DVector aCurrVector(rSource.getNormal(nIndA));
120 const B3DVector aNextVector(rSource.getNormal(nIndB));
121 rDest.setNormal(rDest.count() - 1L, interpolate(aCurrVector, aNextVector, fCut));
124 if(rSource.areTextureCoordinatesUsed())
126 const B2DPoint aCurrTxCo(rSource.getTextureCoordinate(nIndA));
127 const B2DPoint aNextTxCo(rSource.getTextureCoordinate(nIndB));
128 rDest.setTextureCoordinate(rDest.count() - 1L, interpolate(aCurrTxCo, aNextTxCo, fCut));
132 } // end of namespace basegfx
134 //////////////////////////////////////////////////////////////////////////////
136 namespace basegfx
138 namespace tools
140 B3DPolyPolygon clipPolygonOnOrthogonalPlane(const B3DPolygon& rCandidate, B3DOrientation ePlaneOrthogonal, bool bClipPositive, double fPlaneOffset, bool bStroke)
142 B3DPolyPolygon aRetval;
144 if(rCandidate.count())
146 const B3DRange aCandidateRange(getRange(rCandidate));
148 if(B3DORIENTATION_X == ePlaneOrthogonal && fTools::moreOrEqual(aCandidateRange.getMinX(), fPlaneOffset))
150 // completely above and on the clip plane.
151 if(bClipPositive)
153 // add completely
154 aRetval.append(rCandidate);
157 else if(B3DORIENTATION_X == ePlaneOrthogonal && fTools::lessOrEqual(aCandidateRange.getMaxX(), fPlaneOffset))
159 // completely below and on the clip plane.
160 if(!bClipPositive)
162 // add completely
163 aRetval.append(rCandidate);
166 else if(B3DORIENTATION_Y == ePlaneOrthogonal && fTools::moreOrEqual(aCandidateRange.getMinY(), fPlaneOffset))
168 // completely above and on the clip plane.
169 if(bClipPositive)
171 // add completely
172 aRetval.append(rCandidate);
175 else if(B3DORIENTATION_Y == ePlaneOrthogonal && fTools::lessOrEqual(aCandidateRange.getMaxY(), fPlaneOffset))
177 // completely below and on the clip plane.
178 if(!bClipPositive)
180 // add completely
181 aRetval.append(rCandidate);
184 else if(B3DORIENTATION_Z == ePlaneOrthogonal && fTools::moreOrEqual(aCandidateRange.getMinZ(), fPlaneOffset))
186 // completely above and on the clip plane.
187 if(bClipPositive)
189 // add completely
190 aRetval.append(rCandidate);
193 else if(B3DORIENTATION_Z == ePlaneOrthogonal && fTools::lessOrEqual(aCandidateRange.getMaxZ(), fPlaneOffset))
195 // completely below and on the clip plane.
196 if(!bClipPositive)
198 // add completely
199 aRetval.append(rCandidate);
202 else
204 // prepare loop(s)
205 B3DPolygon aNewPolygon;
206 B3DPoint aCurrent(rCandidate.getB3DPoint(0L));
207 const sal_uInt32 nPointCount(rCandidate.count());
208 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
209 bool bCurrentInside(impIsInside(aCurrent, fPlaneOffset, ePlaneOrthogonal) == bClipPositive);
211 if(bCurrentInside)
213 impAppendCopy(aNewPolygon, rCandidate, 0L);
216 if(bStroke)
218 // open polygon, create clipped line snippets.
219 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
221 // get next point data
222 const sal_uInt32 nNextIndex((a + 1L == nPointCount) ? 0L : a + 1L);
223 const B3DPoint aNext(rCandidate.getB3DPoint(nNextIndex));
224 const bool bNextInside(impIsInside(aNext, fPlaneOffset, ePlaneOrthogonal) == bClipPositive);
226 if(bCurrentInside != bNextInside)
228 // change inside/outside
229 if(bNextInside)
231 // entering, finish existing and start new line polygon
232 if(aNewPolygon.count() > 1L)
234 aRetval.append(aNewPolygon);
237 aNewPolygon.clear();
240 // calculate and add cut point
241 const double fCut(impGetCut(aCurrent, aNext, fPlaneOffset, ePlaneOrthogonal));
242 impAppendInterpolate(aNewPolygon, rCandidate, a, nNextIndex, fCut);
244 // pepare next step
245 bCurrentInside = bNextInside;
248 if(bNextInside)
250 impAppendCopy(aNewPolygon, rCandidate, nNextIndex);
253 // pepare next step
254 aCurrent = aNext;
257 if(aNewPolygon.count() > 1L)
259 aRetval.append(aNewPolygon);
262 else
264 // closed polygon, create single clipped closed polygon
265 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
267 // get next point data, use offset
268 const sal_uInt32 nNextIndex((a + 1L == nPointCount) ? 0L : a + 1L);
269 const B3DPoint aNext(rCandidate.getB3DPoint(nNextIndex));
270 const bool bNextInside(impIsInside(aNext, fPlaneOffset, ePlaneOrthogonal) == bClipPositive);
272 if(bCurrentInside != bNextInside)
274 // calculate and add cut point
275 const double fCut(impGetCut(aCurrent, aNext, fPlaneOffset, ePlaneOrthogonal));
276 impAppendInterpolate(aNewPolygon, rCandidate, a, nNextIndex, fCut);
278 // pepare next step
279 bCurrentInside = bNextInside;
282 if(bNextInside && nNextIndex)
284 impAppendCopy(aNewPolygon, rCandidate, nNextIndex);
287 // pepare next step
288 aCurrent = aNext;
291 if(aNewPolygon.count() > 2L)
293 aNewPolygon.setClosed(true);
294 aRetval.append(aNewPolygon);
300 return aRetval;
303 B3DPolyPolygon clipPolyPolygonOnOrthogonalPlane(const B3DPolyPolygon& rCandidate, B3DOrientation ePlaneOrthogonal, bool bClipPositive, double fPlaneOffset, bool bStroke)
305 B3DPolyPolygon aRetval;
307 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
309 aRetval.append(clipPolygonOnOrthogonalPlane(rCandidate.getB3DPolygon(a), ePlaneOrthogonal, bClipPositive, fPlaneOffset, bStroke));
312 return aRetval;
315 B3DPolyPolygon clipPolyPolygonOnRange(const B3DPolyPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke)
317 B3DPolyPolygon aRetval;
319 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
321 aRetval.append(clipPolygonOnRange(rCandidate.getB3DPolygon(a), rRange, bInside, bStroke));
324 return aRetval;
327 B3DPolyPolygon clipPolygonOnRange(const B3DPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke)
329 B3DPolyPolygon aRetval;
331 if(rRange.isEmpty())
333 // clipping against an empty range. Nothing is inside an empty range, so the polygon
334 // is outside the range. So only return if not inside is wanted
335 if(!bInside && rCandidate.count())
337 aRetval.append(rCandidate);
340 else if(rCandidate.count())
342 const B3DRange aCandidateRange3D(getRange(rCandidate));
343 const B2DRange aCandidateRange(
344 aCandidateRange3D.getMinX(), aCandidateRange3D.getMinY(),
345 aCandidateRange3D.getMaxX(), aCandidateRange3D.getMaxY());
347 if(rRange.isInside(aCandidateRange))
349 // candidate is completely inside given range, nothing to do. Is also true with curves.
350 if(bInside)
352 aRetval.append(rCandidate);
355 else if(!rRange.overlaps(aCandidateRange))
357 // candidate is completely outside given range, nothing to do. Is also true with curves.
358 if(!bInside)
360 aRetval.append(rCandidate);
363 else
365 // clip against the six planes of the range
366 // against lower X
367 aRetval = clipPolygonOnOrthogonalPlane(rCandidate, tools::B3DORIENTATION_X, bInside, rRange.getMinX(), bStroke);
369 if(aRetval.count())
371 // against lower Y
372 if(1L == aRetval.count())
374 aRetval = clipPolygonOnOrthogonalPlane(aRetval.getB3DPolygon(0L), tools::B3DORIENTATION_Y, bInside, rRange.getMinY(), bStroke);
376 else
378 aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_Y, bInside, rRange.getMinY(), bStroke);
381 if(aRetval.count())
383 // against higher X
384 if(1L == aRetval.count())
386 aRetval = clipPolygonOnOrthogonalPlane(aRetval.getB3DPolygon(0L), tools::B3DORIENTATION_X, !bInside, rRange.getMaxX(), bStroke);
388 else
390 aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_X, !bInside, rRange.getMaxX(), bStroke);
393 if(aRetval.count())
395 // against higher Y
396 if(1L == aRetval.count())
398 aRetval = clipPolygonOnOrthogonalPlane(aRetval.getB3DPolygon(0L), tools::B3DORIENTATION_Y, !bInside, rRange.getMaxY(), bStroke);
400 else
402 aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_Y, !bInside, rRange.getMaxY(), bStroke);
410 return aRetval;
413 B3DPolyPolygon clipPolyPolygonOnRange(const B3DPolyPolygon& rCandidate, const B3DRange& rRange, bool bInside, bool bStroke)
415 B3DPolyPolygon aRetval;
417 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
419 aRetval.append(clipPolygonOnRange(rCandidate.getB3DPolygon(a), rRange, bInside, bStroke));
422 return aRetval;
425 B3DPolyPolygon clipPolygonOnRange(const B3DPolygon& rCandidate, const B3DRange& rRange, bool bInside, bool bStroke)
427 B3DPolyPolygon aRetval;
429 if(rRange.isEmpty())
431 // clipping against an empty range. Nothing is inside an empty range, so the polygon
432 // is outside the range. So only return if not inside is wanted
433 if(!bInside && rCandidate.count())
435 aRetval.append(rCandidate);
438 else if(rCandidate.count())
440 const B3DRange aCandidateRange(getRange(rCandidate));
442 if(rRange.isInside(aCandidateRange))
444 // candidate is completely inside given range, nothing to do. Is also true with curves.
445 if(bInside)
447 aRetval.append(rCandidate);
450 else if(!rRange.overlaps(aCandidateRange))
452 // candidate is completely outside given range, nothing to do. Is also true with curves.
453 if(!bInside)
455 aRetval.append(rCandidate);
458 else
460 // clip against X,Y first and see if there's something left
461 const B2DRange aCandidateRange2D(rRange.getMinX(), rRange.getMinY(), rRange.getMaxX(), rRange.getMaxY());
462 aRetval = clipPolygonOnRange(rCandidate, aCandidateRange2D, bInside, bStroke);
464 if(aRetval.count())
466 // against lower Z
467 if(1L == aRetval.count())
469 aRetval = clipPolygonOnOrthogonalPlane(aRetval.getB3DPolygon(0L), tools::B3DORIENTATION_Z, bInside, rRange.getMinZ(), bStroke);
471 else
473 aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_Z, bInside, rRange.getMinZ(), bStroke);
476 if(aRetval.count())
478 // against higher Z
479 if(1L == aRetval.count())
481 aRetval = clipPolygonOnOrthogonalPlane(aRetval.getB3DPolygon(0L), tools::B3DORIENTATION_Z, !bInside, rRange.getMaxZ(), bStroke);
483 else
485 aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_Z, !bInside, rRange.getMaxZ(), bStroke);
492 return aRetval;
495 B3DPolyPolygon clipPolygonOnPlane(const B3DPolygon& rCandidate, const B3DPoint& rPointOnPlane, const B3DVector& rPlaneNormal, bool bClipPositive, bool bStroke)
497 B3DPolyPolygon aRetval;
499 if(rPlaneNormal.equalZero())
501 // not really a plane definition, return polygon
502 aRetval.append(rCandidate);
504 else if(rCandidate.count())
506 // build transform to project planeNormal on X-Axis and pointOnPlane to null point
507 B3DHomMatrix aMatrixTransform;
508 aMatrixTransform.translate(-rPointOnPlane.getX(), -rPointOnPlane.getY(), -rPointOnPlane.getZ());
509 const double fRotInXY(atan2(rPlaneNormal.getY(), rPlaneNormal.getX()));
510 const double fRotInXZ(atan2(-rPlaneNormal.getZ(), rPlaneNormal.getXYLength()));
511 if(!fTools::equalZero(fRotInXY) || !fTools::equalZero(fRotInXZ))
513 aMatrixTransform.rotate(0.0, fRotInXZ, fRotInXY);
516 // transform polygon to clip scenario
517 B3DPolygon aCandidate(rCandidate);
518 aCandidate.transform(aMatrixTransform);
520 // clip on YZ plane
521 aRetval = clipPolygonOnOrthogonalPlane(aCandidate, tools::B3DORIENTATION_X, bClipPositive, 0.0, bStroke);
523 if(aRetval.count())
525 // if there is a result, it needs to be transformed back
526 aMatrixTransform.invert();
527 aRetval.transform(aMatrixTransform);
531 return aRetval;
534 B3DPolyPolygon clipPolyPolygonOnPlane(const B3DPolyPolygon& rCandidate, const B3DPoint& rPointOnPlane, const B3DVector& rPlaneNormal, bool bClipPositive, bool bStroke)
536 B3DPolyPolygon aRetval;
538 if(rPlaneNormal.equalZero())
540 // not really a plane definition, return polygon
541 aRetval = rCandidate;
543 else if(rCandidate.count())
545 // build transform to project planeNormal on X-Axis and pointOnPlane to null point
546 B3DHomMatrix aMatrixTransform;
547 aMatrixTransform.translate(-rPointOnPlane.getX(), -rPointOnPlane.getY(), -rPointOnPlane.getZ());
548 const double fRotInXY(atan2(rPlaneNormal.getY(), rPlaneNormal.getX()));
549 const double fRotInXZ(atan2(-rPlaneNormal.getZ(), rPlaneNormal.getXYLength()));
550 if(!fTools::equalZero(fRotInXY) || !fTools::equalZero(fRotInXZ))
552 aMatrixTransform.rotate(0.0, fRotInXZ, fRotInXY);
555 // transform polygon to clip scenario
556 aRetval = rCandidate;
557 aRetval.transform(aMatrixTransform);
559 // clip on YZ plane
560 aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_X, bClipPositive, 0.0, bStroke);
562 if(aRetval.count())
564 // if there is a result, it needs to be transformed back
565 aMatrixTransform.invert();
566 aRetval.transform(aMatrixTransform);
570 return aRetval;
573 } // end of namespace tools
574 } // end of namespace basegfx
576 //////////////////////////////////////////////////////////////////////////////
578 // eof