diff options
Diffstat (limited to 'basegfx/source/tools')
-rw-r--r-- | basegfx/source/tools/b2dclipstate.cxx | 665 | ||||
-rw-r--r-- | basegfx/source/tools/canvastools.cxx | 677 | ||||
-rw-r--r-- | basegfx/source/tools/debugplotter.cxx | 418 | ||||
-rw-r--r-- | basegfx/source/tools/gradienttools.cxx | 352 | ||||
-rw-r--r-- | basegfx/source/tools/keystoplerp.cxx | 110 | ||||
-rw-r--r-- | basegfx/source/tools/liangbarsky.cxx | 135 | ||||
-rwxr-xr-x | basegfx/source/tools/makefile.mk | 51 | ||||
-rw-r--r-- | basegfx/source/tools/tools.cxx | 127 | ||||
-rw-r--r-- | basegfx/source/tools/unopolypolygon.cxx | 489 |
9 files changed, 3024 insertions, 0 deletions
diff --git a/basegfx/source/tools/b2dclipstate.cxx b/basegfx/source/tools/b2dclipstate.cxx new file mode 100644 index 000000000000..4f1d68b0f699 --- /dev/null +++ b/basegfx/source/tools/b2dclipstate.cxx @@ -0,0 +1,665 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: b2dmultirange.cxx,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" +#include <basegfx/tools/b2dclipstate.hxx> + +#include <basegfx/range/b2drange.hxx> +#include <basegfx/range/b2dpolyrange.hxx> +#include <basegfx/range/b2drangeclipper.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygoncutter.hxx> + +namespace basegfx +{ +namespace tools +{ + struct ImplB2DClipState + { + public: + enum Operation {UNION, INTERSECT, XOR, SUBTRACT}; + + ImplB2DClipState() : + maPendingPolygons(), + maPendingRanges(), + maClipPoly(), + mePendingOps(UNION) + {} + + explicit ImplB2DClipState( const B2DRange& rRange ) : + maPendingPolygons(), + maPendingRanges(), + maClipPoly( + tools::createPolygonFromRect(rRange)), + mePendingOps(UNION) + {} + + explicit ImplB2DClipState( const B2DPolygon& rPoly ) : + maPendingPolygons(), + maPendingRanges(), + maClipPoly(rPoly), + mePendingOps(UNION) + {} + + explicit ImplB2DClipState( const B2DPolyPolygon& rPoly ) : + maPendingPolygons(), + maPendingRanges(), + maClipPoly(rPoly), + mePendingOps(UNION) + {} + + bool isCleared() const + { + return !maClipPoly.count() + && !maPendingPolygons.count() + && !maPendingRanges.count(); + } + + void makeClear() + { + maPendingPolygons.clear(); + maPendingRanges.clear(); + maClipPoly.clear(); + mePendingOps = UNION; + } + + bool isNullClipPoly() const + { + return maClipPoly.count() == 1 + && !maClipPoly.getB2DPolygon(0).count(); + } + + bool isNull() const + { + return !maPendingPolygons.count() + && !maPendingRanges.count() + && isNullClipPoly(); + } + + void makeNull() + { + maPendingPolygons.clear(); + maPendingRanges.clear(); + maClipPoly.clear(); + maClipPoly.append(B2DPolygon()); + mePendingOps = UNION; + } + + bool operator==(const ImplB2DClipState& rRHS) const + { + return maPendingPolygons == rRHS.maPendingPolygons + && maPendingRanges == rRHS.maPendingRanges + && maClipPoly == rRHS.maClipPoly + && mePendingOps == rRHS.mePendingOps; + } + + void addRange(const B2DRange& rRange, Operation eOp) + { + if( rRange.isEmpty() ) + return; + + commitPendingPolygons(); + if( mePendingOps != eOp ) + commitPendingRanges(); + + mePendingOps = eOp; + maPendingRanges.appendElement( + rRange, + ORIENTATION_POSITIVE); + } + + void addPolygon(B2DPolygon aPoly, Operation eOp) + { + commitPendingRanges(); + if( mePendingOps != eOp ) + commitPendingPolygons(); + + mePendingOps = eOp; + maPendingPolygons.append(aPoly); + } + + void addPolyPolygon(B2DPolyPolygon aPoly, Operation eOp) + { + commitPendingRanges(); + if( mePendingOps != eOp ) + commitPendingPolygons(); + + mePendingOps = eOp; + maPendingPolygons.append(aPoly); + } + + void addClipState(const ImplB2DClipState& rOther, Operation eOp) + { + if( rOther.mePendingOps == mePendingOps + && !rOther.maClipPoly.count() + && !rOther.maPendingPolygons.count() ) + { + maPendingRanges.appendPolyRange( rOther.maPendingRanges ); + } + else + { + commitPendingRanges(); + commitPendingPolygons(); + rOther.commitPendingRanges(); + rOther.commitPendingPolygons(); + + maPendingPolygons = rOther.maClipPoly; + mePendingOps = eOp; + } + } + + void unionRange(const B2DRange& rRange) + { + if( isCleared() ) + return; + + addRange(rRange,UNION); + } + + void unionPolygon(const B2DPolygon& rPoly) + { + if( isCleared() ) + return; + + addPolygon(rPoly,UNION); + } + + void unionPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + if( isCleared() ) + return; + + addPolyPolygon(rPolyPoly,UNION); + } + + void unionClipState(const ImplB2DClipState& rOther) + { + if( isCleared() ) + return; + + addClipState(rOther, UNION); + } + + void intersectRange(const B2DRange& rRange) + { + if( isNull() ) + return; + + addRange(rRange,INTERSECT); + } + + void intersectPolygon(const B2DPolygon& rPoly) + { + if( isNull() ) + return; + + addPolygon(rPoly,INTERSECT); + } + + void intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + if( isNull() ) + return; + + addPolyPolygon(rPolyPoly,INTERSECT); + } + + void intersectClipState(const ImplB2DClipState& rOther) + { + if( isNull() ) + return; + + addClipState(rOther, INTERSECT); + } + + void subtractRange(const B2DRange& rRange ) + { + if( isNull() ) + return; + + addRange(rRange,SUBTRACT); + } + + void subtractPolygon(const B2DPolygon& rPoly) + { + if( isNull() ) + return; + + addPolygon(rPoly,SUBTRACT); + } + + void subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + if( isNull() ) + return; + + addPolyPolygon(rPolyPoly,SUBTRACT); + } + + void subtractClipState(const ImplB2DClipState& rOther) + { + if( isNull() ) + return; + + addClipState(rOther, SUBTRACT); + } + + void xorRange(const B2DRange& rRange) + { + addRange(rRange,XOR); + } + + void xorPolygon(const B2DPolygon& rPoly) + { + addPolygon(rPoly,XOR); + } + + void xorPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + addPolyPolygon(rPolyPoly,XOR); + } + + void xorClipState(const ImplB2DClipState& rOther) + { + addClipState(rOther, XOR); + } + + B2DPolyPolygon getClipPoly() const + { + commitPendingRanges(); + commitPendingPolygons(); + + return maClipPoly; + } + + private: + void commitPendingPolygons() const + { + if( !maPendingPolygons.count() ) + return; + + // assumption: maClipPoly has kept polygons prepared for + // clipping; i.e. no neutral polygons & correct + // orientation + maPendingPolygons = tools::prepareForPolygonOperation(maPendingPolygons); + const bool bIsEmpty=isNullClipPoly(); + const bool bIsCleared=!maClipPoly.count(); + switch(mePendingOps) + { + case UNION: + OSL_ASSERT( !bIsCleared ); + + if( bIsEmpty ) + maClipPoly = maPendingPolygons; + else + maClipPoly = tools::solvePolygonOperationOr( + maClipPoly, + maPendingPolygons); + break; + case INTERSECT: + OSL_ASSERT( !bIsEmpty ); + + if( bIsCleared ) + maClipPoly = maPendingPolygons; + else + maClipPoly = tools::solvePolygonOperationAnd( + maClipPoly, + maPendingPolygons); + break; + case XOR: + if( bIsEmpty ) + maClipPoly = maPendingPolygons; + else if( bIsCleared ) + { + // not representable, strictly speaking, + // using polygons with the common even/odd + // or nonzero winding number fill rule. If + // we'd want to represent it, fill rule + // would need to be "non-negative winding + // number" (and we then would return + // 'holes' here) + + // going for an ugly hack meanwhile + maClipPoly = tools::solvePolygonOperationXor( + B2DPolyPolygon( + tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))), + maPendingPolygons); + } + else + maClipPoly = tools::solvePolygonOperationXor( + maClipPoly, + maPendingPolygons); + break; + case SUBTRACT: + OSL_ASSERT( !bIsEmpty ); + + // first union all pending ones, subtract en bloc then + maPendingPolygons = solveCrossovers(maPendingPolygons); + maPendingPolygons = stripNeutralPolygons(maPendingPolygons); + maPendingPolygons = stripDispensablePolygons(maPendingPolygons, false); + + if( bIsCleared ) + { + // not representable, strictly speaking, + // using polygons with the common even/odd + // or nonzero winding number fill rule. If + // we'd want to represent it, fill rule + // would need to be "non-negative winding + // number" (and we then would return + // 'holes' here) + + // going for an ugly hack meanwhile + maClipPoly = tools::solvePolygonOperationDiff( + B2DPolyPolygon( + tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))), + maPendingPolygons); + } + else + maClipPoly = tools::solvePolygonOperationDiff( + maClipPoly, + maPendingPolygons); + break; + } + + maPendingPolygons.clear(); + mePendingOps = UNION; + } + + void commitPendingRanges() const + { + if( !maPendingRanges.count() ) + return; + + // use the specialized range clipper for the win + B2DPolyPolygon aCollectedRanges; + const bool bIsEmpty=isNullClipPoly(); + const bool bIsCleared=!maClipPoly.count(); + switch(mePendingOps) + { + case UNION: + OSL_ASSERT( !bIsCleared ); + + aCollectedRanges = maPendingRanges.solveCrossovers(); + aCollectedRanges = stripNeutralPolygons(aCollectedRanges); + aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false); + if( bIsEmpty ) + maClipPoly = aCollectedRanges; + else + maClipPoly = tools::solvePolygonOperationOr( + maClipPoly, + aCollectedRanges); + break; + case INTERSECT: + OSL_ASSERT( !bIsEmpty ); + + aCollectedRanges = maPendingRanges.solveCrossovers(); + aCollectedRanges = stripNeutralPolygons(aCollectedRanges); + if( maPendingRanges.count() > 1 ) + aCollectedRanges = stripDispensablePolygons(aCollectedRanges, true); + + if( bIsCleared ) + maClipPoly = aCollectedRanges; + else + maClipPoly = tools::solvePolygonOperationAnd( + maClipPoly, + aCollectedRanges); + break; + case XOR: + aCollectedRanges = maPendingRanges.solveCrossovers(); + aCollectedRanges = stripNeutralPolygons(aCollectedRanges); + aCollectedRanges = correctOrientations(aCollectedRanges); + + if( bIsEmpty ) + maClipPoly = aCollectedRanges; + else if( bIsCleared ) + { + // not representable, strictly speaking, + // using polygons with the common even/odd + // or nonzero winding number fill rule. If + // we'd want to represent it, fill rule + // would need to be "non-negative winding + // number" (and we then would return + // 'holes' here) + + // going for an ugly hack meanwhile + maClipPoly = tools::solvePolygonOperationXor( + B2DPolyPolygon( + tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))), + aCollectedRanges); + } + else + maClipPoly = tools::solvePolygonOperationXor( + maClipPoly, + aCollectedRanges); + break; + case SUBTRACT: + OSL_ASSERT( !bIsEmpty ); + + // first union all pending ranges, subtract en bloc then + aCollectedRanges = maPendingRanges.solveCrossovers(); + aCollectedRanges = stripNeutralPolygons(aCollectedRanges); + aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false); + + if( bIsCleared ) + { + // not representable, strictly speaking, + // using polygons with the common even/odd + // or nonzero winding number fill rule. If + // we'd want to represent it, fill rule + // would need to be "non-negative winding + // number" (and we then would return + // 'holes' here) + + // going for an ugly hack meanwhile + maClipPoly = tools::solvePolygonOperationDiff( + B2DPolyPolygon( + tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))), + aCollectedRanges); + } + else + maClipPoly = tools::solvePolygonOperationDiff( + maClipPoly, + aCollectedRanges); + break; + } + + maPendingRanges.clear(); + mePendingOps = UNION; + } + + mutable B2DPolyPolygon maPendingPolygons; + mutable B2DPolyRange maPendingRanges; + mutable B2DPolyPolygon maClipPoly; + mutable Operation mePendingOps; + }; + + B2DClipState::B2DClipState() : + mpImpl() + {} + + B2DClipState::~B2DClipState() + {} + + B2DClipState::B2DClipState( const B2DClipState& rOrig ) : + mpImpl(rOrig.mpImpl) + {} + + B2DClipState::B2DClipState( const B2DRange& rRange ) : + mpImpl( ImplB2DClipState(rRange) ) + {} + + B2DClipState::B2DClipState( const B2DPolygon& rPoly ) : + mpImpl( ImplB2DClipState(rPoly) ) + {} + + B2DClipState::B2DClipState( const B2DPolyPolygon& rPolyPoly ) : + mpImpl( ImplB2DClipState(rPolyPoly) ) + {} + + B2DClipState& B2DClipState::operator=( const B2DClipState& rRHS ) + { + mpImpl = rRHS.mpImpl; + return *this; + } + + void B2DClipState::makeUnique() + { + mpImpl.make_unique(); + } + + void B2DClipState::makeNull() + { + mpImpl->makeNull(); + } + + bool B2DClipState::isNull() const + { + return mpImpl->isNull(); + } + + void B2DClipState::makeClear() + { + mpImpl->makeClear(); + } + + bool B2DClipState::isCleared() const + { + return mpImpl->isCleared(); + } + + bool B2DClipState::operator==(const B2DClipState& rRHS) const + { + if(mpImpl.same_object(rRHS.mpImpl)) + return true; + + return ((*mpImpl) == (*rRHS.mpImpl)); + } + + bool B2DClipState::operator!=(const B2DClipState& rRHS) const + { + return !(*this == rRHS); + } + + void B2DClipState::unionRange(const B2DRange& rRange) + { + mpImpl->unionRange(rRange); + } + + void B2DClipState::unionPolygon(const B2DPolygon& rPoly) + { + mpImpl->unionPolygon(rPoly); + } + + void B2DClipState::unionPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + mpImpl->unionPolyPolygon(rPolyPoly); + } + + void B2DClipState::unionClipState(const B2DClipState& rState) + { + mpImpl->unionClipState(*rState.mpImpl); + } + + void B2DClipState::intersectRange(const B2DRange& rRange) + { + mpImpl->intersectRange(rRange); + } + + void B2DClipState::intersectPolygon(const B2DPolygon& rPoly) + { + mpImpl->intersectPolygon(rPoly); + } + + void B2DClipState::intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + mpImpl->intersectPolyPolygon(rPolyPoly); + } + + void B2DClipState::intersectClipState(const B2DClipState& rState) + { + mpImpl->intersectClipState(*rState.mpImpl); + } + + void B2DClipState::subtractRange(const B2DRange& rRange) + { + mpImpl->subtractRange(rRange); + } + + void B2DClipState::subtractPolygon(const B2DPolygon& rPoly) + { + mpImpl->subtractPolygon(rPoly); + } + + void B2DClipState::subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + mpImpl->subtractPolyPolygon(rPolyPoly); + } + + void B2DClipState::subtractClipState(const B2DClipState& rState) + { + mpImpl->subtractClipState(*rState.mpImpl); + } + + void B2DClipState::xorRange(const B2DRange& rRange) + { + mpImpl->xorRange(rRange); + } + + void B2DClipState::xorPolygon(const B2DPolygon& rPoly) + { + mpImpl->xorPolygon(rPoly); + } + + void B2DClipState::xorPolyPolygon(const B2DPolyPolygon& rPolyPoly) + { + mpImpl->xorPolyPolygon(rPolyPoly); + } + + void B2DClipState::xorClipState(const B2DClipState& rState) + { + mpImpl->xorClipState(*rState.mpImpl); + } + + B2DPolyPolygon B2DClipState::getClipPoly() const + { + return mpImpl->getClipPoly(); + } + +} // end of namespace tools +} // end of namespace basegfx + +// eof + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basegfx/source/tools/canvastools.cxx b/basegfx/source/tools/canvastools.cxx new file mode 100644 index 000000000000..c82b13d7cbf8 --- /dev/null +++ b/basegfx/source/tools/canvastools.cxx @@ -0,0 +1,677 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" + +#include <com/sun/star/geometry/RealSize2D.hpp> +#include <com/sun/star/geometry/RealPoint2D.hpp> +#include <com/sun/star/geometry/RealRectangle2D.hpp> +#include <com/sun/star/geometry/RealRectangle3D.hpp> +#include <com/sun/star/geometry/RealBezierSegment2D.hpp> +#include <com/sun/star/geometry/AffineMatrix2D.hpp> +#include <com/sun/star/geometry/AffineMatrix3D.hpp> +#include <com/sun/star/geometry/Matrix2D.hpp> +#include <com/sun/star/geometry/IntegerSize2D.hpp> +#include <com/sun/star/geometry/IntegerPoint2D.hpp> +#include <com/sun/star/geometry/IntegerRectangle2D.hpp> +#include <com/sun/star/rendering/XPolyPolygon2D.hpp> +#include <com/sun/star/rendering/XGraphicDevice.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <basegfx/tools/unopolypolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/matrix/b3dhommatrix.hxx> +#include <basegfx/vector/b2dsize.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/range/b3drange.hxx> +#include <basegfx/vector/b2isize.hxx> +#include <basegfx/point/b2ipoint.hxx> +#include <basegfx/range/b2irectangle.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <limits> + +using namespace ::com::sun::star; + +namespace basegfx +{ + + namespace unotools + { + namespace + { + uno::Sequence< geometry::RealBezierSegment2D > bezierSequenceFromB2DPolygon(const ::basegfx::B2DPolygon& rPoly) + { + const sal_uInt32 nPointCount(rPoly.count()); + uno::Sequence< geometry::RealBezierSegment2D > outputSequence(nPointCount); + geometry::RealBezierSegment2D* pOutput = outputSequence.getArray(); + + // fill sequences and imply clodes polygon on this implementation layer + for(sal_uInt32 a(0); a < nPointCount; a++) + { + const basegfx::B2DPoint aStart(rPoly.getB2DPoint(a)); + const basegfx::B2DPoint aControlA(rPoly.getNextControlPoint(a)); + const basegfx::B2DPoint aControlB(rPoly.getPrevControlPoint((a + 1) % nPointCount)); + + pOutput[a] = geometry::RealBezierSegment2D( + aStart.getX(), aStart.getY(), + aControlA.getX(), aControlA.getY(), + aControlB.getX(), aControlB.getY()); + } + + return outputSequence; + } + + uno::Sequence< geometry::RealPoint2D > pointSequenceFromB2DPolygon( const ::basegfx::B2DPolygon& rPoly ) + { + const sal_uInt32 nNumPoints( rPoly.count() ); + + uno::Sequence< geometry::RealPoint2D > outputSequence( nNumPoints ); + geometry::RealPoint2D* pOutput = outputSequence.getArray(); + + // fill sequence from polygon + sal_uInt32 i; + for( i=0; i<nNumPoints; ++i ) + { + const ::basegfx::B2DPoint aPoint( rPoly.getB2DPoint(i) ); + + pOutput[i] = geometry::RealPoint2D( aPoint.getX(), + aPoint.getY() ); + } + + return outputSequence; + } + } + + //--------------------------------------------------------------------------------------- + + uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > > bezierSequenceSequenceFromB2DPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly ) + { + const sal_uInt32 nNumPolies( rPolyPoly.count() ); + sal_uInt32 i; + + uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > > outputSequence( nNumPolies ); + uno::Sequence< geometry::RealBezierSegment2D >* pOutput = outputSequence.getArray(); + + for( i=0; i<nNumPolies; ++i ) + { + pOutput[i] = bezierSequenceFromB2DPolygon( rPolyPoly.getB2DPolygon(i) ); + } + + return outputSequence; + } + + //--------------------------------------------------------------------------------------- + + uno::Sequence< uno::Sequence< geometry::RealPoint2D > > pointSequenceSequenceFromB2DPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly ) + { + const sal_uInt32 nNumPolies( rPolyPoly.count() ); + sal_uInt32 i; + + uno::Sequence< uno::Sequence< geometry::RealPoint2D > > outputSequence( nNumPolies ); + uno::Sequence< geometry::RealPoint2D >* pOutput = outputSequence.getArray(); + + for( i=0; i<nNumPolies; ++i ) + { + pOutput[i] = pointSequenceFromB2DPolygon( rPolyPoly.getB2DPolygon(i) ); + } + + return outputSequence; + } + + //--------------------------------------------------------------------------------------- + + uno::Reference< rendering::XPolyPolygon2D > xPolyPolygonFromB2DPolygon( const uno::Reference< rendering::XGraphicDevice >& xGraphicDevice, + const ::basegfx::B2DPolygon& rPoly ) + { + uno::Reference< rendering::XPolyPolygon2D > xRes; + + if( !xGraphicDevice.is() ) + return xRes; + + if( rPoly.areControlPointsUsed() ) + { + uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > > outputSequence( 1 ); + outputSequence[0] = bezierSequenceFromB2DPolygon( rPoly ); + + xRes.set( xGraphicDevice->createCompatibleBezierPolyPolygon( outputSequence ), + uno::UNO_QUERY ); + } + else + { + uno::Sequence< uno::Sequence< geometry::RealPoint2D > > outputSequence( 1 ); + outputSequence[0] = pointSequenceFromB2DPolygon( rPoly ); + + xRes.set( xGraphicDevice->createCompatibleLinePolyPolygon( outputSequence ), + uno::UNO_QUERY ); + } + + if( xRes.is() && rPoly.isClosed() ) + xRes->setClosed( 0, sal_True ); + + return xRes; + } + + //--------------------------------------------------------------------------------------- + + uno::Reference< rendering::XPolyPolygon2D > xPolyPolygonFromB2DPolyPolygon( const uno::Reference< rendering::XGraphicDevice >& xGraphicDevice, + const ::basegfx::B2DPolyPolygon& rPolyPoly ) + { + uno::Reference< rendering::XPolyPolygon2D > xRes; + + if( !xGraphicDevice.is() ) + return xRes; + + const sal_uInt32 nNumPolies( rPolyPoly.count() ); + sal_uInt32 i; + + if( rPolyPoly.areControlPointsUsed() ) + { + xRes.set( xGraphicDevice->createCompatibleBezierPolyPolygon( + bezierSequenceSequenceFromB2DPolyPolygon( rPolyPoly ) ), + uno::UNO_QUERY ); + } + else + { + xRes.set( xGraphicDevice->createCompatibleLinePolyPolygon( + pointSequenceSequenceFromB2DPolyPolygon( rPolyPoly ) ), + uno::UNO_QUERY ); + } + + for( i=0; i<nNumPolies; ++i ) + { + xRes->setClosed( i, rPolyPoly.getB2DPolygon(i).isClosed() ); + } + + return xRes; + } + + //--------------------------------------------------------------------------------------- + + ::basegfx::B2DPolygon polygonFromPoint2DSequence( const uno::Sequence< geometry::RealPoint2D >& points ) + { + const sal_Int32 nCurrSize( points.getLength() ); + + ::basegfx::B2DPolygon aPoly; + + for( sal_Int32 nCurrPoint=0; nCurrPoint<nCurrSize; ++nCurrPoint ) + aPoly.append( b2DPointFromRealPoint2D( points[nCurrPoint] ) ); + + return aPoly; + } + + //--------------------------------------------------------------------------------------- + + ::basegfx::B2DPolyPolygon polyPolygonFromPoint2DSequenceSequence( const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points ) + { + ::basegfx::B2DPolyPolygon aRes; + + for( sal_Int32 nCurrPoly=0; nCurrPoly<points.getLength(); ++nCurrPoly ) + { + aRes.append( polygonFromPoint2DSequence( points[nCurrPoly] ) ); + } + + return aRes; + } + + //--------------------------------------------------------------------------------------- + + ::basegfx::B2DPolygon polygonFromBezier2DSequence( const uno::Sequence< geometry::RealBezierSegment2D >& curves ) + { + const sal_Int32 nSize(curves.getLength()); + basegfx::B2DPolygon aRetval; + + if(nSize) + { + // prepare start with providing a start point. Use the first point from + // the sequence for this + const geometry::RealBezierSegment2D& rFirstSegment(curves[0]); // #i79917# first segment, not last + aRetval.append(basegfx::B2DPoint(rFirstSegment.Px, rFirstSegment.Py)); + + for(sal_Int32 a(0); a < nSize; a++) + { + const geometry::RealBezierSegment2D& rCurrSegment(curves[a]); + const geometry::RealBezierSegment2D& rNextSegment(curves[(a + 1) % nSize]); + + // append curved edge with the control points and the next point + aRetval.appendBezierSegment( + basegfx::B2DPoint(rCurrSegment.C1x, rCurrSegment.C1y), + basegfx::B2DPoint(rCurrSegment.C2x, rCurrSegment.C2y), // #i79917# Argh! An x for an y!! + basegfx::B2DPoint(rNextSegment.Px, rNextSegment.Py)); + } + + // rescue the control point and remove the now double-added point + aRetval.setPrevControlPoint(0, aRetval.getPrevControlPoint(aRetval.count() - 1)); + aRetval.remove(aRetval.count() - 1); + } + + return aRetval; + } + + //--------------------------------------------------------------------------------------- + + ::basegfx::B2DPolyPolygon polyPolygonFromBezier2DSequenceSequence( const uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > >& curves ) + { + ::basegfx::B2DPolyPolygon aRes; + + for( sal_Int32 nCurrPoly=0; nCurrPoly<curves.getLength(); ++nCurrPoly ) + { + aRes.append( polygonFromBezier2DSequence( curves[nCurrPoly] ) ); + } + + return aRes; + } + + //--------------------------------------------------------------------------------------- + + ::basegfx::B2DPolyPolygon b2DPolyPolygonFromXPolyPolygon2D( const uno::Reference< rendering::XPolyPolygon2D >& xPoly ) + { + ::basegfx::unotools::UnoPolyPolygon* pPolyImpl = + dynamic_cast< ::basegfx::unotools::UnoPolyPolygon* >( xPoly.get() ); + + if( pPolyImpl ) + { + return pPolyImpl->getPolyPolygon(); + } + else + { + // not a known implementation object - try data source + // interfaces + const sal_Int32 nPolys( xPoly->getNumberOfPolygons() ); + + uno::Reference< rendering::XBezierPolyPolygon2D > xBezierPoly( + xPoly, + uno::UNO_QUERY ); + + if( xBezierPoly.is() ) + { + return ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence( + xBezierPoly->getBezierSegments( 0, + nPolys, + 0, + -1 ) ); + } + else + { + uno::Reference< rendering::XLinePolyPolygon2D > xLinePoly( + xPoly, + uno::UNO_QUERY ); + + // no implementation class and no data provider + // found - contract violation. + if( !xLinePoly.is() ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii( + "basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(): Invalid input" + "poly-polygon, cannot retrieve vertex data"), + uno::Reference< uno::XInterface >(), + 0 ); + } + + return ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence( + xLinePoly->getPoints( 0, + nPolys, + 0, + -1 )); + } + } + } + + //--------------------------------------------------------------------------------------- + + ::basegfx::B2DHomMatrix& homMatrixFromAffineMatrix( ::basegfx::B2DHomMatrix& output, + const geometry::AffineMatrix2D& input ) + { + // ensure last row is [0,0,1] (and optimized away) + output.identity(); + + output.set(0,0, input.m00); + output.set(0,1, input.m01); + output.set(0,2, input.m02); + output.set(1,0, input.m10); + output.set(1,1, input.m11); + output.set(1,2, input.m12); + + return output; + } + + ::basegfx::B2DHomMatrix homMatrixFromAffineMatrix( const geometry::AffineMatrix2D& input ) + { + ::basegfx::B2DHomMatrix output; + + output.set(0,0, input.m00); + output.set(0,1, input.m01); + output.set(0,2, input.m02); + output.set(1,0, input.m10); + output.set(1,1, input.m11); + output.set(1,2, input.m12); + + return output; + } + + ::basegfx::B3DHomMatrix homMatrixFromAffineMatrix3D( const ::com::sun::star::geometry::AffineMatrix3D& input ) + { + ::basegfx::B3DHomMatrix output; + + output.set(0,0, input.m00); + output.set(0,1, input.m01); + output.set(0,2, input.m02); + output.set(0,3, input.m03); + + output.set(1,0, input.m10); + output.set(1,1, input.m11); + output.set(1,2, input.m12); + output.set(1,3, input.m13); + + output.set(2,0, input.m20); + output.set(2,1, input.m21); + output.set(2,2, input.m22); + output.set(2,3, input.m23); + + return output; + } + + geometry::AffineMatrix2D& affineMatrixFromHomMatrix( geometry::AffineMatrix2D& output, + const ::basegfx::B2DHomMatrix& input) + { + output.m00 = input.get(0,0); + output.m01 = input.get(0,1); + output.m02 = input.get(0,2); + output.m10 = input.get(1,0); + output.m11 = input.get(1,1); + output.m12 = input.get(1,2); + + return output; + } + + geometry::AffineMatrix3D& affineMatrixFromHomMatrix3D( + geometry::AffineMatrix3D& output, + const ::basegfx::B3DHomMatrix& input) + { + output.m00 = input.get(0,0); + output.m01 = input.get(0,1); + output.m02 = input.get(0,2); + output.m03 = input.get(0,3); + + output.m10 = input.get(1,0); + output.m11 = input.get(1,1); + output.m12 = input.get(1,2); + output.m13 = input.get(1,3); + + output.m20 = input.get(2,0); + output.m21 = input.get(2,1); + output.m22 = input.get(2,2); + output.m23 = input.get(2,3); + + return output; + } + + //--------------------------------------------------------------------------------------- + + ::basegfx::B2DHomMatrix& homMatrixFromMatrix( ::basegfx::B2DHomMatrix& output, + const geometry::Matrix2D& input ) + { + // ensure last row is [0,0,1] (and optimized away) + output.identity(); + + output.set(0,0, input.m00); + output.set(0,1, input.m01); + output.set(1,0, input.m10); + output.set(1,1, input.m11); + + return output; + } + + //--------------------------------------------------------------------------------------- + + geometry::RealSize2D size2DFromB2DSize( const ::basegfx::B2DVector& rVec ) + { + return geometry::RealSize2D( rVec.getX(), + rVec.getY() ); + } + + geometry::RealPoint2D point2DFromB2DPoint( const ::basegfx::B2DPoint& rPoint ) + { + return geometry::RealPoint2D( rPoint.getX(), + rPoint.getY() ); + } + + geometry::RealRectangle2D rectangle2DFromB2DRectangle( const ::basegfx::B2DRange& rRect ) + { + return geometry::RealRectangle2D( rRect.getMinX(), + rRect.getMinY(), + rRect.getMaxX(), + rRect.getMaxY() ); + } + + geometry::RealRectangle3D rectangle3DFromB3DRectangle( const ::basegfx::B3DRange& rRect ) + { + return geometry::RealRectangle3D( rRect.getMinX(), + rRect.getMinY(), + rRect.getMinZ(), + rRect.getMaxX(), + rRect.getMaxY(), + rRect.getMaxZ()); + } + + ::basegfx::B2DVector b2DSizeFromRealSize2D( const geometry::RealSize2D& rSize ) + { + return ::basegfx::B2DVector( rSize.Width, + rSize.Height ); + } + + ::basegfx::B2DPoint b2DPointFromRealPoint2D( const geometry::RealPoint2D& rPoint ) + { + return ::basegfx::B2DPoint( rPoint.X, + rPoint.Y ); + } + + ::basegfx::B2DRange b2DRectangleFromRealRectangle2D( const geometry::RealRectangle2D& rRect ) + { + return ::basegfx::B2DRange( rRect.X1, + rRect.Y1, + rRect.X2, + rRect.Y2 ); + } + + ::basegfx::B3DRange b3DRectangleFromRealRectangle3D( const geometry::RealRectangle3D& rRect ) + { + return ::basegfx::B3DRange( rRect.X1, + rRect.Y1, + rRect.Z1, + rRect.X2, + rRect.Y2, + rRect.Z2); + } + + geometry::IntegerSize2D integerSize2DFromB2ISize( const ::basegfx::B2IVector& rSize ) + { + return geometry::IntegerSize2D( rSize.getX(), + rSize.getY() ); + } + + geometry::IntegerPoint2D integerPoint2DFromB2IPoint( const ::basegfx::B2IPoint& rPoint ) + { + return geometry::IntegerPoint2D( rPoint.getX(), + rPoint.getY() ); + } + + geometry::IntegerRectangle2D integerRectangle2DFromB2IRectangle( const ::basegfx::B2IRange& rRectangle ) + { + return geometry::IntegerRectangle2D( rRectangle.getMinX(), rRectangle.getMinY(), + rRectangle.getMaxX(), rRectangle.getMaxY() ); + } + + ::basegfx::B2IVector b2ISizeFromIntegerSize2D( const geometry::IntegerSize2D& rSize ) + { + return ::basegfx::B2IVector( rSize.Width, + rSize.Height ); + } + + ::basegfx::B2IPoint b2IPointFromIntegerPoint2D( const geometry::IntegerPoint2D& rPoint ) + { + return ::basegfx::B2IPoint( rPoint.X, + rPoint.Y ); + } + + ::basegfx::B2IRange b2IRectangleFromIntegerRectangle2D( const geometry::IntegerRectangle2D& rRectangle ) + { + return ::basegfx::B2IRange( rRectangle.X1, rRectangle.Y1, + rRectangle.X2, rRectangle.Y2 ); + } + + awt::Size awtSizeFromB2ISize( const ::basegfx::B2IVector& rVec ) + { + return awt::Size( rVec.getX(), + rVec.getY() ); + } + + awt::Point awtPointFromB2IPoint( const ::basegfx::B2IPoint& rPoint ) + { + return awt::Point( rPoint.getX(), + rPoint.getY() ); + } + + awt::Rectangle awtRectangleFromB2IRectangle( const ::basegfx::B2IRange& rRect ) + { + OSL_ENSURE( rRect.getWidth() < ::std::numeric_limits< sal_Int32 >::max() && + rRect.getWidth() > ::std::numeric_limits< sal_Int32 >::min(), + "awtRectangleFromB2IRectangle(): width overflow" ); + OSL_ENSURE( rRect.getHeight() < ::std::numeric_limits< sal_Int32 >::max() && + rRect.getHeight() > ::std::numeric_limits< sal_Int32 >::min(), + "awtRectangleFromB2IRectangle(): height overflow" ); + + return awt::Rectangle( rRect.getMinX(), + rRect.getMinY(), + static_cast< sal_Int32 >(rRect.getWidth()), + static_cast< sal_Int32 >(rRect.getHeight()) ); + } + + ::basegfx::B2IVector b2ISizeFromAwtSize( const awt::Size& rSize ) + { + return ::basegfx::B2IVector( rSize.Width, + rSize.Height ); + } + + ::basegfx::B2IPoint b2IPointFromAwtPoint( const awt::Point& rPoint ) + { + return ::basegfx::B2IPoint( rPoint.X, + rPoint.Y ); + } + + ::basegfx::B2IRange b2IRectangleFromAwtRectangle( const awt::Rectangle& rRect ) + { + return ::basegfx::B2IRange( rRect.X, + rRect.Y, + rRect.X + rRect.Width, + rRect.Y + rRect.Height ); + } + + ::basegfx::B2IRange b2ISurroundingRangeFromB2DRange( const ::basegfx::B2DRange& rRange ) + { + return ::basegfx::B2IRange( static_cast<sal_Int32>( floor(rRange.getMinX()) ), + static_cast<sal_Int32>( floor(rRange.getMinY()) ), + static_cast<sal_Int32>( ceil(rRange.getMaxX()) ), + static_cast<sal_Int32>( ceil(rRange.getMaxY()) ) ); + } + + ::basegfx::B2DRange b2DSurroundingIntegerRangeFromB2DRange( const ::basegfx::B2DRange& rRange ) + { + return ::basegfx::B2DRange( floor(rRange.getMinX()), + floor(rRange.getMinY()), + ceil(rRange.getMaxX()), + ceil(rRange.getMaxY()) ); + } + + // Geometry comparisons + // =================================================================== + + bool RealSize2DAreEqual( const ::com::sun::star::geometry::RealSize2D& rA, const ::com::sun::star::geometry::RealSize2D& rB ) + { + return (rA.Width == rB.Width && rA.Height == rB.Height); + } + + bool RealPoint2DAreEqual( const ::com::sun::star::geometry::RealPoint2D& rA, const ::com::sun::star::geometry::RealPoint2D& rB ) + { + return (rA.X == rB.X && rA.Y == rB.Y); + } + + bool RealRectangle2DAreEqual( const ::com::sun::star::geometry::RealRectangle2D& rA, const ::com::sun::star::geometry::RealRectangle2D& rB ) + { + return (rA.X1 == rB.X1 && rA.Y1 == rB.Y1 && rA.X2 == rB.X2 && rA.Y2 == rB.Y2); + } + + bool RealRectangle3DAreEqual( const ::com::sun::star::geometry::RealRectangle3D& rA, const ::com::sun::star::geometry::RealRectangle3D& rB ) + { + return (rA.X1 == rB.X1 && rA.Y1 == rB.Y1 && rA.Z1 == rB.Z1 && rA.X2 == rB.X2 && rA.Y2 == rB.Y2 && rA.Z2 == rB.Z2); + } + + bool AffineMatrix2DAreEqual( const ::com::sun::star::geometry::AffineMatrix2D& rA, const ::com::sun::star::geometry::AffineMatrix2D& rB ) + { + return (rA.m00 == rB.m00 && rA.m01 == rB.m01 && rA.m02 == rB.m02 && rA.m10 == rB.m10 && rA.m11 == rB.m11 && rA.m12 == rB.m12); + } + + bool IntegerSize2DAreEqual( const ::com::sun::star::geometry::IntegerSize2D& rA, const ::com::sun::star::geometry::IntegerSize2D& rB ) + { + return (rA.Width == rB.Width && rA.Height == rB.Height); + } + + bool IntegerPoint2DAreEqual( const ::com::sun::star::geometry::IntegerPoint2D& rA, const ::com::sun::star::geometry::IntegerPoint2D& rB ) + { + return (rA.X == rB.X && rA.Y == rB.Y); + } + + bool IntegerRectangle2DAreEqual( const ::com::sun::star::geometry::IntegerRectangle2D& rA, const ::com::sun::star::geometry::IntegerRectangle2D& rB ) + { + return (rA.X1 == rB.X1 && rA.Y1 == rB.Y1 && rA.X2 == rB.X2 && rA.Y2 == rB.Y2); + } + + bool awtSizeAreEqual( const ::com::sun::star::awt::Size& rA, const ::com::sun::star::awt::Size& rB ) + { + return (rA.Width == rB.Width && rA.Height == rB.Height); + } + + bool awtPointAreEqual( const ::com::sun::star::awt::Point& rA, const ::com::sun::star::awt::Point& rB ) + { + return (rA.X == rB.X && rA.Y == rB.Y); + } + + bool awtRectangleAreEqual( const ::com::sun::star::awt::Rectangle& rA, const ::com::sun::star::awt::Rectangle& rB ) + { + return (rA.X == rB.X && rA.Y == rB.Y && rA.Width == rB.Width && rA.Height == rB.Height); + } + } // namespace bgfxtools + +} // namespace canvas + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basegfx/source/tools/debugplotter.cxx b/basegfx/source/tools/debugplotter.cxx new file mode 100644 index 000000000000..1e88e3470a45 --- /dev/null +++ b/basegfx/source/tools/debugplotter.cxx @@ -0,0 +1,418 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" +#include <osl/diagnose.h> + +#include <basegfx/curve/b2dcubicbezier.hxx> + +#include <basegfx/tools/debugplotter.hxx> +#include <boost/bind.hpp> + +#include <ostream> + + +namespace basegfx +{ + namespace + { + void outputHeader( const ::rtl::OString& rTitle, + ::std::ostream* pStm ) + { + // output gnuplot setup + if( pStm ) + { + *pStm << "#!/usr/bin/gnuplot -persist" << ::std::endl << + "#" << ::std::endl << + "# automatically generated by basegfx, don't change!" << ::std::endl << + "#" << ::std::endl << + "# --- " << (const sal_Char*)rTitle << " ---" << ::std::endl << + "#" << ::std::endl << + "set parametric" << ::std::endl << + "# set terminal postscript eps enhanced color " << ::std::endl << + "# set output \"plot.eps\"" << ::std::endl << + // This function plots a cubic bezier curve. P,q,r,s + // are the control point elements of the corresponding + // output coordinate component (i.e. x components for + // the x plot, and y components for the y plot) + "cubicBezier(p,q,r,s,t) = p*(1-t)**3+q*3*(1-t)**2*t+r*3*(1-t)*t**2+s*t**3" << ::std::endl << + // This function plots the derivative of a cubic + // bezier curve. P,q,r,s are the control point + // components of the _original_ curve + "cubicBezDerivative(p,q,r,s,t) = 3*(q-p)*(1-t)**2+6*(r-q)*(1-t)*t+3*(s-r)*t**2" << ::std::endl << + // Plot a line's component of a line between a and b + // (where a and b should be the corresponding + // components of the line's start and end point, + // respectively) + "line(p,q,r) = p*(1-t)+q*t" << ::std::endl << + // Plot a line's x component of a line in implicit + // form ax + by + c = 0 + "implicitLineX(a,b,c,t) = a*-c + t*-b" << ::std::endl << + // Plot a line's y component of a line in implicit + // form ax + by + c = 0 + "implicitLineY(a,b,c,t) = b*-c + t*a" << ::std::endl << + "pointmarkx(c,t) = c-0.03*t" << ::std::endl << // hack for displaying single points in parametric form + "pointmarky(c,t) = c+0.03*t" << ::std::endl << // hack for displaying single points in parametric form + "# end of setup" << ::std::endl; + } + else + { + OSL_TRACE( "#!/usr/bin/gnuplot -persist\n", + "#\n", + "# automatically generated by basegfx, don't change!\n", + "#\n", + "# --- %s ---\n", + "#\n", + "set parametric\n", + // This function plots a cubic bezier curve. P,q,r,s + // are the control point elements of the corresponding + // output coordinate component (i.e. x components for + // the x plot, and y components for the y plot) + "cubicBezier(p,q,r,s,t) = p*(1-t)**3+q*3*(1-t)**2*t+r*3*(1-t)*t**2+s*t**3\n", + // This function plots the derivative of a cubic + // bezier curve. P,q,r,s are the control point + // components of the _original_ curve + "cubicBezDerivative(p,q,r,s,t) = 3*(q-p)*(1-t)**2+6*(r-q)*(1-t)*t+3*(s-r)*t**2\n", + // Plot a line's component of a line between a and b + // (where a and b should be the corresponding + // components of the line's start and end point, + // respectively) + "line(p,q,r) = p*(1-t)+q*t\n", + // Plot a line's x component of a line in implicit + // form ax + by + c = 0 + "implicitLineX(a,b,c,t) = a*-c + t*-b\n", + // Plot a line's y component of a line in implicit + // form ax + by + c = 0 + "implicitLineY(a,b,c,t) = b*-c + t*a\n", + "pointmarkx(c,t) = c-0.03*t\n", // hack for displaying single points in parametric form + "pointmarky(c,t) = c+0.03*t\n", // hack for displaying single points in parametric form + "# end of setup\n", + (const sal_Char*)rTitle ); + } + } + + class Writer + { + public: + Writer( ::std::ostream* pStm ) : + mpStream( pStm ) + { + } + + void outputPoint( const ::std::pair< B2DPoint, ::rtl::OString >& rElem ) + { + if( mpStream ) + *mpStream << " " << rElem.first.getX() << "\t" << rElem.first.getY() << ::std::endl; + else + OSL_TRACE( " %f\t%f\n", rElem.first.getX(), rElem.first.getY() ); + } + + void outputVector( const ::std::pair< B2DVector, ::rtl::OString >& rElem ) + { + if( mpStream ) + *mpStream << " " << rElem.first.getX() << "\t" << rElem.first.getY() << ::std::endl << ::std::endl; + else + OSL_TRACE( " %f\t%f\n\n", rElem.first.getX(), rElem.first.getY() ); + } + + void outputRect( const ::std::pair< B2DRange, ::rtl::OString >& rElem ) + { + const double nX0( rElem.first.getMinX() ); + const double nY0( rElem.first.getMinY() ); + const double nX1( rElem.first.getMaxX() ); + const double nY1( rElem.first.getMaxY() ); + + if( mpStream ) + *mpStream << " " + << nX0 << "\t" << nY0 << "\t" + << nX1 << "\t" << nY0 << "\t" + << nX1 << "\t" << nY1 << "\t" + << nX0 << "\t" << nY1 << "\t" + << nX0 << "\t" << nY0 << ::std::endl << ::std::endl; + + else + OSL_TRACE( " %f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n\n", + nX0, nY0, + nX1, nY0, + nX1, nY1, + nX0, nY1, + nX0, nY0 ); + } + + private: + ::std::ostream* mpStream; + }; + } + + DebugPlotter::DebugPlotter( const sal_Char* pTitle ) : + maTitle( pTitle ), + maPoints(), + maVectors(), + maRanges(), + maPolygons(), + mpOutputStream(NULL) + { + } + + DebugPlotter::DebugPlotter( const sal_Char* pTitle, + ::std::ostream& rOutputStream ) : + maTitle( pTitle ), + maPoints(), + maVectors(), + maRanges(), + maPolygons(), + mpOutputStream(&rOutputStream) + { + } + + DebugPlotter::~DebugPlotter() + { + const bool bHavePoints( !maPoints.empty() ); + const bool bHaveVectors( !maVectors.empty() ); + const bool bHaveRanges( !maRanges.empty() ); + const bool bHavePolygons( !maPolygons.empty() ); + + if( bHavePoints || + bHaveVectors || + bHaveRanges || + bHavePolygons ) + { + outputHeader( maTitle, mpOutputStream ); + + print( "\n\n# parametric primitive output\n" + "plot [t=0:1] \\\n" ); + + // output plot declarations for used entities + bool bNeedColon( false ); + if( bHavePoints ) + { + print( " '-' using ($1):($2) title \"Points\" with points" ); + bNeedColon = true; + } + if( bHaveVectors ) + { + if( bNeedColon ) + print( ", \\\n" ); + + print( " '-' using ($1):($2) title \"Vectors\" with lp" ); + bNeedColon = true; + } + if( bHaveRanges ) + { + if( bNeedColon ) + print( ", \\\n" ); + + print( " '-' using ($1):($2) title \"Ranges\" with lines" ); + bNeedColon = true; + } + if( bHavePolygons ) + { + const ::std::size_t nSize( maPolygons.size() ); + for( ::std::size_t i=0; i<nSize; ++i ) + { + if( maPolygons.at(i).first.areControlPointsUsed() ) + { + const B2DPolygon& rCurrPoly( maPolygons.at(i).first ); + + const sal_uInt32 nCount( rCurrPoly.count() ); + for( sal_uInt32 k=0; k<nCount; ++k ) + { + if( bNeedColon ) + print( ", \\\n" ); + + const B2DPoint& rP0( rCurrPoly.getB2DPoint(k) ); + const B2DPoint& rP1( rCurrPoly.getNextControlPoint(k) ); + const B2DPoint& rP2( rCurrPoly.getPrevControlPoint((k + 1) % nCount) ); + const B2DPoint& rP3( k+1<nCount ? rCurrPoly.getB2DPoint(k+1) : rCurrPoly.getB2DPoint(k) ); + + if( mpOutputStream ) + *mpOutputStream << " cubicBezier(" + << rP0.getX() << "," + << rP1.getX() << "," + << rP2.getX() << "," + << rP3.getX() << ",t), \\\n cubicBezier(" + << rP0.getY() << "," + << rP1.getY() << "," + << rP2.getY() << "," + << rP3.getY() << ",t)"; + else + OSL_TRACE( " cubicBezier(%f,%f,%f,%f,t), \\\n" + " cubicBezier(%f,%f,%f,%f,t)", + rP0.getX(), + rP1.getX(), + rP2.getX(), + rP3.getX(), + rP0.getY(), + rP1.getY(), + rP2.getY(), + rP3.getY() ); + + bNeedColon = true; + } + } + else + { + if( bNeedColon ) + print( ", \\\n" ); + + if( mpOutputStream ) + *mpOutputStream << " '-' using ($1):($2) title \"Polygon " + << (const sal_Char*)maPolygons.at(i).second << "\" with lp"; + else + OSL_TRACE( " '-' using ($1):($2) title \"Polygon %s\" with lp", + (const sal_Char*)maPolygons.at(i).second ); + + bNeedColon = true; + } + } + } + + if( bHavePoints ) + { + Writer aWriter( mpOutputStream ); + + ::std::for_each( maPoints.begin(), + maPoints.end(), + ::boost::bind( &Writer::outputPoint, + ::boost::ref( aWriter ), + _1 ) ); + print( "e\n" ); + } + + if( bHaveVectors ) + { + Writer aWriter( mpOutputStream ); + + ::std::for_each( maVectors.begin(), + maVectors.end(), + ::boost::bind( &Writer::outputVector, + ::boost::ref( aWriter ), + _1 ) ); + print( "e\n" ); + } + + if( bHaveRanges ) + { + Writer aWriter( mpOutputStream ); + + ::std::for_each( maRanges.begin(), + maRanges.end(), + ::boost::bind( &Writer::outputRect, + ::boost::ref( aWriter ), + _1 ) ); + print( "e\n" ); + } + + if( bHavePolygons ) + { + const ::std::size_t nSize( maPolygons.size() ); + for( ::std::size_t i=0; i<nSize; ++i ) + { + if( !maPolygons.at(i).first.areControlPointsUsed() ) + { + const B2DPolygon& rCurrPoly( maPolygons.at(i).first ); + + const sal_uInt32 nCount( rCurrPoly.count() ); + for( sal_uInt32 k=0; k<nCount; ++k ) + { + const B2DPoint& rP( rCurrPoly.getB2DPoint(k) ); + + if( mpOutputStream ) + *mpOutputStream << " " << rP.getX() << "," << rP.getY(); + else + OSL_TRACE( " %f,%f", + rP.getX(), + rP.getX() ); + } + + print( "\ne\n" ); + } + } + } + } + } + + void DebugPlotter::plot( const B2DPoint& rPoint, + const sal_Char* pTitle ) + { + maPoints.push_back( ::std::make_pair( rPoint, + ::rtl::OString( pTitle ) ) ); + } + + void DebugPlotter::plot( const B2DVector& rVec, + const sal_Char* pTitle ) + { + maVectors.push_back( ::std::make_pair( rVec, + ::rtl::OString( pTitle ) ) ); + } + + void DebugPlotter::plot( const B2DCubicBezier& rBezier, + const sal_Char* pTitle ) + { + B2DPolygon aPoly; + aPoly.append(rBezier.getStartPoint()); + aPoly.appendBezierSegment(rBezier.getControlPointA(), rBezier.getControlPointB(), rBezier.getEndPoint()); + maPolygons.push_back( ::std::make_pair( aPoly, + ::rtl::OString( pTitle ) ) ); + } + + void DebugPlotter::plot( const B2DRange& rRange, + const sal_Char* pTitle ) + { + maRanges.push_back( ::std::make_pair( rRange, + ::rtl::OString( pTitle ) ) ); + } + + void DebugPlotter::plot( const B2DPolygon& rPoly, + const sal_Char* pTitle ) + { + maPolygons.push_back( ::std::make_pair( rPoly, + ::rtl::OString( pTitle ) ) ); + } + + void DebugPlotter::plot( const B2DPolyPolygon& rPoly, + const sal_Char* pTitle ) + { + const ::rtl::OString aTitle( pTitle ); + const sal_uInt32 nCount( rPoly.count() ); + for( sal_uInt32 i=0; i<nCount; ++i ) + maPolygons.push_back( ::std::make_pair( rPoly.getB2DPolygon( i ), + aTitle ) ); + } + + void DebugPlotter::print( const sal_Char* pStr ) + { + if( mpOutputStream ) + *mpOutputStream << pStr; + else + OSL_TRACE( pStr ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basegfx/source/tools/gradienttools.cxx b/basegfx/source/tools/gradienttools.cxx new file mode 100644 index 000000000000..740db5cba1bc --- /dev/null +++ b/basegfx/source/tools/gradienttools.cxx @@ -0,0 +1,352 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" + +#include <basegfx/tools/gradienttools.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +namespace basegfx +{ + /** Most of the setup for linear & axial gradient is the same, except + for the border treatment. Factored out here. + */ + static void init1DGradientInfo(ODFGradientInfo& o_rGradientInfo, + const B2DRange& rTargetRange, + sal_uInt32 nSteps, + double fBorder, + double fAngle, + bool bAxial) + { + o_rGradientInfo.maTextureTransform.identity(); + o_rGradientInfo.maBackTextureTransform.identity(); + o_rGradientInfo.mnSteps = nSteps; + + fAngle = -fAngle; + + double fTargetSizeX(rTargetRange.getWidth()); + double fTargetSizeY(rTargetRange.getHeight()); + double fTargetOffsetX(rTargetRange.getMinX()); + double fTargetOffsetY(rTargetRange.getMinY()); + + // add object expansion + if(0.0 != fAngle) + { + const double fAbsCos(fabs(cos(fAngle))); + const double fAbsSin(fabs(sin(fAngle))); + const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin); + const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin); + fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0; + fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0; + fTargetSizeX = fNewX; + fTargetSizeY = fNewY; + } + + const double fSizeWithoutBorder=1.0 - fBorder; + if( bAxial ) + { + o_rGradientInfo.maTextureTransform.scale(1.0, fSizeWithoutBorder * .5); + o_rGradientInfo.maTextureTransform.translate(0.0, 0.5); + } + else + { + if(!fTools::equal(fSizeWithoutBorder, 1.0)) + { + o_rGradientInfo.maTextureTransform.scale(1.0, fSizeWithoutBorder); + o_rGradientInfo.maTextureTransform.translate(0.0, fBorder); + } + } + + o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY); + + // add texture rotate after scale to keep perpendicular angles + if(0.0 != fAngle) + { + const B2DPoint aCenter(0.5*fTargetSizeX, + 0.5*fTargetSizeY); + o_rGradientInfo.maTextureTransform *= + basegfx::tools::createRotateAroundPoint(aCenter, fAngle); + } + + // add object translate + o_rGradientInfo.maTextureTransform.translate(fTargetOffsetX, fTargetOffsetY); + + // prepare aspect for texture + o_rGradientInfo.mfAspectRatio = (0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0; + + // build transform from u,v to [0.0 .. 1.0]. + o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform; + o_rGradientInfo.maBackTextureTransform.invert(); + } + + /** Most of the setup for radial & ellipsoidal gradient is the same, + except for the border treatment. Factored out here. + */ + static void initEllipticalGradientInfo(ODFGradientInfo& o_rGradientInfo, + const B2DRange& rTargetRange, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder, + double fAngle, + bool bCircular) + { + o_rGradientInfo.maTextureTransform.identity(); + o_rGradientInfo.maBackTextureTransform.identity(); + o_rGradientInfo.mnSteps = nSteps; + + fAngle = -fAngle; + + double fTargetSizeX(rTargetRange.getWidth()); + double fTargetSizeY(rTargetRange.getHeight()); + double fTargetOffsetX(rTargetRange.getMinX()); + double fTargetOffsetY(rTargetRange.getMinY()); + + // add object expansion + if( bCircular ) + { + const double fOriginalDiag(sqrt((fTargetSizeX * fTargetSizeX) + (fTargetSizeY * fTargetSizeY))); + fTargetOffsetX -= (fOriginalDiag - fTargetSizeX) / 2.0; + fTargetOffsetY -= (fOriginalDiag - fTargetSizeY) / 2.0; + fTargetSizeX = fOriginalDiag; + fTargetSizeY = fOriginalDiag; + } + else + { + fTargetOffsetX -= (0.4142 / 2.0 ) * fTargetSizeX; + fTargetOffsetY -= (0.4142 / 2.0 ) * fTargetSizeY; + fTargetSizeX = 1.4142 * fTargetSizeX; + fTargetSizeY = 1.4142 * fTargetSizeY; + } + + const double fHalfBorder((1.0 - fBorder) * 0.5); + o_rGradientInfo.maTextureTransform.scale(fHalfBorder, fHalfBorder); + + o_rGradientInfo.maTextureTransform.translate(0.5, 0.5); + o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY); + + // add texture rotate after scale to keep perpendicular angles + if( !bCircular && 0.0 != fAngle) + { + const B2DPoint aCenter(0.5*fTargetSizeX, + 0.5*fTargetSizeY); + o_rGradientInfo.maTextureTransform *= + basegfx::tools::createRotateAroundPoint(aCenter, fAngle); + } + + // add defined offsets after rotation + if(0.5 != rOffset.getX() || 0.5 != rOffset.getY()) + { + // use original target size + fTargetOffsetX += (rOffset.getX() - 0.5) * rTargetRange.getWidth(); + fTargetOffsetY += (rOffset.getY() - 0.5) * rTargetRange.getHeight(); + } + + // add object translate + o_rGradientInfo.maTextureTransform.translate(fTargetOffsetX, fTargetOffsetY); + + // prepare aspect for texture + o_rGradientInfo.mfAspectRatio = (0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0; + + // build transform from u,v to [0.0 .. 1.0]. + o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform; + o_rGradientInfo.maBackTextureTransform.invert(); + } + + /** Setup for rect & square gradient is exactly the same. Factored out + here. + */ + static void initRectGradientInfo(ODFGradientInfo& o_rGradientInfo, + const B2DRange& rTargetRange, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + o_rGradientInfo.maTextureTransform.identity(); + o_rGradientInfo.maBackTextureTransform.identity(); + o_rGradientInfo.mnSteps = nSteps; + + fAngle = -fAngle; + + double fTargetSizeX(rTargetRange.getWidth()); + double fTargetSizeY(rTargetRange.getHeight()); + double fTargetOffsetX(rTargetRange.getMinX()); + double fTargetOffsetY(rTargetRange.getMinY()); + + // add object expansion + if(0.0 != fAngle) + { + const double fAbsCos(fabs(cos(fAngle))); + const double fAbsSin(fabs(sin(fAngle))); + const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin); + const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin); + fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0; + fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0; + fTargetSizeX = fNewX; + fTargetSizeY = fNewY; + } + + const double fHalfBorder((1.0 - fBorder) * 0.5); + o_rGradientInfo.maTextureTransform.scale(fHalfBorder, fHalfBorder); + + o_rGradientInfo.maTextureTransform.translate(0.5, 0.5); + o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY); + + // add texture rotate after scale to keep perpendicular angles + if(0.0 != fAngle) + { + const B2DPoint aCenter(0.5*fTargetSizeX, + 0.5*fTargetSizeY); + o_rGradientInfo.maTextureTransform *= + basegfx::tools::createRotateAroundPoint(aCenter, fAngle); + } + + // add defined offsets after rotation + if(0.5 != rOffset.getX() || 0.5 != rOffset.getY()) + { + // use scaled target size + fTargetOffsetX += (rOffset.getX() - 0.5) * fTargetSizeX; + fTargetOffsetY += (rOffset.getY() - 0.5) * fTargetSizeY; + } + + // add object translate + o_rGradientInfo.maTextureTransform.translate(fTargetOffsetX, fTargetOffsetY); + + // prepare aspect for texture + o_rGradientInfo.mfAspectRatio = (0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0; + + // build transform from u,v to [0.0 .. 1.0]. As base, use inverse texture transform + o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform; + o_rGradientInfo.maBackTextureTransform.invert(); + } + + namespace tools + { + ODFGradientInfo& createLinearODFGradientInfo(ODFGradientInfo& o_rGradientInfo, + const B2DRange& rTargetArea, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + init1DGradientInfo(o_rGradientInfo, + rTargetArea, + nSteps, + fBorder, + fAngle, + false); + return o_rGradientInfo; + } + + ODFGradientInfo& createAxialODFGradientInfo(ODFGradientInfo& o_rGradientInfo, + const B2DRange& rTargetArea, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + init1DGradientInfo(o_rGradientInfo, + rTargetArea, + nSteps, + fBorder, + fAngle, + true); + return o_rGradientInfo; + } + + ODFGradientInfo& createRadialODFGradientInfo(ODFGradientInfo& o_rGradientInfo, + const B2DRange& rTargetArea, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder) + { + initEllipticalGradientInfo(o_rGradientInfo, + rTargetArea, + rOffset, + nSteps, + fBorder, + 0.0, + true); + return o_rGradientInfo; + } + + ODFGradientInfo& createEllipticalODFGradientInfo(ODFGradientInfo& o_rGradientInfo, + const B2DRange& rTargetArea, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + initEllipticalGradientInfo(o_rGradientInfo, + rTargetArea, + rOffset, + nSteps, + fBorder, + fAngle, + false); + return o_rGradientInfo; + } + + ODFGradientInfo& createSquareODFGradientInfo(ODFGradientInfo& o_rGradientInfo, + const B2DRange& rTargetArea, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + initRectGradientInfo(o_rGradientInfo, + rTargetArea, + rOffset, + nSteps, + fBorder, + fAngle); + return o_rGradientInfo; + } + + ODFGradientInfo& createRectangularODFGradientInfo(ODFGradientInfo& o_rGradientInfo, + const B2DRange& rTargetArea, + const B2DVector& rOffset, + sal_uInt32 nSteps, + double fBorder, + double fAngle) + { + initRectGradientInfo(o_rGradientInfo, + rTargetArea, + rOffset, + nSteps, + fBorder, + fAngle); + return o_rGradientInfo; + } + + } // namespace tools + +} // namespace basegfx + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basegfx/source/tools/keystoplerp.cxx b/basegfx/source/tools/keystoplerp.cxx new file mode 100644 index 000000000000..7f8dcd70abc2 --- /dev/null +++ b/basegfx/source/tools/keystoplerp.cxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: canvastools.hxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" + +#include "basegfx/tools/keystoplerp.hxx" +#include <com/sun/star/uno/Sequence.hxx> + +#include <algorithm> + +static void validateInput(const std::vector<double>& rKeyStops) +{ + (void)rKeyStops; +#ifdef DBG_UTIL + OSL_ENSURE( rKeyStops.size() > 1, + "KeyStopLerp::KeyStopLerp(): key stop vector must have two entries or more" ); + + // rKeyStops must be sorted in ascending order + for( ::std::size_t i=1, len=rKeyStops.size(); i<len; ++i ) + { + if( rKeyStops[i-1] > rKeyStops[i] ) + OSL_ENSURE( false, + "KeyStopLerp::KeyStopLerp(): time vector is not sorted in ascending order!" ); + } +#endif +} + +namespace basegfx +{ + namespace tools + { + KeyStopLerp::KeyStopLerp( const std::vector<double>& rKeyStops ) : + maKeyStops(rKeyStops), + mnLastIndex(0) + { + validateInput(maKeyStops); + } + + KeyStopLerp::KeyStopLerp( const ::com::sun::star::uno::Sequence<double>& rKeyStops ) : + maKeyStops(rKeyStops.getLength()), + mnLastIndex(0) + { + std::copy( rKeyStops.getConstArray(), + rKeyStops.getConstArray()+rKeyStops.getLength(), + maKeyStops.begin() ); + validateInput(maKeyStops); + } + + KeyStopLerp::ResultType KeyStopLerp::lerp(double fAlpha) const + { + // cached value still okay? + if( maKeyStops.at(mnLastIndex) < fAlpha || + maKeyStops.at(mnLastIndex+1) >= fAlpha ) + { + // nope, find new index + mnLastIndex = std::min<std::ptrdiff_t>( + maKeyStops.size()-2, + // range is ensured by max below + std::max<std::ptrdiff_t>( + 0, + std::distance( maKeyStops.begin(), + std::lower_bound( maKeyStops.begin(), + maKeyStops.end(), + fAlpha )) - 1 )); + } + + // lerp between stop and stop+1 + const double fRawLerp= + (fAlpha-maKeyStops.at(mnLastIndex)) / + (maKeyStops.at(mnLastIndex+1) - maKeyStops.at(mnLastIndex)); + + // clamp to permissible range (input fAlpha might be + // everything) + return ResultType( + mnLastIndex, + clamp(fRawLerp,0.0,1.0)); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basegfx/source/tools/liangbarsky.cxx b/basegfx/source/tools/liangbarsky.cxx new file mode 100644 index 000000000000..5c1d25918ed5 --- /dev/null +++ b/basegfx/source/tools/liangbarsky.cxx @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" + +#include "basegfx/tools/tools.hxx" +#include "basegfx/numeric/ftools.hxx" +#include "basegfx/range/b2drange.hxx" + + +namespace basegfx +{ + namespace tools + { + namespace + { + // see Foley/vanDam, pp. 122 for the Liang-Barsky line + // clipping algorithm + inline bool liangBarskyClipT( double nDenom, + double nNumerator, + double& io_rTE, + double& io_rTL ) + { + double t; + if( nDenom > 0 ) + { + t = nNumerator / nDenom; + if( t > io_rTL ) + return false; + else if( t > io_rTE ) + io_rTE = t; + } + else if( nDenom < 0 ) + { + t = nNumerator / nDenom; + if( t < io_rTE ) + return false; + else + io_rTL = t; + } + else if( nNumerator > 0 ) + { + return false; + } + + return true; + } + } + + // see Foley/vanDam, pp. 122 for the Liang-Barsky line + // clipping algorithm + bool liangBarskyClip2D( ::basegfx::B2DPoint& io_rStart, + ::basegfx::B2DPoint& io_rEnd, + const ::basegfx::B2DRange& rClipRect ) + { + const double nDX( io_rEnd.getX() - io_rStart.getX() ); + const double nDY( io_rEnd.getY() - io_rStart.getY() ); + + if( ::basegfx::fTools::equalZero( nDX ) && + ::basegfx::fTools::equalZero( nDY ) ) + { + return rClipRect.isInside( io_rStart ); + } + else + { + double nTE( 0.0 ); + double nTL( 1.0 ); + if( liangBarskyClipT(nDX, rClipRect.getMinX() - io_rStart.getX(), + nTE, nTL ) ) // inside wrt. left edge + { + if( liangBarskyClipT(-nDX, io_rStart.getX() - rClipRect.getMaxX(), + nTE, nTL ) ) // inside wrt. right edge + { + if( liangBarskyClipT(nDY, rClipRect.getMinY() - io_rStart.getY(), + nTE, nTL ) ) // inside wrt. bottom edge + { + if( liangBarskyClipT(-nDY, io_rStart.getY() - rClipRect.getMaxY(), + nTE, nTL ) ) // inside wrt. top edge + { + // compute actual intersection points, + // if nTL has changed + if( nTL < 1.0 ) + { + io_rEnd.setX( io_rStart.getX() + nTL*nDX ); + io_rEnd.setY( io_rStart.getY() + nTL*nDY ); + } + + // compute actual intersection points, + // if nTE has changed + if( nTE > 0.0 ) + { + io_rStart.setX( io_rStart.getX() + nTE*nDX ); + io_rStart.setY( io_rStart.getY() + nTE*nDY ); + } + + // line is (at least partially) visible + return true; + } + } + } + } + } + + return false; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basegfx/source/tools/makefile.mk b/basegfx/source/tools/makefile.mk new file mode 100755 index 000000000000..0a0977f7305d --- /dev/null +++ b/basegfx/source/tools/makefile.mk @@ -0,0 +1,51 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. +PRJNAME=basegfx +TARGET=tools + +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ---------------------------------- + +.INCLUDE : settings.mk + +# --- Files ------------------------------------- + +SLOFILES= $(SLO)$/b2dclipstate.obj \ + $(SLO)$/canvastools.obj \ + $(SLO)$/gradienttools.obj \ + $(SLO)$/debugplotter.obj \ + $(SLO)$/keystoplerp.obj \ + $(SLO)$/liangbarsky.obj \ + $(SLO)$/tools.obj \ + $(SLO)$/unopolypolygon.obj + +# --- Targets ---------------------------------- + +.INCLUDE : target.mk diff --git a/basegfx/source/tools/tools.cxx b/basegfx/source/tools/tools.cxx new file mode 100644 index 000000000000..d7bdbccbe372 --- /dev/null +++ b/basegfx/source/tools/tools.cxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" + +#include "basegfx/tools/tools.hxx" +#include "basegfx/range/b2drange.hxx" + +#include <algorithm> + + +namespace basegfx +{ + namespace tools + { + namespace + { + inline double distance( const double& nX, + const double& nY, + const ::basegfx::B2DVector& rNormal, + const double& nC ) + { + return nX*rNormal.getX() + nY*rNormal.getY() - nC; + } + + void moveLineOutsideRect( ::basegfx::B2DPoint& io_rStart, + ::basegfx::B2DPoint& io_rEnd, + const ::basegfx::B2DVector& rMoveDirection, + const ::basegfx::B2DRange& rFitTarget ) + { + // calc c for normal line form equation n x - c = 0 + const double nC( rMoveDirection.scalar( io_rStart ) ); + + // calc maximum orthogonal distance for all four bound + // rect corners to the line + const double nMaxDistance( ::std::max( + 0.0, + ::std::max( + distance(rFitTarget.getMinX(), + rFitTarget.getMinY(), + rMoveDirection, + nC), + ::std::max( + distance(rFitTarget.getMinX(), + rFitTarget.getMaxY(), + rMoveDirection, + nC), + ::std::max( + distance(rFitTarget.getMaxX(), + rFitTarget.getMinY(), + rMoveDirection, + nC), + distance(rFitTarget.getMaxX(), + rFitTarget.getMaxY(), + rMoveDirection, + nC) ) ) ) ) ); + + // now move line points, such that the bound rect + // points are all either 'on' or on the negative side + // of the half-plane + io_rStart += nMaxDistance*rMoveDirection; + io_rEnd += nMaxDistance*rMoveDirection; + } + } + + void infiniteLineFromParallelogram( ::basegfx::B2DPoint& io_rLeftTop, + ::basegfx::B2DPoint& io_rLeftBottom, + ::basegfx::B2DPoint& io_rRightTop, + ::basegfx::B2DPoint& io_rRightBottom, + const ::basegfx::B2DRange& rFitTarget ) + { + // For the top and bottom border line of the + // parallelogram, we determine the distance to all four + // corner points of the bound rect (tl, tr, bl, br). When + // using the unit normal form for lines (n x - c = 0), and + // choosing n to point 'outwards' the parallelogram, then + // all bound rect corner points having positive distance + // to the line lie outside the extended gradient rect, and + // thus, the corresponding border line must be moved the + // maximum distance outwards. + + // don't use the top and bottom border line direction, and + // calculate the normal from them. Instead, use the + // vertical lines (lt - lb or rt - rb), as they more + // faithfully represent the direction of the + // to-be-generated infinite line + ::basegfx::B2DVector aDirectionVertical( io_rLeftTop - io_rLeftBottom ); + aDirectionVertical.normalize(); + + const ::basegfx::B2DVector aNormalTop( aDirectionVertical ); + const ::basegfx::B2DVector aNormalBottom( -aDirectionVertical ); + + // now extend parallelogram, such that the bound rect + // point are included + moveLineOutsideRect( io_rLeftTop, io_rRightTop, aNormalTop, rFitTarget ); + moveLineOutsideRect( io_rLeftBottom, io_rRightBottom, aNormalBottom, rFitTarget ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basegfx/source/tools/unopolypolygon.cxx b/basegfx/source/tools/unopolypolygon.cxx new file mode 100644 index 000000000000..e4105925445a --- /dev/null +++ b/basegfx/source/tools/unopolypolygon.cxx @@ -0,0 +1,489 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basegfx.hxx" + +#include <com/sun/star/geometry/AffineMatrix2D.hpp> +#include <com/sun/star/rendering/RenderState.hpp> +#include <com/sun/star/rendering/ViewState.hpp> +#include <com/sun/star/rendering/XCanvas.hpp> +#include <com/sun/star/rendering/CompositeOperation.hpp> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/tools/unopolypolygon.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + + +using namespace ::com::sun::star; + +namespace basegfx +{ +namespace unotools +{ + UnoPolyPolygon::UnoPolyPolygon( const B2DPolyPolygon& rPolyPoly ) : + UnoPolyPolygonBase( m_aMutex ), + maPolyPoly( rPolyPoly ), + meFillRule( rendering::FillRule_EVEN_ODD ) + { + // or else races will haunt us. + maPolyPoly.makeUnique(); + } + + void SAL_CALL UnoPolyPolygon::addPolyPolygon( + const geometry::RealPoint2D& position, + const uno::Reference< rendering::XPolyPolygon2D >& polyPolygon ) throw (lang::IllegalArgumentException,uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + modifying(); + + // TODO(F1): Correctly fulfill the UNO API + // specification. This will probably result in a vector of + // poly-polygons to be stored in this object. + + const sal_Int32 nPolys( polyPolygon->getNumberOfPolygons() ); + + if( !polyPolygon.is() || !nPolys ) + { + // invalid or empty polygon - nothing to do. + return; + } + + B2DPolyPolygon aSrcPoly; + const UnoPolyPolygon* pSrc( dynamic_cast< UnoPolyPolygon* >(polyPolygon.get()) ); + + // try to extract polygon data from interface. First, + // check whether it's the same implementation object, + // which we can tunnel then. + if( pSrc ) + { + aSrcPoly = pSrc->getPolyPolygon(); + } + else + { + // not a known implementation object - try data source + // interfaces + uno::Reference< rendering::XBezierPolyPolygon2D > xBezierPoly( + polyPolygon, + uno::UNO_QUERY ); + + if( xBezierPoly.is() ) + { + aSrcPoly = unotools::polyPolygonFromBezier2DSequenceSequence( + xBezierPoly->getBezierSegments( 0, + nPolys, + 0, + -1 ) ); + } + else + { + uno::Reference< rendering::XLinePolyPolygon2D > xLinePoly( + polyPolygon, + uno::UNO_QUERY ); + + // no implementation class and no data provider + // found - contract violation. + if( !xLinePoly.is() ) + throw lang::IllegalArgumentException( + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "UnoPolyPolygon::addPolyPolygon(): Invalid input " + "poly-polygon, cannot retrieve vertex data")), + static_cast<cppu::OWeakObject*>(this), 1); + + aSrcPoly = unotools::polyPolygonFromPoint2DSequenceSequence( + xLinePoly->getPoints( 0, + nPolys, + 0, + -1 ) ); + } + } + + const B2DRange aBounds( tools::getRange( aSrcPoly ) ); + const B2DVector aOffset( unotools::b2DPointFromRealPoint2D( position ) - + aBounds.getMinimum() ); + + if( !aOffset.equalZero() ) + { + const B2DHomMatrix aTranslate(tools::createTranslateB2DHomMatrix(aOffset)); + aSrcPoly.transform( aTranslate ); + } + + maPolyPoly.append( aSrcPoly ); + } + + sal_Int32 SAL_CALL UnoPolyPolygon::getNumberOfPolygons() throw (uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + return maPolyPoly.count(); + } + + sal_Int32 SAL_CALL UnoPolyPolygon::getNumberOfPolygonPoints( + sal_Int32 polygon ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + checkIndex( polygon ); + + return maPolyPoly.getB2DPolygon(polygon).count(); + } + + rendering::FillRule SAL_CALL UnoPolyPolygon::getFillRule() throw (uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + return meFillRule; + } + + void SAL_CALL UnoPolyPolygon::setFillRule( + rendering::FillRule fillRule ) throw (uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + modifying(); + + meFillRule = fillRule; + } + + sal_Bool SAL_CALL UnoPolyPolygon::isClosed( + sal_Int32 index ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + checkIndex( index ); + + return maPolyPoly.getB2DPolygon(index).isClosed(); + } + + void SAL_CALL UnoPolyPolygon::setClosed( + sal_Int32 index, + sal_Bool closedState ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + modifying(); + + if( index == -1L ) + { + // set all + maPolyPoly.setClosed( closedState ); + } + else + { + checkIndex( index ); + + // fetch referenced polygon, change state + B2DPolygon aTmp( maPolyPoly.getB2DPolygon(index) ); + aTmp.setClosed( closedState ); + + // set back to container + maPolyPoly.setB2DPolygon( index, aTmp ); + } + } + + uno::Sequence< uno::Sequence< geometry::RealPoint2D > > SAL_CALL UnoPolyPolygon::getPoints( + sal_Int32 nPolygonIndex, + sal_Int32 nNumberOfPolygons, + sal_Int32 nPointIndex, + sal_Int32 nNumberOfPoints ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + + return unotools::pointSequenceSequenceFromB2DPolyPolygon( + getSubsetPolyPolygon( nPolygonIndex, + nNumberOfPolygons, + nPointIndex, + nNumberOfPoints ) ); + } + + void SAL_CALL UnoPolyPolygon::setPoints( + const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points, + sal_Int32 nPolygonIndex ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + modifying(); + + const B2DPolyPolygon& rNewPolyPoly( + unotools::polyPolygonFromPoint2DSequenceSequence( points ) ); + + if( nPolygonIndex == -1 ) + { + maPolyPoly = rNewPolyPoly; + } + else + { + checkIndex( nPolygonIndex ); + + maPolyPoly.insert( nPolygonIndex, rNewPolyPoly ); + } + } + + geometry::RealPoint2D SAL_CALL UnoPolyPolygon::getPoint( + sal_Int32 nPolygonIndex, + sal_Int32 nPointIndex ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + checkIndex( nPolygonIndex ); + + const B2DPolygon& rPoly( maPolyPoly.getB2DPolygon( nPolygonIndex ) ); + + if( nPointIndex < 0 || nPointIndex >= static_cast<sal_Int32>(rPoly.count()) ) + throw lang::IndexOutOfBoundsException(); + + return unotools::point2DFromB2DPoint( rPoly.getB2DPoint( nPointIndex ) ); + } + + void SAL_CALL UnoPolyPolygon::setPoint( + const geometry::RealPoint2D& point, + sal_Int32 nPolygonIndex, + sal_Int32 nPointIndex ) throw (lang::IndexOutOfBoundsException,uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + checkIndex( nPolygonIndex ); + modifying(); + + B2DPolygon aPoly( maPolyPoly.getB2DPolygon( nPolygonIndex ) ); + + if( nPointIndex < 0 || nPointIndex >= static_cast<sal_Int32>(aPoly.count()) ) + throw lang::IndexOutOfBoundsException(); + + aPoly.setB2DPoint( nPointIndex, + unotools::b2DPointFromRealPoint2D( point ) ); + maPolyPoly.setB2DPolygon( nPolygonIndex, aPoly ); + } + + uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > > SAL_CALL UnoPolyPolygon::getBezierSegments( + sal_Int32 nPolygonIndex, + sal_Int32 nNumberOfPolygons, + sal_Int32 nPointIndex, + sal_Int32 nNumberOfPoints ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + return unotools::bezierSequenceSequenceFromB2DPolyPolygon( + getSubsetPolyPolygon( nPolygonIndex, + nNumberOfPolygons, + nPointIndex, + nNumberOfPoints ) ); + } + + void SAL_CALL UnoPolyPolygon::setBezierSegments( + const uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > >& points, + sal_Int32 nPolygonIndex ) throw (lang::IndexOutOfBoundsException, + uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + modifying(); + const B2DPolyPolygon& rNewPolyPoly( + unotools::polyPolygonFromBezier2DSequenceSequence( points ) ); + + if( nPolygonIndex == -1 ) + { + maPolyPoly = rNewPolyPoly; + } + else + { + checkIndex( nPolygonIndex ); + + maPolyPoly.insert( nPolygonIndex, rNewPolyPoly ); + } + } + + geometry::RealBezierSegment2D SAL_CALL UnoPolyPolygon::getBezierSegment( sal_Int32 nPolygonIndex, + sal_Int32 nPointIndex ) throw (lang::IndexOutOfBoundsException, + uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + checkIndex( nPolygonIndex ); + + const B2DPolygon& rPoly( maPolyPoly.getB2DPolygon( nPolygonIndex ) ); + const sal_uInt32 nPointCount(rPoly.count()); + + if( nPointIndex < 0 || nPointIndex >= static_cast<sal_Int32>(nPointCount) ) + throw lang::IndexOutOfBoundsException(); + + const B2DPoint& rPt( rPoly.getB2DPoint( nPointIndex ) ); + const B2DPoint& rCtrl0( rPoly.getNextControlPoint(nPointIndex) ); + const B2DPoint& rCtrl1( rPoly.getPrevControlPoint((nPointIndex + 1) % nPointCount) ); + + return geometry::RealBezierSegment2D( rPt.getX(), + rPt.getY(), + rCtrl0.getX(), + rCtrl0.getY(), + rCtrl1.getX(), + rCtrl1.getY() ); + } + + void SAL_CALL UnoPolyPolygon::setBezierSegment( const geometry::RealBezierSegment2D& segment, + sal_Int32 nPolygonIndex, + sal_Int32 nPointIndex ) throw (lang::IndexOutOfBoundsException, + uno::RuntimeException) + { + osl::MutexGuard const guard( m_aMutex ); + checkIndex( nPolygonIndex ); + modifying(); + + B2DPolygon aPoly( maPolyPoly.getB2DPolygon( nPolygonIndex ) ); + const sal_uInt32 nPointCount(aPoly.count()); + + if( nPointIndex < 0 || nPointIndex >= static_cast<sal_Int32>(nPointCount) ) + throw lang::IndexOutOfBoundsException(); + + aPoly.setB2DPoint( nPointIndex, + B2DPoint( segment.Px, + segment.Py ) ); + aPoly.setNextControlPoint(nPointIndex, + B2DPoint(segment.C1x, segment.C1y)); + aPoly.setPrevControlPoint((nPointIndex + 1) % nPointCount, + B2DPoint(segment.C2x, segment.C2y)); + + maPolyPoly.setB2DPolygon( nPolygonIndex, aPoly ); + } + + B2DPolyPolygon UnoPolyPolygon::getSubsetPolyPolygon( + sal_Int32 nPolygonIndex, + sal_Int32 nNumberOfPolygons, + sal_Int32 nPointIndex, + sal_Int32 nNumberOfPoints ) const + { + osl::MutexGuard const guard( m_aMutex ); + checkIndex( nPolygonIndex ); + + const sal_Int32 nPolyCount( maPolyPoly.count() ); + + // check for "full polygon" case + if( !nPolygonIndex && + !nPointIndex && + nNumberOfPolygons == nPolyCount && + nNumberOfPoints == -1 ) + { + return maPolyPoly; + } + + B2DPolyPolygon aSubsetPoly; + + // create temporary polygon (as an extract from maPoly, + // which contains the requested subset) + for( sal_Int32 i=nPolygonIndex; i<nNumberOfPolygons; ++i ) + { + checkIndex(i); + + const B2DPolygon& rCurrPoly( maPolyPoly.getB2DPolygon(i) ); + + sal_Int32 nFirstPoint(0); + sal_Int32 nLastPoint(nPolyCount-1); + + if( nPointIndex && i==nPolygonIndex ) + { + // very first polygon - respect nPointIndex, if + // not zero + + // empty polygon - impossible to specify _any_ + // legal value except 0 here! + if( !nPolyCount && nPointIndex ) + throw lang::IndexOutOfBoundsException(); + + nFirstPoint = nPointIndex; + } + + if( i==nNumberOfPolygons-1 && nNumberOfPoints != -1 ) + { + // very last polygon - respect nNumberOfPoints + + // empty polygon - impossible to specify _any_ + // legal value except -1 here! + if( !nPolyCount ) + throw lang::IndexOutOfBoundsException(); + + nLastPoint = nFirstPoint+nNumberOfPoints; + } + + if( !nPolyCount ) + { + // empty polygon - index checks already performed + // above, now simply append empty polygon + aSubsetPoly.append( rCurrPoly ); + } + else + { + if( nFirstPoint < 0 || nFirstPoint >= nPolyCount ) + throw lang::IndexOutOfBoundsException(); + + if( nLastPoint < 0 || nLastPoint >= nPolyCount ) + throw lang::IndexOutOfBoundsException(); + + B2DPolygon aTmp; + for( sal_Int32 j=nFirstPoint; j<nLastPoint; ++j ) + aTmp.append( rCurrPoly.getB2DPoint(j) ); + + aSubsetPoly.append( aTmp ); + } + } + + return aSubsetPoly; + } + + B2DPolyPolygon UnoPolyPolygon::getPolyPolygonUnsafe() const + { + return maPolyPoly; + } + +#define IMPLEMENTATION_NAME "gfx::internal::UnoPolyPolygon" +#define SERVICE_NAME "com.sun.star.rendering.PolyPolygon2D" + ::rtl::OUString SAL_CALL UnoPolyPolygon::getImplementationName() throw( uno::RuntimeException ) + { + return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) ); + } + + sal_Bool SAL_CALL UnoPolyPolygon::supportsService( const ::rtl::OUString& ServiceName ) throw( uno::RuntimeException ) + { + return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( SERVICE_NAME ) ); + } + + uno::Sequence< ::rtl::OUString > SAL_CALL UnoPolyPolygon::getSupportedServiceNames() throw( uno::RuntimeException ) + { + uno::Sequence< ::rtl::OUString > aRet(1); + aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) ); + + return aRet; + } + + B2DPolyPolygon UnoPolyPolygon::getPolyPolygon() const + { + osl::MutexGuard const guard( m_aMutex ); + + // detach result from us + B2DPolyPolygon aRet( maPolyPoly ); + aRet.makeUnique(); + return aRet; + } + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |