/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_basegfx.hxx" #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////// namespace basegfx { namespace { inline bool impIsInside(const B3DPoint& rCandidate, double fPlaneOffset, tools::B3DOrientation ePlaneOrthogonal) { if(tools::B3DORIENTATION_X == ePlaneOrthogonal) { return fTools::moreOrEqual(rCandidate.getX(), fPlaneOffset); } else if(tools::B3DORIENTATION_Y == ePlaneOrthogonal) { return fTools::moreOrEqual(rCandidate.getY(), fPlaneOffset); } else { return fTools::moreOrEqual(rCandidate.getZ(), fPlaneOffset); } } inline double impGetCut(const B3DPoint& rCurrent, const B3DPoint& rNext, double fPlaneOffset, tools::B3DOrientation ePlaneOrthogonal) { if(tools::B3DORIENTATION_X == ePlaneOrthogonal) { return ((fPlaneOffset - rCurrent.getX())/(rNext.getX() - rCurrent.getX())); } else if(tools::B3DORIENTATION_Y == ePlaneOrthogonal) { return ((fPlaneOffset - rCurrent.getY())/(rNext.getY() - rCurrent.getY())); } else { return ((fPlaneOffset - rCurrent.getZ())/(rNext.getZ() - rCurrent.getZ())); } } void impAppendCopy(B3DPolygon& rDest, const B3DPolygon& rSource, sal_uInt32 nIndex) { rDest.append(rSource.getB3DPoint(nIndex)); if(rSource.areBColorsUsed()) { rDest.setBColor(rDest.count() - 1L, rSource.getBColor(nIndex)); } if(rSource.areNormalsUsed()) { rDest.setNormal(rDest.count() - 1L, rSource.getNormal(nIndex)); } if(rSource.areTextureCoordinatesUsed()) { rDest.setTextureCoordinate(rDest.count() - 1L, rSource.getTextureCoordinate(nIndex)); } } void impAppendInterpolate(B3DPolygon& rDest, const B3DPolygon& rSource, sal_uInt32 nIndA, sal_uInt32 nIndB, double fCut) { const B3DPoint aCurrPoint(rSource.getB3DPoint(nIndA)); const B3DPoint aNextPoint(rSource.getB3DPoint(nIndB)); rDest.append(interpolate(aCurrPoint, aNextPoint, fCut)); if(rSource.areBColorsUsed()) { const BColor aCurrBColor(rSource.getBColor(nIndA)); const BColor aNextBColor(rSource.getBColor(nIndB)); rDest.setBColor(rDest.count() - 1L, interpolate(aCurrBColor, aNextBColor, fCut)); } if(rSource.areNormalsUsed()) { const B3DVector aCurrVector(rSource.getNormal(nIndA)); const B3DVector aNextVector(rSource.getNormal(nIndB)); rDest.setNormal(rDest.count() - 1L, interpolate(aCurrVector, aNextVector, fCut)); } if(rSource.areTextureCoordinatesUsed()) { const B2DPoint aCurrTxCo(rSource.getTextureCoordinate(nIndA)); const B2DPoint aNextTxCo(rSource.getTextureCoordinate(nIndB)); rDest.setTextureCoordinate(rDest.count() - 1L, interpolate(aCurrTxCo, aNextTxCo, fCut)); } } } } // end of namespace basegfx ////////////////////////////////////////////////////////////////////////////// namespace basegfx { namespace tools { B3DPolyPolygon clipPolygonOnOrthogonalPlane(const B3DPolygon& rCandidate, B3DOrientation ePlaneOrthogonal, bool bClipPositive, double fPlaneOffset, bool bStroke) { B3DPolyPolygon aRetval; if(rCandidate.count()) { const B3DRange aCandidateRange(getRange(rCandidate)); if(B3DORIENTATION_X == ePlaneOrthogonal && fTools::moreOrEqual(aCandidateRange.getMinX(), fPlaneOffset)) { // completely above and on the clip plane. if(bClipPositive) { // add completely aRetval.append(rCandidate); } } else if(B3DORIENTATION_X == ePlaneOrthogonal && fTools::lessOrEqual(aCandidateRange.getMaxX(), fPlaneOffset)) { // completely below and on the clip plane. if(!bClipPositive) { // add completely aRetval.append(rCandidate); } } else if(B3DORIENTATION_Y == ePlaneOrthogonal && fTools::moreOrEqual(aCandidateRange.getMinY(), fPlaneOffset)) { // completely above and on the clip plane. if(bClipPositive) { // add completely aRetval.append(rCandidate); } } else if(B3DORIENTATION_Y == ePlaneOrthogonal && fTools::lessOrEqual(aCandidateRange.getMaxY(), fPlaneOffset)) { // completely below and on the clip plane. if(!bClipPositive) { // add completely aRetval.append(rCandidate); } } else if(B3DORIENTATION_Z == ePlaneOrthogonal && fTools::moreOrEqual(aCandidateRange.getMinZ(), fPlaneOffset)) { // completely above and on the clip plane. if(bClipPositive) { // add completely aRetval.append(rCandidate); } } else if(B3DORIENTATION_Z == ePlaneOrthogonal && fTools::lessOrEqual(aCandidateRange.getMaxZ(), fPlaneOffset)) { // completely below and on the clip plane. if(!bClipPositive) { // add completely aRetval.append(rCandidate); } } else { // prepare loop(s) B3DPolygon aNewPolygon; B3DPoint aCurrent(rCandidate.getB3DPoint(0L)); const sal_uInt32 nPointCount(rCandidate.count()); const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L); bool bCurrentInside(impIsInside(aCurrent, fPlaneOffset, ePlaneOrthogonal) == bClipPositive); if(bCurrentInside) { impAppendCopy(aNewPolygon, rCandidate, 0L); } if(bStroke) { // open polygon, create clipped line snippets. for(sal_uInt32 a(0L); a < nEdgeCount; a++) { // get next point data const sal_uInt32 nNextIndex((a + 1L == nPointCount) ? 0L : a + 1L); const B3DPoint aNext(rCandidate.getB3DPoint(nNextIndex)); const bool bNextInside(impIsInside(aNext, fPlaneOffset, ePlaneOrthogonal) == bClipPositive); if(bCurrentInside != bNextInside) { // change inside/outside if(bNextInside) { // entering, finish existing and start new line polygon if(aNewPolygon.count() > 1L) { aRetval.append(aNewPolygon); } aNewPolygon.clear(); } // calculate and add cut point const double fCut(impGetCut(aCurrent, aNext, fPlaneOffset, ePlaneOrthogonal)); impAppendInterpolate(aNewPolygon, rCandidate, a, nNextIndex, fCut); // pepare next step bCurrentInside = bNextInside; } if(bNextInside) { impAppendCopy(aNewPolygon, rCandidate, nNextIndex); } // pepare next step aCurrent = aNext; } if(aNewPolygon.count() > 1L) { aRetval.append(aNewPolygon); } } else { // closed polygon, create single clipped closed polygon for(sal_uInt32 a(0L); a < nEdgeCount; a++) { // get next point data, use offset const sal_uInt32 nNextIndex((a + 1L == nPointCount) ? 0L : a + 1L); const B3DPoint aNext(rCandidate.getB3DPoint(nNextIndex)); const bool bNextInside(impIsInside(aNext, fPlaneOffset, ePlaneOrthogonal) == bClipPositive); if(bCurrentInside != bNextInside) { // calculate and add cut point const double fCut(impGetCut(aCurrent, aNext, fPlaneOffset, ePlaneOrthogonal)); impAppendInterpolate(aNewPolygon, rCandidate, a, nNextIndex, fCut); // pepare next step bCurrentInside = bNextInside; } if(bNextInside && nNextIndex) { impAppendCopy(aNewPolygon, rCandidate, nNextIndex); } // pepare next step aCurrent = aNext; } if(aNewPolygon.count() > 2L) { aNewPolygon.setClosed(true); aRetval.append(aNewPolygon); } } } } return aRetval; } B3DPolyPolygon clipPolyPolygonOnOrthogonalPlane(const B3DPolyPolygon& rCandidate, B3DOrientation ePlaneOrthogonal, bool bClipPositive, double fPlaneOffset, bool bStroke) { B3DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < rCandidate.count(); a++) { aRetval.append(clipPolygonOnOrthogonalPlane(rCandidate.getB3DPolygon(a), ePlaneOrthogonal, bClipPositive, fPlaneOffset, bStroke)); } return aRetval; } B3DPolyPolygon clipPolyPolygonOnRange(const B3DPolyPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke) { B3DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < rCandidate.count(); a++) { aRetval.append(clipPolygonOnRange(rCandidate.getB3DPolygon(a), rRange, bInside, bStroke)); } return aRetval; } B3DPolyPolygon clipPolygonOnRange(const B3DPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke) { B3DPolyPolygon aRetval; if(rRange.isEmpty()) { // clipping against an empty range. Nothing is inside an empty range, so the polygon // is outside the range. So only return if not inside is wanted if(!bInside && rCandidate.count()) { aRetval.append(rCandidate); } } else if(rCandidate.count()) { const B3DRange aCandidateRange3D(getRange(rCandidate)); const B2DRange aCandidateRange( aCandidateRange3D.getMinX(), aCandidateRange3D.getMinY(), aCandidateRange3D.getMaxX(), aCandidateRange3D.getMaxY()); if(rRange.isInside(aCandidateRange)) { // candidate is completely inside given range, nothing to do. Is also true with curves. if(bInside) { aRetval.append(rCandidate); } } else if(!rRange.overlaps(aCandidateRange)) { // candidate is completely outside given range, nothing to do. Is also true with curves. if(!bInside) { aRetval.append(rCandidate); } } else { // clip against the six planes of the range // against lower X aRetval = clipPolygonOnOrthogonalPlane(rCandidate, tools::B3DORIENTATION_X, bInside, rRange.getMinX(), bStroke); if(aRetval.count()) { // against lower Y if(1L == aRetval.count()) { aRetval = clipPolygonOnOrthogonalPlane(aRetval.getB3DPolygon(0L), tools::B3DORIENTATION_Y, bInside, rRange.getMinY(), bStroke); } else { aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_Y, bInside, rRange.getMinY(), bStroke); } if(aRetval.count()) { // against higher X if(1L == aRetval.count()) { aRetval = clipPolygonOnOrthogonalPlane(aRetval.getB3DPolygon(0L), tools::B3DORIENTATION_X, !bInside, rRange.getMaxX(), bStroke); } else { aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_X, !bInside, rRange.getMaxX(), bStroke); } if(aRetval.count()) { // against higher Y if(1L == aRetval.count()) { aRetval = clipPolygonOnOrthogonalPlane(aRetval.getB3DPolygon(0L), tools::B3DORIENTATION_Y, !bInside, rRange.getMaxY(), bStroke); } else { aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_Y, !bInside, rRange.getMaxY(), bStroke); } } } } } } return aRetval; } B3DPolyPolygon clipPolyPolygonOnRange(const B3DPolyPolygon& rCandidate, const B3DRange& rRange, bool bInside, bool bStroke) { B3DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < rCandidate.count(); a++) { aRetval.append(clipPolygonOnRange(rCandidate.getB3DPolygon(a), rRange, bInside, bStroke)); } return aRetval; } B3DPolyPolygon clipPolygonOnRange(const B3DPolygon& rCandidate, const B3DRange& rRange, bool bInside, bool bStroke) { B3DPolyPolygon aRetval; if(rRange.isEmpty()) { // clipping against an empty range. Nothing is inside an empty range, so the polygon // is outside the range. So only return if not inside is wanted if(!bInside && rCandidate.count()) { aRetval.append(rCandidate); } } else if(rCandidate.count()) { const B3DRange aCandidateRange(getRange(rCandidate)); if(rRange.isInside(aCandidateRange)) { // candidate is completely inside given range, nothing to do. Is also true with curves. if(bInside) { aRetval.append(rCandidate); } } else if(!rRange.overlaps(aCandidateRange)) { // candidate is completely outside given range, nothing to do. Is also true with curves. if(!bInside) { aRetval.append(rCandidate); } } else { // clip against X,Y first and see if there's something left const B2DRange aCandidateRange2D(rRange.getMinX(), rRange.getMinY(), rRange.getMaxX(), rRange.getMaxY()); aRetval = clipPolygonOnRange(rCandidate, aCandidateRange2D, bInside, bStroke); if(aRetval.count()) { // against lower Z if(1L == aRetval.count()) { aRetval = clipPolygonOnOrthogonalPlane(aRetval.getB3DPolygon(0L), tools::B3DORIENTATION_Z, bInside, rRange.getMinZ(), bStroke); } else { aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_Z, bInside, rRange.getMinZ(), bStroke); } if(aRetval.count()) { // against higher Z if(1L == aRetval.count()) { aRetval = clipPolygonOnOrthogonalPlane(aRetval.getB3DPolygon(0L), tools::B3DORIENTATION_Z, !bInside, rRange.getMaxZ(), bStroke); } else { aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_Z, !bInside, rRange.getMaxZ(), bStroke); } } } } } return aRetval; } B3DPolyPolygon clipPolygonOnPlane(const B3DPolygon& rCandidate, const B3DPoint& rPointOnPlane, const B3DVector& rPlaneNormal, bool bClipPositive, bool bStroke) { B3DPolyPolygon aRetval; if(rPlaneNormal.equalZero()) { // not really a plane definition, return polygon aRetval.append(rCandidate); } else if(rCandidate.count()) { // build transform to project planeNormal on X-Axis and pointOnPlane to null point B3DHomMatrix aMatrixTransform; aMatrixTransform.translate(-rPointOnPlane.getX(), -rPointOnPlane.getY(), -rPointOnPlane.getZ()); const double fRotInXY(atan2(rPlaneNormal.getY(), rPlaneNormal.getX())); const double fRotInXZ(atan2(-rPlaneNormal.getZ(), rPlaneNormal.getXYLength())); if(!fTools::equalZero(fRotInXY) || !fTools::equalZero(fRotInXZ)) { aMatrixTransform.rotate(0.0, fRotInXZ, fRotInXY); } // transform polygon to clip scenario B3DPolygon aCandidate(rCandidate); aCandidate.transform(aMatrixTransform); // clip on YZ plane aRetval = clipPolygonOnOrthogonalPlane(aCandidate, tools::B3DORIENTATION_X, bClipPositive, 0.0, bStroke); if(aRetval.count()) { // if there is a result, it needs to be transformed back aMatrixTransform.invert(); aRetval.transform(aMatrixTransform); } } return aRetval; } B3DPolyPolygon clipPolyPolygonOnPlane(const B3DPolyPolygon& rCandidate, const B3DPoint& rPointOnPlane, const B3DVector& rPlaneNormal, bool bClipPositive, bool bStroke) { B3DPolyPolygon aRetval; if(rPlaneNormal.equalZero()) { // not really a plane definition, return polygon aRetval = rCandidate; } else if(rCandidate.count()) { // build transform to project planeNormal on X-Axis and pointOnPlane to null point B3DHomMatrix aMatrixTransform; aMatrixTransform.translate(-rPointOnPlane.getX(), -rPointOnPlane.getY(), -rPointOnPlane.getZ()); const double fRotInXY(atan2(rPlaneNormal.getY(), rPlaneNormal.getX())); const double fRotInXZ(atan2(-rPlaneNormal.getZ(), rPlaneNormal.getXYLength())); if(!fTools::equalZero(fRotInXY) || !fTools::equalZero(fRotInXZ)) { aMatrixTransform.rotate(0.0, fRotInXZ, fRotInXY); } // transform polygon to clip scenario aRetval = rCandidate; aRetval.transform(aMatrixTransform); // clip on YZ plane aRetval = clipPolyPolygonOnOrthogonalPlane(aRetval, tools::B3DORIENTATION_X, bClipPositive, 0.0, bStroke); if(aRetval.count()) { // if there is a result, it needs to be transformed back aMatrixTransform.invert(); aRetval.transform(aMatrixTransform); } } return aRetval; } } // end of namespace tools } // end of namespace basegfx ////////////////////////////////////////////////////////////////////////////// // eof