1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: b3dpolygonclipper.cxx,v $
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 //////////////////////////////////////////////////////////////////////////////
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
);
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()));
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 //////////////////////////////////////////////////////////////////////////////
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.
154 aRetval
.append(rCandidate
);
157 else if(B3DORIENTATION_X
== ePlaneOrthogonal
&& fTools::lessOrEqual(aCandidateRange
.getMaxX(), fPlaneOffset
))
159 // completely below and on the clip plane.
163 aRetval
.append(rCandidate
);
166 else if(B3DORIENTATION_Y
== ePlaneOrthogonal
&& fTools::moreOrEqual(aCandidateRange
.getMinY(), fPlaneOffset
))
168 // completely above and on the clip plane.
172 aRetval
.append(rCandidate
);
175 else if(B3DORIENTATION_Y
== ePlaneOrthogonal
&& fTools::lessOrEqual(aCandidateRange
.getMaxY(), fPlaneOffset
))
177 // completely below and on the clip plane.
181 aRetval
.append(rCandidate
);
184 else if(B3DORIENTATION_Z
== ePlaneOrthogonal
&& fTools::moreOrEqual(aCandidateRange
.getMinZ(), fPlaneOffset
))
186 // completely above and on the clip plane.
190 aRetval
.append(rCandidate
);
193 else if(B3DORIENTATION_Z
== ePlaneOrthogonal
&& fTools::lessOrEqual(aCandidateRange
.getMaxZ(), fPlaneOffset
))
195 // completely below and on the clip plane.
199 aRetval
.append(rCandidate
);
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
);
213 impAppendCopy(aNewPolygon
, rCandidate
, 0L);
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
231 // entering, finish existing and start new line polygon
232 if(aNewPolygon
.count() > 1L)
234 aRetval
.append(aNewPolygon
);
240 // calculate and add cut point
241 const double fCut(impGetCut(aCurrent
, aNext
, fPlaneOffset
, ePlaneOrthogonal
));
242 impAppendInterpolate(aNewPolygon
, rCandidate
, a
, nNextIndex
, fCut
);
245 bCurrentInside
= bNextInside
;
250 impAppendCopy(aNewPolygon
, rCandidate
, nNextIndex
);
257 if(aNewPolygon
.count() > 1L)
259 aRetval
.append(aNewPolygon
);
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
);
279 bCurrentInside
= bNextInside
;
282 if(bNextInside
&& nNextIndex
)
284 impAppendCopy(aNewPolygon
, rCandidate
, nNextIndex
);
291 if(aNewPolygon
.count() > 2L)
293 aNewPolygon
.setClosed(true);
294 aRetval
.append(aNewPolygon
);
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
));
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
));
327 B3DPolyPolygon
clipPolygonOnRange(const B3DPolygon
& rCandidate
, const B2DRange
& rRange
, bool bInside
, bool bStroke
)
329 B3DPolyPolygon aRetval
;
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.
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.
360 aRetval
.append(rCandidate
);
365 // clip against the six planes of the range
367 aRetval
= clipPolygonOnOrthogonalPlane(rCandidate
, tools::B3DORIENTATION_X
, bInside
, rRange
.getMinX(), bStroke
);
372 if(1L == aRetval
.count())
374 aRetval
= clipPolygonOnOrthogonalPlane(aRetval
.getB3DPolygon(0L), tools::B3DORIENTATION_Y
, bInside
, rRange
.getMinY(), bStroke
);
378 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_Y
, bInside
, rRange
.getMinY(), bStroke
);
384 if(1L == aRetval
.count())
386 aRetval
= clipPolygonOnOrthogonalPlane(aRetval
.getB3DPolygon(0L), tools::B3DORIENTATION_X
, !bInside
, rRange
.getMaxX(), bStroke
);
390 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_X
, !bInside
, rRange
.getMaxX(), bStroke
);
396 if(1L == aRetval
.count())
398 aRetval
= clipPolygonOnOrthogonalPlane(aRetval
.getB3DPolygon(0L), tools::B3DORIENTATION_Y
, !bInside
, rRange
.getMaxY(), bStroke
);
402 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_Y
, !bInside
, rRange
.getMaxY(), bStroke
);
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
));
425 B3DPolyPolygon
clipPolygonOnRange(const B3DPolygon
& rCandidate
, const B3DRange
& rRange
, bool bInside
, bool bStroke
)
427 B3DPolyPolygon aRetval
;
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.
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.
455 aRetval
.append(rCandidate
);
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
);
467 if(1L == aRetval
.count())
469 aRetval
= clipPolygonOnOrthogonalPlane(aRetval
.getB3DPolygon(0L), tools::B3DORIENTATION_Z
, bInside
, rRange
.getMinZ(), bStroke
);
473 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_Z
, bInside
, rRange
.getMinZ(), bStroke
);
479 if(1L == aRetval
.count())
481 aRetval
= clipPolygonOnOrthogonalPlane(aRetval
.getB3DPolygon(0L), tools::B3DORIENTATION_Z
, !bInside
, rRange
.getMaxZ(), bStroke
);
485 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_Z
, !bInside
, rRange
.getMaxZ(), bStroke
);
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
);
521 aRetval
= clipPolygonOnOrthogonalPlane(aCandidate
, tools::B3DORIENTATION_X
, bClipPositive
, 0.0, bStroke
);
525 // if there is a result, it needs to be transformed back
526 aMatrixTransform
.invert();
527 aRetval
.transform(aMatrixTransform
);
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
);
560 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_X
, bClipPositive
, 0.0, bStroke
);
564 // if there is a result, it needs to be transformed back
565 aMatrixTransform
.invert();
566 aRetval
.transform(aMatrixTransform
);
573 } // end of namespace tools
574 } // end of namespace basegfx
576 //////////////////////////////////////////////////////////////////////////////