1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basegfx.hxx"
31 #include <basegfx/polygon/b3dpolygonclipper.hxx>
32 #include <osl/diagnose.h>
33 #include <basegfx/polygon/b3dpolygontools.hxx>
34 #include <basegfx/numeric/ftools.hxx>
35 #include <basegfx/matrix/b3dhommatrix.hxx>
36 #include <basegfx/polygon/b3dpolygontools.hxx>
37 #include <basegfx/range/b3drange.hxx>
38 #include <basegfx/point/b2dpoint.hxx>
39 #include <basegfx/range/b2drange.hxx>
40 #include <basegfx/color/bcolor.hxx>
42 //////////////////////////////////////////////////////////////////////////////
48 inline bool impIsInside(const B3DPoint
& rCandidate
, double fPlaneOffset
, tools::B3DOrientation ePlaneOrthogonal
)
50 if(tools::B3DORIENTATION_X
== ePlaneOrthogonal
)
52 return fTools::moreOrEqual(rCandidate
.getX(), fPlaneOffset
);
54 else if(tools::B3DORIENTATION_Y
== ePlaneOrthogonal
)
56 return fTools::moreOrEqual(rCandidate
.getY(), fPlaneOffset
);
60 return fTools::moreOrEqual(rCandidate
.getZ(), fPlaneOffset
);
64 inline double impGetCut(const B3DPoint
& rCurrent
, const B3DPoint
& rNext
, double fPlaneOffset
, tools::B3DOrientation ePlaneOrthogonal
)
66 if(tools::B3DORIENTATION_X
== ePlaneOrthogonal
)
68 return ((fPlaneOffset
- rCurrent
.getX())/(rNext
.getX() - rCurrent
.getX()));
70 else if(tools::B3DORIENTATION_Y
== ePlaneOrthogonal
)
72 return ((fPlaneOffset
- rCurrent
.getY())/(rNext
.getY() - rCurrent
.getY()));
76 return ((fPlaneOffset
- rCurrent
.getZ())/(rNext
.getZ() - rCurrent
.getZ()));
80 void impAppendCopy(B3DPolygon
& rDest
, const B3DPolygon
& rSource
, sal_uInt32 nIndex
)
82 rDest
.append(rSource
.getB3DPoint(nIndex
));
84 if(rSource
.areBColorsUsed())
86 rDest
.setBColor(rDest
.count() - 1L, rSource
.getBColor(nIndex
));
89 if(rSource
.areNormalsUsed())
91 rDest
.setNormal(rDest
.count() - 1L, rSource
.getNormal(nIndex
));
94 if(rSource
.areTextureCoordinatesUsed())
96 rDest
.setTextureCoordinate(rDest
.count() - 1L, rSource
.getTextureCoordinate(nIndex
));
100 void impAppendInterpolate(B3DPolygon
& rDest
, const B3DPolygon
& rSource
, sal_uInt32 nIndA
, sal_uInt32 nIndB
, double fCut
)
102 const B3DPoint
aCurrPoint(rSource
.getB3DPoint(nIndA
));
103 const B3DPoint
aNextPoint(rSource
.getB3DPoint(nIndB
));
104 rDest
.append(interpolate(aCurrPoint
, aNextPoint
, fCut
));
106 if(rSource
.areBColorsUsed())
108 const BColor
aCurrBColor(rSource
.getBColor(nIndA
));
109 const BColor
aNextBColor(rSource
.getBColor(nIndB
));
110 rDest
.setBColor(rDest
.count() - 1L, interpolate(aCurrBColor
, aNextBColor
, fCut
));
113 if(rSource
.areNormalsUsed())
115 const B3DVector
aCurrVector(rSource
.getNormal(nIndA
));
116 const B3DVector
aNextVector(rSource
.getNormal(nIndB
));
117 rDest
.setNormal(rDest
.count() - 1L, interpolate(aCurrVector
, aNextVector
, fCut
));
120 if(rSource
.areTextureCoordinatesUsed())
122 const B2DPoint
aCurrTxCo(rSource
.getTextureCoordinate(nIndA
));
123 const B2DPoint
aNextTxCo(rSource
.getTextureCoordinate(nIndB
));
124 rDest
.setTextureCoordinate(rDest
.count() - 1L, interpolate(aCurrTxCo
, aNextTxCo
, fCut
));
128 } // end of namespace basegfx
130 //////////////////////////////////////////////////////////////////////////////
136 B3DPolyPolygon
clipPolygonOnOrthogonalPlane(const B3DPolygon
& rCandidate
, B3DOrientation ePlaneOrthogonal
, bool bClipPositive
, double fPlaneOffset
, bool bStroke
)
138 B3DPolyPolygon aRetval
;
140 if(rCandidate
.count())
142 const B3DRange
aCandidateRange(getRange(rCandidate
));
144 if(B3DORIENTATION_X
== ePlaneOrthogonal
&& fTools::moreOrEqual(aCandidateRange
.getMinX(), fPlaneOffset
))
146 // completely above and on the clip plane.
150 aRetval
.append(rCandidate
);
153 else if(B3DORIENTATION_X
== ePlaneOrthogonal
&& fTools::lessOrEqual(aCandidateRange
.getMaxX(), fPlaneOffset
))
155 // completely below and on the clip plane.
159 aRetval
.append(rCandidate
);
162 else if(B3DORIENTATION_Y
== ePlaneOrthogonal
&& fTools::moreOrEqual(aCandidateRange
.getMinY(), fPlaneOffset
))
164 // completely above and on the clip plane.
168 aRetval
.append(rCandidate
);
171 else if(B3DORIENTATION_Y
== ePlaneOrthogonal
&& fTools::lessOrEqual(aCandidateRange
.getMaxY(), fPlaneOffset
))
173 // completely below and on the clip plane.
177 aRetval
.append(rCandidate
);
180 else if(B3DORIENTATION_Z
== ePlaneOrthogonal
&& fTools::moreOrEqual(aCandidateRange
.getMinZ(), fPlaneOffset
))
182 // completely above and on the clip plane.
186 aRetval
.append(rCandidate
);
189 else if(B3DORIENTATION_Z
== ePlaneOrthogonal
&& fTools::lessOrEqual(aCandidateRange
.getMaxZ(), fPlaneOffset
))
191 // completely below and on the clip plane.
195 aRetval
.append(rCandidate
);
201 B3DPolygon aNewPolygon
;
202 B3DPoint
aCurrent(rCandidate
.getB3DPoint(0L));
203 const sal_uInt32
nPointCount(rCandidate
.count());
204 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
205 bool bCurrentInside(impIsInside(aCurrent
, fPlaneOffset
, ePlaneOrthogonal
) == bClipPositive
);
209 impAppendCopy(aNewPolygon
, rCandidate
, 0L);
214 // open polygon, create clipped line snippets.
215 for(sal_uInt32
a(0L); a
< nEdgeCount
; a
++)
217 // get next point data
218 const sal_uInt32
nNextIndex((a
+ 1L == nPointCount
) ? 0L : a
+ 1L);
219 const B3DPoint
aNext(rCandidate
.getB3DPoint(nNextIndex
));
220 const bool bNextInside(impIsInside(aNext
, fPlaneOffset
, ePlaneOrthogonal
) == bClipPositive
);
222 if(bCurrentInside
!= bNextInside
)
224 // change inside/outside
227 // entering, finish existing and start new line polygon
228 if(aNewPolygon
.count() > 1L)
230 aRetval
.append(aNewPolygon
);
236 // calculate and add cut point
237 const double fCut(impGetCut(aCurrent
, aNext
, fPlaneOffset
, ePlaneOrthogonal
));
238 impAppendInterpolate(aNewPolygon
, rCandidate
, a
, nNextIndex
, fCut
);
241 bCurrentInside
= bNextInside
;
246 impAppendCopy(aNewPolygon
, rCandidate
, nNextIndex
);
253 if(aNewPolygon
.count() > 1L)
255 aRetval
.append(aNewPolygon
);
260 // closed polygon, create single clipped closed polygon
261 for(sal_uInt32
a(0L); a
< nEdgeCount
; a
++)
263 // get next point data, use offset
264 const sal_uInt32
nNextIndex((a
+ 1L == nPointCount
) ? 0L : a
+ 1L);
265 const B3DPoint
aNext(rCandidate
.getB3DPoint(nNextIndex
));
266 const bool bNextInside(impIsInside(aNext
, fPlaneOffset
, ePlaneOrthogonal
) == bClipPositive
);
268 if(bCurrentInside
!= bNextInside
)
270 // calculate and add cut point
271 const double fCut(impGetCut(aCurrent
, aNext
, fPlaneOffset
, ePlaneOrthogonal
));
272 impAppendInterpolate(aNewPolygon
, rCandidate
, a
, nNextIndex
, fCut
);
275 bCurrentInside
= bNextInside
;
278 if(bNextInside
&& nNextIndex
)
280 impAppendCopy(aNewPolygon
, rCandidate
, nNextIndex
);
287 if(aNewPolygon
.count() > 2L)
289 aNewPolygon
.setClosed(true);
290 aRetval
.append(aNewPolygon
);
299 B3DPolyPolygon
clipPolyPolygonOnOrthogonalPlane(const B3DPolyPolygon
& rCandidate
, B3DOrientation ePlaneOrthogonal
, bool bClipPositive
, double fPlaneOffset
, bool bStroke
)
301 B3DPolyPolygon aRetval
;
303 for(sal_uInt32
a(0L); a
< rCandidate
.count(); a
++)
305 aRetval
.append(clipPolygonOnOrthogonalPlane(rCandidate
.getB3DPolygon(a
), ePlaneOrthogonal
, bClipPositive
, fPlaneOffset
, bStroke
));
311 B3DPolyPolygon
clipPolyPolygonOnRange(const B3DPolyPolygon
& rCandidate
, const B2DRange
& rRange
, bool bInside
, bool bStroke
)
313 B3DPolyPolygon aRetval
;
315 for(sal_uInt32
a(0L); a
< rCandidate
.count(); a
++)
317 aRetval
.append(clipPolygonOnRange(rCandidate
.getB3DPolygon(a
), rRange
, bInside
, bStroke
));
323 B3DPolyPolygon
clipPolygonOnRange(const B3DPolygon
& rCandidate
, const B2DRange
& rRange
, bool bInside
, bool bStroke
)
325 B3DPolyPolygon aRetval
;
329 // clipping against an empty range. Nothing is inside an empty range, so the polygon
330 // is outside the range. So only return if not inside is wanted
331 if(!bInside
&& rCandidate
.count())
333 aRetval
.append(rCandidate
);
336 else if(rCandidate
.count())
338 const B3DRange
aCandidateRange3D(getRange(rCandidate
));
339 const B2DRange
aCandidateRange(
340 aCandidateRange3D
.getMinX(), aCandidateRange3D
.getMinY(),
341 aCandidateRange3D
.getMaxX(), aCandidateRange3D
.getMaxY());
343 if(rRange
.isInside(aCandidateRange
))
345 // candidate is completely inside given range, nothing to do. Is also true with curves.
348 aRetval
.append(rCandidate
);
351 else if(!rRange
.overlaps(aCandidateRange
))
353 // candidate is completely outside given range, nothing to do. Is also true with curves.
356 aRetval
.append(rCandidate
);
361 // clip against the six planes of the range
363 aRetval
= clipPolygonOnOrthogonalPlane(rCandidate
, tools::B3DORIENTATION_X
, bInside
, rRange
.getMinX(), bStroke
);
368 if(1L == aRetval
.count())
370 aRetval
= clipPolygonOnOrthogonalPlane(aRetval
.getB3DPolygon(0L), tools::B3DORIENTATION_Y
, bInside
, rRange
.getMinY(), bStroke
);
374 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_Y
, bInside
, rRange
.getMinY(), bStroke
);
380 if(1L == aRetval
.count())
382 aRetval
= clipPolygonOnOrthogonalPlane(aRetval
.getB3DPolygon(0L), tools::B3DORIENTATION_X
, !bInside
, rRange
.getMaxX(), bStroke
);
386 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_X
, !bInside
, rRange
.getMaxX(), bStroke
);
392 if(1L == aRetval
.count())
394 aRetval
= clipPolygonOnOrthogonalPlane(aRetval
.getB3DPolygon(0L), tools::B3DORIENTATION_Y
, !bInside
, rRange
.getMaxY(), bStroke
);
398 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_Y
, !bInside
, rRange
.getMaxY(), bStroke
);
409 B3DPolyPolygon
clipPolyPolygonOnRange(const B3DPolyPolygon
& rCandidate
, const B3DRange
& rRange
, bool bInside
, bool bStroke
)
411 B3DPolyPolygon aRetval
;
413 for(sal_uInt32
a(0L); a
< rCandidate
.count(); a
++)
415 aRetval
.append(clipPolygonOnRange(rCandidate
.getB3DPolygon(a
), rRange
, bInside
, bStroke
));
421 B3DPolyPolygon
clipPolygonOnRange(const B3DPolygon
& rCandidate
, const B3DRange
& rRange
, bool bInside
, bool bStroke
)
423 B3DPolyPolygon aRetval
;
427 // clipping against an empty range. Nothing is inside an empty range, so the polygon
428 // is outside the range. So only return if not inside is wanted
429 if(!bInside
&& rCandidate
.count())
431 aRetval
.append(rCandidate
);
434 else if(rCandidate
.count())
436 const B3DRange
aCandidateRange(getRange(rCandidate
));
438 if(rRange
.isInside(aCandidateRange
))
440 // candidate is completely inside given range, nothing to do. Is also true with curves.
443 aRetval
.append(rCandidate
);
446 else if(!rRange
.overlaps(aCandidateRange
))
448 // candidate is completely outside given range, nothing to do. Is also true with curves.
451 aRetval
.append(rCandidate
);
456 // clip against X,Y first and see if there's something left
457 const B2DRange
aCandidateRange2D(rRange
.getMinX(), rRange
.getMinY(), rRange
.getMaxX(), rRange
.getMaxY());
458 aRetval
= clipPolygonOnRange(rCandidate
, aCandidateRange2D
, bInside
, bStroke
);
463 if(1L == aRetval
.count())
465 aRetval
= clipPolygonOnOrthogonalPlane(aRetval
.getB3DPolygon(0L), tools::B3DORIENTATION_Z
, bInside
, rRange
.getMinZ(), bStroke
);
469 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_Z
, bInside
, rRange
.getMinZ(), bStroke
);
475 if(1L == aRetval
.count())
477 aRetval
= clipPolygonOnOrthogonalPlane(aRetval
.getB3DPolygon(0L), tools::B3DORIENTATION_Z
, !bInside
, rRange
.getMaxZ(), bStroke
);
481 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_Z
, !bInside
, rRange
.getMaxZ(), bStroke
);
491 B3DPolyPolygon
clipPolygonOnPlane(const B3DPolygon
& rCandidate
, const B3DPoint
& rPointOnPlane
, const B3DVector
& rPlaneNormal
, bool bClipPositive
, bool bStroke
)
493 B3DPolyPolygon aRetval
;
495 if(rPlaneNormal
.equalZero())
497 // not really a plane definition, return polygon
498 aRetval
.append(rCandidate
);
500 else if(rCandidate
.count())
502 // build transform to project planeNormal on X-Axis and pointOnPlane to null point
503 B3DHomMatrix aMatrixTransform
;
504 aMatrixTransform
.translate(-rPointOnPlane
.getX(), -rPointOnPlane
.getY(), -rPointOnPlane
.getZ());
505 const double fRotInXY(atan2(rPlaneNormal
.getY(), rPlaneNormal
.getX()));
506 const double fRotInXZ(atan2(-rPlaneNormal
.getZ(), rPlaneNormal
.getXYLength()));
507 if(!fTools::equalZero(fRotInXY
) || !fTools::equalZero(fRotInXZ
))
509 aMatrixTransform
.rotate(0.0, fRotInXZ
, fRotInXY
);
512 // transform polygon to clip scenario
513 B3DPolygon
aCandidate(rCandidate
);
514 aCandidate
.transform(aMatrixTransform
);
517 aRetval
= clipPolygonOnOrthogonalPlane(aCandidate
, tools::B3DORIENTATION_X
, bClipPositive
, 0.0, bStroke
);
521 // if there is a result, it needs to be transformed back
522 aMatrixTransform
.invert();
523 aRetval
.transform(aMatrixTransform
);
530 B3DPolyPolygon
clipPolyPolygonOnPlane(const B3DPolyPolygon
& rCandidate
, const B3DPoint
& rPointOnPlane
, const B3DVector
& rPlaneNormal
, bool bClipPositive
, bool bStroke
)
532 B3DPolyPolygon aRetval
;
534 if(rPlaneNormal
.equalZero())
536 // not really a plane definition, return polygon
537 aRetval
= rCandidate
;
539 else if(rCandidate
.count())
541 // build transform to project planeNormal on X-Axis and pointOnPlane to null point
542 B3DHomMatrix aMatrixTransform
;
543 aMatrixTransform
.translate(-rPointOnPlane
.getX(), -rPointOnPlane
.getY(), -rPointOnPlane
.getZ());
544 const double fRotInXY(atan2(rPlaneNormal
.getY(), rPlaneNormal
.getX()));
545 const double fRotInXZ(atan2(-rPlaneNormal
.getZ(), rPlaneNormal
.getXYLength()));
546 if(!fTools::equalZero(fRotInXY
) || !fTools::equalZero(fRotInXZ
))
548 aMatrixTransform
.rotate(0.0, fRotInXZ
, fRotInXY
);
551 // transform polygon to clip scenario
552 aRetval
= rCandidate
;
553 aRetval
.transform(aMatrixTransform
);
556 aRetval
= clipPolyPolygonOnOrthogonalPlane(aRetval
, tools::B3DORIENTATION_X
, bClipPositive
, 0.0, bStroke
);
560 // if there is a result, it needs to be transformed back
561 aMatrixTransform
.invert();
562 aRetval
.transform(aMatrixTransform
);
569 } // end of namespace tools
570 } // end of namespace basegfx
572 //////////////////////////////////////////////////////////////////////////////