summaryrefslogtreecommitdiff
path: root/basegfx
diff options
context:
space:
mode:
Diffstat (limited to 'basegfx')
-rw-r--r--basegfx/inc/basegfx/color/bcolor.hxx241
-rw-r--r--basegfx/inc/basegfx/color/bcolormodifier.hxx147
-rw-r--r--basegfx/inc/basegfx/color/bcolortools.hxx76
-rw-r--r--basegfx/inc/basegfx/curve/b2dbeziertools.hxx66
-rw-r--r--basegfx/inc/basegfx/curve/b2dcubicbezier.hxx225
-rw-r--r--basegfx/inc/basegfx/curve/b2dquadraticbezier.hxx76
-rw-r--r--basegfx/inc/basegfx/matrix/b2dhommatrix.hxx161
-rw-r--r--basegfx/inc/basegfx/matrix/b2dhommatrixtools.hxx86
-rw-r--r--basegfx/inc/basegfx/matrix/b3dhommatrix.hxx178
-rw-r--r--basegfx/inc/basegfx/numeric/ftools.hxx206
-rw-r--r--basegfx/inc/basegfx/pixel/bpixel.hxx226
-rw-r--r--basegfx/inc/basegfx/point/b2dhompoint.hxx238
-rw-r--r--basegfx/inc/basegfx/point/b2dpoint.hxx154
-rw-r--r--basegfx/inc/basegfx/point/b2ipoint.hxx130
-rw-r--r--basegfx/inc/basegfx/point/b3dhompoint.hxx408
-rw-r--r--basegfx/inc/basegfx/point/b3dpoint.hxx153
-rw-r--r--basegfx/inc/basegfx/point/b3ipoint.hxx142
-rw-r--r--basegfx/inc/basegfx/polygon/b2dlinegeometry.hxx147
-rw-r--r--basegfx/inc/basegfx/polygon/b2dpolygon.hxx270
-rw-r--r--basegfx/inc/basegfx/polygon/b2dpolygonclipper.hxx85
-rw-r--r--basegfx/inc/basegfx/polygon/b2dpolygoncutandtouch.hxx76
-rw-r--r--basegfx/inc/basegfx/polygon/b2dpolygontools.hxx594
-rw-r--r--basegfx/inc/basegfx/polygon/b2dpolygontriangulator.hxx52
-rw-r--r--basegfx/inc/basegfx/polygon/b2dpolypolygon.hxx134
-rw-r--r--basegfx/inc/basegfx/polygon/b2dpolypolygoncutter.hxx122
-rw-r--r--basegfx/inc/basegfx/polygon/b2dpolypolygonfillrule.hxx63
-rw-r--r--basegfx/inc/basegfx/polygon/b2dpolypolygonrasterconverter.hxx144
-rw-r--r--basegfx/inc/basegfx/polygon/b2dpolypolygontools.hxx282
-rw-r--r--basegfx/inc/basegfx/polygon/b3dgeometry.hxx74
-rw-r--r--basegfx/inc/basegfx/polygon/b3dpolygon.hxx144
-rw-r--r--basegfx/inc/basegfx/polygon/b3dpolygonclipper.hxx90
-rw-r--r--basegfx/inc/basegfx/polygon/b3dpolygontools.hxx194
-rw-r--r--basegfx/inc/basegfx/polygon/b3dpolypolygon.hxx128
-rw-r--r--basegfx/inc/basegfx/polygon/b3dpolypolygontools.hxx157
-rw-r--r--basegfx/inc/basegfx/range/b1drange.hxx167
-rw-r--r--basegfx/inc/basegfx/range/b1ibox.hxx146
-rw-r--r--basegfx/inc/basegfx/range/b1irange.hxx147
-rw-r--r--basegfx/inc/basegfx/range/b2dconnectedranges.hxx266
-rw-r--r--basegfx/inc/basegfx/range/b2dmultirange.hxx117
-rw-r--r--basegfx/inc/basegfx/range/b2drange.hxx297
-rw-r--r--basegfx/inc/basegfx/range/b2drectangle.hxx45
-rw-r--r--basegfx/inc/basegfx/range/b2ibox.hxx254
-rw-r--r--basegfx/inc/basegfx/range/b2irange.hxx257
-rw-r--r--basegfx/inc/basegfx/range/b2irectangle.hxx45
-rw-r--r--basegfx/inc/basegfx/range/b3drange.hxx305
-rw-r--r--basegfx/inc/basegfx/range/b3dvolume.hxx45
-rw-r--r--basegfx/inc/basegfx/range/b3ibox.hxx262
-rw-r--r--basegfx/inc/basegfx/range/b3irange.hxx265
-rw-r--r--basegfx/inc/basegfx/range/b3ivolume.hxx45
-rw-r--r--basegfx/inc/basegfx/range/basicbox.hxx139
-rw-r--r--basegfx/inc/basegfx/range/basicrange.hxx299
-rw-r--r--basegfx/inc/basegfx/range/rangeexpander.hxx86
-rw-r--r--basegfx/inc/basegfx/raster/bpixelraster.hxx120
-rw-r--r--basegfx/inc/basegfx/raster/bzpixelraster.hxx100
-rw-r--r--basegfx/inc/basegfx/raster/rasterconvert3d.hxx349
-rwxr-xr-xbasegfx/inc/basegfx/tools/canvastools.hxx224
-rw-r--r--basegfx/inc/basegfx/tools/debugplotter.hxx110
-rw-r--r--basegfx/inc/basegfx/tools/gradienttools.hxx401
-rw-r--r--basegfx/inc/basegfx/tools/rectcliptools.hxx91
-rw-r--r--basegfx/inc/basegfx/tools/tools.hxx134
-rwxr-xr-xbasegfx/inc/basegfx/tools/unopolypolygon.hxx116
-rw-r--r--basegfx/inc/basegfx/tuple/b2dtuple.hxx360
-rw-r--r--basegfx/inc/basegfx/tuple/b2i64tuple.hxx315
-rw-r--r--basegfx/inc/basegfx/tuple/b2ituple.hxx240
-rw-r--r--basegfx/inc/basegfx/tuple/b3dtuple.hxx434
-rw-r--r--basegfx/inc/basegfx/tuple/b3i64tuple.hxx352
-rw-r--r--basegfx/inc/basegfx/tuple/b3ituple.hxx352
-rw-r--r--basegfx/inc/basegfx/vector/b2dsize.hxx45
-rw-r--r--basegfx/inc/basegfx/vector/b2dvector.hxx270
-rw-r--r--basegfx/inc/basegfx/vector/b2enums.hxx79
-rw-r--r--basegfx/inc/basegfx/vector/b2isize.hxx45
-rw-r--r--basegfx/inc/basegfx/vector/b2ivector.hxx233
-rw-r--r--basegfx/inc/basegfx/vector/b3dsize.hxx45
-rw-r--r--basegfx/inc/basegfx/vector/b3dvector.hxx343
-rw-r--r--basegfx/inc/basegfx/vector/b3isize.hxx45
-rw-r--r--basegfx/inc/basegfx/vector/b3ivector.hxx262
-rw-r--r--basegfx/inc/makefile.mk51
-rw-r--r--basegfx/inc/pch/precompiled_basegfx.cxx32
-rw-r--r--basegfx/inc/pch/precompiled_basegfx.hxx35
-rw-r--r--basegfx/prj/build.lst18
-rw-r--r--basegfx/prj/d.lst109
-rw-r--r--basegfx/source/color/bcolor.cxx44
-rw-r--r--basegfx/source/color/bcolormodifier.cxx76
-rw-r--r--basegfx/source/color/bcolortools.cxx272
-rw-r--r--basegfx/source/color/makefile.mk53
-rw-r--r--basegfx/source/curve/b2dbeziertools.cxx166
-rw-r--r--basegfx/source/curve/b2dcubicbezier.cxx1108
-rw-r--r--basegfx/source/curve/b2dquadraticbezier.cxx108
-rw-r--r--basegfx/source/curve/makefile.mk53
-rw-r--r--basegfx/source/inc/PolygonPoint.hxx541
-rw-r--r--basegfx/source/inc/hommatrixtemplate.hxx616
-rw-r--r--basegfx/source/inc/polygontemplate.hxx541
-rw-r--r--basegfx/source/matrix/b2dhommatrix.cxx577
-rw-r--r--basegfx/source/matrix/b2dhommatrixtools.cxx44
-rw-r--r--basegfx/source/matrix/b3dhommatrix.cxx599
-rw-r--r--basegfx/source/matrix/makefile.mk53
-rw-r--r--basegfx/source/numeric/ftools.cxx41
-rw-r--r--basegfx/source/numeric/makefile.mk51
-rw-r--r--basegfx/source/pixel/bpixel.cxx55
-rw-r--r--basegfx/source/pixel/makefile.mk51
-rw-r--r--basegfx/source/point/b2dhompoint.cxx262
-rw-r--r--basegfx/source/point/b2dpoint.cxx88
-rw-r--r--basegfx/source/point/b2ipoint.cxx79
-rw-r--r--basegfx/source/point/b3dhompoint.cxx47
-rw-r--r--basegfx/source/point/b3dpoint.cxx88
-rw-r--r--basegfx/source/point/b3ipoint.cxx82
-rw-r--r--basegfx/source/point/makefile.mk56
-rw-r--r--basegfx/source/polygon/b2dlinegeometry.cxx729
-rw-r--r--basegfx/source/polygon/b2dpolygon.cxx1594
-rw-r--r--basegfx/source/polygon/b2dpolygonclipper.cxx877
-rw-r--r--basegfx/source/polygon/b2dpolygoncutandtouch.cxx1211
-rw-r--r--basegfx/source/polygon/b2dpolygontools.cxx3631
-rw-r--r--basegfx/source/polygon/b2dpolygontriangulator.cxx469
-rw-r--r--basegfx/source/polygon/b2dpolypolygon.cxx383
-rw-r--r--basegfx/source/polygon/b2dpolypolygoncutter.cxx1017
-rw-r--r--basegfx/source/polygon/b2dpolypolygonrasterconverter.cxx705
-rw-r--r--basegfx/source/polygon/b2dpolypolygontools.cxx588
-rw-r--r--basegfx/source/polygon/b2dsvgpolypolygon.cxx1108
-rw-r--r--basegfx/source/polygon/b3dgeometry.cxx59
-rw-r--r--basegfx/source/polygon/b3dpolygon.cxx1819
-rw-r--r--basegfx/source/polygon/b3dpolygonclipper.cxx578
-rw-r--r--basegfx/source/polygon/b3dpolygontools.cxx1266
-rw-r--r--basegfx/source/polygon/b3dpolypolygon.cxx449
-rw-r--r--basegfx/source/polygon/b3dpolypolygontools.cxx559
-rw-r--r--basegfx/source/polygon/makefile.mk66
-rw-r--r--basegfx/source/range/b1drange.cxx59
-rw-r--r--basegfx/source/range/b2dmultirange.cxx282
-rw-r--r--basegfx/source/range/b2drange.cxx77
-rw-r--r--basegfx/source/range/b2xrange.cxx145
-rw-r--r--basegfx/source/range/b3drange.cxx88
-rw-r--r--basegfx/source/range/makefile.mk55
-rw-r--r--basegfx/source/raster/bpixelraster.cxx44
-rw-r--r--basegfx/source/raster/bzpixelraster.cxx44
-rw-r--r--basegfx/source/raster/makefile.mk53
-rw-r--r--basegfx/source/raster/rasterconvert3d.cxx357
-rwxr-xr-xbasegfx/source/tools/canvastools.cxx677
-rwxr-xr-xbasegfx/source/tools/debugplotter.cxx416
-rw-r--r--basegfx/source/tools/gradienttools.cxx366
-rw-r--r--basegfx/source/tools/liangbarsky.cxx135
-rwxr-xr-xbasegfx/source/tools/makefile.mk53
-rw-r--r--basegfx/source/tools/tools.cxx127
-rwxr-xr-xbasegfx/source/tools/unopolypolygon.cxx491
-rw-r--r--basegfx/source/tuple/b2dtuple.cxx87
-rw-r--r--basegfx/source/tuple/b2i64tuple.cxx47
-rw-r--r--basegfx/source/tuple/b2ituple.cxx156
-rw-r--r--basegfx/source/tuple/b3dtuple.cxx58
-rw-r--r--basegfx/source/tuple/b3i64tuple.cxx46
-rw-r--r--basegfx/source/tuple/b3ituple.cxx46
-rw-r--r--basegfx/source/tuple/makefile.mk56
-rw-r--r--basegfx/source/vector/b2dvector.cxx222
-rw-r--r--basegfx/source/vector/b2ivector.cxx162
-rw-r--r--basegfx/source/vector/b3dvector.cxx118
-rw-r--r--basegfx/source/vector/b3ivector.cxx54
-rw-r--r--basegfx/source/vector/makefile.mk54
-rw-r--r--basegfx/source/workbench/Makefile16
-rw-r--r--basegfx/source/workbench/bezierclip.cxx2060
-rw-r--r--basegfx/source/workbench/bezierclip.hxx96
-rw-r--r--basegfx/source/workbench/convexhull.cxx216
-rw-r--r--basegfx/source/workbench/gauss.hxx175
-rw-r--r--basegfx/test/basegfx1d.cxx80
-rw-r--r--basegfx/test/basegfx2d.cxx1644
-rw-r--r--basegfx/test/basegfx3d.cxx229
-rw-r--r--basegfx/test/export.map38
-rw-r--r--basegfx/test/makefile.mk96
-rw-r--r--basegfx/test/tests.txt20
-rw-r--r--basegfx/test/testtools.cxx238
-rw-r--r--basegfx/test/testtools.hxx101
-rw-r--r--basegfx/util/basegfx.flt6
-rw-r--r--basegfx/util/makefile.mk89
169 files changed, 46704 insertions, 0 deletions
diff --git a/basegfx/inc/basegfx/color/bcolor.hxx b/basegfx/inc/basegfx/color/bcolor.hxx
new file mode 100644
index 000000000000..f19f7e74080f
--- /dev/null
+++ b/basegfx/inc/basegfx/color/bcolor.hxx
@@ -0,0 +1,241 @@
+/*************************************************************************
+ *
+ * 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: bcolor.hxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_COLOR_BCOLOR_HXX
+#define _BGFX_COLOR_BCOLOR_HXX
+
+#include <basegfx/tuple/b3dtuple.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <vector>
+
+//////////////////////////////////////////////////////////////////////////////
+// predeclarations
+
+namespace com { namespace sun { namespace star { namespace rendering {
+ class XGraphicDevice;
+}}}}
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ /** Base Color class with three double values
+
+ This class derives all operators and common handling for
+ a 3D data class from B3DTuple. All necessary extensions
+ which are special for colors will be added here.
+
+ @see B3DTuple
+ */
+ class BColor : public B3DTuple
+ {
+ public:
+ /** Create a Color with red, green and blue components from [0.0 to 1.0]
+
+ The color is initialized to (0.0, 0.0, 0.0)
+ */
+ BColor()
+ : B3DTuple()
+ {}
+
+ /** Create a 3D Color
+
+ @param fRed
+ @param fGreen
+ @param fBlue
+ These parameters are used to initialize the red, green and blue intensities of the color
+ */
+ BColor(double fRed, double fGreen, double fBlue)
+ : B3DTuple(fRed, fGreen, fBlue)
+ {}
+
+ /** Create a 3D Color
+
+ @param fLuminosity
+ The parameter is used to initialize the red, green and blue intensities of the color
+ */
+ BColor(double fLuminosity)
+ : B3DTuple(fLuminosity, fLuminosity, fLuminosity)
+ {}
+
+ /** Create a copy of a Color
+
+ @param rVec
+ The Color which will be copied.
+ */
+ BColor(const BColor& rVec)
+ : B3DTuple(rVec)
+ {}
+
+ /** constructor with tuple to allow copy-constructing
+ from B3DTuple-based classes
+ */
+ BColor(const ::basegfx::B3DTuple& rTuple)
+ : B3DTuple(rTuple)
+ {}
+
+ ~BColor()
+ {}
+
+ // data access read
+ double getRed() const { return mfX; }
+ double getGreen() const { return mfY; }
+ double getBlue() const { return mfZ; }
+
+ // data access write
+ void setRed(double fNew) { mfX = fNew; }
+ void setGreen(double fNew) { mfY = fNew; }
+ void setBlue(double fNew) { mfZ = fNew; }
+
+ /** *=operator to allow usage from BColor, too
+ */
+ BColor& operator*=( const BColor& rPnt )
+ {
+ mfX *= rPnt.mfX;
+ mfY *= rPnt.mfY;
+ mfZ *= rPnt.mfZ;
+ return *this;
+ }
+
+ /** *=operator to allow usage from BColor, too
+ */
+ BColor& operator*=(double t)
+ {
+ mfX *= t;
+ mfY *= t;
+ mfZ *= t;
+ return *this;
+ }
+
+ /** assignment operator to allow assigning the results
+ of B3DTuple calculations
+ */
+ BColor& operator=( const ::basegfx::B3DTuple& rVec )
+ {
+ mfX = rVec.getX();
+ mfY = rVec.getY();
+ mfZ = rVec.getZ();
+ return *this;
+ }
+
+ // blend to another color using luminance
+ void blend(const BColor& rColor)
+ {
+ const double fLuminance(luminance());
+ mfX = rColor.getRed() * fLuminance;
+ mfY = rColor.getGreen() * fLuminance;
+ mfZ = rColor.getBlue() * fLuminance;
+ }
+
+ // luminance
+ double luminance() const
+ {
+ const double fRedWeight(77.0 / 256.0);
+ const double fGreenWeight(151.0 / 256.0);
+ const double fBlueWeight(28.0 / 256.0);
+
+ return (mfX * fRedWeight + mfY * fGreenWeight + mfZ * fBlueWeight);
+ }
+
+ // distances in color space
+ double getDistanceRed(const BColor& rColor) const { return (getRed() > rColor.getRed() ? getRed() - rColor.getRed() : rColor.getRed() - getRed()); }
+ double getDistanceGreen(const BColor& rColor) const { return (getGreen() > rColor.getGreen() ? getGreen() - rColor.getGreen() : rColor.getGreen() - getGreen()); }
+ double getDistanceBlue(const BColor& rColor) const { return (getBlue() > rColor.getBlue() ? getBlue() - rColor.getBlue() : rColor.getBlue() - getBlue()); }
+
+ double getDistance(const BColor& rColor) const
+ {
+ const double fDistR(getDistanceRed(rColor));
+ const double fDistG(getDistanceGreen(rColor));
+ const double fDistB(getDistanceBlue(rColor));
+
+ return sqrt(fDistR * fDistR + fDistG * fDistG + fDistB * fDistB);
+ }
+
+ double getMinimumDistance(const BColor& rColor) const
+ {
+ const double fDistR(getDistanceRed(rColor));
+ const double fDistG(getDistanceGreen(rColor));
+ const double fDistB(getDistanceBlue(rColor));
+
+ double fRetval(fDistR < fDistG ? fDistR : fDistG);
+ return (fRetval < fDistB ? fRetval : fDistB);
+ }
+
+ double getMaximumDistance(const BColor& rColor) const
+ {
+ const double fDistR(getDistanceRed(rColor));
+ const double fDistG(getDistanceGreen(rColor));
+ const double fDistB(getDistanceBlue(rColor));
+
+ double fRetval(fDistR > fDistG ? fDistR : fDistG);
+ return (fRetval > fDistB ? fRetval : fDistB);
+ }
+
+ // clamp color to [0.0..1.0] values in all three intensity components
+ void clamp()
+ {
+ mfX = basegfx::clamp(mfX, 0.0, 1.0);
+ mfY = basegfx::clamp(mfY, 0.0, 1.0);
+ mfZ = basegfx::clamp(mfZ, 0.0, 1.0);
+ }
+
+ void invert()
+ {
+ mfX = 1.0 - mfX;
+ mfY = 1.0 - mfY;
+ mfZ = 1.0 - mfZ;
+ }
+
+ static const BColor& getEmptyBColor()
+ {
+ return (const BColor&) ::basegfx::B3DTuple::getEmptyTuple();
+ }
+
+ com::sun::star::uno::Sequence< double > colorToDoubleSequence(const com::sun::star::uno::Reference< com::sun::star::rendering::XGraphicDevice >& /*xGraphicDevice*/) const
+ {
+ com::sun::star::uno::Sequence< double > aRet(4);
+ double* pRet = aRet.getArray();
+
+ pRet[0] = mfX;
+ pRet[1] = mfY;
+ pRet[2] = mfZ;
+ pRet[3] = 1.0;
+
+ return aRet;
+ }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_COLOR_BCOLOR_HXX */
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/inc/basegfx/color/bcolormodifier.hxx b/basegfx/inc/basegfx/color/bcolormodifier.hxx
new file mode 100644
index 000000000000..5116cdbf446e
--- /dev/null
+++ b/basegfx/inc/basegfx/color/bcolormodifier.hxx
@@ -0,0 +1,147 @@
+/*************************************************************************
+ *
+ * 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: bcolormodifier.hxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_COLOR_BCOLORMODIFIER_HXX
+#define _BGFX_COLOR_BCOLORMODIFIER_HXX
+
+#include <basegfx/color/bcolor.hxx>
+#include <vector>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ /** Descriptor for type of color modification
+ */
+ enum BColorModifyMode
+ {
+ BCOLORMODIFYMODE_REPLACE, // replace all color with local color
+ BCOLORMODIFYMODE_INTERPOLATE, // interpolate color between given and local with local value
+ BCOLORMODIFYMODE_GRAY, // convert color to gray
+ BCOLORMODIFYMODE_BLACKANDWHITE // convert color to B&W, local value is treshhold
+ };
+
+ /** Class to hold a color, value and mode for a color modification. Color modification is
+ done calling the getModifiedColor() method
+ */
+ class BColorModifier
+ {
+ protected:
+ ::basegfx::BColor maBColor;
+ double mfValue;
+ BColorModifyMode meMode;
+
+ public:
+ BColorModifier(
+ const ::basegfx::BColor& rBColor,
+ double fValue = 0.5,
+ BColorModifyMode eMode = BCOLORMODIFYMODE_REPLACE)
+ : maBColor(rBColor),
+ mfValue(fValue),
+ meMode(eMode)
+ {}
+
+ // compare operator(s)
+ bool operator==(const BColorModifier& rCompare) const
+ {
+ return (maBColor == rCompare.maBColor && mfValue == rCompare.mfValue && meMode == rCompare.meMode);
+ }
+
+ bool operator!=(const BColorModifier& rCompare) const
+ {
+ return !(operator==(rCompare));
+ }
+
+ // data access
+ const ::basegfx::BColor& getBColor() const { return maBColor; }
+ double getValue() const { return mfValue; }
+ BColorModifyMode getMode() const { return meMode; }
+
+ // compute modified color
+ ::basegfx::BColor getModifiedColor(const ::basegfx::BColor& aSourceColor) const;
+ };
+
+ /** Class to hold a stack of BColorModifiers and to get the modified color with
+ applying all existing entry changes
+ */
+ class BColorModifierStack
+ {
+ protected:
+ ::std::vector< BColorModifier > maBColorModifiers;
+
+ public:
+ sal_uInt32 count() const
+ {
+ return maBColorModifiers.size();
+ }
+
+ const BColorModifier& getBColorModifier(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < count(), "BColorModifierStack: Access out of range (!)");
+ return maBColorModifiers[nIndex];
+ }
+
+ ::basegfx::BColor getModifiedColor(const ::basegfx::BColor& rSource) const
+ {
+ if(count())
+ {
+ ::basegfx::BColor aRetval(rSource);
+ ::std::vector< BColorModifier >::const_iterator aEnd(maBColorModifiers.end());
+
+ while(aEnd != maBColorModifiers.begin())
+ {
+ aRetval = (--aEnd)->getModifiedColor(aRetval);
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rSource;
+ }
+ }
+
+ void push(const BColorModifier& rNew)
+ {
+ maBColorModifiers.push_back(rNew);
+ }
+
+ void pop()
+ {
+ maBColorModifiers.pop_back();
+ }
+ };
+} // end of namespace basegfx
+
+#endif // _BGFX_COLOR_BCOLORMODIFIER_HXX
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/inc/basegfx/color/bcolortools.hxx b/basegfx/inc/basegfx/color/bcolortools.hxx
new file mode 100644
index 000000000000..b4906727d08b
--- /dev/null
+++ b/basegfx/inc/basegfx/color/bcolortools.hxx
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *
+ * 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: bcolor.hxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_COLOR_BCOLORTOOLS_HXX
+#define _BGFX_COLOR_BCOLORTOOLS_HXX
+
+#include <sal/types.h>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class BColor;
+
+ namespace tools
+ {
+ /// Transform from RGB to HSL
+ BColor rgb2hsl(const BColor& rRGBColor);
+ /// Transform from HSL to RGB
+ BColor hsl2rgb(const BColor& rHSLColor);
+
+ /// Transform from RGB to HSV
+ BColor rgb2hsv(const BColor& rRGBColor);
+ /// Transform from HSV to RGB
+ BColor hsv2rgb(const BColor& rHSVColor);
+
+ /// Transform from R'G'B' to YIQ (NTSC color model - Y is used in monochrome mode)
+ BColor rgb2yiq(const BColor& rRGBColor);
+ /// Transform from YIQ to R'G'B' (NTSC color model - Y is used in monochrome mode)
+ BColor yiq2rgb(const BColor& rYIQColor);
+
+ /// Transform from R'G'B' to Y'PbPr (the [0,1]x[-.5,.5]x[-.5,.5] equivalent of Y'CbCr (which is scaled into 8bit))
+ BColor rgb2ypbpr(const BColor& rRGBColor);
+ /// Transform from Y'PbPr (the [0,1]x[-.5,.5]x[-.5,.5] equivalent of Y'CbCr (which is scaled into 8bit)) into R'G'B'
+ BColor ypbpr2rgb(const BColor& rYPbPrColor);
+
+ /// Transform from CIE XYZ into Rec. 709 RGB (D65 white point)
+ BColor ciexyz2rgb( const BColor& rXYZColor );
+ /// Transform from Rec. 709 RGB (D65 white point) into CIE XYZ
+ BColor rgb2ciexyz( const BColor& rRGBColor );
+
+ }
+} // end of namespace basegfx
+
+#endif /* _BGFX_COLOR_BCOLORTOOLS_HXX */
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/inc/basegfx/curve/b2dbeziertools.hxx b/basegfx/inc/basegfx/curve/b2dbeziertools.hxx
new file mode 100644
index 000000000000..125599d2f62b
--- /dev/null
+++ b/basegfx/inc/basegfx/curve/b2dbeziertools.hxx
@@ -0,0 +1,66 @@
+/*************************************************************************
+ *
+ * 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: b2dbeziertools.hxx,v $
+ * $Revision: 1.11 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_CURVE_B2DBEZIERTOOLS_HXX
+#define _BGFX_CURVE_B2DBEZIERTOOLS_HXX
+
+#include <sal/types.h>
+#include <vector>
+
+//////////////////////////////////////////////////////////////////////////////
+// predefines
+
+namespace basegfx
+{
+ class B2DCubicBezier;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class B2DCubicBezierHelper
+ {
+ private:
+ ::std::vector< double > maLengthArray;
+ sal_uInt32 mnEdgeCount;
+
+ public:
+ B2DCubicBezierHelper(const B2DCubicBezier& rBase, sal_uInt32 nDivisions = 9);
+
+ double getLength() const { if(maLengthArray.size()) return maLengthArray[maLengthArray.size() - 1]; else return 0.0; }
+ double distanceToRelative(double fDistance) const;
+ double relativeToDistance(double fRelative) const;
+ };
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif /* _BGFX_CURVE_B2DBEZIERTOOLS_HXX */
diff --git a/basegfx/inc/basegfx/curve/b2dcubicbezier.hxx b/basegfx/inc/basegfx/curve/b2dcubicbezier.hxx
new file mode 100644
index 000000000000..81be451499ea
--- /dev/null
+++ b/basegfx/inc/basegfx/curve/b2dcubicbezier.hxx
@@ -0,0 +1,225 @@
+/*************************************************************************
+ *
+ * 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: b2dcubicbezier.hxx,v $
+ * $Revision: 1.13 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_CURVE_B2DCUBICBEZIER_HXX
+#define _BGFX_CURVE_B2DCUBICBEZIER_HXX
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/range/b2drange.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+// predeclarations
+
+namespace basegfx
+{
+ class B2DPolygon;
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class B2DCubicBezier
+ {
+ B2DPoint maStartPoint;
+ B2DPoint maEndPoint;
+ B2DPoint maControlPointA;
+ B2DPoint maControlPointB;
+
+ public:
+ B2DCubicBezier();
+ B2DCubicBezier(const B2DCubicBezier& rBezier);
+ B2DCubicBezier(const B2DPoint& rStart, const B2DPoint& rEnd);
+ B2DCubicBezier(const B2DPoint& rStart, const B2DPoint& rControlPointA, const B2DPoint& rControlPointB, const B2DPoint& rEnd);
+ ~B2DCubicBezier();
+
+ // assignment operator
+ B2DCubicBezier& operator=(const B2DCubicBezier& rBezier);
+
+ // compare operators
+ bool operator==(const B2DCubicBezier& rBezier) const;
+ bool operator!=(const B2DCubicBezier& rBezier) const;
+ bool equal(const B2DCubicBezier& rBezier) const;
+
+ // test if vectors are used
+ bool isBezier() const;
+
+ // test if contained bezier is trivial and reset vectors accordingly
+ void testAndSolveTrivialBezier();
+
+ /** get length of edge
+
+ This method handles beziers and simple edges. For
+ beziers, the deviation describes the maximum allowed
+ deviation from the real edge length. The default
+ allows a deviation of 1% from the correct length.
+
+ For beziers, there is no direct way to get the length,
+ thus this method may subdivide the bezier edge and may
+ not be cheap.
+
+ @param fDeviation
+ The maximal allowed deviation between correct length
+ and bezier edge length
+
+ @return
+ The length of the edge
+ */
+ double getLength(double fDeviation = 0.01) const;
+
+ // get distance between start and end point
+ double getEdgeLength() const;
+
+ // get length of control polygon
+ double getControlPolygonLength() const;
+
+ // data interface
+ B2DPoint getStartPoint() const { return maStartPoint; }
+ void setStartPoint(const B2DPoint& rValue) { maStartPoint = rValue; }
+
+ B2DPoint getEndPoint() const { return maEndPoint; }
+ void setEndPoint(const B2DPoint& rValue) { maEndPoint = rValue; }
+
+ B2DPoint getControlPointA() const { return maControlPointA; }
+ void setControlPointA(const B2DPoint& rValue) { maControlPointA = rValue; }
+
+ B2DPoint getControlPointB() const { return maControlPointB; }
+ void setControlPointB(const B2DPoint& rValue) { maControlPointB = rValue; }
+
+ /** get the tangent in point t
+
+ This method handles all the exceptions, e.g. when control point
+ A is equal to start point and/or control point B is equal to end
+ point
+
+ @param t
+ The bezier index in the range [0.0 .. 1.0]. It will be truncated.
+
+ @return
+ The tangent vector in point t
+ */
+ B2DVector getTangent(double t) const;
+
+ /** adaptive subdivide by angle criteria
+ no start point is added, but all necessary created edges
+ and the end point
+ #i37443# allow the criteria to get unsharp in recursions
+ */
+ void adaptiveSubdivideByAngle(B2DPolygon& rTarget, double fAngleBound, bool bAllowUnsharpen) const;
+
+ /** #i37443# adaptive subdivide by nCount subdivisions
+ no start point is added, but all necessary created edges
+ and the end point
+ */
+ void adaptiveSubdivideByCount(B2DPolygon& rTarget, sal_uInt32 nCount) const;
+
+ /** Subdivide cubic bezier segment.
+
+ This function adaptively subdivides the bezier
+ segment into as much straight line segments as necessary,
+ such that the maximal orthogonal distance from any of the
+ segments to the true curve is less than the given error
+ value.
+ No start point is added, but all necessary created edges
+ and the end point
+
+ @param rPoly
+ Output polygon. The subdivided bezier segment is added to
+ this polygon via B2DPolygon::append().
+
+ @param rCurve
+ The cubic bezier curve to subdivide
+
+ @param fDistanceBound
+ Bound on the maximal distance of the approximation to the
+ true curve.
+ */
+ void adaptiveSubdivideByDistance(B2DPolygon& rTarget, double fDistanceBound) const;
+
+ // get point at given relative position
+ B2DPoint interpolatePoint(double t) const;
+
+ // calculate the smallest distance from given point to this cubic bezier segment
+ // and return the value. The relative position on the segment is returned in rCut.
+ double getSmallestDistancePointToBezierSegment(const B2DPoint& rTestPoint, double& rCut) const;
+
+ // do a split at position t and fill both resulting segments
+ void split(double t, B2DCubicBezier* pBezierA, B2DCubicBezier* pBezierB) const;
+
+ // extract snippet from fStart to fEnd from this bezier
+ B2DCubicBezier snippet(double fStart, double fEnd) const;
+
+ // get range including conrol points
+ B2DRange getRange() const;
+
+ /** Get the minimum extremum position t
+
+ @param rfResult
+ Will be changed and set to a eventually found split value which should be in the
+ range [0.0 .. 1.0]. It will be the smallest current extremum; there may be more
+
+ @return
+ Returns true if there was at least one extremum found
+ */
+ bool getMinimumExtremumPosition(double& rfResult) const;
+
+ /** Get all extremum pos of this segment
+
+ This method will calculate all extremum positions of the segment
+ and add them to rResults if they are in the range ]0.0 .. 1.0[
+
+ @param rResults
+ The vector of doubles where the results will be added. Evtl.
+ existing contents will be removed since an empty vector is a
+ necessary result to express that there are no extreme positions
+ anymore. Since there is an upper maximum of 4 values, it makes
+ sense to use reserve(4) at the vector as preparation.
+ */
+ void getAllExtremumPositions(::std::vector< double >& rResults) const;
+
+ /** Get optimum-split position on this segment
+
+ This method calculates the positions of all points of the segment
+ that have the maximimum distance to the corresponding line from
+ startpoint-endpoint. This helps to approximate the bezier curve
+ with a minimum number of line segments
+
+ @param fResults
+ Result positions are in the range ]0.0 .. 1.0[
+ Cubic beziers have at most two of these positions
+
+ @return
+ Returns the number of split positions found
+ */
+ int getMaxDistancePositions( double fResults[2]) const;
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_CURVE_B2DCUBICBEZIER_HXX */
diff --git a/basegfx/inc/basegfx/curve/b2dquadraticbezier.hxx b/basegfx/inc/basegfx/curve/b2dquadraticbezier.hxx
new file mode 100644
index 000000000000..6d1ec8bfc7bc
--- /dev/null
+++ b/basegfx/inc/basegfx/curve/b2dquadraticbezier.hxx
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *
+ * 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: b2dquadraticbezier.hxx,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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_CURVE_B2DQUADRATICBEZIER_HXX
+#define _BGFX_CURVE_B2DQUADRATICBEZIER_HXX
+
+#include <basegfx/point/b2dpoint.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class B2DQuadraticBezier
+ {
+ ::basegfx::B2DPoint maStartPoint;
+ ::basegfx::B2DPoint maEndPoint;
+ ::basegfx::B2DPoint maControlPoint;
+
+ public:
+ B2DQuadraticBezier();
+ B2DQuadraticBezier(const B2DQuadraticBezier& rBezier);
+ B2DQuadraticBezier(const ::basegfx::B2DPoint& rStart, const ::basegfx::B2DPoint& rEnd);
+ B2DQuadraticBezier(const ::basegfx::B2DPoint& rStart,
+ const ::basegfx::B2DPoint& rControlPoint, const ::basegfx::B2DPoint& rEnd);
+ ~B2DQuadraticBezier();
+
+ // assignment operator
+ B2DQuadraticBezier& operator=(const B2DQuadraticBezier& rBezier);
+
+ // compare operators
+ bool operator==(const B2DQuadraticBezier& rBezier) const;
+ bool operator!=(const B2DQuadraticBezier& rBezier) const;
+
+ // test if control point is placed on the edge
+ bool isBezier() const;
+
+ // data interface
+ ::basegfx::B2DPoint getStartPoint() const { return maStartPoint; }
+ void setStartPoint(const ::basegfx::B2DPoint& rValue) { maStartPoint = rValue; }
+
+ ::basegfx::B2DPoint getEndPoint() const { return maEndPoint; }
+ void setEndPoint(const ::basegfx::B2DPoint& rValue) { maEndPoint = rValue; }
+
+ ::basegfx::B2DPoint getControlPoint() const { return maControlPoint; }
+ void setControlPoint(const ::basegfx::B2DPoint& rValue) { maControlPoint = rValue; }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_CURVE_B2DQUADRATICBEZIER_HXX */
diff --git a/basegfx/inc/basegfx/matrix/b2dhommatrix.hxx b/basegfx/inc/basegfx/matrix/b2dhommatrix.hxx
new file mode 100644
index 000000000000..c7c79d0cd6e9
--- /dev/null
+++ b/basegfx/inc/basegfx/matrix/b2dhommatrix.hxx
@@ -0,0 +1,161 @@
+/*************************************************************************
+ *
+ * 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: b2dhommatrix.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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_MATRIX_B2DHOMMATRIX_HXX
+#define _BGFX_MATRIX_B2DHOMMATRIX_HXX
+
+#include <sal/types.h>
+#include <o3tl/cow_wrapper.hxx>
+
+namespace basegfx
+{
+ class B2DTuple;
+ class Impl2DHomMatrix;
+
+ class B2DHomMatrix
+ {
+ public:
+ typedef o3tl::cow_wrapper< Impl2DHomMatrix > ImplType;
+
+ private:
+ ImplType mpImpl;
+
+ public:
+ B2DHomMatrix();
+ B2DHomMatrix(const B2DHomMatrix& rMat);
+ ~B2DHomMatrix();
+
+ /// unshare this matrix with all internally shared instances
+ void makeUnique();
+
+ double get(sal_uInt16 nRow, sal_uInt16 nColumn) const;
+ void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue);
+
+ // test if last line is default to see if last line needs to be
+ // involved in calculations
+ bool isLastLineDefault() const;
+
+ // Auf Einheitsmatrix zuruecksetzen
+ bool isIdentity() const;
+ void identity();
+
+ // Invertierung
+ bool isInvertible() const;
+ bool invert();
+
+ // Normalisierung
+ bool isNormalized() const;
+ void normalize();
+
+ // Determinante
+ double determinant() const;
+
+ // Trace
+ double trace() const;
+
+ // Transpose
+ void transpose();
+
+ // Rotation
+ void rotate(double fRadiant);
+
+ // Translation
+ void translate(double fX, double fY);
+
+ // Skalierung
+ void scale(double fX, double fY);
+
+ // Shearing-Matrices
+ void shearX(double fSx);
+ void shearY(double fSy);
+
+ // Addition, Subtraktion
+ B2DHomMatrix& operator+=(const B2DHomMatrix& rMat);
+ B2DHomMatrix& operator-=(const B2DHomMatrix& rMat);
+
+ // Vergleichsoperatoren
+ bool operator==(const B2DHomMatrix& rMat) const;
+ bool operator!=(const B2DHomMatrix& rMat) const;
+
+ // Multiplikation, Division mit Konstante
+ B2DHomMatrix& operator*=(double fValue);
+ B2DHomMatrix& operator/=(double fValue);
+
+ // Matritzenmultiplikation von links auf die lokale
+ B2DHomMatrix& operator*=(const B2DHomMatrix& rMat);
+
+ // assignment operator
+ B2DHomMatrix& operator=(const B2DHomMatrix& rMat);
+
+ // Help routine to decompose given homogen 3x3 matrix to components. A correction of
+ // the components is done to avoid inaccuracies.
+ // Zerlegung
+ bool decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const;
+ };
+
+ // Addition, Subtraktion
+ inline B2DHomMatrix operator+(const B2DHomMatrix& rMatA, const B2DHomMatrix& rMatB)
+ {
+ B2DHomMatrix aSum(rMatA);
+ aSum += rMatB;
+ return aSum;
+ }
+
+ inline B2DHomMatrix operator-(const B2DHomMatrix& rMatA, const B2DHomMatrix& rMatB)
+ {
+ B2DHomMatrix aDiv(rMatA);
+ aDiv -= rMatB;
+ return aDiv;
+ }
+
+ // Multiplikation, Division mit Konstante
+ inline B2DHomMatrix operator*(const B2DHomMatrix& rMat, double fValue)
+ {
+ B2DHomMatrix aNew(rMat);
+ aNew *= fValue;
+ return aNew;
+ }
+
+ inline B2DHomMatrix operator/(const B2DHomMatrix& rMat, double fValue)
+ {
+ B2DHomMatrix aNew(rMat);
+ aNew *= 1.0 / fValue;
+ return aNew;
+ }
+
+ inline B2DHomMatrix operator*(const B2DHomMatrix& rMatA, const B2DHomMatrix& rMatB)
+ {
+ B2DHomMatrix aMul(rMatB);
+ aMul *= rMatA;
+ return aMul;
+ }
+} // end of namespace basegfx
+
+#endif /* _BGFX_MATRIX_B2DHOMMATRIX_HXX */
diff --git a/basegfx/inc/basegfx/matrix/b2dhommatrixtools.hxx b/basegfx/inc/basegfx/matrix/b2dhommatrixtools.hxx
new file mode 100644
index 000000000000..0b200b812bed
--- /dev/null
+++ b/basegfx/inc/basegfx/matrix/b2dhommatrixtools.hxx
@@ -0,0 +1,86 @@
+/*************************************************************************
+ *
+ * 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: b2dhommatrixtools.hxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_MATRIX_B2DHOMMATRIXTOOLS_HXX
+#define _BGFX_MATRIX_B2DHOMMATRIXTOOLS_HXX
+
+#include <sal/types.h>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class DecomposedB2DHomMatrixContainer
+ {
+ private:
+ B2DHomMatrix maB2DHomMatrix;
+ B2DVector maScale;
+ B2DVector maTranslate;
+ double mfRotate;
+ double mfShearX;
+
+ // bitfield
+ unsigned mbDecomposed : 1;
+
+ void impCheckDecompose()
+ {
+ if(!mbDecomposed)
+ {
+ maB2DHomMatrix.decompose(maScale, maTranslate, mfRotate, mfShearX);
+ mbDecomposed = true;
+ }
+ }
+
+ public:
+ DecomposedB2DHomMatrixContainer(const B2DHomMatrix& rB2DHomMatrix)
+ : maB2DHomMatrix(rB2DHomMatrix),
+ maScale(),
+ maTranslate(),
+ mfRotate(0.0),
+ mfShearX(0.0),
+ mbDecomposed(false)
+ {
+ }
+
+ // data access
+ const B2DHomMatrix& getB2DHomMatrix() const { return maB2DHomMatrix; }
+ const B2DVector& getScale() const { const_cast< DecomposedB2DHomMatrixContainer* >(this)->impCheckDecompose(); return maScale; }
+ const B2DVector& getTranslate() const { const_cast< DecomposedB2DHomMatrixContainer* >(this)->impCheckDecompose(); return maTranslate; }
+ double getRotate() const { const_cast< DecomposedB2DHomMatrixContainer* >(this)->impCheckDecompose(); return mfRotate; }
+ double getShearX() const { const_cast< DecomposedB2DHomMatrixContainer* >(this)->impCheckDecompose(); return mfShearX; }
+ };
+} // end of namespace basegfx
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif /* _BGFX_MATRIX_B2DHOMMATRIXTOOLS_HXX */
diff --git a/basegfx/inc/basegfx/matrix/b3dhommatrix.hxx b/basegfx/inc/basegfx/matrix/b3dhommatrix.hxx
new file mode 100644
index 000000000000..a6b9a147111e
--- /dev/null
+++ b/basegfx/inc/basegfx/matrix/b3dhommatrix.hxx
@@ -0,0 +1,178 @@
+/*************************************************************************
+ *
+ * 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: b3dhommatrix.hxx,v $
+ * $Revision: 1.11 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_MATRIX_B3DHOMMATRIX_HXX
+#define _BGFX_MATRIX_B3DHOMMATRIX_HXX
+
+#include <sal/types.h>
+#include <basegfx/point/b3dpoint.hxx>
+#include <basegfx/vector/b3dvector.hxx>
+#include <o3tl/cow_wrapper.hxx>
+
+namespace basegfx
+{
+ class B3DTuple;
+ class Impl3DHomMatrix;
+
+ class B3DHomMatrix
+ {
+ public:
+ typedef o3tl::cow_wrapper< Impl3DHomMatrix > ImplType;
+
+ private:
+ ImplType mpImpl;
+
+ public:
+ B3DHomMatrix();
+ B3DHomMatrix(const B3DHomMatrix& rMat);
+ ~B3DHomMatrix();
+
+ /// unshare this matrix with all internally shared instances
+ void makeUnique();
+
+ double get(sal_uInt16 nRow, sal_uInt16 nColumn) const;
+ void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue);
+
+ // test if last line is default to see if last line needs to be
+ // involved in calculations
+ bool isLastLineDefault() const;
+
+ bool isIdentity() const;
+ /// Reset to the identity matrix
+ void identity();
+
+ bool isInvertible() const;
+ /// Invert the matrix (if possible)
+ bool invert();
+
+ bool isNormalized() const;
+ /// Normalize (i.e. force w=1) the matrix
+ void normalize();
+
+ /// Calc the matrix determinant
+ double determinant() const;
+
+ /// Calc the matrix trace
+ double trace() const;
+
+ /// Transpose the matrix
+ void transpose();
+
+ /// Rotation
+ void rotate(double fAngleX,double fAngleY,double fAngleZ);
+
+ /// Translation
+ void translate(double fX, double fY, double fZ);
+
+ /// Scaling
+ void scale(double fX, double fY, double fZ);
+
+ // Shearing-Matrices
+ void shearXY(double fSx, double fSy);
+ void shearYZ(double fSy, double fSz);
+ void shearXZ(double fSx, double fSz);
+
+ // Projection matrices, used for converting between eye and
+ // clip coordinates
+ void frustum(double fLeft = -1.0, double fRight = 1.0,
+ double fBottom = -1.0, double fTop = 1.0,
+ double fNear = 0.001, double fFar = 1.0);
+
+ void ortho(double fLeft = -1.0, double fRight = 1.0,
+ double fBottom = -1.0, double fTop = 1.0,
+ double fNear = 0.0, double fFar = 1.0);
+
+ // build orientation matrix
+ void orientation(
+ B3DPoint aVRP = B3DPoint(0.0,0.0,1.0),
+ B3DVector aVPN = B3DVector(0.0,0.0,1.0),
+ B3DVector aVUV = B3DVector(0.0,1.0,0.0));
+
+ // addition, subtraction
+ B3DHomMatrix& operator+=(const B3DHomMatrix& rMat);
+ B3DHomMatrix& operator-=(const B3DHomMatrix& rMat);
+
+ // comparison
+ bool operator==(const B3DHomMatrix& rMat) const;
+ bool operator!=(const B3DHomMatrix& rMat) const;
+
+ // multiplication, division by constant value
+ B3DHomMatrix& operator*=(double fValue);
+ B3DHomMatrix& operator/=(double fValue);
+
+ // matrix multiplication (from the left)
+ B3DHomMatrix& operator*=(const B3DHomMatrix& rMat);
+
+ // assignment operator
+ B3DHomMatrix& operator=(const B3DHomMatrix& rMat);
+
+ // decomposition
+ bool decompose(B3DTuple& rScale, B3DTuple& rTranslate, B3DTuple& rRotate, B3DTuple& rShear) const;
+ };
+
+ // addition, subtraction
+ inline B3DHomMatrix operator+(const B3DHomMatrix& rMatA, const B3DHomMatrix& rMatB)
+ {
+ B3DHomMatrix aSum(rMatA);
+ aSum += rMatB;
+ return aSum;
+ }
+
+ inline B3DHomMatrix operator-(const B3DHomMatrix& rMatA, const B3DHomMatrix& rMatB)
+ {
+ B3DHomMatrix aDiv(rMatA);
+ aDiv -= rMatB;
+ return aDiv;
+ }
+
+ // multiplication, division by constant value
+ inline B3DHomMatrix operator*(const B3DHomMatrix& rMat, double fValue)
+ {
+ B3DHomMatrix aNew(rMat);
+ aNew *= fValue;
+ return aNew;
+ }
+
+ inline B3DHomMatrix operator/(const B3DHomMatrix& rMat, double fValue)
+ {
+ B3DHomMatrix aNew(rMat);
+ aNew *= 1.0 / fValue;
+ return aNew;
+ }
+
+ inline B3DHomMatrix operator*(const B3DHomMatrix& rMatA, const B3DHomMatrix& rMatB)
+ {
+ B3DHomMatrix aMul(rMatB);
+ aMul *= rMatA;
+ return aMul;
+ }
+} // end of namespace basegfx
+
+#endif /* _BGFX_MATRIX_B3DHOMMATRIX_HXX */
diff --git a/basegfx/inc/basegfx/numeric/ftools.hxx b/basegfx/inc/basegfx/numeric/ftools.hxx
new file mode 100644
index 000000000000..5003ede0c4cf
--- /dev/null
+++ b/basegfx/inc/basegfx/numeric/ftools.hxx
@@ -0,0 +1,206 @@
+/*************************************************************************
+ *
+ * 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: ftools.hxx,v $
+ * $Revision: 1.14 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_NUMERIC_FTOOLS_HXX
+#define _BGFX_NUMERIC_FTOOLS_HXX
+
+#include <rtl/math.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+// standard PI defines from solar.h, but we do not want to link against tools
+
+#ifndef F_PI
+#define F_PI M_PI
+#endif
+#ifndef F_PI2
+#define F_PI2 M_PI_2
+#endif
+#ifndef F_PI4
+#define F_PI4 M_PI_4
+#endif
+#ifndef F_PI180
+#define F_PI180 (M_PI/180.0)
+#endif
+#ifndef F_PI1800
+#define F_PI1800 (M_PI/1800.0)
+#endif
+#ifndef F_PI18000
+#define F_PI18000 (M_PI/18000.0)
+#endif
+#ifndef F_2PI
+#define F_2PI (2.0*M_PI)
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+// fTools defines
+
+namespace basegfx
+{
+ /** Round double to nearest integer
+
+ @return the nearest integer
+ */
+ inline sal_Int32 fround( double fVal )
+ {
+ return fVal > 0.0 ? static_cast<sal_Int32>( fVal + .5 ) : -static_cast<sal_Int32>( -fVal + .5 );
+ }
+
+ /** Round double to nearest integer
+
+ @return the nearest 64 bit integer
+ */
+ inline sal_Int64 fround64( double fVal )
+ {
+ return fVal > 0.0 ? static_cast<sal_Int64>( fVal + .5 ) : -static_cast<sal_Int64>( -fVal + .5 );
+ }
+
+ /** Prune a small epsilon range around zero.
+
+ Use this method e.g. for calculating scale values. There, it
+ is usually advisable not to set a scaling to 0.0, because that
+ yields singular transformation matrices.
+
+ @param fVal
+ An arbitrary, but finite and valid number
+
+ @return either fVal, or a small value slightly above (when
+ fVal>0) or below (when fVal<0) zero.
+ */
+ inline double pruneScaleValue( double fVal )
+ {
+ // old version used ::std::min/max, but this collides if min is defined as preprocessor
+ // macro which is the case e.g with windows.h headers. The simplest way to avoid this is to
+ // just use the full comparison. I keep the original here, maybe there will be a better
+ // solution some day.
+ //
+ //return fVal < 0.0 ?
+ // (::std::min(fVal,-0.00001)) :
+ // (::std::max(fVal,0.00001));
+
+ if(fVal < 0.0)
+ return (fVal < -0.00001 ? fVal : -0.00001);
+ else
+ return (fVal > 0.00001 ? fVal : 0.00001);
+ }
+
+ /** clamp given value against given minimum and maximum values
+ */
+ template <class T> const T& clamp(const T& value, const T& minimum, const T& maximum)
+ {
+ if(value < minimum)
+ {
+ return minimum;
+ }
+ else if(value > maximum)
+ {
+ return maximum;
+ }
+ else
+ {
+ return value;
+ }
+ }
+
+ /** Convert value from degrees to radians
+ */
+ inline double deg2rad( double v )
+ {
+ // divide first, to get exact values for v being a multiple of
+ // 90 degrees
+ return v / 90.0 * M_PI_2;
+ }
+
+ /** Convert value radians to degrees
+ */
+ inline double rad2deg( double v )
+ {
+ // divide first, to get exact values for v being a multiple of
+ // pi/2
+ return v / M_PI_2 * 90.0;
+ }
+
+
+ class fTools
+ {
+ /// Threshold value for equalZero()
+ static double mfSmallValue;
+
+ public:
+ /// Get threshold value for equalZero and friends
+ static double getSmallValue() { return mfSmallValue; }
+ /// Set threshold value for equalZero and friends
+ static void setSmallValue(const double& rfNew) { mfSmallValue = rfNew; }
+
+ /// Compare against small value
+ static bool equalZero(const double& rfVal)
+ {
+ return (fabs(rfVal) <= getSmallValue());
+ }
+
+ /// Compare against given small value
+ static bool equalZero(const double& rfVal, const double& rfSmallValue)
+ {
+ return (fabs(rfVal) <= rfSmallValue);
+ }
+
+ static bool equal(const double& rfValA, const double& rfValB)
+ {
+ // changed to approxEqual usage for better numerical correctness
+ return rtl::math::approxEqual(rfValA, rfValB);
+ }
+
+ static bool equal(const double& rfValA, const double& rfValB, const double& rfSmallValue)
+ {
+ return (fabs(rfValA) - fabs(rfValB) <= rfSmallValue);
+ }
+
+ static bool less(const double& rfValA, const double& rfValB)
+ {
+ return (rfValA < rfValB && !equal(rfValA, rfValB));
+ }
+
+ static bool lessOrEqual(const double& rfValA, const double& rfValB)
+ {
+ return (rfValA < rfValB || equal(rfValA, rfValB));
+ }
+
+ static bool more(const double& rfValA, const double& rfValB)
+ {
+ return (rfValA > rfValB && !equal(rfValA, rfValB));
+ }
+
+ static bool moreOrEqual(const double& rfValA, const double& rfValB)
+ {
+ return (rfValA > rfValB || equal(rfValA, rfValB));
+ }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_NUMERIC_FTOOLS_HXX */
diff --git a/basegfx/inc/basegfx/pixel/bpixel.hxx b/basegfx/inc/basegfx/pixel/bpixel.hxx
new file mode 100644
index 000000000000..df3b72e9d21f
--- /dev/null
+++ b/basegfx/inc/basegfx/pixel/bpixel.hxx
@@ -0,0 +1,226 @@
+/*************************************************************************
+ *
+ * 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: bpixel.hxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_PIXEL_BPIXEL_HXX
+#define _BGFX_PIXEL_BPIXEL_HXX
+
+#include <sal/types.h>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+// predeclarations
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class BPixel
+ {
+ protected:
+ union
+ {
+ struct
+ {
+ // bitfield
+ unsigned mnR : 8; // red intensity
+ unsigned mnG : 8; // green intensity
+ unsigned mnB : 8; // blue intensity
+ unsigned mnO : 8; // opacity, 0 == full transparence
+ } maRGBO;
+
+ struct
+ {
+ // bitfield
+ unsigned mnValue : 32; // all values
+ } maCombinedRGBO;
+ } maPixelUnion;
+
+ public:
+ BPixel()
+ {
+ maPixelUnion.maCombinedRGBO.mnValue = 0L;
+ }
+
+ // use explicit here to make sure everyone knows what he is doing. Values range from
+ // 0..255 integer here.
+ explicit BPixel(sal_uInt8 nRed, sal_uInt8 nGreen, sal_uInt8 nBlue, sal_uInt8 nOpacity)
+ {
+ maPixelUnion.maRGBO.mnR = nRed;
+ maPixelUnion.maRGBO.mnG = nGreen;
+ maPixelUnion.maRGBO.mnB = nBlue;
+ maPixelUnion.maRGBO.mnO = nOpacity;
+ }
+
+ // constructor from BColor which uses double precision color, so change it
+ // to local integer format. It will also be clamped here.
+ BPixel(const BColor& rColor, sal_uInt8 nOpacity)
+ {
+ maPixelUnion.maRGBO.mnR = sal_uInt8((rColor.getRed() * 255.0) + 0.5);
+ maPixelUnion.maRGBO.mnG = sal_uInt8((rColor.getGreen() * 255.0) + 0.5);
+ maPixelUnion.maRGBO.mnB = sal_uInt8((rColor.getBlue() * 255.0) + 0.5);
+ maPixelUnion.maRGBO.mnO = nOpacity;
+ }
+
+ // copy constructor
+ BPixel(const BPixel& rPixel)
+ {
+ maPixelUnion.maCombinedRGBO.mnValue = rPixel.maPixelUnion.maCombinedRGBO.mnValue;
+ }
+
+ ~BPixel()
+ {}
+
+ // assignment operator
+ BPixel& operator=( const BPixel& rPixel )
+ {
+ maPixelUnion.maCombinedRGBO.mnValue = rPixel.maPixelUnion.maCombinedRGBO.mnValue;
+ return *this;
+ }
+
+ // data access read
+ sal_uInt8 getRed() const { return maPixelUnion.maRGBO.mnR; }
+ sal_uInt8 getGreen() const { return maPixelUnion.maRGBO.mnG; }
+ sal_uInt8 getBlue() const { return maPixelUnion.maRGBO.mnB; }
+ sal_uInt8 getOpacity() const { return maPixelUnion.maRGBO.mnO; }
+ sal_uInt32 getRedGreenBlueOpacity() const { return maPixelUnion.maCombinedRGBO.mnValue; }
+
+ // data access write
+ void setRed(sal_uInt8 nNew) { maPixelUnion.maRGBO.mnR = nNew; }
+ void setGreen(sal_uInt8 nNew) { maPixelUnion.maRGBO.mnG = nNew; }
+ void setBlue(sal_uInt8 nNew) { maPixelUnion.maRGBO.mnB = nNew; }
+ void setOpacity(sal_uInt8 nNew) { maPixelUnion.maRGBO.mnO = nNew; }
+ void setRedGreenBlueOpacity(sal_uInt32 nRedGreenBlueOpacity) { maPixelUnion.maCombinedRGBO.mnValue = nRedGreenBlueOpacity; }
+ void setRedGreenBlue(sal_uInt8 nR, sal_uInt8 nG, sal_uInt8 nB) { maPixelUnion.maRGBO.mnR = nR; maPixelUnion.maRGBO.mnG = nG; maPixelUnion.maRGBO.mnB = nB; }
+
+ // comparators
+ bool isInvisible() const { return (0 == maPixelUnion.maRGBO.mnO); }
+ bool isVisible() const { return (0 != maPixelUnion.maRGBO.mnO); }
+ bool isEmpty() const { return isInvisible(); }
+ bool isUsed() const { return isVisible(); }
+
+ bool operator==( const BPixel& rPixel ) const
+ {
+ return (rPixel.maPixelUnion.maCombinedRGBO.mnValue == maPixelUnion.maCombinedRGBO.mnValue);
+ }
+
+ bool operator!=( const BPixel& rPixel ) const
+ {
+ return (rPixel.maPixelUnion.maCombinedRGBO.mnValue != maPixelUnion.maCombinedRGBO.mnValue);
+ }
+
+ // empty element
+ static const BPixel& getEmptyBPixel();
+ };
+
+ //////////////////////////////////////////////////////////////////////////
+ // external operators
+
+ inline BPixel minimum(const BPixel& rTupA, const BPixel& rTupB)
+ {
+ BPixel aMin(
+ (rTupB.getRed() < rTupA.getRed()) ? rTupB.getRed() : rTupA.getRed(),
+ (rTupB.getGreen() < rTupA.getGreen()) ? rTupB.getGreen() : rTupA.getGreen(),
+ (rTupB.getBlue() < rTupA.getBlue()) ? rTupB.getBlue() : rTupA.getBlue(),
+ (rTupB.getOpacity() < rTupA.getOpacity()) ? rTupB.getOpacity() : rTupA.getOpacity());
+ return aMin;
+ }
+
+ inline BPixel maximum(const BPixel& rTupA, const BPixel& rTupB)
+ {
+ BPixel aMax(
+ (rTupB.getRed() > rTupA.getRed()) ? rTupB.getRed() : rTupA.getRed(),
+ (rTupB.getGreen() > rTupA.getGreen()) ? rTupB.getGreen() : rTupA.getGreen(),
+ (rTupB.getBlue() > rTupA.getBlue()) ? rTupB.getBlue() : rTupA.getBlue(),
+ (rTupB.getOpacity() > rTupA.getOpacity()) ? rTupB.getOpacity() : rTupA.getOpacity());
+ return aMax;
+ }
+
+ inline BPixel interpolate(const BPixel& rOld1, const BPixel& rOld2, double t)
+ {
+ if(rOld1 == rOld2)
+ {
+ return rOld1;
+ }
+ else if(0.0 >= t)
+ {
+ return rOld1;
+ }
+ else if(1.0 <= t)
+ {
+ return rOld2;
+ }
+ else
+ {
+ const sal_uInt32 nFactor(fround(256.0 * t));
+ const sal_uInt32 nNegFac(256L - nFactor);
+ return BPixel(
+ (sal_uInt8)(((sal_uInt32)rOld1.getRed() * nNegFac + (sal_uInt32)rOld2.getRed() * nFactor) >> 8L),
+ (sal_uInt8)(((sal_uInt32)rOld1.getGreen() * nNegFac + (sal_uInt32)rOld2.getGreen() * nFactor) >> 8L),
+ (sal_uInt8)(((sal_uInt32)rOld1.getBlue() * nNegFac + (sal_uInt32)rOld2.getBlue() * nFactor) >> 8L),
+ (sal_uInt8)(((sal_uInt32)rOld1.getOpacity() * nNegFac + (sal_uInt32)rOld2.getOpacity() * nFactor) >> 8L));
+ }
+ }
+
+ inline BPixel average(const BPixel& rOld1, const BPixel& rOld2)
+ {
+ if(rOld1 == rOld2)
+ {
+ return rOld1;
+ }
+ else
+ {
+ return BPixel(
+ (sal_uInt8)(((sal_uInt32)rOld1.getRed() + (sal_uInt32)rOld2.getRed()) >> 1L),
+ (sal_uInt8)(((sal_uInt32)rOld1.getGreen() + (sal_uInt32)rOld2.getGreen()) >> 1L),
+ (sal_uInt8)(((sal_uInt32)rOld1.getBlue() + (sal_uInt32)rOld2.getBlue()) >> 1L),
+ (sal_uInt8)(((sal_uInt32)rOld1.getOpacity() + (sal_uInt32)rOld2.getOpacity()) >> 1L));
+ }
+ }
+
+ inline BPixel average(const BPixel& rOld1, const BPixel& rOld2, const BPixel& rOld3)
+ {
+ if(rOld1 == rOld2 && rOld2 == rOld3)
+ {
+ return rOld1;
+ }
+ else
+ {
+ return BPixel(
+ (sal_uInt8)(((sal_uInt32)rOld1.getRed() + (sal_uInt32)rOld2.getRed() + (sal_uInt32)rOld3.getRed()) / 3L),
+ (sal_uInt8)(((sal_uInt32)rOld1.getGreen() + (sal_uInt32)rOld2.getGreen() + (sal_uInt32)rOld3.getGreen()) / 3L),
+ (sal_uInt8)(((sal_uInt32)rOld1.getBlue() + (sal_uInt32)rOld2.getBlue() + (sal_uInt32)rOld3.getBlue()) / 3L),
+ (sal_uInt8)(((sal_uInt32)rOld1.getOpacity() + (sal_uInt32)rOld2.getOpacity() + (sal_uInt32)rOld3.getOpacity()) / 3L));
+ }
+ }
+} // end of namespace basegfx
+
+#endif /* _BGFX_PIXEL_BPIXEL_HXX */
diff --git a/basegfx/inc/basegfx/point/b2dhompoint.hxx b/basegfx/inc/basegfx/point/b2dhompoint.hxx
new file mode 100644
index 000000000000..591aec5e9ad3
--- /dev/null
+++ b/basegfx/inc/basegfx/point/b2dhompoint.hxx
@@ -0,0 +1,238 @@
+/*************************************************************************
+ *
+ * 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: b2dhompoint.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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POINT_B2DHOMPOINT_HXX
+#define _BGFX_POINT_B2DHOMPOINT_HXX
+
+#include <basegfx/point/b2dpoint.hxx>
+
+namespace basegfx
+{
+ /** Basic homogen Point class with two double values and one homogen factor
+
+ This class provides access to homogen coordinates in 2D.
+ For this purpose all the operators which need to do specific
+ action due to their homogenity are implemented here.
+ The only caveat are member methods which are declared as const
+ but do change the content. These are documented for that reason.
+ The class is designed to provide homogenous coordinates without
+ direct access to the homogen part (mfW). This is also the reason
+ for leaving out the [] operators which return references to members.
+
+ @see B2DTuple
+ */
+ class B2DHomPoint
+ {
+ protected:
+ /// This member contains the coordinate part of the point
+ ::basegfx::B2DTuple maTuple;
+
+ /// This Member holds the homogenous part of the point
+ double mfW;
+
+ /** Test if this homogen point does have a homogenous part
+
+ @return Returns true if this point has no homogenous part
+ */
+ bool implIsHomogenized() const;
+
+ /** Remove homogenous part of this Point
+
+ This method does necessary calculations to remove
+ the evtl. homogenous part of this Point. This may
+ change all members.
+ */
+ void implHomogenize();
+
+ /** Test and on demand remove homogenous part
+
+ This method tests if this Point does have a homogenous part
+ and then evtl. takes actions to remove that part.
+
+ @attention Even when this method is const it may change all
+ members of this instance. This is due to the fact that changing
+ the homogenous part of a homogenous point does from a mathematical
+ point of view not change the point at all.
+ */
+ void implTestAndHomogenize() const;
+
+ public:
+ /** Create a homogen point
+
+ The point is initialized to (0.0, 0.0)
+ */
+ B2DHomPoint()
+ : maTuple(),
+ mfW(1.0)
+ {}
+
+ /** Create a homogen point
+
+ @param fX
+ This parameter is used to initialize the X-coordinate
+ of the Point. The homogenous part is initialized to 1.0.
+
+ @param fY
+ This parameter is used to initialize the Y-coordinate
+ of the Point. The homogenous part is initialized to 1.0.
+ */
+ B2DHomPoint(double fX, double fY)
+ : maTuple(fX, fY),
+ mfW(1.0)
+ {}
+
+ /** Create a copy of a 2D Point
+
+ @param rVec
+ The 2D point which will be copied. The homogenous part
+ is initialized to 1.0.
+ */
+ B2DHomPoint(const B2DPoint& rVec)
+ : maTuple(rVec),
+ mfW(1.0)
+ {}
+
+ /** Create a copy of a homogen point
+
+ @param rVec
+ The homogen point which will be copied. The homogenous part
+ is copied, too.
+ */
+ B2DHomPoint(const B2DHomPoint& rVec)
+ : maTuple(rVec.maTuple.getX(), rVec.maTuple.getY()),
+ mfW(rVec.mfW)
+ {}
+
+ ~B2DHomPoint()
+ {}
+
+ /** Get a 2D point from this homogenous point
+
+ This method normalizes this homogen point if necessary and
+ returns the corresponding 2D point for this homogen point.
+
+ @attention Even when this method is const it may change all
+ members of this instance.
+ */
+ B2DPoint getB2DPoint() const;
+
+ /** Get X-coordinate
+
+ This method normalizes this homogen point if necessary and
+ returns the corresponding X-coordinate for this homogen point.
+
+ @attention Even when this method is const it may change all
+ members of this instance.
+ */
+ double getX() const;
+
+ /** Get Y-coordinate
+
+ This method normalizes this homogen point if necessary and
+ returns the corresponding Y-coordinate for this homogen point.
+
+ @attention Even when this method is const it may change all
+ members of this instance.
+ */
+ double getY() const;
+
+ /** Set X-coordinate of the homogen point.
+
+ This method sets the X-coordinate of the homogen point. If
+ the point does have a homogenous part this is taken into account.
+
+ @param fX
+ The to-be-set X-coordinate without homogenous part.
+ */
+ void setX(double fX);
+
+ /** Set Y-coordinate of the homogen point.
+
+ This method sets the Y-coordinate of the homogen point. If
+ the point does have a homogenous part this is taken into account.
+
+ @param fY
+ The to-be-set Y-coordinate without homogenous part.
+ */
+ void setY(double fY);
+
+ // operators
+ //////////////////////////////////////////////////////////////////////
+
+ B2DHomPoint& operator+=( const B2DHomPoint& rPnt );
+
+ B2DHomPoint& operator-=( const B2DHomPoint& rPnt );
+
+ B2DHomPoint& operator*=(double t);
+
+ B2DHomPoint& operator*=( const B2DHomMatrix& rMat );
+
+ B2DHomPoint& operator/=(double t);
+
+ B2DHomPoint& operator-(void);
+
+ bool operator==( const B2DHomPoint& rPnt ) const;
+
+ bool operator!=( const B2DHomPoint& rPnt ) const;
+
+ B2DHomPoint& operator=( const B2DHomPoint& rPnt );
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ B2DHomPoint minimum(const B2DHomPoint& rVecA, const B2DHomPoint& rVecB);
+
+ B2DHomPoint maximum(const B2DHomPoint& rVecA, const B2DHomPoint& rVecB);
+
+ B2DHomPoint absolute(const B2DHomPoint& rVec);
+
+ B2DHomPoint interpolate(B2DHomPoint& rOld1, B2DHomPoint& rOld2, double t);
+
+ B2DHomPoint average(B2DHomPoint& rOld1, B2DHomPoint& rOld2);
+
+ B2DHomPoint average(B2DHomPoint& rOld1, B2DHomPoint& rOld2, B2DHomPoint& rOld3);
+
+ B2DHomPoint operator+(const B2DHomPoint& rVecA, const B2DHomPoint& rVecB);
+
+ B2DHomPoint operator-(const B2DHomPoint& rVecA, const B2DHomPoint& rVecB);
+
+ B2DHomPoint operator*(const B2DHomPoint& rVec, double t);
+
+ B2DHomPoint operator*(double t, const B2DHomPoint& rVec);
+
+ B2DHomPoint operator*( const B2DHomMatrix& rMat, const B2DHomPoint& rPoint );
+
+ B2DHomPoint operator/(const B2DHomPoint& rVec, double t);
+
+ B2DHomPoint operator/(double t, const B2DHomPoint& rVec);
+} // end of namespace basegfx
+
+#endif /* _BGFX_POINT_B2DHOMPOINT_HXX */
diff --git a/basegfx/inc/basegfx/point/b2dpoint.hxx b/basegfx/inc/basegfx/point/b2dpoint.hxx
new file mode 100644
index 000000000000..ad65b836409c
--- /dev/null
+++ b/basegfx/inc/basegfx/point/b2dpoint.hxx
@@ -0,0 +1,154 @@
+/*************************************************************************
+ *
+ * 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: b2dpoint.hxx,v $
+ * $Revision: 1.12 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POINT_B2DPOINT_HXX
+#define _BGFX_POINT_B2DPOINT_HXX
+
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <basegfx/point/b2ipoint.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ // predeclaration
+ class B2DHomMatrix;
+
+ /** Base Point class with two double values
+
+ This class derives all operators and common handling for
+ a 2D data class from B2DTuple. All necessary extensions
+ which are special for points will be added here.
+
+ @see B2DTuple
+ */
+ class B2DPoint : public ::basegfx::B2DTuple
+ {
+ public:
+ /** Create a 2D Point
+
+ The point is initialized to (0.0, 0.0)
+ */
+ B2DPoint()
+ : B2DTuple()
+ {}
+
+ /** Create a 2D Point
+
+ @param fX
+ This parameter is used to initialize the X-coordinate
+ of the 2D Point.
+
+ @param fY
+ This parameter is used to initialize the Y-coordinate
+ of the 2D Point.
+ */
+ B2DPoint(double fX, double fY)
+ : B2DTuple(fX, fY)
+ {}
+
+ /** Create a copy of a 2D Point
+
+ @param rPoint
+ The 2D Point which will be copied.
+ */
+ B2DPoint(const B2DPoint& rPoint)
+ : B2DTuple(rPoint)
+ {}
+
+ /** Create a copy of a 2D Point
+
+ @param rPoint
+ The 2D Point which will be copied.
+ */
+ B2DPoint(const ::basegfx::B2IPoint& rPoint)
+ : B2DTuple(rPoint)
+ {}
+
+ /** constructor with tuple to allow copy-constructing
+ from B2DTuple-based classes
+ */
+ B2DPoint(const ::basegfx::B2DTuple& rTuple)
+ : B2DTuple(rTuple)
+ {}
+
+ ~B2DPoint()
+ {}
+
+ /** *=operator to allow usage from B2DPoint, too
+ */
+ B2DPoint& operator*=( const B2DPoint& rPnt )
+ {
+ mfX *= rPnt.mfX;
+ mfY *= rPnt.mfY;
+ return *this;
+ }
+
+ /** *=operator to allow usage from B2DPoint, too
+ */
+ B2DPoint& operator*=(double t)
+ {
+ mfX *= t;
+ mfY *= t;
+ return *this;
+ }
+
+ /** assignment operator to allow assigning the results
+ of B2DTuple calculations
+ */
+ B2DPoint& operator=( const ::basegfx::B2DTuple& rPoint );
+
+ /** Transform point by given transformation matrix.
+
+ The translational components of the matrix are, in
+ contrast to B2DVector, applied.
+ */
+ B2DPoint& operator*=( const ::basegfx::B2DHomMatrix& rMat );
+
+ static const B2DPoint& getEmptyPoint()
+ {
+ return (const B2DPoint&) ::basegfx::B2DTuple::getEmptyTuple();
+ }
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ /** Transform B2DPoint by given transformation matrix.
+
+ Since this is a Point, translational components of the
+ matrix are used.
+ */
+ B2DPoint operator*( const B2DHomMatrix& rMat, const B2DPoint& rPoint );
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif /* _BGFX_POINT_B2DPOINT_HXX */
diff --git a/basegfx/inc/basegfx/point/b2ipoint.hxx b/basegfx/inc/basegfx/point/b2ipoint.hxx
new file mode 100644
index 000000000000..198e7a1dd22b
--- /dev/null
+++ b/basegfx/inc/basegfx/point/b2ipoint.hxx
@@ -0,0 +1,130 @@
+/*************************************************************************
+ *
+ * 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: b2ipoint.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POINT_B2IPOINT_HXX
+#define _BGFX_POINT_B2IPOINT_HXX
+
+#include <basegfx/tuple/b2ituple.hxx>
+
+namespace basegfx
+{
+ // predeclaration
+ class B2DHomMatrix;
+
+ /** Base Point class with two sal_Int32 values
+
+ This class derives all operators and common handling for
+ a 2D data class from B2ITuple. All necessary extensions
+ which are special for points will be added here.
+
+ @see B2ITuple
+ */
+ class B2IPoint : public ::basegfx::B2ITuple
+ {
+ public:
+ /** Create a 2D Point
+
+ The point is initialized to (0, 0)
+ */
+ B2IPoint()
+ : B2ITuple()
+ {}
+
+ /** Create a 2D Point
+
+ @param nX
+ This parameter is used to initialize the X-coordinate
+ of the 2D Point.
+
+ @param nY
+ This parameter is used to initialize the Y-coordinate
+ of the 2D Point.
+ */
+ B2IPoint(sal_Int32 nX, sal_Int32 nY)
+ : B2ITuple(nX, nY)
+ {}
+
+ /** Create a copy of a 2D Point
+
+ @param rPoint
+ The 2D Point which will be copied.
+ */
+ B2IPoint(const B2IPoint& rPoint)
+ : B2ITuple(rPoint)
+ {}
+
+ /** constructor with tuple to allow copy-constructing
+ from B2ITuple-based classes
+ */
+ B2IPoint(const ::basegfx::B2ITuple& rTuple)
+ : B2ITuple(rTuple)
+ {}
+
+ ~B2IPoint()
+ {}
+
+ /** *=operator to allow usage from B2IPoint, too
+ */
+ B2IPoint& operator*=( const B2IPoint& rPnt )
+ {
+ mnX *= rPnt.mnX;
+ mnY *= rPnt.mnY;
+ return *this;
+ }
+
+ /** *=operator to allow usage from B2IPoint, too
+ */
+ B2IPoint& operator*=(sal_Int32 t)
+ {
+ mnX *= t;
+ mnY *= t;
+ return *this;
+ }
+
+ /** assignment operator to allow assigning the results
+ of B2ITuple calculations
+ */
+ B2IPoint& operator=( const ::basegfx::B2ITuple& rPoint );
+
+ /** Transform point by given transformation matrix.
+
+ The translational components of the matrix are, in
+ contrast to B2DVector, applied.
+ */
+ B2IPoint& operator*=( const ::basegfx::B2DHomMatrix& rMat );
+
+ static const B2IPoint& getEmptyPoint()
+ {
+ return (const B2IPoint&) ::basegfx::B2ITuple::getEmptyTuple();
+ }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_POINT_B2IPOINT_HXX */
diff --git a/basegfx/inc/basegfx/point/b3dhompoint.hxx b/basegfx/inc/basegfx/point/b3dhompoint.hxx
new file mode 100644
index 000000000000..bbe684144641
--- /dev/null
+++ b/basegfx/inc/basegfx/point/b3dhompoint.hxx
@@ -0,0 +1,408 @@
+/*************************************************************************
+ *
+ * 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: b3dhompoint.hxx,v $
+ * $Revision: 1.11 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POINT_B3DHOMPOINT_HXX
+#define _BGFX_POINT_B3DHOMPOINT_HXX
+
+#include <basegfx/point/b3dpoint.hxx>
+
+namespace basegfx
+{
+ /** Basic homogen Point class with three double values and one homogen factor
+
+ This class provides access to homogen coordinates in 3D.
+ For this purpose all the operators which need to do specific
+ action due to their homogenity are implemented here.
+ The only caveat are member methods which are declared as const
+ but do change the content. These are documented for that reason.
+ The class is designed to provide homogenous coordinates without
+ direct access to the homogen part (mfW). This is also the reason
+ for leaving out the [] operators which return references to members.
+
+ @see B3DTuple
+ */
+ class B3DHomPoint
+ {
+ protected:
+ /// This member contains the coordinate part of the point
+ ::basegfx::B3DTuple maTuple;
+
+ /// This Member holds the homogenous part of the point
+ double mfW;
+
+ /** Test if this homogen point does have a homogenous part
+
+ @return Returns true if this point has no homogenous part
+ */
+ bool implIsHomogenized() const
+ {
+ const double fOne(1.0);
+ return ::basegfx::fTools::equal(mfW, fOne);
+ }
+
+ /** Remove homogenous part of this Point
+
+ This method does necessary calculations to remove
+ the evtl. homogenous part of this Point. This may
+ change all members.
+ */
+ void implHomogenize();
+
+ /** Test and on demand remove homogenous part
+
+ This method tests if this Point does have a homogenous part
+ and then evtl. takes actions to remove that part.
+
+ @attention Even when this method is const it may change all
+ members of this instance. This is due to the fact that changing
+ the homogenous part of a homogenous point does from a mathematical
+ point of view not change the point at all.
+ */
+ void implTestAndHomogenize() const
+ {
+ if(!implIsHomogenized())
+ ((B3DHomPoint*)this)->implHomogenize();
+ }
+
+ public:
+ /** Create a homogen point
+
+ The point is initialized to (0.0, 0.0, 0.0)
+ */
+ B3DHomPoint()
+ : maTuple(),
+ mfW(1.0)
+ {}
+
+ /** Create a homogen point
+
+ @param fX
+ This parameter is used to initialize the X-coordinate
+ of the Point. The homogenous part is initialized to 1.0.
+
+ @param fY
+ This parameter is used to initialize the Y-coordinate
+ of the Point. The homogenous part is initialized to 1.0.
+
+ @param fZ
+ This parameter is used to initialize the Z-coordinate
+ of the Point. The homogenous part is initialized to 1.0.
+ */
+ B3DHomPoint(double fX, double fY, double fZ)
+ : maTuple(fX, fY, fZ),
+ mfW(1.0)
+ {}
+
+ /** Create a copy of a 3D Point
+
+ @param rVec
+ The 3D point which will be copied. The homogenous part
+ is initialized to 1.0.
+ */
+ B3DHomPoint(const B3DPoint& rVec)
+ : maTuple(rVec),
+ mfW(1.0)
+ {}
+
+ /** Create a copy of a homogen point
+
+ @param rVec
+ The homogen point which will be copied. The homogenous part
+ is copied, too.
+ */
+ B3DHomPoint(const B3DHomPoint& rVec)
+ : maTuple(rVec.maTuple.getX(), rVec.maTuple.getY(), rVec.maTuple.getZ()),
+ mfW(rVec.mfW)
+ {}
+
+ ~B3DHomPoint()
+ {}
+
+ /** get a 3D point from this homogenous point
+
+ This method normalizes this homogen point if necessary and
+ returns the corresponding 3D point for this homogen point.
+
+ @attention Even when this method is const it may change all
+ members of this instance.
+ */
+ B3DPoint getB3DPoint() const
+ {
+ implTestAndHomogenize();
+ return B3DPoint(maTuple.getX(), maTuple.getY(), maTuple.getZ());
+ }
+
+ /** get X-coordinate
+
+ This method normalizes this homogen point if necessary and
+ returns the corresponding X-coordinate for this homogen point.
+
+ @attention Even when this method is const it may change all
+ members of this instance.
+ */
+ double getX() const
+ {
+ implTestAndHomogenize();
+ return maTuple.getX();
+ }
+
+ /** get Y-coordinate
+
+ This method normalizes this homogen point if necessary and
+ returns the corresponding Y-coordinate for this homogen point.
+
+ @attention Even when this method is const it may change all
+ members of this instance.
+ */
+ double getY() const
+ {
+ implTestAndHomogenize();
+ return maTuple.getY();
+ }
+
+ /** get Z-coordinate
+
+ This method normalizes this homogen point if necessary and
+ returns the corresponding Z-coordinate for this homogen point.
+
+ @attention Even when this method is const it may change all
+ members of this instance.
+ */
+ double getZ() const
+ {
+ implTestAndHomogenize();
+ return maTuple.getY();
+ }
+
+ /** Set X-coordinate of the homogen point.
+
+ This method sets the X-coordinate of the homogen point. If
+ the point does have a homogenous part this is taken into account.
+
+ @param fX
+ The to-be-set X-coordinate without homogenous part.
+ */
+ void setX(double fX)
+ {
+ maTuple.setX(implIsHomogenized() ? fX : fX * mfW );
+ }
+
+ /** Set Y-coordinate of the homogen point.
+
+ This method sets the Y-coordinate of the homogen point. If
+ the point does have a homogenous part this is taken into account.
+
+ @param fY
+ The to-be-set Y-coordinate without homogenous part.
+ */
+ void setY(double fY)
+ {
+ maTuple.setY(implIsHomogenized() ? fY : fY * mfW );
+ }
+
+ /** Set Z-coordinate of the homogen point.
+
+ This method sets the Z-coordinate of the homogen point. If
+ the point does have a homogenous part this is taken into account.
+
+ @param fZ
+ The to-be-set Z-coordinate without homogenous part.
+ */
+ void setZ(double fZ)
+ {
+ maTuple.setZ(implIsHomogenized() ? fZ : fZ * mfW );
+ }
+
+ // operators
+ //////////////////////////////////////////////////////////////////////
+
+ B3DHomPoint& operator+=( const B3DHomPoint& rPnt )
+ {
+ maTuple.setX(getX() * rPnt.mfW + rPnt.getX() * mfW);
+ maTuple.setY(getY() * rPnt.mfW + rPnt.getY() * mfW);
+ maTuple.setZ(getZ() * rPnt.mfW + rPnt.getZ() * mfW);
+ mfW = mfW * rPnt.mfW;
+
+ return *this;
+ }
+
+ B3DHomPoint& operator-=( const B3DHomPoint& rPnt )
+ {
+ maTuple.setX(getX() * rPnt.mfW - rPnt.getX() * mfW);
+ maTuple.setY(getY() * rPnt.mfW - rPnt.getY() * mfW);
+ maTuple.setZ(getZ() * rPnt.mfW - rPnt.getZ() * mfW);
+ mfW = mfW * rPnt.mfW;
+
+ return *this;
+ }
+
+ B3DHomPoint& operator*=(double t)
+ {
+ if(!::basegfx::fTools::equalZero(t))
+ {
+ mfW /= t;
+ }
+
+ return *this;
+ }
+
+ B3DHomPoint& operator/=(double t)
+ {
+ mfW *= t;
+ return *this;
+ }
+
+ B3DHomPoint& operator-(void)
+ {
+ mfW = -mfW;
+ return *this;
+ }
+
+ bool operator==( const B3DHomPoint& rPnt ) const
+ {
+ implTestAndHomogenize();
+ return (maTuple == rPnt.maTuple);
+ }
+
+ bool operator!=( const B3DHomPoint& rPnt ) const
+ {
+ implTestAndHomogenize();
+ return (maTuple != rPnt.maTuple);
+ }
+
+ B3DHomPoint& operator=( const B3DHomPoint& rPnt )
+ {
+ maTuple = rPnt.maTuple;
+ mfW = rPnt.mfW;
+ return *this;
+ }
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ inline B3DHomPoint minimum(const B3DHomPoint& rVecA, const B3DHomPoint& rVecB)
+ {
+ B3DHomPoint aMin(
+ (rVecB.getX() < rVecA.getX()) ? rVecB.getX() : rVecA.getX(),
+ (rVecB.getY() < rVecA.getY()) ? rVecB.getY() : rVecA.getY(),
+ (rVecB.getZ() < rVecA.getZ()) ? rVecB.getZ() : rVecA.getZ());
+ return aMin;
+ }
+
+ inline B3DHomPoint maximum(const B3DHomPoint& rVecA, const B3DHomPoint& rVecB)
+ {
+ B3DHomPoint aMax(
+ (rVecB.getX() > rVecA.getX()) ? rVecB.getX() : rVecA.getX(),
+ (rVecB.getY() > rVecA.getY()) ? rVecB.getY() : rVecA.getY(),
+ (rVecB.getZ() > rVecA.getZ()) ? rVecB.getZ() : rVecA.getZ());
+ return aMax;
+ }
+
+ inline B3DHomPoint absolute(const B3DHomPoint& rVec)
+ {
+ B3DHomPoint aAbs(
+ (0.0 > rVec.getX()) ? -rVec.getX() : rVec.getX(),
+ (0.0 > rVec.getY()) ? -rVec.getY() : rVec.getY(),
+ (0.0 > rVec.getZ()) ? -rVec.getZ() : rVec.getZ());
+ return aAbs;
+ }
+
+ inline B3DHomPoint interpolate(B3DHomPoint& rOld1, B3DHomPoint& rOld2, double t)
+ {
+ B3DHomPoint aInt(
+ ((rOld2.getX() - rOld1.getX()) * t) + rOld1.getX(),
+ ((rOld2.getY() - rOld1.getY()) * t) + rOld1.getY(),
+ ((rOld2.getZ() - rOld1.getZ()) * t) + rOld1.getZ());
+ return aInt;
+ }
+
+ inline B3DHomPoint average(B3DHomPoint& rOld1, B3DHomPoint& rOld2)
+ {
+ B3DHomPoint aAvg(
+ (rOld1.getX() + rOld2.getX()) * 0.5,
+ (rOld1.getY() + rOld2.getY()) * 0.5,
+ (rOld1.getZ() + rOld2.getZ()) * 0.5);
+ return aAvg;
+ }
+
+ inline B3DHomPoint average(B3DHomPoint& rOld1, B3DHomPoint& rOld2, B3DHomPoint& rOld3)
+ {
+ B3DHomPoint aAvg(
+ (rOld1.getX() + rOld2.getX() + rOld3.getX()) * (1.0 / 3.0),
+ (rOld1.getY() + rOld2.getY() + rOld3.getY()) * (1.0 / 3.0),
+ (rOld1.getZ() + rOld2.getZ() + rOld3.getZ()) * (1.0 / 3.0));
+ return aAvg;
+ }
+
+ inline B3DHomPoint operator+(const B3DHomPoint& rVecA, const B3DHomPoint& rVecB)
+ {
+ B3DHomPoint aSum(rVecA);
+ aSum += rVecB;
+ return aSum;
+ }
+
+ inline B3DHomPoint operator-(const B3DHomPoint& rVecA, const B3DHomPoint& rVecB)
+ {
+ B3DHomPoint aSub(rVecA);
+ aSub -= rVecB;
+ return aSub;
+ }
+
+ inline B3DHomPoint operator*(const B3DHomPoint& rVec, double t)
+ {
+ B3DHomPoint aNew(rVec);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B3DHomPoint operator*(double t, const B3DHomPoint& rVec)
+ {
+ B3DHomPoint aNew(rVec);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B3DHomPoint operator/(const B3DHomPoint& rVec, double t)
+ {
+ B3DHomPoint aNew(rVec);
+ aNew /= t;
+ return aNew;
+ }
+
+ inline B3DHomPoint operator/(double t, const B3DHomPoint& rVec)
+ {
+ B3DHomPoint aNew(rVec);
+ aNew /= t;
+ return aNew;
+ }
+} // end of namespace basegfx
+
+#endif /* _BGFX_POINT_B3DHOMPOINT_HXX */
diff --git a/basegfx/inc/basegfx/point/b3dpoint.hxx b/basegfx/inc/basegfx/point/b3dpoint.hxx
new file mode 100644
index 000000000000..15a78986f636
--- /dev/null
+++ b/basegfx/inc/basegfx/point/b3dpoint.hxx
@@ -0,0 +1,153 @@
+/*************************************************************************
+ *
+ * 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: b3dpoint.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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POINT_B3DPOINT_HXX
+#define _BGFX_POINT_B3DPOINT_HXX
+
+#include <basegfx/tuple/b3dtuple.hxx>
+
+namespace basegfx
+{
+ // predeclaration
+ class B3DHomMatrix;
+
+ /** Base Point class with three double values
+
+ This class derives all operators and common handling for
+ a 3D data class from B3DTuple. All necessary extensions
+ which are special for points will be added here.
+
+ @see B3DTuple
+ */
+ class B3DPoint : public ::basegfx::B3DTuple
+ {
+ public:
+ /** Create a 3D Point
+
+ The point is initialized to (0.0, 0.0, 0.0)
+ */
+ B3DPoint()
+ : B3DTuple()
+ {}
+
+ /** Create a 3D Point
+
+ @param fX
+ This parameter is used to initialize the X-coordinate
+ of the 3D Point.
+
+ @param fY
+ This parameter is used to initialize the Y-coordinate
+ of the 3D Point.
+
+ @param fZ
+ This parameter is used to initialize the Z-coordinate
+ of the 3D Point.
+ */
+ B3DPoint(double fX, double fY, double fZ)
+ : B3DTuple(fX, fY, fZ)
+ {}
+
+ /** Create a copy of a 3D Point
+
+ @param rVec
+ The 3D Point which will be copied.
+ */
+ B3DPoint(const B3DPoint& rVec)
+ : B3DTuple(rVec)
+ {}
+
+ /** constructor with tuple to allow copy-constructing
+ from B3DTuple-based classes
+ */
+ B3DPoint(const ::basegfx::B3DTuple& rTuple)
+ : B3DTuple(rTuple)
+ {}
+
+ ~B3DPoint()
+ {}
+
+ /** *=operator to allow usage from B3DPoint, too
+ */
+ B3DPoint& operator*=( const B3DPoint& rPnt )
+ {
+ mfX *= rPnt.mfX;
+ mfY *= rPnt.mfY;
+ mfZ *= rPnt.mfZ;
+ return *this;
+ }
+
+ /** *=operator to allow usage from B3DPoint, too
+ */
+ B3DPoint& operator*=(double t)
+ {
+ mfX *= t;
+ mfY *= t;
+ mfZ *= t;
+ return *this;
+ }
+
+ /** assignment operator to allow assigning the results
+ of B3DTuple calculations
+ */
+ B3DPoint& operator=( const ::basegfx::B3DTuple& rVec )
+ {
+ mfX = rVec.getX();
+ mfY = rVec.getY();
+ mfZ = rVec.getZ();
+ return *this;
+ }
+
+ /** Transform point by given transformation matrix.
+
+ The translational components of the matrix are, in
+ contrast to B3DVector, applied.
+ */
+ B3DPoint& operator*=( const ::basegfx::B3DHomMatrix& rMat );
+
+ static const B3DPoint& getEmptyPoint()
+ {
+ return (const B3DPoint&) ::basegfx::B3DTuple::getEmptyTuple();
+ }
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ /** Transform B3DPoint by given transformation matrix.
+
+ Since this is a Point, translational components of the
+ matrix are used.
+ */
+ B3DPoint operator*( const B3DHomMatrix& rMat, const B3DPoint& rPoint );
+
+} // end of namespace basegfx
+
+#endif /* _BGFX_POINT_B3DPOINT_HXX */
diff --git a/basegfx/inc/basegfx/point/b3ipoint.hxx b/basegfx/inc/basegfx/point/b3ipoint.hxx
new file mode 100644
index 000000000000..4d40e0becb37
--- /dev/null
+++ b/basegfx/inc/basegfx/point/b3ipoint.hxx
@@ -0,0 +1,142 @@
+/*************************************************************************
+ *
+ * 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: b3ipoint.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POINT_B3IPOINT_HXX
+#define _BGFX_POINT_B3IPOINT_HXX
+
+#include <basegfx/tuple/b3ituple.hxx>
+
+namespace basegfx
+{
+ // predeclaration
+ class B3DHomMatrix;
+
+ /** Base Point class with three sal_Int32 values
+
+ This class derives all operators and common handling for
+ a 3D data class from B3ITuple. All necessary extensions
+ which are special for points will be added here.
+
+ @see B3ITuple
+ */
+ class B3IPoint : public ::basegfx::B3ITuple
+ {
+ public:
+ /** Create a 3D Point
+
+ The point is initialized to (0, 0, 0)
+ */
+ B3IPoint()
+ : B3ITuple()
+ {}
+
+ /** Create a 3D Point
+
+ @param nX
+ This parameter is used to initialize the X-coordinate
+ of the 3D Point.
+
+ @param nY
+ This parameter is used to initialize the Y-coordinate
+ of the 3D Point.
+
+ @param nZ
+ This parameter is used to initialize the Z-coordinate
+ of the 3D Point.
+ */
+ B3IPoint(sal_Int32 nX, sal_Int32 nY, sal_Int32 nZ)
+ : B3ITuple(nX, nY, nZ)
+ {}
+
+ /** Create a copy of a 3D Point
+
+ @param rVec
+ The 3D Point which will be copied.
+ */
+ B3IPoint(const B3IPoint& rVec)
+ : B3ITuple(rVec)
+ {}
+
+ /** constructor with tuple to allow copy-constructing
+ from B3ITuple-based classes
+ */
+ B3IPoint(const ::basegfx::B3ITuple& rTuple)
+ : B3ITuple(rTuple)
+ {}
+
+ ~B3IPoint()
+ {}
+
+ /** *=operator to allow usage from B3IPoint, too
+ */
+ B3IPoint& operator*=( const B3IPoint& rPnt )
+ {
+ mnX *= rPnt.mnX;
+ mnY *= rPnt.mnY;
+ mnZ *= rPnt.mnZ;
+ return *this;
+ }
+
+ /** *=operator to allow usage from B3IPoint, too
+ */
+ B3IPoint& operator*=(sal_Int32 t)
+ {
+ mnX *= t;
+ mnY *= t;
+ mnZ *= t;
+ return *this;
+ }
+
+ /** assignment operator to allow assigning the results
+ of B3ITuple calculations
+ */
+ B3IPoint& operator=( const ::basegfx::B3ITuple& rVec )
+ {
+ mnX = rVec.getX();
+ mnY = rVec.getY();
+ mnZ = rVec.getZ();
+ return *this;
+ }
+
+ /** Transform point by given transformation matrix.
+
+ The translational components of the matrix are, in
+ contrast to B3DVector, applied.
+ */
+ B3IPoint& operator*=( const ::basegfx::B3DHomMatrix& rMat );
+
+ static const B3IPoint& getEmptyPoint()
+ {
+ return (const B3IPoint&) ::basegfx::B3ITuple::getEmptyTuple();
+ }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_POINT_B3IPOINT_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b2dlinegeometry.hxx b/basegfx/inc/basegfx/polygon/b2dlinegeometry.hxx
new file mode 100644
index 000000000000..d95f0687dbf8
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dlinegeometry.hxx
@@ -0,0 +1,147 @@
+/*************************************************************************
+ *
+ * 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: b2dlinegeometry.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B2DLINEGEOMETRY_HXX
+#define _BGFX_POLYGON_B2DLINEGEOMETRY_HXX
+
+#include <sal/types.h>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ /** Create line start/end geometry element, mostly arrows and things like that.
+
+ @param rCandidate
+ The polygon which needs to get that line ends and needs to have two points
+ at least.
+
+ @param rArrow
+ The line start/end geometry. It is assumed that the tip is pointing
+ upwards. Result will be rotated and scaled to fit.
+
+ @param bStart
+ describes if creation is for start or end of candidate.
+
+ @param fWidth
+ defines the size of the element, it's describing the target width in X
+ of the arrow.
+
+ @param fDockingPosition needs to be in [0.0 ..1.0] range, where 0.0 means
+ that the tip of the arrow will be aligned with the polygon start, 1.0 means
+ the bottom. The default of 0.5 describes a centered arrow.
+
+ @param pConsumedLength
+ Using this parameter it is possible to get back how much from the candidate
+ geometry is overlapped by the created element (consumed).
+
+ @param fCandidateLength
+ This should contain the length of rCandidate to allow work without
+ again calculating the length (which may be expensive with beziers). If 0.0 is
+ given, the length is calculated on demand.
+
+ @return
+ The Line start and end polygon, correctly rotated and scaled
+ */
+ B2DPolyPolygon createAreaGeometryForLineStartEnd(
+ const B2DPolygon& rCandidate,
+ const B2DPolyPolygon& rArrow,
+ bool bStart,
+ double fWidth,
+ double fCandidateLength = 0.0, // 0.0 -> calculate self
+ double fDockingPosition = 0.5, // 0->top, 1->bottom
+ double* pConsumedLength = 0L);
+
+ /** create filled polygon geometry for lines with a line width
+
+ This method will create bezier based, fillable polygons which
+ will resample the curve if it was extended for the given half
+ line width. It will remove extrema positions from contained
+ bezier segments and get as close as possible and defined by
+ the given parameters to the ideal result.
+
+ It will check edges for trivial bezier to avoid unnecessary
+ bezier polygons. Care is taken to produce the in-between
+ polygon points (the ones original on the source poygon) since
+ it has showed that without those, the raster converters leave
+ non-filled gaps.
+
+ @param rCandidate
+ The source polygon defining the hairline polygon path
+
+ @param fHalfLineWidth
+ The width of the line to one side
+
+ @param eJoin
+ The LineJoin if the edges meeting in a point do not have a C1
+ or C2 continuity
+
+ @param fMaxAllowedAngle
+ Allows to hand over the maximum allowed angle between an edge and
+ it's control vectors. The smaller, the more subdivisions will be
+ needed to create the filled geometry. Allowed range is cropped to
+ [F_PI2 .. 0.01 * F_PI2].
+
+ @param fMaxPartOfEdge
+ Allows to influence from with relative length of a control vector
+ compared to it's edge a split is forced. The smaller, the more
+ subdivisions will be needed to create the filled geometry. Allowed
+ range is cropped to [1.0 .. 0.01]
+
+ @praram fMiterMinimumAngle
+ The minimum wanted angle between two edges when edge rounding
+ is using miter. When an edge is smaller than this (tighter)
+ the usual fallback to bevel is used. Allowed range is cropped
+ to [F_PI .. 0.01 * F_PI].
+
+ @return
+ The PolyPolygon containing the geometry of the extended line by
+ it's line width. Contains bezier segments and edge roundings as
+ needed and defined.
+ */
+ B2DPolyPolygon createAreaGeometry(
+ const B2DPolygon& rCandidate,
+ double fHalfLineWidth,
+ B2DLineJoin eJoin = B2DLINEJOIN_ROUND,
+ double fMaxAllowedAngle = (12.5 * F_PI180),
+ double fMaxPartOfEdge = 0.4,
+ double fMiterMinimumAngle = (15.0 * F_PI180));
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif /* _BGFX_POLYGON_B2DLINEGEOMETRY_HXX */
+// eof
diff --git a/basegfx/inc/basegfx/polygon/b2dpolygon.hxx b/basegfx/inc/basegfx/polygon/b2dpolygon.hxx
new file mode 100644
index 000000000000..c0de4b57ced8
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dpolygon.hxx
@@ -0,0 +1,270 @@
+/*************************************************************************
+ *
+ * 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: b2dpolygon.hxx,v $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B2DPOLYGON_HXX
+#define _BGFX_POLYGON_B2DPOLYGON_HXX
+
+#include <sal/types.h>
+#include <o3tl/cow_wrapper.hxx>
+#include <basegfx/vector/b2enums.hxx>
+#include <basegfx/range/b2drange.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+// predeclarations
+class ImplB2DPolygon;
+
+namespace basegfx
+{
+ class B2DPolygon;
+ class B2DPoint;
+ class B2DVector;
+ class B2DHomMatrix;
+ class B2DCubicBezier;
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class B2DPolygon
+ {
+ public:
+ typedef o3tl::cow_wrapper< ImplB2DPolygon > ImplType;
+
+ private:
+ // internal data.
+ ImplType mpPolygon;
+
+ public:
+ /// diverse constructors
+ B2DPolygon();
+ B2DPolygon(const B2DPolygon& rPolygon);
+ B2DPolygon(const B2DPolygon& rPolygon, sal_uInt32 nIndex, sal_uInt32 nCount);
+ ~B2DPolygon();
+
+ /// assignment operator
+ B2DPolygon& operator=(const B2DPolygon& rPolygon);
+
+ /// unshare this polygon with all internally shared instances
+ void makeUnique();
+
+ /// compare operators
+ bool operator==(const B2DPolygon& rPolygon) const;
+ bool operator!=(const B2DPolygon& rPolygon) const;
+
+ /// member count
+ sal_uInt32 count() const;
+
+ /// Coordinate interface
+ basegfx::B2DPoint getB2DPoint(sal_uInt32 nIndex) const;
+ void setB2DPoint(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue);
+
+ /// Coordinate insert/append
+ void insert(sal_uInt32 nIndex, const basegfx::B2DPoint& rPoint, sal_uInt32 nCount = 1);
+ void append(const basegfx::B2DPoint& rPoint, sal_uInt32 nCount);
+ void append(const basegfx::B2DPoint& rPoint);
+ void reserve(sal_uInt32 nCount);
+
+ /// Basic ControlPoint interface
+ basegfx::B2DPoint getPrevControlPoint(sal_uInt32 nIndex) const;
+ basegfx::B2DPoint getNextControlPoint(sal_uInt32 nIndex) const;
+ void setPrevControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue);
+ void setNextControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue);
+ void setControlPoints(sal_uInt32 nIndex, const basegfx::B2DPoint& rPrev, const basegfx::B2DPoint& rNext);
+
+ /// ControlPoint resets
+ void resetPrevControlPoint(sal_uInt32 nIndex);
+ void resetNextControlPoint(sal_uInt32 nIndex);
+ void resetControlPoints(sal_uInt32 nIndex);
+ void resetControlPoints();
+
+ /// Bezier segment append with control points. The current last polygon point is implicitly taken as start point.
+ void appendBezierSegment(const basegfx::B2DPoint& rNextControlPoint, const basegfx::B2DPoint& rPrevControlPoint, const basegfx::B2DPoint& rPoint);
+
+ /// ControlPoint checks
+ bool areControlPointsUsed() const;
+ bool isPrevControlPointUsed(sal_uInt32 nIndex) const;
+ bool isNextControlPointUsed(sal_uInt32 nIndex) const;
+ B2VectorContinuity getContinuityInPoint(sal_uInt32 nIndex) const;
+
+ /** check edge for being a bezier segment
+
+ This test the existance of control vectors, but do not apply
+ testAndSolveTrivialBezier() to the bezier segment, so it is still useful
+ to do so.
+ Since it can use internal data representations, it is faster
+ than using getBezierSegment() and applying isBezier() on it.
+
+ @param nIndex
+ Index of the addressed edge's start point
+
+ @return
+ true if edge exists and at least one control vector is used
+ */
+ bool isBezierSegment(sal_uInt32 nIndex) const;
+
+ /** bezier segment access
+
+ This method also works when it is no bezier segment at all and will fill
+ the given B2DCubicBezier as needed.
+ In any case, the given B2DCubicBezier will be filled, if necessary with
+ the single start point (if no valid edge exists).
+
+ @param nIndex
+ Index of the addressed edge's start point
+
+ @param rTarget
+ The B2DCubicBezier to be filled. It's data WILL be changed.
+ */
+ void getBezierSegment(sal_uInt32 nIndex, B2DCubicBezier& rTarget) const;
+
+ /** Default adaptive subdivision access
+
+ This method will return a default adapive subdivision of the polygon.
+ If the polygon does not contain any bezier curve segments, it will
+ just return itself.
+
+ The subdivision is created on first request and buffered, so when using
+ this subdivision You have the guarantee for fast accesses for multiple
+ usages. It is intended for tooling usage for tasks which would be hard
+ to accomplish on bezier segments (e.g. isInEpsilonRange).
+
+ The current default subdivision uses adaptiveSubdivideByCount with 9
+ subdivisions which gives 10 edges and 11 points per segment and is
+ usually pretty usable for processing purposes. There is no parameter
+ passing here ATM but it may be changed on demand. If needed, a TYPE
+ and PARAMETER (both defaulted) may be added to allow for switching
+ between the different kinds of subdivisiond and passing them one
+ parameter.
+
+ The lifetime of the buffered subdivision is based on polygon changes.
+ When changing the polygon, it will be flushed. It is buffered at the
+ refcounted implementation class, so it will survive copy by value and
+ combinations in PolyPolygons.
+
+ @return
+ The default (and buffered) subdivision of this polygon. It may
+ be this polygon itself when it has no bezier segments. It is guaranteed
+ to have no more bezier segments
+ */
+ B2DPolygon getDefaultAdaptiveSubdivision() const;
+
+ /** Get the B2DRange (Rectangle dimensions) of this B2DPolygon
+
+ A polygon may have up to three ranges:
+
+ (a) the range of the polygon points
+ (b) the range of the polygon points and control points
+ (c) the outer range of the subdivided bezier curve
+
+ Ranges (a) and (c) are produced by tools::getRange(); resp. this
+ getB2DRange(). tools::getRangeWithControlPoints handles case (b).
+
+ To get range (c) a simple solution would be to subdivide the polygon
+ and use getRange() on it. Since subdivision is expensive and decreases
+ the polygon quality, i added this new method. It will use a
+ methodology suggested by HDU. First, it gets the range (a).
+ Then it iterates over the bezier segments and for each it
+ first tests if the outer range of the bezier segment is already
+ contained in the result range.
+
+ The subdivision itself uses getAllExtremumPositions() to only
+ calculate extremum points and to expand the result accordingly.
+ Thus it calculates maximal four extremum points on the bezier
+ segment, no split is used at all.
+
+ @return
+ The outer range of the bezier curve/polygon
+ */
+ B2DRange getB2DRange() const;
+
+ /** insert other 2D polygons
+
+ The default (with nIndex2 == 0 && nCount == 0) inserts the whole
+ rPoly at position nIndex
+
+ @param nIndex
+ Target index for points to be inserted
+
+ @param rPoly
+ The source for new points
+
+ @param nIndex2
+ The index to the first source point into rPoly
+
+ @param nCount
+ How many points to add from rPoly to this polygon. Null
+ means to copy all (starting from nIndex2)
+ */
+ void insert(sal_uInt32 nIndex, const B2DPolygon& rPoly, sal_uInt32 nIndex2 = 0, sal_uInt32 nCount = 0);
+
+ /** append other 2D polygons
+
+ The default (nIndex ==0 && nCount == 0) will append
+ the whole rPoly
+
+ @param rPoly
+ The source polygon
+
+ @param nIndex
+ The index to the first point of rPoly to append
+
+ @param nCount
+ The number of points to append from rPoly, starting
+ from nIndex. If zero, as much as possibel is appended
+ */
+ void append(const B2DPolygon& rPoly, sal_uInt32 nIndex = 0, sal_uInt32 nCount = 0);
+
+ /// remove points
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount = 1);
+
+ /// clear all points
+ void clear();
+
+ /// closed state interface
+ bool isClosed() const;
+ void setClosed(bool bNew);
+
+ /// flip polygon direction
+ void flip();
+
+ /// test if Polygon has double points
+ bool hasDoublePoints() const;
+
+ /// remove double points, at the begin/end and follow-ups, too
+ void removeDoublePoints();
+
+ /// apply transformation given in matrix form
+ void transform(const basegfx::B2DHomMatrix& rMatrix);
+ };
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif /* _BGFX_POLYGON_B2DPOLYGON_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b2dpolygonclipper.hxx b/basegfx/inc/basegfx/polygon/b2dpolygonclipper.hxx
new file mode 100644
index 000000000000..d604c454a81f
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dpolygonclipper.hxx
@@ -0,0 +1,85 @@
+/*************************************************************************
+ *
+ * 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: b2dpolygonclipper.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYPOLYGON_B2DPOLYGONCLIPPER_HXX
+#define _BGFX_POLYPOLYGON_B2DPOLYGONCLIPPER_HXX
+
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ // predefinitions
+ class B2DRange;
+
+ namespace tools
+ {
+ // This method clips the given PolyPolygon against a horizontal or vertical axis (parallell to X or Y axis). The axis is
+ // defined by bParallelToXAxis (true -> it's parallel to the X-Axis of the coordinate system, else to the Y-Axis) and the
+ // fValueOnOtherAxis (gives the translation to the coordinate system axis). For example, when You want to define
+ // a clip axis parallel to X.Axis and 100 above it, use bParallelToXAxis = true and fValueOnOtherAxis = 100.
+ // The value bAboveAxis defines on which side the return value will be (true -> above X, right of Y).
+ // The switch bStroke decides if the polygon is interpreted as area (false) or strokes (true).
+ B2DPolyPolygon clipPolyPolygonOnParallelAxis(const B2DPolyPolygon& rCandidate, bool bParallelToXAxis, bool bAboveAxis, double fValueOnOtherAxis, bool bStroke);
+ B2DPolyPolygon clipPolygonOnParallelAxis(const B2DPolygon& rCandidate, bool bParallelToXAxis, bool bAboveAxis, double fValueOnOtherAxis, bool bStroke);
+
+ // Clip the given PolyPolygon against the given range. bInside defines if the result will contain the
+ // parts which are contained in the range or vice versa.
+ // The switch bStroke decides if the polygon is interpreted as area (false) or strokes (true).
+ B2DPolyPolygon clipPolyPolygonOnRange(const B2DPolyPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke);
+ B2DPolyPolygon clipPolygonOnRange(const B2DPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke);
+
+ // Clip given PolyPolygon against the endless edge (ray) defined by the given two points. bAbove defines on which side
+ // of the edge the result will be together with the definition of the edge. If the edge is seen as a vector
+ // from A to B and bAbove is true, the result will contain the geometry left of the vector.
+ // The switch bStroke decides if the polygon is interpreted as area (false) or strokes (true).
+ B2DPolyPolygon clipPolyPolygonOnEdge(const B2DPolyPolygon& rCandidate, const B2DPoint& rPointA, const B2DPoint& rPointB, bool bAbove, bool bStroke);
+ B2DPolyPolygon clipPolygonOnEdge(const B2DPolygon& rCandidate, const B2DPoint& rPointA, const B2DPoint& rPointB, bool bAbove, bool bStroke);
+
+ // Clip given PolyPolygon against given clipping polygon.
+ // The switch bStroke decides if the polygon is interpreted as area (false) or strokes (true).
+ // With stroke polygons, You get all line snippets inside rCip.
+ // With filled polygons, You get all PolyPolygon parts which were inside rClip.
+ // The switch bInside decides if the parts inside the clip polygon or outside shall be created.
+ // The clip polygon is always assumed closed, even when it's isClosed() is false.
+ B2DPolyPolygon clipPolyPolygonOnPolyPolygon(const B2DPolyPolygon& rCandidate, const B2DPolyPolygon& rClip, bool bInside, bool bStroke);
+ B2DPolyPolygon clipPolygonOnPolyPolygon(const B2DPolygon& rCandidate, const B2DPolyPolygon& rClip, bool bInside, bool bStroke);
+
+ // clip the given polygon against the given range. the resulting polygon will always contain
+ // the inside parts which will always be interpreted as areas. the incoming polygon is expected
+ // to be a simple triangle list. the result is also a simple triangle list.
+ B2DPolygon clipTriangleListOnRange( const B2DPolygon& rCandidate, const B2DRange& rRange );
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+#endif /* _BGFX_POLYPOLYGON_B2DPOLYGONCLIPPER_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b2dpolygoncutandtouch.hxx b/basegfx/inc/basegfx/polygon/b2dpolygoncutandtouch.hxx
new file mode 100644
index 000000000000..aa4682a665cd
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dpolygoncutandtouch.hxx
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *
+ * 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: b2dpolygoncutandtouch.hxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_CUTANDTOUCH_HXX
+#define _BGFX_POLYGON_CUTANDTOUCH_HXX
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ // look for self-intersections and self-touches (points on an edge) in given polygon and add
+ // extra points there. Result will have no touches or intersections on an edge, only on points
+ B2DPolygon addPointsAtCutsAndTouches(const B2DPolygon& rCandidate);
+
+ // look for polypolygon-intersections and polypolygon-touches (point of poly A on an edge of poly B) in given PolyPolygon and add
+ // extra points there. Result will have no touches or intersections between contained polygons on an edge, only on points. For
+ // convenience, the correction for self-intersections for each member polygon will be used, too.
+ // Changed: Self intersections are searched by default, but may be switched off by 2nd parameter.
+ B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rCandidate, bool bSelfIntersections = true);
+
+ // look for intersections of rCandidate with all polygons from rMask and add extra points there. Do
+ // not change or add points to rMask.
+ B2DPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rMask, const B2DPolygon& rCandidate);
+
+ // look for intersections of rCandidate with all polygons from rMask and add extra points there. Do
+ // not change or add points to rMask.
+ B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rMask, const B2DPolyPolygon& rCandidate);
+
+ // look for intersections of rCandidate with the edge from rStart to rEnd and add extra points there.
+ // Points are only added in the range of the edge, not on the endless vector.
+ B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd);
+ B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd);
+
+ // look for intersections of rCandidate with the mask Polygon and add extra points there.
+ // The mask polygon is assumed to be closed, even when it's not explicitely.
+ B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPolyPolygon& rMask);
+ B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, const B2DPolyPolygon& rMask);
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif /* _BGFX_POLYGON_CUTANDTOUCH_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx b/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx
new file mode 100644
index 000000000000..5eff6b0b9cc1
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx
@@ -0,0 +1,594 @@
+/*************************************************************************
+ *
+ * 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: b2dpolygontools.hxx,v $
+ * $Revision: 1.24.4.1 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B2DPOLYGONTOOLS_HXX
+#define _BGFX_POLYGON_B2DPOLYGONTOOLS_HXX
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <vector>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ // predefinitions
+ class B2DPolygon;
+ class B2DRange;
+
+ namespace tools
+ {
+ // B2DPolygon tools
+
+ // open/close with point add/remove and control point corrections
+ void openWithGeometryChange(B2DPolygon& rCandidate);
+ void closeWithGeometryChange(B2DPolygon& rCandidate);
+
+ /** Check if given polygon is closed.
+
+ This is kind of a 'classic' method to support old polygon
+ definitions. Those old polygon definitions define the
+ closed state of the polygon using identical start and
+ endpoints. This method corrects this (removes double
+ start/end points) and sets the Closed()-state of the
+ polygon correctly.
+ */
+ void checkClosed(B2DPolygon& rCandidate);
+
+ // Get successor and predecessor indices. Returning the same index means there
+ // is none. Same for successor.
+ sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate);
+ sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate);
+
+ // Get orientation of Polygon
+ B2VectorOrientation getOrientation(const B2DPolygon& rCandidate);
+
+ // isInside tests for B2dPoint and other B2dPolygon. On border is not inside as long as
+ // not true is given in bWithBorder flag.
+ bool isInside(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder = false);
+ bool isInside(const B2DPolygon& rCandidate, const B2DPolygon& rPolygon, bool bWithBorder = false);
+
+ /** Get the range of a polygon including bezier control points
+
+ For detailed discussion, see B2DPolygon::getB2DRange()
+
+ @param rCandidate
+ The B2DPolygon eventually containing bezier segments
+
+ @return
+ The outer range of the bezier curve containing bezier control points
+ */
+ B2DRange getRangeWithControlPoints(const B2DPolygon& rCandidate);
+
+ /** Get the range of a polygon
+
+ This method creates the outer range of the subdivided bezier curve.
+ For detailed discussion see B2DPolygon::getB2DRange()
+
+ @param rCandidate
+ The B2DPolygon eventually containing bezier segments
+
+ @return
+ The outer range of the bezier curve
+ */
+ B2DRange getRange(const B2DPolygon& rCandidate);
+
+ // get signed area of polygon
+ double getSignedArea(const B2DPolygon& rCandidate);
+
+ // get area of polygon
+ double getArea(const B2DPolygon& rCandidate);
+
+ /** get length of polygon edge from point nIndex to nIndex + 1 */
+ double getEdgeLength(const B2DPolygon& rCandidate, sal_uInt32 nIndex);
+
+ /** get length of polygon */
+ double getLength(const B2DPolygon& rCandidate);
+
+ // get position on polygon for absolute given distance. If
+ // length is given, it is assumed the correct polygon length, if 0.0 it is calculated
+ // using getLength(...)
+ B2DPoint getPositionAbsolute(const B2DPolygon& rCandidate, double fDistance, double fLength = 0.0);
+
+ // get position on polygon for relative given distance in range [0.0 .. 1.0]. If
+ // length is given, it is assumed the correct polygon length, if 0.0 it is calculated
+ // using getLength(...)
+ B2DPoint getPositionRelative(const B2DPolygon& rCandidate, double fDistance, double fLength = 0.0);
+
+ // get a snippet from given polygon for absolute distances. The polygon is assumed
+ // to be opened (not closed). fFrom and fTo need to be in range [0.0 .. fLength], where
+ // fTo >= fFrom. If length is given, it is assumed the correct polygon length,
+ // if 0.0 it is calculated using getLength(...)
+ B2DPolygon getSnippetAbsolute(const B2DPolygon& rCandidate, double fFrom, double fTo, double fLength = 0.0);
+
+ // get a snippet from given polygon for relative distances. The polygon is assumed
+ // to be opened (not closed). fFrom and fTo need to be in range [0.0 .. 1.0], where
+ // fTo >= fFrom. If length is given, it is assumed the correct polygon length,
+ // if 0.0 it is calculated using getLength(...)
+ B2DPolygon getSnippetRelative(const B2DPolygon& rCandidate, double fFrom = 0.0, double fTo = 1.0, double fLength = 0.0);
+
+ // Continuity check for point with given index
+ B2VectorContinuity getContinuityInPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex);
+
+ // Subdivide all contained curves. Use distanceBound value if given.
+ B2DPolygon adaptiveSubdivideByDistance(const B2DPolygon& rCandidate, double fDistanceBound = 0.0);
+
+ // Subdivide all contained curves. Use angleBound value if given.
+ B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon& rCandidate, double fAngleBound = 0.0);
+
+ // #i37443# Subdivide all contained curves.
+ B2DPolygon adaptiveSubdivideByCount(const B2DPolygon& rCandidate, sal_uInt32 nCount = 0L);
+
+ // Definitions for the cut flags used from the findCut methods
+ typedef sal_uInt16 CutFlagValue;
+
+ #define CUTFLAG_NONE (0x0000)
+ #define CUTFLAG_LINE (0x0001)
+ #define CUTFLAG_START1 (0x0002)
+ #define CUTFLAG_START2 (0x0004)
+ #define CUTFLAG_END1 (0x0008)
+ #define CUTFLAG_END2 (0x0010)
+ #define CUTFLAG_ALL (CUTFLAG_LINE|CUTFLAG_START1|CUTFLAG_START2|CUTFLAG_END1|CUTFLAG_END2)
+ #define CUTFLAG_DEFAULT (CUTFLAG_LINE|CUTFLAG_START2|CUTFLAG_END2)
+
+ // Calculate cut between the points given by the two indices. pCut1
+ // and pCut2 will contain the cut coordinate on each edge in ]0.0, 1.0]
+ // (if given) and the return value will contain a cut description.
+ CutFlagValue findCut(
+ const B2DPolygon& rCandidate,
+ sal_uInt32 nIndex1, sal_uInt32 nIndex2,
+ CutFlagValue aCutFlags = CUTFLAG_DEFAULT,
+ double* pCut1 = 0L, double* pCut2 = 0L);
+
+ // This version is working with two indexed edges from different
+ // polygons.
+ CutFlagValue findCut(
+ const B2DPolygon& rCandidate1, sal_uInt32 nIndex1,
+ const B2DPolygon& rCandidate2, sal_uInt32 nIndex2,
+ CutFlagValue aCutFlags = CUTFLAG_DEFAULT,
+ double* pCut1 = 0L, double* pCut2 = 0L);
+
+ // This version works with two points and vectors to define the
+ // edges for the cut test.
+ CutFlagValue findCut(
+ const B2DPoint& rEdge1Start, const B2DVector& rEdge1Delta,
+ const B2DPoint& rEdge2Start, const B2DVector& rEdge2Delta,
+ CutFlagValue aCutFlags = CUTFLAG_DEFAULT,
+ double* pCut1 = 0L, double* pCut2 = 0L);
+
+ // test if point is on the given edge in range ]0.0..1.0[ without
+ // the start/end points. If so, return true and put the parameter
+ // value in pCut (if provided)
+ bool isPointOnEdge(
+ const B2DPoint& rPoint,
+ const B2DPoint& rEdgeStart,
+ const B2DVector& rEdgeDelta,
+ double* pCut = 0L);
+
+ /** Apply given LineDashing to given polygon
+
+ This method is used to cut down line polygons to the needed
+ pieces when a dashing needs to be applied.
+ It is now capable of keeping contained bezier segments.
+ It is also capable of delivering line and non-line portions
+ depending on what target polygons You provide. This is useful
+ e.g. for dashed lines with two colors.
+ If the last and the first snippet in one of the results have
+ a common start/end ppoint, they will be merged to achieve as
+ view as needed result line snippets. This is also relevant for
+ further processing the results.
+
+ @param rCandidate
+ The polygon based on which the snippets will be created.
+
+ @param rDotDashArray
+ The line pattern given as array of length values
+
+ @param pLineTarget
+ The target for line snippets, e.g. the first entry will be
+ a line segment with length rDotDashArray[0]. The given
+ polygon will be emptied as preparation.
+
+ @param pGapTarget
+ The target for gap snippets, e.g. the first entry will be
+ a line segment with length rDotDashArray[1]. The given
+ polygon will be emptied as preparation.
+
+ @param fFullDashDotLen
+ The sumed-up length of the rDotDashArray. If zero, it will
+ be calculated internally.
+ */
+ void applyLineDashing(
+ const B2DPolygon& rCandidate,
+ const ::std::vector<double>& rDotDashArray,
+ B2DPolyPolygon* pLineTarget,
+ B2DPolyPolygon* pGapTarget = 0,
+ double fFullDashDotLen = 0.0);
+
+ // test if point is inside epsilon-range around an edge defined
+ // by the two given points. Can be used for HitTesting. The epsilon-range
+ // is defined to be the rectangle centered to the given edge, using height
+ // 2 x fDistance, and the circle around both points with radius fDistance.
+ bool isInEpsilonRange(const B2DPoint& rEdgeStart, const B2DPoint& rEdgeEnd, const B2DPoint& rTestPosition, double fDistance);
+
+ // test if point is inside epsilon-range around the given Polygon. Can be used
+ // for HitTesting. The epsilon-range is defined to be the rectangle centered
+ // to the given edge, using height 2 x fDistance, and the circle around both points
+ // with radius fDistance.
+ bool isInEpsilonRange(const B2DPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance);
+
+ /** Create a polygon from a rectangle.
+
+ @param rRect
+ The rectangle which describes the polygon size
+
+ @param fRadius
+ Radius of the edge rounding, relative to the rectangle size. 0.0 means no
+ rounding, 1.0 will lead to an ellipse
+ */
+ B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadius );
+
+ /** Create a polygon from a rectangle.
+
+ @param rRect
+ The rectangle which describes the polygon size
+
+ @param fRadiusX
+ @param fRadiusY
+ Radius of the edge rounding, relative to the rectangle size. 0.0 means no
+ rounding, 1.0 will lead to an ellipse
+ */
+ B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadiusX, double fRadiusY );
+
+ /** Create a polygon from a rectangle.
+ */
+ B2DPolygon createPolygonFromRect( const B2DRectangle& rRect );
+
+ /** Create a circle polygon with given radius.
+
+ This method creates a circle approximation consisting of
+ four cubic bezier segments, which approximate the given
+ circle with an error of less than 0.5 percent.
+
+ @param rCenter
+ Center point of the circle
+
+ @param fRadius
+ Radius of the circle
+ */
+ B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius );
+
+ /** append a unit circle with one point and the control vectors to the given polygon
+ */
+ void appendUnitCircleQuadrant(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, bool bEndPoint);
+
+ /** append a segment of unit circle with one point and the control vectors to the given polygon
+ */
+ void appendUnitCircleQuadrantSegment(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, double fStart, double fEnd, bool bEndPoint);
+
+ /** create a polygon which describes the unit circle and close it
+
+ @param nStartQuadrant
+ To be able to rebuild the old behaviour where the circles started at bottom,
+ this parameter is used. Default is 0 which is the first quadrant and the
+ polygon's start point will be the rightmost one. When using e.g. 1, the
+ first created quadrant will start at the YMax-position (with Y down on screens,
+ this is the lowest one). This is needed since when lines are dashed, toe old
+ geometry started at bottom point, else it would look different.
+ */
+ B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant = 0);
+
+ /** Create an ellipse polygon with given radii.
+
+ This method creates an ellipse approximation consisting of
+ four cubic bezier segments, which approximate the given
+ ellipse with an error of less than 0.5 percent.
+
+ @param rCenter
+ Center point of the circle
+
+ @param fRadiusX
+ Radius of the ellipse in X direction
+
+ @param fRadiusY
+ Radius of the ellipse in Y direction
+ */
+ B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY );
+
+ /** append a unit circle with one point and the control vectors to the given polygon
+ */
+ void appendUnitCircleQuadrant(B2DPolygon& rPolygon, sal_uInt32 nQuadrant);
+
+ /** append a segment of unit circle with start point, the control vectors and end point to the given polygon
+ */
+ void appendUnitCircleQuadrantSegment(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, double fStart, double fEnd);
+
+ /** Create an ellipse polygon with given radii.
+
+ This method creates an ellipse approximation consisting of
+ four cubic bezier segments, which approximate the given
+ ellipse with an error of less than 0.5 percent.
+
+ @param rCenter
+ Center point of the circle
+
+ @param fRadiusX
+ Radius of the ellipse in X direction
+
+ @param fRadiusY
+ Radius of the ellipse in Y direction
+
+ @param fStart
+ Start angle where the ellipe segment starts in the range [0.0 .. 2PI[
+
+ @param fEnd
+ End angle where the ellipe segment ends in the range [0.0 .. 2PI[
+ */
+ B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY );
+
+ /** Create an ellipse polygon with given radii and the given angles, from start to end
+
+ This method creates an ellipse approximation consisting of
+ four cubic bezier segments, which approximate the given
+ ellipse with an error of less than 0.5 percent.
+
+ @param rCenter
+ Center point of the circle
+
+ @param fRadiusX
+ Radius of the ellipse in X direction
+
+ @param fRadiusY
+ Radius of the ellipse in Y direction
+
+ @param fStart
+ Start angle where the ellipe segment starts in the range [0.0 .. 2PI[
+
+ @param fEnd
+ End angle where the ellipe segment ends in the range [0.0 .. 2PI[
+ */
+
+ /** Create an unit ellipse polygon with the given angles, from start to end
+ */
+ B2DPolygon createPolygonFromEllipseSegment( const B2DPoint& rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd );
+
+ B2DPolygon createPolygonFromUnitEllipseSegment( double fStart, double fEnd );
+
+ /** Predicate whether a given polygon is a rectangle.
+
+ @param rPoly
+ Polygon to check
+
+ @return true, if the polygon describes a rectangle
+ (polygon is closed, and the points are either cw or ccw
+ enumerations of a rectangle's vertices). Note that
+ intermediate points and duplicate points are ignored.
+ */
+ bool isRectangle( const B2DPolygon& rPoly );
+
+ // create 3d polygon from given 2d polygon. The given fZCoordinate is used to expand the
+ // third coordinate.
+ B3DPolygon createB3DPolygonFromB2DPolygon(const B2DPolygon& rCandidate, double fZCoordinate = 0.0);
+
+ // create 2d PolyPolygon from given 3d PolyPolygon. All coordinates are transformed using the given
+ // matrix and the resulting x,y is used to form the new polygon.
+ B2DPolygon createB2DPolygonFromB3DPolygon(const B3DPolygon& rCandidate, const B3DHomMatrix& rMat);
+
+ // create simplified version of the original polygon by
+ // replacing segments with spikes/loops and self intersections
+ // by several trivial sub-segments
+ B2DPolygon createSimplifiedPolygon(const B2DPolygon&);
+
+ // calculate the distance to the given endless ray and return. The relative position on the edge is returned in Cut.
+ // That position may be less than 0.0 or more than 1.0
+ double getDistancePointToEndlessRay(const B2DPoint& rPointA, const B2DPoint& rPointB, const B2DPoint& rTestPoint, double& rCut);
+
+ // calculate the smallest distance to given edge and return. The relative position on the edge is returned in Cut.
+ // That position is in the range [0.0 .. 1.0] and the returned distance is adapted accordingly to the start or end
+ // point of the edge
+ double getSmallestDistancePointToEdge(const B2DPoint& rPointA, const B2DPoint& rPointB, const B2DPoint& rTestPoint, double& rCut);
+
+ // for each contained edge calculate the smallest distance. Return the index to the smallest
+ // edge in rEdgeIndex. The relative position on the edge is returned in rCut.
+ // If nothing was found (e.g. empty input plygon), DBL_MAX is returned.
+ double getSmallestDistancePointToPolygon(const B2DPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rEdgeIndex, double& rCut);
+
+ // distort single point. rOriginal describes the original range, where the given points describe the distorted corresponding points.
+ B2DPoint distort(const B2DPoint& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight);
+
+ // distort polygon. rOriginal describes the original range, where the given points describe the distorted corresponding points.
+ B2DPolygon distort(const B2DPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight);
+
+ // rotate polygon around given point with given angle.
+ B2DPolygon rotateAroundPoint(const B2DPolygon& rCandidate, const B2DPoint& rCenter, double fAngle);
+
+ // expand all segments (which are not yet) to curve segments. This is done with setting the control
+ // vectors on the 1/3 resp. 2/3 distances on each segment.
+ B2DPolygon expandToCurve(const B2DPolygon& rCandidate);
+
+ // expand given segment to curve segment. This is done with setting the control
+ // vectors on the 1/3 resp. 2/3 distances. The return value describes if a change took place.
+ bool expandToCurveInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex);
+
+ // set continuity for the whole curve. If not a curve, nothing will change. Non-curve points are not changed, too.
+ B2DPolygon setContinuity(const B2DPolygon& rCandidate, B2VectorContinuity eContinuity);
+
+ // set continuity for given index. If not a curve, nothing will change. Non-curve points are not changed, too.
+ // The return value describes if a change took place.
+ bool setContinuityInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex, B2VectorContinuity eContinuity);
+
+ // test if polygon contains neutral points. A neutral point is one whos orientation is neutral
+ // e.g. positioned on the edge of it's predecessor and successor
+ bool hasNeutralPoints(const B2DPolygon& rCandidate);
+
+ // remove neutral points. A neutral point is one whos orientation is neutral
+ // e.g. positioned on the edge of it's predecessor and successor
+ B2DPolygon removeNeutralPoints(const B2DPolygon& rCandidate);
+
+ // tests if polygon is convex
+ bool isConvex(const B2DPolygon& rCandidate);
+
+ // calculates the orientation at edge nIndex
+ B2VectorOrientation getOrientationForIndex(const B2DPolygon& rCandidate, sal_uInt32 nIndex);
+
+ // calculates if given point is on given line, taking care of the numerical epsilon
+ bool isPointOnLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidate, bool bWithPoints = false);
+
+ // calculates if given point is on given polygon, taking care of the numerical epsilon. Uses
+ // isPointOnLine internally
+ bool isPointOnPolygon(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithPoints = true);
+
+ // test if candidate is inside triangle
+ bool isPointInTriangle(const B2DPoint& rA, const B2DPoint& rB, const B2DPoint& rC, const B2DPoint& rCandidate, bool bWithBorder = false);
+
+ // test if candidateA and candidateB are on the same side of the given line
+ bool arePointsOnSameSideOfLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidateA, const B2DPoint& rCandidateB, bool bWithLine = false);
+
+ // add triangles for given rCandidate to rTarget. For each triangle, 3 points will be added to rCandidate.
+ // All triangles will go from the start point of rCandidate to two consecutive points, building (rCandidate.count() - 2)
+ // triangles.
+ void addTriangleFan(const B2DPolygon& rCandidate, B2DPolygon& rTarget);
+
+ // grow for polygon. Move all geometry in each point in the direction of the normal in that point
+ // with the given amount. Value may be negative.
+ B2DPolygon growInNormalDirection(const B2DPolygon& rCandidate, double fValue);
+
+ // force all sub-polygons to a point count of nSegments
+ B2DPolygon reSegmentPolygon(const B2DPolygon& rCandidate, sal_uInt32 nSegments);
+
+ // create polygon state at t from 0.0 to 1.0 between the two polygons. Both polygons must have the same
+ // organisation, e.g. same amount of points
+ B2DPolygon interpolate(const B2DPolygon& rOld1, const B2DPolygon& rOld2, double t);
+
+ bool isPolyPolygonEqualRectangle( const B2DPolyPolygon& rPolyPoly, const B2DRange& rRect );
+
+ // #i76891# Try to remove existing curve segments if they are simply edges
+ B2DPolygon simplifyCurveSegments(const B2DPolygon& rCandidate);
+
+ // makes the given indexed point the new polygon start point. To do that, the points in the
+ // polygon will be rotated. This is only valid for closed polygons, for non-closed ones
+ // an assertion will be triggered
+ B2DPolygon makeStartPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndexOfNewStatPoint);
+
+ /** create edges of given length along given B2DPolygon
+
+ @param rCandidate
+ The polygon to move along. Points at the given polygon are created, starting
+ at position fStart and stopping at less or equal to fEnd. The closed state is
+ preserved.
+ The polygon is subdivided if curve segments are included. That subdivision is the base
+ for the newly created points.
+ If the source is closed, the indirectly existing last edge may NOT have the
+ given length.
+ If the source is open, all edges will have the given length. You may use the last
+ point of the original when You want to add the last edge Yourself.
+
+ @param fLength
+ The length of the created edges. If less or equal zero, an empty polygon is returned.
+
+ @param fStart
+ The start distance for the first to be generated point. Use 0.0 to get the
+ original start point. Negative values are truncated to 0.0.
+
+ @param fEnd
+ The maximum distance for the last point. No more points behind this distance will be created.
+ Use 0.0 to proccess the whole polygon. Negative values are truncated to 0.0. It also
+ needs to be more or equal to fStart, else it is truncated to fStart.
+
+ @return
+ The newly created polygon
+ */
+ B2DPolygon createEdgesOfGivenLength(const B2DPolygon& rCandidate, double fLength, double fStart = 0.0, double fEnd = 0.0);
+
+ /** Create Waveline along given polygon
+ The implementation is based on createEdgesOfGivenLength and creates a curve
+ segment with the given dimensions for each created line segment. The polygon
+ is treated as if opened (closed state will be ignored) and only for whole
+ edges a curve segment will be created (no rest handling)
+
+ @param rCandidate
+ The polygon along which the waveline will be created
+
+ @param fWaveWidth
+ The length of a single waveline curve segment
+
+ @param fgWaveHeight
+ The height of the waveline (amplitude)
+ */
+ B2DPolygon createWaveline(const B2DPolygon& rCandidate, double fWaveWidth, double fWaveHeight);
+
+ /** split each edge of a polygon in exactly nSubEdges equidistant edges
+
+ @param rCandidate
+ The source polygon. If too small (no edges), nSubEdges too small (<2)
+ or neither bHandleCurvedEdgesnor bHandleStraightEdges it will just be returned.
+ Else for each edge nSubEdges will be created. Closed state is preserved.
+
+ @param nSubEdges
+ How many edges shall be created as replacement for each single edge
+
+ @param bHandleCurvedEdges
+ Process curved edges or not. If to handle the curved edges will be splitted
+ into nSubEdges part curved edges of equidistant bezier distances. If not,
+ curved edges will just be copied.
+
+ @param bHandleStraightEdges
+ Process straight edges or not. If to handle the straight edges will be splitted
+ into nSubEdges part curved edges of equidistant length. If not,
+ straight edges will just be copied.
+ */
+ B2DPolygon reSegmentPolygonEdges(const B2DPolygon& rCandidate, sal_uInt32 nSubEdges, bool bHandleCurvedEdges, bool bHandleStraightEdges);
+
+ //////////////////////////////////////////////////////////////////////
+ // comparators with tolerance for 2D Polygons
+ bool equal(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, const double& rfSmallValue);
+ bool equal(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB);
+
+ /** snap some polygon coordinates to discrete coordinates
+
+ This method allows to snap some polygon points to discrete (integer) values
+ which equals e.g. a snap to discrete coordinates. It will snap points of
+ horizontal and vertical edges
+
+ @param rCandidate
+ The source polygon
+
+ @return
+ The modified version of the source polygon
+ */
+ B2DPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolygon& rCandidate);
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+#endif /* _BGFX_POLYGON_B2DPOLYGONTOOLS_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b2dpolygontriangulator.hxx b/basegfx/inc/basegfx/polygon/b2dpolygontriangulator.hxx
new file mode 100644
index 000000000000..a2e2a1958e0e
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dpolygontriangulator.hxx
@@ -0,0 +1,52 @@
+/*************************************************************************
+ *
+ * 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: b2dpolygontriangulator.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B2DPOLYGONTRIANGULATOR_HXX
+#define _BGFX_POLYGON_B2DPOLYGONTRIANGULATOR_HXX
+
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <vector>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace triangulator
+ {
+ // triangulate given polygon
+ ::basegfx::B2DPolygon triangulate(const ::basegfx::B2DPolygon& rCandidate);
+
+ // triangulate given PolyPolygon
+ ::basegfx::B2DPolygon triangulate(const ::basegfx::B2DPolyPolygon& rCandidate);
+
+ } // end of namespace triangulator
+} // end of namespace basegfx
+
+#endif /* _BGFX_POLYGON_B2DPOLYGONTRIANGULATOR_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b2dpolypolygon.hxx b/basegfx/inc/basegfx/polygon/b2dpolypolygon.hxx
new file mode 100644
index 000000000000..9c8724b8ee6d
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dpolypolygon.hxx
@@ -0,0 +1,134 @@
+/*************************************************************************
+ *
+ * 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: b2dpolypolygon.hxx,v $
+ * $Revision: 1.16 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B2DPOLYPOLYGON_HXX
+#define _BGFX_POLYGON_B2DPOLYPOLYGON_HXX
+
+#include <sal/types.h>
+#include <o3tl/cow_wrapper.hxx>
+#include <basegfx/range/b2drange.hxx>
+
+// predeclarations
+class ImplB2DPolyPolygon;
+
+namespace basegfx
+{
+ class B2DPolygon;
+ class B2DHomMatrix;
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class B2DPolyPolygon
+ {
+ public:
+ typedef o3tl::cow_wrapper< ImplB2DPolyPolygon > ImplType;
+
+ private:
+ ImplType mpPolyPolygon;
+
+ public:
+ B2DPolyPolygon();
+ B2DPolyPolygon(const B2DPolyPolygon& rPolyPolygon);
+ explicit B2DPolyPolygon(const B2DPolygon& rPolygon);
+ ~B2DPolyPolygon();
+
+ // assignment operator
+ B2DPolyPolygon& operator=(const B2DPolyPolygon& rPolyPolygon);
+
+ /// unshare this poly-polygon (and all included polygons) with all internally shared instances
+ void makeUnique();
+
+ // compare operators
+ bool operator==(const B2DPolyPolygon& rPolyPolygon) const;
+ bool operator!=(const B2DPolyPolygon& rPolyPolygon) const;
+
+ // polygon interface
+ sal_uInt32 count() const;
+
+ B2DPolygon getB2DPolygon(sal_uInt32 nIndex) const;
+ void setB2DPolygon(sal_uInt32 nIndex, const B2DPolygon& rPolygon);
+
+ // test for curve
+ bool areControlPointsUsed() const;
+
+ // insert/append single polygon
+ void insert(sal_uInt32 nIndex, const B2DPolygon& rPolygon, sal_uInt32 nCount = 1);
+ void append(const B2DPolygon& rPolygon, sal_uInt32 nCount = 1);
+
+ /** Default adaptive subdivision access
+
+ For details refer to B2DPolygon::getDefaultAdaptiveSubdivision()
+
+ @return
+ The default subdivision of this polygon
+ */
+ B2DPolyPolygon getDefaultAdaptiveSubdivision() const;
+
+ /** Get the B2DRange (Rectangle dimensions) of this B2DPolyPolygon
+
+ For details refer to B2DPolygon::getB2DRange()
+
+ @return
+ The outer range of the bezier curve/polygon
+ */
+ B2DRange getB2DRange() const;
+
+ // insert/append multiple polygons
+ void insert(sal_uInt32 nIndex, const B2DPolyPolygon& rPolyPolygon);
+ void append(const B2DPolyPolygon& rPolyPolygon);
+
+ // remove
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount = 1);
+
+ // reset to empty state
+ void clear();
+
+ // closed state
+ bool isClosed() const;
+ void setClosed(bool bNew);
+
+ // flip polygon direction
+ void flip();
+
+ // test if PolyPolygon has double points
+ bool hasDoublePoints() const;
+
+ // remove double points, at the begin/end and follow-ups, too
+ void removeDoublePoints();
+
+ // apply transformation given in matrix form to the polygon
+ void transform(const basegfx::B2DHomMatrix& rMatrix);
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_POLYGON_B2DPOLYPOLYGON_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b2dpolypolygoncutter.hxx b/basegfx/inc/basegfx/polygon/b2dpolypolygoncutter.hxx
new file mode 100644
index 000000000000..12532ff078f3
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dpolypolygoncutter.hxx
@@ -0,0 +1,122 @@
+/*************************************************************************
+ *
+ * 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: b2dpolypolygoncutter.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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B2DPOLYPOLYGONCUTTER_HXX
+#define _BGFX_POLYGON_B2DPOLYPOLYGONCUTTER_HXX
+
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ // Solve all crossovers in a polyPolygon. This re-layouts all contained polygons so that the
+ // result will contain only non-cutting polygons. For that reason, points will be added at
+ // crossover and touch points and the single Polygons may be re-combined. The orientations
+ // of the contained polygons in not changed but used as topological information.
+ // Self crossovers of the contained sub-polygons are implicitely handled, but to not lose
+ // the topological information, it may be necessary to remove self-intersections of the
+ // contained sub-polygons in a preparing step and to explicitely correct their orientations.
+ B2DPolyPolygon solveCrossovers(const B2DPolyPolygon& rCandidate);
+
+ // Version for single polygons. This is for solving self-intersections. Result will be free of
+ // crossovers. When result contains multiple polygons, it may be necessary to rearrange their
+ // orientations since holes may have been created (use correctOrientations eventually).
+ B2DPolyPolygon solveCrossovers(const B2DPolygon& rCandidate);
+
+ // Neutral polygons will be stripped. Neutral polygons are ones who's orientation is
+ // neutral, so normally they have no volume -> just closed paths. A polygon with the same
+ // positive and negative oriented volume is also neutral, so this may not be wanted. It is
+ // safe to call with crossover-free polygons, though (that's where it's mostly used).
+ B2DPolyPolygon stripNeutralPolygons(const B2DPolyPolygon& rCandidate);
+
+ // Remove not necessary polygons. Works only correct with crossover-free polygons. For each
+ // polygon, the depth for the PolyPolygon is calculated. The orientation is used to identify holes.
+ // Start value for holes is -1, for polygons it's zero. Ech time a polygon is contained in another one,
+ // it's depth is increased when inside a polygon, decreased when inside a hole. The result is a depth
+ // which e.g. is -1 for holes outside everything, 1 for a polygon covered by another polygon and zero
+ // for e.g. holes in a polygon or polygons outside everythig else.
+ // In the 2nd step, all polygons with depth other than zero are removed. If bKeepAboveZero is used,
+ // all polygons < 1 are removed. The bKeepAboveZero mode is useful for clipping, e.g. just append
+ // one polygon to another and use this mode -> only parts where two polygons overlapped will be kept.
+ // In combination with correct orientation of the input orientations and the SolveCrossover calls this
+ // can be combined for logical polygon operations or polygon clipping.
+ B2DPolyPolygon stripDispensablePolygons(const B2DPolyPolygon& rCandidate, bool bKeepAboveZero = false);
+
+ // For convenience: The four basic operations OR, XOR, AND and DIFF for
+ // two PolyPolygons. These are combinations of the above methods. To not be forced
+ // to do evtl. already done preparations twice, You have to do the operations Yourself.
+ //
+ // A source preparation consists of preparing it to be seen as XOR-Rule PolyPolygon,
+ // so it is freed of intersections, self-intersections and the orientations are corrected.
+ // Important is that it will define the same areas as before, but is intersection-free.
+ // As an example think about a single polygon looping in itself and having holes. To
+ // topologically correctly handle this, it is necessary to remove all intersections and
+ // to correct the orientations. The orientation of the isolated holes e.g. will be negative.
+ // Topologically it is necessary to prepare each polygon which is seen as entity. It is
+ // not sufficient just to concatenate them and prepare the result, this may be topologically
+ // different since the simple concatenation will be seen as XOR. To work correctly, You
+ // may need to OR those polygons.
+
+ // Preparations: solve self-intersections and intersections, remove neutral
+ // parts and correct orientations.
+ B2DPolyPolygon prepareForPolygonOperation(const B2DPolygon& rCandidate);
+ B2DPolyPolygon prepareForPolygonOperation(const B2DPolyPolygon& rCandidate);
+
+ // OR: Return all areas where CandidateA or CandidateB exist
+ B2DPolyPolygon solvePolygonOperationOr(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB);
+
+ // XOR: Return all areas where CandidateA or CandidateB exist, but not both
+ B2DPolyPolygon solvePolygonOperationXor(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB);
+
+ // AND: Return all areas where CandidateA and CandidateB exist
+ B2DPolyPolygon solvePolygonOperationAnd(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB);
+
+ // DIFF: Return all areas where CandidateA is not covered by CandidateB (cut B out of A)
+ B2DPolyPolygon solvePolygonOperationDiff(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB);
+
+ /** merge all single PolyPolygons to a single, OR-ed PolyPolygon
+
+ @param rInput
+ The source PolyPolygons
+
+ @return A single PolyPolygon containing the Or-merged result
+ */
+ B2DPolyPolygon mergeToSinglePolyPolygon(const std::vector< basegfx::B2DPolyPolygon >& rInput);
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+#endif /* _BGFX_POLYGON_B2DPOLYPOLYGONCUTTER_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b2dpolypolygonfillrule.hxx b/basegfx/inc/basegfx/polygon/b2dpolypolygonfillrule.hxx
new file mode 100644
index 000000000000..e90d0bbc1e32
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dpolypolygonfillrule.hxx
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *
+ * 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: b2dpolypolygonfillrule.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B2DPOLYPOLYGONFILLRULE_HXX
+#define _BGFX_POLYGON_B2DPOLYPOLYGONFILLRULE_HXX
+
+#include <sal/types.h>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ /** Fill rule to use for poly-polygon filling.
+
+ The fill rule determines which areas are inside, and which are
+ outside the poly-polygon.
+ */
+ enum FillRule
+ {
+ /** Areas, for which a scanline has crossed an odd number of
+ vertices, are regarded 'inside', the remainder 'outside'
+ of the poly-polygon.
+ */
+ FillRule_EVEN_ODD,
+
+ /** For each edge a scanline crosses, a current winding number
+ is updated. Downward edges count +1, upward edges count
+ -1. If the total accumulated winding number for one area
+ is not zero, this area is regarded 'inside', otherwise,
+ 'outside'.
+ */
+ FillRule_NONZERO_WINDING_NUMBER
+ };
+}
+
+#endif /* _BGFX_POLYGON_B2DPOLYPOLYGONFILLRULE_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b2dpolypolygonrasterconverter.hxx b/basegfx/inc/basegfx/polygon/b2dpolypolygonrasterconverter.hxx
new file mode 100644
index 000000000000..e26b2063c3e9
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dpolypolygonrasterconverter.hxx
@@ -0,0 +1,144 @@
+/*************************************************************************
+ *
+ * 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: b2dpolypolygonrasterconverter.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B2DPOLYPOLYGONRASTERCONVERTER_HXX
+#define _BGFX_POLYGON_B2DPOLYPOLYGONRASTERCONVERTER_HXX
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygonfillrule.hxx>
+#include <vector>
+#include <utility>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ /** Raster-convert a poly-polygon.
+
+ This class can raster-convert a given poly-polygon. Simply
+ derive from this, and override the span() method, which will
+ get called for every scanline span of the poly-polygon.
+
+ @derive
+ Overwrite span() with the render output method of your choice.
+ */
+ class B2DPolyPolygonRasterConverter
+ {
+ public:
+ /** Create raster-converter for given poly-polygon
+ */
+ B2DPolyPolygonRasterConverter(const B2DPolyPolygon& rPolyPolyRaster);
+
+ /** Create raster-converter for given poly-polygon and raster
+ area.
+
+ @param rPolyPolyRaster
+ Poly-Polygon to raster convert
+
+ @param rMinUpdateArea
+ Minimal area to touch when raster-converting. The
+ rectangle given here is guaranteed to be iterated through
+ scanline by scanline (but the raster converter might
+ actually use more scanlines, e.g. if the poly-polygon's
+ bound rect is larger). One of the cases where this
+ parameter comes in handy is when rendering in the 'off'
+ spans, and a certain area must be filled. <em>Do not</em>
+ use this for clipping, as described above, the touched
+ area might also be larger.
+ */
+ B2DPolyPolygonRasterConverter(const B2DPolyPolygon& rPolyPolyRaster,
+ const B2DRectangle& rRasterArea );
+
+ virtual ~B2DPolyPolygonRasterConverter();
+
+ /** Raster-convert the contained poly-polygon
+
+ @param eFillRule
+ Fill rule to use for span filling
+ */
+ void rasterConvert( FillRule eFillRule);
+
+ /** Override this method, to be called for every scanline span
+ of the poly-polygon
+
+ @param rfXLeft
+ The left end of the current horizontal span
+
+ @param rfXRight
+ The right end of the current horizontal span
+
+ @param nY
+ The y position of the current horizontal span
+
+ @param bOn
+ Denotes whether this span is on or off, according to the
+ active fill rule.
+ */
+ virtual void span(const double& rfXLeft,
+ const double& rfXRight,
+ sal_Int32 nY,
+ bool bOn ) = 0;
+
+ /// @internal
+ struct Vertex
+ {
+ inline Vertex();
+ inline Vertex( const B2DPoint&, const B2DPoint&, bool );
+
+ B2DPoint aP1;
+ B2DPoint aP2;
+ bool bDownwards;
+ };
+
+ private:
+ // default: disabled copy/assignment
+ B2DPolyPolygonRasterConverter(const B2DPolyPolygonRasterConverter&);
+ B2DPolyPolygonRasterConverter& operator=( const B2DPolyPolygonRasterConverter& );
+
+ void init();
+
+ typedef ::std::vector<Vertex> VectorOfVertices;
+ typedef ::std::vector<VectorOfVertices> VectorOfVertexVectors;
+
+ /// The poly-polygon to raster-convert
+ B2DPolyPolygon maPolyPolygon;
+ /// Total bound rect of the poly-polygon
+ const B2DRectangle maPolyPolyRectangle;
+
+ /** Vector containing for each scanline a vector which in turn
+ contains all vertices that start on the specific scanline
+ */
+ VectorOfVertexVectors maScanlines;
+ };
+}
+
+#endif /* _BGFX_POLYGON_B2DPOLYPOLYGONRASTERCONVERTER_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b2dpolypolygontools.hxx b/basegfx/inc/basegfx/polygon/b2dpolypolygontools.hxx
new file mode 100644
index 000000000000..c4687b3cfc5f
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b2dpolypolygontools.hxx
@@ -0,0 +1,282 @@
+/*************************************************************************
+ *
+ * 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: b2dpolypolygontools.hxx,v $
+ * $Revision: 1.20 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYPOLYGON_B2DPOLYGONTOOLS_HXX
+#define _BGFX_POLYPOLYGON_B2DPOLYGONTOOLS_HXX
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <vector>
+
+namespace rtl
+{
+ class OUString;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ // predefinitions
+ class B2DPolyPolygon;
+ class B2DRange;
+
+ namespace tools
+ {
+ // B2DPolyPolygon tools
+
+ // Check and evtl. correct orientations of all contained Polygons so that
+ // the orientations of contained polygons will variate to express areas and
+ // holes
+ B2DPolyPolygon correctOrientations(const B2DPolyPolygon& rCandidate);
+
+ // make sure polygon with index 0L is not a hole. This may evtl. change the
+ // sequence of polygons, but allows to use polygon with index 0L to
+ // get the correct normal for the whole polyPolygon
+ B2DPolyPolygon correctOutmostPolygon(const B2DPolyPolygon& rCandidate);
+
+ // Subdivide all contained curves. Use distanceBound value if given.
+ B2DPolyPolygon adaptiveSubdivideByDistance(const B2DPolyPolygon& rCandidate, double fDistanceBound = 0.0);
+
+ // Subdivide all contained curves. Use distanceBound value if given. Else, a convenient one
+ // is created.
+ B2DPolyPolygon adaptiveSubdivideByAngle(const B2DPolyPolygon& rCandidate, double fAngleBound = 0.0);
+
+ // Subdivide all contained curves. Use nCount divisions if given. Else, a convenient one
+ // is created.
+ B2DPolyPolygon adaptiveSubdivideByCount(const B2DPolyPolygon& rCandidate, sal_uInt32 nCount = 0L);
+
+ // isInside test for B2dPoint. On border is not inside as long as not true is given
+ // in bWithBorder flag. It is assumed that the orientations of the given polygon are correct.
+ bool isInside(const B2DPolyPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder = false);
+
+ /** get range of PolyPolygon. Control points are included.
+
+ For detailed description look at getRangeWithControlPoints(const B2DPolygon&).
+ This method just expands by the range of every sub-Polygon.
+
+ @param rCandidate
+ The B2DPolyPolygon eventually containing bezier segments
+
+ @return
+ The outer range including control points
+ */
+ B2DRange getRangeWithControlPoints(const B2DPolyPolygon& rCandidate);
+
+ /** Get the range of a polyPolygon
+
+ For detailed description look at getRange(const B2DPolygon&).
+ This method just expands by the range of every sub-Polygon.
+
+ @param rCandidate
+ The B2DPolyPolygon eventually containing bezier segments
+
+ @return
+ The outer range of the polygon
+ */
+ B2DRange getRange(const B2DPolyPolygon& rCandidate);
+
+ /** Apply given LineDashing to given polyPolygon
+
+ For a description see applyLineDashing in b2dpolygontoos.hxx
+ */
+ void applyLineDashing(
+ const B2DPolyPolygon& rCandidate,
+ const ::std::vector<double>& rDotDashArray,
+ B2DPolyPolygon* pLineTarget,
+ B2DPolyPolygon* pGapTarget = 0,
+ double fFullDashDotLen = 0.0);
+
+ // test if point is inside epsilon-range around the given PolyPolygon. Can be used
+ // for HitTesting. The epsilon-range is defined to be the tube around the PolyPolygon
+ // with distance fDistance and rounded edges (start and end point).
+ bool isInEpsilonRange(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance);
+
+ /** Read poly-polygon from SVG.
+
+ This function imports a poly-polygon from an SVG-D
+ attribute. Currently, elliptical arc elements are not yet
+ supported (and ignored during parsing).
+
+ @param o_rPolyPoly
+ The output poly-polygon
+
+ @param rSvgDAttribute
+ A valid SVG-D attribute string
+
+ @return true, if the string was successfully parsed
+ */
+ bool importFromSvgD( B2DPolyPolygon& o_rPolyPoly,
+ const ::rtl::OUString& rSvgDAttribute );
+
+ /** Read poly-polygon from SVG.
+
+ This function imports a poly-polygon from an SVG points
+ attribute (a plain list of coordinate pairs).
+
+ @param o_rPoly
+ The output polygon. Note that svg:points can only define a
+ single polygon
+
+ @param rSvgPointsAttribute
+ A valid SVG points attribute string
+
+ @return true, if the string was successfully parsed
+ */
+ bool importFromSvgPoints( B2DPolygon& o_rPoly,
+ const ::rtl::OUString& rSvgPointsAttribute );
+
+
+ // grow for polyPolygon. Move all geometry in each point in the direction of the normal in that point
+ // with the given amount. Value may be negative.
+ B2DPolyPolygon growInNormalDirection(const B2DPolyPolygon& rCandidate, double fValue);
+
+ // This method will correct a pair of polyPolygons where the goal is to keep same point count
+ // to allow direct point association and also to remove self-intersections produced by shrinks.
+ // This method will eventually change both polyPolygons to reach that goal because there are cases
+ // where it is necessary to add new cut points to the original
+ void correctGrowShrinkPolygonPair(B2DPolyPolygon& rOriginal, B2DPolyPolygon& rGrown);
+
+ // force all sub-polygons to a point count of nSegments
+ B2DPolyPolygon reSegmentPolyPolygon(const B2DPolyPolygon& rCandidate, sal_uInt32 nSegments);
+
+ // create polygon state at t from 0.0 to 1.0 between the two polygons. Both polygons must have the same
+ // organisation, e.g. same amount of polygons
+ B2DPolyPolygon interpolate(const B2DPolyPolygon& rOld1, const B2DPolyPolygon& rOld2, double t);
+
+ // create 3d PolyPolygon from given 2d PolyPolygon. The given fZCoordinate is used to expand the
+ // third coordinate.
+ B3DPolyPolygon createB3DPolyPolygonFromB2DPolyPolygon(const B2DPolyPolygon& rCandidate, double fZCoordinate = 0.0);
+
+ // create 2d PolyPolygon from given 3d PolyPolygon. All coordinates are transformed using the given
+ // matrix and the resulting x,y is used to form the new polygon.
+ B2DPolyPolygon createB2DPolyPolygonFromB3DPolyPolygon(const B3DPolyPolygon& rCandidate, const B3DHomMatrix& rMat);
+
+ // for each contained edge in each contained polygon calculate the smallest distance. Return the index to the smallest
+ // edge in rEdgeIndex and the index to the polygon in rPolygonIndex. The relative position on the edge is returned in rCut.
+ // If nothing was found (e.g. empty input plygon), DBL_MAX is returned.
+ double getSmallestDistancePointToPolyPolygon(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rPolygonIndex, sal_uInt32& rEdgeIndex, double& rCut);
+
+ // distort PolyPolygon. rOriginal describes the original range, where the given points describe the distorted
+ // corresponding points.
+ B2DPolyPolygon distort(const B2DPolyPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight);
+
+ // rotate PolyPolygon around given point with given angle.
+ B2DPolyPolygon rotateAroundPoint(const B2DPolyPolygon& rCandidate, const B2DPoint& rCenter, double fAngle);
+
+ // expand all segments (which are not yet) to curve segments. This is done with setting the control
+ // vectors on the 1/3 resp. 2/3 distances on each segment.
+ B2DPolyPolygon expandToCurve(const B2DPolyPolygon& rCandidate);
+
+ // set continuity for the whole curve. If not a curve, nothing will change. Non-curve points are not changed, too.
+ B2DPolyPolygon setContinuity(const B2DPolyPolygon& rCandidate, B2VectorContinuity eContinuity);
+
+ /** Predicate whether a given poly-polygon is a rectangle.
+
+ @param rPoly
+ PolyPolygon to check
+
+ @return true, if the poly-polygon describes a rectangle
+ (contains exactly one polygon, polygon is closed, and the
+ points are either cw or ccw enumerations of a rectangle's
+ vertices). Note that intermediate points and duplicate
+ points are ignored.
+ */
+ bool isRectangle( const B2DPolyPolygon& rPoly );
+
+ /** Export poly-polygon to SVG.
+
+ This function exports a poly-polygon into an SVG-D
+ statement. Currently, output of relative point sequences
+ is not yet supported (might cause slightly larger output)
+
+ @param rPolyPoly
+ The poly-polygon to export
+
+ @param bUseRelativeCoordinates
+ When true, all coordinate values are exported as relative
+ to the current position. This tends to save some space,
+ since fewer digits needs to be written.
+
+ @param bDetectQuadraticBeziers
+ When true, the export tries to detect cubic bezier
+ segments in the input polygon, which can be represented by
+ quadratic bezier segments. Note that the generated string
+ causes versions prior to OOo2.0 to crash.
+
+ @return the generated SVG-D statement (the XML d attribute
+ value alone, without any "<path ...>" or "d="...")
+ */
+ ::rtl::OUString exportToSvgD( const B2DPolyPolygon& rPolyPoly,
+ bool bUseRelativeCoordinates=true,
+ bool bDetectQuadraticBeziers=true );
+
+ // #i76891# Try to remove existing curve segments if they are simply edges
+ B2DPolyPolygon simplifyCurveSegments(const B2DPolyPolygon& rCandidate);
+
+ /** split each edge of a polyPolygon in exactly nSubEdges equidistant edges
+
+ @param rCandidate
+ The source polyPolygon. If too small (no edges), nSubEdges too small (<2)
+ or neither bHandleCurvedEdgesnor bHandleStraightEdges it will just be returned.
+ Else for each edge nSubEdges will be created. Closed state is preserved.
+
+ @param nSubEdges
+ @param bHandleCurvedEdges
+ @param bHandleStraightEdges
+ Please take a look at reSegmentPolygonEdges description, these are the same.
+ */
+ B2DPolyPolygon reSegmentPolyPolygonEdges(const B2DPolyPolygon& rCandidate, sal_uInt32 nSubEdges, bool bHandleCurvedEdges, bool bHandleStraightEdges);
+
+ //////////////////////////////////////////////////////////////////////
+ // comparators with tolerance for 2D PolyPolygons
+ bool equal(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB, const double& rfSmallValue);
+ bool equal(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB);
+
+ /** snap some polygon coordinates to discrete coordinates
+
+ This method allows to snap some polygon points to discrete (integer) values
+ which equals e.g. a snap to discrete coordinates. It will snap points of
+ horizontal and vertical edges
+
+ @param rCandidate
+ The source polygon
+
+ @return
+ The modified version of the source polygon
+ */
+ B2DPolyPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolyPolygon& rCandidate);
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+#endif /* _BGFX_POLYPOLYGON_B2DPOLYGONTOOLS_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b3dgeometry.hxx b/basegfx/inc/basegfx/polygon/b3dgeometry.hxx
new file mode 100644
index 000000000000..024c7e695b68
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b3dgeometry.hxx
@@ -0,0 +1,74 @@
+/*************************************************************************
+ *
+ * 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: b3dgeometry.hxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B3DGEOMETRY_HXX
+#define _BGFX_POLYGON_B3DGEOMETRY_HXX
+
+//////////////////////////////////////////////////////////////////////////////
+// predeclarations
+
+namespace basegfx
+{
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class B3DGeometry
+ {
+ private:
+ B2DPolyPolygon maPolyPolygon; // the PolyPolygon geometry data, defines point number
+ B3DHomMatrix maPolygonTo3D; // transformation to create 3D PolyPolygon
+ B3DPolyPolygon maPolyNormal; // normal for each point or empty -> unified normal
+ B2DPolyPolygon maPolyTexture; // texture coordinate for each point or empty -> unified coordinate
+ B3DVector maUnifiedVector; // used when maNormal is empty
+
+ // bitfield
+ unsigned mbUnifiedVectorValid : 1; // flag to know if uvec is calculated yet
+
+ public:
+ B3DGeometry();
+ ~B3DGeometry();
+
+ // compare operators
+ bool operator==(const B3DGeometry& rGeometry) const;
+ bool operator!=(const B3DGeometry& rGeometry) const { return (!operator==(rGeometry)); }
+
+ // member count
+ sal_uInt32 count() const { return maPolyPolygon.count(); }
+ };
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+#endif /* _BGFX_POLYGON_B3DPOLYGON_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b3dpolygon.hxx b/basegfx/inc/basegfx/polygon/b3dpolygon.hxx
new file mode 100644
index 000000000000..4ac904b7aa8c
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b3dpolygon.hxx
@@ -0,0 +1,144 @@
+/*************************************************************************
+ *
+ * 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: b3dpolygon.hxx,v $
+ * $Revision: 1.9 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B3DPOLYGON_HXX
+#define _BGFX_POLYGON_B3DPOLYGON_HXX
+
+#include <sal/types.h>
+#include <o3tl/cow_wrapper.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+// predeclarations
+class ImplB3DPolygon;
+
+namespace basegfx
+{
+ class B3DPolygon;
+ class B3DPoint;
+ class B3DHomMatrix;
+ class B3DVector;
+ class B2DPoint;
+ class B2DHomMatrix;
+ class BColor;
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class B3DPolygon
+ {
+ public:
+ typedef o3tl::cow_wrapper< ImplB3DPolygon > ImplType;
+
+ private:
+ // internal data.
+ ImplType mpPolygon;
+
+ public:
+ B3DPolygon();
+ B3DPolygon(const B3DPolygon& rPolygon);
+ B3DPolygon(const B3DPolygon& rPolygon, sal_uInt32 nIndex, sal_uInt32 nCount);
+ ~B3DPolygon();
+
+ // assignment operator
+ B3DPolygon& operator=(const B3DPolygon& rPolygon);
+
+ /// unshare this polygon with all internally shared instances
+ void makeUnique();
+
+ // compare operators
+ bool operator==(const B3DPolygon& rPolygon) const;
+ bool operator!=(const B3DPolygon& rPolygon) const;
+
+ // member count
+ sal_uInt32 count() const;
+
+ // Coordinate interface
+ B3DPoint getB3DPoint(sal_uInt32 nIndex) const;
+ void setB3DPoint(sal_uInt32 nIndex, const B3DPoint& rValue);
+
+ // Coordinate insert/append
+ void insert(sal_uInt32 nIndex, const B3DPoint& rPoint, sal_uInt32 nCount = 1);
+ void append(const B3DPoint& rPoint, sal_uInt32 nCount = 1);
+
+ // BColor interface
+ BColor getBColor(sal_uInt32 nIndex) const;
+ void setBColor(sal_uInt32 nIndex, const BColor& rValue);
+ bool areBColorsUsed() const;
+ void clearBColors();
+
+ // Normals interface
+ B3DVector getNormal() const; // plane normal
+ B3DVector getNormal(sal_uInt32 nIndex) const; // normal in each point
+ void setNormal(sal_uInt32 nIndex, const B3DVector& rValue);
+ void transformNormals(const B3DHomMatrix& rMatrix);
+ bool areNormalsUsed() const;
+ void clearNormals();
+
+ // TextureCoordinate interface
+ B2DPoint getTextureCoordinate(sal_uInt32 nIndex) const;
+ void setTextureCoordinate(sal_uInt32 nIndex, const B2DPoint& rValue);
+ void transformTextureCoordiantes(const B2DHomMatrix& rMatrix);
+ bool areTextureCoordinatesUsed() const;
+ void clearTextureCoordinates();
+
+ // insert/append other 2D polygons
+ void insert(sal_uInt32 nIndex, const B3DPolygon& rPoly, sal_uInt32 nIndex2 = 0, sal_uInt32 nCount = 0);
+ void append(const B3DPolygon& rPoly, sal_uInt32 nIndex = 0, sal_uInt32 nCount = 0);
+
+ // remove
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount = 1);
+
+ // clear all points
+ void clear();
+
+ // closed state
+ bool isClosed() const;
+ void setClosed(bool bNew);
+
+ // flip polygon direction
+ void flip();
+
+ // test if Polygon has double points
+ bool hasDoublePoints() const;
+
+ // remove double points, at the begin/end and follow-ups, too
+ void removeDoublePoints();
+
+ // apply transformation given in matrix form to the polygon
+ void transform(const B3DHomMatrix& rMatrix);
+ };
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+#endif /* _BGFX_POLYGON_B3DPOLYGON_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b3dpolygonclipper.hxx b/basegfx/inc/basegfx/polygon/b3dpolygonclipper.hxx
new file mode 100644
index 000000000000..c33f703424ff
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b3dpolygonclipper.hxx
@@ -0,0 +1,90 @@
+/*************************************************************************
+ *
+ * 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: b3dpolygonclipper.hxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYPOLYGON_B3DPOLYGONCLIPPER_HXX
+#define _BGFX_POLYPOLYGON_B3DPOLYGONCLIPPER_HXX
+
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ // predefinitions
+ class B3DRange;
+ class B2DRange;
+
+ namespace tools
+ {
+ /** define for deciding one of X,Y,Z directions
+ */
+ enum B3DOrientation
+ {
+ B3DORIENTATION_X, // X-Axis
+ B3DORIENTATION_Y, // Y-Axis
+ B3DORIENTATION_Z // Z-Axis
+ };
+
+ // Clip given 3D polygon against a plane orthogonal to X,Y or Z axis. The plane is defined using the
+ // enum ePlaneOrthogonal which names the vector orthogonal to the plane, the fPlaneOffset gives the distance
+ // of the plane from the center (0.0).
+ // The value bClipPositive defines on which side the return value will be (true -> on positive side of plane).
+ // The switch bStroke decides if the polygon is interpreted as area (false) or strokes (true).
+ B3DPolyPolygon clipPolyPolygonOnOrthogonalPlane(const B3DPolyPolygon& rCandidate, B3DOrientation ePlaneOrthogonal, bool bClipPositive, double fPlaneOffset, bool bStroke);
+
+ // version for Polygons
+ B3DPolyPolygon clipPolygonOnOrthogonalPlane(const B3DPolygon& rCandidate, B3DOrientation ePlaneOrthogonal, bool bClipPositive, double fPlaneOffset, bool bStroke);
+
+ // Clip the given PolyPolygon against the given range. bInside defines if the result will contain the
+ // parts which are contained in the range or vice versa.
+ // The switch bStroke decides if the polygon is interpreted as area (false) or strokes (true).
+ B3DPolyPolygon clipPolyPolygonOnRange(const B3DPolyPolygon& rCandidate, const B3DRange& rRange, bool bInside, bool bStroke);
+
+ // version for Polygons
+ B3DPolyPolygon clipPolygonOnRange(const B3DPolygon& rCandidate, const B3DRange& rRange, bool bInside, bool bStroke);
+
+ // versions for B2DRange, clips only against X,Y
+ B3DPolyPolygon clipPolyPolygonOnRange(const B3DPolyPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke);
+ B3DPolyPolygon clipPolygonOnRange(const B3DPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke);
+
+ // Clip the given PolyPolygon against given plane in 3D. The plane is defined by a plane normal and a point on the plane.
+ // The value bClipPositive defines on which side the return value will be (true -> on positive side of plane).
+ // The switch bStroke decides if the polygon is interpreted as area (false) or strokes (true).
+ B3DPolyPolygon clipPolyPolygonOnPlane(const B3DPolyPolygon& rCandidate, const B3DPoint& rPointOnPlane, const B3DVector& rPlaneNormal, bool bClipPositive, bool bStroke);
+
+ // version for Polygons
+ B3DPolyPolygon clipPolygonOnPlane(const B3DPolygon& rCandidate, const B3DPoint& rPointOnPlane, const B3DVector& rPlaneNormal, bool bClipPositive, bool bStroke);
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+#endif /* _BGFX_POLYPOLYGON_B3DPOLYGONCLIPPER_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b3dpolygontools.hxx b/basegfx/inc/basegfx/polygon/b3dpolygontools.hxx
new file mode 100644
index 000000000000..a29204eadc3f
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b3dpolygontools.hxx
@@ -0,0 +1,194 @@
+/*************************************************************************
+ *
+ * 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: b3dpolygontools.hxx,v $
+ * $Revision: 1.10.4.1 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B3DPOLYGONTOOLS_HXX
+#define _BGFX_POLYGON_B3DPOLYGONTOOLS_HXX
+
+#include <basegfx/point/b3dpoint.hxx>
+#include <basegfx/vector/b3dvector.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <basegfx/vector/b2enums.hxx>
+#include <vector>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ // predefinitions
+ class B3DPolygon;
+ class B3DRange;
+
+ namespace tools
+ {
+ // B3DPolygon tools
+
+ /** Check if given polygon is closed. This is kind of a
+ 'classic' method to support old polygon definitions.
+ Those old polygon definitions define the closed state
+ of the polygon using identical start and endpoints. This
+ method corrects this (removes double start/end points)
+ and sets the Closed()-state of the polygon correctly.
+ */
+ void checkClosed(B3DPolygon& rCandidate);
+
+ // Get successor and predecessor indices. Returning the same index means there
+ // is none. Same for successor.
+ sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B3DPolygon& rCandidate);
+ sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B3DPolygon& rCandidate);
+
+ // Get orientation of Polygon
+ B2VectorOrientation getOrientation(const B3DPolygon& rCandidate);
+
+ // get size of polygon. Control vectors are included in that ranges.
+ B3DRange getRange(const B3DPolygon& rCandidate);
+
+ // get normal vector of polygon
+ B3DVector getNormal(const B3DPolygon& rCandidate);
+
+ // get normal vector of positive oriented polygon
+ B3DVector getPositiveOrientedNormal(const B3DPolygon& rCandidate);
+
+ // get signed area of polygon
+ double getSignedArea(const B3DPolygon& rCandidate);
+
+ // get area of polygon
+ double getArea(const B3DPolygon& rCandidate);
+
+ // get signed area of polygon
+ double getSignedArea(const B3DPolygon& rCandidate);
+
+ // get area of polygon
+ double getArea(const ::basegfx::B3DPolygon& rCandidate);
+
+ // get length of polygon edge from point nIndex to nIndex + 1
+ double getEdgeLength(const B3DPolygon& rCandidate, sal_uInt32 nIndex);
+
+ // get length of polygon
+ double getLength(const B3DPolygon& rCandidate);
+
+ // get position on polygon for absolute given distance. If
+ // length is given, it is assumed the correct polygon length, if 0.0 it is calculated
+ // using getLength(...)
+ B3DPoint getPositionAbsolute(const B3DPolygon& rCandidate, double fDistance, double fLength = 0.0);
+
+ // get position on polygon for relative given distance in range [0.0 .. 1.0]. If
+ // length is given, it is assumed the correct polygon length, if 0.0 it is calculated
+ // using getLength(...)
+ B3DPoint getPositionRelative(const B3DPolygon& rCandidate, double fDistance, double fLength = 0.0);
+
+ /** Apply given LineDashing to given polygon
+
+ For a description see applyLineDashing in b2dpolygontoos.hxx
+ */
+ void applyLineDashing(
+ const B3DPolygon& rCandidate,
+ const ::std::vector<double>& rDotDashArray,
+ B3DPolyPolygon* pLineTarget,
+ B3DPolyPolygon* pGapTarget = 0,
+ double fFullDashDotLen = 0.0);
+
+ /** Create/replace normals for given 3d geometry with default normals from given center to outside.
+ rCandidate: the 3d geometry to change
+ rCenter: the center of the 3d geometry
+ */
+ B3DPolygon applyDefaultNormalsSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter);
+
+ /** invert normals for given 3d geometry.
+ */
+ B3DPolygon invertNormals( const B3DPolygon& rCandidate);
+
+ /** Create/replace texture coordinates for given 3d geometry with parallel projected one
+ rRange: the full range of the 3d geometry
+ If bChangeX, x texture coordinate will be recalculated.
+ If bChangeY, y texture coordinate will be recalculated.
+ */
+ B3DPolygon applyDefaultTextureCoordinatesParallel( const B3DPolygon& rCandidate, const B3DRange& rRange, bool bChangeX = true, bool bChangeY = true);
+
+ /** Create/replace texture coordinates for given 3d geometry with spherical one
+ rCenter: the centre of the used 3d geometry
+ If bChangeX, x texture coordinate will be recalculated.
+ If bChangeY, y texture coordinate will be recalculated.
+ */
+ B3DPolygon applyDefaultTextureCoordinatesSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter, bool bChangeX = true, bool bChangeY = true);
+
+ // test if point is inside epsilon-range around an edge defined
+ // by the two given points. Can be used for HitTesting. The epsilon-range
+ // is defined to be the cylinder centered to the given edge, using radius
+ // fDistance, and the sphere around both points with radius fDistance.
+ bool isInEpsilonRange(const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, const B3DPoint& rTestPosition, double fDistance);
+
+ // test if point is inside epsilon-range around the given Polygon. Can be used
+ // for HitTesting. The epsilon-range is defined to be the cylinder centered to
+ // the given edge, using radius fDistance, and the sphere around both points with radius fDistance.
+ bool isInEpsilonRange(const B3DPolygon& rCandidate, const B3DPoint& rTestPosition, double fDistance);
+
+ // isInside tests for B3DPoint and other B3DPolygon. On border is not inside as long as
+ // not true is given in bWithBorder flag.
+ bool isInside(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder = false);
+ bool isInside(const B3DPolygon& rCandidate, const B3DPolygon& rPolygon, bool bWithBorder = false);
+
+ // calculates if given point is on given line, taking care of the numerical epsilon
+ bool isPointOnLine(const B3DPoint& rStart, const B3DPoint& rEnd, const B3DPoint& rCandidate, bool bWithPoints = false);
+
+ // calculates if given point is on given polygon, taking care of the numerical epsilon. Uses
+ // isPointOnLine internally
+ bool isPointOnPolygon(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithPoints = true);
+
+ // helper to get a fCut position between a plane (given with normal and a point)
+ // and a line given by start and end point
+ bool getCutBetweenLineAndPlane(const B3DVector& rPlaneNormal, const B3DPoint& rPlanePoint, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut);
+
+ // helper to get a fCut position between a 3d Polygon
+ // and a line given by start and end point
+ bool getCutBetweenLineAndPolygon(const B3DPolygon& rCandidate, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut);
+
+ //////////////////////////////////////////////////////////////////////
+ // comparators with tolerance for 3D Polygons
+ bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB, const double& rfSmallValue);
+ bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB);
+
+ /** snap some polygon coordinates to discrete coordinates
+
+ This method allows to snap some polygon points to discrete (integer) values
+ which equals e.g. a snap to discrete coordinates. It will snap points of
+ horizontal and vertical edges
+
+ @param rCandidate
+ The source polygon
+
+ @return
+ The modified version of the source polygon
+ */
+ B3DPolygon snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon& rCandidate);
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+#endif /* _BGFX_POLYGON_B3DPOLYGONTOOLS_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b3dpolypolygon.hxx b/basegfx/inc/basegfx/polygon/b3dpolypolygon.hxx
new file mode 100644
index 000000000000..01a053883499
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b3dpolypolygon.hxx
@@ -0,0 +1,128 @@
+/*************************************************************************
+ *
+ * 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: b3dpolypolygon.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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYGON_B3DPOLYPOLYGON_HXX
+#define _BGFX_POLYGON_B3DPOLYPOLYGON_HXX
+
+#include <sal/types.h>
+#include <o3tl/cow_wrapper.hxx>
+
+// predeclarations
+class ImplB3DPolyPolygon;
+
+namespace basegfx
+{
+ class B3DPolygon;
+ class B3DHomMatrix;
+ class B2DHomMatrix;
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class B3DPolyPolygon
+ {
+ public:
+ typedef o3tl::cow_wrapper< ImplB3DPolyPolygon > ImplType;
+
+ private:
+ ImplType mpPolyPolygon;
+
+ public:
+ B3DPolyPolygon();
+ B3DPolyPolygon(const B3DPolyPolygon& rPolyPolygon);
+ explicit B3DPolyPolygon(const B3DPolygon& rPolygon);
+ ~B3DPolyPolygon();
+
+ // assignment operator
+ B3DPolyPolygon& operator=(const B3DPolyPolygon& rPolyPolygon);
+
+ /// unshare this poly-polygon (and all included polygons) with all internally shared instances
+ void makeUnique();
+
+ // compare operators
+ bool operator==(const B3DPolyPolygon& rPolyPolygon) const;
+ bool operator!=(const B3DPolyPolygon& rPolyPolygon) const;
+
+ // polygon interface
+ sal_uInt32 count() const;
+
+ // B3DPolygon interface
+ B3DPolygon getB3DPolygon(sal_uInt32 nIndex) const;
+ void setB3DPolygon(sal_uInt32 nIndex, const B3DPolygon& rPolygon);
+
+ // BColor interface
+ bool areBColorsUsed() const;
+ void clearBColors();
+
+ // Normals interface
+ void transformNormals(const B3DHomMatrix& rMatrix);
+ bool areNormalsUsed() const;
+ void clearNormals();
+
+ // TextureCoordinate interface
+ void transformTextureCoordiantes(const B2DHomMatrix& rMatrix);
+ bool areTextureCoordinatesUsed() const;
+ void clearTextureCoordinates();
+
+ // insert/append single polygon
+ void insert(sal_uInt32 nIndex, const B3DPolygon& rPolygon, sal_uInt32 nCount = 1);
+ void append(const B3DPolygon& rPolygon, sal_uInt32 nCount = 1);
+
+ // insert/append multiple polygons
+ void insert(sal_uInt32 nIndex, const B3DPolyPolygon& rPolyPolygon);
+ void append(const B3DPolyPolygon& rPolyPolygon);
+
+ // remove
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount = 1);
+
+ // reset to empty state
+ void clear();
+
+ // closed state
+ bool isClosed() const;
+ void setClosed(bool bNew);
+
+ // flip polygon direction
+ void flip();
+
+ // test if PolyPolygon has double points
+ bool hasDoublePoints() const;
+
+ // remove double points, at the begin/end and follow-ups, too
+ void removeDoublePoints();
+
+ // apply transformation given in matrix form to the polygon
+ void transform(const basegfx::B3DHomMatrix& rMatrix);
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_POLYGON_B3DPOLYPOLYGON_HXX */
diff --git a/basegfx/inc/basegfx/polygon/b3dpolypolygontools.hxx b/basegfx/inc/basegfx/polygon/b3dpolypolygontools.hxx
new file mode 100644
index 000000000000..466aa25edd96
--- /dev/null
+++ b/basegfx/inc/basegfx/polygon/b3dpolypolygontools.hxx
@@ -0,0 +1,157 @@
+/*************************************************************************
+ *
+ * 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: b3dpolypolygontools.hxx,v $
+ * $Revision: 1.8.4.1 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_POLYPOLYGON_B3DPOLYGONTOOLS_HXX
+#define _BGFX_POLYPOLYGON_B3DPOLYGONTOOLS_HXX
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <vector>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ // predefinitions
+ class B3DPolyPolygon;
+ class B3DRange;
+
+ namespace tools
+ {
+ // B3DPolyPolygon tools
+
+ // get size of PolyPolygon. Control vectors are included in that ranges.
+ B3DRange getRange(const B3DPolyPolygon& rCandidate);
+
+ /** Apply given LineDashing to given polyPolygon
+
+ For a description see applyLineDashing in b2dpolygontoos.hxx
+ */
+ void applyLineDashing(
+ const B3DPolyPolygon& rCandidate,
+ const ::std::vector<double>& rDotDashArray,
+ B3DPolyPolygon* pLineTarget,
+ B3DPolyPolygon* pGapTarget = 0,
+ double fFullDashDotLen = 0.0);
+
+ /** Create a unit 3D line polyPolygon which defines a cube.
+ */
+ B3DPolyPolygon createUnitCubePolyPolygon();
+
+ /** Create a unit 3D fill polyPolygon which defines a cube.
+ */
+ B3DPolyPolygon createUnitCubeFillPolyPolygon();
+
+ /** Create a 3D line polyPolygon from a B3DRange which defines a cube.
+ */
+ B3DPolyPolygon createCubePolyPolygonFromB3DRange( const B3DRange& rRange);
+
+ /** Create a 3D fill polyPolygon from a B3DRange which defines a cube.
+ */
+ B3DPolyPolygon createCubeFillPolyPolygonFromB3DRange( const B3DRange& rRange);
+
+ /** Create a unit 3D line polyPolygon which defines a sphere with the given count of hor and ver segments.
+ Result will be centered at (0.0, 0.0, 0.0) and sized [-1.0 .. 1.0] in all dimensions.
+ If nHorSeg == 0 and/or nVerSeg == 0, a default will be calculated to have a step at least each 15 degrees.
+ With VerStart, VerStop and hor range in cartesian may be specified to create a partial sphere only.
+ */
+ B3DPolyPolygon createUnitSpherePolyPolygon(
+ sal_uInt32 nHorSeg = 0L, sal_uInt32 nVerSeg = 0L,
+ double fVerStart = F_PI2, double fVerStop = -F_PI2,
+ double fHorStart = 0.0, double fHorStop = F_2PI);
+
+ /** Create a 3D line polyPolygon from a B3DRange which defines a sphere with the given count of hor and ver segments.
+ If nHorSeg == 0 and/or nVerSeg == 0, a default will be calculated to have a step at least each 15 degrees.
+ With VerStart, VerStop and hor range in cartesian may be specified to create a partial sphere only.
+ */
+ B3DPolyPolygon createSpherePolyPolygonFromB3DRange(
+ const B3DRange& rRange,
+ sal_uInt32 nHorSeg = 0L, sal_uInt32 nVerSeg = 0L,
+ double fVerStart = F_PI2, double fVerStop = -F_PI2,
+ double fHorStart = 0.0, double fHorStop = F_2PI);
+
+ /** same as createUnitSpherePolyPolygon, but creates filled polygons (closed and oriented)
+ There is one extra, the bool bNormals defines if normals will be set, default is false
+ */
+ B3DPolyPolygon createUnitSphereFillPolyPolygon(
+ sal_uInt32 nHorSeg = 0L, sal_uInt32 nVerSeg = 0L,
+ bool bNormals = false,
+ double fVerStart = F_PI2, double fVerStop = -F_PI2,
+ double fHorStart = 0.0, double fHorStop = F_2PI);
+
+ /** same as createSpherePolyPolygonFromB3DRange, but creates filled polygons (closed and oriented)
+ There is one extra, the bool bNormals defines if normals will be set, default is false
+ */
+ B3DPolyPolygon createSphereFillPolyPolygonFromB3DRange(
+ const B3DRange& rRange,
+ sal_uInt32 nHorSeg = 0L, sal_uInt32 nVerSeg = 0L,
+ bool bNormals = false,
+ double fVerStart = F_PI2, double fVerStop = -F_PI2,
+ double fHorStart = 0.0, double fHorStop = F_2PI);
+
+ /** Create/replace normals for given 3d geometry with default normals from given center to outside.
+ rCandidate: the 3d geometry to change
+ rCenter: the center of the 3d geometry
+ */
+ B3DPolyPolygon applyDefaultNormalsSphere( const B3DPolyPolygon& rCandidate, const B3DPoint& rCenter);
+
+ /** invert normals for given 3d geometry.
+ */
+ B3DPolyPolygon invertNormals( const B3DPolyPolygon& rCandidate);
+
+ /** Create/replace texture coordinates for given 3d geometry with parallel projected one
+ rRange: the full range of the 3d geometry
+ If bChangeX, x texture coordinate will be recalculated.
+ If bChangeY, y texture coordinate will be recalculated.
+ */
+ B3DPolyPolygon applyDefaultTextureCoordinatesParallel( const B3DPolyPolygon& rCandidate, const B3DRange& rRange, bool bChangeX = true, bool bChangeY = true);
+
+ /** Create/replace texture coordinates for given 3d geometry with spherical one
+ rCenter: the centre of the used 3d geometry
+ If bChangeX, x texture coordinate will be recalculated.
+ If bChangeY, y texture coordinate will be recalculated.
+ */
+ B3DPolyPolygon applyDefaultTextureCoordinatesSphere( const B3DPolyPolygon& rCandidate, const B3DPoint& rCenter, bool bChangeX = true, bool bChangeY = true);
+
+ // isInside test for B3DPoint. On border is not inside as long as not true is given
+ // in bWithBorder flag. It is assumed that the orientations of the given polygon are correct.
+ bool isInside(const B3DPolyPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder = false);
+
+ //////////////////////////////////////////////////////////////////////
+ // comparators with tolerance for 3D PolyPolygons
+ bool equal(const B3DPolyPolygon& rCandidateA, const B3DPolyPolygon& rCandidateB, const double& rfSmallValue);
+ bool equal(const B3DPolyPolygon& rCandidateA, const B3DPolyPolygon& rCandidateB);
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+#endif /* _BGFX_POLYPOLYGON_B3DPOLYGONTOOLS_HXX */
diff --git a/basegfx/inc/basegfx/range/b1drange.hxx b/basegfx/inc/basegfx/range/b1drange.hxx
new file mode 100644
index 000000000000..366431c3cd50
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b1drange.hxx
@@ -0,0 +1,167 @@
+/*************************************************************************
+ *
+ * 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: b1drange.hxx,v $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B1DRANGE_HXX
+#define _BGFX_RANGE_B1DRANGE_HXX
+
+#include <basegfx/range/basicrange.hxx>
+
+
+namespace basegfx
+{
+ class B1IRange;
+
+ class B1DRange
+ {
+ ::basegfx::BasicRange< double, DoubleTraits > maRange;
+
+ public:
+ B1DRange()
+ {
+ }
+
+ explicit B1DRange(double fStartValue)
+ : maRange(fStartValue)
+ {
+ }
+
+ B1DRange(double fStartValue1, double fStartValue2)
+ : maRange(fStartValue1)
+ {
+ expand(fStartValue2);
+ }
+
+ B1DRange(const B1DRange& rRange)
+ : maRange(rRange.maRange)
+ {
+ }
+
+ explicit B1DRange( const B1IRange& rRange );
+
+ bool isEmpty() const
+ {
+ return maRange.isEmpty();
+ }
+
+ void reset()
+ {
+ maRange.reset();
+ }
+
+ bool operator==( const B1DRange& rRange ) const
+ {
+ return (maRange == rRange.maRange);
+ }
+
+ bool operator!=( const B1DRange& rRange ) const
+ {
+ return (maRange != rRange.maRange);
+ }
+
+ B1DRange& operator=(const B1DRange& rRange)
+ {
+ maRange = rRange.maRange;
+ return *this;
+ }
+
+ bool equal(const B1DRange& rRange) const
+ {
+ return (maRange.equal(rRange.maRange));
+ }
+
+ double getMinimum() const
+ {
+ return maRange.getMinimum();
+ }
+
+ double getMaximum() const
+ {
+ return maRange.getMaximum();
+ }
+
+ double getRange() const
+ {
+ return maRange.getRange();
+ }
+
+ double getCenter() const
+ {
+ return maRange.getCenter();
+ }
+
+ bool isInside(double fValue) const
+ {
+ return maRange.isInside(fValue);
+ }
+
+ bool isInside(const B1DRange& rRange) const
+ {
+ return maRange.isInside(rRange.maRange);
+ }
+
+ bool overlaps(const B1DRange& rRange) const
+ {
+ return maRange.overlaps(rRange.maRange);
+ }
+
+ bool overlapsMore(const B1DRange& rRange) const
+ {
+ return maRange.overlapsMore(rRange.maRange);
+ }
+
+ void expand(double fValue)
+ {
+ maRange.expand(fValue);
+ }
+
+ void expand(const B1DRange& rRange)
+ {
+ maRange.expand(rRange.maRange);
+ }
+
+ void intersect(const B1DRange& rRange)
+ {
+ maRange.intersect(rRange.maRange);
+ }
+
+ void grow(double fValue)
+ {
+ maRange.grow(fValue);
+ }
+ };
+
+ /** Round double to nearest integer for 1D range
+
+ @return the nearest integer for this range
+ */
+ B1IRange fround(const B1DRange& rRange);
+} // end of namespace basegfx
+
+
+#endif /* _BGFX_RANGE_B1DRANGE_HXX */
diff --git a/basegfx/inc/basegfx/range/b1ibox.hxx b/basegfx/inc/basegfx/range/b1ibox.hxx
new file mode 100644
index 000000000000..f2d2e058c4f8
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b1ibox.hxx
@@ -0,0 +1,146 @@
+/*************************************************************************
+ *
+ * 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: b1ibox.hxx,v $
+ * $Revision: 1.3 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B1IBOX_HXX
+#define _BGFX_RANGE_B1IBOX_HXX
+
+#include <basegfx/range/basicbox.hxx>
+
+
+namespace basegfx
+{
+ class B1IBox
+ {
+ ::basegfx::BasicBox maRange;
+
+ public:
+ B1IBox()
+ {
+ }
+
+ explicit B1IBox(sal_Int32 nStartValue)
+ : maRange(nStartValue)
+ {
+ }
+
+ B1IBox(sal_Int32 nStartValue1, sal_Int32 nStartValue2)
+ : maRange(nStartValue1)
+ {
+ expand(nStartValue2);
+ }
+
+ B1IBox(const B1IBox& rBox)
+ : maRange(rBox.maRange)
+ {
+ }
+
+ bool isEmpty() const
+ {
+ return maRange.isEmpty();
+ }
+
+ void reset()
+ {
+ maRange.reset();
+ }
+
+ bool operator==( const B1IBox& rBox ) const
+ {
+ return (maRange == rBox.maRange);
+ }
+
+ bool operator!=( const B1IBox& rBox ) const
+ {
+ return (maRange != rBox.maRange);
+ }
+
+ void operator=(const B1IBox& rBox)
+ {
+ maRange = rBox.maRange;
+ }
+
+ sal_Int32 getMinimum() const
+ {
+ return maRange.getMinimum();
+ }
+
+ sal_Int32 getMaximum() const
+ {
+ return maRange.getMaximum();
+ }
+
+ Int32Traits::DifferenceType getRange() const
+ {
+ return maRange.getRange();
+ }
+
+ double getCenter() const
+ {
+ return maRange.getCenter();
+ }
+
+ bool isInside(sal_Int32 nValue) const
+ {
+ return maRange.isInside(nValue);
+ }
+
+ bool isInside(const B1IBox& rBox) const
+ {
+ return maRange.isInside(rBox.maRange);
+ }
+
+ bool overlaps(const B1IBox& rBox) const
+ {
+ return maRange.overlaps(rBox.maRange);
+ }
+
+ void expand(sal_Int32 nValue)
+ {
+ maRange.expand(nValue);
+ }
+
+ void expand(const B1IBox& rBox)
+ {
+ maRange.expand(rBox.maRange);
+ }
+
+ void intersect(const B1IBox& rBox)
+ {
+ maRange.intersect(rBox.maRange);
+ }
+
+ void grow(sal_Int32 nValue)
+ {
+ maRange.grow(nValue);
+ }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_RANGE_B1IBOX_HXX */
diff --git a/basegfx/inc/basegfx/range/b1irange.hxx b/basegfx/inc/basegfx/range/b1irange.hxx
new file mode 100644
index 000000000000..79b4c2bed933
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b1irange.hxx
@@ -0,0 +1,147 @@
+/*************************************************************************
+ *
+ * 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: b1irange.hxx,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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B1IRANGE_HXX
+#define _BGFX_RANGE_B1IRANGE_HXX
+
+#include <basegfx/range/basicrange.hxx>
+
+
+namespace basegfx
+{
+ class B1IRange
+ {
+ ::basegfx::BasicRange< sal_Int32, Int32Traits > maRange;
+
+ public:
+ B1IRange()
+ {
+ }
+
+ explicit B1IRange(sal_Int32 nStartValue)
+ : maRange(nStartValue)
+ {
+ }
+
+ B1IRange(sal_Int32 nStartValue1, sal_Int32 nStartValue2)
+ : maRange(nStartValue1)
+ {
+ expand(nStartValue2);
+ }
+
+ B1IRange(const B1IRange& rRange)
+ : maRange(rRange.maRange)
+ {
+ }
+
+ bool isEmpty() const
+ {
+ return maRange.isEmpty();
+ }
+
+ void reset()
+ {
+ maRange.reset();
+ }
+
+ bool operator==( const B1IRange& rRange ) const
+ {
+ return (maRange == rRange.maRange);
+ }
+
+ bool operator!=( const B1IRange& rRange ) const
+ {
+ return (maRange != rRange.maRange);
+ }
+
+ B1IRange& operator=(const B1IRange& rRange)
+ {
+ maRange = rRange.maRange;
+ return *this;
+ }
+
+ sal_Int32 getMinimum() const
+ {
+ return maRange.getMinimum();
+ }
+
+ sal_Int32 getMaximum() const
+ {
+ return maRange.getMaximum();
+ }
+
+ Int32Traits::DifferenceType getRange() const
+ {
+ return maRange.getRange();
+ }
+
+ double getCenter() const
+ {
+ return maRange.getCenter();
+ }
+
+ bool isInside(sal_Int32 nValue) const
+ {
+ return maRange.isInside(nValue);
+ }
+
+ bool isInside(const B1IRange& rRange) const
+ {
+ return maRange.isInside(rRange.maRange);
+ }
+
+ bool overlaps(const B1IRange& rRange) const
+ {
+ return maRange.overlaps(rRange.maRange);
+ }
+
+ void expand(sal_Int32 nValue)
+ {
+ maRange.expand(nValue);
+ }
+
+ void expand(const B1IRange& rRange)
+ {
+ maRange.expand(rRange.maRange);
+ }
+
+ void intersect(const B1IRange& rRange)
+ {
+ maRange.intersect(rRange.maRange);
+ }
+
+ void grow(sal_Int32 nValue)
+ {
+ maRange.grow(nValue);
+ }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_RANGE_B1IRANGE_HXX */
diff --git a/basegfx/inc/basegfx/range/b2dconnectedranges.hxx b/basegfx/inc/basegfx/range/b2dconnectedranges.hxx
new file mode 100644
index 000000000000..ce56a9efd62a
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b2dconnectedranges.hxx
@@ -0,0 +1,266 @@
+/*************************************************************************
+ *
+ * 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: b2dconnectedranges.hxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B2DCONNECTEDRANGES_HXX
+#define _BGFX_RANGE_B2DCONNECTEDRANGES_HXX
+
+#include <osl/diagnose.h>
+#include <basegfx/range/b2drange.hxx>
+#include <list>
+#include <utility>
+#include <algorithm>
+
+
+namespace basegfx
+{
+ /** Calculate connected ranges from input ranges.
+
+ This template constructs a list of connected ranges from the
+ given input ranges. That is, the output will contain a set of
+ ranges, itself containing a number of input ranges, which will
+ be mutually non-intersecting.
+
+ Example:
+ <code>
+ -------------------
+ | -------|
+ | | ||
+ | --- | ||
+ | | | -------| --------
+ | | +--------- | | |
+ | --+ | | | |
+ | | | | --------
+ | ---------- |
+ -------------------
+ </code
+
+ Here, the outer rectangles represent the output
+ ranges. Contained are the input rectangles that comprise these
+ output ranges.
+
+ @tpl UserData
+ User data to be stored along with the range, to later identify
+ which range went into which connected component. Must be
+ assignable, default- and copy-constructible.
+ */
+ template< typename UserData > class B2DConnectedRanges
+ {
+ public:
+ /// Type of the basic entity (rect + user data)
+ typedef ::std::pair< B2DRange, UserData > ComponentType;
+ typedef ::std::list< ComponentType > ComponentListType;
+
+ /// List of (intersecting) components, plus overall bounds
+ struct ConnectedComponents
+ {
+ ComponentListType maComponentList;
+ B2DRange maTotalBounds;
+ };
+
+ typedef ::std::list< ConnectedComponents > ConnectedComponentsType;
+
+
+ /// Create the range calculator
+ B2DConnectedRanges() :
+ maDisjunctAggregatesList(),
+ maTotalBounds()
+ {
+ }
+
+ /** Query total bounds of all added ranges.
+
+ @return the union bound rect over all added ranges.
+ */
+ B2DRange getBounds() const
+ {
+ return maTotalBounds;
+ }
+
+ /** Add an additional range.
+
+ This method integrates a new range into the connected
+ ranges lists. The method has a worst-case time complexity
+ of O(n^2), with n denoting the number of already added
+ ranges (typically, for well-behaved input, it is O(n)
+ though).
+ */
+ void addRange( const B2DRange& rRange,
+ const UserData& rUserData )
+ {
+ // check whether fast path is possible: if new range is
+ // outside accumulated total range, can add it as a
+ // separate component right away.
+ const bool bNotOutsideEverything(
+ maTotalBounds.overlaps( rRange ) );
+
+ // update own global bounds range
+ maTotalBounds.expand( rRange );
+
+ // assemble anything intersecting with rRange into
+ // this new connected component
+ ConnectedComponents aNewConnectedComponent;
+
+ // as at least rRange will be a member of
+ // aNewConnectedComponent (will be added below), can
+ // preset the overall bounds here.
+ aNewConnectedComponent.maTotalBounds = rRange;
+
+
+ //
+ // STAGE 1: Search for intersecting maDisjunctAggregatesList entries
+ // =================================================================
+ //
+
+ // if rRange is empty, it will intersect with no
+ // maDisjunctAggregatesList member. Thus, we can safe us
+ // the check.
+ // if rRange is outside all other rectangle, skip here,
+ // too
+ if( bNotOutsideEverything &&
+ !rRange.isEmpty() )
+ {
+ typename ConnectedComponentsType::iterator aCurrAggregate;
+ typename ConnectedComponentsType::iterator aLastAggregate;
+
+ // flag, determining whether we touched one or more of
+ // the maDisjunctAggregatesList entries. _If_ we did,
+ // we have to repeat the intersection process, because
+ // these changes might have generated new
+ // intersections.
+ bool bSomeAggregatesChanged;
+
+ // loop, until bSomeAggregatesChanged stays false
+ do
+ {
+ // only continue loop if 'intersects' branch below was hit
+ bSomeAggregatesChanged = false;
+
+ // iterate over all current members of maDisjunctAggregatesList
+ for( aCurrAggregate=maDisjunctAggregatesList.begin(),
+ aLastAggregate=maDisjunctAggregatesList.end();
+ aCurrAggregate != aLastAggregate; )
+ {
+ // first check if current component's bounds
+ // are empty. This ensures that distinct empty
+ // components are not merged into one
+ // aggregate. As a matter of fact, they have
+ // no position and size.
+
+ if( !aCurrAggregate->maTotalBounds.isEmpty() &&
+ aCurrAggregate->maTotalBounds.overlaps(
+ aNewConnectedComponent.maTotalBounds ) )
+ {
+ // union the intersecting
+ // maDisjunctAggregatesList element into
+ // aNewConnectedComponent
+
+ // calc union bounding box
+ aNewConnectedComponent.maTotalBounds.expand( aCurrAggregate->maTotalBounds );
+
+ // extract all aCurrAggregate components
+ // to aNewConnectedComponent
+ aNewConnectedComponent.maComponentList.splice(
+ aNewConnectedComponent.maComponentList.end(),
+ aCurrAggregate->maComponentList );
+
+ // remove and delete aCurrAggregate entry
+ // from list (we've gutted it's content
+ // above). list::erase() will update our
+ // iterator with the predecessor here.
+ aCurrAggregate = maDisjunctAggregatesList.erase( aCurrAggregate );
+
+ // at least one aggregate changed, need to rescan everything
+ bSomeAggregatesChanged = true;
+ }
+ else
+ {
+ aCurrAggregate++;
+ }
+ }
+ }
+ while( bSomeAggregatesChanged );
+ }
+
+ //
+ // STAGE 2: Add newly generated connected component list element
+ // =============================================================
+ //
+
+ // add new component to the end of the component list
+ aNewConnectedComponent.maComponentList.push_back(
+ ComponentType( rRange, rUserData ) );
+
+ // do some consistency checks (aka post conditions)
+ OSL_ENSURE( !aNewConnectedComponent.maComponentList.empty(),
+ "B2DConnectedRanges::addRange(): empty aggregate list" );
+ OSL_ENSURE( !aNewConnectedComponent.maTotalBounds.isEmpty() ||
+ (aNewConnectedComponent.maTotalBounds.isEmpty() &&
+ aNewConnectedComponent.maComponentList.size() == 1),
+ "B2DConnectedRanges::addRange(): empty ranges must be solitary");
+
+ // add aNewConnectedComponent as a new entry to
+ // maDisjunctAggregatesList
+ maDisjunctAggregatesList.push_back( aNewConnectedComponent );
+ }
+
+ /** Apply a functor to each of the disjunct component
+ aggregates.
+
+ @param aFunctor
+ Functor to apply. Must provide an operator( const ConnectedComponents& ).
+
+ @return a copy of the functor, as applied to all aggregates.
+ */
+ template< typename UnaryFunctor > UnaryFunctor forEachAggregate( UnaryFunctor aFunctor ) const
+ {
+ return ::std::for_each( maDisjunctAggregatesList.begin(),
+ maDisjunctAggregatesList.end(),
+ aFunctor );
+ }
+
+ private:
+ // default: disabled copy/assignment
+ B2DConnectedRanges(const B2DConnectedRanges&);
+ B2DConnectedRanges& operator=( const B2DConnectedRanges& );
+
+ /** Current list of disjunct sets of connected components
+
+ Each entry corresponds to one of the top-level rectangles
+ in the drawing above.
+ */
+ ConnectedComponentsType maDisjunctAggregatesList;
+
+ /** Global bound rect over all added ranges.
+ */
+ B2DRange maTotalBounds;
+ };
+}
+
+#endif /* _BGFX_RANGE_B2DCONNECTEDRANGES_HXX */
diff --git a/basegfx/inc/basegfx/range/b2dmultirange.hxx b/basegfx/inc/basegfx/range/b2dmultirange.hxx
new file mode 100644
index 000000000000..d3a0259f0d27
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b2dmultirange.hxx
@@ -0,0 +1,117 @@
+/*************************************************************************
+ *
+ * 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.hxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B2DMULTIRANGE_HXX
+#define _BGFX_RANGE_B2DMULTIRANGE_HXX
+
+#include <o3tl/cow_wrapper.hxx>
+#include <memory>
+
+
+namespace basegfx
+{
+ class B2DTuple;
+ class B2DRange;
+ class B2DPolyPolygon;
+ class ImplB2DMultiRange;
+
+ /** Multiple ranges in one object.
+
+ This class combines multiple ranges in one object, providing a
+ total, enclosing range for it.
+
+ You can use this class e.g. when updating views containing
+ rectangular objects. Add each modified object to a
+ B2DMultiRange, then test each viewable object against
+ intersection with the multi range.
+ */
+ class B2DMultiRange
+ {
+ public:
+ B2DMultiRange();
+ ~B2DMultiRange();
+
+ /** Create a multi range with exactly one containing range
+ */
+ explicit B2DMultiRange( const B2DRange& rRange );
+
+ B2DMultiRange( const B2DMultiRange& );
+ B2DMultiRange& operator=( const B2DMultiRange& );
+
+ /** Check whether range is empty.
+
+ @return true, if this object either contains no ranges at
+ all, or all contained ranges are empty.
+ */
+ bool isEmpty() const;
+
+ /** Reset to empty.
+
+ After this call, the object will not contain any ranges,
+ and isEmpty() will return true.
+ */
+ void reset();
+
+ /** Test whether given tuple is inside one or more of the
+ included ranges.
+ */
+ bool isInside( const B2DTuple& rTuple ) const;
+
+ /** Test whether given range is inside one or more of the
+ included ranges.
+ */
+ bool isInside( const B2DRange& rRange ) const;
+
+ /** Test whether given range overlaps one or more of the
+ included ranges.
+ */
+ bool overlaps( const B2DRange& rRange ) const;
+
+ /** Add given range to the number of contained ranges.
+ */
+ void addRange( const B2DRange& rRange );
+
+ /** Get overall bound rect for all included ranges.
+ */
+ B2DRange getBounds() const;
+
+ /** Request poly-polygon representing the added ranges.
+
+ This method creates a poly-polygon, consisting exactly out
+ of the contained ranges.
+ */
+ B2DPolyPolygon getPolyPolygon() const;
+
+ private:
+ o3tl::cow_wrapper< ImplB2DMultiRange > mpImpl;
+ };
+}
+
+#endif /* _BGFX_RANGE_B2DMULTIRANGE_HXX */
diff --git a/basegfx/inc/basegfx/range/b2drange.hxx b/basegfx/inc/basegfx/range/b2drange.hxx
new file mode 100644
index 000000000000..8a70d4782f47
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b2drange.hxx
@@ -0,0 +1,297 @@
+/*************************************************************************
+ *
+ * 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: b2drange.hxx,v $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B2DRANGE_HXX
+#define _BGFX_RANGE_B2DRANGE_HXX
+
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <basegfx/range/basicrange.hxx>
+#include <vector>
+
+
+namespace basegfx
+{
+ // predeclarations
+ class B2IRange;
+ class B2DHomMatrix;
+
+ class B2DRange
+ {
+ public:
+ typedef double ValueType;
+ typedef DoubleTraits TraitsType;
+
+ B2DRange()
+ {
+ }
+
+ explicit B2DRange(const B2DTuple& rTuple)
+ : maRangeX(rTuple.getX()),
+ maRangeY(rTuple.getY())
+ {
+ }
+
+ B2DRange(double x1,
+ double y1,
+ double x2,
+ double y2)
+ : maRangeX(x1),
+ maRangeY(y1)
+ {
+ maRangeX.expand(x2);
+ maRangeY.expand(y2);
+ }
+
+ B2DRange(const B2DTuple& rTuple1,
+ const B2DTuple& rTuple2)
+ : maRangeX(rTuple1.getX()),
+ maRangeY(rTuple1.getY())
+ {
+ expand( rTuple2 );
+ }
+
+ B2DRange(const B2DRange& rRange)
+ : maRangeX(rRange.maRangeX),
+ maRangeY(rRange.maRangeY)
+ {
+ }
+
+ explicit B2DRange(const B2IRange& rRange);
+
+ bool isEmpty() const
+ {
+ return (
+ maRangeX.isEmpty()
+ || maRangeY.isEmpty()
+ );
+ }
+
+ void reset()
+ {
+ maRangeX.reset();
+ maRangeY.reset();
+ }
+
+ bool operator==( const B2DRange& rRange ) const
+ {
+ return (maRangeX == rRange.maRangeX
+ && maRangeY == rRange.maRangeY);
+ }
+
+ bool operator!=( const B2DRange& rRange ) const
+ {
+ return (maRangeX != rRange.maRangeX
+ || maRangeY != rRange.maRangeY);
+ }
+
+ B2DRange& operator=(const B2DRange& rRange)
+ {
+ maRangeX = rRange.maRangeX;
+ maRangeY = rRange.maRangeY;
+ return *this;
+ }
+
+ bool equal(const B2DRange& rRange) const
+ {
+ return (maRangeX.equal(rRange.maRangeX)
+ && maRangeY.equal(rRange.maRangeY));
+ }
+
+ double getMinX() const
+ {
+ return maRangeX.getMinimum();
+ }
+
+ double getMinY() const
+ {
+ return maRangeY.getMinimum();
+ }
+
+ double getMaxX() const
+ {
+ return maRangeX.getMaximum();
+ }
+
+ double getMaxY() const
+ {
+ return maRangeY.getMaximum();
+ }
+
+ double getWidth() const
+ {
+ return maRangeX.getRange();
+ }
+
+ double getHeight() const
+ {
+ return maRangeY.getRange();
+ }
+
+ B2DPoint getMinimum() const
+ {
+ return B2DPoint(
+ maRangeX.getMinimum(),
+ maRangeY.getMinimum()
+ );
+ }
+
+ B2DPoint getMaximum() const
+ {
+ return B2DPoint(
+ maRangeX.getMaximum(),
+ maRangeY.getMaximum()
+ );
+ }
+
+ B2DVector getRange() const
+ {
+ return B2DVector(
+ maRangeX.getRange(),
+ maRangeY.getRange()
+ );
+ }
+
+ B2DPoint getCenter() const
+ {
+ return B2DPoint(
+ maRangeX.getCenter(),
+ maRangeY.getCenter()
+ );
+ }
+
+ double getCenterX() const
+ {
+ return maRangeX.getCenter();
+ }
+
+ double getCenterY() const
+ {
+ return maRangeY.getCenter();
+ }
+
+ bool isInside(const B2DTuple& rTuple) const
+ {
+ return (
+ maRangeX.isInside(rTuple.getX())
+ && maRangeY.isInside(rTuple.getY())
+ );
+ }
+
+ bool isInside(const B2DRange& rRange) const
+ {
+ return (
+ maRangeX.isInside(rRange.maRangeX)
+ && maRangeY.isInside(rRange.maRangeY)
+ );
+ }
+
+ bool overlaps(const B2DRange& rRange) const
+ {
+ return (
+ maRangeX.overlaps(rRange.maRangeX)
+ && maRangeY.overlaps(rRange.maRangeY)
+ );
+ }
+
+ bool overlapsMore(const B2DRange& rRange) const
+ {
+ return (
+ maRangeX.overlapsMore(rRange.maRangeX)
+ && maRangeY.overlapsMore(rRange.maRangeY)
+ );
+ }
+
+ void expand(const B2DTuple& rTuple)
+ {
+ maRangeX.expand(rTuple.getX());
+ maRangeY.expand(rTuple.getY());
+ }
+
+ void expand(const B2DRange& rRange)
+ {
+ maRangeX.expand(rRange.maRangeX);
+ maRangeY.expand(rRange.maRangeY);
+ }
+
+ void intersect(const B2DRange& rRange)
+ {
+ maRangeX.intersect(rRange.maRangeX);
+ maRangeY.intersect(rRange.maRangeY);
+ }
+
+ void grow(double fValue)
+ {
+ maRangeX.grow(fValue);
+ maRangeY.grow(fValue);
+ }
+
+ void transform(const B2DHomMatrix& rMatrix);
+
+ private:
+ typedef ::basegfx::BasicRange< ValueType, TraitsType > MyBasicRange;
+
+ MyBasicRange maRangeX;
+ MyBasicRange maRangeY;
+ };
+
+ /** Round double to nearest integer for 2D range
+
+ @return the nearest integer for this range
+ */
+ B2IRange fround(const B2DRange& rRange);
+
+ /** Compute the set difference of the two given ranges
+
+ This method calculates the symmetric difference (aka XOR)
+ between the two given ranges, and returning the resulting
+ ranges. Thus, the result will contain all areas where one, but
+ not both ranges lie.
+
+ @param o_rResult
+ Result vector. The up to four difference ranges are returned
+ within this vector
+
+ @param rFirst
+ The first range
+
+ @param rSecond
+ The second range
+
+ @return the input vector
+ */
+ ::std::vector< B2DRange >& computeSetDifference( ::std::vector< B2DRange >& o_rResult,
+ const B2DRange& rFirst,
+ const B2DRange& rSecond );
+
+} // end of namespace basegfx
+
+
+#endif /* _BGFX_RANGE_B2DRANGE_HXX */
diff --git a/basegfx/inc/basegfx/range/b2drectangle.hxx b/basegfx/inc/basegfx/range/b2drectangle.hxx
new file mode 100644
index 000000000000..b1f385e504a4
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b2drectangle.hxx
@@ -0,0 +1,45 @@
+/*************************************************************************
+ *
+ * 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: b2drectangle.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B2DRECTANGLE_HXX
+#define _BGFX_RANGE_B2DRECTANGLE_HXX
+
+#include <basegfx/range/b2drange.hxx>
+
+namespace basegfx
+{
+ // syntactic sugar: a B2DRange exactly models a Rectangle, thus,
+ // for interface clarity, we provide an alias name
+
+ /// Alias name for interface clarity (not everybody is aware of the identity)
+ typedef B2DRange B2DRectangle;
+}
+
+#endif /* _BGFX_RANGE_B2DRECTANGLE_HXX */
diff --git a/basegfx/inc/basegfx/range/b2ibox.hxx b/basegfx/inc/basegfx/range/b2ibox.hxx
new file mode 100644
index 000000000000..24a3d19f5ea1
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b2ibox.hxx
@@ -0,0 +1,254 @@
+/*************************************************************************
+ *
+ * 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: b2ibox.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B2IBOX_HXX
+#define _BGFX_RANGE_B2IBOX_HXX
+
+#include <basegfx/point/b2ipoint.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/tuple/b2ituple.hxx>
+#include <basegfx/tuple/b2i64tuple.hxx>
+#include <basegfx/range/basicbox.hxx>
+#include <vector>
+
+
+namespace basegfx
+{
+ class B2IBox
+ {
+ public:
+ typedef sal_Int32 ValueType;
+ typedef Int32Traits TraitsType;
+
+ B2IBox()
+ {
+ }
+
+ explicit B2IBox(const B2ITuple& rTuple)
+ : maRangeX(rTuple.getX()),
+ maRangeY(rTuple.getY())
+ {
+ }
+
+ B2IBox(sal_Int32 x1,
+ sal_Int32 y1,
+ sal_Int32 x2,
+ sal_Int32 y2) :
+ maRangeX(x1),
+ maRangeY(y1)
+ {
+ maRangeX.expand(x2);
+ maRangeY.expand(y2);
+ }
+
+ B2IBox(const B2ITuple& rTuple1,
+ const B2ITuple& rTuple2) :
+ maRangeX(rTuple1.getX()),
+ maRangeY(rTuple1.getY())
+ {
+ expand( rTuple2 );
+ }
+
+ B2IBox(const B2IBox& rBox) :
+ maRangeX(rBox.maRangeX),
+ maRangeY(rBox.maRangeY)
+ {
+ }
+
+ bool isEmpty() const
+ {
+ return maRangeX.isEmpty() || maRangeY.isEmpty();
+ }
+
+ void reset()
+ {
+ maRangeX.reset();
+ maRangeY.reset();
+ }
+
+ bool operator==( const B2IBox& rBox ) const
+ {
+ return (maRangeX == rBox.maRangeX
+ && maRangeY == rBox.maRangeY);
+ }
+
+ bool operator!=( const B2IBox& rBox ) const
+ {
+ return (maRangeX != rBox.maRangeX
+ || maRangeY != rBox.maRangeY);
+ }
+
+ void operator=(const B2IBox& rBox)
+ {
+ maRangeX = rBox.maRangeX;
+ maRangeY = rBox.maRangeY;
+ }
+
+ sal_Int32 getMinX() const
+ {
+ return maRangeX.getMinimum();
+ }
+
+ sal_Int32 getMinY() const
+ {
+ return maRangeY.getMinimum();
+ }
+
+ sal_Int32 getMaxX() const
+ {
+ return maRangeX.getMaximum();
+ }
+
+ sal_Int32 getMaxY() const
+ {
+ return maRangeY.getMaximum();
+ }
+
+ sal_Int64 getWidth() const
+ {
+ return maRangeX.getRange();
+ }
+
+ sal_Int64 getHeight() const
+ {
+ return maRangeY.getRange();
+ }
+
+ B2IPoint getMinimum() const
+ {
+ return B2IPoint(
+ maRangeX.getMinimum(),
+ maRangeY.getMinimum()
+ );
+ }
+
+ B2IPoint getMaximum() const
+ {
+ return B2IPoint(
+ maRangeX.getMaximum(),
+ maRangeY.getMaximum()
+ );
+ }
+
+ B2I64Tuple getRange() const
+ {
+ return B2I64Tuple(
+ maRangeX.getRange(),
+ maRangeY.getRange()
+ );
+ }
+
+ B2DPoint getCenter() const
+ {
+ return B2DPoint(
+ maRangeX.getCenter(),
+ maRangeY.getCenter()
+ );
+ }
+
+ bool isInside(const B2ITuple& rTuple) const
+ {
+ return (
+ maRangeX.isInside(rTuple.getX())
+ && maRangeY.isInside(rTuple.getY())
+ );
+ }
+
+ bool isInside(const B2IBox& rBox) const
+ {
+ return (
+ maRangeX.isInside(rBox.maRangeX)
+ && maRangeY.isInside(rBox.maRangeY)
+ );
+ }
+
+ bool overlaps(const B2IBox& rBox) const
+ {
+ return (
+ maRangeX.overlaps(rBox.maRangeX)
+ && maRangeY.overlaps(rBox.maRangeY)
+ );
+ }
+
+ void expand(const B2ITuple& rTuple)
+ {
+ maRangeX.expand(rTuple.getX());
+ maRangeY.expand(rTuple.getY());
+ }
+
+ void expand(const B2IBox& rBox)
+ {
+ maRangeX.expand(rBox.maRangeX);
+ maRangeY.expand(rBox.maRangeY);
+ }
+
+ void intersect(const B2IBox& rBox)
+ {
+ maRangeX.intersect(rBox.maRangeX);
+ maRangeY.intersect(rBox.maRangeY);
+ }
+
+ void grow(sal_Int32 nValue)
+ {
+ maRangeX.grow(nValue);
+ maRangeY.grow(nValue);
+ }
+
+ private:
+ BasicBox maRangeX;
+ BasicBox maRangeY;
+ };
+
+ /** Compute the set difference of the two given boxes
+
+ This method calculates the symmetric difference (aka XOR)
+ between the two given boxes, and returning the resulting
+ boxes. Thus, the result will contain all areas where one, but
+ not both boxes lie.
+
+ @param o_rResult
+ Result vector. The up to four difference boxes are returned
+ within this vector
+
+ @param rFirst
+ The first box
+
+ @param rSecond
+ The second box
+
+ @return the input vector
+ */
+ ::std::vector< B2IBox >& computeSetDifference( ::std::vector< B2IBox >& o_rResult,
+ const B2IBox& rFirst,
+ const B2IBox& rSecond );
+
+} // end of namespace basegfx
+
+#endif /* _BGFX_RANGE_B2IBOX_HXX */
diff --git a/basegfx/inc/basegfx/range/b2irange.hxx b/basegfx/inc/basegfx/range/b2irange.hxx
new file mode 100644
index 000000000000..282ff20249bf
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b2irange.hxx
@@ -0,0 +1,257 @@
+/*************************************************************************
+ *
+ * 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: b2irange.hxx,v $
+ * $Revision: 1.11 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B2IRANGE_HXX
+#define _BGFX_RANGE_B2IRANGE_HXX
+
+#include <basegfx/point/b2ipoint.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/tuple/b2ituple.hxx>
+#include <basegfx/tuple/b2i64tuple.hxx>
+#include <basegfx/range/basicrange.hxx>
+#include <vector>
+
+
+namespace basegfx
+{
+ class B2IRange
+ {
+ public:
+ typedef sal_Int32 ValueType;
+ typedef Int32Traits TraitsType;
+
+ B2IRange()
+ {
+ }
+
+ explicit B2IRange(const B2ITuple& rTuple)
+ : maRangeX(rTuple.getX()),
+ maRangeY(rTuple.getY())
+ {
+ }
+
+ B2IRange(sal_Int32 x1,
+ sal_Int32 y1,
+ sal_Int32 x2,
+ sal_Int32 y2)
+ : maRangeX(x1),
+ maRangeY(y1)
+ {
+ maRangeX.expand(x2);
+ maRangeY.expand(y2);
+ }
+
+ B2IRange(const B2ITuple& rTuple1,
+ const B2ITuple& rTuple2)
+ : maRangeX(rTuple1.getX()),
+ maRangeY(rTuple1.getY())
+ {
+ expand( rTuple2 );
+ }
+
+ B2IRange(const B2IRange& rRange)
+ : maRangeX(rRange.maRangeX),
+ maRangeY(rRange.maRangeY)
+ {
+ }
+
+ bool isEmpty() const
+ {
+ return maRangeX.isEmpty() || maRangeY.isEmpty();
+ }
+
+ void reset()
+ {
+ maRangeX.reset();
+ maRangeY.reset();
+ }
+
+ bool operator==( const B2IRange& rRange ) const
+ {
+ return (maRangeX == rRange.maRangeX
+ && maRangeY == rRange.maRangeY);
+ }
+
+ bool operator!=( const B2IRange& rRange ) const
+ {
+ return (maRangeX != rRange.maRangeX
+ || maRangeY != rRange.maRangeY);
+ }
+
+ B2IRange& operator=(const B2IRange& rRange)
+ {
+ maRangeX = rRange.maRangeX;
+ maRangeY = rRange.maRangeY;
+ return *this;
+ }
+
+ sal_Int32 getMinX() const
+ {
+ return maRangeX.getMinimum();
+ }
+
+ sal_Int32 getMinY() const
+ {
+ return maRangeY.getMinimum();
+ }
+
+ sal_Int32 getMaxX() const
+ {
+ return maRangeX.getMaximum();
+ }
+
+ sal_Int32 getMaxY() const
+ {
+ return maRangeY.getMaximum();
+ }
+
+ sal_Int64 getWidth() const
+ {
+ return maRangeX.getRange();
+ }
+
+ sal_Int64 getHeight() const
+ {
+ return maRangeY.getRange();
+ }
+
+ B2IPoint getMinimum() const
+ {
+ return B2IPoint(
+ maRangeX.getMinimum(),
+ maRangeY.getMinimum()
+ );
+ }
+
+ B2IPoint getMaximum() const
+ {
+ return B2IPoint(
+ maRangeX.getMaximum(),
+ maRangeY.getMaximum()
+ );
+ }
+
+ B2I64Tuple getRange() const
+ {
+ return B2I64Tuple(
+ maRangeX.getRange(),
+ maRangeY.getRange()
+ );
+ }
+
+ B2DPoint getCenter() const
+ {
+ return B2DPoint(
+ maRangeX.getCenter(),
+ maRangeY.getCenter()
+ );
+ }
+
+ bool isInside(const B2ITuple& rTuple) const
+ {
+ return (
+ maRangeX.isInside(rTuple.getX())
+ && maRangeY.isInside(rTuple.getY())
+ );
+ }
+
+ bool isInside(const B2IRange& rRange) const
+ {
+ return (
+ maRangeX.isInside(rRange.maRangeX)
+ && maRangeY.isInside(rRange.maRangeY)
+ );
+ }
+
+ bool overlaps(const B2IRange& rRange) const
+ {
+ return (
+ maRangeX.overlaps(rRange.maRangeX)
+ && maRangeY.overlaps(rRange.maRangeY)
+ );
+ }
+
+ void expand(const B2ITuple& rTuple)
+ {
+ maRangeX.expand(rTuple.getX());
+ maRangeY.expand(rTuple.getY());
+ }
+
+ void expand(const B2IRange& rRange)
+ {
+ maRangeX.expand(rRange.maRangeX);
+ maRangeY.expand(rRange.maRangeY);
+ }
+
+ void intersect(const B2IRange& rRange)
+ {
+ maRangeX.intersect(rRange.maRangeX);
+ maRangeY.intersect(rRange.maRangeY);
+ }
+
+ void grow(sal_Int32 nValue)
+ {
+ maRangeX.grow(nValue);
+ maRangeY.grow(nValue);
+ }
+
+ private:
+ typedef ::basegfx::BasicRange< ValueType, TraitsType > MyBasicRange;
+
+ MyBasicRange maRangeX;
+ MyBasicRange maRangeY;
+ };
+
+ /** Compute the set difference of the two given ranges
+
+ This method calculates the symmetric difference (aka XOR)
+ between the two given ranges, and returning the resulting
+ ranges. Thus, the result will contain all areas where one, but
+ not both ranges lie.
+
+ @param o_rResult
+ Result vector. The up to four difference ranges are returned
+ within this vector
+
+ @param rFirst
+ The first range
+
+ @param rSecond
+ The second range
+
+ @return the input vector
+ */
+ ::std::vector< B2IRange >& computeSetDifference( ::std::vector< B2IRange >& o_rResult,
+ const B2IRange& rFirst,
+ const B2IRange& rSecond );
+
+} // end of namespace basegfx
+
+#endif /* _BGFX_RANGE_B2IRANGE_HXX */
diff --git a/basegfx/inc/basegfx/range/b2irectangle.hxx b/basegfx/inc/basegfx/range/b2irectangle.hxx
new file mode 100644
index 000000000000..d4f669855a4b
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b2irectangle.hxx
@@ -0,0 +1,45 @@
+/*************************************************************************
+ *
+ * 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: b2irectangle.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B2IRECTANGLE_HXX
+#define _BGFX_RANGE_B2IRECTANGLE_HXX
+
+#include <basegfx/range/b2irange.hxx>
+
+namespace basegfx
+{
+ // syntactic sugar: a B2IRange exactly models a Rectangle, thus,
+ // for interface clarity, we provide an alias name
+
+ /// Alias name for interface clarity (not everybody is aware of the identity)
+ typedef B2IRange B2IRectangle;
+}
+
+#endif /* _BGFX_RANGE_B2IRECTANGLE_HXX */
diff --git a/basegfx/inc/basegfx/range/b3drange.hxx b/basegfx/inc/basegfx/range/b3drange.hxx
new file mode 100644
index 000000000000..dfd41b4f0f39
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b3drange.hxx
@@ -0,0 +1,305 @@
+/*************************************************************************
+ *
+ * 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: b3drange.hxx,v $
+ * $Revision: 1.17 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B3DRANGE_HXX
+#define _BGFX_RANGE_B3DRANGE_HXX
+
+#include <basegfx/vector/b3dvector.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <basegfx/tuple/b3dtuple.hxx>
+#include <basegfx/range/basicrange.hxx>
+
+namespace basegfx
+{
+ // predeclarations
+ class B3IRange;
+ class B3DHomMatrix;
+
+ class B3DRange
+ {
+ typedef ::basegfx::BasicRange< double, DoubleTraits > MyBasicRange;
+
+ MyBasicRange maRangeX;
+ MyBasicRange maRangeY;
+ MyBasicRange maRangeZ;
+
+ public:
+ B3DRange()
+ {
+ }
+
+ explicit B3DRange(const B3DTuple& rTuple)
+ : maRangeX(rTuple.getX()),
+ maRangeY(rTuple.getY()),
+ maRangeZ(rTuple.getZ())
+ {
+ }
+
+ B3DRange(double x1,
+ double y1,
+ double z1,
+ double x2,
+ double y2,
+ double z2)
+ : maRangeX(x1),
+ maRangeY(y1),
+ maRangeZ(z1)
+ {
+ maRangeX.expand(x2);
+ maRangeY.expand(y2);
+ maRangeZ.expand(z2);
+ }
+
+ B3DRange(const B3DTuple& rTuple1,
+ const B3DTuple& rTuple2)
+ : maRangeX(rTuple1.getX()),
+ maRangeY(rTuple1.getY()),
+ maRangeZ(rTuple1.getZ())
+ {
+ expand(rTuple2);
+ }
+
+ B3DRange(const B3DRange& rRange)
+ : maRangeX(rRange.maRangeX),
+ maRangeY(rRange.maRangeY),
+ maRangeZ(rRange.maRangeZ)
+ {
+ }
+
+ explicit B3DRange(const B3IRange& rRange);
+
+ bool isEmpty() const
+ {
+ return (
+ maRangeX.isEmpty()
+ || maRangeY.isEmpty()
+ || maRangeZ.isEmpty()
+ );
+ }
+
+ void reset()
+ {
+ maRangeX.reset();
+ maRangeY.reset();
+ maRangeZ.reset();
+ }
+
+ bool operator==( const B3DRange& rRange ) const
+ {
+ return (maRangeX == rRange.maRangeX
+ && maRangeY == rRange.maRangeY
+ && maRangeZ == rRange.maRangeZ);
+ }
+
+ bool operator!=( const B3DRange& rRange ) const
+ {
+ return (maRangeX != rRange.maRangeX
+ || maRangeY != rRange.maRangeY
+ || maRangeZ != rRange.maRangeZ);
+ }
+
+ B3DRange& operator=(const B3DRange& rRange)
+ {
+ maRangeX = rRange.maRangeX;
+ maRangeY = rRange.maRangeY;
+ maRangeZ = rRange.maRangeZ;
+ return *this;
+ }
+
+ bool equal(const B3DRange& rRange) const
+ {
+ return (maRangeX.equal(rRange.maRangeX)
+ && maRangeY.equal(rRange.maRangeY)
+ && maRangeZ.equal(rRange.maRangeZ));
+ }
+
+ double getMinX() const
+ {
+ return maRangeX.getMinimum();
+ }
+
+ double getMinY() const
+ {
+ return maRangeY.getMinimum();
+ }
+
+ double getMinZ() const
+ {
+ return maRangeZ.getMinimum();
+ }
+
+ double getMaxX() const
+ {
+ return maRangeX.getMaximum();
+ }
+
+ double getMaxY() const
+ {
+ return maRangeY.getMaximum();
+ }
+
+ double getMaxZ() const
+ {
+ return maRangeZ.getMaximum();
+ }
+
+ double getWidth() const
+ {
+ return maRangeX.getRange();
+ }
+
+ double getHeight() const
+ {
+ return maRangeY.getRange();
+ }
+
+ double getDepth() const
+ {
+ return maRangeZ.getRange();
+ }
+
+ B3DPoint getMinimum() const
+ {
+ return B3DPoint(
+ maRangeX.getMinimum(),
+ maRangeY.getMinimum(),
+ maRangeZ.getMinimum()
+ );
+ }
+
+ B3DPoint getMaximum() const
+ {
+ return B3DPoint(
+ maRangeX.getMaximum(),
+ maRangeY.getMaximum(),
+ maRangeZ.getMaximum()
+ );
+ }
+
+ B3DVector getRange() const
+ {
+ return B3DVector(
+ maRangeX.getRange(),
+ maRangeY.getRange(),
+ maRangeZ.getRange()
+ );
+ }
+
+ B3DPoint getCenter() const
+ {
+ return B3DPoint(
+ maRangeX.getCenter(),
+ maRangeY.getCenter(),
+ maRangeZ.getCenter()
+ );
+ }
+
+ double getCenterX() const
+ {
+ return maRangeX.getCenter();
+ }
+
+ double getCenterY() const
+ {
+ return maRangeY.getCenter();
+ }
+
+ double getCenterZ() const
+ {
+ return maRangeZ.getCenter();
+ }
+
+ bool isInside(const B3DTuple& rTuple) const
+ {
+ return (
+ maRangeX.isInside(rTuple.getX())
+ && maRangeY.isInside(rTuple.getY())
+ && maRangeZ.isInside(rTuple.getZ())
+ );
+ }
+
+ bool isInside(const B3DRange& rRange) const
+ {
+ return (
+ maRangeX.isInside(rRange.maRangeX)
+ && maRangeY.isInside(rRange.maRangeY)
+ && maRangeZ.isInside(rRange.maRangeZ)
+ );
+ }
+
+ bool overlaps(const B3DRange& rRange) const
+ {
+ return (
+ maRangeX.overlaps(rRange.maRangeX)
+ && maRangeY.overlaps(rRange.maRangeY)
+ && maRangeZ.overlaps(rRange.maRangeZ)
+ );
+ }
+
+ void expand(const B3DTuple& rTuple)
+ {
+ maRangeX.expand(rTuple.getX());
+ maRangeY.expand(rTuple.getY());
+ maRangeZ.expand(rTuple.getZ());
+ }
+
+ void expand(const B3DRange& rRange)
+ {
+ maRangeX.expand(rRange.maRangeX);
+ maRangeY.expand(rRange.maRangeY);
+ maRangeZ.expand(rRange.maRangeZ);
+ }
+
+ void intersect(const B3DRange& rRange)
+ {
+ maRangeX.intersect(rRange.maRangeX);
+ maRangeY.intersect(rRange.maRangeY);
+ maRangeZ.intersect(rRange.maRangeZ);
+ }
+
+ void grow(double fValue)
+ {
+ maRangeX.grow(fValue);
+ maRangeY.grow(fValue);
+ maRangeZ.grow(fValue);
+ }
+
+ void transform(const B3DHomMatrix& rMatrix);
+ };
+
+ /** Round double to nearest integer for 3D range
+
+ @return the nearest integer for this range
+ */
+ B3IRange fround(const B3DRange& rRange);
+} // end of namespace basegfx
+
+
+#endif /* _BGFX_RANGE_B3DRANGE_HXX */
diff --git a/basegfx/inc/basegfx/range/b3dvolume.hxx b/basegfx/inc/basegfx/range/b3dvolume.hxx
new file mode 100644
index 000000000000..836af3a3ef08
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b3dvolume.hxx
@@ -0,0 +1,45 @@
+/*************************************************************************
+ *
+ * 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: b3dvolume.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B3DBOX_HXX
+#define _BGFX_RANGE_B3DBOX_HXX
+
+#include <basegfx/range/b3drange.hxx>
+
+namespace basegfx
+{
+ // syntactic sugar: a B3DRange exactly models a Volume in 3D, thus,
+ // for interface clarity, we provide an alias name
+
+ /// Alias name for interface clarity (not everybody is aware of the identity)
+ typedef B3DRange B3DVolume;
+}
+
+#endif /* _BGFX_RANGE_B3DBOX_HXX */
diff --git a/basegfx/inc/basegfx/range/b3ibox.hxx b/basegfx/inc/basegfx/range/b3ibox.hxx
new file mode 100644
index 000000000000..35b11512962f
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b3ibox.hxx
@@ -0,0 +1,262 @@
+/*************************************************************************
+ *
+ * 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: b3ibox.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B3IBOX_HXX
+#define _BGFX_RANGE_B3IBOX_HXX
+
+#include <basegfx/point/b3ipoint.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <basegfx/tuple/b3ituple.hxx>
+#include <basegfx/tuple/b3i64tuple.hxx>
+#include <basegfx/range/basicbox.hxx>
+
+namespace basegfx
+{
+ class B3IBox
+ {
+ BasicBox maRangeX;
+ BasicBox maRangeY;
+ BasicBox maRangeZ;
+
+ public:
+ B3IBox()
+ {
+ }
+
+ explicit B3IBox(const B3ITuple& rTuple) :
+ maRangeX(rTuple.getX()),
+ maRangeY(rTuple.getY()),
+ maRangeZ(rTuple.getZ())
+ {
+ }
+
+ B3IBox(sal_Int32 x1,
+ sal_Int32 y1,
+ sal_Int32 z1,
+ sal_Int32 x2,
+ sal_Int32 y2,
+ sal_Int32 z2) :
+ maRangeX(x1),
+ maRangeY(y1),
+ maRangeZ(z1)
+ {
+ maRangeX.expand(x2);
+ maRangeY.expand(y2);
+ maRangeZ.expand(z2);
+ }
+
+ B3IBox(const B3ITuple& rTuple1,
+ const B3ITuple& rTuple2) :
+ maRangeX(rTuple1.getX()),
+ maRangeY(rTuple1.getY()),
+ maRangeZ(rTuple1.getZ())
+ {
+ expand(rTuple2);
+ }
+
+ B3IBox(const B3IBox& rBox) :
+ maRangeX(rBox.maRangeX),
+ maRangeY(rBox.maRangeY),
+ maRangeZ(rBox.maRangeZ)
+ {
+ }
+
+ bool isEmpty() const
+ {
+ return maRangeX.isEmpty() || maRangeY.isEmpty() || maRangeZ.isEmpty();
+ }
+
+ void reset()
+ {
+ maRangeX.reset();
+ maRangeY.reset();
+ maRangeZ.reset();
+ }
+
+ bool operator==( const B3IBox& rBox ) const
+ {
+ return (maRangeX == rBox.maRangeX
+ && maRangeY == rBox.maRangeY
+ && maRangeZ == rBox.maRangeZ);
+ }
+
+ bool operator!=( const B3IBox& rBox ) const
+ {
+ return (maRangeX != rBox.maRangeX
+ || maRangeY != rBox.maRangeY
+ || maRangeZ != rBox.maRangeZ);
+ }
+
+ void operator=(const B3IBox& rBox)
+ {
+ maRangeX = rBox.maRangeX;
+ maRangeY = rBox.maRangeY;
+ maRangeZ = rBox.maRangeZ;
+ }
+
+ sal_Int32 getMinX() const
+ {
+ return maRangeX.getMinimum();
+ }
+
+ sal_Int32 getMinY() const
+ {
+ return maRangeY.getMinimum();
+ }
+
+ sal_Int32 getMinZ() const
+ {
+ return maRangeZ.getMinimum();
+ }
+
+ sal_Int32 getMaxX() const
+ {
+ return maRangeX.getMaximum();
+ }
+
+ sal_Int32 getMaxY() const
+ {
+ return maRangeY.getMaximum();
+ }
+
+ sal_Int32 getMaxZ() const
+ {
+ return maRangeZ.getMaximum();
+ }
+
+ sal_Int64 getWidth() const
+ {
+ return maRangeX.getRange();
+ }
+
+ sal_Int64 getHeight() const
+ {
+ return maRangeY.getRange();
+ }
+
+ sal_Int64 getDepth() const
+ {
+ return maRangeZ.getRange();
+ }
+
+ B3IPoint getMinimum() const
+ {
+ return B3IPoint(
+ maRangeX.getMinimum(),
+ maRangeY.getMinimum(),
+ maRangeZ.getMinimum()
+ );
+ }
+
+ B3IPoint getMaximum() const
+ {
+ return B3IPoint(
+ maRangeX.getMaximum(),
+ maRangeY.getMaximum(),
+ maRangeZ.getMaximum()
+ );
+ }
+
+ B3I64Tuple getRange() const
+ {
+ return B3I64Tuple(
+ maRangeX.getRange(),
+ maRangeY.getRange(),
+ maRangeZ.getRange()
+ );
+ }
+
+ B3DPoint getCenter() const
+ {
+ return B3DPoint(
+ maRangeX.getCenter(),
+ maRangeY.getCenter(),
+ maRangeZ.getCenter()
+ );
+ }
+
+ bool isInside(const B3ITuple& rTuple) const
+ {
+ return (
+ maRangeX.isInside(rTuple.getX())
+ && maRangeY.isInside(rTuple.getY())
+ && maRangeZ.isInside(rTuple.getZ())
+ );
+ }
+
+ bool isInside(const B3IBox& rBox) const
+ {
+ return (
+ maRangeX.isInside(rBox.maRangeX)
+ && maRangeY.isInside(rBox.maRangeY)
+ && maRangeZ.isInside(rBox.maRangeZ)
+ );
+ }
+
+ bool overlaps(const B3IBox& rBox) const
+ {
+ return (
+ maRangeX.overlaps(rBox.maRangeX)
+ && maRangeY.overlaps(rBox.maRangeY)
+ && maRangeZ.overlaps(rBox.maRangeZ)
+ );
+ }
+
+ void expand(const B3ITuple& rTuple)
+ {
+ maRangeX.expand(rTuple.getX());
+ maRangeY.expand(rTuple.getY());
+ maRangeZ.expand(rTuple.getZ());
+ }
+
+ void expand(const B3IBox& rBox)
+ {
+ maRangeX.expand(rBox.maRangeX);
+ maRangeY.expand(rBox.maRangeY);
+ maRangeZ.expand(rBox.maRangeZ);
+ }
+
+ void intersect(const B3IBox& rBox)
+ {
+ maRangeX.intersect(rBox.maRangeX);
+ maRangeY.intersect(rBox.maRangeY);
+ maRangeZ.intersect(rBox.maRangeZ);
+ }
+
+ void grow(sal_Int32 nValue)
+ {
+ maRangeX.grow(nValue);
+ maRangeY.grow(nValue);
+ maRangeZ.grow(nValue);
+ }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_RANGE_B3IBOX_HXX */
diff --git a/basegfx/inc/basegfx/range/b3irange.hxx b/basegfx/inc/basegfx/range/b3irange.hxx
new file mode 100644
index 000000000000..770bb509182c
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b3irange.hxx
@@ -0,0 +1,265 @@
+/*************************************************************************
+ *
+ * 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: b3irange.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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B3IRANGE_HXX
+#define _BGFX_RANGE_B3IRANGE_HXX
+
+#include <basegfx/point/b3ipoint.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <basegfx/tuple/b3ituple.hxx>
+#include <basegfx/tuple/b3i64tuple.hxx>
+#include <basegfx/range/basicrange.hxx>
+
+namespace basegfx
+{
+ class B3IRange
+ {
+ typedef ::basegfx::BasicRange< sal_Int32, Int32Traits > MyBasicRange;
+
+ MyBasicRange maRangeX;
+ MyBasicRange maRangeY;
+ MyBasicRange maRangeZ;
+
+ public:
+ B3IRange()
+ {
+ }
+
+ explicit B3IRange(const B3ITuple& rTuple)
+ : maRangeX(rTuple.getX()),
+ maRangeY(rTuple.getY()),
+ maRangeZ(rTuple.getZ())
+ {
+ }
+
+ B3IRange(sal_Int32 x1,
+ sal_Int32 y1,
+ sal_Int32 z1,
+ sal_Int32 x2,
+ sal_Int32 y2,
+ sal_Int32 z2)
+ : maRangeX(x1),
+ maRangeY(y1),
+ maRangeZ(z1)
+ {
+ maRangeX.expand(x2);
+ maRangeY.expand(y2);
+ maRangeZ.expand(z2);
+ }
+
+ B3IRange(const B3ITuple& rTuple1,
+ const B3ITuple& rTuple2)
+ : maRangeX(rTuple1.getX()),
+ maRangeY(rTuple1.getY()),
+ maRangeZ(rTuple1.getZ())
+ {
+ expand(rTuple2);
+ }
+
+ B3IRange(const B3IRange& rRange)
+ : maRangeX(rRange.maRangeX),
+ maRangeY(rRange.maRangeY),
+ maRangeZ(rRange.maRangeZ)
+ {
+ }
+
+ bool isEmpty() const
+ {
+ return maRangeX.isEmpty() || maRangeY.isEmpty() || maRangeZ.isEmpty();
+ }
+
+ void reset()
+ {
+ maRangeX.reset();
+ maRangeY.reset();
+ maRangeZ.reset();
+ }
+
+ bool operator==( const B3IRange& rRange ) const
+ {
+ return (maRangeX == rRange.maRangeX
+ && maRangeY == rRange.maRangeY
+ && maRangeZ == rRange.maRangeZ);
+ }
+
+ bool operator!=( const B3IRange& rRange ) const
+ {
+ return (maRangeX != rRange.maRangeX
+ || maRangeY != rRange.maRangeY
+ || maRangeZ != rRange.maRangeZ);
+ }
+
+ B3IRange& operator=(const B3IRange& rRange)
+ {
+ maRangeX = rRange.maRangeX;
+ maRangeY = rRange.maRangeY;
+ maRangeZ = rRange.maRangeZ;
+ return *this;
+ }
+
+ sal_Int32 getMinX() const
+ {
+ return maRangeX.getMinimum();
+ }
+
+ sal_Int32 getMinY() const
+ {
+ return maRangeY.getMinimum();
+ }
+
+ sal_Int32 getMinZ() const
+ {
+ return maRangeZ.getMinimum();
+ }
+
+ sal_Int32 getMaxX() const
+ {
+ return maRangeX.getMaximum();
+ }
+
+ sal_Int32 getMaxY() const
+ {
+ return maRangeY.getMaximum();
+ }
+
+ sal_Int32 getMaxZ() const
+ {
+ return maRangeZ.getMaximum();
+ }
+
+ sal_Int64 getWidth() const
+ {
+ return maRangeX.getRange();
+ }
+
+ sal_Int64 getHeight() const
+ {
+ return maRangeY.getRange();
+ }
+
+ sal_Int64 getDepth() const
+ {
+ return maRangeZ.getRange();
+ }
+
+ B3IPoint getMinimum() const
+ {
+ return B3IPoint(
+ maRangeX.getMinimum(),
+ maRangeY.getMinimum(),
+ maRangeZ.getMinimum()
+ );
+ }
+
+ B3IPoint getMaximum() const
+ {
+ return B3IPoint(
+ maRangeX.getMaximum(),
+ maRangeY.getMaximum(),
+ maRangeZ.getMaximum()
+ );
+ }
+
+ B3I64Tuple getRange() const
+ {
+ return B3I64Tuple(
+ maRangeX.getRange(),
+ maRangeY.getRange(),
+ maRangeZ.getRange()
+ );
+ }
+
+ B3DPoint getCenter() const
+ {
+ return B3DPoint(
+ maRangeX.getCenter(),
+ maRangeY.getCenter(),
+ maRangeZ.getCenter()
+ );
+ }
+
+ bool isInside(const B3ITuple& rTuple) const
+ {
+ return (
+ maRangeX.isInside(rTuple.getX())
+ && maRangeY.isInside(rTuple.getY())
+ && maRangeZ.isInside(rTuple.getZ())
+ );
+ }
+
+ bool isInside(const B3IRange& rRange) const
+ {
+ return (
+ maRangeX.isInside(rRange.maRangeX)
+ && maRangeY.isInside(rRange.maRangeY)
+ && maRangeZ.isInside(rRange.maRangeZ)
+ );
+ }
+
+ bool overlaps(const B3IRange& rRange) const
+ {
+ return (
+ maRangeX.overlaps(rRange.maRangeX)
+ && maRangeY.overlaps(rRange.maRangeY)
+ && maRangeZ.overlaps(rRange.maRangeZ)
+ );
+ }
+
+ void expand(const B3ITuple& rTuple)
+ {
+ maRangeX.expand(rTuple.getX());
+ maRangeY.expand(rTuple.getY());
+ maRangeZ.expand(rTuple.getZ());
+ }
+
+ void expand(const B3IRange& rRange)
+ {
+ maRangeX.expand(rRange.maRangeX);
+ maRangeY.expand(rRange.maRangeY);
+ maRangeZ.expand(rRange.maRangeZ);
+ }
+
+ void intersect(const B3IRange& rRange)
+ {
+ maRangeX.intersect(rRange.maRangeX);
+ maRangeY.intersect(rRange.maRangeY);
+ maRangeZ.intersect(rRange.maRangeZ);
+ }
+
+ void grow(sal_Int32 nValue)
+ {
+ maRangeX.grow(nValue);
+ maRangeY.grow(nValue);
+ maRangeZ.grow(nValue);
+ }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_RANGE_B3IRANGE_HXX */
diff --git a/basegfx/inc/basegfx/range/b3ivolume.hxx b/basegfx/inc/basegfx/range/b3ivolume.hxx
new file mode 100644
index 000000000000..30e1db168d31
--- /dev/null
+++ b/basegfx/inc/basegfx/range/b3ivolume.hxx
@@ -0,0 +1,45 @@
+/*************************************************************************
+ *
+ * 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: b3ivolume.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_B3IBOX_HXX
+#define _BGFX_RANGE_B3IBOX_HXX
+
+#include <basegfx/range/b3irange.hxx>
+
+namespace basegfx
+{
+ // syntactic sugar: a B3IRange exactly models a Box in 3D, thus,
+ // for interface clarity, we provide an alias name
+
+ /// Alias name for interface clarity (not everybody is aware of the identity)
+ typedef B3IRange B3IBox;
+}
+
+#endif /* _BGFX_RANGE_B3IBOX_HXX */
diff --git a/basegfx/inc/basegfx/range/basicbox.hxx b/basegfx/inc/basegfx/range/basicbox.hxx
new file mode 100644
index 000000000000..da6a1ab6b94c
--- /dev/null
+++ b/basegfx/inc/basegfx/range/basicbox.hxx
@@ -0,0 +1,139 @@
+/*************************************************************************
+ *
+ * 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: basicbox.hxx,v $
+ * $Revision: 1.3 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_BASICBOX_HXX
+#define _BGFX_RANGE_BASICBOX_HXX
+
+#include <basegfx/range/basicrange.hxx>
+
+
+namespace basegfx
+{
+ /** Specialization of BasicRange, handling the inside predicates
+ differently.
+
+ This template considers the rightmost and bottommost border as
+ <em>outside</em> of the range, in contrast to BasicRange,
+ which considers them inside.
+ */
+ class BasicBox : public BasicRange< sal_Int32, Int32Traits >
+ {
+ typedef BasicRange< sal_Int32, Int32Traits > Base;
+ public:
+ BasicBox() :
+ Base()
+ {
+ }
+
+ BasicBox( sal_Int32 nValue ) :
+ Base( nValue )
+ {
+ }
+
+ BasicBox(const BasicBox& rBox) :
+ Base( rBox )
+ {
+ }
+
+ double getCenter() const
+ {
+ if(isEmpty())
+ {
+ return 0.0;
+ }
+ else
+ {
+ return ((mnMaximum + mnMinimum - 1.0) / 2.0);
+ }
+ }
+
+ using Base::isInside;
+
+ bool isInside(sal_Int32 nValue) const
+ {
+ if(isEmpty())
+ {
+ return false;
+ }
+ else
+ {
+ return (nValue >= mnMinimum) && (nValue < mnMaximum);
+ }
+ }
+
+ using Base::overlaps;
+
+ bool overlaps(const BasicBox& rBox) const
+ {
+ if(isEmpty())
+ {
+ return false;
+ }
+ else
+ {
+ if(rBox.isEmpty())
+ {
+ return false;
+ }
+ else
+ {
+ return !((rBox.mnMaximum <= mnMinimum) || (rBox.mnMinimum >= mnMaximum));
+ }
+ }
+ }
+
+ void grow(sal_Int32 nValue)
+ {
+ if(!isEmpty())
+ {
+ bool bLessThanZero(nValue < 0);
+
+ if(nValue > 0 || bLessThanZero)
+ {
+ mnMinimum -= nValue;
+ mnMaximum += nValue;
+
+ if(bLessThanZero)
+ {
+ // test if range did collapse
+ if(mnMinimum > mnMaximum)
+ {
+ // if yes, collapse to center
+ mnMinimum = mnMaximum = ((mnMaximum + mnMinimum - 1) / 2);
+ }
+ }
+ }
+ }
+ }
+ };
+
+} // end of namespace basegfx
+
+#endif /* _BGFX_RANGE_BASICBOX_HXX */
diff --git a/basegfx/inc/basegfx/range/basicrange.hxx b/basegfx/inc/basegfx/range/basicrange.hxx
new file mode 100644
index 000000000000..59d13cf530c0
--- /dev/null
+++ b/basegfx/inc/basegfx/range/basicrange.hxx
@@ -0,0 +1,299 @@
+/*************************************************************************
+ *
+ * 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: basicrange.hxx,v $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_BASICRANGE_HXX
+#define _BGFX_RANGE_BASICRANGE_HXX
+
+#include <sal/types.h>
+#include <float.h>
+#include <basegfx/numeric/ftools.hxx>
+
+
+namespace basegfx
+{
+ template< typename T, typename Traits > class BasicRange
+ {
+ protected:
+ T mnMinimum;
+ T mnMaximum;
+
+ public:
+ typedef T ValueType;
+ typedef Traits TraitsType;
+
+ BasicRange() :
+ mnMinimum(Traits::maxVal()),
+ mnMaximum(Traits::minVal())
+ {
+ }
+
+ BasicRange( T nValue ) :
+ mnMinimum(nValue),
+ mnMaximum(nValue)
+ {
+ }
+
+ BasicRange(const BasicRange& rRange) :
+ mnMinimum(rRange.mnMinimum),
+ mnMaximum(rRange.mnMaximum)
+ {
+ }
+
+ void reset()
+ {
+ mnMinimum = Traits::maxVal();
+ mnMaximum = Traits::minVal();
+ }
+
+ bool isEmpty() const
+ {
+ return Traits::maxVal() == mnMinimum;
+ }
+
+ T getMinimum() const { return mnMinimum; }
+ T getMaximum() const { return mnMaximum; }
+
+ double getCenter() const
+ {
+ if(isEmpty())
+ {
+ return 0.0;
+ }
+ else
+ {
+ return ((mnMaximum + mnMinimum) / 2.0);
+ }
+ }
+
+ bool isInside(T nValue) const
+ {
+ if(isEmpty())
+ {
+ return false;
+ }
+ else
+ {
+ return (nValue >= mnMinimum) && (nValue <= mnMaximum);
+ }
+ }
+
+ bool isInside(const BasicRange& rRange) const
+ {
+ if(isEmpty())
+ {
+ return false;
+ }
+ else
+ {
+ if(rRange.isEmpty())
+ {
+ return false;
+ }
+ else
+ {
+ return (rRange.mnMinimum >= mnMinimum) && (rRange.mnMaximum <= mnMaximum);
+ }
+ }
+ }
+
+ bool overlaps(const BasicRange& rRange) const
+ {
+ if(isEmpty())
+ {
+ return false;
+ }
+ else
+ {
+ if(rRange.isEmpty())
+ {
+ return false;
+ }
+ else
+ {
+ return !((rRange.mnMaximum < mnMinimum) || (rRange.mnMinimum > mnMaximum));
+ }
+ }
+ }
+
+ bool overlapsMore(const BasicRange& rRange) const
+ {
+ if(isEmpty() || rRange.isEmpty())
+ return false;
+ // returns true if the overlap is more than just a touching at the limits
+ return ((rRange.mnMaximum > mnMinimum) && (rRange.mnMinimum < mnMaximum));
+ }
+
+ bool operator==( const BasicRange& rRange ) const
+ {
+ return (mnMinimum == rRange.mnMinimum && mnMaximum == rRange.mnMaximum);
+ }
+
+ bool operator!=( const BasicRange& rRange ) const
+ {
+ return (mnMinimum != rRange.mnMinimum || mnMaximum != rRange.mnMaximum);
+ }
+
+ BasicRange& operator=(const BasicRange& rRange)
+ {
+ mnMinimum = rRange.mnMinimum;
+ mnMaximum = rRange.mnMaximum;
+ return *this;
+ }
+
+ bool equal(const BasicRange& rRange) const
+ {
+ return (
+ fTools::equal(mnMinimum, rRange.mnMinimum) &&
+ fTools::equal(mnMaximum, rRange.mnMaximum));
+ }
+
+ void expand(T nValue)
+ {
+ if(isEmpty())
+ {
+ mnMinimum = mnMaximum = nValue;
+ }
+ else
+ {
+ if(nValue < mnMinimum)
+ {
+ mnMinimum = nValue;
+ }
+
+ if(nValue > mnMaximum)
+ {
+ mnMaximum = nValue;
+ }
+ }
+ }
+
+ void expand(const BasicRange& rRange)
+ {
+ if(isEmpty())
+ {
+ mnMinimum = rRange.mnMinimum;
+ mnMaximum = rRange.mnMaximum;
+ }
+ else
+ {
+ if(!rRange.isEmpty())
+ {
+ if(rRange.mnMinimum < mnMinimum)
+ {
+ mnMinimum = rRange.mnMinimum;
+ }
+
+ if(rRange.mnMaximum > mnMaximum)
+ {
+ mnMaximum = rRange.mnMaximum;
+ }
+ }
+ }
+ }
+
+ void intersect(const BasicRange& rRange)
+ {
+ // here, overlaps also tests all isEmpty() conditions already.
+ if( !overlaps( rRange ) )
+ {
+ reset();
+ }
+ else
+ {
+ if(rRange.mnMinimum > mnMinimum)
+ {
+ mnMinimum = rRange.mnMinimum;
+ }
+
+ if(rRange.mnMaximum < mnMaximum)
+ {
+ mnMaximum = rRange.mnMaximum;
+ }
+ }
+ }
+
+ void grow(T nValue)
+ {
+ if(!isEmpty())
+ {
+ bool bLessThanZero(nValue < 0);
+
+ if(nValue > 0 || bLessThanZero)
+ {
+ mnMinimum -= nValue;
+ mnMaximum += nValue;
+
+ if(bLessThanZero)
+ {
+ // test if range did collapse
+ if(mnMinimum > mnMaximum)
+ {
+ // if yes, collapse to center
+ mnMinimum = mnMaximum = (mnMinimum + mnMaximum) / 2;
+ }
+ }
+ }
+ }
+ }
+
+ typename Traits::DifferenceType getRange() const
+ {
+ if(isEmpty())
+ {
+ return Traits::neutral();
+ }
+ else
+ {
+ return (mnMaximum - mnMinimum);
+ }
+ }
+ };
+
+ // some pre-fabricated traits
+ struct DoubleTraits
+ {
+ static double minVal() { return DBL_MIN; };
+ static double maxVal() { return DBL_MAX; };
+ static double neutral() { return 0.0; };
+
+ typedef double DifferenceType;
+ };
+
+ struct Int32Traits
+ {
+ static sal_Int32 minVal() { return SAL_MIN_INT32; };
+ static sal_Int32 maxVal() { return SAL_MAX_INT32; };
+ static sal_Int32 neutral() { return 0L; };
+
+ typedef sal_Int64 DifferenceType;
+ };
+
+} // end of namespace basegfx
+
+#endif /* _BGFX_RANGE_BASICRANGE_HXX */
diff --git a/basegfx/inc/basegfx/range/rangeexpander.hxx b/basegfx/inc/basegfx/range/rangeexpander.hxx
new file mode 100644
index 000000000000..e1dce0c2821d
--- /dev/null
+++ b/basegfx/inc/basegfx/range/rangeexpander.hxx
@@ -0,0 +1,86 @@
+/*************************************************************************
+ *
+ * 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: rangeexpander.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RANGE_RANGEEXPANDER_HXX
+#define _BGFX_RANGE_RANGEEXPANDER_HXX
+
+#include <basegfx/range/b1drange.hxx>
+#include <basegfx/range/b1irange.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/b2irange.hxx>
+#include <basegfx/range/b3drange.hxx>
+#include <basegfx/range/b3irange.hxx>
+
+namespace basegfx
+{
+ /** Generic functor for expanding a range with a number of other
+ ranges.
+
+ Since *Range::expand() is overloaded, straight-forward
+ application of ::boost::bind and friends fails (because of
+ ambiguities). Thus, this functor template can be used, to
+ expand the given range with a number of other ranges, passed
+ in at the function operator.
+
+ @tpl RangeType
+ Range type to operate with. Preferrably, one of B1*Range,
+ B2*Range, or B3*Range.
+ */
+ template< typename RangeType > class RangeExpander
+ {
+ public:
+ typedef RangeType ValueType;
+ typedef void result_type;
+
+ explicit RangeExpander( ValueType& rBounds ) :
+ mrBounds( rBounds )
+ {
+ }
+
+ void operator()( const ValueType& rBounds )
+ {
+ mrBounds.expand( rBounds );
+ }
+
+ private:
+ ValueType& mrBounds;
+ };
+
+ typedef RangeExpander< B1DRange > B1DRangeExpander;
+ typedef RangeExpander< B1IRange > B1IRangeExpander;
+ typedef RangeExpander< B2DRange > B2DRangeExpander;
+ typedef RangeExpander< B2IRange > B2IRangeExpander;
+ typedef RangeExpander< B3DRange > B3DRangeExpander;
+ typedef RangeExpander< B3IRange > B3IRangeExpander;
+
+} // end of namespace basegfx
+
+
+#endif /* _BGFX_RANGE_RANGEEXPANDER_HXX */
diff --git a/basegfx/inc/basegfx/raster/bpixelraster.hxx b/basegfx/inc/basegfx/raster/bpixelraster.hxx
new file mode 100644
index 000000000000..25d924d304ea
--- /dev/null
+++ b/basegfx/inc/basegfx/raster/bpixelraster.hxx
@@ -0,0 +1,120 @@
+/*************************************************************************
+ *
+ * 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: bpixelraster.hxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RASTER_BPIXELRASTER_HXX
+#define _BGFX_RASTER_BPIXELRASTER_HXX
+
+#include <algorithm>
+#include <sal/types.h>
+#include <basegfx/pixel/bpixel.hxx>
+#include <rtl/memory.h>
+
+//////////////////////////////////////////////////////////////////////////////
+// predeclarations
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class BPixelRaster
+ {
+ private:
+ // do not allow copy constructor and assignment operator
+ BPixelRaster(const BPixelRaster&);
+ BPixelRaster& operator=(const BPixelRaster&);
+
+ protected:
+ sal_uInt32 mnWidth;
+ sal_uInt32 mnHeight;
+ sal_uInt32 mnCount;
+ BPixel* mpContent;
+
+ public:
+ // reset
+ void reset()
+ {
+ rtl_zeroMemory(mpContent, sizeof(BPixel) * mnCount);
+ }
+
+ // constructor/destructor
+ BPixelRaster(sal_uInt32 nWidth, sal_uInt32 nHeight)
+ : mnWidth(nWidth),
+ mnHeight(nHeight),
+ mnCount(nWidth * nHeight),
+ mpContent(new BPixel[mnCount])
+ {
+ reset();
+ }
+
+ ~BPixelRaster()
+ {
+ delete [] mpContent;
+ }
+
+ // coordinate calcs between X/Y and span
+ sal_uInt32 getIndexFromXY(sal_uInt32 nX, sal_uInt32 nY) const { return (nX + (nY * mnWidth)); }
+ sal_uInt32 getXFromIndex(sal_uInt32 nIndex) const { return (nIndex % mnWidth); }
+ sal_uInt32 getYFromIndex(sal_uInt32 nIndex) const { return (nIndex / mnWidth); }
+
+ // data access read
+ sal_uInt32 getWidth() const { return mnWidth; }
+ sal_uInt32 getHeight() const { return mnHeight; }
+ sal_uInt32 getCount() const { return mnCount; }
+
+ // data access read only
+ const BPixel& getBPixel(sal_uInt32 nIndex) const
+ {
+#ifdef DBG_UTIL
+ if(nIndex >= mnCount)
+ {
+ OSL_ENSURE(false, "getBPixel: Access out of range (!)");
+ return BPixel::getEmptyBPixel();
+ }
+#endif
+ return mpContent[nIndex];
+ }
+
+ // data access read/write
+ BPixel& getBPixel(sal_uInt32 nIndex)
+ {
+#ifdef DBG_UTIL
+ if(nIndex >= mnCount)
+ {
+ OSL_ENSURE(false, "getBPixel: Access out of range (!)");
+ return mpContent[0L];
+ }
+#endif
+ return mpContent[nIndex];
+ }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_RASTER_BPIXELRASTER_HXX */
diff --git a/basegfx/inc/basegfx/raster/bzpixelraster.hxx b/basegfx/inc/basegfx/raster/bzpixelraster.hxx
new file mode 100644
index 000000000000..f0bbad04d4ab
--- /dev/null
+++ b/basegfx/inc/basegfx/raster/bzpixelraster.hxx
@@ -0,0 +1,100 @@
+/*************************************************************************
+ *
+ * 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: bzpixelraster.hxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RASTER_BZPIXELRASTER_HXX
+#define _BGFX_RASTER_BZPIXELRASTER_HXX
+
+#include <basegfx/raster/bpixelraster.hxx>
+#include <rtl/memory.h>
+
+//////////////////////////////////////////////////////////////////////////////
+// predeclarations
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ class BZPixelRaster : public BPixelRaster
+ {
+ protected:
+ // additionally, host a ZBuffer
+ sal_uInt16* mpZBuffer;
+
+ public:
+ // reset
+ void resetZ()
+ {
+ reset();
+ rtl_zeroMemory(mpZBuffer, sizeof(sal_uInt16) * mnCount);
+ }
+
+ // constructor/destructor
+ BZPixelRaster(sal_uInt32 nWidth, sal_uInt32 nHeight)
+ : BPixelRaster(nWidth, nHeight),
+ mpZBuffer(new sal_uInt16[mnCount])
+ {
+ rtl_zeroMemory(mpZBuffer, sizeof(sal_uInt16) * mnCount);
+ }
+
+ ~BZPixelRaster()
+ {
+ delete [] mpZBuffer;
+ }
+
+ // data access read only
+ const sal_uInt16& getZ(sal_uInt32 nIndex) const
+ {
+#ifdef DBG_UTIL
+ if(nIndex >= mnCount)
+ {
+ OSL_ENSURE(false, "getZ: Access out of range (!)");
+ return mpZBuffer[0L];
+ }
+#endif
+ return mpZBuffer[nIndex];
+ }
+
+ // data access read/write
+ sal_uInt16& getZ(sal_uInt32 nIndex)
+ {
+#ifdef DBG_UTIL
+ if(nIndex >= mnCount)
+ {
+ OSL_ENSURE(false, "getZ: Access out of range (!)");
+ return mpZBuffer[0L];
+ }
+#endif
+ return mpZBuffer[nIndex];
+ }
+ };
+} // end of namespace basegfx
+
+#endif /* _BGFX_RASTER_BZPIXELRASTER_HXX */
diff --git a/basegfx/inc/basegfx/raster/rasterconvert3d.hxx b/basegfx/inc/basegfx/raster/rasterconvert3d.hxx
new file mode 100644
index 000000000000..6a2b550003e7
--- /dev/null
+++ b/basegfx/inc/basegfx/raster/rasterconvert3d.hxx
@@ -0,0 +1,349 @@
+/*************************************************************************
+ *
+ * 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: rasterconvert3d.hxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_RASTER_RASTERCONVERT3D_HXX
+#define _BGFX_RASTER_RASTERCONVERT3D_HXX
+
+#include <sal/types.h>
+#include <vector>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/vector/b3dvector.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+// predeclarations
+
+namespace basegfx
+{
+ class B3DPolygon;
+ class B3DPolyPolygon;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// interpolators for double precision
+
+namespace basegfx
+{
+ class ip_single
+ {
+ private:
+ double mfVal;
+ double mfInc;
+
+ public:
+ ip_single()
+ : mfVal(0.0),
+ mfInc(0.0)
+ {}
+
+ ip_single(double fVal, double fInc)
+ : mfVal(fVal),
+ mfInc(fInc)
+ {}
+
+ double getVal() const { return mfVal; }
+ double getInc() const { return mfInc; }
+
+ void increment(double fStep) { mfVal += fStep * mfInc; }
+ };
+} // end of namespace basegfx
+
+namespace basegfx
+{
+ class ip_double
+ {
+ private:
+ ip_single maX;
+ ip_single maY;
+
+ public:
+ ip_double()
+ : maX(),
+ maY()
+ {}
+
+ ip_double(double fXVal, double fXInc, double fYVal, double fYInc)
+ : maX(fXVal, fXInc),
+ maY(fYVal, fYInc)
+ {}
+
+ const ip_single& getX() const { return maX; }
+ const ip_single& getY() const { return maY; }
+
+ void increment(double fStep) { maX.increment(fStep); maY.increment(fStep); }
+ };
+} // end of namespace basegfx
+
+namespace basegfx
+{
+ class ip_triple
+ {
+ private:
+ ip_single maX;
+ ip_single maY;
+ ip_single maZ;
+
+ public:
+ ip_triple()
+ : maX(),
+ maY(),
+ maZ()
+ {}
+
+ ip_triple(double fXVal, double fXInc, double fYVal, double fYInc, double fZVal, double fZInc)
+ : maX(fXVal, fXInc),
+ maY(fYVal, fYInc),
+ maZ(fZVal, fZInc)
+ {}
+
+ const ip_single& getX() const { return maX; }
+ const ip_single& getY() const { return maY; }
+ const ip_single& getZ() const { return maZ; }
+
+ void increment(double fStep) { maX.increment(fStep); maY.increment(fStep); maZ.increment(fStep); }
+ };
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// InterpolatorProvider3D to have a common source for allocating interpolators
+// which may then be addressed using the index to the vectors
+
+namespace basegfx
+{
+ #define SCANLINE_EMPTY_INDEX (0xffffffff)
+
+ class InterpolatorProvider3D
+ {
+ private:
+ ::std::vector< ip_triple > maColorInterpolators;
+ ::std::vector< ip_triple > maNormalInterpolators;
+ ::std::vector< ip_double > maTextureInterpolators;
+ ::std::vector< ip_triple > maInverseTextureInterpolators;
+
+ protected:
+ sal_uInt32 addColorInterpolator(const BColor& rA, const BColor& rB, double fInvYDelta)
+ {
+ B3DVector aDelta(rB.getRed() - rA.getRed(), rB.getGreen() - rA.getGreen(), rB.getBlue() - rA.getBlue());
+ aDelta *= fInvYDelta;
+ maColorInterpolators.push_back(ip_triple(rA.getRed(), aDelta.getX(), rA.getGreen(), aDelta.getY(), rA.getBlue(), aDelta.getZ()));
+ return (maColorInterpolators.size() - 1L);
+ }
+
+ sal_uInt32 addNormalInterpolator(const B3DVector& rA, const B3DVector& rB, double fInvYDelta)
+ {
+ B3DVector aDelta(rB.getX() - rA.getX(), rB.getY() - rA.getY(), rB.getZ() - rA.getZ());
+ aDelta *= fInvYDelta;
+ maNormalInterpolators.push_back(ip_triple(rA.getX(), aDelta.getX(), rA.getY(), aDelta.getY(), rA.getZ(), aDelta.getZ()));
+ return (maNormalInterpolators.size() - 1L);
+ }
+
+ sal_uInt32 addTextureInterpolator(const B2DPoint& rA, const B2DPoint& rB, double fInvYDelta)
+ {
+ B2DVector aDelta(rB.getX() - rA.getX(), rB.getY() - rA.getY());
+ aDelta *= fInvYDelta;
+ maTextureInterpolators.push_back(ip_double(rA.getX(), aDelta.getX(), rA.getY(), aDelta.getY()));
+ return (maTextureInterpolators.size() - 1L);
+ }
+
+ sal_uInt32 addInverseTextureInterpolator(const B2DPoint& rA, const B2DPoint& rB, double fZEyeA, double fZEyeB, double fInvYDelta)
+ {
+ const double fInvZEyeA(fTools::equalZero(fZEyeA) ? fZEyeA : 1.0 / fZEyeA);
+ const double fInvZEyeB(fTools::equalZero(fZEyeB) ? fZEyeB : 1.0 / fZEyeB);
+ const B2DPoint aInvA(rA * fInvZEyeA);
+ const B2DPoint aInvB(rB * fInvZEyeB);
+ double fZDelta(fInvZEyeB - fInvZEyeA);
+ B2DVector aDelta(aInvB.getX() - aInvA.getX(), aInvB.getY() - aInvA.getY());
+
+ fZDelta *= fInvYDelta;
+ aDelta *= fInvYDelta;
+
+ maInverseTextureInterpolators.push_back(ip_triple(aInvA.getX(), aDelta.getX(), aInvA.getY(), aDelta.getY(), fInvZEyeA, fZDelta));
+ return (maInverseTextureInterpolators.size() - 1L);
+ }
+
+ void reset()
+ {
+ maColorInterpolators.clear();
+ maNormalInterpolators.clear();
+ maTextureInterpolators.clear();
+ maInverseTextureInterpolators.clear();
+ }
+
+ public:
+ InterpolatorProvider3D() {}
+
+ ::std::vector< ip_triple >& getColorInterpolators() { return maColorInterpolators; }
+ ::std::vector< ip_triple >& getNormalInterpolators() { return maNormalInterpolators; }
+ ::std::vector< ip_double >& getTextureInterpolators() { return maTextureInterpolators; }
+ ::std::vector< ip_triple >& getInverseTextureInterpolators() { return maInverseTextureInterpolators; }
+ };
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// RasterConversionLineEntry3D for Raterconversion of 3D PolyPolygons
+
+namespace basegfx
+{
+ class RasterConversionLineEntry3D
+ {
+ private:
+ ip_single maX;
+ ip_single maZ;
+ sal_Int32 mnY;
+ sal_uInt32 mnCount;
+
+ sal_uInt32 mnColorIndex;
+ sal_uInt32 mnNormalIndex;
+ sal_uInt32 mnTextureIndex;
+ sal_uInt32 mnInverseTextureIndex;
+
+ public:
+ RasterConversionLineEntry3D(const double& rfX, const double& rfDeltaX, const double& rfZ, const double& rfDeltaZ, sal_Int32 nY, sal_uInt32 nCount)
+ : maX(rfX, rfDeltaX),
+ maZ(rfZ, rfDeltaZ),
+ mnY(nY),
+ mnCount(nCount),
+ mnColorIndex(SCANLINE_EMPTY_INDEX),
+ mnNormalIndex(SCANLINE_EMPTY_INDEX),
+ mnTextureIndex(SCANLINE_EMPTY_INDEX),
+ mnInverseTextureIndex(SCANLINE_EMPTY_INDEX)
+ {}
+
+ void setColorIndex(sal_uInt32 nIndex) { mnColorIndex = nIndex; }
+ void setNormalIndex(sal_uInt32 nIndex) { mnNormalIndex = nIndex; }
+ void setTextureIndex(sal_uInt32 nIndex) { mnTextureIndex = nIndex; }
+ void setInverseTextureIndex(sal_uInt32 nIndex) { mnInverseTextureIndex = nIndex; }
+
+ bool operator<(const RasterConversionLineEntry3D& rComp) const
+ {
+ if(mnY == rComp.mnY)
+ {
+ return maX.getVal() < rComp.maX.getVal();
+ }
+
+ return mnY < rComp.mnY;
+ }
+
+ bool decrementRasterConversionLineEntry3D(sal_uInt32 nStep)
+ {
+ if(nStep >= mnCount)
+ {
+ return false;
+ }
+ else
+ {
+ mnCount -= nStep;
+ return true;
+ }
+ }
+
+ void incrementRasterConversionLineEntry3D(sal_uInt32 nStep, InterpolatorProvider3D& rProvider)
+ {
+ const double fStep((double)nStep);
+ maX.increment(fStep);
+ maZ.increment(fStep);
+ mnY += nStep;
+
+ if(SCANLINE_EMPTY_INDEX != mnColorIndex)
+ {
+ rProvider.getColorInterpolators()[mnColorIndex].increment(fStep);
+ }
+
+ if(SCANLINE_EMPTY_INDEX != mnNormalIndex)
+ {
+ rProvider.getNormalInterpolators()[mnNormalIndex].increment(fStep);
+ }
+
+ if(SCANLINE_EMPTY_INDEX != mnTextureIndex)
+ {
+ rProvider.getTextureInterpolators()[mnTextureIndex].increment(fStep);
+ }
+
+ if(SCANLINE_EMPTY_INDEX != mnInverseTextureIndex)
+ {
+ rProvider.getInverseTextureInterpolators()[mnInverseTextureIndex].increment(fStep);
+ }
+ }
+
+ // data read access
+ const ip_single& getX() const { return maX; }
+ sal_Int32 getY() const { return mnY; }
+ const ip_single& getZ() const { return maZ; }
+ sal_uInt32 getColorIndex() const { return mnColorIndex; }
+ sal_uInt32 getNormalIndex() const { return mnNormalIndex; }
+ sal_uInt32 getTextureIndex() const { return mnTextureIndex; }
+ sal_uInt32 getInverseTextureIndex() const { return mnInverseTextureIndex; }
+ };
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// the basic RaterConverter itself. Only one method needs to be overloaded. The
+// class itself is strictly virtual
+
+namespace basegfx
+{
+ class RasterConverter3D : public InterpolatorProvider3D
+ {
+ private:
+ // the line entries for an area conversion run
+ ::std::vector< RasterConversionLineEntry3D > maLineEntries;
+
+ struct lineComparator
+ {
+ bool operator()(const RasterConversionLineEntry3D* pA, const RasterConversionLineEntry3D* pB)
+ {
+ OSL_ENSURE(pA && pB, "lineComparator: empty pointer (!)");
+ return pA->getX().getVal() < pB->getX().getVal();
+ }
+ };
+
+ void addArea(const B3DPolygon& rFill, const B3DHomMatrix* pViewToEye);
+ void addArea(const B3DPolyPolygon& rFill, const B3DHomMatrix* pViewToEye);
+ void addEdge(const B3DPolygon& rFill, sal_uInt32 a, sal_uInt32 b, const B3DHomMatrix* pViewToEye);
+
+ void rasterconvertB3DArea(sal_Int32 nStartLine, sal_Int32 nStopLine);
+ void rasterconvertB3DEdge(const B3DPolygon& rLine, sal_uInt32 nA, sal_uInt32 nB, sal_Int32 nStartLine, sal_Int32 nStopLine, sal_uInt16 nLineWidth);
+
+ virtual void processLineSpan(const RasterConversionLineEntry3D& rA, const RasterConversionLineEntry3D& rB, sal_Int32 nLine, sal_uInt32 nSpanCount) = 0;
+
+ public:
+ RasterConverter3D();
+ virtual ~RasterConverter3D();
+
+ void rasterconvertB3DPolyPolygon(const B3DPolyPolygon& rFill, const B3DHomMatrix* pViewToEye, sal_Int32 nStartLine, sal_Int32 nStopLine);
+ void rasterconvertB3DPolygon(const B3DPolygon& rLine, sal_Int32 nStartLine, sal_Int32 nStopLine, sal_uInt16 nLineWidth);
+ };
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif /* _BGFX_RASTER_RASTERCONVERT3D_HXX */
diff --git a/basegfx/inc/basegfx/tools/canvastools.hxx b/basegfx/inc/basegfx/tools/canvastools.hxx
new file mode 100755
index 000000000000..746beeb60709
--- /dev/null
+++ b/basegfx/inc/basegfx/tools/canvastools.hxx
@@ -0,0 +1,224 @@
+/*************************************************************************
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TOOLS_CANVASTOOLS_HXX
+#define _BGFX_TOOLS_CANVASTOOLS_HXX
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+
+namespace com { namespace sun { namespace star { namespace geometry
+{
+ struct AffineMatrix2D;
+ struct AffineMatrix3D;
+ struct Matrix2D;
+ struct RealPoint2D;
+ struct RealSize2D;
+ struct RealRectangle2D;
+ struct RealRectangle3D;
+ struct IntegerPoint2D;
+ struct IntegerSize2D;
+ struct IntegerRectangle2D;
+ struct RealBezierSegment2D;
+} } } }
+
+namespace com { namespace sun { namespace star { namespace rendering
+{
+ class XGraphicDevice;
+ class XPolyPolygon2D;
+} } } }
+
+namespace com { namespace sun { namespace star { namespace awt
+{
+ struct Point;
+ struct Size;
+ struct Rectangle;
+} } } }
+
+namespace basegfx
+{
+ class B2DHomMatrix;
+ class B3DHomMatrix;
+ class B2DVector;
+ class B2DPoint;
+ class B2DRange;
+ class B3DRange;
+ class B2IVector;
+ class B2IPoint;
+ class B2IRange;
+ class B2DPolygon;
+ class B2DPolyPolygon;
+
+ namespace unotools
+ {
+ // Polygon conversions
+ // ===================================================================
+
+ ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XPolyPolygon2D >
+ xPolyPolygonFromB2DPolygon( const ::com::sun::star::uno::Reference<
+ ::com::sun::star::rendering::XGraphicDevice >& xGraphicDevice,
+ const ::basegfx::B2DPolygon& rPoly );
+
+ ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XPolyPolygon2D >
+ xPolyPolygonFromB2DPolyPolygon( const ::com::sun::star::uno::Reference<
+ ::com::sun::star::rendering::XGraphicDevice >& xGraphicDevice,
+ const ::basegfx::B2DPolyPolygon& rPolyPoly );
+
+
+ ::com::sun::star::uno::Sequence<
+ ::com::sun::star::uno::Sequence<
+ ::com::sun::star::geometry::RealBezierSegment2D > >
+ bezierSequenceSequenceFromB2DPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly );
+
+ ::com::sun::star::uno::Sequence<
+ ::com::sun::star::uno::Sequence<
+ ::com::sun::star::geometry::RealPoint2D > >
+ pointSequenceSequenceFromB2DPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly );
+
+ ::basegfx::B2DPolygon polygonFromPoint2DSequence(
+ const ::com::sun::star::uno::Sequence<
+ ::com::sun::star::geometry::RealPoint2D >& rPoints );
+
+ ::basegfx::B2DPolyPolygon polyPolygonFromPoint2DSequenceSequence(
+ const ::com::sun::star::uno::Sequence<
+ ::com::sun::star::uno::Sequence< ::com::sun::star::geometry::RealPoint2D > >& rPoints );
+
+ ::basegfx::B2DPolygon polygonFromBezier2DSequence(
+ const ::com::sun::star::uno::Sequence<
+ ::com::sun::star::geometry::RealBezierSegment2D >& rPoints );
+
+ ::basegfx::B2DPolyPolygon polyPolygonFromBezier2DSequenceSequence(
+ const ::com::sun::star::uno::Sequence<
+ ::com::sun::star::uno::Sequence< ::com::sun::star::geometry::RealBezierSegment2D > >& rPoints );
+
+ ::basegfx::B2DPolyPolygon b2DPolyPolygonFromXPolyPolygon2D(
+ const ::com::sun::star::uno::Reference<
+ ::com::sun::star::rendering::XPolyPolygon2D >& rPoly );
+
+ // Matrix conversions
+ // ===================================================================
+
+ ::com::sun::star::geometry::AffineMatrix2D&
+ affineMatrixFromHomMatrix( ::com::sun::star::geometry::AffineMatrix2D& matrix,
+ const ::basegfx::B2DHomMatrix& transform);
+
+ ::com::sun::star::geometry::AffineMatrix3D& affineMatrixFromHomMatrix3D(
+ ::com::sun::star::geometry::AffineMatrix3D& matrix,
+ const ::basegfx::B3DHomMatrix& transform);
+
+ ::basegfx::B2DHomMatrix&
+ homMatrixFromAffineMatrix( ::basegfx::B2DHomMatrix& transform,
+ const ::com::sun::star::geometry::AffineMatrix2D& matrix );
+
+ ::basegfx::B2DHomMatrix homMatrixFromAffineMatrix( const ::com::sun::star::geometry::AffineMatrix2D& matrix );
+ ::basegfx::B3DHomMatrix homMatrixFromAffineMatrix3D( const ::com::sun::star::geometry::AffineMatrix3D& matrix );
+
+ ::com::sun::star::geometry::Matrix2D&
+ matrixFromHomMatrix( ::com::sun::star::geometry::Matrix2D& matrix,
+ const ::basegfx::B2DHomMatrix& transform);
+
+ ::basegfx::B2DHomMatrix&
+ homMatrixFromMatrix( ::basegfx::B2DHomMatrix& transform,
+ const ::com::sun::star::geometry::Matrix2D& matrix );
+
+ // Geometry conversions
+ // ===================================================================
+
+ ::com::sun::star::geometry::RealSize2D size2DFromB2DSize( const ::basegfx::B2DVector& );
+ ::com::sun::star::geometry::RealPoint2D point2DFromB2DPoint( const ::basegfx::B2DPoint& );
+ ::com::sun::star::geometry::RealRectangle2D rectangle2DFromB2DRectangle( const ::basegfx::B2DRange& );
+ ::com::sun::star::geometry::RealRectangle3D rectangle3DFromB3DRectangle( const ::basegfx::B3DRange& );
+
+ ::basegfx::B2DVector b2DSizeFromRealSize2D( const ::com::sun::star::geometry::RealSize2D& );
+ ::basegfx::B2DPoint b2DPointFromRealPoint2D( const ::com::sun::star::geometry::RealPoint2D& );
+ ::basegfx::B2DRange b2DRectangleFromRealRectangle2D( const ::com::sun::star::geometry::RealRectangle2D& );
+ ::basegfx::B3DRange b3DRectangleFromRealRectangle3D( const ::com::sun::star::geometry::RealRectangle3D& );
+
+ ::com::sun::star::geometry::IntegerSize2D integerSize2DFromB2ISize( const ::basegfx::B2IVector& );
+ ::com::sun::star::geometry::IntegerPoint2D integerPoint2DFromB2IPoint( const ::basegfx::B2IPoint& );
+ ::com::sun::star::geometry::IntegerRectangle2D integerRectangle2DFromB2IRectangle( const ::basegfx::B2IRange& );
+
+ ::basegfx::B2IVector b2ISizeFromIntegerSize2D( const ::com::sun::star::geometry::IntegerSize2D& );
+ ::basegfx::B2IPoint b2IPointFromIntegerPoint2D( const ::com::sun::star::geometry::IntegerPoint2D& );
+ ::basegfx::B2IRange b2IRectangleFromIntegerRectangle2D( const ::com::sun::star::geometry::IntegerRectangle2D& );
+
+ ::com::sun::star::awt::Size awtSizeFromB2ISize( const ::basegfx::B2IVector& );
+ ::com::sun::star::awt::Point awtPointFromB2IPoint( const ::basegfx::B2IPoint& );
+ ::com::sun::star::awt::Rectangle awtRectangleFromB2IRectangle( const ::basegfx::B2IRange& );
+
+ ::basegfx::B2IVector b2ISizeFromAwtSize( const ::com::sun::star::awt::Size& );
+ ::basegfx::B2IPoint b2IPointFromAwtPoint( const ::com::sun::star::awt::Point& );
+ ::basegfx::B2IRange b2IRectangleFromAwtRectangle( const ::com::sun::star::awt::Rectangle& );
+
+ // Geometry comparisons
+ // ===================================================================
+
+ bool RealSize2DAreEqual( const ::com::sun::star::geometry::RealSize2D& rA, const ::com::sun::star::geometry::RealSize2D& rB );
+ bool RealPoint2DAreEqual( const ::com::sun::star::geometry::RealPoint2D& rA, const ::com::sun::star::geometry::RealPoint2D& rB );
+ bool RealRectangle2DAreEqual( const ::com::sun::star::geometry::RealRectangle2D& rA, const ::com::sun::star::geometry::RealRectangle2D& rB );
+ bool RealRectangle3DAreEqual( const ::com::sun::star::geometry::RealRectangle3D& rA, const ::com::sun::star::geometry::RealRectangle3D& rB );
+ bool AffineMatrix2DAreEqual( const ::com::sun::star::geometry::AffineMatrix2D& rA, const ::com::sun::star::geometry::AffineMatrix2D& rB );
+
+ bool IntegerSize2DAreEqual( const ::com::sun::star::geometry::IntegerSize2D& rA, const ::com::sun::star::geometry::IntegerSize2D& rB );
+ bool IntegerPoint2DAreEqual( const ::com::sun::star::geometry::IntegerPoint2D& rA, const ::com::sun::star::geometry::IntegerPoint2D& rB );
+ bool IntegerRectangle2DAreEqual( const ::com::sun::star::geometry::IntegerRectangle2D& rA, const ::com::sun::star::geometry::IntegerRectangle2D& rB );
+
+ bool awtSizeAreEqual( const ::com::sun::star::awt::Size& rA, const ::com::sun::star::awt::Size& rB );
+ bool awtPointAreEqual( const ::com::sun::star::awt::Point& rA, const ::com::sun::star::awt::Point& rB );
+ bool awtRectangleAreEqual( const ::com::sun::star::awt::Rectangle& rA, const ::com::sun::star::awt::Rectangle& rB );
+
+ /** Return smalltest integer range, which completely contains
+ given floating point range.
+
+ @param rRange
+ Input range. Values must be within the representable
+ bounds of sal_Int32
+
+ @return the closest integer range, which completely
+ contains rRange.
+ */
+ ::basegfx::B2IRange b2ISurroundingRangeFromB2DRange( const ::basegfx::B2DRange& rRange );
+
+ /** Return smalltest B2DRange with integer values, which
+ completely contains given floating point range.
+
+ @param rRange
+ Input range.
+
+ @return the closest B2DRange with integer coordinates,
+ which completely contains rRange.
+ */
+ ::basegfx::B2DRange b2DSurroundingIntegerRangeFromB2DRange( const ::basegfx::B2DRange& rRange );
+
+ }
+}
+
+#endif /* _BGFX_TOOLS_CANVASTOOLS_HXX */
diff --git a/basegfx/inc/basegfx/tools/debugplotter.hxx b/basegfx/inc/basegfx/tools/debugplotter.hxx
new file mode 100644
index 000000000000..f91446ac3148
--- /dev/null
+++ b/basegfx/inc/basegfx/tools/debugplotter.hxx
@@ -0,0 +1,110 @@
+/*************************************************************************
+ *
+ * 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: debugplotter.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TOOLS_DEBUGPLOTTER_HXX
+#define _BGFX_TOOLS_DEBUGPLOTTER_HXX
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <rtl/string.hxx>
+#include <boost/utility.hpp> // for noncopyable
+#include <vector>
+#include <utility>
+#include <iostream>
+
+
+namespace basegfx
+{
+ class B2DCubicBezier;
+
+ /** Generates debug output for various basegfx data types.
+
+ Use this class to produce debug (trace) output for various
+ basegfx geometry data types. By default, this class outputs
+ via OSL_TRACE (i.e. to stderr), and uses the gnuplot output
+ format.
+
+ To be able to generate one coherent block of output, this
+ class delays actual writing to its destructor
+ */
+ class DebugPlotter : private ::boost::noncopyable
+ {
+ public:
+ /** Create new debug output object
+
+ @param pTitle
+ Title of the debug output, will appear in trace output
+ */
+ explicit DebugPlotter( const sal_Char* pTitle );
+
+ /** Create new debug output object
+
+ @param pTitle
+ Title of the debug output, will appear in trace output
+
+ @param rOutputStream
+ Stream to write output to. Must stay valid over the
+ lifetime of this object!
+ */
+ DebugPlotter( const sal_Char* pTitle,
+ ::std::ostream& rOutputStream );
+
+ ~DebugPlotter();
+
+ void plot( const B2DPoint& rPoint,
+ const sal_Char* pTitle );
+ void plot( const B2DVector& rVec,
+ const sal_Char* pTitle );
+ void plot( const B2DCubicBezier& rBezier,
+ const sal_Char* pTitle );
+ void plot( const B2DRange& rRange,
+ const sal_Char* pTitle );
+ void plot( const B2DPolygon& rPoly,
+ const sal_Char* pTitle );
+ void plot( const B2DPolyPolygon& rPoly,
+ const sal_Char* pTitle );
+
+ private:
+ void print( const sal_Char* );
+
+ ::rtl::OString maTitle;
+ ::std::vector< ::std::pair< B2DPoint, ::rtl::OString > > maPoints;
+ ::std::vector< ::std::pair< B2DVector, ::rtl::OString > > maVectors;
+ ::std::vector< ::std::pair< B2DRange, ::rtl::OString > > maRanges;
+ ::std::vector< ::std::pair< B2DPolygon, ::rtl::OString > > maPolygons;
+
+ ::std::ostream* mpOutputStream;
+ };
+}
+
+#endif /* _BGFX_TOOLS_DEBUGPLOTTER_HXX */
diff --git a/basegfx/inc/basegfx/tools/gradienttools.hxx b/basegfx/inc/basegfx/tools/gradienttools.hxx
new file mode 100644
index 000000000000..0c7f2ab2c060
--- /dev/null
+++ b/basegfx/inc/basegfx/tools/gradienttools.hxx
@@ -0,0 +1,401 @@
+/*************************************************************************
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TOOLS_GRADIENTTOOLS_HXX
+#define _BGFX_TOOLS_GRADIENTTOOLS_HXX
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ /** Gradient definition as used in ODF 1.2
+
+ This struct collects all data necessary for rendering ODF
+ 1.2-compatible gradients. Use the createXXXODFGradientInfo()
+ methods below for initializing from ODF attributes.
+ */
+ struct ODFGradientInfo
+ {
+ /** transformation mapping from [0,1]^2 texture coordinate
+ space to [0,1]^2 shape coordinate space
+ */
+ B2DHomMatrix maTextureTransform;
+
+ /** transformation mapping from [0,1]^2 shape coordinate space
+ to [0,1]^2 texture coordinate space. This is the
+ transformation commonly used to create gradients from a
+ scanline rasterizer (put shape u/v coordinates into it, get
+ texture s/t coordinates out of it)
+ */
+ B2DHomMatrix maBackTextureTransform;
+
+ /** Aspect ratio of the gradient. Only used in drawinglayer
+ for generating nested gradient polygons currently. Already
+ catered for in the transformations above.
+ */
+ double mfAspectRatio;
+
+ /** Requested gradient steps to render. See the
+ implementations of the getXXXGradientAlpha() methods below,
+ the semantic differs slightly for the different gradient
+ types.
+ */
+ sal_uInt32 mnSteps;
+ };
+
+ namespace tools
+ {
+ /** Create matrix for ODF's linear gradient definition
+
+ @param o_rGradientInfo
+ Receives the calculated texture transformation matrix (for
+ use with standard [0,1]x[0,1] texture coordinates)
+
+ @param rTargetArea
+ Output area, needed for aspect ratio calculations and
+ texture transformation
+
+ @param nSteps
+ Number of gradient steps (from ODF)
+
+ @param fBorder
+ Width of gradient border (from ODF)
+
+ @param fAngle
+ Gradient angle (from ODF)
+ */
+ ODFGradientInfo& createLinearODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
+ const B2DRange& rTargetArea,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fAngle);
+
+ /** Calculate linear gradient blend value
+
+ This method generates you the lerp alpha value for
+ blending linearly between gradient start and end color,
+ according to the formula (startCol*(1.0-alpha) + endCol*alpha)
+
+ @param rUV
+ Current uv coordinate. Values outside [0,1] will be
+ clamped.
+
+ @param rGradInfo
+ Gradient info, for transformation and number of steps
+ */
+ inline double getLinearGradientAlpha(const B2DPoint& rUV,
+ const ODFGradientInfo& rGradInfo )
+ {
+ const B2DPoint aCoor(rGradInfo.maBackTextureTransform * rUV);
+ const double t(clamp(aCoor.getY(), 0.0, 1.0));
+ const sal_uInt32 nSteps(rGradInfo.mnSteps);
+
+ if(nSteps > 2L && nSteps < 128L)
+ return floor(t * nSteps) / double(nSteps + 1L);
+
+ return t;
+ }
+
+ /** Create matrix for ODF's axial gradient definition
+
+ @param o_rGradientInfo
+ Receives the calculated texture transformation matrix (for
+ use with standard [0,1]x[0,1] texture coordinates)
+
+ @param rTargetArea
+ Output area, needed for aspect ratio calculations and
+ texture transformation
+
+ @param nSteps
+ Number of gradient steps (from ODF)
+
+ @param fBorder
+ Width of gradient border (from ODF)
+
+ @param fAngle
+ Gradient angle (from ODF)
+ */
+ ODFGradientInfo& createAxialODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
+ const B2DRange& rTargetArea,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fAngle);
+
+ /** Calculate axial gradient blend value
+
+ This method generates you the lerp alpha value for
+ blending linearly between gradient start and end color,
+ according to the formula (startCol*(1.0-alpha) + endCol*alpha)
+
+ @param rUV
+ Current uv coordinate. Values outside [0,1] will be
+ clamped.
+
+ @param rGradInfo
+ Gradient info, for transformation and number of steps
+ */
+ inline double getAxialGradientAlpha(const B2DPoint& rUV,
+ const ODFGradientInfo& rGradInfo )
+ {
+ const B2DPoint aCoor(rGradInfo.maBackTextureTransform * rUV);
+ const double t(clamp(fabs(aCoor.getY()), 0.0, 1.0));
+ const sal_uInt32 nSteps(rGradInfo.mnSteps);
+ const double fInternalSteps((nSteps * 2L) - 1L);
+
+ if(nSteps > 2L && nSteps < 128L)
+ return floor(((t * fInternalSteps) + 1.0) / 2.0) / double(nSteps - 1L);
+
+ return t;
+ }
+
+ /** Create matrix for ODF's radial gradient definition
+
+ @param o_rGradientInfo
+ Receives the calculated texture transformation matrix (for
+ use with standard [0,1]x[0,1] texture coordinates)
+
+ @param rTargetArea
+ Output area, needed for aspect ratio calculations and
+ texture transformation
+
+ @param rOffset
+ Gradient offset value (from ODF)
+
+ @param nSteps
+ Number of gradient steps (from ODF)
+
+ @param fBorder
+ Width of gradient border (from ODF)
+
+ @param fAngle
+ Gradient angle (from ODF)
+ */
+ ODFGradientInfo& createRadialODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
+ const B2DRange& rTargetArea,
+ const B2DVector& rOffset,
+ sal_uInt32 nSteps,
+ double fBorder);
+
+ /** Calculate radial gradient blend value
+
+ This method generates you the lerp alpha value for
+ blending linearly between gradient start and end color,
+ according to the formula (startCol*(1.0-alpha) + endCol*alpha)
+
+ @param rUV
+ Current uv coordinate. Values outside [0,1] will be
+ clamped.
+
+ @param rGradInfo
+ Gradient info, for transformation and number of steps
+ */
+ inline double getRadialGradientAlpha(const B2DPoint& rUV,
+ const ODFGradientInfo& rGradInfo )
+ {
+ const B2DPoint aCoor(rGradInfo.maBackTextureTransform * rUV);
+ const double fDist(
+ clamp(aCoor.getX() * aCoor.getX() + aCoor.getY() * aCoor.getY(),
+ 0.0,
+ 1.0));
+
+ const double t(1.0 - sqrt(fDist));
+ const sal_uInt32 nSteps(rGradInfo.mnSteps);
+
+ if(nSteps > 2L && nSteps < 128L)
+ return floor(t * nSteps) / double(nSteps - 1L);
+
+ return t;
+ }
+
+ /** Create matrix for ODF's elliptical gradient definition
+
+ @param o_rGradientInfo
+ Receives the calculated texture transformation matrix (for
+ use with standard [0,1]x[0,1] texture coordinates)
+
+ @param rTargetArea
+ Output area, needed for aspect ratio calculations and
+ texture transformation
+
+ @param rOffset
+ Gradient offset value (from ODF)
+
+ @param nSteps
+ Number of gradient steps (from ODF)
+
+ @param fBorder
+ Width of gradient border (from ODF)
+
+ @param fAngle
+ Gradient angle (from ODF)
+ */
+ ODFGradientInfo& createEllipticalODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
+ const B2DRange& rTargetArea,
+ const B2DVector& rOffset,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fAngle);
+
+ /** Calculate elliptical gradient blend value
+
+ This method generates you the lerp alpha value for
+ blending linearly between gradient start and end color,
+ according to the formula (startCol*(1.0-alpha) + endCol*alpha)
+
+ @param rUV
+ Current uv coordinate. Values outside [0,1] will be
+ clamped.
+
+ @param rGradInfo
+ Gradient info, for transformation and number of steps
+ */
+ inline double getEllipticalGradientAlpha(const B2DPoint& rUV,
+ const ODFGradientInfo& rGradInfo )
+ {
+ return getRadialGradientAlpha(rUV,rGradInfo); // only matrix setup differs
+ }
+
+ /** Create matrix for ODF's square gradient definition
+
+ @param o_rGradientInfo
+ Receives the calculated texture transformation matrix (for
+ use with standard [0,1]x[0,1] texture coordinates)
+
+ @param rTargetArea
+ Output area, needed for aspect ratio calculations and
+ texture transformation
+
+ @param rOffset
+ Gradient offset value (from ODF)
+
+ @param nSteps
+ Number of gradient steps (from ODF)
+
+ @param fBorder
+ Width of gradient border (from ODF)
+
+ @param fAngle
+ Gradient angle (from ODF)
+ */
+ ODFGradientInfo& createSquareODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
+ const B2DRange& rTargetArea,
+ const B2DVector& rOffset,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fAngle);
+
+ /** Calculate square gradient blend value
+
+ This method generates you the lerp alpha value for
+ blending linearly between gradient start and end color,
+ according to the formula (startCol*(1.0-alpha) + endCol*alpha)
+
+ @param rUV
+ Current uv coordinate. Values outside [0,1] will be
+ clamped.
+
+ @param rGradInfo
+ Gradient info, for transformation and number of steps
+ */
+ inline double getSquareGradientAlpha(const B2DPoint& rUV,
+ const ODFGradientInfo& rGradInfo )
+ {
+ const B2DPoint aCoor(rGradInfo.maBackTextureTransform * rUV);
+ const double fAbsX(fabs(aCoor.getX()));
+ const double fAbsY(fabs(aCoor.getY()));
+
+ if(fTools::moreOrEqual(fAbsX, 1.0) || fTools::moreOrEqual(fAbsY, 1.0))
+ return 0.0;
+
+ const double t(1.0 - (fAbsX > fAbsY ? fAbsX : fAbsY));
+ const sal_uInt32 nSteps(rGradInfo.mnSteps);
+
+ if(nSteps > 2L && nSteps < 128L)
+ return floor(t * nSteps) / double(nSteps - 1L);
+
+ return t;
+ }
+
+ /** Create matrix for ODF's rectangular gradient definition
+
+ @param o_rGradientInfo
+ Receives the calculated texture transformation matrix (for
+ use with standard [0,1]x[0,1] texture coordinates)
+
+ @param rTargetArea
+ Output area, needed for aspect ratio calculations and
+ texture transformation
+
+ @param rOffset
+ Gradient offset value (from ODF)
+
+ @param nSteps
+ Number of gradient steps (from ODF)
+
+ @param fBorder
+ Width of gradient border (from ODF)
+
+ @param fAngle
+ Gradient angle (from ODF)
+ */
+ ODFGradientInfo& createRectangularODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
+ const B2DRange& rTargetArea,
+ const B2DVector& rOffset,
+ sal_uInt32 nSteps,
+ double fBorder,
+ double fAngle);
+
+ /** Calculate rectangular gradient blend value
+
+ This method generates you the lerp alpha value for
+ blending linearly between gradient start and end color,
+ according to the formula (startCol*(1.0-alpha) + endCol*alpha)
+
+ @param rUV
+ Current uv coordinate. Values outside [0,1] will be
+ clamped.
+
+ @param rGradInfo
+ Gradient info, for transformation and number of steps
+ */
+ inline double getRectangularGradientAlpha(const B2DPoint& rUV,
+ const ODFGradientInfo& rGradInfo )
+ {
+ return getSquareGradientAlpha(rUV, rGradInfo); // only matrix setup differs
+ }
+
+ }
+}
+
+#endif
diff --git a/basegfx/inc/basegfx/tools/rectcliptools.hxx b/basegfx/inc/basegfx/tools/rectcliptools.hxx
new file mode 100644
index 000000000000..bfcd808ed19c
--- /dev/null
+++ b/basegfx/inc/basegfx/tools/rectcliptools.hxx
@@ -0,0 +1,91 @@
+/*************************************************************************
+ *
+ * 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: rectcliptools.hxx,v $
+ * $Revision: 1.3 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TOOLS_RECTCLIPTOOLS_HXX
+#define _BGFX_TOOLS_RECTCLIPTOOLS_HXX
+
+#include <sal/types.h>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ namespace RectClipFlags
+ {
+ static const sal_uInt32 LEFT = (sal_Int32)0x01;
+ static const sal_uInt32 RIGHT = (sal_Int32)0x02;
+ static const sal_uInt32 TOP = (sal_Int32)0x04;
+ static const sal_uInt32 BOTTOM = (sal_Int32)0x08;
+ }
+
+ /** Calc clip mask for Cohen-Sutherland rectangle clip
+
+ This function returns a clip mask used for the
+ Cohen-Sutherland rectangle clip method, where one or more
+ of the lower four bits are set, if the given point is
+ outside one or more of the four half planes defining the
+ rectangle (see RectClipFlags for possible values)
+ */
+ template< class Point, class Rect > inline
+ sal_uInt32 getCohenSutherlandClipFlags( const Point& rP,
+ const Rect& rR )
+ {
+ // maxY | minY | maxX | minX
+ sal_uInt32 clip = (rP.getX() < rR.getMinX()) << 0;
+ clip |= (rP.getX() > rR.getMaxX()) << 1;
+ clip |= (rP.getY() < rR.getMinY()) << 2;
+ clip |= (rP.getY() > rR.getMaxY()) << 3;
+ return clip;
+ }
+
+ /** Determine number of clip planes hit by given clip mask
+
+ This method returns the number of one bits in the four
+ least significant bits of the argument, which amounts to
+ the number of clip planes hit within the
+ getCohenSutherlandClipFlags() method.
+ */
+ inline sal_uInt32 getNumberOfClipPlanes( sal_uInt32 nFlags )
+ {
+ // classic bit count algo, see e.g. Reingold, Nievergelt,
+ // Deo: Combinatorial Algorithms, Theory and Practice,
+ // Prentice-Hall 1977
+ nFlags = (nFlags & 0x05) + ((nFlags >> 1) & 0x05);
+ nFlags = (nFlags & 0x03) + (nFlags >> 2); // no need for &
+ // 0x03, can't
+ // overflow
+ return nFlags;
+ }
+ }
+}
+
+#endif // _BGFX_TOOLS_RECTCLIPTOOLS_HXX
diff --git a/basegfx/inc/basegfx/tools/tools.hxx b/basegfx/inc/basegfx/tools/tools.hxx
new file mode 100644
index 000000000000..1e33ccbbf995
--- /dev/null
+++ b/basegfx/inc/basegfx/tools/tools.hxx
@@ -0,0 +1,134 @@
+/*************************************************************************
+ *
+ * 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: tools.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TOOLS_TOOLS_HXX
+#define _BGFX_TOOLS_TOOLS_HXX
+
+#include <sal/types.h>
+
+namespace basegfx
+{
+ class B2DPoint;
+ class B2DRange;
+
+ namespace tools
+ {
+ /** Liang-Barsky 2D line clipping algorithm
+
+ This function clips a line given by two points against the
+ given rectangle. The resulting line is returned in the
+ given points.
+
+ @param io_rStart
+ Start point of the line. On return, contains the clipped
+ start point.
+
+ @param io_rEnd
+ End point of the line. On return, contains the clipped
+ end point.
+
+ @param rClipRect
+ The rectangle to clip against
+
+ @return true, when at least part of the line is visible
+ after the clip, false otherwise
+ */
+ bool liangBarskyClip2D( ::basegfx::B2DPoint& io_rStart,
+ ::basegfx::B2DPoint& io_rEnd,
+ const ::basegfx::B2DRange& rClipRect );
+
+ /** Expand given parallelogram, such that it extends beyond
+ bound rect in a given direction.
+
+ This method is useful when e.g. generating one-dimensional
+ gradients, such as linear or axial gradients: those
+ gradients vary only in one direction, the other has
+ constant color. Most of the time, those gradients extends
+ infinitely in the direction with the constant color, but
+ practically, one always has a limiting bound rect into
+ which the gradient is painted. The method at hand now
+ extends a given parallelogram (e.g. the transformed
+ bounding box of a gradient) virtually into infinity to the
+ top and to the bottom (i.e. normal to the line io_rLeftTop
+ io_rRightTop), such that the given rectangle is guaranteed
+ to be covered in that direction.
+
+ @attention There might be some peculiarities with this
+ method, that might limit its usage to the described
+ gradients. One of them is the fact that when determining
+ how far the parallelogram has to be extended to the top or
+ the bottom, the upper and lower border are assumed to be
+ infinite lines.
+
+ @param io_rLeftTop
+ Left, top edge of the parallelogramm. Note that this need
+ not be the left, top edge geometrically, it's just used
+ when determining the extension direction. Thus, it's
+ perfectly legal to affine-transform a rectangle, and given
+ the transformed point here. On method return, this
+ parameter will contain the adapted output.
+
+ @param io_rLeftBottom
+ Left, bottom edge of the parallelogramm. Note that this need
+ not be the left, bottom edge geometrically, it's just used
+ when determining the extension direction. Thus, it's
+ perfectly legal to affine-transform a rectangle, and given
+ the transformed point here. On method return, this
+ parameter will contain the adapted output.
+
+ @param io_rRightTop
+ Right, top edge of the parallelogramm. Note that this need
+ not be the right, top edge geometrically, it's just used
+ when determining the extension direction. Thus, it's
+ perfectly legal to affine-transform a rectangle, and given
+ the transformed point here. On method return, this
+ parameter will contain the adapted output.
+
+ @param io_rRightBottom
+ Right, bottom edge of the parallelogramm. Note that this need
+ not be the right, bottom edge geometrically, it's just used
+ when determining the extension direction. Thus, it's
+ perfectly legal to affine-transform a rectangle, and given
+ the transformed point here. On method return, this
+ parameter will contain the adapted output.
+
+ @param rFitTarget
+ The rectangle to fit the parallelogram into.
+ */
+ void infiniteLineFromParallelogram( ::basegfx::B2DPoint& io_rLeftTop,
+ ::basegfx::B2DPoint& io_rLeftBottom,
+ ::basegfx::B2DPoint& io_rRightTop,
+ ::basegfx::B2DPoint& io_rRightBottom,
+ const ::basegfx::B2DRange& rFitTarget );
+
+ }
+}
+
+#endif /* _BGFX_TOOLS_TOOLS_HXX */
diff --git a/basegfx/inc/basegfx/tools/unopolypolygon.hxx b/basegfx/inc/basegfx/tools/unopolypolygon.hxx
new file mode 100755
index 000000000000..db5b1ca22427
--- /dev/null
+++ b/basegfx/inc/basegfx/tools/unopolypolygon.hxx
@@ -0,0 +1,116 @@
+/*************************************************************************
+ *
+ * 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: unopolypolygon.hxx,v $
+ *
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_BASEGFX_UNOPOLYPOLYGON_HXX
+#define INCLUDED_BASEGFX_UNOPOLYPOLYGON_HXX
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase3.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/rendering/FillRule.hpp>
+#include <com/sun/star/rendering/XLinePolyPolygon2D.hpp>
+#include <com/sun/star/rendering/XBezierPolyPolygon2D.hpp>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+
+namespace basegfx
+{
+namespace unotools
+{
+ typedef ::cppu::WeakComponentImplHelper3<
+ ::com::sun::star::rendering::XLinePolyPolygon2D,
+ ::com::sun::star::rendering::XBezierPolyPolygon2D,
+ ::com::sun::star::lang::XServiceInfo > UnoPolyPolygonBase;
+
+ class UnoPolyPolygon : private cppu::BaseMutex,
+ public UnoPolyPolygonBase
+ {
+ public:
+ explicit UnoPolyPolygon( const B2DPolyPolygon& );
+
+ // XPolyPolygon2D
+ virtual void SAL_CALL addPolyPolygon( const ::com::sun::star::geometry::RealPoint2D& position, const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XPolyPolygon2D >& polyPolygon ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException);
+ virtual ::sal_Int32 SAL_CALL getNumberOfPolygons( ) throw (::com::sun::star::uno::RuntimeException);
+ virtual ::sal_Int32 SAL_CALL getNumberOfPolygonPoints( ::sal_Int32 polygon ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+ virtual ::com::sun::star::rendering::FillRule SAL_CALL getFillRule( ) throw (::com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL setFillRule( ::com::sun::star::rendering::FillRule fillRule ) throw (::com::sun::star::uno::RuntimeException);
+ virtual ::sal_Bool SAL_CALL isClosed( ::sal_Int32 index ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL setClosed( ::sal_Int32 index, ::sal_Bool closedState ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+
+ // XLinePolyPolygon2D
+ virtual ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< ::com::sun::star::geometry::RealPoint2D > > SAL_CALL getPoints( ::sal_Int32 nPolygonIndex, ::sal_Int32 nNumberOfPolygons, ::sal_Int32 nPointIndex, ::sal_Int32 nNumberOfPoints ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL setPoints( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< ::com::sun::star::geometry::RealPoint2D > >& points, ::sal_Int32 nPolygonIndex ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+ virtual ::com::sun::star::geometry::RealPoint2D SAL_CALL getPoint( ::sal_Int32 nPolygonIndex, ::sal_Int32 nPointIndex ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL setPoint( const ::com::sun::star::geometry::RealPoint2D& point, ::sal_Int32 nPolygonIndex, ::sal_Int32 nPointIndex ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+
+ // XBezierPolyPolygon2D
+ virtual ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< ::com::sun::star::geometry::RealBezierSegment2D > > SAL_CALL getBezierSegments( ::sal_Int32 nPolygonIndex, ::sal_Int32 nNumberOfPolygons, ::sal_Int32 nPointIndex, ::sal_Int32 nNumberOfPoints ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL setBezierSegments( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< ::com::sun::star::geometry::RealBezierSegment2D > >& points, ::sal_Int32 nPolygonIndex ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+ virtual ::com::sun::star::geometry::RealBezierSegment2D SAL_CALL getBezierSegment( ::sal_Int32 nPolygonIndex, ::sal_Int32 nPointIndex ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL setBezierSegment( const ::com::sun::star::geometry::RealBezierSegment2D& point, ::sal_Int32 nPolygonIndex, ::sal_Int32 nPointIndex ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
+
+ // XServiceInfo
+ virtual ::rtl::OUString SAL_CALL getImplementationName() throw( ::com::sun::star::uno::RuntimeException );
+ virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw( ::com::sun::star::uno::RuntimeException );
+ virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() throw( ::com::sun::star::uno::RuntimeException );
+
+ B2DPolyPolygon getPolyPolygon() const;
+
+ protected:
+ /// Check whether index is a valid polygon index
+ void checkIndex( sal_Int32 nIndex ) const // throw (::com::sun::star::lang::IndexOutOfBoundsException);
+ {
+ if( nIndex < 0 || nIndex >= static_cast<sal_Int32>(maPolyPoly.count()) )
+ throw ::com::sun::star::lang::IndexOutOfBoundsException();
+ }
+
+ B2DPolyPolygon getSubsetPolyPolygon( sal_Int32 nPolygonIndex,
+ sal_Int32 nNumberOfPolygons,
+ sal_Int32 nPointIndex,
+ sal_Int32 nNumberOfPoints ) const;
+
+ /// Get cow copy of internal polygon. not thread-safe outside this object.
+ B2DPolyPolygon getPolyPolygonUnsafe() const;
+
+ /// Called whenever internal polypolygon gets modified
+ virtual void modifying() const {}
+
+ private:
+ UnoPolyPolygon( const UnoPolyPolygon& );
+ UnoPolyPolygon& operator=( const UnoPolyPolygon& );
+
+ B2DPolyPolygon maPolyPoly;
+ ::com::sun::star::rendering::FillRule meFillRule;
+ };
+}
+}
+
+#endif /* INCLUDED_BASEGFX_UNOPOLYPOLYGON_HXX */
diff --git a/basegfx/inc/basegfx/tuple/b2dtuple.hxx b/basegfx/inc/basegfx/tuple/b2dtuple.hxx
new file mode 100644
index 000000000000..7c4c1d585d2c
--- /dev/null
+++ b/basegfx/inc/basegfx/tuple/b2dtuple.hxx
@@ -0,0 +1,360 @@
+/*************************************************************************
+ *
+ * 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: b2dtuple.hxx,v $
+ * $Revision: 1.16 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TUPLE_B2DTUPLE_HXX
+#define _BGFX_TUPLE_B2DTUPLE_HXX
+
+#include <sal/types.h>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ // predeclarations
+ class B2ITuple;
+
+ /** Base class for all Points/Vectors with two double values
+
+ This class provides all methods common to Point
+ avd Vector classes which are derived from here.
+
+ @derive Use this class to implement Points or Vectors
+ which are based on two double values
+ */
+ class B2DTuple
+ {
+ protected:
+ double mfX;
+ double mfY;
+
+ public:
+ /** Create a 2D Tuple
+
+ The tuple is initialized to (0.0, 0.0)
+ */
+ B2DTuple()
+ : mfX(0.0),
+ mfY(0.0)
+ {}
+
+ /** Create a 2D Tuple
+
+ @param fX
+ This parameter is used to initialize the X-coordinate
+ of the 2D Tuple.
+
+ @param fY
+ This parameter is used to initialize the Y-coordinate
+ of the 2D Tuple.
+ */
+ B2DTuple(double fX, double fY)
+ : mfX( fX ),
+ mfY( fY )
+ {}
+
+ /** Create a copy of a 2D Tuple
+
+ @param rTup
+ The 2D Tuple which will be copied.
+ */
+ B2DTuple(const B2DTuple& rTup)
+ : mfX( rTup.mfX ),
+ mfY( rTup.mfY )
+ {}
+
+ /** Create a copy of a 2D integer Tuple
+
+ @param rTup
+ The 2D Tuple which will be copied.
+ */
+ explicit B2DTuple(const B2ITuple& rTup);
+
+ ~B2DTuple()
+ {}
+
+ /// Get X-Coordinate of 2D Tuple
+ double getX() const
+ {
+ return mfX;
+ }
+
+ /// Get Y-Coordinate of 2D Tuple
+ double getY() const
+ {
+ return mfY;
+ }
+
+ /// Set X-Coordinate of 2D Tuple
+ void setX(double fX)
+ {
+ mfX = fX;
+ }
+
+ /// Set Y-Coordinate of 2D Tuple
+ void setY(double fY)
+ {
+ mfY = fY;
+ }
+
+ /// Array-access to 2D Tuple
+ const double& operator[] (int nPos) const
+ {
+ // Here, normally one if(...) should be used. In the assumption that
+ // both double members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mfX; return mfY;
+ return *((&mfX) + nPos);
+ }
+
+ /// Array-access to 2D Tuple
+ double& operator[] (int nPos)
+ {
+ // Here, normally one if(...) should be used. In the assumption that
+ // both double members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mfX; return mfY;
+ return *((&mfX) + nPos);
+ }
+
+ // comparators with tolerance
+ //////////////////////////////////////////////////////////////////////
+
+ bool equalZero() const
+ {
+ return (this == &getEmptyTuple() ||
+ (fTools::equalZero(mfX) && fTools::equalZero(mfY)));
+ }
+
+ bool equalZero(const double& rfSmallValue) const
+ {
+ return (this == &getEmptyTuple() ||
+ (fTools::equalZero(mfX, rfSmallValue) && fTools::equalZero(mfY, rfSmallValue)));
+ }
+
+ bool equal(const B2DTuple& rTup) const
+ {
+ return (
+ fTools::equal(mfX, rTup.mfX) &&
+ fTools::equal(mfY, rTup.mfY));
+ }
+
+ bool equal(const B2DTuple& rTup, const double& rfSmallValue) const
+ {
+ return (
+ fTools::equal(mfX, rTup.mfX, rfSmallValue) &&
+ fTools::equal(mfY, rTup.mfY, rfSmallValue));
+ }
+
+ // operators
+ //////////////////////////////////////////////////////////////////////
+
+ B2DTuple& operator+=( const B2DTuple& rTup )
+ {
+ mfX += rTup.mfX;
+ mfY += rTup.mfY;
+ return *this;
+ }
+
+ B2DTuple& operator-=( const B2DTuple& rTup )
+ {
+ mfX -= rTup.mfX;
+ mfY -= rTup.mfY;
+ return *this;
+ }
+
+ B2DTuple& operator/=( const B2DTuple& rTup )
+ {
+ mfX /= rTup.mfX;
+ mfY /= rTup.mfY;
+ return *this;
+ }
+
+ B2DTuple& operator*=( const B2DTuple& rTup )
+ {
+ mfX *= rTup.mfX;
+ mfY *= rTup.mfY;
+ return *this;
+ }
+
+ B2DTuple& operator*=(double t)
+ {
+ mfX *= t;
+ mfY *= t;
+ return *this;
+ }
+
+ B2DTuple& operator/=(double t)
+ {
+ const double fVal(1.0 / t);
+ mfX *= fVal;
+ mfY *= fVal;
+ return *this;
+ }
+
+ B2DTuple operator-(void) const
+ {
+ return B2DTuple(-mfX, -mfY);
+ }
+
+ bool operator==( const B2DTuple& rTup ) const
+ {
+ return equal(rTup);
+ }
+
+ bool operator!=( const B2DTuple& rTup ) const
+ {
+ return !equal(rTup);
+ }
+
+ B2DTuple& operator=( const B2DTuple& rTup )
+ {
+ mfX = rTup.mfX;
+ mfY = rTup.mfY;
+ return *this;
+ }
+
+ void correctValues(const double fCompareValue = 0.0);
+
+ static const B2DTuple& getEmptyTuple();
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ inline B2DTuple minimum(const B2DTuple& rTupA, const B2DTuple& rTupB)
+ {
+ B2DTuple aMin(
+ (rTupB.getX() < rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() < rTupA.getY()) ? rTupB.getY() : rTupA.getY());
+ return aMin;
+ }
+
+ inline B2DTuple maximum(const B2DTuple& rTupA, const B2DTuple& rTupB)
+ {
+ B2DTuple aMax(
+ (rTupB.getX() > rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() > rTupA.getY()) ? rTupB.getY() : rTupA.getY());
+ return aMax;
+ }
+
+ inline B2DTuple absolute(const B2DTuple& rTup)
+ {
+ B2DTuple aAbs(
+ (0.0 > rTup.getX()) ? -rTup.getX() : rTup.getX(),
+ (0.0 > rTup.getY()) ? -rTup.getY() : rTup.getY());
+ return aAbs;
+ }
+
+ inline B2DTuple interpolate(const B2DTuple& rOld1, const B2DTuple& rOld2, double t)
+ {
+ B2DTuple aInt(
+ ((rOld2.getX() - rOld1.getX()) * t) + rOld1.getX(),
+ ((rOld2.getY() - rOld1.getY()) * t) + rOld1.getY());
+ return aInt;
+ }
+
+ inline B2DTuple average(const B2DTuple& rOld1, const B2DTuple& rOld2)
+ {
+ B2DTuple aAvg(
+ (rOld1.getX() + rOld2.getX()) * 0.5,
+ (rOld1.getY() + rOld2.getY()) * 0.5);
+ return aAvg;
+ }
+
+ inline B2DTuple average(const B2DTuple& rOld1, const B2DTuple& rOld2, const B2DTuple& rOld3)
+ {
+ B2DTuple aAvg(
+ (rOld1.getX() + rOld2.getX() + rOld3.getX()) * (1.0 / 3.0),
+ (rOld1.getY() + rOld2.getY() + rOld3.getY()) * (1.0 / 3.0));
+ return aAvg;
+ }
+
+ inline B2DTuple operator+(const B2DTuple& rTupA, const B2DTuple& rTupB)
+ {
+ B2DTuple aSum(rTupA);
+ aSum += rTupB;
+ return aSum;
+ }
+
+ inline B2DTuple operator-(const B2DTuple& rTupA, const B2DTuple& rTupB)
+ {
+ B2DTuple aSub(rTupA);
+ aSub -= rTupB;
+ return aSub;
+ }
+
+ inline B2DTuple operator/(const B2DTuple& rTupA, const B2DTuple& rTupB)
+ {
+ B2DTuple aDiv(rTupA);
+ aDiv /= rTupB;
+ return aDiv;
+ }
+
+ inline B2DTuple operator*(const B2DTuple& rTupA, const B2DTuple& rTupB)
+ {
+ B2DTuple aMul(rTupA);
+ aMul *= rTupB;
+ return aMul;
+ }
+
+ inline B2DTuple operator*(const B2DTuple& rTup, double t)
+ {
+ B2DTuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B2DTuple operator*(double t, const B2DTuple& rTup)
+ {
+ B2DTuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B2DTuple operator/(const B2DTuple& rTup, double t)
+ {
+ B2DTuple aNew(rTup);
+ aNew /= t;
+ return aNew;
+ }
+
+ inline B2DTuple operator/(double t, const B2DTuple& rTup)
+ {
+ B2DTuple aNew(t, t);
+ B2DTuple aTmp(rTup);
+ aNew /= aTmp;
+ return aNew;
+ }
+
+ /** Round double to nearest integer for 2D tuple
+
+ @return the nearest integer for this tuple
+ */
+ B2ITuple fround(const B2DTuple& rTup);
+} // end of namespace basegfx
+
+#endif /* _BGFX_TUPLE_B2DTUPLE_HXX */
diff --git a/basegfx/inc/basegfx/tuple/b2i64tuple.hxx b/basegfx/inc/basegfx/tuple/b2i64tuple.hxx
new file mode 100644
index 000000000000..627feb9f7875
--- /dev/null
+++ b/basegfx/inc/basegfx/tuple/b2i64tuple.hxx
@@ -0,0 +1,315 @@
+/*************************************************************************
+ *
+ * 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: b2i64tuple.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TUPLE_B2I64TUPLE_HXX
+#define _BGFX_TUPLE_B2I64TUPLE_HXX
+
+#include <sal/types.h>
+#include <basegfx/tuple/b2dtuple.hxx>
+
+
+namespace basegfx
+{
+ /** Base class for all Points/Vectors with two sal_Int64 values
+
+ This class provides all methods common to Point
+ avd Vector classes which are derived from here.
+
+ @derive Use this class to implement Points or Vectors
+ which are based on two sal_Int64 values
+ */
+ class B2I64Tuple
+ {
+ protected:
+ sal_Int64 mnX;
+ sal_Int64 mnY;
+
+ public:
+ /** Create a 2D Tuple
+
+ The tuple is initialized to (0, 0)
+ */
+ B2I64Tuple()
+ : mnX(0),
+ mnY(0)
+ {}
+
+ /** Create a 2D Tuple
+
+ @param fX
+ This parameter is used to initialize the X-coordinate
+ of the 2D Tuple.
+
+ @param fY
+ This parameter is used to initialize the Y-coordinate
+ of the 2D Tuple.
+ */
+ B2I64Tuple(sal_Int64 fX, sal_Int64 fY)
+ : mnX( fX ),
+ mnY( fY )
+ {}
+
+ /** Create a copy of a 2D Tuple
+
+ @param rTup
+ The 2D Tuple which will be copied.
+ */
+ B2I64Tuple(const B2I64Tuple& rTup)
+ : mnX( rTup.mnX ),
+ mnY( rTup.mnY )
+ {}
+
+ ~B2I64Tuple()
+ {}
+
+ /// Get X-Coordinate of 2D Tuple
+ sal_Int64 getX() const
+ {
+ return mnX;
+ }
+
+ /// Get Y-Coordinate of 2D Tuple
+ sal_Int64 getY() const
+ {
+ return mnY;
+ }
+
+ /// Set X-Coordinate of 2D Tuple
+ void setX(sal_Int64 fX)
+ {
+ mnX = fX;
+ }
+
+ /// Set Y-Coordinate of 2D Tuple
+ void setY(sal_Int64 fY)
+ {
+ mnY = fY;
+ }
+
+ /// Array-access to 2D Tuple
+ const sal_Int64& operator[] (int nPos) const
+ {
+ // Here, normally one if(...) should be used. In the assumption that
+ // both sal_Int64 members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mnX; return mnY;
+ return *((&mnX) + nPos);
+ }
+
+ /// Array-access to 2D Tuple
+ sal_Int64& operator[] (int nPos)
+ {
+ // Here, normally one if(...) should be used. In the assumption that
+ // both sal_Int64 members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mnX; return mnY;
+ return *((&mnX) + nPos);
+ }
+
+ // operators
+ //////////////////////////////////////////////////////////////////////
+
+ B2I64Tuple& operator+=( const B2I64Tuple& rTup )
+ {
+ mnX += rTup.mnX;
+ mnY += rTup.mnY;
+ return *this;
+ }
+
+ B2I64Tuple& operator-=( const B2I64Tuple& rTup )
+ {
+ mnX -= rTup.mnX;
+ mnY -= rTup.mnY;
+ return *this;
+ }
+
+ B2I64Tuple& operator/=( const B2I64Tuple& rTup )
+ {
+ mnX /= rTup.mnX;
+ mnY /= rTup.mnY;
+ return *this;
+ }
+
+ B2I64Tuple& operator*=( const B2I64Tuple& rTup )
+ {
+ mnX *= rTup.mnX;
+ mnY *= rTup.mnY;
+ return *this;
+ }
+
+ B2I64Tuple& operator*=(sal_Int64 t)
+ {
+ mnX *= t;
+ mnY *= t;
+ return *this;
+ }
+
+ B2I64Tuple& operator/=(sal_Int64 t)
+ {
+ mnX /= t;
+ mnY /= t;
+ return *this;
+ }
+
+ B2I64Tuple operator-(void) const
+ {
+ return B2I64Tuple(-mnX, -mnY);
+ }
+
+ bool equalZero() const { return mnX == 0 && mnY == 0; }
+
+ bool operator==( const B2I64Tuple& rTup ) const
+ {
+ return rTup.mnX == mnX && rTup.mnY == mnY;
+ }
+
+ bool operator!=( const B2I64Tuple& rTup ) const
+ {
+ return !(*this == rTup);
+ }
+
+ B2I64Tuple& operator=( const B2I64Tuple& rTup )
+ {
+ mnX = rTup.mnX;
+ mnY = rTup.mnY;
+ return *this;
+ }
+
+ static const B2I64Tuple& getEmptyTuple();
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ inline B2I64Tuple minimum(const B2I64Tuple& rTupA, const B2I64Tuple& rTupB)
+ {
+ B2I64Tuple aMin(
+ (rTupB.getX() < rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() < rTupA.getY()) ? rTupB.getY() : rTupA.getY());
+ return aMin;
+ }
+
+ inline B2I64Tuple maximum(const B2I64Tuple& rTupA, const B2I64Tuple& rTupB)
+ {
+ B2I64Tuple aMax(
+ (rTupB.getX() > rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() > rTupA.getY()) ? rTupB.getY() : rTupA.getY());
+ return aMax;
+ }
+
+ inline B2I64Tuple absolute(const B2I64Tuple& rTup)
+ {
+ B2I64Tuple aAbs(
+ (0 > rTup.getX()) ? -rTup.getX() : rTup.getX(),
+ (0 > rTup.getY()) ? -rTup.getY() : rTup.getY());
+ return aAbs;
+ }
+
+ inline B2DTuple interpolate(const B2I64Tuple& rOld1, const B2I64Tuple& rOld2, double t)
+ {
+ B2DTuple aInt(
+ ((rOld2.getX() - rOld1.getX()) * t) + rOld1.getX(),
+ ((rOld2.getY() - rOld1.getY()) * t) + rOld1.getY());
+ return aInt;
+ }
+
+ inline B2DTuple average(const B2I64Tuple& rOld1, const B2I64Tuple& rOld2)
+ {
+ B2DTuple aAvg(
+ (rOld1.getX() + rOld2.getX()) * 0.5,
+ (rOld1.getY() + rOld2.getY()) * 0.5);
+ return aAvg;
+ }
+
+ inline B2DTuple average(const B2I64Tuple& rOld1, const B2I64Tuple& rOld2, const B2I64Tuple& rOld3)
+ {
+ B2DTuple aAvg(
+ (rOld1.getX() + rOld2.getX() + rOld3.getX()) * (1.0 / 3.0),
+ (rOld1.getY() + rOld2.getY() + rOld3.getY()) * (1.0 / 3.0));
+ return aAvg;
+ }
+
+ inline B2I64Tuple operator+(const B2I64Tuple& rTupA, const B2I64Tuple& rTupB)
+ {
+ B2I64Tuple aSum(rTupA);
+ aSum += rTupB;
+ return aSum;
+ }
+
+ inline B2I64Tuple operator-(const B2I64Tuple& rTupA, const B2I64Tuple& rTupB)
+ {
+ B2I64Tuple aSub(rTupA);
+ aSub -= rTupB;
+ return aSub;
+ }
+
+ inline B2I64Tuple operator/(const B2I64Tuple& rTupA, const B2I64Tuple& rTupB)
+ {
+ B2I64Tuple aDiv(rTupA);
+ aDiv /= rTupB;
+ return aDiv;
+ }
+
+ inline B2I64Tuple operator*(const B2I64Tuple& rTupA, const B2I64Tuple& rTupB)
+ {
+ B2I64Tuple aMul(rTupA);
+ aMul *= rTupB;
+ return aMul;
+ }
+
+ inline B2I64Tuple operator*(const B2I64Tuple& rTup, sal_Int64 t)
+ {
+ B2I64Tuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B2I64Tuple operator*(sal_Int64 t, const B2I64Tuple& rTup)
+ {
+ B2I64Tuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B2I64Tuple operator/(const B2I64Tuple& rTup, sal_Int64 t)
+ {
+ B2I64Tuple aNew(rTup);
+ aNew /= t;
+ return aNew;
+ }
+
+ inline B2I64Tuple operator/(sal_Int64 t, const B2I64Tuple& rTup)
+ {
+ B2I64Tuple aNew(t, t);
+ B2I64Tuple aTmp(rTup);
+ aNew /= aTmp;
+ return aNew;
+ }
+} // end of namespace basegfx
+
+#endif /* _BGFX_TUPLE_B2I64TUPLE_HXX */
diff --git a/basegfx/inc/basegfx/tuple/b2ituple.hxx b/basegfx/inc/basegfx/tuple/b2ituple.hxx
new file mode 100644
index 000000000000..bee473e88837
--- /dev/null
+++ b/basegfx/inc/basegfx/tuple/b2ituple.hxx
@@ -0,0 +1,240 @@
+/*************************************************************************
+ *
+ * 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: b2ituple.hxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TUPLE_B2ITUPLE_HXX
+#define _BGFX_TUPLE_B2ITUPLE_HXX
+
+#include <sal/types.h>
+
+
+namespace basegfx
+{
+ /** Base class for all Points/Vectors with two sal_Int32 values
+
+ This class provides all methods common to Point
+ avd Vector classes which are derived from here.
+
+ @derive Use this class to implement Points or Vectors
+ which are based on two sal_Int32 values
+ */
+ class B2ITuple
+ {
+ protected:
+ sal_Int32 mnX;
+ sal_Int32 mnY;
+
+ public:
+ /** Create a 2D Tuple
+
+ The tuple is initialized to (0, 0)
+ */
+ B2ITuple()
+ : mnX(0),
+ mnY(0)
+ {}
+
+ /** Create a 2D Tuple
+
+ @param fX
+ This parameter is used to initialize the X-coordinate
+ of the 2D Tuple.
+
+ @param fY
+ This parameter is used to initialize the Y-coordinate
+ of the 2D Tuple.
+ */
+ B2ITuple(sal_Int32 fX, sal_Int32 fY)
+ : mnX( fX ),
+ mnY( fY )
+ {}
+
+ /** Create a copy of a 2D Tuple
+
+ @param rTup
+ The 2D Tuple which will be copied.
+ */
+ B2ITuple(const B2ITuple& rTup)
+ : mnX( rTup.mnX ),
+ mnY( rTup.mnY )
+ {}
+
+ ~B2ITuple()
+ {}
+
+ /// Get X-Coordinate of 2D Tuple
+ sal_Int32 getX() const
+ {
+ return mnX;
+ }
+
+ /// Get Y-Coordinate of 2D Tuple
+ sal_Int32 getY() const
+ {
+ return mnY;
+ }
+
+ /// Set X-Coordinate of 2D Tuple
+ void setX(sal_Int32 fX)
+ {
+ mnX = fX;
+ }
+
+ /// Set Y-Coordinate of 2D Tuple
+ void setY(sal_Int32 fY)
+ {
+ mnY = fY;
+ }
+
+ /// Array-access to 2D Tuple
+ const sal_Int32& operator[] (int nPos) const
+ {
+ // Here, normally one if(...) should be used. In the assumption that
+ // both sal_Int32 members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mnX; return mnY;
+ return *((&mnX) + nPos);
+ }
+
+ /// Array-access to 2D Tuple
+ sal_Int32& operator[] (int nPos)
+ {
+ // Here, normally one if(...) should be used. In the assumption that
+ // both sal_Int32 members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mnX; return mnY;
+ return *((&mnX) + nPos);
+ }
+
+ // operators
+ //////////////////////////////////////////////////////////////////////
+
+ B2ITuple& operator+=( const B2ITuple& rTup )
+ {
+ mnX += rTup.mnX;
+ mnY += rTup.mnY;
+ return *this;
+ }
+
+ B2ITuple& operator-=( const B2ITuple& rTup )
+ {
+ mnX -= rTup.mnX;
+ mnY -= rTup.mnY;
+ return *this;
+ }
+
+ B2ITuple& operator/=( const B2ITuple& rTup )
+ {
+ mnX /= rTup.mnX;
+ mnY /= rTup.mnY;
+ return *this;
+ }
+
+ B2ITuple& operator*=( const B2ITuple& rTup )
+ {
+ mnX *= rTup.mnX;
+ mnY *= rTup.mnY;
+ return *this;
+ }
+
+ B2ITuple& operator*=(sal_Int32 t)
+ {
+ mnX *= t;
+ mnY *= t;
+ return *this;
+ }
+
+ B2ITuple& operator/=(sal_Int32 t)
+ {
+ mnX /= t;
+ mnY /= t;
+ return *this;
+ }
+
+ B2ITuple operator-(void) const
+ {
+ return B2ITuple(-mnX, -mnY);
+ }
+
+ bool equalZero() const { return mnX == 0 && mnY == 0; }
+
+ bool operator==( const B2ITuple& rTup ) const
+ {
+ return rTup.mnX == mnX && rTup.mnY == mnY;
+ }
+
+ bool operator!=( const B2ITuple& rTup ) const
+ {
+ return !(*this == rTup);
+ }
+
+ B2ITuple& operator=( const B2ITuple& rTup )
+ {
+ mnX = rTup.mnX;
+ mnY = rTup.mnY;
+ return *this;
+ }
+
+ static const B2ITuple& getEmptyTuple();
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ class B2DTuple;
+
+ B2ITuple minimum(const B2ITuple& rTupA, const B2ITuple& rTupB);
+
+ B2ITuple maximum(const B2ITuple& rTupA, const B2ITuple& rTupB);
+
+ B2ITuple absolute(const B2ITuple& rTup);
+
+ B2DTuple interpolate(const B2ITuple& rOld1, const B2ITuple& rOld2, double t);
+
+ B2DTuple average(const B2ITuple& rOld1, const B2ITuple& rOld2);
+
+ B2DTuple average(const B2ITuple& rOld1, const B2ITuple& rOld2, const B2ITuple& rOld3);
+
+ B2ITuple operator+(const B2ITuple& rTupA, const B2ITuple& rTupB);
+
+ B2ITuple operator-(const B2ITuple& rTupA, const B2ITuple& rTupB);
+
+ B2ITuple operator/(const B2ITuple& rTupA, const B2ITuple& rTupB);
+
+ B2ITuple operator*(const B2ITuple& rTupA, const B2ITuple& rTupB);
+
+ B2ITuple operator*(const B2ITuple& rTup, sal_Int32 t);
+
+ B2ITuple operator*(sal_Int32 t, const B2ITuple& rTup);
+
+ B2ITuple operator/(const B2ITuple& rTup, sal_Int32 t);
+
+ B2ITuple operator/(sal_Int32 t, const B2ITuple& rTup);
+
+} // end of namespace basegfx
+
+#endif /* _BGFX_TUPLE_B2ITUPLE_HXX */
diff --git a/basegfx/inc/basegfx/tuple/b3dtuple.hxx b/basegfx/inc/basegfx/tuple/b3dtuple.hxx
new file mode 100644
index 000000000000..04711aae017f
--- /dev/null
+++ b/basegfx/inc/basegfx/tuple/b3dtuple.hxx
@@ -0,0 +1,434 @@
+/*************************************************************************
+ *
+ * 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: b3dtuple.hxx,v $
+ * $Revision: 1.15 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TUPLE_B3DTUPLE_HXX
+#define _BGFX_TUPLE_B3DTUPLE_HXX
+
+#include <sal/types.h>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ // predeclarations
+ class B3ITuple;
+
+ /** Base class for all Points/Vectors with three double values
+
+ This class provides all methods common to Point
+ avd Vector classes which are derived from here.
+
+ @derive Use this class to implement Points or Vectors
+ which are based on three double values
+ */
+ class B3DTuple
+ {
+ protected:
+ double mfX;
+ double mfY;
+ double mfZ;
+
+ public:
+ /** Create a 3D Tuple
+
+ The tuple is initialized to (0.0, 0.0, 0.0)
+ */
+ B3DTuple()
+ : mfX(0.0),
+ mfY(0.0),
+ mfZ(0.0)
+ {}
+
+ /** Create a 3D Tuple
+
+ @param fX
+ This parameter is used to initialize the X-coordinate
+ of the 3D Tuple.
+
+ @param fY
+ This parameter is used to initialize the Y-coordinate
+ of the 3D Tuple.
+
+ @param fZ
+ This parameter is used to initialize the Z-coordinate
+ of the 3D Tuple.
+ */
+ B3DTuple(double fX, double fY, double fZ)
+ : mfX(fX),
+ mfY(fY),
+ mfZ(fZ)
+ {}
+
+ /** Create a copy of a 3D Tuple
+
+ @param rTup
+ The 3D Tuple which will be copied.
+ */
+ B3DTuple(const B3DTuple& rTup)
+ : mfX( rTup.mfX ),
+ mfY( rTup.mfY ),
+ mfZ( rTup.mfZ )
+ {}
+
+ /** Create a copy of a 3D integer Tuple
+
+ @param rTup
+ The 3D Tuple which will be copied.
+ */
+ explicit B3DTuple(const B3ITuple& rTup);
+
+ ~B3DTuple()
+ {}
+
+ /// get X-Coordinate of 3D Tuple
+ double getX() const
+ {
+ return mfX;
+ }
+
+ /// get Y-Coordinate of 3D Tuple
+ double getY() const
+ {
+ return mfY;
+ }
+
+ /// get Z-Coordinate of 3D Tuple
+ double getZ() const
+ {
+ return mfZ;
+ }
+
+ /// set X-Coordinate of 3D Tuple
+ void setX(double fX)
+ {
+ mfX = fX;
+ }
+
+ /// set Y-Coordinate of 3D Tuple
+ void setY(double fY)
+ {
+ mfY = fY;
+ }
+
+ /// set Z-Coordinate of 3D Tuple
+ void setZ(double fZ)
+ {
+ mfZ = fZ;
+ }
+
+ /// Array-access to 3D Tuple
+ const double& operator[] (int nPos) const
+ {
+ // Here, normally two if(...)'s should be used. In the assumption that
+ // both double members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mfX; if(1 == nPos) return mfY; return mfZ;
+ return *((&mfX) + nPos);
+ }
+
+ /// Array-access to 3D Tuple
+ double& operator[] (int nPos)
+ {
+ // Here, normally two if(...)'s should be used. In the assumption that
+ // both double members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mfX; if(1 == nPos) return mfY; return mfZ;
+ return *((&mfX) + nPos);
+ }
+
+ // comparators with tolerance
+ //////////////////////////////////////////////////////////////////////
+
+ bool equalZero() const
+ {
+ return (this == &getEmptyTuple() ||
+ (::basegfx::fTools::equalZero(mfX)
+ && ::basegfx::fTools::equalZero(mfY)
+ && ::basegfx::fTools::equalZero(mfZ)));
+ }
+
+ bool equalZero(const double& rfSmallValue) const
+ {
+ return (this == &getEmptyTuple() ||
+ (::basegfx::fTools::equalZero(mfX, rfSmallValue)
+ && ::basegfx::fTools::equalZero(mfY, rfSmallValue)
+ && ::basegfx::fTools::equalZero(mfZ, rfSmallValue)));
+ }
+
+ bool equal(const B3DTuple& rTup) const
+ {
+ return (
+ ::basegfx::fTools::equal(mfX, rTup.mfX) &&
+ ::basegfx::fTools::equal(mfY, rTup.mfY) &&
+ ::basegfx::fTools::equal(mfZ, rTup.mfZ));
+ }
+
+ bool equal(const B3DTuple& rTup, const double& rfSmallValue) const
+ {
+ return (
+ ::basegfx::fTools::equal(mfX, rTup.mfX, rfSmallValue) &&
+ ::basegfx::fTools::equal(mfY, rTup.mfY, rfSmallValue) &&
+ ::basegfx::fTools::equal(mfZ, rTup.mfZ, rfSmallValue));
+ }
+
+ // operators
+ //////////////////////////////////////////////////////////////////////
+
+ B3DTuple& operator+=( const B3DTuple& rTup )
+ {
+ mfX += rTup.mfX;
+ mfY += rTup.mfY;
+ mfZ += rTup.mfZ;
+ return *this;
+ }
+
+ B3DTuple& operator-=( const B3DTuple& rTup )
+ {
+ mfX -= rTup.mfX;
+ mfY -= rTup.mfY;
+ mfZ -= rTup.mfZ;
+ return *this;
+ }
+
+ B3DTuple& operator/=( const B3DTuple& rTup )
+ {
+ mfX /= rTup.mfX;
+ mfY /= rTup.mfY;
+ mfZ /= rTup.mfZ;
+ return *this;
+ }
+
+ B3DTuple& operator*=( const B3DTuple& rTup )
+ {
+ mfX *= rTup.mfX;
+ mfY *= rTup.mfY;
+ mfZ *= rTup.mfZ;
+ return *this;
+ }
+
+ B3DTuple& operator*=(double t)
+ {
+ mfX *= t;
+ mfY *= t;
+ mfZ *= t;
+ return *this;
+ }
+
+ B3DTuple& operator/=(double t)
+ {
+ const double fVal(1.0 / t);
+ mfX *= fVal;
+ mfY *= fVal;
+ mfZ *= fVal;
+ return *this;
+ }
+
+ B3DTuple operator-(void) const
+ {
+ return B3DTuple(-mfX, -mfY, -mfZ);
+ }
+
+ bool operator==( const B3DTuple& rTup ) const
+ {
+ return equal(rTup);
+ }
+
+ bool operator!=( const B3DTuple& rTup ) const
+ {
+ return !equal(rTup);
+ }
+
+ B3DTuple& operator=( const B3DTuple& rTup )
+ {
+ mfX = rTup.mfX;
+ mfY = rTup.mfY;
+ mfZ = rTup.mfZ;
+ return *this;
+ }
+
+ void correctValues(const double fCompareValue = 0.0)
+ {
+ if(0.0 == fCompareValue)
+ {
+ if(::basegfx::fTools::equalZero(mfX))
+ {
+ mfX = 0.0;
+ }
+
+ if(::basegfx::fTools::equalZero(mfY))
+ {
+ mfY = 0.0;
+ }
+
+ if(::basegfx::fTools::equalZero(mfZ))
+ {
+ mfZ = 0.0;
+ }
+ }
+ else
+ {
+ if(::basegfx::fTools::equal(mfX, fCompareValue))
+ {
+ mfX = fCompareValue;
+ }
+
+ if(::basegfx::fTools::equal(mfY, fCompareValue))
+ {
+ mfY = fCompareValue;
+ }
+
+ if(::basegfx::fTools::equal(mfZ, fCompareValue))
+ {
+ mfZ = fCompareValue;
+ }
+ }
+ }
+
+ static const B3DTuple& getEmptyTuple();
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ inline B3DTuple minimum(const B3DTuple& rTupA, const B3DTuple& rTupB)
+ {
+ B3DTuple aMin(
+ (rTupB.getX() < rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() < rTupA.getY()) ? rTupB.getY() : rTupA.getY(),
+ (rTupB.getZ() < rTupA.getZ()) ? rTupB.getZ() : rTupA.getZ());
+ return aMin;
+ }
+
+ inline B3DTuple maximum(const B3DTuple& rTupA, const B3DTuple& rTupB)
+ {
+ B3DTuple aMax(
+ (rTupB.getX() > rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() > rTupA.getY()) ? rTupB.getY() : rTupA.getY(),
+ (rTupB.getZ() > rTupA.getZ()) ? rTupB.getZ() : rTupA.getZ());
+ return aMax;
+ }
+
+ inline B3DTuple absolute(const B3DTuple& rTup)
+ {
+ B3DTuple aAbs(
+ (0.0 > rTup.getX()) ? -rTup.getX() : rTup.getX(),
+ (0.0 > rTup.getY()) ? -rTup.getY() : rTup.getY(),
+ (0.0 > rTup.getZ()) ? -rTup.getZ() : rTup.getZ());
+ return aAbs;
+ }
+
+ inline B3DTuple interpolate(const B3DTuple& rOld1, const B3DTuple& rOld2, double t)
+ {
+ B3DTuple aInt(
+ ((rOld2.getX() - rOld1.getX()) * t) + rOld1.getX(),
+ ((rOld2.getY() - rOld1.getY()) * t) + rOld1.getY(),
+ ((rOld2.getZ() - rOld1.getZ()) * t) + rOld1.getZ());
+ return aInt;
+ }
+
+ inline B3DTuple average(const B3DTuple& rOld1, const B3DTuple& rOld2)
+ {
+ B3DTuple aAvg(
+ (rOld1.getX() + rOld2.getX()) * 0.5,
+ (rOld1.getY() + rOld2.getY()) * 0.5,
+ (rOld1.getZ() + rOld2.getZ()) * 0.5);
+ return aAvg;
+ }
+
+ inline B3DTuple average(const B3DTuple& rOld1, const B3DTuple& rOld2, const B3DTuple& rOld3)
+ {
+ B3DTuple aAvg(
+ (rOld1.getX() + rOld2.getX() + rOld3.getX()) * (1.0 / 3.0),
+ (rOld1.getY() + rOld2.getY() + rOld3.getY()) * (1.0 / 3.0),
+ (rOld1.getZ() + rOld2.getZ() + rOld3.getZ()) * (1.0 / 3.0));
+ return aAvg;
+ }
+
+ inline B3DTuple operator+(const B3DTuple& rTupA, const B3DTuple& rTupB)
+ {
+ B3DTuple aSum(rTupA);
+ aSum += rTupB;
+ return aSum;
+ }
+
+ inline B3DTuple operator-(const B3DTuple& rTupA, const B3DTuple& rTupB)
+ {
+ B3DTuple aSub(rTupA);
+ aSub -= rTupB;
+ return aSub;
+ }
+
+ inline B3DTuple operator/(const B3DTuple& rTupA, const B3DTuple& rTupB)
+ {
+ B3DTuple aDiv(rTupA);
+ aDiv /= rTupB;
+ return aDiv;
+ }
+
+ inline B3DTuple operator*(const B3DTuple& rTupA, const B3DTuple& rTupB)
+ {
+ B3DTuple aMul(rTupA);
+ aMul *= rTupB;
+ return aMul;
+ }
+
+ inline B3DTuple operator*(const B3DTuple& rTup, double t)
+ {
+ B3DTuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B3DTuple operator*(double t, const B3DTuple& rTup)
+ {
+ B3DTuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B3DTuple operator/(const B3DTuple& rTup, double t)
+ {
+ B3DTuple aNew(rTup);
+ aNew /= t;
+ return aNew;
+ }
+
+ inline B3DTuple operator/(double t, const B3DTuple& rTup)
+ {
+ B3DTuple aNew(rTup);
+ aNew /= t;
+ return aNew;
+ }
+
+ /** Round double to nearest integer for 3D tuple
+
+ @return the nearest integer for this tuple
+ */
+ B3ITuple fround(const B3DTuple& rTup);
+} // end of namespace basegfx
+
+#endif /* _BGFX_TUPLE_B3DTUPLE_HXX */
diff --git a/basegfx/inc/basegfx/tuple/b3i64tuple.hxx b/basegfx/inc/basegfx/tuple/b3i64tuple.hxx
new file mode 100644
index 000000000000..3791cbd63480
--- /dev/null
+++ b/basegfx/inc/basegfx/tuple/b3i64tuple.hxx
@@ -0,0 +1,352 @@
+/*************************************************************************
+ *
+ * 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: b3i64tuple.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TUPLE_B3I64TUPLE_HXX
+#define _BGFX_TUPLE_B3I64TUPLE_HXX
+
+#include <sal/types.h>
+#include <basegfx/tuple/b3dtuple.hxx>
+
+
+namespace basegfx
+{
+ /** Base class for all Points/Vectors with three sal_Int64 values
+
+ This class provides all methods common to Point
+ avd Vector classes which are derived from here.
+
+ @derive Use this class to implement Points or Vectors
+ which are based on three sal_Int64 values
+ */
+ class B3I64Tuple
+ {
+ protected:
+ sal_Int64 mnX;
+ sal_Int64 mnY;
+ sal_Int64 mnZ;
+
+ public:
+ /** Create a 3D Tuple
+
+ The tuple is initialized to (0, 0, 0)
+ */
+ B3I64Tuple()
+ : mnX(0),
+ mnY(0),
+ mnZ(0)
+ {}
+
+ /** Create a 3D Tuple
+
+ @param nX
+ This parameter is used to initialize the X-coordinate
+ of the 3D Tuple.
+
+ @param nY
+ This parameter is used to initialize the Y-coordinate
+ of the 3D Tuple.
+
+ @param nZ
+ This parameter is used to initialize the Z-coordinate
+ of the 3D Tuple.
+ */
+ B3I64Tuple(sal_Int64 nX, sal_Int64 nY, sal_Int64 nZ)
+ : mnX(nX),
+ mnY(nY),
+ mnZ(nZ)
+ {}
+
+ /** Create a copy of a 3D Tuple
+
+ @param rTup
+ The 3D Tuple which will be copied.
+ */
+ B3I64Tuple(const B3I64Tuple& rTup)
+ : mnX( rTup.mnX ),
+ mnY( rTup.mnY ),
+ mnZ( rTup.mnZ )
+ {}
+
+ ~B3I64Tuple()
+ {}
+
+ /// get X-Coordinate of 3D Tuple
+ sal_Int64 getX() const
+ {
+ return mnX;
+ }
+
+ /// get Y-Coordinate of 3D Tuple
+ sal_Int64 getY() const
+ {
+ return mnY;
+ }
+
+ /// get Z-Coordinate of 3D Tuple
+ sal_Int64 getZ() const
+ {
+ return mnZ;
+ }
+
+ /// set X-Coordinate of 3D Tuple
+ void setX(sal_Int64 nX)
+ {
+ mnX = nX;
+ }
+
+ /// set Y-Coordinate of 3D Tuple
+ void setY(sal_Int64 nY)
+ {
+ mnY = nY;
+ }
+
+ /// set Z-Coordinate of 3D Tuple
+ void setZ(sal_Int64 nZ)
+ {
+ mnZ = nZ;
+ }
+
+ /// Array-access to 3D Tuple
+ const sal_Int64& operator[] (int nPos) const
+ {
+ // Here, normally two if(...)'s should be used. In the assumption that
+ // both sal_Int64 members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mnX; if(1 == nPos) return mnY; return mnZ;
+ return *((&mnX) + nPos);
+ }
+
+ /// Array-access to 3D Tuple
+ sal_Int64& operator[] (int nPos)
+ {
+ // Here, normally two if(...)'s should be used. In the assumption that
+ // both sal_Int64 members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mnX; if(1 == nPos) return mnY; return mnZ;
+ return *((&mnX) + nPos);
+ }
+
+ // operators
+ //////////////////////////////////////////////////////////////////////
+
+ B3I64Tuple& operator+=( const B3I64Tuple& rTup )
+ {
+ mnX += rTup.mnX;
+ mnY += rTup.mnY;
+ mnZ += rTup.mnZ;
+ return *this;
+ }
+
+ B3I64Tuple& operator-=( const B3I64Tuple& rTup )
+ {
+ mnX -= rTup.mnX;
+ mnY -= rTup.mnY;
+ mnZ -= rTup.mnZ;
+ return *this;
+ }
+
+ B3I64Tuple& operator/=( const B3I64Tuple& rTup )
+ {
+ mnX /= rTup.mnX;
+ mnY /= rTup.mnY;
+ mnZ /= rTup.mnZ;
+ return *this;
+ }
+
+ B3I64Tuple& operator*=( const B3I64Tuple& rTup )
+ {
+ mnX *= rTup.mnX;
+ mnY *= rTup.mnY;
+ mnZ *= rTup.mnZ;
+ return *this;
+ }
+
+ B3I64Tuple& operator*=(sal_Int64 t)
+ {
+ mnX *= t;
+ mnY *= t;
+ mnZ *= t;
+ return *this;
+ }
+
+ B3I64Tuple& operator/=(sal_Int64 t)
+ {
+ mnX /= t;
+ mnY /= t;
+ mnZ /= t;
+ return *this;
+ }
+
+ B3I64Tuple operator-(void) const
+ {
+ return B3I64Tuple(-mnX, -mnY, -mnZ);
+ }
+
+ bool equalZero() const
+ {
+ return (this == &getEmptyTuple() ||
+ (mnX == 0 && mnY == 0 && mnZ == 0));
+ }
+
+ bool operator==( const B3I64Tuple& rTup ) const
+ {
+ return rTup.mnX == mnX && rTup.mnY == mnY && rTup.mnZ == mnZ;
+ }
+
+ bool operator!=( const B3I64Tuple& rTup ) const
+ {
+ return !(*this == rTup);
+ }
+
+ B3I64Tuple& operator=( const B3I64Tuple& rTup )
+ {
+ mnX = rTup.mnX;
+ mnY = rTup.mnY;
+ mnZ = rTup.mnZ;
+ return *this;
+ }
+
+ static const B3I64Tuple& getEmptyTuple();
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ inline B3I64Tuple minimum(const B3I64Tuple& rTupA, const B3I64Tuple& rTupB)
+ {
+ B3I64Tuple aMin(
+ (rTupB.getX() < rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() < rTupA.getY()) ? rTupB.getY() : rTupA.getY(),
+ (rTupB.getZ() < rTupA.getZ()) ? rTupB.getZ() : rTupA.getZ());
+ return aMin;
+ }
+
+ inline B3I64Tuple maximum(const B3I64Tuple& rTupA, const B3I64Tuple& rTupB)
+ {
+ B3I64Tuple aMax(
+ (rTupB.getX() > rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() > rTupA.getY()) ? rTupB.getY() : rTupA.getY(),
+ (rTupB.getZ() > rTupA.getZ()) ? rTupB.getZ() : rTupA.getZ());
+ return aMax;
+ }
+
+ inline B3I64Tuple absolute(const B3I64Tuple& rTup)
+ {
+ B3I64Tuple aAbs(
+ (0 > rTup.getX()) ? -rTup.getX() : rTup.getX(),
+ (0 > rTup.getY()) ? -rTup.getY() : rTup.getY(),
+ (0 > rTup.getZ()) ? -rTup.getZ() : rTup.getZ());
+ return aAbs;
+ }
+
+ inline B3DTuple interpolate(const B3I64Tuple& rOld1, const B3I64Tuple& rOld2, double t)
+ {
+ B3DTuple aInt(
+ ((rOld2.getX() - rOld1.getX()) * t) + rOld1.getX(),
+ ((rOld2.getY() - rOld1.getY()) * t) + rOld1.getY(),
+ ((rOld2.getZ() - rOld1.getZ()) * t) + rOld1.getZ());
+ return aInt;
+ }
+
+ inline B3DTuple average(const B3I64Tuple& rOld1, const B3I64Tuple& rOld2)
+ {
+ B3DTuple aAvg(
+ (rOld1.getX() + rOld2.getX()) * 0.5,
+ (rOld1.getY() + rOld2.getY()) * 0.5,
+ (rOld1.getZ() + rOld2.getZ()) * 0.5);
+ return aAvg;
+ }
+
+ inline B3DTuple average(const B3I64Tuple& rOld1, const B3I64Tuple& rOld2, const B3I64Tuple& rOld3)
+ {
+ B3DTuple aAvg(
+ (rOld1.getX() + rOld2.getX() + rOld3.getX()) * (1.0 / 3.0),
+ (rOld1.getY() + rOld2.getY() + rOld3.getY()) * (1.0 / 3.0),
+ (rOld1.getZ() + rOld2.getZ() + rOld3.getZ()) * (1.0 / 3.0));
+ return aAvg;
+ }
+
+ inline B3I64Tuple operator+(const B3I64Tuple& rTupA, const B3I64Tuple& rTupB)
+ {
+ B3I64Tuple aSum(rTupA);
+ aSum += rTupB;
+ return aSum;
+ }
+
+ inline B3I64Tuple operator-(const B3I64Tuple& rTupA, const B3I64Tuple& rTupB)
+ {
+ B3I64Tuple aSub(rTupA);
+ aSub -= rTupB;
+ return aSub;
+ }
+
+ inline B3I64Tuple operator/(const B3I64Tuple& rTupA, const B3I64Tuple& rTupB)
+ {
+ B3I64Tuple aDiv(rTupA);
+ aDiv /= rTupB;
+ return aDiv;
+ }
+
+ inline B3I64Tuple operator*(const B3I64Tuple& rTupA, const B3I64Tuple& rTupB)
+ {
+ B3I64Tuple aMul(rTupA);
+ aMul *= rTupB;
+ return aMul;
+ }
+
+ inline B3I64Tuple operator*(const B3I64Tuple& rTup, sal_Int64 t)
+ {
+ B3I64Tuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B3I64Tuple operator*(sal_Int64 t, const B3I64Tuple& rTup)
+ {
+ B3I64Tuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B3I64Tuple operator/(const B3I64Tuple& rTup, sal_Int64 t)
+ {
+ B3I64Tuple aNew(rTup);
+ aNew /= t;
+ return aNew;
+ }
+
+ inline B3I64Tuple operator/(sal_Int64 t, const B3I64Tuple& rTup)
+ {
+ B3I64Tuple aNew(t, t, t);
+ B3I64Tuple aTmp(rTup);
+ aNew /= aTmp;
+ return aNew;
+ }
+} // end of namespace basegfx
+
+#endif /* _BGFX_TUPLE_B3I64TUPLE_HXX */
diff --git a/basegfx/inc/basegfx/tuple/b3ituple.hxx b/basegfx/inc/basegfx/tuple/b3ituple.hxx
new file mode 100644
index 000000000000..33432122913b
--- /dev/null
+++ b/basegfx/inc/basegfx/tuple/b3ituple.hxx
@@ -0,0 +1,352 @@
+/*************************************************************************
+ *
+ * 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: b3ituple.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_TUPLE_B3ITUPLE_HXX
+#define _BGFX_TUPLE_B3ITUPLE_HXX
+
+#include <sal/types.h>
+#include <basegfx/tuple/b3dtuple.hxx>
+
+
+namespace basegfx
+{
+ /** Base class for all Points/Vectors with three sal_Int32 values
+
+ This class provides all methods common to Point
+ avd Vector classes which are derived from here.
+
+ @derive Use this class to implement Points or Vectors
+ which are based on three sal_Int32 values
+ */
+ class B3ITuple
+ {
+ protected:
+ sal_Int32 mnX;
+ sal_Int32 mnY;
+ sal_Int32 mnZ;
+
+ public:
+ /** Create a 3D Tuple
+
+ The tuple is initialized to (0, 0, 0)
+ */
+ B3ITuple()
+ : mnX(0),
+ mnY(0),
+ mnZ(0)
+ {}
+
+ /** Create a 3D Tuple
+
+ @param nX
+ This parameter is used to initialize the X-coordinate
+ of the 3D Tuple.
+
+ @param nY
+ This parameter is used to initialize the Y-coordinate
+ of the 3D Tuple.
+
+ @param nZ
+ This parameter is used to initialize the Z-coordinate
+ of the 3D Tuple.
+ */
+ B3ITuple(sal_Int32 nX, sal_Int32 nY, sal_Int32 nZ)
+ : mnX(nX),
+ mnY(nY),
+ mnZ(nZ)
+ {}
+
+ /** Create a copy of a 3D Tuple
+
+ @param rTup
+ The 3D Tuple which will be copied.
+ */
+ B3ITuple(const B3ITuple& rTup)
+ : mnX( rTup.mnX ),
+ mnY( rTup.mnY ),
+ mnZ( rTup.mnZ )
+ {}
+
+ ~B3ITuple()
+ {}
+
+ /// get X-Coordinate of 3D Tuple
+ sal_Int32 getX() const
+ {
+ return mnX;
+ }
+
+ /// get Y-Coordinate of 3D Tuple
+ sal_Int32 getY() const
+ {
+ return mnY;
+ }
+
+ /// get Z-Coordinate of 3D Tuple
+ sal_Int32 getZ() const
+ {
+ return mnZ;
+ }
+
+ /// set X-Coordinate of 3D Tuple
+ void setX(sal_Int32 nX)
+ {
+ mnX = nX;
+ }
+
+ /// set Y-Coordinate of 3D Tuple
+ void setY(sal_Int32 nY)
+ {
+ mnY = nY;
+ }
+
+ /// set Z-Coordinate of 3D Tuple
+ void setZ(sal_Int32 nZ)
+ {
+ mnZ = nZ;
+ }
+
+ /// Array-access to 3D Tuple
+ const sal_Int32& operator[] (int nPos) const
+ {
+ // Here, normally two if(...)'s should be used. In the assumption that
+ // both sal_Int32 members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mnX; if(1 == nPos) return mnY; return mnZ;
+ return *((&mnX) + nPos);
+ }
+
+ /// Array-access to 3D Tuple
+ sal_Int32& operator[] (int nPos)
+ {
+ // Here, normally two if(...)'s should be used. In the assumption that
+ // both sal_Int32 members can be accessed as an array a shortcut is used here.
+ // if(0 == nPos) return mnX; if(1 == nPos) return mnY; return mnZ;
+ return *((&mnX) + nPos);
+ }
+
+ // operators
+ //////////////////////////////////////////////////////////////////////
+
+ B3ITuple& operator+=( const B3ITuple& rTup )
+ {
+ mnX += rTup.mnX;
+ mnY += rTup.mnY;
+ mnZ += rTup.mnZ;
+ return *this;
+ }
+
+ B3ITuple& operator-=( const B3ITuple& rTup )
+ {
+ mnX -= rTup.mnX;
+ mnY -= rTup.mnY;
+ mnZ -= rTup.mnZ;
+ return *this;
+ }
+
+ B3ITuple& operator/=( const B3ITuple& rTup )
+ {
+ mnX /= rTup.mnX;
+ mnY /= rTup.mnY;
+ mnZ /= rTup.mnZ;
+ return *this;
+ }
+
+ B3ITuple& operator*=( const B3ITuple& rTup )
+ {
+ mnX *= rTup.mnX;
+ mnY *= rTup.mnY;
+ mnZ *= rTup.mnZ;
+ return *this;
+ }
+
+ B3ITuple& operator*=(sal_Int32 t)
+ {
+ mnX *= t;
+ mnY *= t;
+ mnZ *= t;
+ return *this;
+ }
+
+ B3ITuple& operator/=(sal_Int32 t)
+ {
+ mnX /= t;
+ mnY /= t;
+ mnZ /= t;
+ return *this;
+ }
+
+ B3ITuple operator-(void) const
+ {
+ return B3ITuple(-mnX, -mnY, -mnZ);
+ }
+
+ bool equalZero() const
+ {
+ return (this == &getEmptyTuple() ||
+ (mnX == 0 && mnY == 0 && mnZ == 0));
+ }
+
+ bool operator==( const B3ITuple& rTup ) const
+ {
+ return rTup.mnX == mnX && rTup.mnY == mnY && rTup.mnZ == mnZ;
+ }
+
+ bool operator!=( const B3ITuple& rTup ) const
+ {
+ return !(*this == rTup);
+ }
+
+ B3ITuple& operator=( const B3ITuple& rTup )
+ {
+ mnX = rTup.mnX;
+ mnY = rTup.mnY;
+ mnZ = rTup.mnZ;
+ return *this;
+ }
+
+ static const B3ITuple& getEmptyTuple();
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ inline B3ITuple minimum(const B3ITuple& rTupA, const B3ITuple& rTupB)
+ {
+ B3ITuple aMin(
+ (rTupB.getX() < rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() < rTupA.getY()) ? rTupB.getY() : rTupA.getY(),
+ (rTupB.getZ() < rTupA.getZ()) ? rTupB.getZ() : rTupA.getZ());
+ return aMin;
+ }
+
+ inline B3ITuple maximum(const B3ITuple& rTupA, const B3ITuple& rTupB)
+ {
+ B3ITuple aMax(
+ (rTupB.getX() > rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() > rTupA.getY()) ? rTupB.getY() : rTupA.getY(),
+ (rTupB.getZ() > rTupA.getZ()) ? rTupB.getZ() : rTupA.getZ());
+ return aMax;
+ }
+
+ inline B3ITuple absolute(const B3ITuple& rTup)
+ {
+ B3ITuple aAbs(
+ (0 > rTup.getX()) ? -rTup.getX() : rTup.getX(),
+ (0 > rTup.getY()) ? -rTup.getY() : rTup.getY(),
+ (0 > rTup.getZ()) ? -rTup.getZ() : rTup.getZ());
+ return aAbs;
+ }
+
+ inline B3DTuple interpolate(const B3ITuple& rOld1, const B3ITuple& rOld2, double t)
+ {
+ B3DTuple aInt(
+ ((rOld2.getX() - rOld1.getX()) * t) + rOld1.getX(),
+ ((rOld2.getY() - rOld1.getY()) * t) + rOld1.getY(),
+ ((rOld2.getZ() - rOld1.getZ()) * t) + rOld1.getZ());
+ return aInt;
+ }
+
+ inline B3DTuple average(const B3ITuple& rOld1, const B3ITuple& rOld2)
+ {
+ B3DTuple aAvg(
+ (rOld1.getX() + rOld2.getX()) * 0.5,
+ (rOld1.getY() + rOld2.getY()) * 0.5,
+ (rOld1.getZ() + rOld2.getZ()) * 0.5);
+ return aAvg;
+ }
+
+ inline B3DTuple average(const B3ITuple& rOld1, const B3ITuple& rOld2, const B3ITuple& rOld3)
+ {
+ B3DTuple aAvg(
+ (rOld1.getX() + rOld2.getX() + rOld3.getX()) * (1.0 / 3.0),
+ (rOld1.getY() + rOld2.getY() + rOld3.getY()) * (1.0 / 3.0),
+ (rOld1.getZ() + rOld2.getZ() + rOld3.getZ()) * (1.0 / 3.0));
+ return aAvg;
+ }
+
+ inline B3ITuple operator+(const B3ITuple& rTupA, const B3ITuple& rTupB)
+ {
+ B3ITuple aSum(rTupA);
+ aSum += rTupB;
+ return aSum;
+ }
+
+ inline B3ITuple operator-(const B3ITuple& rTupA, const B3ITuple& rTupB)
+ {
+ B3ITuple aSub(rTupA);
+ aSub -= rTupB;
+ return aSub;
+ }
+
+ inline B3ITuple operator/(const B3ITuple& rTupA, const B3ITuple& rTupB)
+ {
+ B3ITuple aDiv(rTupA);
+ aDiv /= rTupB;
+ return aDiv;
+ }
+
+ inline B3ITuple operator*(const B3ITuple& rTupA, const B3ITuple& rTupB)
+ {
+ B3ITuple aMul(rTupA);
+ aMul *= rTupB;
+ return aMul;
+ }
+
+ inline B3ITuple operator*(const B3ITuple& rTup, sal_Int32 t)
+ {
+ B3ITuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B3ITuple operator*(sal_Int32 t, const B3ITuple& rTup)
+ {
+ B3ITuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ inline B3ITuple operator/(const B3ITuple& rTup, sal_Int32 t)
+ {
+ B3ITuple aNew(rTup);
+ aNew /= t;
+ return aNew;
+ }
+
+ inline B3ITuple operator/(sal_Int32 t, const B3ITuple& rTup)
+ {
+ B3ITuple aNew(t, t, t);
+ B3ITuple aTmp(rTup);
+ aNew /= aTmp;
+ return aNew;
+ }
+} // end of namespace basegfx
+
+#endif /* _BGFX_TUPLE_B3ITUPLE_HXX */
diff --git a/basegfx/inc/basegfx/vector/b2dsize.hxx b/basegfx/inc/basegfx/vector/b2dsize.hxx
new file mode 100644
index 000000000000..eda0ca30cf32
--- /dev/null
+++ b/basegfx/inc/basegfx/vector/b2dsize.hxx
@@ -0,0 +1,45 @@
+/*************************************************************************
+ *
+ * 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: b2dsize.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_VECTOR_B2DSIZE_HXX
+#define _BGFX_VECTOR_B2DSIZE_HXX
+
+#include <basegfx/vector/b2dvector.hxx>
+
+namespace basegfx
+{
+ // syntactic sugar: a B2DVector exactly models a Size object,
+ // thus, for interface clarity, we provide an alias name
+
+ /// Alias name for interface clarity (not everybody is aware of the identity)
+ typedef B2DVector B2DSize;
+}
+
+#endif /* _BGFX_VECTOR_B2DSIZE_HXX */
diff --git a/basegfx/inc/basegfx/vector/b2dvector.hxx b/basegfx/inc/basegfx/vector/b2dvector.hxx
new file mode 100644
index 000000000000..299abee2baa2
--- /dev/null
+++ b/basegfx/inc/basegfx/vector/b2dvector.hxx
@@ -0,0 +1,270 @@
+/*************************************************************************
+ *
+ * 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: b2dvector.hxx,v $
+ * $Revision: 1.16 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_VECTOR_B2DVECTOR_HXX
+#define _BGFX_VECTOR_B2DVECTOR_HXX
+
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <basegfx/vector/b2ivector.hxx>
+#include <basegfx/vector/b2enums.hxx>
+
+namespace basegfx
+{
+ // predeclaration
+ class B2DHomMatrix;
+
+ /** Base Point class with two double values
+
+ This class derives all operators and common handling for
+ a 2D data class from B2DTuple. All necessary extensions
+ which are special for 2D Vectors are added here.
+
+ @see B2DTuple
+ */
+ class B2DVector : public ::basegfx::B2DTuple
+ {
+ public:
+ /** Create a 2D Vector
+
+ The vector is initialized to (0.0, 0.0)
+ */
+ B2DVector()
+ : B2DTuple()
+ {}
+
+ /** Create a 2D Vector
+
+ @param fX
+ This parameter is used to initialize the X-coordinate
+ of the 2D Vector.
+
+ @param fY
+ This parameter is used to initialize the Y-coordinate
+ of the 2D Vector.
+ */
+ B2DVector(double fX, double fY)
+ : B2DTuple(fX, fY)
+ {}
+
+ /** Create a copy of a 2D Vector
+
+ @param rVec
+ The 2D Vector which will be copied.
+ */
+ B2DVector(const B2DVector& rVec)
+ : B2DTuple(rVec)
+ {}
+
+ /** Create a copy of a 2D Vector
+
+ @param rVec
+ The 2D Vector which will be copied.
+ */
+ B2DVector(const ::basegfx::B2IVector& rVec)
+ : B2DTuple(rVec)
+ {}
+
+ /** constructor with tuple to allow copy-constructing
+ from B2DTuple-based classes
+ */
+ B2DVector(const ::basegfx::B2DTuple& rTuple)
+ : B2DTuple(rTuple)
+ {}
+
+ ~B2DVector()
+ {}
+
+ /** *=operator to allow usage from B2DVector, too
+ */
+ B2DVector& operator*=( const B2DVector& rPnt )
+ {
+ mfX *= rPnt.mfX;
+ mfY *= rPnt.mfY;
+ return *this;
+ }
+
+ /** *=operator to allow usage from B2DVector, too
+ */
+ B2DVector& operator*=(double t)
+ {
+ mfX *= t;
+ mfY *= t;
+ return *this;
+ }
+
+ /** assignment operator to allow assigning the results
+ of B2DTuple calculations
+ */
+ B2DVector& operator=( const ::basegfx::B2DTuple& rVec );
+
+ /** Calculate the length of this 2D Vector
+
+ @return The Length of the 2D Vector
+ */
+ double getLength() const;
+
+ /** Set the length of this 2D Vector
+
+ @param fLen
+ The to be achieved length of the 2D Vector
+ */
+ B2DVector& setLength(double fLen);
+
+ /** Normalize this 2D Vector
+
+ The length of the 2D Vector is set to 1.0
+ */
+ B2DVector& normalize();
+
+ /** Test if this 2D Vector is normalized
+
+ @return
+ true if lenth of vector is equal to 1.0
+ false else
+ */
+ bool isNormalized() const;
+
+ /** Calculate the Scalar with another 2D Vector
+
+ @param rVec
+ The second 2D Vector
+
+ @return
+ The Scalar value of the two involved 2D Vectors
+ */
+ double scalar( const B2DVector& rVec ) const;
+
+ /** Calculate the length of the cross product with another 2D Vector
+
+ In 2D, returning an actual vector does not make much
+ sense here. The magnitude, although, can be readily
+ used for tasks such as angle calculations, since for
+ the returned value, the following equation holds:
+ retVal = getLength(this)*getLength(rVec)*sin(theta),
+ with theta being the angle between the two vectors.
+
+ @param rVec
+ The second 2D Vector
+
+ @return
+ The length of the cross product of the two involved 2D Vectors
+ */
+ double cross( const B2DVector& rVec ) const;
+
+ /** Calculate the Angle with another 2D Vector
+
+ @param rVec
+ The second 2D Vector
+
+ @return
+ The Angle value of the two involved 2D Vectors in -pi/2 < return < pi/2
+ */
+ double angle( const B2DVector& rVec ) const;
+
+ /** Transform vector by given transformation matrix.
+
+ Since this is a vector, translational components of the
+ matrix are disregarded.
+ */
+ B2DVector& operator*=( const B2DHomMatrix& rMat );
+
+ static const B2DVector& getEmptyVector();
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ /** Calculate the orientation to another 2D Vector
+
+ @param rVecA
+ The first 2D Vector
+
+ @param rVecB
+ The second 2D Vector
+
+ @return
+ The mathematical Orientation of the two involved 2D Vectors
+ */
+ B2VectorOrientation getOrientation( const B2DVector& rVecA, const B2DVector& rVecB );
+
+ /** Calculate a perpendicular 2D Vector to the given one
+
+ @param rVec
+ The source 2D Vector
+
+ @attention This only works if the given 2D Vector is normalized.
+
+ @return
+ A 2D Vector perpendicular to the one given in parameter rVec
+ */
+ B2DVector getPerpendicular( const B2DVector& rNormalizedVec );
+
+ /** Calculate a perpendicular 2D Vector to the given one,
+ normalize the given one as preparation
+
+ @param rVec
+ The source 2D Vector
+
+ @return
+ A normalized 2D Vector perpendicular to the one given in parameter rVec
+ */
+ B2DVector getNormalizedPerpendicular( const B2DVector& rVec );
+
+ /** Test two vectors which need not to be normalized for parallelism
+
+ @param rVecA
+ The first 2D Vector
+
+ @param rVecB
+ The second 2D Vector
+
+ @return
+ bool if the two values are parallel. Also true if
+ one of the vectors is empty.
+ */
+ bool areParallel( const B2DVector& rVecA, const B2DVector& rVecB );
+
+ /** Transform vector by given transformation matrix.
+
+ Since this is a vector, translational components of the
+ matrix are disregarded.
+ */
+ B2DVector operator*( const B2DHomMatrix& rMat, const B2DVector& rVec );
+
+ /** Test continuity between given vectors.
+
+ The two given vectors are assumed to describe control points on a
+ common point. Calculate if there is a continuity between them.
+ */
+ B2VectorContinuity getContinuity( const B2DVector& rBackVector, const B2DVector& rForwardVector );
+
+} // end of namespace basegfx
+
+#endif /* _BGFX_VECTOR_B2DVECTOR_HXX */
diff --git a/basegfx/inc/basegfx/vector/b2enums.hxx b/basegfx/inc/basegfx/vector/b2enums.hxx
new file mode 100644
index 000000000000..d2c481294ed2
--- /dev/null
+++ b/basegfx/inc/basegfx/vector/b2enums.hxx
@@ -0,0 +1,79 @@
+/*************************************************************************
+ *
+ * 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: b2enums.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_VECTOR_B2ENUMS_HXX
+#define _BGFX_VECTOR_B2ENUMS_HXX
+
+#include <sal/types.h>
+
+namespace basegfx
+{
+ /** Descriptor for the mathematical orientations of two 2D Vectors
+ */
+ enum B2VectorOrientation
+ {
+ /// mathematically positive oriented
+ ORIENTATION_POSITIVE = 0,
+
+ /// mathematically negative oriented
+ ORIENTATION_NEGATIVE,
+
+ /// mathematically neutral, thus parallel
+ ORIENTATION_NEUTRAL
+ };
+
+ /** Descriptor for the mathematical continuity of two 2D Vectors
+ */
+ enum B2VectorContinuity
+ {
+ /// none
+ CONTINUITY_NONE = 0,
+
+ /// mathematically negative oriented
+ CONTINUITY_C1,
+
+ /// mathematically neutral, thus parallel
+ CONTINUITY_C2
+ };
+
+ /** Descriptor for possible line joins between two line segments
+ */
+ enum B2DLineJoin
+ {
+ B2DLINEJOIN_NONE, // no rounding
+ B2DLINEJOIN_MIDDLE, // calc middle value between joints
+ B2DLINEJOIN_BEVEL, // join edges with line
+ B2DLINEJOIN_MITER, // extend till cut
+ B2DLINEJOIN_ROUND // create arc
+ };
+
+} // end of namespace basegfx
+
+#endif /* _BGFX_VECTOR_B2ENUMS_HXX */
diff --git a/basegfx/inc/basegfx/vector/b2isize.hxx b/basegfx/inc/basegfx/vector/b2isize.hxx
new file mode 100644
index 000000000000..aef03e8a17cb
--- /dev/null
+++ b/basegfx/inc/basegfx/vector/b2isize.hxx
@@ -0,0 +1,45 @@
+/*************************************************************************
+ *
+ * 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: b2isize.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_VECTOR_B2ISIZE_HXX
+#define _BGFX_VECTOR_B2ISIZE_HXX
+
+#include <basegfx/vector/b2ivector.hxx>
+
+namespace basegfx
+{
+ // syntactic sugar: a B2IVector exactly models a Size object,
+ // thus, for interface clarity, we provide an alias name
+
+ /// Alias name for interface clarity (not everybody is aware of the identity)
+ typedef B2IVector B2ISize;
+}
+
+#endif /* _BGFX_VECTOR_B2ISIZE_HXX */
diff --git a/basegfx/inc/basegfx/vector/b2ivector.hxx b/basegfx/inc/basegfx/vector/b2ivector.hxx
new file mode 100644
index 000000000000..fdc5ba6a6cfd
--- /dev/null
+++ b/basegfx/inc/basegfx/vector/b2ivector.hxx
@@ -0,0 +1,233 @@
+/*************************************************************************
+ *
+ * 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: b2ivector.hxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_VECTOR_B2IVECTOR_HXX
+#define _BGFX_VECTOR_B2IVECTOR_HXX
+
+#include <basegfx/tuple/b2ituple.hxx>
+#include <basegfx/vector/b2enums.hxx>
+
+namespace basegfx
+{
+ // predeclaration
+ class B2DHomMatrix;
+
+ /** Base Point class with two sal_Int32 values
+
+ This class derives all operators and common handling for
+ a 2D data class from B2ITuple. All necessary extensions
+ which are special for 2D Vectors are added here.
+
+ @see B2ITuple
+ */
+ class B2IVector : public ::basegfx::B2ITuple
+ {
+ public:
+ /** Create a 2D Vector
+
+ The vector is initialized to (0, 0)
+ */
+ B2IVector()
+ : B2ITuple()
+ {}
+
+ /** Create a 2D Vector
+
+ @param nX
+ This parameter is used to initialize the X-coordinate
+ of the 2D Vector.
+
+ @param nY
+ This parameter is used to initialize the Y-coordinate
+ of the 2D Vector.
+ */
+ B2IVector(sal_Int32 nX, sal_Int32 nY)
+ : B2ITuple(nX, nY)
+ {}
+
+ /** Create a copy of a 2D Vector
+
+ @param rVec
+ The 2D Vector which will be copied.
+ */
+ B2IVector(const B2IVector& rVec)
+ : B2ITuple(rVec)
+ {}
+
+ /** constructor with tuple to allow copy-constructing
+ from B2ITuple-based classes
+ */
+ B2IVector(const ::basegfx::B2ITuple& rTuple)
+ : B2ITuple(rTuple)
+ {}
+
+ ~B2IVector()
+ {}
+
+ /** *=operator to allow usage from B2IVector, too
+ */
+ B2IVector& operator*=( const B2IVector& rPnt )
+ {
+ mnX *= rPnt.mnX;
+ mnY *= rPnt.mnY;
+ return *this;
+ }
+
+ /** *=operator to allow usage from B2IVector, too
+ */
+ B2IVector& operator*=(sal_Int32 t)
+ {
+ mnX *= t;
+ mnY *= t;
+ return *this;
+ }
+
+ /** assignment operator to allow assigning the results
+ of B2ITuple calculations
+ */
+ B2IVector& operator=( const ::basegfx::B2ITuple& rVec );
+
+ /** Calculate the length of this 2D Vector
+
+ @return The Length of the 2D Vector
+ */
+ double getLength() const;
+
+ /** Set the length of this 2D Vector
+
+ @param fLen
+ The to be achieved length of the 2D Vector
+ */
+ B2IVector& setLength(double fLen);
+
+ /** Calculate the Scalar with another 2D Vector
+
+ @param rVec
+ The second 2D Vector
+
+ @return
+ The Scalar value of the two involved 2D Vectors
+ */
+ double scalar( const B2IVector& rVec ) const;
+
+ /** Calculate the length of the cross product with another 2D Vector
+
+ In 2D, returning an actual vector does not make much
+ sense here. The magnitude, although, can be readily
+ used for tasks such as angle calculations, since for
+ the returned value, the following equation holds:
+ retVal = getLength(this)*getLength(rVec)*sin(theta),
+ with theta being the angle between the two vectors.
+
+ @param rVec
+ The second 2D Vector
+
+ @return
+ The length of the cross product of the two involved 2D Vectors
+ */
+ double cross( const B2IVector& rVec ) const;
+
+ /** Calculate the Angle with another 2D Vector
+
+ @param rVec
+ The second 2D Vector
+
+ @return
+ The Angle value of the two involved 2D Vectors in -pi/2 < return < pi/2
+ */
+ double angle( const B2IVector& rVec ) const;
+
+ /** Transform vector by given transformation matrix.
+
+ Since this is a vector, translational components of the
+ matrix are disregarded.
+ */
+ B2IVector& operator*=( const B2DHomMatrix& rMat );
+
+ static const B2IVector& getEmptyVector();
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ /** Calculate the orientation to another 2D Vector
+
+ @param rVecA
+ The first 2D Vector
+
+ @param rVecB
+ The second 2D Vector
+
+ @return
+ The mathematical Orientation of the two involved 2D Vectors
+ */
+ B2VectorOrientation getOrientation( const B2IVector& rVecA, const B2IVector& rVecB );
+
+ /** Calculate a perpendicular 2D Vector to the given one
+
+ @param rVec
+ The source 2D Vector
+
+ @return
+ A 2D Vector perpendicular to the one given in parameter rVec
+ */
+ B2IVector getPerpendicular( const B2IVector& rVec );
+
+ /** Test two vectors which need not to be normalized for parallelism
+
+ @param rVecA
+ The first 2D Vector
+
+ @param rVecB
+ The second 2D Vector
+
+ @return
+ bool if the two values are parallel. Also true if
+ one of the vectors is empty.
+ */
+ bool areParallel( const B2IVector& rVecA, const B2IVector& rVecB );
+
+ /** Transform vector by given transformation matrix.
+
+ Since this is a vector, translational components of the
+ matrix are disregarded.
+ */
+ B2IVector operator*( const B2DHomMatrix& rMat, const B2IVector& rVec );
+
+ /** Test continuity between given vectors.
+
+ The two given vectors are assumed to describe control points on a
+ common point. Calculate if there is a continuity between them.
+ */
+ B2VectorContinuity getContinuity( const B2IVector& rBackVector, const B2IVector& rForwardVector );
+
+} // end of namespace basegfx
+
+#endif /* _BGFX_VECTOR_B2IVECTOR_HXX */
diff --git a/basegfx/inc/basegfx/vector/b3dsize.hxx b/basegfx/inc/basegfx/vector/b3dsize.hxx
new file mode 100644
index 000000000000..4969f8a80655
--- /dev/null
+++ b/basegfx/inc/basegfx/vector/b3dsize.hxx
@@ -0,0 +1,45 @@
+/*************************************************************************
+ *
+ * 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: b3dsize.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_VECTOR_B3DSIZE_HXX
+#define _BGFX_VECTOR_B3DSIZE_HXX
+
+#include <basegfx/vector/b3dvector.hxx>
+
+namespace basegfx
+{
+ // syntactic sugar: a B3DVector exactly models a Size3D object,
+ // thus, for interface clarity, we provide an alias name
+
+ /// Alias name for interface clarity (not everybody is aware of the identity)
+ typedef B3DVector B3DSize;
+}
+
+#endif /* _BGFX_VECTOR_B3DSIZE_HXX */
diff --git a/basegfx/inc/basegfx/vector/b3dvector.hxx b/basegfx/inc/basegfx/vector/b3dvector.hxx
new file mode 100644
index 000000000000..e39500a15cd7
--- /dev/null
+++ b/basegfx/inc/basegfx/vector/b3dvector.hxx
@@ -0,0 +1,343 @@
+/*************************************************************************
+ *
+ * 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: b3dvector.hxx,v $
+ * $Revision: 1.12.4.1 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_VECTOR_B3DVECTOR_HXX
+#define _BGFX_VECTOR_B3DVECTOR_HXX
+
+#include <basegfx/tuple/b3dtuple.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ // predeclaration
+ class B3DHomMatrix;
+
+ /** Base Point class with three double values
+
+ This class derives all operators and common handling for
+ a 3D data class from B3DTuple. All necessary extensions
+ which are special for 3D Vectors are added here.
+
+ @see B3DTuple
+ */
+ class B3DVector : public ::basegfx::B3DTuple
+ {
+ public:
+ /** Create a 3D Vector
+
+ The vector is initialized to (0.0, 0.0, 0.0)
+ */
+ B3DVector()
+ : B3DTuple()
+ {}
+
+ /** Create a 3D Vector
+
+ @param fX
+ This parameter is used to initialize the X-coordinate
+ of the 3D Vector.
+
+ @param fY
+ This parameter is used to initialize the Y-coordinate
+ of the 3D Vector.
+
+ @param fZ
+ This parameter is used to initialize the Z-coordinate
+ of the 3D Vector.
+ */
+ B3DVector(double fX, double fY, double fZ)
+ : B3DTuple(fX, fY, fZ)
+ {}
+
+ /** Create a copy of a 3D Vector
+
+ @param rVec
+ The 3D Vector which will be copied.
+ */
+ B3DVector(const B3DVector& rVec)
+ : B3DTuple(rVec)
+ {}
+
+ /** constructor with tuple to allow copy-constructing
+ from B3DTuple-based classes
+ */
+ B3DVector(const ::basegfx::B3DTuple& rTuple)
+ : B3DTuple(rTuple)
+ {}
+
+ ~B3DVector()
+ {}
+
+ /** *=operator to allow usage from B3DVector, too
+ */
+ B3DVector& operator*=( const B3DVector& rPnt )
+ {
+ mfX *= rPnt.mfX;
+ mfY *= rPnt.mfY;
+ mfZ *= rPnt.mfZ;
+ return *this;
+ }
+
+ /** *=operator to allow usage from B3DVector, too
+ */
+ B3DVector& operator*=(double t)
+ {
+ mfX *= t;
+ mfY *= t;
+ mfZ *= t;
+ return *this;
+ }
+
+ /** assignment operator to allow assigning the results
+ of B3DTuple calculations
+ */
+ B3DVector& operator=( const ::basegfx::B3DTuple& rVec )
+ {
+ mfX = rVec.getX();
+ mfY = rVec.getY();
+ mfZ = rVec.getZ();
+ return *this;
+ }
+
+ /** Calculate the length of this 3D Vector
+
+ @return The Length of the 3D Vector
+ */
+ double getLength(void) const
+ {
+ double fLen(scalar(*this));
+ if((0.0 == fLen) || (1.0 == fLen))
+ return fLen;
+ return sqrt(fLen);
+ }
+
+ /** Calculate the length in the XY-Plane for this 3D Vector
+
+ @return The XY-Plane Length of the 3D Vector
+ */
+ double getXYLength(void) const
+ {
+ double fLen((mfX * mfX) + (mfY * mfY));
+ if((0.0 == fLen) || (1.0 == fLen))
+ return fLen;
+ return sqrt(fLen);
+ }
+
+ /** Calculate the length in the XZ-Plane for this 3D Vector
+
+ @return The XZ-Plane Length of the 3D Vector
+ */
+ double getXZLength(void) const
+ {
+ double fLen((mfX * mfX) + (mfZ * mfZ)); // #i73040#
+ if((0.0 == fLen) || (1.0 == fLen))
+ return fLen;
+ return sqrt(fLen);
+ }
+
+ /** Calculate the length in the YZ-Plane for this 3D Vector
+
+ @return The YZ-Plane Length of the 3D Vector
+ */
+ double getYZLength(void) const
+ {
+ double fLen((mfY * mfY) + (mfZ * mfZ));
+ if((0.0 == fLen) || (1.0 == fLen))
+ return fLen;
+ return sqrt(fLen);
+ }
+
+ /** Set the length of this 3D Vector
+
+ @param fLen
+ The to be achieved length of the 3D Vector
+ */
+ B3DVector& setLength(double fLen)
+ {
+ double fLenNow(scalar(*this));
+
+ if(!::basegfx::fTools::equalZero(fLenNow))
+ {
+ const double fOne(1.0);
+
+ if(!::basegfx::fTools::equal(fOne, fLenNow))
+ {
+ fLen /= sqrt(fLenNow);
+ }
+
+ mfX *= fLen;
+ mfY *= fLen;
+ mfZ *= fLen;
+ }
+
+ return *this;
+ }
+
+ /** Normalize this 3D Vector
+
+ The length of the 3D Vector is set to 1.0
+ */
+ B3DVector& normalize();
+
+ /** Test if this 3D Vector is normalized
+
+ @return
+ true if lenth of vector is equal to 1.0
+ false else
+ */
+ bool isNormalized() const
+ {
+ const double fOne(1.0);
+ const double fScalar(scalar(*this));
+
+ return (::basegfx::fTools::equal(fOne, fScalar));
+ }
+
+ /** get a 3D Vector which is perpendicular to this and a given 3D Vector
+
+ @attention This only works if this and the given 3D Vector are
+ both normalized.
+
+ @param rNormalizedVec
+ A normalized 3D Vector.
+
+ @return
+ A 3D Vector perpendicular to this and the given one
+ */
+ B3DVector getPerpendicular(const B3DVector& rNormalizedVec) const;
+
+ /** get the projection of this Vector on the given Plane
+
+ @attention This only works if the given 3D Vector defining
+ the Plane is normalized.
+
+ @param rNormalizedPlane
+ A normalized 3D Vector defining a Plane.
+
+ @return
+ The projected 3D Vector
+ */
+ B3DVector getProjectionOnPlane(const B3DVector& rNormalizedPlane) const;
+
+ /** Calculate the Scalar product
+
+ This method calculates the Scalar product between this
+ and the given 3D Vector.
+
+ @param rVec
+ A second 3D Vector.
+
+ @return
+ The Scalar Product of two 3D Vectors
+ */
+ double scalar(const B3DVector& rVec) const
+ {
+ return ((mfX * rVec.mfX) + (mfY * rVec.mfY) + (mfZ * rVec.mfZ));
+ }
+
+ /** Transform vector by given transformation matrix.
+
+ Since this is a vector, translational components of the
+ matrix are disregarded.
+ */
+ B3DVector& operator*=( const B3DHomMatrix& rMat );
+
+ static const B3DVector& getEmptyVector()
+ {
+ return (const B3DVector&) ::basegfx::B3DTuple::getEmptyTuple();
+ }
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ /** get a 3D Vector which is in 2D (ignoring
+ the Z-Coordinate) perpendicular to a given 3D Vector
+
+ @attention This only works if the given 3D Vector is normalized.
+
+ @param rNormalizedVec
+ A normalized 3D Vector.
+
+ @return
+ A 3D Vector perpendicular to the given one in X,Y (2D).
+ */
+ inline B3DVector getPerpendicular2D( const B3DVector& rNormalizedVec )
+ {
+ B3DVector aPerpendicular(-rNormalizedVec.getY(), rNormalizedVec.getX(), rNormalizedVec.getZ());
+ return aPerpendicular;
+ }
+
+ /** Test two vectors which need not to be normalized for parallelism
+
+ @param rVecA
+ The first 3D Vector
+
+ @param rVecB
+ The second 3D Vector
+
+ @return
+ bool if the two values are parallel. Also true if
+ one of the vectors is empty.
+ */
+ bool areParallel( const B3DVector& rVecA, const B3DVector& rVecB );
+
+ /** Transform vector by given transformation matrix.
+
+ Since this is a vector, translational components of the
+ matrix are disregarded.
+ */
+ B3DVector operator*( const B3DHomMatrix& rMat, const B3DVector& rVec );
+
+ /** Calculate the Cross Product of two 3D Vectors
+
+ @param rVecA
+ A first 3D Vector.
+
+ @param rVecB
+ A second 3D Vector.
+
+ @return
+ The Cross Product of both 3D Vectors
+ */
+ inline B3DVector cross(const B3DVector& rVecA, const B3DVector& rVecB)
+ {
+ B3DVector aVec(
+ rVecA.getY() * rVecB.getZ() - rVecA.getZ() * rVecB.getY(),
+ rVecA.getZ() * rVecB.getX() - rVecA.getX() * rVecB.getZ(),
+ rVecA.getX() * rVecB.getY() - rVecA.getY() * rVecB.getX());
+ return aVec;
+ }
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif /* _BGFX_VECTOR_B3DVECTOR_HXX */
diff --git a/basegfx/inc/basegfx/vector/b3isize.hxx b/basegfx/inc/basegfx/vector/b3isize.hxx
new file mode 100644
index 000000000000..5946c18e9399
--- /dev/null
+++ b/basegfx/inc/basegfx/vector/b3isize.hxx
@@ -0,0 +1,45 @@
+/*************************************************************************
+ *
+ * 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: b3isize.hxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_VECTOR_B3ISIZE_HXX
+#define _BGFX_VECTOR_B3ISIZE_HXX
+
+#include <basegfx/vector/b3ivector.hxx>
+
+namespace basegfx
+{
+ // syntactic sugar: a B3IVector exactly models a Size3D object,
+ // thus, for interface clarity, we provide an alias name
+
+ /// Alias name for interface clarity (not everybody is aware of the identity)
+ typedef B3IVector B3ISize;
+}
+
+#endif /* _BGFX_VECTOR_B3ISIZE_HXX */
diff --git a/basegfx/inc/basegfx/vector/b3ivector.hxx b/basegfx/inc/basegfx/vector/b3ivector.hxx
new file mode 100644
index 000000000000..bc379d0c514d
--- /dev/null
+++ b/basegfx/inc/basegfx/vector/b3ivector.hxx
@@ -0,0 +1,262 @@
+/*************************************************************************
+ *
+ * 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: b3ivector.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BGFX_VECTOR_B3IVECTOR_HXX
+#define _BGFX_VECTOR_B3IVECTOR_HXX
+
+#include <basegfx/tuple/b3ituple.hxx>
+
+namespace basegfx
+{
+ // predeclaration
+ class B3DHomMatrix;
+
+ /** Base Point class with three sal_Int32 values
+
+ This class derives all operators and common handling for
+ a 3D data class from B3ITuple. All necessary extensions
+ which are special for 3D Vectors are added here.
+
+ @see B3ITuple
+ */
+ class B3IVector : public ::basegfx::B3ITuple
+ {
+ public:
+ /** Create a 3D Vector
+
+ The vector is initialized to (0, 0, 0)
+ */
+ B3IVector()
+ : B3ITuple()
+ {}
+
+ /** Create a 3D Vector
+
+ @param nX
+ This parameter is used to initialize the X-coordinate
+ of the 3D Vector.
+
+ @param nY
+ This parameter is used to initialize the Y-coordinate
+ of the 3D Vector.
+
+ @param nZ
+ This parameter is used to initialize the Z-coordinate
+ of the 3D Vector.
+ */
+ B3IVector(sal_Int32 nX, sal_Int32 nY, sal_Int32 nZ)
+ : B3ITuple(nX, nY, nZ)
+ {}
+
+ /** Create a copy of a 3D Vector
+
+ @param rVec
+ The 3D Vector which will be copied.
+ */
+ B3IVector(const B3IVector& rVec)
+ : B3ITuple(rVec)
+ {}
+
+ /** constructor with tuple to allow copy-constructing
+ from B3ITuple-based classes
+ */
+ B3IVector(const ::basegfx::B3ITuple& rTuple)
+ : B3ITuple(rTuple)
+ {}
+
+ ~B3IVector()
+ {}
+
+ /** *=operator to allow usage from B3IVector, too
+ */
+ B3IVector& operator*=( const B3IVector& rPnt )
+ {
+ mnX *= rPnt.mnX;
+ mnY *= rPnt.mnY;
+ mnZ *= rPnt.mnZ;
+ return *this;
+ }
+
+ /** *=operator to allow usage from B3IVector, too
+ */
+ B3IVector& operator*=(sal_Int32 t)
+ {
+ mnX *= t;
+ mnY *= t;
+ mnZ *= t;
+ return *this;
+ }
+
+ /** assignment operator to allow assigning the results
+ of B3ITuple calculations
+ */
+ B3IVector& operator=( const ::basegfx::B3ITuple& rVec )
+ {
+ mnX = rVec.getX();
+ mnY = rVec.getY();
+ mnZ = rVec.getZ();
+ return *this;
+ }
+
+ /** Calculate the length of this 3D Vector
+
+ @return The Length of the 3D Vector
+ */
+ double getLength(void) const
+ {
+ double fLen(scalar(*this));
+ if((0 == fLen) || (1.0 == fLen))
+ return fLen;
+ return sqrt(fLen);
+ }
+
+ /** Calculate the length in the XY-Plane for this 3D Vector
+
+ @return The XY-Plane Length of the 3D Vector
+ */
+ double getXYLength(void) const
+ {
+ double fLen((mnX * mnX) + (mnY * mnY));
+ if((0 == fLen) || (1.0 == fLen))
+ return fLen;
+ return sqrt(fLen);
+ }
+
+ /** Calculate the length in the XZ-Plane for this 3D Vector
+
+ @return The XZ-Plane Length of the 3D Vector
+ */
+ double getXZLength(void) const
+ {
+ double fLen((mnX * mnZ) + (mnY * mnZ));
+ if((0 == fLen) || (1.0 == fLen))
+ return fLen;
+ return sqrt(fLen);
+ }
+
+ /** Calculate the length in the YZ-Plane for this 3D Vector
+
+ @return The YZ-Plane Length of the 3D Vector
+ */
+ double getYZLength(void) const
+ {
+ double fLen((mnY * mnY) + (mnZ * mnZ));
+ if((0 == fLen) || (1.0 == fLen))
+ return fLen;
+ return sqrt(fLen);
+ }
+
+ /** Set the length of this 3D Vector
+
+ @param fLen
+ The to be achieved length of the 3D Vector
+ */
+ B3IVector& setLength(double fLen)
+ {
+ double fLenNow(scalar(*this));
+
+ if(!::basegfx::fTools::equalZero(fLenNow))
+ {
+ const double fOne(1.0);
+
+ if(!::basegfx::fTools::equal(fOne, fLenNow))
+ {
+ fLen /= sqrt(fLenNow);
+ }
+
+ mnX = fround(mnX*fLen);
+ mnY = fround(mnY*fLen);
+ mnZ = fround(mnZ*fLen);
+ }
+
+ return *this;
+ }
+
+ /** Calculate the Scalar product
+
+ This method calculates the Scalar product between this
+ and the given 3D Vector.
+
+ @param rVec
+ A second 3D Vector.
+
+ @return
+ The Scalar Product of two 3D Vectors
+ */
+ double scalar(const B3IVector& rVec) const
+ {
+ return ((mnX * rVec.mnX) + (mnY * rVec.mnY) + (mnZ * rVec.mnZ));
+ }
+
+ /** Transform vector by given transformation matrix.
+
+ Since this is a vector, translational components of the
+ matrix are disregarded.
+ */
+ B3IVector& operator*=( const B3DHomMatrix& rMat );
+
+ static const B3IVector& getEmptyVector()
+ {
+ return (const B3IVector&) ::basegfx::B3ITuple::getEmptyTuple();
+ }
+ };
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ /** Transform vector by given transformation matrix.
+
+ Since this is a vector, translational components of the
+ matrix are disregarded.
+ */
+ B3IVector operator*( const B3DHomMatrix& rMat, const B3IVector& rVec );
+
+ /** Calculate the Cross Product of two 3D Vectors
+
+ @param rVecA
+ A first 3D Vector.
+
+ @param rVecB
+ A second 3D Vector.
+
+ @return
+ The Cross Product of both 3D Vectors
+ */
+ inline B3IVector cross(const B3IVector& rVecA, const B3IVector& rVecB)
+ {
+ B3IVector aVec(
+ rVecA.getY() * rVecB.getZ() - rVecA.getZ() * rVecB.getY(),
+ rVecA.getZ() * rVecB.getX() - rVecA.getX() * rVecB.getZ(),
+ rVecA.getX() * rVecB.getY() - rVecA.getY() * rVecB.getX());
+ return aVec;
+ }
+} // end of namespace basegfx
+
+#endif /* _BGFX_VECTOR_B3DVECTOR_HXX */
diff --git a/basegfx/inc/makefile.mk b/basegfx/inc/makefile.mk
new file mode 100644
index 000000000000..8c9579b1b673
--- /dev/null
+++ b/basegfx/inc/makefile.mk
@@ -0,0 +1,51 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.3 $
+#
+# 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=inc
+
+# --- Settings -----------------------------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files --------------------------------------------------------
+# --- Targets -------------------------------------------------------
+
+.INCLUDE : target.mk
+
+.IF "$(ENABLE_PCH)"!=""
+ALLTAR : \
+ $(SLO)$/precompiled.pch \
+ $(SLO)$/precompiled_ex.pch
+
+.ENDIF # "$(ENABLE_PCH)"!=""
+
diff --git a/basegfx/inc/pch/precompiled_basegfx.cxx b/basegfx/inc/pch/precompiled_basegfx.cxx
new file mode 100644
index 000000000000..9a32a5d91d8a
--- /dev/null
+++ b/basegfx/inc/pch/precompiled_basegfx.cxx
@@ -0,0 +1,32 @@
+/*************************************************************************
+ *
+ * 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: precompiled_basegfx.cxx,v $
+ * $Revision: 1.3 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#include "precompiled_basegfx.hxx"
+
diff --git a/basegfx/inc/pch/precompiled_basegfx.hxx b/basegfx/inc/pch/precompiled_basegfx.hxx
new file mode 100644
index 000000000000..36d8dafc609d
--- /dev/null
+++ b/basegfx/inc/pch/precompiled_basegfx.hxx
@@ -0,0 +1,35 @@
+/*************************************************************************
+ *
+ * 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: precompiled_basegfx.hxx,v $
+ * $Revision: 1.3 $
+ *
+ * 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): Generated on 2006-09-01 17:49:30.796084
+
+#ifdef PRECOMPILED_HEADERS
+#endif
+
diff --git a/basegfx/prj/build.lst b/basegfx/prj/build.lst
new file mode 100644
index 000000000000..418b3822a7ee
--- /dev/null
+++ b/basegfx/prj/build.lst
@@ -0,0 +1,18 @@
+fx basegfx : o3tl sal offuh cppuhelper cppu testshl2 NULL
+fx basegfx usr1 - all fx_mkout NULL
+fx basegfx\inc nmake - all fx_inc NULL
+fx basegfx\prj get - all fx_prj NULL
+fx basegfx\source\curve nmake - all fx_curve fx_inc NULL
+fx basegfx\source\matrix nmake - all fx_matrix fx_inc NULL
+fx basegfx\source\numeric nmake - all fx_numeric fx_inc NULL
+fx basegfx\source\point nmake - all fx_point fx_inc NULL
+fx basegfx\source\polygon nmake - all fx_polygon fx_inc NULL
+fx basegfx\source\range nmake - all fx_range fx_inc NULL
+fx basegfx\source\tuple nmake - all fx_tuple fx_inc NULL
+fx basegfx\source\tools nmake - all fx_tools fx_inc NULL
+fx basegfx\source\vector nmake - all fx_vector fx_inc NULL
+fx basegfx\source\color nmake - all fx_color fx_inc NULL
+fx basegfx\source\pixel nmake - all fx_pixel fx_inc NULL
+fx basegfx\source\raster nmake - all fx_raster fx_inc NULL
+fx basegfx\util nmake - all fx_util fx_curve fx_matrix fx_numeric fx_point fx_polygon fx_range fx_tuple fx_tools fx_vector fx_color fx_pixel fx_raster NULL
+fx basegfx\test nmake - all fx_tests fx_util NULL
diff --git a/basegfx/prj/d.lst b/basegfx/prj/d.lst
new file mode 100644
index 000000000000..a58cd33e4f9c
--- /dev/null
+++ b/basegfx/prj/d.lst
@@ -0,0 +1,109 @@
+..\%__SRC%\lib\ibasegfx.lib %_DEST%\lib%_EXT%\ibasegfx.lib
+..\%__SRC%\lib\basegfx_s.lib %_DEST%\lib%_EXT%\basegfx_s.lib
+
+..\%__SRC%\bin\basegfx?????.dll %_DEST%\bin%_EXT%\basegfx?????.dll
+..\%__SRC%\bin\bgfx*.dll %_DEST%\bin%_EXT%\bgfx*.dll
+
+..\%__SRC%\lib\libbasegfx*.* %_DEST%\lib%_EXT%\libbasegfx*.*
+..\%__SRC%\lib\*.a %_DEST%\lib%_EXT%\*.a
+
+mkdir: %_DEST%\inc%_EXT%\basegfx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\matrix
+..\inc\basegfx\matrix\b2dhommatrix.hxx %_DEST%\inc%_EXT%\basegfx\matrix\b2dhommatrix.hxx
+..\inc\basegfx\matrix\b2dhommatrixtools.hxx %_DEST%\inc%_EXT%\basegfx\matrix\b2dhommatrixtools.hxx
+..\inc\basegfx\matrix\b3dhommatrix.hxx %_DEST%\inc%_EXT%\basegfx\matrix\b3dhommatrix.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\point
+..\inc\basegfx\point\b2dpoint.hxx %_DEST%\inc%_EXT%\basegfx\point\b2dpoint.hxx
+..\inc\basegfx\point\b3dpoint.hxx %_DEST%\inc%_EXT%\basegfx\point\b3dpoint.hxx
+..\inc\basegfx\point\b2ipoint.hxx %_DEST%\inc%_EXT%\basegfx\point\b2ipoint.hxx
+..\inc\basegfx\point\b3ipoint.hxx %_DEST%\inc%_EXT%\basegfx\point\b3ipoint.hxx
+..\inc\basegfx\point\b2dhompoint.hxx %_DEST%\inc%_EXT%\basegfx\point\b2dhompoint.hxx
+..\inc\basegfx\point\b3dhompoint.hxx %_DEST%\inc%_EXT%\basegfx\point\b3dhompoint.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\range
+..\inc\basegfx\range\rangeexpander.hxx %_DEST%\inc%_EXT%\basegfx\range\rangeexpander.hxx
+..\inc\basegfx\range\basicrange.hxx %_DEST%\inc%_EXT%\basegfx\range\basicrange.hxx
+..\inc\basegfx\range\basicbox.hxx %_DEST%\inc%_EXT%\basegfx\range\basicbox.hxx
+..\inc\basegfx\range\b1drange.hxx %_DEST%\inc%_EXT%\basegfx\range\b1drange.hxx
+..\inc\basegfx\range\b2dmultirange.hxx %_DEST%\inc%_EXT%\basegfx\range\b2dmultirange.hxx
+..\inc\basegfx\range\b2drange.hxx %_DEST%\inc%_EXT%\basegfx\range\b2drange.hxx
+..\inc\basegfx\range\b2drectangle.hxx %_DEST%\inc%_EXT%\basegfx\range\b2drectangle.hxx
+..\inc\basegfx\range\b2dconnectedranges.hxx %_DEST%\inc%_EXT%\basegfx\range\b2dconnectedranges.hxx
+..\inc\basegfx\range\b3drange.hxx %_DEST%\inc%_EXT%\basegfx\range\b3drange.hxx
+..\inc\basegfx\range\b3dvolume.hxx %_DEST%\inc%_EXT%\basegfx\range\b3dvolume.hxx
+..\inc\basegfx\range\b1irange.hxx %_DEST%\inc%_EXT%\basegfx\range\b1irange.hxx
+..\inc\basegfx\range\b2irange.hxx %_DEST%\inc%_EXT%\basegfx\range\b2irange.hxx
+..\inc\basegfx\range\b2irectangle.hxx %_DEST%\inc%_EXT%\basegfx\range\b2irectangle.hxx
+..\inc\basegfx\range\b3irange.hxx %_DEST%\inc%_EXT%\basegfx\range\b3irange.hxx
+..\inc\basegfx\range\b3ivolume.hxx %_DEST%\inc%_EXT%\basegfx\range\b3ivolume.hxx
+..\inc\basegfx\range\b1ibox.hxx %_DEST%\inc%_EXT%\basegfx\range\b1ibox.hxx
+..\inc\basegfx\range\b2ibox.hxx %_DEST%\inc%_EXT%\basegfx\range\b2ibox.hxx
+..\inc\basegfx\range\b3ibox.hxx %_DEST%\inc%_EXT%\basegfx\range\b3ibox.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\vector
+..\inc\basegfx\vector\b2dvector.hxx %_DEST%\inc%_EXT%\basegfx\vector\b2dvector.hxx
+..\inc\basegfx\vector\b2enums.hxx %_DEST%\inc%_EXT%\basegfx\vector\b2enums.hxx
+..\inc\basegfx\vector\b2dsize.hxx %_DEST%\inc%_EXT%\basegfx\vector\b2dsize.hxx
+..\inc\basegfx\vector\b3dvector.hxx %_DEST%\inc%_EXT%\basegfx\vector\b3dvector.hxx
+..\inc\basegfx\vector\b3dsize.hxx %_DEST%\inc%_EXT%\basegfx\vector\b3dsize.hxx
+..\inc\basegfx\vector\b2ivector.hxx %_DEST%\inc%_EXT%\basegfx\vector\b2ivector.hxx
+..\inc\basegfx\vector\b2isize.hxx %_DEST%\inc%_EXT%\basegfx\vector\b2isize.hxx
+..\inc\basegfx\vector\b3ivector.hxx %_DEST%\inc%_EXT%\basegfx\vector\b3ivector.hxx
+..\inc\basegfx\vector\b3isize.hxx %_DEST%\inc%_EXT%\basegfx\vector\b3isize.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\curve
+..\inc\basegfx\curve\b2dcubicbezier.hxx %_DEST%\inc%_EXT%\basegfx\curve\b2dcubicbezier.hxx
+..\inc\basegfx\curve\b2dquadraticbezier.hxx %_DEST%\inc%_EXT%\basegfx\curve\b2dquadraticbezier.hxx
+..\inc\basegfx\curve\b2dbeziertools.hxx %_DEST%\inc%_EXT%\basegfx\curve\b2dbeziertools.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\numeric
+..\inc\basegfx\numeric\ftools.hxx %_DEST%\inc%_EXT%\basegfx\numeric\ftools.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\polygon
+..\inc\basegfx\polygon\b2dpolygon.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dpolygon.hxx
+..\inc\basegfx\polygon\b2dpolypolygon.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dpolypolygon.hxx
+..\inc\basegfx\polygon\b2dpolypolygonfillrule.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dpolypolygonfillrule.hxx
+..\inc\basegfx\polygon\b2dpolygontools.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dpolygontools.hxx
+..\inc\basegfx\polygon\b2dpolypolygontools.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dpolypolygontools.hxx
+..\inc\basegfx\polygon\b2dpolypolygonrasterconverter.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dpolypolygonrasterconverter.hxx
+..\inc\basegfx\polygon\b2dlinegeometry.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dlinegeometry.hxx
+..\inc\basegfx\polygon\b2dpolygonclipper.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dpolygonclipper.hxx
+..\inc\basegfx\polygon\b2dpolygontriangulator.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dpolygontriangulator.hxx
+..\inc\basegfx\polygon\b2dpolygoncutandtouch.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dpolygoncutandtouch.hxx
+..\inc\basegfx\polygon\b2dpolypolygoncutter.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b2dpolypolygoncutter.hxx
+..\inc\basegfx\polygon\b3dpolygon.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b3dpolygon.hxx
+..\inc\basegfx\polygon\b3dpolypolygon.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b3dpolypolygon.hxx
+..\inc\basegfx\polygon\b3dpolygontools.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b3dpolygontools.hxx
+..\inc\basegfx\polygon\b3dpolypolygontools.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b3dpolypolygontools.hxx
+..\inc\basegfx\polygon\b3dpolygonclipper.hxx %_DEST%\inc%_EXT%\basegfx\polygon\b3dpolygonclipper.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\tuple
+..\inc\basegfx\tuple\b2dtuple.hxx %_DEST%\inc%_EXT%\basegfx\tuple\b2dtuple.hxx
+..\inc\basegfx\tuple\b3dtuple.hxx %_DEST%\inc%_EXT%\basegfx\tuple\b3dtuple.hxx
+..\inc\basegfx\tuple\b2ituple.hxx %_DEST%\inc%_EXT%\basegfx\tuple\b2ituple.hxx
+..\inc\basegfx\tuple\b3ituple.hxx %_DEST%\inc%_EXT%\basegfx\tuple\b3ituple.hxx
+..\inc\basegfx\tuple\b2i64tuple.hxx %_DEST%\inc%_EXT%\basegfx\tuple\b2i64tuple.hxx
+..\inc\basegfx\tuple\b3i64tuple.hxx %_DEST%\inc%_EXT%\basegfx\tuple\b3i64tuple.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\tools
+..\inc\basegfx\tools\canvastools.hxx %_DEST%\inc%_EXT%\basegfx\tools\canvastools.hxx
+..\inc\basegfx\tools\unopolypolygon.hxx %_DEST%\inc%_EXT%\basegfx\tools\unopolypolygon.hxx
+..\inc\basegfx\tools\rectcliptools.hxx %_DEST%\inc%_EXT%\basegfx\tools\rectcliptools.hxx
+..\inc\basegfx\tools\tools.hxx %_DEST%\inc%_EXT%\basegfx\tools\tools.hxx
+..\inc\basegfx\tools\gradienttools.hxx %_DEST%\inc%_EXT%\basegfx\tools\gradienttools.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\color
+..\inc\basegfx\color\bcolor.hxx %_DEST%\inc%_EXT%\basegfx\color\bcolor.hxx
+..\inc\basegfx\color\bcolortools.hxx %_DEST%\inc%_EXT%\basegfx\color\bcolortools.hxx
+..\inc\basegfx\color\bcolormodifier.hxx %_DEST%\inc%_EXT%\basegfx\color\bcolormodifier.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\pixel
+..\inc\basegfx\pixel\bpixel.hxx %_DEST%\inc%_EXT%\basegfx\pixel\bpixel.hxx
+..\inc\basegfx\pixel\bzpixel.hxx %_DEST%\inc%_EXT%\basegfx\pixel\bzpixel.hxx
+
+mkdir: %_DEST%\inc%_EXT%\basegfx\raster
+..\inc\basegfx\raster\bpixelraster.hxx %_DEST%\inc%_EXT%\basegfx\raster\bpixelraster.hxx
+..\inc\basegfx\raster\bzpixelraster.hxx %_DEST%\inc%_EXT%\basegfx\raster\bzpixelraster.hxx
+..\inc\basegfx\raster\rasterconvert3d.hxx %_DEST%\inc%_EXT%\basegfx\raster\rasterconvert3d.hxx
diff --git a/basegfx/source/color/bcolor.cxx b/basegfx/source/color/bcolor.cxx
new file mode 100644
index 000000000000..f6e8b90b38e2
--- /dev/null
+++ b/basegfx/source/color/bcolor.cxx
@@ -0,0 +1,44 @@
+/*************************************************************************
+ *
+ * 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: bcolor.cxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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/color/bcolor.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/color/bcolormodifier.cxx b/basegfx/source/color/bcolormodifier.cxx
new file mode 100644
index 000000000000..f1947c70e0f2
--- /dev/null
+++ b/basegfx/source/color/bcolormodifier.cxx
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *
+ * 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: bcolormodifier.cxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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/color/bcolormodifier.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ ::basegfx::BColor BColorModifier::getModifiedColor(const ::basegfx::BColor& aSourceColor) const
+ {
+ switch(meMode)
+ {
+ case BCOLORMODIFYMODE_INTERPOLATE :
+ {
+ return interpolate(maBColor, aSourceColor, mfValue);
+ }
+ case BCOLORMODIFYMODE_GRAY :
+ {
+ const double fLuminance(aSourceColor.luminance());
+ return ::basegfx::BColor(fLuminance, fLuminance, fLuminance);
+ }
+ case BCOLORMODIFYMODE_BLACKANDWHITE :
+ {
+ const double fLuminance(aSourceColor.luminance());
+
+ if(fLuminance < mfValue)
+ {
+ return ::basegfx::BColor::getEmptyBColor();
+ }
+ else
+ {
+ return ::basegfx::BColor(1.0, 1.0, 1.0);
+ }
+ }
+ default : // BCOLORMODIFYMODE_REPLACE
+ {
+ return maBColor;
+ }
+ }
+ }
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/color/bcolortools.cxx b/basegfx/source/color/bcolortools.cxx
new file mode 100644
index 000000000000..1c7d2b222fff
--- /dev/null
+++ b/basegfx/source/color/bcolortools.cxx
@@ -0,0 +1,272 @@
+/*************************************************************************
+ *
+ * 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: bcolor.cxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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/numeric/ftools.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/color/bcolortools.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx { namespace tools
+{
+ BColor rgb2hsl(const BColor& rRGBColor)
+ {
+ const double r=rRGBColor.getRed(), g=rRGBColor.getGreen(), b=rRGBColor.getBlue();
+ const double minVal = ::std::min( ::std::min( r, g ), b );
+ const double maxVal = ::std::max( ::std::max( r, g ), b );
+ const double d = maxVal - minVal;
+
+ double h=0, s=0, l=0;
+
+ l = (maxVal + minVal) / 2.0;
+
+ if( ::basegfx::fTools::equalZero(d) )
+ {
+ s = h = 0; // hue undefined (achromatic case)
+ }
+ else
+ {
+ s = l > 0.5 ? d/(2.0-maxVal-minVal) :
+ d/(maxVal + minVal);
+
+ if( r == maxVal )
+ h = (g - b)/d;
+ else if( g == maxVal )
+ h = 2.0 + (b - r)/d;
+ else
+ h = 4.0 + (r - h)/d;
+
+ h *= 60.0;
+
+ if( h < 0.0 )
+ h += 360.0;
+ }
+
+ return BColor(h,s,l);
+ }
+
+ static inline double hsl2rgbHelper( double nValue1, double nValue2, double nHue )
+ {
+ // clamp hue to [0,360]
+ nHue = fmod( nHue, 360.0 );
+
+ // cope with wrap-arounds
+ if( nHue < 0.0 )
+ nHue += 360.0;
+
+ if( nHue < 60.0 )
+ return nValue1 + (nValue2 - nValue1)*nHue/60.0;
+ else if( nHue < 180.0 )
+ return nValue2;
+ else if( nHue < 240.0 )
+ return nValue1 + (nValue2 - nValue1)*(240.0 - nHue)/60.0;
+ else
+ return nValue1;
+ }
+
+ BColor hsl2rgb(const BColor& rHSLColor)
+ {
+ const double h=rHSLColor.getRed(), s=rHSLColor.getGreen(), l=rHSLColor.getBlue();
+
+ if( fTools::equalZero(s) )
+ return BColor(l, l, l ); // achromatic case
+
+ const double nVal1( l <= 0.5 ? l*(1.0 + s) : l + s - l*s );
+ const double nVal2( 2.0*l - nVal1 );
+
+ return BColor(
+ hsl2rgbHelper(nVal2,
+ nVal1,
+ h + 120.0),
+ hsl2rgbHelper(nVal2,
+ nVal1,
+ h),
+ hsl2rgbHelper(nVal2,
+ nVal1,
+ h - 120.0) );
+ }
+
+ BColor rgb2hsv(const BColor& rRGBColor)
+ {
+ const double r=rRGBColor.getRed(), g=rRGBColor.getGreen(), b=rRGBColor.getBlue();
+ const double maxVal = std::max(std::max(r,g),b);
+ const double minVal = std::min(std::min(r,g),b);
+ const double delta = maxVal-minVal;
+
+ double h=0, s=0, v=0;
+
+ v = maxVal;
+ if( fTools::equalZero(v) )
+ s = 0;
+ else
+ s = delta / v;
+
+ if( !fTools::equalZero(s) )
+ {
+ if( maxVal == r )
+ {
+ h = (g - b) / delta;
+ }
+ else if( maxVal == g )
+ {
+ h = 2.0 + (b - r) / delta;
+ }
+ else
+ {
+ h = 4.0 + (r - g) / delta;
+ }
+
+ h *= 60.0;
+
+ if( h < 0 )
+ h += 360;
+ }
+
+ return BColor(h,s,v);
+ }
+
+ BColor hsv2rgb(const BColor& rHSVColor)
+ {
+ double h=rHSVColor.getRed();
+ const double s=rHSVColor.getGreen(), v=rHSVColor.getBlue();
+
+ if( fTools::equalZero(s) )
+ {
+ // achromatic case: no hue.
+ return BColor(v,v,v);
+ }
+ else
+ {
+ if( fTools::equal(h,360) )
+ h = 0; // 360 degrees is equivalent to 0 degrees
+
+ h /= 60.0;
+ const sal_Int32 intval = static_cast< sal_Int32 >( h );
+ const double f = h - intval;
+ const double p = v*(1.0-s);
+ const double q = v*(1.0-(s*f));
+ const double t = v*(1.0-(s*(1.0-f)));
+
+ /* which hue area? */
+ switch( intval )
+ {
+ case 0:
+ return BColor(v,t,p);
+
+ case 1:
+ return BColor(q,v,p);
+
+ case 2:
+ return BColor(p,v,t);
+
+ case 3:
+ return BColor(p,q,v);
+
+ case 4:
+ return BColor(t,p,v);
+
+ case 5:
+ return BColor(v,p,q);
+
+ default:
+ // hue overflow
+ return BColor();
+ }
+ }
+ }
+
+ BColor rgb2yiq(const BColor& rRGBColor)
+ {
+ // from Foley, van Dam, Computer Graphics
+ const double r=rRGBColor.getRed(), g=rRGBColor.getGreen(), b=rRGBColor.getBlue();
+ return BColor(
+ 0.299*r + 0.587*g + 0.114*b,
+ 0.596*r - 0.274*g - 0.322*b,
+ 0.211*r - 0.522*g + 0.311*b);
+ }
+
+ BColor yiq2rgb(const BColor& rYIQColor)
+ {
+ // from Foley, van Dam, Computer Graphics
+ const double y=rYIQColor.getRed(), i=rYIQColor.getGreen(), q=rYIQColor.getBlue();
+ return BColor(
+ y + 0.956*i + 0.623*q,
+ y - 0.272*i - 0.648*q,
+ y - 1.105*i + 1.705*q );
+ }
+
+ BColor ciexyz2rgb( const BColor& rXYZColor )
+ {
+ // from Poynton color faq, and SMPTE RP 177-1993, Derivation
+ // of Basic Television Color Equations
+ const double x=rXYZColor.getRed(), y=rXYZColor.getGreen(), z=rXYZColor.getBlue();
+ return BColor(
+ 3.240479*x - 1.53715*y - 0.498535*z,
+ -0.969256*x + 1.875991*y + 0.041556*z,
+ 0.055648*x - 0.204043*y + 1.057311*z );
+ }
+
+ BColor rgb2ciexyz( const BColor& rRGBColor )
+ {
+ // from Poynton color faq, and SMPTE RP 177-1993, Derivation
+ // of Basic Television Color Equations
+ const double r=rRGBColor.getRed(), g=rRGBColor.getGreen(), b=rRGBColor.getBlue();
+ return BColor(
+ 0.412453*r + 0.35758*g + 0.180423*b,
+ 0.212671*r + 0.71516*g + 0.072169*b,
+ 0.019334*r + 0.119193*g + 0.950227*b);
+ }
+
+ BColor rgb2ypbpr(const BColor& rRGBColor)
+ {
+ const double r=rRGBColor.getRed(), g=rRGBColor.getGreen(), b=rRGBColor.getBlue();
+ return BColor(
+ 0.299*r + 0.587*g + 0.114*b,
+ -0.168736*r - 0.331264*g + 0.5*b,
+ 0.5*r - 0.418688*g - 0.081312*b);
+ }
+
+ BColor ypbpr2rgb(const BColor& rYPbPrColor)
+ {
+ const double y=rYPbPrColor.getRed(), pb=rYPbPrColor.getGreen(), pr=rYPbPrColor.getBlue();
+ return BColor(
+ 1.*y + 0.*pb + 1.402*pr,
+ 1.*y - 0.344136*pb - 0.714136*pr,
+ 1.*y + 1.772*pb + 0.*pr);
+ }
+
+} } // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/color/makefile.mk b/basegfx/source/color/makefile.mk
new file mode 100644
index 000000000000..77b91a82f3ac
--- /dev/null
+++ b/basegfx/source/color/makefile.mk
@@ -0,0 +1,53 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.2 $
+#
+# 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=color
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+#ENABLE_EXCEPTIONS=FALSE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/bcolor.obj \
+ $(SLO)$/bcolortools.obj \
+ $(SLO)$/bcolormodifier.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/curve/b2dbeziertools.cxx b/basegfx/source/curve/b2dbeziertools.cxx
new file mode 100644
index 000000000000..dd19356af488
--- /dev/null
+++ b/basegfx/source/curve/b2dbeziertools.cxx
@@ -0,0 +1,166 @@
+/*************************************************************************
+ *
+ * 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: b2dbeziertools.cxx,v $
+ * $Revision: 1.14 $
+ *
+ * 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/curve/b2dbeziertools.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <algorithm>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ B2DCubicBezierHelper::B2DCubicBezierHelper(const B2DCubicBezier& rBase, sal_uInt32 nDivisions)
+ : maLengthArray(),
+ mnEdgeCount(0)
+ {
+ const bool bIsBezier(rBase.isBezier());
+
+ if(bIsBezier)
+ {
+ // check nDivisions; at least one is needed, but also prevent too big values
+ if(nDivisions < 1)
+ {
+ nDivisions = 1;
+ }
+ else if(nDivisions > 1000)
+ {
+ nDivisions = 1000;
+ }
+
+ // set nEdgeCount
+ mnEdgeCount = nDivisions + 1;
+
+ // fill in maLengthArray
+ maLengthArray.clear();
+ maLengthArray.reserve(mnEdgeCount);
+ B2DPoint aCurrent(rBase.getStartPoint());
+ double fLength(0.0);
+
+ for(sal_uInt32 a(1);;)
+ {
+ const B2DPoint aNext(rBase.interpolatePoint((double)a / (double)mnEdgeCount));
+ const B2DVector aEdge(aNext - aCurrent);
+
+ fLength += aEdge.getLength();
+ maLengthArray.push_back(fLength);
+
+ if(++a < mnEdgeCount)
+ {
+ aCurrent = aNext;
+ }
+ else
+ {
+ const B2DPoint aLastNext(rBase.getEndPoint());
+ const B2DVector aLastEdge(aLastNext - aNext);
+
+ fLength += aLastEdge.getLength();
+ maLengthArray.push_back(fLength);
+ break;
+ }
+ }
+ }
+ else
+ {
+ maLengthArray.clear();
+ maLengthArray.push_back(rBase.getEdgeLength());
+ mnEdgeCount = 1;
+ }
+ }
+
+ double B2DCubicBezierHelper::distanceToRelative(double fDistance) const
+ {
+ if(fDistance <= 0.0)
+ {
+ return 0.0;
+ }
+
+ const double fLength(getLength());
+
+ if(fTools::moreOrEqual(fDistance, fLength))
+ {
+ return 1.0;
+ }
+
+ // fDistance is in ]0.0 .. fLength[
+
+ if(1 == mnEdgeCount)
+ {
+ // not a bezier, linear edge
+ return fDistance / fLength;
+ }
+
+ // it is a bezier
+ ::std::vector< double >::const_iterator aIter = ::std::lower_bound(maLengthArray.begin(), maLengthArray.end(), fDistance);
+ const sal_uInt32 nIndex(aIter - maLengthArray.begin());
+ const double fHighBound(maLengthArray[nIndex]);
+ const double fLowBound(nIndex ? maLengthArray[nIndex - 1] : 0.0);
+ const double fLinearInterpolatedLength((fDistance - fLowBound) / (fHighBound - fLowBound));
+
+ return (static_cast< double >(nIndex) + fLinearInterpolatedLength) / static_cast< double >(mnEdgeCount);
+ }
+
+ double B2DCubicBezierHelper::relativeToDistance(double fRelative) const
+ {
+ if(fRelative <= 0.0)
+ {
+ return 0.0;
+ }
+
+ const double fLength(getLength());
+
+ if(fTools::moreOrEqual(fRelative, 1.0))
+ {
+ return fLength;
+ }
+
+ // fRelative is in ]0.0 .. 1.0[
+
+ if(1 == mnEdgeCount)
+ {
+ // not a bezier, linear edge
+ return fRelative * fLength;
+ }
+
+ // fRelative is in ]0.0 .. 1.0[
+ const double fIndex(fRelative * static_cast< double >(mnEdgeCount));
+ double fIntIndex;
+ const double fFractIndex(modf(fIndex, &fIntIndex));
+ const sal_uInt32 nIntIndex(static_cast< sal_uInt32 >(fIntIndex));
+ const double fStartDistance(nIntIndex ? maLengthArray[nIntIndex - 1] : 0.0);
+
+ return fStartDistance + ((maLengthArray[nIntIndex] - fStartDistance) * fFractIndex);
+ }
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+// eof
diff --git a/basegfx/source/curve/b2dcubicbezier.cxx b/basegfx/source/curve/b2dcubicbezier.cxx
new file mode 100644
index 000000000000..83c620df7870
--- /dev/null
+++ b/basegfx/source/curve/b2dcubicbezier.cxx
@@ -0,0 +1,1108 @@
+/*************************************************************************
+ *
+ * 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: b2dcubicbezier.cxx,v $
+ *
+ * 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/curve/b2dcubicbezier.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+#include <limits>
+
+// #i37443#
+#define FACTOR_FOR_UNSHARPEN (1.6)
+#ifdef DBG_UTIL
+static double fMultFactUnsharpen = FACTOR_FOR_UNSHARPEN;
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace
+ {
+ void ImpSubDivAngle(
+ const B2DPoint& rfPA, // start point
+ const B2DPoint& rfEA, // edge on A
+ const B2DPoint& rfEB, // edge on B
+ const B2DPoint& rfPB, // end point
+ B2DPolygon& rTarget, // target polygon
+ double fAngleBound, // angle bound in [0.0 .. 2PI]
+ bool bAllowUnsharpen, // #i37443# allow the criteria to get unsharp in recursions
+ sal_uInt16 nMaxRecursionDepth) // endless loop protection
+ {
+ if(nMaxRecursionDepth)
+ {
+ // do angle test
+ B2DVector aLeft(rfEA - rfPA);
+ B2DVector aRight(rfEB - rfPB);
+
+ // #i72104#
+ if(aLeft.equalZero())
+ {
+ aLeft = rfEB - rfPA;
+ }
+
+ if(aRight.equalZero())
+ {
+ aRight = rfEA - rfPB;
+ }
+
+ const double fCurrentAngle(aLeft.angle(aRight));
+
+ if(fabs(fCurrentAngle) > (F_PI - fAngleBound))
+ {
+ // end recursion
+ nMaxRecursionDepth = 0;
+ }
+ else
+ {
+ if(bAllowUnsharpen)
+ {
+ // #i37443# unsharpen criteria
+#ifdef DBG_UTIL
+ fAngleBound *= fMultFactUnsharpen;
+#else
+ fAngleBound *= FACTOR_FOR_UNSHARPEN;
+#endif
+ }
+ }
+ }
+
+ if(nMaxRecursionDepth)
+ {
+ // divide at 0.5
+ const B2DPoint aS1L(average(rfPA, rfEA));
+ const B2DPoint aS1C(average(rfEA, rfEB));
+ const B2DPoint aS1R(average(rfEB, rfPB));
+ const B2DPoint aS2L(average(aS1L, aS1C));
+ const B2DPoint aS2R(average(aS1C, aS1R));
+ const B2DPoint aS3C(average(aS2L, aS2R));
+
+ // left recursion
+ ImpSubDivAngle(rfPA, aS1L, aS2L, aS3C, rTarget, fAngleBound, bAllowUnsharpen, nMaxRecursionDepth - 1);
+
+ // right recursion
+ ImpSubDivAngle(aS3C, aS2R, aS1R, rfPB, rTarget, fAngleBound, bAllowUnsharpen, nMaxRecursionDepth - 1);
+ }
+ else
+ {
+ rTarget.append(rfPB);
+ }
+ }
+
+ void ImpSubDivAngleStart(
+ const B2DPoint& rfPA, // start point
+ const B2DPoint& rfEA, // edge on A
+ const B2DPoint& rfEB, // edge on B
+ const B2DPoint& rfPB, // end point
+ B2DPolygon& rTarget, // target polygon
+ const double& rfAngleBound, // angle bound in [0.0 .. 2PI]
+ bool bAllowUnsharpen) // #i37443# allow the criteria to get unsharp in recursions
+ {
+ sal_uInt16 nMaxRecursionDepth(8);
+ const B2DVector aLeft(rfEA - rfPA);
+ const B2DVector aRight(rfEB - rfPB);
+ bool bLeftEqualZero(aLeft.equalZero());
+ bool bRightEqualZero(aRight.equalZero());
+ bool bAllParallel(false);
+
+ if(bLeftEqualZero && bRightEqualZero)
+ {
+ nMaxRecursionDepth = 0;
+ }
+ else
+ {
+ const B2DVector aBase(rfPB - rfPA);
+ const bool bBaseEqualZero(aBase.equalZero()); // #i72104#
+
+ if(!bBaseEqualZero)
+ {
+ const bool bLeftParallel(bLeftEqualZero ? true : areParallel(aLeft, aBase));
+ const bool bRightParallel(bRightEqualZero ? true : areParallel(aRight, aBase));
+
+ if(bLeftParallel && bRightParallel)
+ {
+ bAllParallel = true;
+
+ if(!bLeftEqualZero)
+ {
+ double fFactor;
+
+ if(fabs(aBase.getX()) > fabs(aBase.getY()))
+ {
+ fFactor = aLeft.getX() / aBase.getX();
+ }
+ else
+ {
+ fFactor = aLeft.getY() / aBase.getY();
+ }
+
+ if(fFactor >= 0.0 && fFactor <= 1.0)
+ {
+ bLeftEqualZero = true;
+ }
+ }
+
+ if(!bRightEqualZero)
+ {
+ double fFactor;
+
+ if(fabs(aBase.getX()) > fabs(aBase.getY()))
+ {
+ fFactor = aRight.getX() / -aBase.getX();
+ }
+ else
+ {
+ fFactor = aRight.getY() / -aBase.getY();
+ }
+
+ if(fFactor >= 0.0 && fFactor <= 1.0)
+ {
+ bRightEqualZero = true;
+ }
+ }
+
+ if(bLeftEqualZero && bRightEqualZero)
+ {
+ nMaxRecursionDepth = 0;
+ }
+ }
+ }
+ }
+
+ if(nMaxRecursionDepth)
+ {
+ // divide at 0.5 ad test both edges for angle criteria
+ const B2DPoint aS1L(average(rfPA, rfEA));
+ const B2DPoint aS1C(average(rfEA, rfEB));
+ const B2DPoint aS1R(average(rfEB, rfPB));
+ const B2DPoint aS2L(average(aS1L, aS1C));
+ const B2DPoint aS2R(average(aS1C, aS1R));
+ const B2DPoint aS3C(average(aS2L, aS2R));
+
+ // test left
+ bool bAngleIsSmallerLeft(bAllParallel && bLeftEqualZero);
+ if(!bAngleIsSmallerLeft)
+ {
+ const B2DVector aLeftLeft(bLeftEqualZero ? aS2L - aS1L : aS1L - rfPA); // #i72104#
+ const B2DVector aRightLeft(aS2L - aS3C);
+ const double fCurrentAngleLeft(aLeftLeft.angle(aRightLeft));
+ bAngleIsSmallerLeft = (fabs(fCurrentAngleLeft) > (F_PI - rfAngleBound));
+ }
+
+ // test right
+ bool bAngleIsSmallerRight(bAllParallel && bRightEqualZero);
+ if(!bAngleIsSmallerRight)
+ {
+ const B2DVector aLeftRight(aS2R - aS3C);
+ const B2DVector aRightRight(bRightEqualZero ? aS2R - aS1R : aS1R - rfPB); // #i72104#
+ const double fCurrentAngleRight(aLeftRight.angle(aRightRight));
+ bAngleIsSmallerRight = (fabs(fCurrentAngleRight) > (F_PI - rfAngleBound));
+ }
+
+ if(bAngleIsSmallerLeft && bAngleIsSmallerRight)
+ {
+ // no recursion necessary at all
+ nMaxRecursionDepth = 0;
+ }
+ else
+ {
+ // left
+ if(bAngleIsSmallerLeft)
+ {
+ rTarget.append(aS3C);
+ }
+ else
+ {
+ ImpSubDivAngle(rfPA, aS1L, aS2L, aS3C, rTarget, rfAngleBound, bAllowUnsharpen, nMaxRecursionDepth);
+ }
+
+ // right
+ if(bAngleIsSmallerRight)
+ {
+ rTarget.append(rfPB);
+ }
+ else
+ {
+ ImpSubDivAngle(aS3C, aS2R, aS1R, rfPB, rTarget, rfAngleBound, bAllowUnsharpen, nMaxRecursionDepth);
+ }
+ }
+ }
+
+ if(!nMaxRecursionDepth)
+ {
+ rTarget.append(rfPB);
+ }
+ }
+
+ void ImpSubDivDistance(
+ const B2DPoint& rfPA, // start point
+ const B2DPoint& rfEA, // edge on A
+ const B2DPoint& rfEB, // edge on B
+ const B2DPoint& rfPB, // end point
+ B2DPolygon& rTarget, // target polygon
+ double fDistanceBound2, // quadratic distance criteria
+ double fLastDistanceError2, // the last quadratic distance error
+ sal_uInt16 nMaxRecursionDepth) // endless loop protection
+ {
+ if(nMaxRecursionDepth)
+ {
+ // decide if another recursion is needed. If not, set
+ // nMaxRecursionDepth to zero
+
+ // Perform bezier flatness test (lecture notes from R. Schaback,
+ // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
+ //
+ // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
+ // 0<=j<=n
+ //
+ // What is calculated here is an upper bound to the distance from
+ // a line through b_0 and b_3 (rfPA and P4 in our notation) and the
+ // curve. We can drop 0 and n from the running indices, since the
+ // argument of max becomes zero for those cases.
+ const double fJ1x(rfEA.getX() - rfPA.getX() - 1.0/3.0*(rfPB.getX() - rfPA.getX()));
+ const double fJ1y(rfEA.getY() - rfPA.getY() - 1.0/3.0*(rfPB.getY() - rfPA.getY()));
+ const double fJ2x(rfEB.getX() - rfPA.getX() - 2.0/3.0*(rfPB.getX() - rfPA.getX()));
+ const double fJ2y(rfEB.getY() - rfPA.getY() - 2.0/3.0*(rfPB.getY() - rfPA.getY()));
+ const double fDistanceError2(::std::max(fJ1x*fJ1x + fJ1y*fJ1y, fJ2x*fJ2x + fJ2y*fJ2y));
+
+ // stop if error measure does not improve anymore. This is a
+ // safety guard against floating point inaccuracies.
+ // stop if distance from line is guaranteed to be bounded by d
+ const bool bFurtherDivision(fLastDistanceError2 > fDistanceError2 && fDistanceError2 >= fDistanceBound2);
+
+ if(bFurtherDivision)
+ {
+ // remember last error value
+ fLastDistanceError2 = fDistanceError2;
+ }
+ else
+ {
+ // stop recustion
+ nMaxRecursionDepth = 0;
+ }
+ }
+
+ if(nMaxRecursionDepth)
+ {
+ // divide at 0.5
+ const B2DPoint aS1L(average(rfPA, rfEA));
+ const B2DPoint aS1C(average(rfEA, rfEB));
+ const B2DPoint aS1R(average(rfEB, rfPB));
+ const B2DPoint aS2L(average(aS1L, aS1C));
+ const B2DPoint aS2R(average(aS1C, aS1R));
+ const B2DPoint aS3C(average(aS2L, aS2R));
+
+ // left recursion
+ ImpSubDivDistance(rfPA, aS1L, aS2L, aS3C, rTarget, fDistanceBound2, fLastDistanceError2, nMaxRecursionDepth - 1);
+
+ // right recursion
+ ImpSubDivDistance(aS3C, aS2R, aS1R, rfPB, rTarget, fDistanceBound2, fLastDistanceError2, nMaxRecursionDepth - 1);
+ }
+ else
+ {
+ rTarget.append(rfPB);
+ }
+ }
+ } // end of anonymous namespace
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ B2DCubicBezier::B2DCubicBezier(const B2DCubicBezier& rBezier)
+ : maStartPoint(rBezier.maStartPoint),
+ maEndPoint(rBezier.maEndPoint),
+ maControlPointA(rBezier.maControlPointA),
+ maControlPointB(rBezier.maControlPointB)
+ {
+ }
+
+ B2DCubicBezier::B2DCubicBezier()
+ {
+ }
+
+ B2DCubicBezier::B2DCubicBezier(const B2DPoint& rStart, const B2DPoint& rEnd)
+ : maStartPoint(rStart),
+ maEndPoint(rEnd),
+ maControlPointA(rStart),
+ maControlPointB(rEnd)
+ {
+ }
+
+ B2DCubicBezier::B2DCubicBezier(const B2DPoint& rStart, const B2DPoint& rControlPointA, const B2DPoint& rControlPointB, const B2DPoint& rEnd)
+ : maStartPoint(rStart),
+ maEndPoint(rEnd),
+ maControlPointA(rControlPointA),
+ maControlPointB(rControlPointB)
+ {
+ }
+
+ B2DCubicBezier::~B2DCubicBezier()
+ {
+ }
+
+ // assignment operator
+ B2DCubicBezier& B2DCubicBezier::operator=(const B2DCubicBezier& rBezier)
+ {
+ maStartPoint = rBezier.maStartPoint;
+ maEndPoint = rBezier.maEndPoint;
+ maControlPointA = rBezier.maControlPointA;
+ maControlPointB = rBezier.maControlPointB;
+
+ return *this;
+ }
+
+ // compare operators
+ bool B2DCubicBezier::operator==(const B2DCubicBezier& rBezier) const
+ {
+ return (
+ maStartPoint == rBezier.maStartPoint
+ && maEndPoint == rBezier.maEndPoint
+ && maControlPointA == rBezier.maControlPointA
+ && maControlPointB == rBezier.maControlPointB
+ );
+ }
+
+ bool B2DCubicBezier::operator!=(const B2DCubicBezier& rBezier) const
+ {
+ return (
+ maStartPoint != rBezier.maStartPoint
+ || maEndPoint != rBezier.maEndPoint
+ || maControlPointA != rBezier.maControlPointA
+ || maControlPointB != rBezier.maControlPointB
+ );
+ }
+
+ bool B2DCubicBezier::equal(const B2DCubicBezier& rBezier) const
+ {
+ return (
+ maStartPoint.equal(rBezier.maStartPoint)
+ && maEndPoint.equal(rBezier.maEndPoint)
+ && maControlPointA.equal(rBezier.maControlPointA)
+ && maControlPointB.equal(rBezier.maControlPointB)
+ );
+ }
+
+ // test if vectors are used
+ bool B2DCubicBezier::isBezier() const
+ {
+ if(maControlPointA != maStartPoint || maControlPointB != maEndPoint)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ void B2DCubicBezier::testAndSolveTrivialBezier()
+ {
+ if(maControlPointA != maStartPoint || maControlPointB != maEndPoint)
+ {
+ const B2DVector aEdge(maEndPoint - maStartPoint);
+
+ // controls parallel to edge can be trivial. No edge -> not parallel -> control can
+ // still not be trivial (e.g. ballon loop)
+ if(!aEdge.equalZero())
+ {
+ // get control vectors
+ const B2DVector aVecA(maControlPointA - maStartPoint);
+ const B2DVector aVecB(maControlPointB - maEndPoint);
+
+ // check if trivial per se
+ bool bAIsTrivial(aVecA.equalZero());
+ bool bBIsTrivial(aVecB.equalZero());
+
+ // #i102241# prepare inverse edge length to normalize cross values;
+ // else the small compare value used in fTools::equalZero
+ // will be length dependent and this detection will work as less
+ // precise as longer the edge is. In principle, the length of the control
+ // vector would need to be used too, but to be trivial it is assumed to
+ // be of roughly equal length to the edge, so edge length can be used
+ // for both. Only needed when one of both is not trivial per se.
+ const double fInverseEdgeLength(bAIsTrivial && bBIsTrivial
+ ? 1.0
+ : 1.0 / aEdge.getLength());
+
+ // if A is not zero, check if it could be
+ if(!bAIsTrivial)
+ {
+ // #i102241# parallel to edge? Check aVecA, aEdge. Use cross() which does what
+ // we need here with the precision we need
+ const double fCross(aVecA.cross(aEdge) * fInverseEdgeLength);
+
+ if(fTools::equalZero(fCross))
+ {
+ // get scale to edge. Use bigger distance for numeric quality
+ const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY())
+ ? aVecA.getX() / aEdge.getX()
+ : aVecA.getY() / aEdge.getY());
+
+ // relative end point of vector in edge range?
+ if(fTools::moreOrEqual(fScale, 0.0) && fTools::lessOrEqual(fScale, 1.0))
+ {
+ bAIsTrivial = true;
+ }
+ }
+ }
+
+ // if B is not zero, check if it could be, but only if A is already trivial;
+ // else solve to trivial will not be possible for whole edge
+ if(bAIsTrivial && !bBIsTrivial)
+ {
+ // parallel to edge? Check aVecB, aEdge
+ const double fCross(aVecB.cross(aEdge) * fInverseEdgeLength);
+
+ if(fTools::equalZero(fCross))
+ {
+ // get scale to edge. Use bigger distance for numeric quality
+ const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY())
+ ? aVecB.getX() / aEdge.getX()
+ : aVecB.getY() / aEdge.getY());
+
+ // end point of vector in edge range? Caution: controlB is directed AGAINST edge
+ if(fTools::lessOrEqual(fScale, 0.0) && fTools::moreOrEqual(fScale, -1.0))
+ {
+ bBIsTrivial = true;
+ }
+ }
+ }
+
+ // if both are/can be reduced, do it.
+ // Not possible if only one is/can be reduced (!)
+ if(bAIsTrivial && bBIsTrivial)
+ {
+ maControlPointA = maStartPoint;
+ maControlPointB = maEndPoint;
+ }
+ }
+ }
+ }
+
+ namespace {
+ double impGetLength(const B2DCubicBezier& rEdge, double fDeviation, sal_uInt32 nRecursionWatch)
+ {
+ const double fEdgeLength(rEdge.getEdgeLength());
+ const double fControlPolygonLength(rEdge.getControlPolygonLength());
+ const double fCurrentDeviation(fTools::equalZero(fControlPolygonLength) ? 0.0 : 1.0 - (fEdgeLength / fControlPolygonLength));
+
+ if(!nRecursionWatch || fTools:: lessOrEqual(fCurrentDeviation, fDeviation))
+ {
+ return (fEdgeLength + fControlPolygonLength) * 0.5;
+ }
+ else
+ {
+ B2DCubicBezier aLeft, aRight;
+ const double fNewDeviation(fDeviation * 0.5);
+ const sal_uInt32 nNewRecursionWatch(nRecursionWatch - 1);
+
+ rEdge.split(0.5, &aLeft, &aRight);
+
+ return impGetLength(aLeft, fNewDeviation, nNewRecursionWatch)
+ + impGetLength(aRight, fNewDeviation, nNewRecursionWatch);
+ }
+ }
+ }
+
+ double B2DCubicBezier::getLength(double fDeviation) const
+ {
+ if(isBezier())
+ {
+ if(fDeviation < 0.00000001)
+ {
+ fDeviation = 0.00000001;
+ }
+
+ return impGetLength(*this, fDeviation, 6);
+ }
+ else
+ {
+ return B2DVector(getEndPoint() - getStartPoint()).getLength();
+ }
+ }
+
+ double B2DCubicBezier::getEdgeLength() const
+ {
+ const B2DVector aEdge(maEndPoint - maStartPoint);
+ return aEdge.getLength();
+ }
+
+ double B2DCubicBezier::getControlPolygonLength() const
+ {
+ const B2DVector aVectorA(maControlPointA - maStartPoint);
+ const B2DVector aVectorB(maEndPoint - maControlPointB);
+
+ if(!aVectorA.equalZero() || !aVectorB.equalZero())
+ {
+ const B2DVector aTop(maControlPointB - maControlPointA);
+ return (aVectorA.getLength() + aVectorB.getLength() + aTop.getLength());
+ }
+ else
+ {
+ return getEdgeLength();
+ }
+ }
+
+ void B2DCubicBezier::adaptiveSubdivideByAngle(B2DPolygon& rTarget, double fAngleBound, bool bAllowUnsharpen) const
+ {
+ if(isBezier())
+ {
+ // use support method #i37443# and allow unsharpen the criteria
+ ImpSubDivAngleStart(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget, fAngleBound * F_PI180, bAllowUnsharpen);
+ }
+ else
+ {
+ rTarget.append(getEndPoint());
+ }
+ }
+
+ B2DVector B2DCubicBezier::getTangent(double t) const
+ {
+ if(fTools::lessOrEqual(t, 0.0))
+ {
+ // tangent in start point
+ B2DVector aTangent(getControlPointA() - getStartPoint());
+
+ if(!aTangent.equalZero())
+ {
+ return aTangent;
+ }
+
+ // start point and control vector are the same, fallback
+ // to implicit start vector to control point B
+ aTangent = (getControlPointB() - getStartPoint()) * 0.3;
+
+ if(!aTangent.equalZero())
+ {
+ return aTangent;
+ }
+
+ // not a bezier at all, return edge vector
+ return (getEndPoint() - getStartPoint()) * 0.3;
+ }
+ else if(fTools::moreOrEqual(t, 1.0))
+ {
+ // tangent in end point
+ B2DVector aTangent(getEndPoint() - getControlPointB());
+
+ if(!aTangent.equalZero())
+ {
+ return aTangent;
+ }
+
+ // end point and control vector are the same, fallback
+ // to implicit start vector from control point A
+ aTangent = (getEndPoint() - getControlPointA()) * 0.3;
+
+ if(!aTangent.equalZero())
+ {
+ return aTangent;
+ }
+
+ // not a bezier at all, return edge vector
+ return (getEndPoint() - getStartPoint()) * 0.3;
+ }
+ else
+ {
+ // t is in ]0.0 .. 1.0[. Split and extract
+ B2DCubicBezier aRight;
+ split(t, 0, &aRight);
+
+ return aRight.getControlPointA() - aRight.getStartPoint();
+ }
+ }
+
+ // #i37443# adaptive subdivide by nCount subdivisions
+ void B2DCubicBezier::adaptiveSubdivideByCount(B2DPolygon& rTarget, sal_uInt32 nCount) const
+ {
+ const double fLenFact(1.0 / static_cast< double >(nCount + 1));
+
+ for(sal_uInt32 a(1); a <= nCount; a++)
+ {
+ const double fPos(static_cast< double >(a) * fLenFact);
+ rTarget.append(interpolatePoint(fPos));
+ }
+
+ rTarget.append(getEndPoint());
+ }
+
+ // adaptive subdivide by distance
+ void B2DCubicBezier::adaptiveSubdivideByDistance(B2DPolygon& rTarget, double fDistanceBound) const
+ {
+ if(isBezier())
+ {
+ ImpSubDivDistance(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget,
+ fDistanceBound * fDistanceBound, ::std::numeric_limits<double>::max(), 30);
+ }
+ else
+ {
+ rTarget.append(getEndPoint());
+ }
+ }
+
+ B2DPoint B2DCubicBezier::interpolatePoint(double t) const
+ {
+ OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::interpolatePoint: Access out of range (!)");
+
+ if(isBezier())
+ {
+ const B2DPoint aS1L(interpolate(maStartPoint, maControlPointA, t));
+ const B2DPoint aS1C(interpolate(maControlPointA, maControlPointB, t));
+ const B2DPoint aS1R(interpolate(maControlPointB, maEndPoint, t));
+ const B2DPoint aS2L(interpolate(aS1L, aS1C, t));
+ const B2DPoint aS2R(interpolate(aS1C, aS1R, t));
+
+ return interpolate(aS2L, aS2R, t);
+ }
+ else
+ {
+ return interpolate(maStartPoint, maEndPoint, t);
+ }
+ }
+
+ double B2DCubicBezier::getSmallestDistancePointToBezierSegment(const B2DPoint& rTestPoint, double& rCut) const
+ {
+ const sal_uInt32 nInitialDivisions(3L);
+ B2DPolygon aInitialPolygon;
+
+ // as start make a fix division, creates nInitialDivisions + 2L points
+ aInitialPolygon.append(getStartPoint());
+ adaptiveSubdivideByCount(aInitialPolygon, nInitialDivisions);
+
+ // now look for the closest point
+ const sal_uInt32 nPointCount(aInitialPolygon.count());
+ B2DVector aVector(rTestPoint - aInitialPolygon.getB2DPoint(0L));
+ double fQuadDist(aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY());
+ double fNewQuadDist;
+ sal_uInt32 nSmallestIndex(0L);
+
+ for(sal_uInt32 a(1L); a < nPointCount; a++)
+ {
+ aVector = B2DVector(rTestPoint - aInitialPolygon.getB2DPoint(a));
+ fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
+
+ if(fNewQuadDist < fQuadDist)
+ {
+ fQuadDist = fNewQuadDist;
+ nSmallestIndex = a;
+ }
+ }
+
+ // look right and left for even smaller distances
+ double fStepValue(1.0 / (double)((nPointCount - 1L) * 2L)); // half the edge step width
+ double fPosition((double)nSmallestIndex / (double)(nPointCount - 1L));
+ bool bDone(false);
+
+ while(!bDone)
+ {
+ if(!bDone)
+ {
+ // test left
+ double fPosLeft(fPosition - fStepValue);
+
+ if(fPosLeft < 0.0)
+ {
+ fPosLeft = 0.0;
+ aVector = B2DVector(rTestPoint - maStartPoint);
+ }
+ else
+ {
+ aVector = B2DVector(rTestPoint - interpolatePoint(fPosLeft));
+ }
+
+ fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
+
+ if(fTools::less(fNewQuadDist, fQuadDist))
+ {
+ fQuadDist = fNewQuadDist;
+ fPosition = fPosLeft;
+ }
+ else
+ {
+ // test right
+ double fPosRight(fPosition + fStepValue);
+
+ if(fPosRight > 1.0)
+ {
+ fPosRight = 1.0;
+ aVector = B2DVector(rTestPoint - maEndPoint);
+ }
+ else
+ {
+ aVector = B2DVector(rTestPoint - interpolatePoint(fPosRight));
+ }
+
+ fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
+
+ if(fTools::less(fNewQuadDist, fQuadDist))
+ {
+ fQuadDist = fNewQuadDist;
+ fPosition = fPosRight;
+ }
+ else
+ {
+ // not less left or right, done
+ bDone = true;
+ }
+ }
+ }
+
+ if(0.0 == fPosition || 1.0 == fPosition)
+ {
+ // if we are completely left or right, we are done
+ bDone = true;
+ }
+
+ if(!bDone)
+ {
+ // prepare next step value
+ fStepValue /= 2.0;
+ }
+ }
+
+ rCut = fPosition;
+ return sqrt(fQuadDist);
+ }
+
+ void B2DCubicBezier::split(double t, B2DCubicBezier* pBezierA, B2DCubicBezier* pBezierB) const
+ {
+ OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::split: Access out of range (!)");
+
+ if(!pBezierA && !pBezierB)
+ {
+ return;
+ }
+
+ if(isBezier())
+ {
+ const B2DPoint aS1L(interpolate(maStartPoint, maControlPointA, t));
+ const B2DPoint aS1C(interpolate(maControlPointA, maControlPointB, t));
+ const B2DPoint aS1R(interpolate(maControlPointB, maEndPoint, t));
+ const B2DPoint aS2L(interpolate(aS1L, aS1C, t));
+ const B2DPoint aS2R(interpolate(aS1C, aS1R, t));
+ const B2DPoint aS3C(interpolate(aS2L, aS2R, t));
+
+ if(pBezierA)
+ {
+ pBezierA->setStartPoint(maStartPoint);
+ pBezierA->setEndPoint(aS3C);
+ pBezierA->setControlPointA(aS1L);
+ pBezierA->setControlPointB(aS2L);
+ }
+
+ if(pBezierB)
+ {
+ pBezierB->setStartPoint(aS3C);
+ pBezierB->setEndPoint(maEndPoint);
+ pBezierB->setControlPointA(aS2R);
+ pBezierB->setControlPointB(aS1R);
+ }
+ }
+ else
+ {
+ const B2DPoint aSplit(interpolate(maStartPoint, maEndPoint, t));
+
+ if(pBezierA)
+ {
+ pBezierA->setStartPoint(maStartPoint);
+ pBezierA->setEndPoint(aSplit);
+ pBezierA->setControlPointA(maStartPoint);
+ pBezierA->setControlPointB(aSplit);
+ }
+
+ if(pBezierB)
+ {
+ pBezierB->setStartPoint(aSplit);
+ pBezierB->setEndPoint(maEndPoint);
+ pBezierB->setControlPointA(aSplit);
+ pBezierB->setControlPointB(maEndPoint);
+ }
+ }
+ }
+
+ B2DCubicBezier B2DCubicBezier::snippet(double fStart, double fEnd) const
+ {
+ B2DCubicBezier aRetval;
+
+ if(fTools::more(fStart, 1.0))
+ {
+ fStart = 1.0;
+ }
+ else if(fTools::less(fStart, 0.0))
+ {
+ fStart = 0.0;
+ }
+
+ if(fTools::more(fEnd, 1.0))
+ {
+ fEnd = 1.0;
+ }
+ else if(fTools::less(fEnd, 0.0))
+ {
+ fEnd = 0.0;
+ }
+
+ if(fEnd <= fStart)
+ {
+ // empty or NULL, create single point at center
+ const double fSplit((fEnd + fStart) * 0.5);
+ const B2DPoint aPoint(interpolate(getStartPoint(), getEndPoint(), fSplit));
+ aRetval.setStartPoint(aPoint);
+ aRetval.setEndPoint(aPoint);
+ aRetval.setControlPointA(aPoint);
+ aRetval.setControlPointB(aPoint);
+ }
+ else
+ {
+ if(isBezier())
+ {
+ // copy bezier; cut off right, then cut off left. Do not forget to
+ // adapt cut value when both cuts happen
+ const bool bEndIsOne(fTools::equal(fEnd, 1.0));
+ const bool bStartIsZero(fTools::equalZero(fStart));
+ aRetval = *this;
+
+ if(!bEndIsOne)
+ {
+ aRetval.split(fEnd, &aRetval, 0);
+
+ if(!bStartIsZero)
+ {
+ fStart /= fEnd;
+ }
+ }
+
+ if(!bStartIsZero)
+ {
+ aRetval.split(fStart, 0, &aRetval);
+ }
+ }
+ else
+ {
+ // no bezier, create simple edge
+ const B2DPoint aPointA(interpolate(getStartPoint(), getEndPoint(), fStart));
+ const B2DPoint aPointB(interpolate(getStartPoint(), getEndPoint(), fEnd));
+ aRetval.setStartPoint(aPointA);
+ aRetval.setEndPoint(aPointB);
+ aRetval.setControlPointA(aPointA);
+ aRetval.setControlPointB(aPointB);
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DRange B2DCubicBezier::getRange() const
+ {
+ B2DRange aRetval(maStartPoint, maEndPoint);
+
+ aRetval.expand(maControlPointA);
+ aRetval.expand(maControlPointB);
+
+ return aRetval;
+ }
+
+ bool B2DCubicBezier::getMinimumExtremumPosition(double& rfResult) const
+ {
+ ::std::vector< double > aAllResults;
+
+ aAllResults.reserve(4);
+ getAllExtremumPositions(aAllResults);
+
+ const sal_uInt32 nCount(aAllResults.size());
+
+ if(!nCount)
+ {
+ return false;
+ }
+ else if(1 == nCount)
+ {
+ rfResult = aAllResults[0];
+ return true;
+ }
+ else
+ {
+ rfResult = *(::std::min_element(aAllResults.begin(), aAllResults.end()));
+ return true;
+ }
+ }
+
+ namespace
+ {
+ inline void impCheckExtremumResult(double fCandidate, ::std::vector< double >& rResult)
+ {
+ // check for range ]0.0 .. 1.0[ with excluding 1.0 and 0.0 clearly
+ // by using the equalZero test, NOT ::more or ::less which will use the
+ // ApproxEqual() which is too exact here
+ if(fCandidate > 0.0 && !fTools::equalZero(fCandidate))
+ {
+ if(fCandidate < 1.0 && !fTools::equalZero(fCandidate - 1.0))
+ {
+ rResult.push_back(fCandidate);
+ }
+ }
+ }
+ }
+
+ void B2DCubicBezier::getAllExtremumPositions(::std::vector< double >& rResults) const
+ {
+ rResults.clear();
+
+ // calculate the x-extrema parameters by zeroing first x-derivative
+ // of the cubic bezier's parametric formula, which results in a
+ // quadratic equation: dBezier/dt = t*t*fAX - 2*t*fBX + fCX
+ const B2DPoint aRelativeEndPoint(maEndPoint-maStartPoint);
+ const double fAX = 3 * (maControlPointA.getX() - maControlPointB.getX()) + aRelativeEndPoint.getX();
+ const double fBX = 2 * maControlPointA.getX() - maControlPointB.getX() - maStartPoint.getX();
+ double fCX(maControlPointA.getX() - maStartPoint.getX());
+
+ if(fTools::equalZero(fCX))
+ {
+ // detect fCX equal zero and truncate to real zero value in that case
+ fCX = 0.0;
+ }
+
+ if( !fTools::equalZero(fAX) )
+ {
+ // derivative is polynomial of order 2 => use binomial formula
+ const double fD = fBX*fBX - fAX*fCX;
+ if( fD >= 0.0 )
+ {
+ const double fS = sqrt(fD);
+ // same as above but for very small fAX and/or fCX
+ // this has much better numerical stability
+ // see NRC chapter 5-6 (thanks THB!)
+ const double fQ = fBX + ((fBX >= 0) ? +fS : -fS);
+ impCheckExtremumResult(fQ / fAX, rResults);
+ impCheckExtremumResult(fCX / fQ, rResults);
+ }
+ }
+ else if( !fTools::equalZero(fBX) )
+ {
+ // derivative is polynomial of order 1 => one extrema
+ impCheckExtremumResult(fCX / (2 * fBX), rResults);
+ }
+
+ // calculate the y-extrema parameters by zeroing first y-derivative
+ const double fAY = 3 * (maControlPointA.getY() - maControlPointB.getY()) + aRelativeEndPoint.getY();
+ const double fBY = 2 * maControlPointA.getY() - maControlPointB.getY() - maStartPoint.getY();
+ double fCY(maControlPointA.getY() - maStartPoint.getY());
+
+ if(fTools::equalZero(fCY))
+ {
+ // detect fCY equal zero and truncate to real zero value in that case
+ fCY = 0.0;
+ }
+
+ if( !fTools::equalZero(fAY) )
+ {
+ // derivative is polynomial of order 2 => use binomial formula
+ const double fD = fBY*fBY - fAY*fCY;
+ if( fD >= 0 )
+ {
+ const double fS = sqrt(fD);
+ // same as above but for very small fAX and/or fCX
+ // this has much better numerical stability
+ // see NRC chapter 5-6 (thanks THB!)
+ const double fQ = fBY + ((fBY >= 0) ? +fS : -fS);
+ impCheckExtremumResult(fQ / fAY, rResults);
+ impCheckExtremumResult(fCY / fQ, rResults);
+ }
+ }
+ else if( !fTools::equalZero(fBY) )
+ {
+ // derivative is polynomial of order 1 => one extrema
+ impCheckExtremumResult(fCY / (2 * fBY), rResults);
+ }
+ }
+
+ int B2DCubicBezier::getMaxDistancePositions( double pResult[2]) const
+ {
+ // the distance from the bezier to a line through start and end
+ // is proportional to (ENDx-STARTx,ENDy-STARTy)*(+BEZIERy(t),-BEZIERx(t))
+ // this distance becomes zero for at least t==0 and t==1
+ // its extrema that are between 0..1 are interesting as split candidates
+ // its derived function has the form dD/dt = fA*t^2 + 2*fB*t + fC
+ const B2DPoint aRelativeEndPoint(maEndPoint-maStartPoint);
+ const double fA = 3 * (maEndPoint.getX() - maControlPointB.getX()) * aRelativeEndPoint.getY()
+ - 3 * (maEndPoint.getY() - maControlPointB.getY()) * aRelativeEndPoint.getX();
+ const double fB = (maControlPointB.getX() - maControlPointA.getX()) * aRelativeEndPoint.getY()
+ - (maControlPointB.getY() - maControlPointA.getY()) * aRelativeEndPoint.getX();
+ const double fC = (maControlPointA.getX() - maStartPoint.getX()) * aRelativeEndPoint.getY()
+ - (maControlPointA.getY() - maStartPoint.getY()) * aRelativeEndPoint.getX();
+
+ // test for degenerated case: non-cubic curve
+ if( fTools::equalZero(fA) )
+ {
+ // test for degenerated case: straight line
+ if( fTools::equalZero(fB) )
+ return 0;
+
+ // degenerated case: quadratic bezier
+ pResult[0] = -fC / (2*fB);
+
+ // test root: ignore it when it is outside the curve
+ int nCount = ((pResult[0] > 0) && (pResult[0] < 1));
+ return nCount;
+ }
+
+ // derivative is polynomial of order 2
+ // check if the polynomial has non-imaginary roots
+ const double fD = fB*fB - fA*fC;
+ if( fD >= 0.0 ) // TODO: is this test needed? geometrically not IMHO
+ {
+ // calculate the first root
+ const double fS = sqrt(fD);
+ const double fQ = fB + ((fB >= 0) ? +fS : -fS);
+ pResult[0] = fQ / fA;
+ // test root: ignore it when it is outside the curve
+ int nCount = ((pResult[0] > 0) && (pResult[0] < 1));
+
+ // ignore multiplicit roots
+ if( !fTools::equalZero(fD) )
+ {
+ // calculate the second root
+ const double fRoot = fC / fQ;
+ pResult[ nCount ] = fC / fQ;
+ // test root: ignore it when it is outside the curve
+ nCount += ((fRoot > 0) && (fRoot < 1));
+ }
+
+ return nCount;
+ }
+
+ return 0;
+ }
+
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/curve/b2dquadraticbezier.cxx b/basegfx/source/curve/b2dquadraticbezier.cxx
new file mode 100644
index 000000000000..9ae3a3d726a1
--- /dev/null
+++ b/basegfx/source/curve/b2dquadraticbezier.cxx
@@ -0,0 +1,108 @@
+/*************************************************************************
+ *
+ * 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: b2dquadraticbezier.cxx,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/curve/b2dquadraticbezier.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ B2DQuadraticBezier::B2DQuadraticBezier(const B2DQuadraticBezier& rBezier)
+ : maStartPoint(rBezier.maStartPoint),
+ maEndPoint(rBezier.maEndPoint),
+ maControlPoint(rBezier.maControlPoint)
+ {
+ }
+
+ B2DQuadraticBezier::B2DQuadraticBezier()
+ {
+ }
+
+ B2DQuadraticBezier::B2DQuadraticBezier(const ::basegfx::B2DPoint& rStart, const ::basegfx::B2DPoint& rEnd)
+ : maStartPoint(rStart),
+ maEndPoint(rEnd)
+ {
+ }
+
+ B2DQuadraticBezier::B2DQuadraticBezier(const ::basegfx::B2DPoint& rStart, const ::basegfx::B2DPoint& rControl, const ::basegfx::B2DPoint& rEnd)
+ : maStartPoint(rStart),
+ maEndPoint(rEnd),
+ maControlPoint(rControl)
+ {
+ }
+
+ B2DQuadraticBezier::~B2DQuadraticBezier()
+ {
+ }
+
+ // assignment operator
+ B2DQuadraticBezier& B2DQuadraticBezier::operator=(const B2DQuadraticBezier& rBezier)
+ {
+ maStartPoint = rBezier.maStartPoint;
+ maEndPoint = rBezier.maEndPoint;
+ maControlPoint = rBezier.maControlPoint;
+
+ return *this;
+ }
+
+ // compare operators
+ bool B2DQuadraticBezier::operator==(const B2DQuadraticBezier& rBezier) const
+ {
+ return (
+ maStartPoint == rBezier.maStartPoint
+ && maEndPoint == rBezier.maEndPoint
+ && maControlPoint == rBezier.maControlPoint
+ );
+ }
+
+ bool B2DQuadraticBezier::operator!=(const B2DQuadraticBezier& rBezier) const
+ {
+ return (
+ maStartPoint != rBezier.maStartPoint
+ || maEndPoint != rBezier.maEndPoint
+ || maControlPoint != rBezier.maControlPoint
+ );
+ }
+
+ // test if control vector is used
+ bool B2DQuadraticBezier::isBezier() const
+ {
+ // if control vector is empty, bezier is not used
+ if(maControlPoint == maStartPoint || maControlPoint == maEndPoint)
+ return false;
+
+ return true;
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/curve/makefile.mk b/basegfx/source/curve/makefile.mk
new file mode 100644
index 000000000000..0cef45ad4893
--- /dev/null
+++ b/basegfx/source/curve/makefile.mk
@@ -0,0 +1,53 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.5 $
+#
+# 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=curve
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+ENABLE_EXCEPTIONS=FALSE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/b2dcubicbezier.obj \
+ $(SLO)$/b2dquadraticbezier.obj \
+ $(SLO)$/b2dbeziertools.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/inc/PolygonPoint.hxx b/basegfx/source/inc/PolygonPoint.hxx
new file mode 100644
index 000000000000..0f971c3065bd
--- /dev/null
+++ b/basegfx/source/inc/PolygonPoint.hxx
@@ -0,0 +1,541 @@
+/*************************************************************************
+ *
+ * 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: PolygonPoint.hxx,v $
+ * $Revision: 1.9 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _POLYGON_POINT_HXX
+#define _POLYGON_POINT_HXX
+
+#include <vector>
+
+//////////////////////////////////////////////////////////////////////////////
+
+template < class Point > class SimplePointEntry
+{
+ Point maPoint;
+
+public:
+ SimplePointEntry()
+ : maPoint(Point::getEmptyPoint())
+ {
+ }
+
+ SimplePointEntry(const Point& rInitPoint)
+ : maPoint(rInitPoint)
+ {
+ }
+
+ const Point& getPoint() const
+ {
+ return maPoint;
+ }
+
+ void setPoint(const Point& rValue)
+ {
+ maPoint = rValue;
+ }
+
+ bool operator==(const SimplePointEntry& rEntry) const
+ {
+ return (maPoint == rEntry.maPoint);
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+template < class Vector > class SimpleBezierEntry
+{
+ Vector maBackward;
+ Vector maForward;
+
+public:
+ SimpleBezierEntry()
+ : maBackward(Vector::getEmptyVector()),
+ maForward(Vector::getEmptyVector())
+ {
+ }
+
+ SimpleBezierEntry(const Vector& rInitBackward, const Vector& rInitForward)
+ : maBackward(rInitBackward),
+ maForward(rInitForward)
+ {
+ }
+
+ const Vector& getBackwardVector() const
+ {
+ return maBackward;
+ }
+
+ void setBackwardVector(const Vector& rValue)
+ {
+ maBackward = rValue;
+ }
+
+ const Vector& getForwardVector() const
+ {
+ return maForward;
+ }
+
+ void setForwardVector(const Vector& rValue)
+ {
+ maForward = rValue;
+ }
+
+ bool isBezierNeeded()
+ {
+ if(maBackward != Vector::getEmptyVector() || maForward != Vector::getEmptyVector())
+ return true;
+ return false;
+ }
+
+ bool operator==(const SimpleBezierEntry& rEntry) const
+ {
+ return ((maBackward == rEntry.maBackward) && (maForward == rEntry.maForward));
+ }
+
+ void doInvertForFlip()
+ {
+ maBackward = -maBackward;
+ maForward = -maForward;
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+template < class Point, class Vector > class PolygonPointList
+{
+ typedef SimplePointEntry< Point > LocalSimplePointEntry;
+ typedef SimpleBezierEntry< Vector > LocalSimpleBezierEntry;
+ typedef ::std::vector< LocalSimplePointEntry > SimplePointVector;
+ typedef ::std::vector< LocalSimpleBezierEntry > SimpleBezierVector;
+
+ sal_uInt32 mnBezierCount;
+ SimplePointVector maPoints;
+ SimpleBezierVector* mpVectors;
+
+ unsigned mbIsClosed : 1;
+
+ void implTryToReduceToPointVector()
+ {
+ if(!mnBezierCount && mpVectors)
+ {
+ delete mpVectors;
+ mpVectors = 0L;
+ }
+ }
+
+public:
+ bool isBezier() const
+ {
+ return bool(mnBezierCount);
+ }
+
+ bool isClosed() const
+ {
+ return bool(mbIsClosed);
+ }
+
+ void setClosed(bool bNew)
+ {
+ mbIsClosed = bNew;
+ }
+
+ sal_uInt32 count() const
+ {
+ return maPoints.size();
+ }
+
+ PolygonPointList()
+ : mnBezierCount(0L),
+ mpVectors(0L),
+ mbIsClosed(false)
+ {
+ // complete initialization with defaults
+ }
+
+ PolygonPointList(const PolygonPointList& rSource)
+ : mnBezierCount(0L),
+ maPoints(rSource.maPoints),
+ mpVectors(0L),
+ mbIsClosed(rSource.mbIsClosed)
+ {
+ // complete initialization using copy
+ if(rSource.mpVectors && rSource.mnBezierCount)
+ {
+ mpVectors = new SimpleBezierVector(*rSource.mpVectors);
+ mnBezierCount = rSource.mnBezierCount;
+ }
+ }
+
+ PolygonPointList(const PolygonPointList& rSource, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : mnBezierCount(0L),
+ maPoints(nCount),
+ mpVectors(0L),
+ mbIsClosed(rSource.mbIsClosed)
+ {
+ // complete initialization using partly copy
+ if(nCount)
+ {
+ // copy point data
+ {
+ SimplePointVector::const_iterator aStart(rSource.maPoints.begin());
+ aStart += nIndex;
+ SimplePointVector::const_iterator aEnd(aStart);
+ aEnd += nCount;
+ maPoints.insert(0L, aStart, aEnd);
+ }
+
+ // copy bezier data
+ if(rSource.mpVectors && rSource.mnBezierCount)
+ {
+ mpVectors = new SimpleBezierVector();
+ mpVectors->reserve(nCount);
+
+ SimpleBezierVector::iterator aStart(mpVectors->begin());
+ aStart += nIndex;
+ SimpleBezierVector::iterator aEnd(aStart);
+ aEnd += nCount;
+
+ for( ; aStart != aEnd; ++aStart )
+ {
+ if(aStart->IsBezierNeeded())
+ {
+ mnBezierCount++;
+ }
+
+ mpVectors->push_back(*aStart);
+ }
+
+ // maybe vectors are not needed anymore, try to reduce memory footprint
+ implTryToReduceToPointVector();
+ }
+ }
+ }
+
+ ~PolygonPointList()
+ {
+ if(mpVectors)
+ {
+ delete mpVectors;
+ }
+ }
+
+ bool isEqual(const PolygonPointList& rPointList) const
+ {
+ // same point count?
+ if(maPoints.size() != rPointList.maPoints.size())
+ return false;
+
+ // if zero points the polys are equal
+ if(!maPoints.size())
+ return true;
+
+ // if bezier count used it needs to be equal
+ if(mnBezierCount != rPointList.mnBezierCount)
+ return false;
+
+ // compare point content
+ if(maPoints != rPointList.maPoints)
+ return false;
+
+ // beziercounts are equal: if it's zero, we are done
+ if(!mnBezierCount)
+ return true;
+
+ // beziercounts are equal and not zero; compare them
+ OSL_ENSURE(0L != mpVectors, "Error: Bezier list needs to exist here(!)");
+ OSL_ENSURE(0L != rPointList.mpVectors, "Error: Bezier list needs to exist here(!)");
+
+ return (*mpVectors == *rPointList.mpVectors);
+ }
+
+ const Point& getPoint(sal_uInt32 nIndex) const
+ {
+ return maPoints[nIndex].getPoint();
+ }
+
+ void setPoint(sal_uInt32 nIndex, const Point& rValue)
+ {
+ maPoints[nIndex].setPoint(rValue);
+ }
+
+ const Vector& getBackwardVector(sal_uInt32 nIndex) const
+ {
+ if(mpVectors)
+ return ((*mpVectors)[nIndex]).getBackwardVector();
+ else
+ return Vector::getEmptyVector();
+ }
+
+ void setBackwardVector(sal_uInt32 nIndex, const Vector& rValue)
+ {
+ if(mpVectors)
+ {
+ LocalSimpleBezierEntry& rDest = (*mpVectors)[nIndex];
+ bool bBezierNeededBefore(rDest.isBezierNeeded());
+ ((*mpVectors)[nIndex]).setBackwardVector(rValue);
+ bool bBezierNeededAfter(rDest.isBezierNeeded());
+
+ if(bBezierNeededBefore != bBezierNeededAfter)
+ {
+ if(bBezierNeededAfter)
+ mnBezierCount++;
+ else
+ mnBezierCount--;
+ }
+ }
+ else
+ {
+ bool bEmptyVector(rValue == Vector::getEmptyVector());
+
+ if(bEmptyVector)
+ return;
+
+ mpVectors = new SimpleBezierVector(maPoints.size());
+ ((*mpVectors)[nIndex]).setBackwardVector(rValue);
+ mnBezierCount++;
+ }
+ }
+
+ const Vector& getForwardVector(sal_uInt32 nIndex) const
+ {
+ if(mpVectors)
+ return ((*mpVectors)[nIndex]).getForwardVector();
+ else
+ return Vector::getEmptyVector();
+ }
+
+ void setForwardVector(sal_uInt32 nIndex, const Vector& rValue)
+ {
+ if(mpVectors)
+ {
+ LocalSimpleBezierEntry& rDest = (*mpVectors)[nIndex];
+ bool bBezierNeededBefore(rDest.isBezierNeeded());
+ ((*mpVectors)[nIndex]).setForwardVector(rValue);
+ bool bBezierNeededAfter(rDest.isBezierNeeded());
+
+ if(bBezierNeededBefore != bBezierNeededAfter)
+ {
+ if(bBezierNeededAfter)
+ mnBezierCount++;
+ else
+ mnBezierCount--;
+ }
+ }
+ else
+ {
+ bool bEmptyVector(rValue == Vector::getEmptyVector());
+
+ if(bEmptyVector)
+ return;
+
+ mpVectors = new SimpleBezierVector(maPoints.size());
+ ((*mpVectors)[nIndex]).setForwardVector(rValue);
+ mnBezierCount++;
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const Point& rPoint, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // maybe vectors are not needed anymore, try to reduce memory footprint
+ implTryToReduceToPointVector();
+
+ // add nCount copies of rPoint
+ {
+ LocalSimplePointEntry aNode(rPoint);
+ SimplePointVector::iterator aIndex(maPoints.begin());
+ aIndex += nIndex;
+ maPoints.insert(aIndex, nCount, aNode);
+ }
+
+ // add nCount empty entries to keep indices synchronized
+ if(mpVectors)
+ {
+ LocalSimpleBezierEntry aNode;
+ SimpleBezierVector::iterator aIndex(mpVectors->begin());
+ aIndex += nIndex;
+ mpVectors->insert(aIndex, nCount, aNode);
+ }
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const PolygonPointList& rSource)
+ {
+ const sal_uInt32 nCount(rSource.maPoints.size());
+
+ if(nCount)
+ {
+ // instert point data
+ {
+ SimplePointVector::iterator aIndex(maPoints.begin());
+ aIndex += nIndex;
+
+ SimplePointVector::const_iterator aStart(rSource.maPoints.begin());
+ SimplePointVector::const_iterator aEnd(rSource.maPoints.end());
+
+ maPoints.insert(aIndex, aStart, aEnd);
+ }
+
+ // insert bezier data
+ if(rSource.mpVectors && rSource.mnBezierCount)
+ {
+ SimpleBezierVector::iterator aIndex(mpVectors->begin());
+ aIndex += nIndex;
+
+ SimpleBezierVector::iterator aStart(rSource.mpVectors->begin());
+ SimpleBezierVector::iterator aEnd(rSource.mpVectors->end());
+
+ if(!mpVectors)
+ {
+ mpVectors = new SimpleBezierVector(maPoints.size() - nCount);
+ }
+
+ mpVectors->insert(aIndex, aStart, aEnd);
+
+ mnBezierCount += rSource.mnBezierCount;
+ }
+ else
+ {
+ // maybe vectors are not needed anymore, try to reduce memory footprint
+ implTryToReduceToPointVector();
+
+ // add nCount empty entries to keep indices synchronized
+ if(mpVectors)
+ {
+ LocalSimpleBezierEntry aNode;
+ SimpleBezierVector::iterator aIndex(mpVectors->begin());
+ aIndex += nIndex;
+ mpVectors->insert(aIndex, nCount, aNode);
+ }
+ }
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // maybe vectors are not needed anymore, try to reduce memory footprint
+ implTryToReduceToPointVector();
+
+ // remove point data
+ {
+ SimplePointVector::iterator aStart(maPoints.begin());
+ aStart += nIndex;
+ const SimplePointVector::iterator aEnd(aStart + nCount);
+
+ maPoints.erase(aStart, aEnd);
+ }
+
+ // remove bezier data
+ if(mpVectors)
+ {
+ SimpleBezierVector::iterator aStart(mpVectors->begin());
+ aStart += nIndex;
+ const SimpleBezierVector::iterator aEnd(aStart + nCount);
+
+ // take care for correct mnBezierCount BEFORE erase
+ if(mnBezierCount)
+ {
+ SimpleBezierVector::iterator aTestIter(aStart);
+
+ for( ; mnBezierCount && aTestIter != aEnd; ++aTestIter)
+ {
+ if(aTestIter->isBezierNeeded())
+ mnBezierCount--;
+ }
+ }
+
+ if(mnBezierCount)
+ {
+ // erase nodes
+ mpVectors->erase(aStart, aEnd);
+ }
+ else
+ {
+ // try to reduce, maybe 0L == mnBezierCount
+ implTryToReduceToPointVector();
+ }
+ }
+ }
+ }
+
+ void flip()
+ {
+ if(maPoints.size() > 1)
+ {
+ // maybe vectors are not needed anymore, try to reduce memory footprint
+ implTryToReduceToPointVector();
+
+ // calculate half size
+ const sal_uInt32 nHalfSize(maPoints.size() >> 1L);
+
+ // flip point data
+ {
+ SimplePointVector::iterator aStart(maPoints.begin());
+ SimplePointVector::iterator aEnd(maPoints.end());
+
+ for(sal_uInt32 a(0); a < nHalfSize; a++)
+ {
+ LocalSimplePointEntry aTemp = *aStart;
+ *aStart++ = *aEnd;
+ *aEnd-- = aTemp;
+ }
+ }
+
+ // flip bezier data
+ if(mpVectors)
+ {
+ SimpleBezierVector::iterator aStart(mpVectors->begin());
+ SimpleBezierVector::iterator aEnd(mpVectors->end());
+
+ for(sal_uInt32 a(0); a < nHalfSize; a++)
+ {
+ LocalSimpleBezierEntry aTemp = *aStart;
+ aTemp.doInvertForFlip();
+ *aStart = *aEnd;
+ aStart->doInvertForFlip();
+ aStart++;
+ *aEnd-- = aTemp;
+ }
+
+ // also flip vectors of middle point (if existing)
+ if(maPoints.size() % 2)
+ {
+ (*mpVectors)[nHalfSize].doInvertForFlip();
+ }
+ }
+ }
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif _POLYGON_POINT_HXX
diff --git a/basegfx/source/inc/hommatrixtemplate.hxx b/basegfx/source/inc/hommatrixtemplate.hxx
new file mode 100644
index 000000000000..7baca88bf28a
--- /dev/null
+++ b/basegfx/source/inc/hommatrixtemplate.hxx
@@ -0,0 +1,616 @@
+/*************************************************************************
+ *
+ * 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: hommatrixtemplate.hxx,v $
+ * $Revision: 1.18.12.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _HOMMATRIX_TEMPLATE_HXX
+#define _HOMMATRIX_TEMPLATE_HXX
+
+#include <sal/types.h>
+#include <basegfx/numeric/ftools.hxx>
+#include <math.h>
+#include <string.h>
+
+namespace basegfx
+{
+ namespace internal
+ {
+
+ inline double implGetDefaultValue(sal_uInt16 nRow, sal_uInt16 nColumn)
+ {
+ if(nRow == nColumn)
+ return 1.0;
+ return 0.0;
+ }
+
+ template < unsigned int _RowSize > class ImplMatLine
+ {
+ enum { RowSize = _RowSize };
+
+ double mfValue[RowSize];
+
+ public:
+ ImplMatLine()
+ {
+ }
+
+ ImplMatLine(sal_uInt16 nRow, ImplMatLine< RowSize >* pToBeCopied = 0L)
+ {
+ if(pToBeCopied)
+ {
+ memcpy(&mfValue, pToBeCopied, sizeof(double) * RowSize);
+ }
+ else
+ {
+ for(sal_uInt16 a(0); a < RowSize; a++)
+ {
+ mfValue[a] = implGetDefaultValue(nRow, a);
+ }
+ }
+ }
+
+ double get(sal_uInt16 nColumn) const
+ {
+ return mfValue[nColumn];
+ }
+
+ void set(sal_uInt16 nColumn, const double& rValue)
+ {
+ mfValue[nColumn] = rValue;
+ }
+ };
+
+ template < unsigned int _RowSize > class ImplHomMatrixTemplate
+ {
+ enum { RowSize = _RowSize };
+
+ ImplMatLine< RowSize > maLine[RowSize - 1];
+ ImplMatLine< RowSize >* mpLine;
+
+ public:
+ // Is last line used?
+ bool isLastLineDefault() const
+ {
+ if(!mpLine)
+ return true;
+
+ for(sal_uInt16 a(0); a < RowSize; a++)
+ {
+ const double fDefault(implGetDefaultValue((RowSize - 1), a));
+ const double fLineValue(mpLine->get(a));
+
+ if(!::basegfx::fTools::equal(fDefault, fLineValue))
+ {
+ return false;
+ }
+ }
+
+ // reset last line, it equals default
+ delete ((ImplHomMatrixTemplate< RowSize >*)this)->mpLine;
+ ((ImplHomMatrixTemplate< RowSize >*)this)->mpLine = 0L;
+
+ return true;
+ }
+
+ ImplHomMatrixTemplate()
+ : mpLine(0L)
+ {
+ // complete initialization with identity matrix, all lines
+ // were initialized with a trailing 1 followed by 0's.
+ for(sal_uInt16 a(0); a < RowSize-1; a++)
+ {
+ for(sal_uInt16 b(0); b < RowSize; b++)
+ maLine[a].set(b, implGetDefaultValue(a, b) );
+ }
+ }
+
+ ImplHomMatrixTemplate(const ImplHomMatrixTemplate& rToBeCopied)
+ : mpLine(0L)
+ {
+ // complete initialization using copy
+ for(sal_uInt16 a(0); a < (RowSize - 1); a++)
+ {
+ memcpy(&maLine[a], &rToBeCopied.maLine[a], sizeof(ImplMatLine< RowSize >));
+ }
+
+ if(rToBeCopied.mpLine)
+ {
+ mpLine = new ImplMatLine< RowSize >((RowSize - 1), rToBeCopied.mpLine);
+ }
+ }
+
+ ~ImplHomMatrixTemplate()
+ {
+ if(mpLine)
+ {
+ delete mpLine;
+ }
+ }
+
+ sal_uInt16 getEdgeLength() const { return RowSize; }
+
+ double get(sal_uInt16 nRow, sal_uInt16 nColumn) const
+ {
+ if(nRow < (RowSize - 1))
+ {
+ return maLine[nRow].get(nColumn);
+ }
+
+ if(mpLine)
+ {
+ return mpLine->get(nColumn);
+ }
+
+ return implGetDefaultValue((RowSize - 1), nColumn);
+ }
+
+ void set(sal_uInt16 nRow, sal_uInt16 nColumn, const double& rValue)
+ {
+ if(nRow < (RowSize - 1))
+ {
+ maLine[nRow].set(nColumn, rValue);
+ }
+ else if(mpLine)
+ {
+ mpLine->set(nColumn, rValue);
+ }
+ else
+ {
+ const double fDefault(implGetDefaultValue((RowSize - 1), nColumn));
+
+ if(!::basegfx::fTools::equal(fDefault, rValue))
+ {
+ mpLine = new ImplMatLine< RowSize >((RowSize - 1), 0L);
+ mpLine->set(nColumn, rValue);
+ }
+ }
+ }
+
+ void testLastLine()
+ {
+ if(mpLine)
+ {
+ bool bNecessary(false);
+
+ for(sal_uInt16 a(0);!bNecessary && a < RowSize; a++)
+ {
+ const double fDefault(implGetDefaultValue((RowSize - 1), a));
+ const double fLineValue(mpLine->get(a));
+
+ if(!::basegfx::fTools::equal(fDefault, fLineValue))
+ {
+ bNecessary = true;
+ }
+ }
+
+ if(!bNecessary)
+ {
+ delete mpLine;
+ mpLine = 0L;
+ }
+ }
+ }
+
+ // Left-upper decompositon
+ bool ludcmp(sal_uInt16 nIndex[], sal_Int16& nParity)
+ {
+ double fBig, fSum, fDum;
+ double fStorage[RowSize];
+ sal_uInt16 a, b, c;
+
+ // #i30874# Initialize nAMax (compiler warns)
+ sal_uInt16 nAMax = 0;
+
+ nParity = 1;
+
+ // Calc the max of each line. If a line is empty,
+ // stop immediately since matrix is not invertible then.
+ for(a = 0; a < RowSize; a++)
+ {
+ fBig = 0.0;
+
+ for(b = 0; b < RowSize; b++)
+ {
+ double fTemp(fabs(get(a, b)));
+
+ if(::basegfx::fTools::more(fTemp, fBig))
+ {
+ fBig = fTemp;
+ }
+ }
+
+ if(::basegfx::fTools::equalZero(fBig))
+ {
+ return false;
+ }
+
+ fStorage[a] = 1.0 / fBig;
+ }
+
+ // start normalizing
+ for(b = 0; b < RowSize; b++)
+ {
+ for(a = 0; a < b; a++)
+ {
+ fSum = get(a, b);
+
+ for(c = 0; c < a; c++)
+ {
+ fSum -= get(a, c) * get(c, b);
+ }
+
+ set(a, b, fSum);
+ }
+
+ fBig = 0.0;
+
+ for(a = b; a < RowSize; a++)
+ {
+ fSum = get(a, b);
+
+ for(c = 0; c < b; c++)
+ {
+ fSum -= get(a, c) * get(c, b);
+ }
+
+ set(a, b, fSum);
+ fDum = fStorage[a] * fabs(fSum);
+
+ if(::basegfx::fTools::moreOrEqual(fDum, fBig))
+ {
+ fBig = fDum;
+ nAMax = a;
+ }
+ }
+
+ if(b != nAMax)
+ {
+ for(c = 0; c < RowSize; c++)
+ {
+ fDum = get(nAMax, c);
+ set(nAMax, c, get(b, c));
+ set(b, c, fDum);
+ }
+
+ nParity = -nParity;
+ fStorage[nAMax] = fStorage[b];
+ }
+
+ nIndex[b] = nAMax;
+
+ // here the failure of precision occurs
+ const double fValBB(fabs(get(b, b)));
+
+ if(::basegfx::fTools::equalZero(fValBB))
+ {
+ return false;
+ }
+
+ if(b != (RowSize - 1))
+ {
+ fDum = 1.0 / get(b, b);
+
+ for(a = b + 1; a < RowSize; a++)
+ {
+ set(a, b, get(a, b) * fDum);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ void lubksb(const sal_uInt16 nIndex[], double fRow[]) const
+ {
+ sal_uInt16 b, ip;
+ sal_Int16 a, a2 = -1;
+ double fSum;
+
+ for(a = 0; a < RowSize; a++)
+ {
+ ip = nIndex[a];
+ fSum = fRow[ip];
+ fRow[ip] = fRow[a];
+
+ if(a2 >= 0)
+ {
+ for(b = a2; b < a; b++)
+ {
+ fSum -= get(a, b) * fRow[b];
+ }
+ }
+ else if(!::basegfx::fTools::equalZero(fSum))
+ {
+ a2 = a;
+ }
+
+ fRow[a] = fSum;
+ }
+
+ for(a = (RowSize - 1); a >= 0; a--)
+ {
+ fSum = fRow[a];
+
+ for(b = a + 1; b < RowSize; b++)
+ {
+ fSum -= get(a, b) * fRow[b];
+ }
+
+ const double fValueAA(get(a, a));
+
+ if(!::basegfx::fTools::equalZero(fValueAA))
+ {
+ fRow[a] = fSum / get(a, a);
+ }
+ }
+ }
+
+ bool isIdentity() const
+ {
+ // last line needs no testing if not existing
+ const sal_uInt16 nMaxLine(
+ sal::static_int_cast<sal_uInt16>(mpLine ? RowSize : (RowSize - 1)) );
+
+ for(sal_uInt16 a(0); a < nMaxLine; a++)
+ {
+ for(sal_uInt16 b(0); b < RowSize; b++)
+ {
+ const double fDefault(implGetDefaultValue(a, b));
+ const double fValueAB(get(a, b));
+
+ if(!::basegfx::fTools::equal(fDefault, fValueAB))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool isInvertible() const
+ {
+ ImplHomMatrixTemplate aWork(*this);
+ sal_uInt16 nIndex[RowSize];
+ sal_Int16 nParity;
+
+ return aWork.ludcmp(nIndex, nParity);
+ }
+
+ bool isNormalized() const
+ {
+ if(!mpLine)
+ return true;
+
+ const double fHomValue(get((RowSize - 1), (RowSize - 1)));
+
+ if(::basegfx::fTools::equalZero(fHomValue))
+ {
+ return true;
+ }
+
+ const double fOne(1.0);
+
+ if(::basegfx::fTools::equal(fOne, fHomValue))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ void doInvert(const ImplHomMatrixTemplate& rWork, const sal_uInt16 nIndex[])
+ {
+ double fArray[RowSize];
+
+ for(sal_uInt16 a(0); a < RowSize; a++)
+ {
+ // prepare line
+ sal_uInt16 b;
+ for( b = 0; b < RowSize; b++)
+ {
+ fArray[b] = implGetDefaultValue(a, b);
+ }
+
+ // expand line
+ rWork.lubksb(nIndex, fArray);
+
+ // copy line transposed to this matrix
+ for( b = 0; b < RowSize; b++)
+ {
+ set(b, a, fArray[b]);
+ }
+ }
+
+ // evtl. get rid of last matrix line
+ testLastLine();
+ }
+
+ void doNormalize()
+ {
+ if(mpLine)
+ {
+ const double fHomValue(get((RowSize - 1), (RowSize - 1)));
+
+ for(sal_uInt16 a(0); a < RowSize; a++)
+ {
+ for(sal_uInt16 b(0); b < RowSize; b++)
+ {
+ set(a, b, get(a, b) / fHomValue);
+ }
+ }
+
+ // evtl. get rid of last matrix line
+ testLastLine();
+ }
+ }
+
+ double doDeterminant() const
+ {
+ ImplHomMatrixTemplate aWork(*this);
+ sal_uInt16 nIndex[RowSize];
+ sal_Int16 nParity;
+ double fRetval(0.0);
+
+ if(aWork.ludcmp(nIndex, nParity))
+ {
+ fRetval = (double)nParity;
+
+ // last line needs no multiply if not existing; default value would be 1.
+ const sal_uInt16 nMaxLine(
+ sal::static_int_cast<sal_uInt16>(aWork.mpLine ? RowSize : (RowSize - 1)) );
+
+ for(sal_uInt16 a(0); a < nMaxLine; a++)
+ {
+ fRetval *= aWork.get(a, a);
+ }
+ }
+
+ return fRetval;
+ }
+
+ double doTrace() const
+ {
+ double fTrace = (mpLine) ? 0.0 : 1.0;
+ const sal_uInt16 nMaxLine(
+ sal::static_int_cast<sal_uInt16>(mpLine ? RowSize : (RowSize - 1)) );
+
+ for(sal_uInt16 a(0); a < nMaxLine; a++)
+ {
+ fTrace += get(a, a);
+ }
+
+ return fTrace;
+ }
+
+ void doTranspose()
+ {
+ for(sal_uInt16 a(0); a < (RowSize - 1); a++)
+ {
+ for(sal_uInt16 b(a + 1); b < RowSize; b++)
+ {
+ const double fTemp(get(a, b));
+ set(a, b, get(b, a));
+ set(b, a, fTemp);
+ }
+ }
+
+ testLastLine();
+ }
+
+ void doAddMatrix(const ImplHomMatrixTemplate& rMat)
+ {
+ for(sal_uInt16 a(0); a < RowSize; a++)
+ {
+ for(sal_uInt16 b(0); b < RowSize; b++)
+ {
+ set(a, b, get(a, b) + rMat.get(a, b));
+ }
+ }
+
+ testLastLine();
+ }
+
+ void doSubMatrix(const ImplHomMatrixTemplate& rMat)
+ {
+ for(sal_uInt16 a(0); a < RowSize; a++)
+ {
+ for(sal_uInt16 b(0); b < RowSize; b++)
+ {
+ set(a, b, get(a, b) - rMat.get(a, b));
+ }
+ }
+
+ testLastLine();
+ }
+
+ void doMulMatrix(const double& rfValue)
+ {
+ for(sal_uInt16 a(0); a < RowSize; a++)
+ {
+ for(sal_uInt16 b(0); b < RowSize; b++)
+ {
+ set(a, b, get(a, b) * rfValue);
+ }
+ }
+
+ testLastLine();
+ }
+
+ void doMulMatrix(const ImplHomMatrixTemplate& rMat)
+ {
+ // create a copy as source for the original values
+ const ImplHomMatrixTemplate aCopy(*this);
+
+ // TODO: maybe optimize cases where last line is [0 0 1].
+
+ double fValue(0.0);
+
+ for(sal_uInt16 a(0); a < RowSize; ++a)
+ {
+ for(sal_uInt16 b(0); b < RowSize; ++b)
+ {
+ fValue = 0.0;
+
+ for(sal_uInt16 c(0); c < RowSize; ++c)
+ fValue += aCopy.get(c, b) * rMat.get(a, c);
+
+ set(a, b, fValue);
+ }
+ }
+
+ testLastLine();
+ }
+
+ bool isEqual(const ImplHomMatrixTemplate& rMat) const
+ {
+ const sal_uInt16 nMaxLine(
+ sal::static_int_cast<sal_uInt16>((mpLine || rMat.mpLine) ? RowSize : (RowSize - 1)) );
+
+ for(sal_uInt16 a(0); a < nMaxLine; a++)
+ {
+ for(sal_uInt16 b(0); b < RowSize; b++)
+ {
+ const double fValueA(get(a, b));
+ const double fValueB(rMat.get(a, b));
+
+ if(!::basegfx::fTools::equal(fValueA, fValueB))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+ };
+
+ } // namespace internal
+} // namespace basegfx
+
+#endif /* _HOMMATRIX_TEMPLATE_HXX */
diff --git a/basegfx/source/inc/polygontemplate.hxx b/basegfx/source/inc/polygontemplate.hxx
new file mode 100644
index 000000000000..878854ce4380
--- /dev/null
+++ b/basegfx/source/inc/polygontemplate.hxx
@@ -0,0 +1,541 @@
+/*************************************************************************
+ *
+ * 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: polygontemplate.hxx,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.
+ *
+ ************************************************************************/
+
+#ifndef _POLYGON_TEMPLATE_HXX
+#define _POLYGON_TEMPLATE_HXX
+
+#include <vector>
+
+//////////////////////////////////////////////////////////////////////////////
+
+template < class Point > class ImplSimplePointEntry
+{
+ Point maPoint;
+
+public:
+ ImplSimplePointEntry()
+ : maPoint(Point::getEmptyPoint())
+ {
+ }
+
+ ImplSimplePointEntry(const Point& rInitPoint)
+ : maPoint(rInitPoint)
+ {
+ }
+
+ const Point& getPoint() const
+ {
+ return maPoint;
+ }
+
+ void setPoint(const Point& rValue)
+ {
+ maPoint = rValue;
+ }
+
+ bool operator==(const ImplSimplePointEntry& rEntry) const
+ {
+ return (maPoint == rEntry.maPoint);
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+template < class Vector > class ImplSimpleBezierEntry
+{
+ Vector maBackward;
+ Vector maForward;
+
+public:
+ ImplSimpleBezierEntry()
+ : maBackward(Vector::getEmptyVector()),
+ maForward(Vector::getEmptyVector())
+ {
+ }
+
+ ImplSimpleBezierEntry(const Vector& rInitBackward, const Vector& rInitForward)
+ : maBackward(rInitBackward),
+ maForward(rInitForward)
+ {
+ }
+
+ const Vector& getBackwardVector() const
+ {
+ return maBackward;
+ }
+
+ void setBackwardVector(const Vector& rValue)
+ {
+ maBackward = rValue;
+ }
+
+ const Vector& getForwardVector() const
+ {
+ return maForward;
+ }
+
+ void setForwardVector(const Vector& rValue)
+ {
+ maForward = rValue;
+ }
+
+ bool isBezierNeeded()
+ {
+ if(!maBackward.equalZero() || !maForward.equalZero())
+ return true;
+ return false;
+ }
+
+ bool operator==(const ImplSimpleBezierEntry& rEntry) const
+ {
+ return ((maBackward == rEntry.maBackward) && (maForward == rEntry.maForward));
+ }
+
+ void doInvertForFlip()
+ {
+ maBackward = -maBackward;
+ maForward = -maForward;
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+template < class Point, class Vector > class ImplPolygonTemplate
+{
+ typedef ImplSimplePointEntry< Point > LocalImplSimplePointEntry;
+ typedef ImplSimpleBezierEntry< Vector > LocalImplSimpleBezierEntry;
+ typedef ::std::vector< LocalImplSimplePointEntry > SimplePointVector;
+ typedef ::std::vector< LocalImplSimpleBezierEntry > SimpleBezierVector;
+
+ sal_uInt32 mnBezierCount;
+ SimplePointVector maPoints;
+ SimpleBezierVector* mpVectors;
+
+ unsigned mbIsClosed : 1;
+
+ void implTryToReduceToPointVector()
+ {
+ if(!mnBezierCount && mpVectors)
+ {
+ delete mpVectors;
+ mpVectors = 0L;
+ }
+ }
+
+public:
+ bool isBezier() const
+ {
+ return bool(mnBezierCount);
+ }
+
+ bool isClosed() const
+ {
+ return bool(mbIsClosed);
+ }
+
+ void setClosed(bool bNew)
+ {
+ mbIsClosed = bNew;
+ }
+
+ sal_uInt32 count() const
+ {
+ return maPoints.size();
+ }
+
+ ImplPolygonTemplate()
+ : mnBezierCount(0L),
+ mpVectors(0L),
+ mbIsClosed(false)
+ {
+ // complete initialization with defaults
+ }
+
+ ImplPolygonTemplate(const ImplPolygonTemplate& rSource)
+ : mnBezierCount(0L),
+ maPoints(rSource.maPoints),
+ mpVectors(0L),
+ mbIsClosed(rSource.mbIsClosed)
+ {
+ // complete initialization using copy
+ if(rSource.mpVectors && rSource.mnBezierCount)
+ {
+ mpVectors = new SimpleBezierVector(*rSource.mpVectors);
+ mnBezierCount = rSource.mnBezierCount;
+ }
+ }
+
+ ImplPolygonTemplate(const ImplPolygonTemplate& rSource, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : mnBezierCount(0L),
+ maPoints(nCount),
+ mpVectors(0L),
+ mbIsClosed(rSource.mbIsClosed)
+ {
+ // complete initialization using partly copy
+ if(nCount)
+ {
+ // copy point data
+ {
+ SimplePointVector::const_iterator aStart(rSource.maPoints.begin());
+ aStart += nIndex;
+ SimplePointVector::const_iterator aEnd(aStart);
+ aEnd += nCount;
+ maPoints.insert(0L, aStart, aEnd);
+ }
+
+ // copy bezier data
+ if(rSource.mpVectors && rSource.mnBezierCount)
+ {
+ mpVectors = new SimpleBezierVector();
+ mpVectors->reserve(nCount);
+
+ SimpleBezierVector::iterator aStart(mpVectors->begin());
+ aStart += nIndex;
+ SimpleBezierVector::iterator aEnd(aStart);
+ aEnd += nCount;
+
+ for( ; aStart != aEnd; ++aStart )
+ {
+ if(aStart->isBezierNeeded())
+ {
+ mnBezierCount++;
+ }
+
+ mpVectors->push_back(*aStart);
+ }
+
+ // maybe vectors are not needed anymore, try to reduce memory footprint
+ implTryToReduceToPointVector();
+ }
+ }
+ }
+
+ ~ImplPolygonTemplate()
+ {
+ if(mpVectors)
+ {
+ delete mpVectors;
+ }
+ }
+
+ bool isEqual(const ImplPolygonTemplate& rPointList) const
+ {
+ // same point count?
+ if(maPoints.size() != rPointList.maPoints.size())
+ return false;
+
+ // if zero points the polys are equal
+ if(!maPoints.size())
+ return true;
+
+ // if bezier count used it needs to be equal
+ if(mnBezierCount != rPointList.mnBezierCount)
+ return false;
+
+ // compare point content
+ if(maPoints != rPointList.maPoints)
+ return false;
+
+ // beziercounts are equal: if it's zero, we are done
+ if(!mnBezierCount)
+ return true;
+
+ // beziercounts are equal and not zero; compare them
+ OSL_ENSURE(0L != mpVectors, "Error: Bezier list needs to exist here(!)");
+ OSL_ENSURE(0L != rPointList.mpVectors, "Error: Bezier list needs to exist here(!)");
+
+ return (*mpVectors == *rPointList.mpVectors);
+ }
+
+ const Point& getPoint(sal_uInt32 nIndex) const
+ {
+ return maPoints[nIndex].getPoint();
+ }
+
+ void setPoint(sal_uInt32 nIndex, const Point& rValue)
+ {
+ maPoints[nIndex].setPoint(rValue);
+ }
+
+ const Vector& getBackwardVector(sal_uInt32 nIndex) const
+ {
+ if(mpVectors)
+ return ((*mpVectors)[nIndex]).getBackwardVector();
+ else
+ return Vector::getEmptyVector();
+ }
+
+ void setBackwardVector(sal_uInt32 nIndex, const Vector& rValue)
+ {
+ if(mpVectors)
+ {
+ LocalImplSimpleBezierEntry& rDest = (*mpVectors)[nIndex];
+ bool bBezierNeededBefore(rDest.isBezierNeeded());
+ ((*mpVectors)[nIndex]).setBackwardVector(rValue);
+ bool bBezierNeededAfter(rDest.isBezierNeeded());
+
+ if(bBezierNeededBefore != bBezierNeededAfter)
+ {
+ if(bBezierNeededAfter)
+ mnBezierCount++;
+ else
+ mnBezierCount--;
+ }
+ }
+ else
+ {
+ bool bEmptyVector(rValue.equalZero());
+
+ if(bEmptyVector)
+ return;
+
+ mpVectors = new SimpleBezierVector(maPoints.size());
+ ((*mpVectors)[nIndex]).setBackwardVector(rValue);
+ mnBezierCount++;
+ }
+ }
+
+ const Vector& getForwardVector(sal_uInt32 nIndex) const
+ {
+ if(mpVectors)
+ return ((*mpVectors)[nIndex]).getForwardVector();
+ else
+ return Vector::getEmptyVector();
+ }
+
+ void setForwardVector(sal_uInt32 nIndex, const Vector& rValue)
+ {
+ if(mpVectors)
+ {
+ LocalImplSimpleBezierEntry& rDest = (*mpVectors)[nIndex];
+ bool bBezierNeededBefore(rDest.isBezierNeeded());
+ ((*mpVectors)[nIndex]).setForwardVector(rValue);
+ bool bBezierNeededAfter(rDest.isBezierNeeded());
+
+ if(bBezierNeededBefore != bBezierNeededAfter)
+ {
+ if(bBezierNeededAfter)
+ mnBezierCount++;
+ else
+ mnBezierCount--;
+ }
+ }
+ else
+ {
+ bool bEmptyVector(rValue.equalZero());
+
+ if(bEmptyVector)
+ return;
+
+ mpVectors = new SimpleBezierVector(maPoints.size());
+ ((*mpVectors)[nIndex]).setForwardVector(rValue);
+ mnBezierCount++;
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const Point& rPoint, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // maybe vectors are not needed anymore, try to reduce memory footprint
+ implTryToReduceToPointVector();
+
+ // add nCount copies of rPoint
+ {
+ LocalImplSimplePointEntry aNode(rPoint);
+ SimplePointVector::iterator aIndex(maPoints.begin());
+ aIndex += nIndex;
+ maPoints.insert(aIndex, nCount, aNode);
+ }
+
+ // add nCount empty entries to keep indices synchronized
+ if(mpVectors)
+ {
+ LocalImplSimpleBezierEntry aNode;
+ SimpleBezierVector::iterator aIndex(mpVectors->begin());
+ aIndex += nIndex;
+ mpVectors->insert(aIndex, nCount, aNode);
+ }
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const ImplPolygonTemplate& rSource)
+ {
+ const sal_uInt32 nCount(rSource.maPoints.size());
+
+ if(nCount)
+ {
+ // instert point data
+ {
+ SimplePointVector::iterator aIndex(maPoints.begin());
+ aIndex += nIndex;
+
+ SimplePointVector::const_iterator aStart(rSource.maPoints.begin());
+ SimplePointVector::const_iterator aEnd(rSource.maPoints.end());
+
+ maPoints.insert(aIndex, aStart, aEnd);
+ }
+
+ // insert bezier data
+ if(rSource.mpVectors && rSource.mnBezierCount)
+ {
+ SimpleBezierVector::iterator aIndex(mpVectors->begin());
+ aIndex += nIndex;
+
+ SimpleBezierVector::iterator aStart(rSource.mpVectors->begin());
+ SimpleBezierVector::iterator aEnd(rSource.mpVectors->end());
+
+ if(!mpVectors)
+ {
+ mpVectors = new SimpleBezierVector(maPoints.size() - nCount);
+ }
+
+ mpVectors->insert(aIndex, aStart, aEnd);
+
+ mnBezierCount += rSource.mnBezierCount;
+ }
+ else
+ {
+ // maybe vectors are not needed anymore, try to reduce memory footprint
+ implTryToReduceToPointVector();
+
+ // add nCount empty entries to keep indices synchronized
+ if(mpVectors)
+ {
+ LocalImplSimpleBezierEntry aNode;
+ SimpleBezierVector::iterator aIndex(mpVectors->begin());
+ aIndex += nIndex;
+ mpVectors->insert(aIndex, nCount, aNode);
+ }
+ }
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // maybe vectors are not needed anymore, try to reduce memory footprint
+ implTryToReduceToPointVector();
+
+ // remove point data
+ {
+ SimplePointVector::iterator aStart(maPoints.begin());
+ aStart += nIndex;
+ const SimplePointVector::iterator aEnd(aStart + nCount);
+
+ maPoints.erase(aStart, aEnd);
+ }
+
+ // remove bezier data
+ if(mpVectors)
+ {
+ SimpleBezierVector::iterator aStart(mpVectors->begin());
+ aStart += nIndex;
+ const SimpleBezierVector::iterator aEnd(aStart + nCount);
+
+ // take care for correct mnBezierCount BEFORE erase
+ if(mnBezierCount)
+ {
+ SimpleBezierVector::iterator aTestIter(aStart);
+
+ for( ; mnBezierCount && aTestIter != aEnd; ++aTestIter)
+ {
+ if(aTestIter->isBezierNeeded())
+ mnBezierCount--;
+ }
+ }
+
+ if(mnBezierCount)
+ {
+ // erase nodes
+ mpVectors->erase(aStart, aEnd);
+ }
+ else
+ {
+ // try to reduce, maybe 0L == mnBezierCount
+ implTryToReduceToPointVector();
+ }
+ }
+ }
+ }
+
+ void flip()
+ {
+ if(maPoints.size() > 1)
+ {
+ // maybe vectors are not needed anymore, try to reduce memory footprint
+ implTryToReduceToPointVector();
+
+ // calculate half size
+ const sal_uInt32 nHalfSize(maPoints.size() >> 1L);
+
+ // flip point data
+ {
+ SimplePointVector::iterator aStart(maPoints.begin());
+ SimplePointVector::iterator aEnd(maPoints.end());
+
+ for(sal_uInt32 a(0); a < nHalfSize; a++)
+ {
+ LocalImplSimplePointEntry aTemp = *aStart;
+ *aStart++ = *aEnd;
+ *aEnd-- = aTemp;
+ }
+ }
+
+ // flip bezier data
+ if(mpVectors)
+ {
+ SimpleBezierVector::iterator aStart(mpVectors->begin());
+ SimpleBezierVector::iterator aEnd(mpVectors->end());
+
+ for(sal_uInt32 a(0); a < nHalfSize; a++)
+ {
+ LocalImplSimpleBezierEntry aTemp = *aStart;
+ aTemp.doInvertForFlip();
+ *aStart = *aEnd;
+ aStart->doInvertForFlip();
+ aStart++;
+ *aEnd-- = aTemp;
+ }
+
+ // also flip vectors of middle point (if existing)
+ if(maPoints.size() % 2)
+ {
+ (*mpVectors)[nHalfSize].doInvertForFlip();
+ }
+ }
+ }
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif _POLYGON_TEMPLATE_HXX
diff --git a/basegfx/source/matrix/b2dhommatrix.cxx b/basegfx/source/matrix/b2dhommatrix.cxx
new file mode 100644
index 000000000000..352113fa8ed3
--- /dev/null
+++ b/basegfx/source/matrix/b2dhommatrix.cxx
@@ -0,0 +1,577 @@
+/*************************************************************************
+ *
+ * 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: b2dhommatrix.cxx,v $
+ * $Revision: 1.20 $
+ *
+ * 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 <rtl/instance.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <hommatrixtemplate.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+
+namespace basegfx
+{
+ class Impl2DHomMatrix : public ::basegfx::internal::ImplHomMatrixTemplate< 3 >
+ {
+ };
+
+ namespace { struct IdentityMatrix : public rtl::Static< B2DHomMatrix::ImplType,
+ IdentityMatrix > {}; }
+
+ B2DHomMatrix::B2DHomMatrix() :
+ mpImpl( IdentityMatrix::get() ) // use common identity matrix
+ {
+ }
+
+ B2DHomMatrix::B2DHomMatrix(const B2DHomMatrix& rMat) :
+ mpImpl(rMat.mpImpl)
+ {
+ }
+
+ B2DHomMatrix::~B2DHomMatrix()
+ {
+ }
+
+ B2DHomMatrix& B2DHomMatrix::operator=(const B2DHomMatrix& rMat)
+ {
+ mpImpl = rMat.mpImpl;
+ return *this;
+ }
+
+ void B2DHomMatrix::makeUnique()
+ {
+ mpImpl.make_unique();
+ }
+
+ double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
+ {
+ return mpImpl->get(nRow, nColumn);
+ }
+
+ void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
+ {
+ mpImpl->set(nRow, nColumn, fValue);
+ }
+
+ bool B2DHomMatrix::isLastLineDefault() const
+ {
+ return mpImpl->isLastLineDefault();
+ }
+
+ bool B2DHomMatrix::isIdentity() const
+ {
+ if(mpImpl.same_object(IdentityMatrix::get()))
+ return true;
+
+ return mpImpl->isIdentity();
+ }
+
+ void B2DHomMatrix::identity()
+ {
+ mpImpl = IdentityMatrix::get();
+ }
+
+ bool B2DHomMatrix::isInvertible() const
+ {
+ return mpImpl->isInvertible();
+ }
+
+ bool B2DHomMatrix::invert()
+ {
+ Impl2DHomMatrix aWork(*mpImpl);
+ sal_uInt16* pIndex = new sal_uInt16[mpImpl->getEdgeLength()];
+ sal_Int16 nParity;
+
+ if(aWork.ludcmp(pIndex, nParity))
+ {
+ mpImpl->doInvert(aWork, pIndex);
+ delete[] pIndex;
+
+ return true;
+ }
+
+ delete[] pIndex;
+ return false;
+ }
+
+ bool B2DHomMatrix::isNormalized() const
+ {
+ return mpImpl->isNormalized();
+ }
+
+ void B2DHomMatrix::normalize()
+ {
+ if(!const_cast<const B2DHomMatrix*>(this)->mpImpl->isNormalized())
+ mpImpl->doNormalize();
+ }
+
+ double B2DHomMatrix::determinant() const
+ {
+ return mpImpl->doDeterminant();
+ }
+
+ double B2DHomMatrix::trace() const
+ {
+ return mpImpl->doTrace();
+ }
+
+ void B2DHomMatrix::transpose()
+ {
+ mpImpl->doTranspose();
+ }
+
+ B2DHomMatrix& B2DHomMatrix::operator+=(const B2DHomMatrix& rMat)
+ {
+ mpImpl->doAddMatrix(*rMat.mpImpl);
+ return *this;
+ }
+
+ B2DHomMatrix& B2DHomMatrix::operator-=(const B2DHomMatrix& rMat)
+ {
+ mpImpl->doSubMatrix(*rMat.mpImpl);
+ return *this;
+ }
+
+ B2DHomMatrix& B2DHomMatrix::operator*=(double fValue)
+ {
+ const double fOne(1.0);
+
+ if(!fTools::equal(fOne, fValue))
+ mpImpl->doMulMatrix(fValue);
+
+ return *this;
+ }
+
+ B2DHomMatrix& B2DHomMatrix::operator/=(double fValue)
+ {
+ const double fOne(1.0);
+
+ if(!fTools::equal(fOne, fValue))
+ mpImpl->doMulMatrix(1.0 / fValue);
+
+ return *this;
+ }
+
+ B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat)
+ {
+ if(!rMat.isIdentity())
+ mpImpl->doMulMatrix(*rMat.mpImpl);
+
+ return *this;
+ }
+
+ bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const
+ {
+ if(mpImpl.same_object(rMat.mpImpl))
+ return true;
+
+ return mpImpl->isEqual(*rMat.mpImpl);
+ }
+
+ bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const
+ {
+ return !(*this == rMat);
+ }
+
+ void B2DHomMatrix::rotate(double fRadiant)
+ {
+ if(!fTools::equalZero(fRadiant))
+ {
+ double fSin(0.0);
+ double fCos(0.0);
+
+ // is the rotation angle an approximate multiple of pi/2?
+ // If yes, force fSin/fCos to -1/0/1, to maintain
+ // orthogonality (which might also be advantageous for the
+ // other cases, but: for multiples of pi/2, the exact
+ // values _can_ be attained. It would be largely
+ // unintuitive, if a 180 degrees rotation would introduce
+ // slight roundoff errors, instead of exactly mirroring
+ // the coordinate system).
+ if( fTools::equalZero( fmod( fRadiant, F_PI2 ) ) )
+ {
+ // determine quadrant
+ const sal_Int32 nQuad(
+ (4 + fround( 4/F_2PI*fmod( fRadiant, F_2PI ) )) % 4 );
+ switch( nQuad )
+ {
+ case 0: // -2pi,0,2pi
+ fSin = 0.0;
+ fCos = 1.0;
+ break;
+
+ case 1: // -3/2pi,1/2pi
+ fSin = 1.0;
+ fCos = 0.0;
+ break;
+
+ case 2: // -pi,pi
+ fSin = 0.0;
+ fCos = -1.0;
+ break;
+
+ case 3: // -1/2pi,3/2pi
+ fSin = -1.0;
+ fCos = 0.0;
+ break;
+
+ default:
+ OSL_ENSURE( false,
+ "B2DHomMatrix::rotate(): Impossible case reached" );
+ }
+ }
+ else
+ {
+ // TODO(P1): Maybe use glibc's sincos here (though
+ // that's kinda non-portable...)
+ fSin = sin(fRadiant);
+ fCos = cos(fRadiant);
+ }
+
+ Impl2DHomMatrix aRotMat;
+
+ aRotMat.set(0, 0, fCos);
+ aRotMat.set(1, 1, fCos);
+ aRotMat.set(1, 0, fSin);
+ aRotMat.set(0, 1, -fSin);
+
+ mpImpl->doMulMatrix(aRotMat);
+ }
+ }
+
+ void B2DHomMatrix::translate(double fX, double fY)
+ {
+ if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
+ {
+ Impl2DHomMatrix aTransMat;
+
+ aTransMat.set(0, 2, fX);
+ aTransMat.set(1, 2, fY);
+
+ mpImpl->doMulMatrix(aTransMat);
+ }
+ }
+
+ void B2DHomMatrix::scale(double fX, double fY)
+ {
+ const double fOne(1.0);
+
+ if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
+ {
+ Impl2DHomMatrix aScaleMat;
+
+ aScaleMat.set(0, 0, fX);
+ aScaleMat.set(1, 1, fY);
+
+ mpImpl->doMulMatrix(aScaleMat);
+ }
+ }
+
+ void B2DHomMatrix::shearX(double fSx)
+ {
+ // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
+ if(!fTools::equalZero(fSx))
+ {
+ Impl2DHomMatrix aShearXMat;
+
+ aShearXMat.set(0, 1, fSx);
+
+ mpImpl->doMulMatrix(aShearXMat);
+ }
+ }
+
+ void B2DHomMatrix::shearY(double fSy)
+ {
+ // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
+ if(!fTools::equalZero(fSy))
+ {
+ Impl2DHomMatrix aShearYMat;
+
+ aShearYMat.set(1, 0, fSy);
+
+ mpImpl->doMulMatrix(aShearYMat);
+ }
+ }
+
+ /** Decomposition
+
+ New, optimized version with local shearX detection. Old version (keeping
+ below, is working well, too) used the 3D matrix decomposition when
+ shear was used. Keeping old version as comment below since it may get
+ necessary to add the determinant() test from there here, too.
+ */
+ bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
+ {
+ // when perspective is used, decompose is not made here
+ if(!mpImpl->isLastLineDefault())
+ {
+ return false;
+ }
+
+ // reset rotate and shear and copy translation values in every case
+ rRotate = rShearX = 0.0;
+ rTranslate.setX(get(0, 2));
+ rTranslate.setY(get(1, 2));
+
+ // test for rotation and shear
+ if(fTools::equalZero(get(0, 1)) && fTools::equalZero(get(1, 0)))
+ {
+ // no rotation and shear, copy scale values
+ rScale.setX(get(0, 0));
+ rScale.setY(get(1, 1));
+ }
+ else
+ {
+ // get the unit vectors of the transformation -> the perpendicular vectors
+ B2DVector aUnitVecX(get(0, 0), get(1, 0));
+ B2DVector aUnitVecY(get(0, 1), get(1, 1));
+ const double fScalarXY(aUnitVecX.scalar(aUnitVecY));
+
+ // Test if shear is zero. That's the case if the unit vectors in the matrix
+ // are perpendicular -> scalar is zero. This is also the case when one of
+ // the unit vectors is zero.
+ if(fTools::equalZero(fScalarXY))
+ {
+ // calculate unsigned scale values
+ rScale.setX(aUnitVecX.getLength());
+ rScale.setY(aUnitVecY.getLength());
+
+ // check unit vectors for zero lengths
+ const bool bXIsZero(fTools::equalZero(rScale.getX()));
+ const bool bYIsZero(fTools::equalZero(rScale.getY()));
+
+ if(bXIsZero || bYIsZero)
+ {
+ // still extract as much as possible. Scalings are already set
+ if(!bXIsZero)
+ {
+ // get rotation of X-Axis
+ rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
+ }
+ else if(!bYIsZero)
+ {
+ // get rotation of X-Axis. When assuming X and Y perpendicular
+ // and correct rotation, it's the Y-Axis rotation minus 90 degrees
+ rRotate = atan2(aUnitVecY.getY(), aUnitVecY.getX()) - M_PI_2;
+ }
+
+ // one or both unit vectors do not extist, determinant is zero, no decomposition possible.
+ // Eventually used rotations or shears are lost
+ return false;
+ }
+ else
+ {
+ // no shear
+ // calculate rotation of X unit vector relative to (1, 0)
+ rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
+
+ // use orientation to evtl. correct sign of Y-Scale
+ const double fCrossXY(aUnitVecX.cross(aUnitVecY));
+
+ if(fCrossXY < 0.0)
+ {
+ rScale.setY(-rScale.getY());
+ }
+ }
+ }
+ else
+ {
+ // fScalarXY is not zero, thus both unit vectors exist. No need to handle that here
+ // shear, extract it
+ double fCrossXY(aUnitVecX.cross(aUnitVecY));
+
+ // get rotation by calculating angle of X unit vector relative to (1, 0).
+ // This is before the parallell test following the motto to extract
+ // as much as possible
+ rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
+
+ // get unsigned scale value for X. It will not change and is useful
+ // for further corrections
+ rScale.setX(aUnitVecX.getLength());
+
+ if(fTools::equalZero(fCrossXY))
+ {
+ // extract as much as possible
+ rScale.setY(aUnitVecY.getLength());
+
+ // unit vectors are parallel, thus not linear independent. No
+ // useful decomposition possible. This should not happen since
+ // the only way to get the unit vectors nearly parallell is
+ // a very big shearing. Anyways, be prepared for hand-filled
+ // matrices
+ // Eventually used rotations or shears are lost
+ return false;
+ }
+ else
+ {
+ // calculate the contained shear
+ rShearX = fScalarXY / fCrossXY;
+
+ if(!fTools::equalZero(rRotate))
+ {
+ // To be able to correct the shear for aUnitVecY, rotation needs to be
+ // removed first. Correction of aUnitVecX is easy, it will be rotated back to (1, 0).
+ aUnitVecX.setX(rScale.getX());
+ aUnitVecX.setY(0.0);
+
+ // for Y correction we rotate the UnitVecY back about -rRotate
+ const double fNegRotate(-rRotate);
+ const double fSin(sin(fNegRotate));
+ const double fCos(cos(fNegRotate));
+
+ const double fNewX(aUnitVecY.getX() * fCos - aUnitVecY.getY() * fSin);
+ const double fNewY(aUnitVecY.getX() * fSin + aUnitVecY.getY() * fCos);
+
+ aUnitVecY.setX(fNewX);
+ aUnitVecY.setY(fNewY);
+ }
+
+ // Correct aUnitVecY and fCrossXY to fShear=0. Rotation is already removed.
+ // Shear correction can only work with removed rotation
+ aUnitVecY.setX(aUnitVecY.getX() - (aUnitVecY.getY() * rShearX));
+ fCrossXY = aUnitVecX.cross(aUnitVecY);
+
+ // calculate unsigned scale value for Y, after the corrections since
+ // the shear correction WILL change the length of aUnitVecY
+ rScale.setY(aUnitVecY.getLength());
+
+ // use orientation to set sign of Y-Scale
+ if(fCrossXY < 0.0)
+ {
+ rScale.setY(-rScale.getY());
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+/* Old version: Used 3D decompose when shaer was involved and also a determinant test
+ (but only in that case). Keeping as comment since it also worked and to allow a
+ fallback in case the new version makes trouble somehow. Definitely missing in the 2nd
+ case is the sign correction for Y-Scale, this would need to be added following the above
+ pattern
+
+ bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
+ {
+ // when perspective is used, decompose is not made here
+ if(!mpImpl->isLastLineDefault())
+ return false;
+
+ // test for rotation and shear
+ if(fTools::equalZero(get(0, 1))
+ && fTools::equalZero(get(1, 0)))
+ {
+ // no rotation and shear, direct value extraction
+ rRotate = rShearX = 0.0;
+
+ // copy scale values
+ rScale.setX(get(0, 0));
+ rScale.setY(get(1, 1));
+
+ // copy translation values
+ rTranslate.setX(get(0, 2));
+ rTranslate.setY(get(1, 2));
+
+ return true;
+ }
+ else
+ {
+ // test if shear is zero. That's the case, if the unit vectors in the matrix
+ // are perpendicular -> scalar is zero
+ const ::basegfx::B2DVector aUnitVecX(get(0, 0), get(1, 0));
+ const ::basegfx::B2DVector aUnitVecY(get(0, 1), get(1, 1));
+
+ if(fTools::equalZero(aUnitVecX.scalar(aUnitVecY)))
+ {
+ // no shear, direct value extraction
+ rShearX = 0.0;
+
+ // calculate rotation
+ rShearX = 0.0;
+ rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());
+
+ // calculate scale values
+ rScale.setX(aUnitVecX.getLength());
+ rScale.setY(aUnitVecY.getLength());
+
+ // copy translation values
+ rTranslate.setX(get(0, 2));
+ rTranslate.setY(get(1, 2));
+
+ return true;
+ }
+ else
+ {
+ // If determinant is zero, decomposition is not possible
+ if(0.0 == determinant())
+ return false;
+
+ // copy 2x2 matrix and translate vector to 3x3 matrix
+ ::basegfx::B3DHomMatrix a3DHomMat;
+
+ a3DHomMat.set(0, 0, get(0, 0));
+ a3DHomMat.set(0, 1, get(0, 1));
+ a3DHomMat.set(1, 0, get(1, 0));
+ a3DHomMat.set(1, 1, get(1, 1));
+ a3DHomMat.set(0, 3, get(0, 2));
+ a3DHomMat.set(1, 3, get(1, 2));
+
+ ::basegfx::B3DTuple r3DScale, r3DTranslate, r3DRotate, r3DShear;
+
+ if(a3DHomMat.decompose(r3DScale, r3DTranslate, r3DRotate, r3DShear))
+ {
+ // copy scale values
+ rScale.setX(r3DScale.getX());
+ rScale.setY(r3DScale.getY());
+
+ // copy shear
+ rShearX = r3DShear.getX();
+
+ // copy rotate
+ rRotate = r3DRotate.getZ();
+
+ // copy translate
+ rTranslate.setX(r3DTranslate.getX());
+ rTranslate.setY(r3DTranslate.getY());
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ } */
+
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/matrix/b2dhommatrixtools.cxx b/basegfx/source/matrix/b2dhommatrixtools.cxx
new file mode 100644
index 000000000000..59a1ff432fc7
--- /dev/null
+++ b/basegfx/source/matrix/b2dhommatrixtools.cxx
@@ -0,0 +1,44 @@
+/*************************************************************************
+ *
+ * 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: b2dhommatrixtools.cxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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/matrix/b2dhommatrixtools.hxx>
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+} // end of namespace basegfx
+
+///////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/matrix/b3dhommatrix.cxx b/basegfx/source/matrix/b3dhommatrix.cxx
new file mode 100644
index 000000000000..4c88f794ec46
--- /dev/null
+++ b/basegfx/source/matrix/b3dhommatrix.cxx
@@ -0,0 +1,599 @@
+/*************************************************************************
+ *
+ * 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: b3dhommatrix.cxx,v $
+ * $Revision: 1.15 $
+ *
+ * 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 <rtl/instance.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <hommatrixtemplate.hxx>
+#include <basegfx/vector/b3dvector.hxx>
+
+namespace basegfx
+{
+ class Impl3DHomMatrix : public ::basegfx::internal::ImplHomMatrixTemplate< 4 >
+ {
+ };
+
+ namespace { struct IdentityMatrix : public rtl::Static< B3DHomMatrix::ImplType,
+ IdentityMatrix > {}; }
+
+ B3DHomMatrix::B3DHomMatrix() :
+ mpImpl( IdentityMatrix::get() ) // use common identity matrix
+ {
+ }
+
+ B3DHomMatrix::B3DHomMatrix(const B3DHomMatrix& rMat) :
+ mpImpl(rMat.mpImpl)
+ {
+ }
+
+ B3DHomMatrix::~B3DHomMatrix()
+ {
+ }
+
+ B3DHomMatrix& B3DHomMatrix::operator=(const B3DHomMatrix& rMat)
+ {
+ mpImpl = rMat.mpImpl;
+ return *this;
+ }
+
+ void B3DHomMatrix::makeUnique()
+ {
+ mpImpl.make_unique();
+ }
+
+ double B3DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
+ {
+ return mpImpl->get(nRow, nColumn);
+ }
+
+ void B3DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
+ {
+ mpImpl->set(nRow, nColumn, fValue);
+ }
+
+ bool B3DHomMatrix::isLastLineDefault() const
+ {
+ return mpImpl->isLastLineDefault();
+ }
+
+ bool B3DHomMatrix::isIdentity() const
+ {
+ if(mpImpl.same_object(IdentityMatrix::get()))
+ return true;
+
+ return mpImpl->isIdentity();
+ }
+
+ void B3DHomMatrix::identity()
+ {
+ mpImpl = IdentityMatrix::get();
+ }
+
+ bool B3DHomMatrix::isInvertible() const
+ {
+ return mpImpl->isInvertible();
+ }
+
+ bool B3DHomMatrix::invert()
+ {
+ Impl3DHomMatrix aWork(*mpImpl);
+ sal_uInt16* pIndex = new sal_uInt16[mpImpl->getEdgeLength()];
+ sal_Int16 nParity;
+
+ if(aWork.ludcmp(pIndex, nParity))
+ {
+ mpImpl->doInvert(aWork, pIndex);
+ delete[] pIndex;
+
+ return true;
+ }
+
+ delete[] pIndex;
+ return false;
+ }
+
+ bool B3DHomMatrix::isNormalized() const
+ {
+ return mpImpl->isNormalized();
+ }
+
+ void B3DHomMatrix::normalize()
+ {
+ if(!const_cast<const B3DHomMatrix*>(this)->mpImpl->isNormalized())
+ mpImpl->doNormalize();
+ }
+
+ double B3DHomMatrix::determinant() const
+ {
+ return mpImpl->doDeterminant();
+ }
+
+ double B3DHomMatrix::trace() const
+ {
+ return mpImpl->doTrace();
+ }
+
+ void B3DHomMatrix::transpose()
+ {
+ mpImpl->doTranspose();
+ }
+
+ B3DHomMatrix& B3DHomMatrix::operator+=(const B3DHomMatrix& rMat)
+ {
+ mpImpl->doAddMatrix(*rMat.mpImpl);
+ return *this;
+ }
+
+ B3DHomMatrix& B3DHomMatrix::operator-=(const B3DHomMatrix& rMat)
+ {
+ mpImpl->doSubMatrix(*rMat.mpImpl);
+ return *this;
+ }
+
+ B3DHomMatrix& B3DHomMatrix::operator*=(double fValue)
+ {
+ const double fOne(1.0);
+
+ if(!fTools::equal(fOne, fValue))
+ mpImpl->doMulMatrix(fValue);
+
+ return *this;
+ }
+
+ B3DHomMatrix& B3DHomMatrix::operator/=(double fValue)
+ {
+ const double fOne(1.0);
+
+ if(!fTools::equal(fOne, fValue))
+ mpImpl->doMulMatrix(1.0 / fValue);
+
+ return *this;
+ }
+
+ B3DHomMatrix& B3DHomMatrix::operator*=(const B3DHomMatrix& rMat)
+ {
+ if(!rMat.isIdentity())
+ mpImpl->doMulMatrix(*rMat.mpImpl);
+
+ return *this;
+ }
+
+ bool B3DHomMatrix::operator==(const B3DHomMatrix& rMat) const
+ {
+ if(mpImpl.same_object(rMat.mpImpl))
+ return true;
+
+ return mpImpl->isEqual(*rMat.mpImpl);
+ }
+
+ bool B3DHomMatrix::operator!=(const B3DHomMatrix& rMat) const
+ {
+ return !(*this == rMat);
+ }
+
+ void B3DHomMatrix::rotate(double fAngleX,double fAngleY,double fAngleZ)
+ {
+ if(!fTools::equalZero(fAngleX) || !fTools::equalZero(fAngleY) || !fTools::equalZero(fAngleZ))
+ {
+ if(!fTools::equalZero(fAngleX))
+ {
+ Impl3DHomMatrix aRotMatX;
+ double fSin(sin(fAngleX));
+ double fCos(cos(fAngleX));
+
+ aRotMatX.set(1, 1, fCos);
+ aRotMatX.set(2, 2, fCos);
+ aRotMatX.set(2, 1, fSin);
+ aRotMatX.set(1, 2, -fSin);
+
+ mpImpl->doMulMatrix(aRotMatX);
+ }
+
+ if(!fTools::equalZero(fAngleY))
+ {
+ Impl3DHomMatrix aRotMatY;
+ double fSin(sin(fAngleY));
+ double fCos(cos(fAngleY));
+
+ aRotMatY.set(0, 0, fCos);
+ aRotMatY.set(2, 2, fCos);
+ aRotMatY.set(0, 2, fSin);
+ aRotMatY.set(2, 0, -fSin);
+
+ mpImpl->doMulMatrix(aRotMatY);
+ }
+
+ if(!fTools::equalZero(fAngleZ))
+ {
+ Impl3DHomMatrix aRotMatZ;
+ double fSin(sin(fAngleZ));
+ double fCos(cos(fAngleZ));
+
+ aRotMatZ.set(0, 0, fCos);
+ aRotMatZ.set(1, 1, fCos);
+ aRotMatZ.set(1, 0, fSin);
+ aRotMatZ.set(0, 1, -fSin);
+
+ mpImpl->doMulMatrix(aRotMatZ);
+ }
+ }
+ }
+
+ void B3DHomMatrix::translate(double fX, double fY, double fZ)
+ {
+ if(!fTools::equalZero(fX) || !fTools::equalZero(fY) || !fTools::equalZero(fZ))
+ {
+ Impl3DHomMatrix aTransMat;
+
+ aTransMat.set(0, 3, fX);
+ aTransMat.set(1, 3, fY);
+ aTransMat.set(2, 3, fZ);
+
+ mpImpl->doMulMatrix(aTransMat);
+ }
+ }
+
+ void B3DHomMatrix::scale(double fX, double fY, double fZ)
+ {
+ const double fOne(1.0);
+
+ if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY) ||!fTools::equal(fOne, fZ))
+ {
+ Impl3DHomMatrix aScaleMat;
+
+ aScaleMat.set(0, 0, fX);
+ aScaleMat.set(1, 1, fY);
+ aScaleMat.set(2, 2, fZ);
+
+ mpImpl->doMulMatrix(aScaleMat);
+ }
+ }
+
+ void B3DHomMatrix::shearXY(double fSx, double fSy)
+ {
+ // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
+ if(!fTools::equalZero(fSx) || !fTools::equalZero(fSy))
+ {
+ Impl3DHomMatrix aShearXYMat;
+
+ aShearXYMat.set(0, 2, fSx);
+ aShearXYMat.set(1, 2, fSy);
+
+ mpImpl->doMulMatrix(aShearXYMat);
+ }
+ }
+
+ void B3DHomMatrix::shearYZ(double fSy, double fSz)
+ {
+ // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
+ if(!fTools::equalZero(fSy) || !fTools::equalZero(fSz))
+ {
+ Impl3DHomMatrix aShearYZMat;
+
+ aShearYZMat.set(1, 0, fSy);
+ aShearYZMat.set(2, 0, fSz);
+
+ mpImpl->doMulMatrix(aShearYZMat);
+ }
+ }
+
+ void B3DHomMatrix::shearXZ(double fSx, double fSz)
+ {
+ // #i76239# do not test againt 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
+ if(!fTools::equalZero(fSx) || !fTools::equalZero(fSz))
+ {
+ Impl3DHomMatrix aShearXZMat;
+
+ aShearXZMat.set(0, 1, fSx);
+ aShearXZMat.set(2, 1, fSz);
+
+ mpImpl->doMulMatrix(aShearXZMat);
+ }
+ }
+
+ void B3DHomMatrix::frustum(double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar)
+ {
+ const double fZero(0.0);
+ const double fOne(1.0);
+
+ if(!fTools::more(fNear, fZero))
+ {
+ fNear = 0.001;
+ }
+
+ if(!fTools::more(fFar, fZero))
+ {
+ fFar = fOne;
+ }
+
+ if(fTools::equal(fNear, fFar))
+ {
+ fFar = fNear + fOne;
+ }
+
+ if(fTools::equal(fLeft, fRight))
+ {
+ fLeft -= fOne;
+ fRight += fOne;
+ }
+
+ if(fTools::equal(fTop, fBottom))
+ {
+ fBottom -= fOne;
+ fTop += fOne;
+ }
+
+ Impl3DHomMatrix aFrustumMat;
+
+ aFrustumMat.set(0, 0, 2.0 * fNear / (fRight - fLeft));
+ aFrustumMat.set(1, 1, 2.0 * fNear / (fTop - fBottom));
+ aFrustumMat.set(0, 2, (fRight + fLeft) / (fRight - fLeft));
+ aFrustumMat.set(1, 2, (fTop + fBottom) / (fTop - fBottom));
+ aFrustumMat.set(2, 2, -fOne * ((fFar + fNear) / (fFar - fNear)));
+ aFrustumMat.set(3, 2, -fOne);
+ aFrustumMat.set(2, 3, -fOne * ((2.0 * fFar * fNear) / (fFar - fNear)));
+ aFrustumMat.set(3, 3, fZero);
+
+ mpImpl->doMulMatrix(aFrustumMat);
+ }
+
+ void B3DHomMatrix::ortho(double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar)
+ {
+ if(fTools::equal(fNear, fFar))
+ {
+ fFar = fNear + 1.0;
+ }
+
+ if(fTools::equal(fLeft, fRight))
+ {
+ fLeft -= 1.0;
+ fRight += 1.0;
+ }
+
+ if(fTools::equal(fTop, fBottom))
+ {
+ fBottom -= 1.0;
+ fTop += 1.0;
+ }
+
+ Impl3DHomMatrix aOrthoMat;
+
+ aOrthoMat.set(0, 0, 2.0 / (fRight - fLeft));
+ aOrthoMat.set(1, 1, 2.0 / (fTop - fBottom));
+ aOrthoMat.set(2, 2, -1.0 * (2.0 / (fFar - fNear)));
+ aOrthoMat.set(0, 3, -1.0 * ((fRight + fLeft) / (fRight - fLeft)));
+ aOrthoMat.set(1, 3, -1.0 * ((fTop + fBottom) / (fTop - fBottom)));
+ aOrthoMat.set(2, 3, -1.0 * ((fFar + fNear) / (fFar - fNear)));
+
+ mpImpl->doMulMatrix(aOrthoMat);
+ }
+
+ void B3DHomMatrix::orientation(B3DPoint aVRP, B3DVector aVPN, B3DVector aVUV)
+ {
+ Impl3DHomMatrix aOrientationMat;
+
+ // translate -VRP
+ aOrientationMat.set(0, 3, -aVRP.getX());
+ aOrientationMat.set(1, 3, -aVRP.getY());
+ aOrientationMat.set(2, 3, -aVRP.getZ());
+
+ // build rotation
+ aVUV.normalize();
+ aVPN.normalize();
+
+ // build x-axis as peroendicular fron aVUV and aVPN
+ B3DVector aRx(aVUV.getPerpendicular(aVPN));
+ aRx.normalize();
+
+ // y-axis perpendicular to that
+ B3DVector aRy(aVPN.getPerpendicular(aRx));
+ aRy.normalize();
+
+ // the calculated normals are the line vectors of the rotation matrix,
+ // set them to create rotation
+ aOrientationMat.set(0, 0, aRx.getX());
+ aOrientationMat.set(0, 1, aRx.getY());
+ aOrientationMat.set(0, 2, aRx.getZ());
+ aOrientationMat.set(1, 0, aRy.getX());
+ aOrientationMat.set(1, 1, aRy.getY());
+ aOrientationMat.set(1, 2, aRy.getZ());
+ aOrientationMat.set(2, 0, aVPN.getX());
+ aOrientationMat.set(2, 1, aVPN.getY());
+ aOrientationMat.set(2, 2, aVPN.getZ());
+
+ mpImpl->doMulMatrix(aOrientationMat);
+ }
+
+ bool B3DHomMatrix::decompose(B3DTuple& rScale, B3DTuple& rTranslate, B3DTuple& rRotate, B3DTuple& rShear) const
+ {
+ // when perspective is used, decompose is not made here
+ if(!mpImpl->isLastLineDefault())
+ return false;
+
+ // If determinant is zero, decomposition is not possible
+ if(0.0 == determinant())
+ return false;
+
+ // isolate translation
+ rTranslate.setX(mpImpl->get(0, 3));
+ rTranslate.setY(mpImpl->get(1, 3));
+ rTranslate.setZ(mpImpl->get(2, 3));
+
+ // correct translate values
+ rTranslate.correctValues();
+
+ // get scale and shear
+ B3DVector aCol0(mpImpl->get(0, 0), mpImpl->get(1, 0), mpImpl->get(2, 0));
+ B3DVector aCol1(mpImpl->get(0, 1), mpImpl->get(1, 1), mpImpl->get(2, 1));
+ B3DVector aCol2(mpImpl->get(0, 2), mpImpl->get(1, 2), mpImpl->get(2, 2));
+ B3DVector aTemp;
+
+ // get ScaleX
+ rScale.setX(aCol0.getLength());
+ aCol0.normalize();
+
+ // get ShearXY
+ rShear.setX(aCol0.scalar(aCol1));
+
+ if(fTools::equalZero(rShear.getX()))
+ {
+ rShear.setX(0.0);
+ }
+ else
+ {
+ aTemp.setX(aCol1.getX() - rShear.getX() * aCol0.getX());
+ aTemp.setY(aCol1.getY() - rShear.getX() * aCol0.getY());
+ aTemp.setZ(aCol1.getZ() - rShear.getX() * aCol0.getZ());
+ aCol1 = aTemp;
+ }
+
+ // get ScaleY
+ rScale.setY(aCol1.getLength());
+ aCol1.normalize();
+
+ const double fShearX(rShear.getX());
+
+ if(!fTools::equalZero(fShearX))
+ {
+ rShear.setX(rShear.getX() / rScale.getY());
+ }
+
+ // get ShearXZ
+ rShear.setY(aCol0.scalar(aCol2));
+
+ if(fTools::equalZero(rShear.getY()))
+ {
+ rShear.setY(0.0);
+ }
+ else
+ {
+ aTemp.setX(aCol2.getX() - rShear.getY() * aCol0.getX());
+ aTemp.setY(aCol2.getY() - rShear.getY() * aCol0.getY());
+ aTemp.setZ(aCol2.getZ() - rShear.getY() * aCol0.getZ());
+ aCol2 = aTemp;
+ }
+
+ // get ShearYZ
+ rShear.setZ(aCol1.scalar(aCol2));
+
+ if(fTools::equalZero(rShear.getZ()))
+ {
+ rShear.setZ(0.0);
+ }
+ else
+ {
+ aTemp.setX(aCol2.getX() - rShear.getZ() * aCol1.getX());
+ aTemp.setY(aCol2.getY() - rShear.getZ() * aCol1.getY());
+ aTemp.setZ(aCol2.getZ() - rShear.getZ() * aCol1.getZ());
+ aCol2 = aTemp;
+ }
+
+ // get ScaleZ
+ rScale.setZ(aCol2.getLength());
+ aCol2.normalize();
+
+ const double fShearY(rShear.getY());
+
+ if(!fTools::equalZero(fShearY))
+ {
+ rShear.setY(rShear.getY() / rScale.getZ());
+ }
+
+ const double fShearZ(rShear.getZ());
+
+ if(!fTools::equalZero(fShearZ))
+ {
+ rShear.setZ(rShear.getZ() / rScale.getZ());
+ }
+
+ // correct shear values
+ rShear.correctValues();
+
+ // Coordinate system flip?
+ if(0.0 > aCol0.scalar(aCol1.getPerpendicular(aCol2)))
+ {
+ rScale = -rScale;
+ aCol0 = -aCol0;
+ aCol1 = -aCol1;
+ aCol2 = -aCol2;
+ }
+
+ // correct scale values
+ rScale.correctValues(1.0);
+
+ // Get rotations
+ {
+ double fy=0;
+ double cy=0;
+
+ if( ::basegfx::fTools::equal( aCol0.getZ(), 1.0 )
+ || aCol0.getZ() > 1.0 )
+ {
+ fy = -F_PI/2.0;
+ cy = 0.0;
+ }
+ else if( ::basegfx::fTools::equal( aCol0.getZ(), -1.0 )
+ || aCol0.getZ() < -1.0 )
+ {
+ fy = F_PI/2.0;
+ cy = 0.0;
+ }
+ else
+ {
+ fy = asin( -aCol0.getZ() );
+ cy = cos(fy);
+ }
+
+ rRotate.setY(fy);
+ if( ::basegfx::fTools::equalZero( cy ) )
+ {
+ if( aCol0.getZ() > 0.0 )
+ rRotate.setX(atan2(-1.0*aCol1.getX(), aCol1.getY()));
+ else
+ rRotate.setX(atan2(aCol1.getX(), aCol1.getY()));
+ rRotate.setZ(0.0);
+ }
+ else
+ {
+ rRotate.setX(atan2(aCol1.getZ(), aCol2.getZ()));
+ rRotate.setZ(atan2(aCol0.getY(), aCol0.getX()));
+ }
+
+ // corrcet rotate values
+ rRotate.correctValues();
+ }
+
+ return true;
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/matrix/makefile.mk b/basegfx/source/matrix/makefile.mk
new file mode 100644
index 000000000000..7cf3ef0961f0
--- /dev/null
+++ b/basegfx/source/matrix/makefile.mk
@@ -0,0 +1,53 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.4 $
+#
+# 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=matrix
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+#ENABLE_EXCEPTIONS=FALSE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/b2dhommatrix.obj \
+ $(SLO)$/b2dhommatrixtools.obj \
+ $(SLO)$/b3dhommatrix.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/numeric/ftools.cxx b/basegfx/source/numeric/ftools.cxx
new file mode 100644
index 000000000000..25e04a8e770e
--- /dev/null
+++ b/basegfx/source/numeric/ftools.cxx
@@ -0,0 +1,41 @@
+/*************************************************************************
+ *
+ * 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: ftools.cxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ // init static member of class fTools
+ double ::basegfx::fTools::mfSmallValue = 0.000000001;
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/numeric/makefile.mk b/basegfx/source/numeric/makefile.mk
new file mode 100644
index 000000000000..fd758b09ede3
--- /dev/null
+++ b/basegfx/source/numeric/makefile.mk
@@ -0,0 +1,51 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.3 $
+#
+# 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=numeric
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+#ENABLE_EXCEPTIONS=FALSE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/ftools.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/pixel/bpixel.cxx b/basegfx/source/pixel/bpixel.cxx
new file mode 100644
index 000000000000..b393c83fad70
--- /dev/null
+++ b/basegfx/source/pixel/bpixel.cxx
@@ -0,0 +1,55 @@
+/*************************************************************************
+ *
+ * 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: bpixel.cxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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/pixel/bpixel.hxx>
+#include <rtl/instance.hxx>
+
+namespace { struct EmptyBPixel : public rtl::Static<basegfx::BPixel, EmptyBPixel> {}; }
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ const BPixel& BPixel::getEmptyBPixel()
+ {
+ return EmptyBPixel::get();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // external operators
+
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/pixel/makefile.mk b/basegfx/source/pixel/makefile.mk
new file mode 100644
index 000000000000..f0d51187e12a
--- /dev/null
+++ b/basegfx/source/pixel/makefile.mk
@@ -0,0 +1,51 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.2 $
+#
+# 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=pixel
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+#ENABLE_EXCEPTIONS=FALSE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/bpixel.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/point/b2dhompoint.cxx b/basegfx/source/point/b2dhompoint.cxx
new file mode 100644
index 000000000000..ccac775e6995
--- /dev/null
+++ b/basegfx/source/point/b2dhompoint.cxx
@@ -0,0 +1,262 @@
+/*************************************************************************
+ *
+ * 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: b2dhompoint.cxx,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/point/b2dhompoint.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ bool B2DHomPoint::implIsHomogenized() const
+ {
+ const double fOne(1.0);
+ return ::basegfx::fTools::equal(fOne, mfW);
+ }
+
+ void B2DHomPoint::implHomogenize()
+ {
+ const double fFactor(1.0 / mfW);
+ maTuple.setX(maTuple.getX() * fFactor);
+ maTuple.setY(maTuple.getY() * fFactor);
+ mfW = 1.0;
+ }
+
+ void B2DHomPoint::implTestAndHomogenize() const
+ {
+ if(!implIsHomogenized())
+ ((B2DHomPoint*)this)->implHomogenize();
+ }
+
+ B2DPoint B2DHomPoint::getB2DPoint() const
+ {
+ implTestAndHomogenize();
+ return B2DPoint(maTuple.getX(), maTuple.getY());
+ }
+
+ double B2DHomPoint::getX() const
+ {
+ implTestAndHomogenize();
+ return maTuple.getX();
+ }
+
+ double B2DHomPoint::getY() const
+ {
+ implTestAndHomogenize();
+ return maTuple.getY();
+ }
+
+ void B2DHomPoint::setX(double fX)
+ {
+ maTuple.setX(implIsHomogenized() ? fX : fX * mfW );
+ }
+
+ void B2DHomPoint::setY(double fY)
+ {
+ maTuple.setY(implIsHomogenized() ? fY : fY * mfW );
+ }
+
+ B2DHomPoint& B2DHomPoint::operator+=( const B2DHomPoint& rPnt )
+ {
+ maTuple.setX(getX() * rPnt.mfW + rPnt.getX() * mfW);
+ maTuple.setY(getY() * rPnt.mfW + rPnt.getY() * mfW);
+ mfW = mfW * rPnt.mfW;
+
+ return *this;
+ }
+
+ B2DHomPoint& B2DHomPoint::operator-=( const B2DHomPoint& rPnt )
+ {
+ maTuple.setX(getX() * rPnt.mfW - rPnt.getX() * mfW);
+ maTuple.setY(getY() * rPnt.mfW - rPnt.getY() * mfW);
+ mfW = mfW * rPnt.mfW;
+
+ return *this;
+ }
+
+ B2DHomPoint& B2DHomPoint::operator*=(double t)
+ {
+ if(!::basegfx::fTools::equalZero(t))
+ {
+ mfW /= t;
+ }
+
+ return *this;
+ }
+
+ B2DHomPoint& B2DHomPoint::operator*=( const B2DHomMatrix& rMat )
+ {
+ const double fTempX( rMat.get(0,0)*maTuple.getX() +
+ rMat.get(0,1)*maTuple.getY() +
+ rMat.get(0,2)*mfW );
+
+ const double fTempY( rMat.get(1,0)*maTuple.getX() +
+ rMat.get(1,1)*maTuple.getY() +
+ rMat.get(1,2)*mfW );
+
+ const double fTempZ( rMat.get(2,0)*maTuple.getX() +
+ rMat.get(2,1)*maTuple.getY() +
+ rMat.get(2,2)*mfW );
+ maTuple.setX( fTempX );
+ maTuple.setY( fTempY );
+ mfW = fTempZ;
+
+ return *this;
+ }
+
+ B2DHomPoint& B2DHomPoint::operator/=(double t)
+ {
+ mfW *= t;
+ return *this;
+ }
+
+ B2DHomPoint& B2DHomPoint::operator-(void)
+ {
+ mfW = -mfW;
+ return *this;
+ }
+
+ bool B2DHomPoint::operator==( const B2DHomPoint& rPnt ) const
+ {
+ implTestAndHomogenize();
+ return (maTuple == rPnt.maTuple);
+ }
+
+ bool B2DHomPoint::operator!=( const B2DHomPoint& rPnt ) const
+ {
+ implTestAndHomogenize();
+ return (maTuple != rPnt.maTuple);
+ }
+
+ B2DHomPoint& B2DHomPoint::operator=( const B2DHomPoint& rPnt )
+ {
+ maTuple = rPnt.maTuple;
+ mfW = rPnt.mfW;
+ return *this;
+ }
+
+ B2DHomPoint minimum(const B2DHomPoint& rVecA, const B2DHomPoint& rVecB)
+ {
+ B2DHomPoint aMin(
+ (rVecB.getX() < rVecA.getX()) ? rVecB.getX() : rVecA.getX(),
+ (rVecB.getY() < rVecA.getY()) ? rVecB.getY() : rVecA.getY());
+ return aMin;
+ }
+
+ B2DHomPoint maximum(const B2DHomPoint& rVecA, const B2DHomPoint& rVecB)
+ {
+ B2DHomPoint aMax(
+ (rVecB.getX() > rVecA.getX()) ? rVecB.getX() : rVecA.getX(),
+ (rVecB.getY() > rVecA.getY()) ? rVecB.getY() : rVecA.getY());
+ return aMax;
+ }
+ B2DHomPoint absolute(const B2DHomPoint& rVec)
+ {
+ B2DHomPoint aAbs(
+ (0.0 > rVec.getX()) ? -rVec.getX() : rVec.getX(),
+ (0.0 > rVec.getY()) ? -rVec.getY() : rVec.getY());
+ return aAbs;
+ }
+
+ B2DHomPoint interpolate(B2DHomPoint& rOld1, B2DHomPoint& rOld2, double t)
+ {
+ B2DHomPoint aInt(
+ ((rOld2.getX() - rOld1.getX()) * t) + rOld1.getX(),
+ ((rOld2.getY() - rOld1.getY()) * t) + rOld1.getY());
+ return aInt;
+ }
+
+ B2DHomPoint average(B2DHomPoint& rOld1, B2DHomPoint& rOld2)
+ {
+ B2DHomPoint aAvg(
+ (rOld1.getX() + rOld2.getX()) * 0.5,
+ (rOld1.getY() + rOld2.getY()) * 0.5);
+ return aAvg;
+ }
+
+ B2DHomPoint average(B2DHomPoint& rOld1, B2DHomPoint& rOld2, B2DHomPoint& rOld3)
+ {
+ B2DHomPoint aAvg(
+ (rOld1.getX() + rOld2.getX() + rOld3.getX()) * (1.0 / 3.0),
+ (rOld1.getY() + rOld2.getY() + rOld3.getY()) * (1.0 / 3.0));
+ return aAvg;
+ }
+
+ B2DHomPoint operator+(const B2DHomPoint& rVecA, const B2DHomPoint& rVecB)
+ {
+ B2DHomPoint aSum(rVecA);
+ aSum += rVecB;
+ return aSum;
+ }
+
+ B2DHomPoint operator-(const B2DHomPoint& rVecA, const B2DHomPoint& rVecB)
+ {
+ B2DHomPoint aSub(rVecA);
+ aSub -= rVecB;
+ return aSub;
+ }
+
+ B2DHomPoint operator*(const B2DHomPoint& rVec, double t)
+ {
+ B2DHomPoint aNew(rVec);
+ aNew *= t;
+ return aNew;
+ }
+
+ B2DHomPoint operator*(double t, const B2DHomPoint& rVec)
+ {
+ B2DHomPoint aNew(rVec);
+ aNew *= t;
+ return aNew;
+ }
+
+ B2DHomPoint operator*( const B2DHomMatrix& rMat, const B2DHomPoint& rPoint )
+ {
+ B2DHomPoint aNew(rPoint);
+ return aNew*=rMat;
+ }
+
+ B2DHomPoint operator/(const B2DHomPoint& rVec, double t)
+ {
+ B2DHomPoint aNew(rVec);
+ aNew /= t;
+ return aNew;
+ }
+
+ B2DHomPoint operator/(double t, const B2DHomPoint& rVec)
+ {
+ B2DHomPoint aNew(rVec);
+ aNew /= t;
+ return aNew;
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/point/b2dpoint.cxx b/basegfx/source/point/b2dpoint.cxx
new file mode 100644
index 000000000000..e9fb1d8aea14
--- /dev/null
+++ b/basegfx/source/point/b2dpoint.cxx
@@ -0,0 +1,88 @@
+/*************************************************************************
+ *
+ * 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: b2dpoint.cxx,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/point/b2dpoint.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ B2DPoint& B2DPoint::operator=( const ::basegfx::B2DTuple& rPoint )
+ {
+ mfX = rPoint.getX();
+ mfY = rPoint.getY();
+ return *this;
+ }
+
+ B2DPoint& B2DPoint::operator*=( const ::basegfx::B2DHomMatrix& rMat )
+ {
+ double fTempX(
+ rMat.get(0, 0) * mfX +
+ rMat.get(0, 1) * mfY +
+ rMat.get(0, 2));
+ double fTempY(
+ rMat.get(1, 0) * mfX +
+ rMat.get(1, 1) * mfY +
+ rMat.get(1, 2));
+
+ if(!rMat.isLastLineDefault())
+ {
+ const double fOne(1.0);
+ const double fTempM(
+ rMat.get(2, 0) * mfX +
+ rMat.get(2, 1) * mfY +
+ rMat.get(2, 2));
+
+ if(!fTools::equalZero(fTempM) && !fTools::equal(fOne, fTempM))
+ {
+ fTempX /= fTempM;
+ fTempY /= fTempM;
+ }
+ }
+
+ mfX = fTempX;
+ mfY = fTempY;
+
+ return *this;
+ }
+
+ B2DPoint operator*( const ::basegfx::B2DHomMatrix& rMat, const B2DPoint& rPoint )
+ {
+ B2DPoint aRes( rPoint );
+ return aRes *= rMat;
+ }
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/point/b2ipoint.cxx b/basegfx/source/point/b2ipoint.cxx
new file mode 100644
index 000000000000..f2017e59dac1
--- /dev/null
+++ b/basegfx/source/point/b2ipoint.cxx
@@ -0,0 +1,79 @@
+/*************************************************************************
+ *
+ * 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: b2ipoint.cxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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/point/b2ipoint.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ B2IPoint& B2IPoint::operator=( const ::basegfx::B2ITuple& rPoint )
+ {
+ mnX = rPoint.getX();
+ mnY = rPoint.getY();
+ return *this;
+ }
+
+ B2IPoint& B2IPoint::operator*=( const ::basegfx::B2DHomMatrix& rMat )
+ {
+ double fTempX(
+ rMat.get(0, 0) * mnX +
+ rMat.get(0, 1) * mnY +
+ rMat.get(0, 2));
+ double fTempY(
+ rMat.get(1, 0) * mnX +
+ rMat.get(1, 1) * mnY +
+ rMat.get(1, 2));
+
+ if(!rMat.isLastLineDefault())
+ {
+ const double fOne(1.0);
+ const double fTempM(
+ rMat.get(2, 0) * mnX +
+ rMat.get(2, 1) * mnY +
+ rMat.get(2, 2));
+
+ if(!fTools::equalZero(fTempM) && !fTools::equal(fOne, fTempM))
+ {
+ fTempX /= fTempM;
+ fTempY /= fTempM;
+ }
+ }
+
+ mnX = fround(fTempX);
+ mnY = fround(fTempY);
+
+ return *this;
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/point/b3dhompoint.cxx b/basegfx/source/point/b3dhompoint.cxx
new file mode 100644
index 000000000000..0a495692e042
--- /dev/null
+++ b/basegfx/source/point/b3dhompoint.cxx
@@ -0,0 +1,47 @@
+/*************************************************************************
+ *
+ * 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: b3dhompoint.cxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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/point/b3dhompoint.hxx>
+
+namespace basegfx
+{
+ void B3DHomPoint::implHomogenize()
+ {
+ const double fFactor(1.0 / mfW);
+ maTuple.setX(maTuple.getX() * fFactor);
+ maTuple.setY(maTuple.getY() * fFactor);
+ maTuple.setZ(maTuple.getZ() * fFactor);
+ mfW = 1.0;
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/point/b3dpoint.cxx b/basegfx/source/point/b3dpoint.cxx
new file mode 100644
index 000000000000..9a46e5767d87
--- /dev/null
+++ b/basegfx/source/point/b3dpoint.cxx
@@ -0,0 +1,88 @@
+/*************************************************************************
+ *
+ * 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: b3dpoint.cxx,v $
+ * $Revision: 1.9 $
+ *
+ * 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/point/b3dpoint.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ B3DPoint& B3DPoint::operator*=( const ::basegfx::B3DHomMatrix& rMat )
+ {
+ double fTempX(
+ rMat.get(0, 0) * mfX +
+ rMat.get(0, 1) * mfY +
+ rMat.get(0, 2) * mfZ +
+ rMat.get(0, 3));
+ double fTempY(
+ rMat.get(1, 0) * mfX +
+ rMat.get(1, 1) * mfY +
+ rMat.get(1, 2) * mfZ +
+ rMat.get(1, 3));
+ double fTempZ(
+ rMat.get(2, 0) * mfX +
+ rMat.get(2, 1) * mfY +
+ rMat.get(2, 2) * mfZ +
+ rMat.get(2, 3));
+
+ if(!rMat.isLastLineDefault())
+ {
+ const double fOne(1.0);
+ const double fTempM(
+ rMat.get(3, 0) * mfX +
+ rMat.get(3, 1) * mfY +
+ rMat.get(3, 2) * mfZ +
+ rMat.get(3, 3));
+
+ if(!fTools::equalZero(fTempM) && !fTools::equal(fOne, fTempM))
+ {
+ fTempX /= fTempM;
+ fTempY /= fTempM;
+ fTempZ /= fTempM;
+ }
+ }
+
+ mfX = fTempX;
+ mfY = fTempY;
+ mfZ = fTempZ;
+
+ return *this;
+ }
+
+ B3DPoint operator*( const ::basegfx::B3DHomMatrix& rMat, const B3DPoint& rPoint )
+ {
+ B3DPoint aRes( rPoint );
+ return aRes *= rMat;
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/point/b3ipoint.cxx b/basegfx/source/point/b3ipoint.cxx
new file mode 100644
index 000000000000..fee39d47b126
--- /dev/null
+++ b/basegfx/source/point/b3ipoint.cxx
@@ -0,0 +1,82 @@
+/*************************************************************************
+ *
+ * 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: b3ipoint.cxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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/point/b3ipoint.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ B3IPoint& B3IPoint::operator*=( const ::basegfx::B3DHomMatrix& rMat )
+ {
+ double fTempX(
+ rMat.get(0, 0) * mnX +
+ rMat.get(0, 1) * mnY +
+ rMat.get(0, 2) * mnZ +
+ rMat.get(0, 3));
+ double fTempY(
+ rMat.get(1, 0) * mnX +
+ rMat.get(1, 1) * mnY +
+ rMat.get(1, 2) * mnZ +
+ rMat.get(1, 3));
+ double fTempZ(
+ rMat.get(2, 0) * mnX +
+ rMat.get(2, 1) * mnY +
+ rMat.get(2, 2) * mnZ +
+ rMat.get(2, 3));
+
+ if(!rMat.isLastLineDefault())
+ {
+ const double fOne(1.0);
+ const double fTempM(
+ rMat.get(3, 0) * mnX +
+ rMat.get(3, 1) * mnY +
+ rMat.get(3, 2) * mnZ +
+ rMat.get(3, 3));
+
+ if(!fTools::equalZero(fTempM) && !fTools::equal(fOne, fTempM))
+ {
+ fTempX /= fTempM;
+ fTempY /= fTempM;
+ fTempZ /= fTempM;
+ }
+ }
+
+ mnX = fround(fTempX);
+ mnY = fround(fTempY);
+ mnZ = fround(fTempZ);
+
+ return *this;
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/point/makefile.mk b/basegfx/source/point/makefile.mk
new file mode 100644
index 000000000000..b6bb56c3b464
--- /dev/null
+++ b/basegfx/source/point/makefile.mk
@@ -0,0 +1,56 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.5 $
+#
+# 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=point
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+#ENABLE_EXCEPTIONS=FALSE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/b2dpoint.obj \
+ $(SLO)$/b2dhompoint.obj \
+ $(SLO)$/b3dpoint.obj \
+ $(SLO)$/b3dhompoint.obj \
+ $(SLO)$/b2ipoint.obj \
+ $(SLO)$/b3ipoint.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/polygon/b2dlinegeometry.cxx b/basegfx/source/polygon/b2dlinegeometry.cxx
new file mode 100644
index 000000000000..1a9264ab769e
--- /dev/null
+++ b/basegfx/source/polygon/b2dlinegeometry.cxx
@@ -0,0 +1,729 @@
+/*************************************************************************
+ *
+ * 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: b2dlinegeometry.cxx,v $
+ * $Revision: 1.7 $
+ *
+ * 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 <cstdio>
+#include <osl/diagnose.h>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ B2DPolyPolygon createAreaGeometryForLineStartEnd(
+ const B2DPolygon& rCandidate,
+ const B2DPolyPolygon& rArrow,
+ bool bStart,
+ double fWidth,
+ double fCandidateLength,
+ double fDockingPosition, // 0->top, 1->bottom
+ double* pConsumedLength)
+ {
+ B2DPolyPolygon aRetval;
+ OSL_ENSURE(rCandidate.count() > 1L, "createAreaGeometryForLineStartEnd: Line polygon has too less points (!)");
+ OSL_ENSURE(rArrow.count() > 0L, "createAreaGeometryForLineStartEnd: Empty arrow PolyPolygon (!)");
+ OSL_ENSURE(fWidth > 0.0, "createAreaGeometryForLineStartEnd: Width too small (!)");
+ OSL_ENSURE(fDockingPosition >= 0.0 && fDockingPosition <= 1.0,
+ "createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0] (!)");
+
+ if(fWidth < 0.0)
+ {
+ fWidth = -fWidth;
+ }
+
+ if(rCandidate.count() > 1 && rArrow.count() && !fTools::equalZero(fWidth))
+ {
+ if(fDockingPosition < 0.0)
+ {
+ fDockingPosition = 0.0;
+ }
+ else if(fDockingPosition > 1.0)
+ {
+ fDockingPosition = 1.0;
+ }
+
+ // init return value from arrow
+ aRetval.append(rArrow);
+
+ // get size of the arrow
+ const B2DRange aArrowSize(getRange(rArrow));
+
+ // build ArrowTransform
+ B2DHomMatrix aArrowTransform;
+
+ // center in X, align with axis in Y
+ aArrowTransform.translate(-aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY());
+
+ // scale to target size
+ const double fArrowScale(fWidth / (aArrowSize.getRange().getX()));
+ aArrowTransform.scale(fArrowScale, fArrowScale);
+
+ // get arrow size in Y
+ B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY());
+ aUpperCenter *= aArrowTransform;
+ const double fArrowYLength(B2DVector(aUpperCenter).getLength());
+
+ // move arrow to have docking position centered
+ aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition);
+
+ // prepare polygon length
+ if(fTools::equalZero(fCandidateLength))
+ {
+ fCandidateLength = getLength(rCandidate);
+ }
+
+ // get the polygon vector we want to plant this arrow on
+ const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition));
+ const B2DVector aHead(rCandidate.getB2DPoint((bStart) ? 0L : rCandidate.count() - 1L));
+ const B2DVector aTail(getPositionAbsolute(rCandidate,
+ (bStart) ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
+
+ // from that vector, take the needed rotation and add rotate for arrow to transformation
+ const B2DVector aTargetDirection(aHead - aTail);
+ const double fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + (90.0 * F_PI180));
+
+ // rotate around docking position
+ aArrowTransform.rotate(fRotation);
+
+ // move arrow docking position to polygon head
+ aArrowTransform.translate(aHead.getX(), aHead.getY());
+
+ // transform retval and close
+ aRetval.transform(aArrowTransform);
+ aRetval.setClosed(true);
+
+ // if pConsumedLength is asked for, fill it
+ if(pConsumedLength)
+ {
+ *pConsumedLength = fConsumedLength;
+ }
+ }
+
+ return aRetval;
+ }
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ // anonymus namespace for local helpers
+ namespace
+ {
+ bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
+ {
+ // isBezier() is true, already tested by caller
+ const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
+
+ if(aEdge.equalZero())
+ {
+ // start and end point the same, but control vectors used -> baloon curve loop
+ // is not a simple edge
+ return false;
+ }
+
+ // get tangentA and scalar with edge
+ const B2DVector aTangentA(rCandidate.getTangent(0.0));
+ const double fScalarAE(aEdge.scalar(aTangentA));
+
+ if(fTools::lessOrEqual(fScalarAE, 0.0))
+ {
+ // angle between TangentA and Edge is bigger or equal 90 degrees
+ return false;
+ }
+
+ // get self-scalars for E and A
+ const double fScalarE(aEdge.scalar(aEdge));
+ const double fScalarA(aTangentA.scalar(aTangentA));
+ const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
+
+ if(fTools::moreOrEqual(fScalarA, fLengthCompareE))
+ {
+ // length of TangentA is more than fMaxPartOfEdge of length of edge
+ return false;
+ }
+
+ if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad))
+ {
+ // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos
+ return false;
+ }
+
+ // get tangentB and scalar with edge
+ const B2DVector aTangentB(rCandidate.getTangent(1.0));
+ const double fScalarBE(aEdge.scalar(aTangentB));
+
+ if(fTools::lessOrEqual(fScalarBE, 0.0))
+ {
+ // angle between TangentB and Edge is bigger or equal 90 degrees
+ return false;
+ }
+
+ // get self-scalar for B
+ const double fScalarB(aTangentB.scalar(aTangentB));
+
+ if(fTools::moreOrEqual(fScalarB, fLengthCompareE))
+ {
+ // length of TangentB is more than fMaxPartOfEdge of length of edge
+ return false;
+ }
+
+ if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad))
+ {
+ // angle between TangentB and Edge is bigger or equal defined by fMaxCos
+ return false;
+ }
+
+ return true;
+ }
+
+ void impSubdivideToSimple(const B2DCubicBezier& rCandidate, B2DPolygon& rTarget, double fMaxCosQuad, double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth)
+ {
+ if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad))
+ {
+ rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint());
+ }
+ else
+ {
+ B2DCubicBezier aLeft, aRight;
+ rCandidate.split(0.5, &aLeft, &aRight);
+
+ impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
+ impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
+ }
+ }
+
+ B2DPolygon subdivideToSimple(const B2DPolygon& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(rCandidate.areControlPointsUsed() && nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+ B2DPolygon aRetval;
+ B2DCubicBezier aEdge;
+
+ // prepare edge for loop
+ aEdge.setStartPoint(rCandidate.getB2DPoint(0));
+ aRetval.append(aEdge.getStartPoint());
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ // fill B2DCubicBezier
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
+ aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+
+ // get rid of unnecessary bezier segments
+ aEdge.testAndSolveTrivialBezier();
+
+ if(aEdge.isBezier())
+ {
+ // before splitting recursively with internal simple criteria, use
+ // ExtremumPosFinder to remove those
+ ::std::vector< double > aExtremumPositions;
+
+ aExtremumPositions.reserve(4);
+ aEdge.getAllExtremumPositions(aExtremumPositions);
+
+ const sal_uInt32 nCount(aExtremumPositions.size());
+
+ if(nCount)
+ {
+ if(nCount > 1)
+ {
+ // create order from left to right
+ ::std::sort(aExtremumPositions.begin(), aExtremumPositions.end());
+ }
+
+ for(sal_uInt32 b(0); b < nCount;)
+ {
+ // split aEdge at next split pos
+ B2DCubicBezier aLeft;
+ const double fSplitPos(aExtremumPositions[b++]);
+
+ aEdge.split(fSplitPos, &aLeft, &aEdge);
+ aLeft.testAndSolveTrivialBezier();
+
+ // consume left part
+ if(aLeft.isBezier())
+ {
+ impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
+ }
+ else
+ {
+ aRetval.append(aLeft.getEndPoint());
+ }
+
+ if(b < nCount)
+ {
+ // correct the remaining split positions to fit to shortened aEdge
+ const double fScaleFactor(1.0 / (1.0 - fSplitPos));
+
+ for(sal_uInt32 c(b); c < nCount; c++)
+ {
+ aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor;
+ }
+ }
+ }
+
+ // test the shortened rest of aEdge
+ aEdge.testAndSolveTrivialBezier();
+
+ // consume right part
+ if(aEdge.isBezier())
+ {
+ impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
+ }
+ else
+ {
+ aRetval.append(aEdge.getEndPoint());
+ }
+ }
+ else
+ {
+ impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
+ }
+ }
+ else
+ {
+ // straight edge, add point
+ aRetval.append(aEdge.getEndPoint());
+ }
+
+ // prepare edge for next step
+ aEdge.setStartPoint(aEdge.getEndPoint());
+ }
+
+ // copy closed flag and check for double points
+ aRetval.setClosed(rCandidate.isClosed());
+ aRetval.removeDoublePoints();
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ B2DPolygon createAreaGeometryForEdge(const B2DCubicBezier& rEdge, double fHalfLineWidth)
+ {
+ // create polygon for edge
+ // Unfortunately, while it would be geometrically correct to not add
+ // the in-between points EdgeEnd and EdgeStart, it leads to rounding
+ // errors when converting to integer polygon coordinates for painting
+ if(rEdge.isBezier())
+ {
+ // prepare target and data common for upper and lower
+ B2DPolygon aBezierPolygon;
+ const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
+ const double fEdgeLength(aPureEdgeVector.getLength());
+ const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
+ const B2DVector aTangentA(rEdge.getTangent(0.0));
+ const B2DVector aTangentB(rEdge.getTangent(1.0));
+
+ // create upper edge.
+ {
+ // create displacement vectors and check if they cut
+ const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * -fHalfLineWidth);
+ const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * -fHalfLineWidth);
+ double fCut(0.0);
+ const tools::CutFlagValue aCut(tools::findCut(
+ rEdge.getStartPoint(), aPerpendStart,
+ rEdge.getEndPoint(), aPerpendEnd,
+ CUTFLAG_ALL, &fCut));
+
+ if(CUTFLAG_NONE != aCut)
+ {
+ // calculate cut point and add
+ const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStart * fCut));
+ aBezierPolygon.append(aCutPoint);
+ }
+ else
+ {
+ // create scaled bezier segment
+ const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStart);
+ const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEnd);
+ const B2DVector aEdge(aEnd - aStart);
+ const double fLength(aEdge.getLength());
+ const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
+ const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint());
+ const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint());
+
+ aBezierPolygon.append(aStart);
+ aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
+ }
+ }
+
+ // append original in-between point
+ aBezierPolygon.append(rEdge.getEndPoint());
+
+ // create lower edge.
+ {
+ // create displacement vectors and check if they cut
+ const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * fHalfLineWidth);
+ const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * fHalfLineWidth);
+ double fCut(0.0);
+ const tools::CutFlagValue aCut(tools::findCut(
+ rEdge.getEndPoint(), aPerpendEnd,
+ rEdge.getStartPoint(), aPerpendStart,
+ CUTFLAG_ALL, &fCut));
+
+ if(CUTFLAG_NONE != aCut)
+ {
+ // calculate cut point and add
+ const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEnd * fCut));
+ aBezierPolygon.append(aCutPoint);
+ }
+ else
+ {
+ // create scaled bezier segment
+ const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEnd);
+ const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStart);
+ const B2DVector aEdge(aEnd - aStart);
+ const double fLength(aEdge.getLength());
+ const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
+ const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint());
+ const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint());
+
+ aBezierPolygon.append(aStart);
+ aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
+ }
+ }
+
+ // append original in-between point
+ aBezierPolygon.append(rEdge.getStartPoint());
+
+ // close and return
+ aBezierPolygon.setClosed(true);
+ return aBezierPolygon;
+ }
+ else
+ {
+ // #i101491# emulate rEdge.getTangent call which applies a factor of 0.3 to the
+ // full-length edge vector to have numerically exactly the same results as in the
+ // createAreaGeometryForJoin implementation
+ const B2DVector aEdgeTangent((rEdge.getEndPoint() - rEdge.getStartPoint()) * 0.3);
+ const B2DVector aPerpendEdgeVector(getNormalizedPerpendicular(aEdgeTangent) * fHalfLineWidth);
+ B2DPolygon aEdgePolygon;
+
+ // create upper edge
+ aEdgePolygon.append(rEdge.getStartPoint() - aPerpendEdgeVector);
+ aEdgePolygon.append(rEdge.getEndPoint() - aPerpendEdgeVector);
+
+ // append original in-between point
+ aEdgePolygon.append(rEdge.getEndPoint());
+
+ // create lower edge
+ aEdgePolygon.append(rEdge.getEndPoint() + aPerpendEdgeVector);
+ aEdgePolygon.append(rEdge.getStartPoint() + aPerpendEdgeVector);
+
+ // append original in-between point
+ aEdgePolygon.append(rEdge.getStartPoint());
+
+ // close and return
+ aEdgePolygon.setClosed(true);
+ return aEdgePolygon;
+ }
+ }
+
+ B2DPolygon createAreaGeometryForJoin(
+ const B2DVector& rTangentPrev,
+ const B2DVector& rTangentEdge,
+ const B2DVector& rPerpendPrev,
+ const B2DVector& rPerpendEdge,
+ const B2DPoint& rPoint,
+ double fHalfLineWidth,
+ B2DLineJoin eJoin,
+ double fMiterMinimumAngle)
+ {
+ OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)");
+ OSL_ENSURE(B2DLINEJOIN_NONE != eJoin, "createAreaGeometryForJoin: B2DLINEJOIN_NONE not allowed (!)");
+
+ // LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint
+ B2DPolygon aEdgePolygon;
+ const B2DPoint aStartPoint(rPoint + rPerpendPrev);
+ const B2DPoint aEndPoint(rPoint + rPerpendEdge);
+
+ // test if for Miter, the angle is too small and the fallback
+ // to bevel needs to be used
+ if(B2DLINEJOIN_MITER == eJoin)
+ {
+ const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
+
+ if((F_PI - fAngle) < fMiterMinimumAngle)
+ {
+ // fallback to bevel
+ eJoin = B2DLINEJOIN_BEVEL;
+ }
+ }
+
+ switch(eJoin)
+ {
+ case B2DLINEJOIN_MITER :
+ {
+ aEdgePolygon.append(aEndPoint);
+ aEdgePolygon.append(rPoint);
+ aEdgePolygon.append(aStartPoint);
+
+ // Look for the cut point between start point along rTangentPrev and
+ // end point along rTangentEdge. -rTangentEdge should be used, but since
+ // the cut value is used for interpolating along the first edge, the negation
+ // is not needed since the same fCut will be found on the first edge.
+ // If it exists, insert it to complete the mitered fill polygon.
+ double fCutPos(0.0);
+ tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos);
+
+ if(0.0 != fCutPos)
+ {
+ const B2DPoint aCutPoint(interpolate(aStartPoint, aStartPoint + rTangentPrev, fCutPos));
+ aEdgePolygon.append(aCutPoint);
+ }
+
+ break;
+ }
+ case B2DLINEJOIN_ROUND :
+ {
+ // use tooling to add needed EllipseSegment
+ double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX()));
+ double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX()));
+
+ // atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI]
+ if(fAngleStart < 0.0)
+ {
+ fAngleStart += F_2PI;
+ }
+
+ if(fAngleEnd < 0.0)
+ {
+ fAngleEnd += F_2PI;
+ }
+
+ const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd));
+
+ if(aBow.count() > 1)
+ {
+ // #i101491#
+ // use the original start/end positions; the ones from bow creation may be numerically
+ // different due to their different creation. To guarantee good merging quality with edges
+ // and edge roundings (and to reduce point count)
+ aEdgePolygon = aBow;
+ aEdgePolygon.setB2DPoint(0, aStartPoint);
+ aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
+ aEdgePolygon.append(rPoint);
+
+ break;
+ }
+ else
+ {
+ // wanted fall-through to default
+ }
+ }
+ default: // B2DLINEJOIN_BEVEL
+ {
+ aEdgePolygon.append(aEndPoint);
+ aEdgePolygon.append(rPoint);
+ aEdgePolygon.append(aStartPoint);
+
+ break;
+ }
+ }
+
+ // create last polygon part for edge
+ aEdgePolygon.setClosed(true);
+
+ return aEdgePolygon;
+ }
+ } // end of anonymus namespace
+
+ namespace tools
+ {
+ B2DPolyPolygon createAreaGeometry(
+ const B2DPolygon& rCandidate,
+ double fHalfLineWidth,
+ B2DLineJoin eJoin,
+ double fMaxAllowedAngle,
+ double fMaxPartOfEdge,
+ double fMiterMinimumAngle)
+ {
+ if(fMaxAllowedAngle > F_PI2)
+ {
+ fMaxAllowedAngle = F_PI2;
+ }
+ else if(fMaxAllowedAngle < 0.01 * F_PI2)
+ {
+ fMaxAllowedAngle = 0.01 * F_PI2;
+ }
+
+ if(fMaxPartOfEdge > 1.0)
+ {
+ fMaxPartOfEdge = 1.0;
+ }
+ else if(fMaxPartOfEdge < 0.01)
+ {
+ fMaxPartOfEdge = 0.01;
+ }
+
+ if(fMiterMinimumAngle > F_PI)
+ {
+ fMiterMinimumAngle = F_PI;
+ }
+ else if(fMiterMinimumAngle < 0.01 * F_PI)
+ {
+ fMiterMinimumAngle = 0.01 * F_PI;
+ }
+
+ B2DPolygon aCandidate(rCandidate);
+ const double fMaxCos(cos(fMaxAllowedAngle));
+
+ aCandidate.removeDoublePoints();
+ aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge);
+
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount)
+ {
+ B2DPolyPolygon aRetval;
+ const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin);
+ const bool bIsClosed(aCandidate.isClosed());
+ const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
+
+ if(nEdgeCount)
+ {
+ B2DCubicBezier aEdge;
+ B2DCubicBezier aPrev;
+
+ // prepare edge
+ aEdge.setStartPoint(aCandidate.getB2DPoint(0));
+
+ if(bIsClosed && bEventuallyCreateLineJoin)
+ {
+ // prepare previous edge
+ const sal_uInt32 nPrevIndex(nPointCount - 1);
+ aPrev.setStartPoint(aCandidate.getB2DPoint(nPrevIndex));
+ aPrev.setControlPointA(aCandidate.getNextControlPoint(nPrevIndex));
+ aPrev.setControlPointB(aCandidate.getPrevControlPoint(0));
+ aPrev.setEndPoint(aEdge.getStartPoint());
+ }
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ // fill current Edge
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aEdge.setControlPointA(aCandidate.getNextControlPoint(a));
+ aEdge.setControlPointB(aCandidate.getPrevControlPoint(nNextIndex));
+ aEdge.setEndPoint(aCandidate.getB2DPoint(nNextIndex));
+
+ // check and create linejoin
+ if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a))
+ {
+ const B2DVector aTangentPrev(aPrev.getTangent(1.0));
+ const B2DVector aTangentEdge(aEdge.getTangent(0.0));
+ B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge));
+
+ if(ORIENTATION_NEUTRAL == aOrientation)
+ {
+ // they are parallell or empty; if they are both not zero and point
+ // in opposite direction, a half-circle is needed
+ if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
+ {
+ const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
+
+ if(fTools::equal(fAngle, F_PI))
+ {
+ // for half-circle production, fallback to positive
+ // orientation
+ aOrientation = ORIENTATION_POSITIVE;
+ }
+ }
+ }
+
+ if(ORIENTATION_POSITIVE == aOrientation)
+ {
+ const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * -fHalfLineWidth);
+ const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * -fHalfLineWidth);
+
+ aRetval.append(createAreaGeometryForJoin(
+ aTangentPrev, aTangentEdge,
+ aPerpendPrev, aPerpendEdge,
+ aEdge.getStartPoint(), fHalfLineWidth,
+ eJoin, fMiterMinimumAngle));
+ }
+ else if(ORIENTATION_NEGATIVE == aOrientation)
+ {
+ const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * fHalfLineWidth);
+ const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * fHalfLineWidth);
+
+ aRetval.append(createAreaGeometryForJoin(
+ aTangentEdge, aTangentPrev,
+ aPerpendEdge, aPerpendPrev,
+ aEdge.getStartPoint(), fHalfLineWidth,
+ eJoin, fMiterMinimumAngle));
+ }
+ }
+
+ // create geometry for edge
+ aRetval.append(createAreaGeometryForEdge(aEdge, fHalfLineWidth));
+
+ // prepare next step
+ if(bEventuallyCreateLineJoin)
+ {
+ aPrev = aEdge;
+ }
+
+ aEdge.setStartPoint(aEdge.getEndPoint());
+ }
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return B2DPolyPolygon(rCandidate);
+ }
+ }
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/polygon/b2dpolygon.cxx b/basegfx/source/polygon/b2dpolygon.cxx
new file mode 100644
index 000000000000..48d00ddcec7d
--- /dev/null
+++ b/basegfx/source/polygon/b2dpolygon.cxx
@@ -0,0 +1,1594 @@
+/*************************************************************************
+ *
+ * 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: b2dpolygon.cxx,v $
+ *
+ * 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/polygon/b2dpolygon.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <rtl/instance.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <boost/scoped_ptr.hpp>
+#include <vector>
+#include <algorithm>
+
+//////////////////////////////////////////////////////////////////////////////
+
+class CoordinateData2D
+{
+ basegfx::B2DPoint maPoint;
+
+public:
+ CoordinateData2D()
+ : maPoint()
+ {}
+
+ explicit CoordinateData2D(const basegfx::B2DPoint& rData)
+ : maPoint(rData)
+ {}
+
+ const basegfx::B2DPoint& getCoordinate() const
+ {
+ return maPoint;
+ }
+
+ void setCoordinate(const basegfx::B2DPoint& rValue)
+ {
+ if(rValue != maPoint)
+ maPoint = rValue;
+ }
+
+ bool operator==(const CoordinateData2D& rData ) const
+ {
+ return (maPoint == rData.getCoordinate());
+ }
+
+ void transform(const basegfx::B2DHomMatrix& rMatrix)
+ {
+ maPoint *= rMatrix;
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class CoordinateDataArray2D
+{
+ typedef ::std::vector< CoordinateData2D > CoordinateData2DVector;
+
+ CoordinateData2DVector maVector;
+
+public:
+ explicit CoordinateDataArray2D(sal_uInt32 nCount)
+ : maVector(nCount)
+ {
+ }
+
+ explicit CoordinateDataArray2D(const CoordinateDataArray2D& rOriginal)
+ : maVector(rOriginal.maVector)
+ {
+ }
+
+ CoordinateDataArray2D(const CoordinateDataArray2D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : maVector(rOriginal.maVector.begin() + nIndex, rOriginal.maVector.begin() + (nIndex + nCount))
+ {
+ }
+
+ sal_uInt32 count() const
+ {
+ return maVector.size();
+ }
+
+ bool operator==(const CoordinateDataArray2D& rCandidate) const
+ {
+ return (maVector == rCandidate.maVector);
+ }
+
+ const basegfx::B2DPoint& getCoordinate(sal_uInt32 nIndex) const
+ {
+ return maVector[nIndex].getCoordinate();
+ }
+
+ void setCoordinate(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue)
+ {
+ maVector[nIndex].setCoordinate(rValue);
+ }
+
+ void reserve(sal_uInt32 nCount)
+ {
+ maVector.reserve(nCount);
+ }
+
+ void append(const CoordinateData2D& rValue)
+ {
+ maVector.push_back(rValue);
+ }
+
+ void insert(sal_uInt32 nIndex, const CoordinateData2D& rValue, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // add nCount copies of rValue
+ CoordinateData2DVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ maVector.insert(aIndex, nCount, rValue);
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const CoordinateDataArray2D& rSource)
+ {
+ const sal_uInt32 nCount(rSource.maVector.size());
+
+ if(nCount)
+ {
+ // insert data
+ CoordinateData2DVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ CoordinateData2DVector::const_iterator aStart(rSource.maVector.begin());
+ CoordinateData2DVector::const_iterator aEnd(rSource.maVector.end());
+ maVector.insert(aIndex, aStart, aEnd);
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // remove point data
+ CoordinateData2DVector::iterator aStart(maVector.begin());
+ aStart += nIndex;
+ const CoordinateData2DVector::iterator aEnd(aStart + nCount);
+ maVector.erase(aStart, aEnd);
+ }
+ }
+
+ void flip(bool bIsClosed)
+ {
+ if(maVector.size() > 1)
+ {
+ // to keep the same point at index 0, just flip all points except the
+ // first one when closed
+ const sal_uInt32 nHalfSize(bIsClosed ? (maVector.size() - 1) >> 1 : maVector.size() >> 1);
+ CoordinateData2DVector::iterator aStart(bIsClosed ? maVector.begin() + 1 : maVector.begin());
+ CoordinateData2DVector::iterator aEnd(maVector.end() - 1);
+
+ for(sal_uInt32 a(0); a < nHalfSize; a++)
+ {
+ ::std::swap(*aStart, *aEnd);
+ aStart++;
+ aEnd--;
+ }
+ }
+ }
+
+ void removeDoublePointsAtBeginEnd()
+ {
+ // remove from end as long as there are at least two points
+ // and begin/end are equal
+ while((maVector.size() > 1) && (maVector[0] == maVector[maVector.size() - 1]))
+ {
+ maVector.pop_back();
+ }
+ }
+
+ void removeDoublePointsWholeTrack()
+ {
+ sal_uInt32 nIndex(0);
+
+ // test as long as there are at least two points and as long as the index
+ // is smaller or equal second last point
+ while((maVector.size() > 1) && (nIndex <= maVector.size() - 2))
+ {
+ if(maVector[nIndex] == maVector[nIndex + 1])
+ {
+ // if next is same as index, delete next
+ maVector.erase(maVector.begin() + (nIndex + 1));
+ }
+ else
+ {
+ // if different, step forward
+ nIndex++;
+ }
+ }
+ }
+
+ void transform(const basegfx::B2DHomMatrix& rMatrix)
+ {
+ CoordinateData2DVector::iterator aStart(maVector.begin());
+ CoordinateData2DVector::iterator aEnd(maVector.end());
+
+ for(; aStart != aEnd; aStart++)
+ {
+ aStart->transform(rMatrix);
+ }
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class ControlVectorPair2D
+{
+ basegfx::B2DVector maPrevVector;
+ basegfx::B2DVector maNextVector;
+
+public:
+ const basegfx::B2DVector& getPrevVector() const
+ {
+ return maPrevVector;
+ }
+
+ void setPrevVector(const basegfx::B2DVector& rValue)
+ {
+ if(rValue != maPrevVector)
+ maPrevVector = rValue;
+ }
+
+ const basegfx::B2DVector& getNextVector() const
+ {
+ return maNextVector;
+ }
+
+ void setNextVector(const basegfx::B2DVector& rValue)
+ {
+ if(rValue != maNextVector)
+ maNextVector = rValue;
+ }
+
+ bool operator==(const ControlVectorPair2D& rData) const
+ {
+ return (maPrevVector == rData.getPrevVector() && maNextVector == rData.getNextVector());
+ }
+
+ void flip()
+ {
+ ::std::swap(maPrevVector, maNextVector);
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class ControlVectorArray2D
+{
+ typedef ::std::vector< ControlVectorPair2D > ControlVectorPair2DVector;
+
+ ControlVectorPair2DVector maVector;
+ sal_uInt32 mnUsedVectors;
+
+public:
+ explicit ControlVectorArray2D(sal_uInt32 nCount)
+ : maVector(nCount),
+ mnUsedVectors(0)
+ {}
+
+ ControlVectorArray2D(const ControlVectorArray2D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : maVector(),
+ mnUsedVectors(0)
+ {
+ ControlVectorPair2DVector::const_iterator aStart(rOriginal.maVector.begin());
+ aStart += nIndex;
+ ControlVectorPair2DVector::const_iterator aEnd(aStart);
+ aEnd += nCount;
+ maVector.reserve(nCount);
+
+ for(; aStart != aEnd; aStart++)
+ {
+ if(!aStart->getPrevVector().equalZero())
+ mnUsedVectors++;
+
+ if(!aStart->getNextVector().equalZero())
+ mnUsedVectors++;
+
+ maVector.push_back(*aStart);
+ }
+ }
+
+ sal_uInt32 count() const
+ {
+ return maVector.size();
+ }
+
+ bool operator==(const ControlVectorArray2D& rCandidate) const
+ {
+ return (maVector == rCandidate.maVector);
+ }
+
+ bool isUsed() const
+ {
+ return (0 != mnUsedVectors);
+ }
+
+ const basegfx::B2DVector& getPrevVector(sal_uInt32 nIndex) const
+ {
+ return maVector[nIndex].getPrevVector();
+ }
+
+ void setPrevVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
+ {
+ bool bWasUsed(mnUsedVectors && !maVector[nIndex].getPrevVector().equalZero());
+ bool bIsUsed(!rValue.equalZero());
+
+ if(bWasUsed)
+ {
+ if(bIsUsed)
+ {
+ maVector[nIndex].setPrevVector(rValue);
+ }
+ else
+ {
+ maVector[nIndex].setPrevVector(basegfx::B2DVector::getEmptyVector());
+ mnUsedVectors--;
+ }
+ }
+ else
+ {
+ if(bIsUsed)
+ {
+ maVector[nIndex].setPrevVector(rValue);
+ mnUsedVectors++;
+ }
+ }
+ }
+
+ const basegfx::B2DVector& getNextVector(sal_uInt32 nIndex) const
+ {
+ return maVector[nIndex].getNextVector();
+ }
+
+ void setNextVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
+ {
+ bool bWasUsed(mnUsedVectors && !maVector[nIndex].getNextVector().equalZero());
+ bool bIsUsed(!rValue.equalZero());
+
+ if(bWasUsed)
+ {
+ if(bIsUsed)
+ {
+ maVector[nIndex].setNextVector(rValue);
+ }
+ else
+ {
+ maVector[nIndex].setNextVector(basegfx::B2DVector::getEmptyVector());
+ mnUsedVectors--;
+ }
+ }
+ else
+ {
+ if(bIsUsed)
+ {
+ maVector[nIndex].setNextVector(rValue);
+ mnUsedVectors++;
+ }
+ }
+ }
+
+ void append(const ControlVectorPair2D& rValue)
+ {
+ maVector.push_back(rValue);
+
+ if(!rValue.getPrevVector().equalZero())
+ mnUsedVectors += 1;
+
+ if(!rValue.getNextVector().equalZero())
+ mnUsedVectors += 1;
+ }
+
+ void insert(sal_uInt32 nIndex, const ControlVectorPair2D& rValue, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // add nCount copies of rValue
+ ControlVectorPair2DVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ maVector.insert(aIndex, nCount, rValue);
+
+ if(!rValue.getPrevVector().equalZero())
+ mnUsedVectors += nCount;
+
+ if(!rValue.getNextVector().equalZero())
+ mnUsedVectors += nCount;
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const ControlVectorArray2D& rSource)
+ {
+ const sal_uInt32 nCount(rSource.maVector.size());
+
+ if(nCount)
+ {
+ // insert data
+ ControlVectorPair2DVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ ControlVectorPair2DVector::const_iterator aStart(rSource.maVector.begin());
+ ControlVectorPair2DVector::const_iterator aEnd(rSource.maVector.end());
+ maVector.insert(aIndex, aStart, aEnd);
+
+ for(; aStart != aEnd; aStart++)
+ {
+ if(!aStart->getPrevVector().equalZero())
+ mnUsedVectors++;
+
+ if(!aStart->getNextVector().equalZero())
+ mnUsedVectors++;
+ }
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ const ControlVectorPair2DVector::iterator aDeleteStart(maVector.begin() + nIndex);
+ const ControlVectorPair2DVector::iterator aDeleteEnd(aDeleteStart + nCount);
+ ControlVectorPair2DVector::const_iterator aStart(aDeleteStart);
+
+ for(; mnUsedVectors && aStart != aDeleteEnd; aStart++)
+ {
+ if(!aStart->getPrevVector().equalZero())
+ mnUsedVectors--;
+
+ if(mnUsedVectors && !aStart->getNextVector().equalZero())
+ mnUsedVectors--;
+ }
+
+ // remove point data
+ maVector.erase(aDeleteStart, aDeleteEnd);
+ }
+ }
+
+ void flip(bool bIsClosed)
+ {
+ if(maVector.size() > 1)
+ {
+ // to keep the same point at index 0, just flip all points except the
+ // first one when closed
+ const sal_uInt32 nHalfSize(bIsClosed ? (maVector.size() - 1) >> 1 : maVector.size() >> 1);
+ ControlVectorPair2DVector::iterator aStart(bIsClosed ? maVector.begin() + 1 : maVector.begin());
+ ControlVectorPair2DVector::iterator aEnd(maVector.end() - 1);
+
+ for(sal_uInt32 a(0); a < nHalfSize; a++)
+ {
+ // swap Prev and Next
+ aStart->flip();
+ aEnd->flip();
+
+ // swap entries
+ ::std::swap(*aStart, *aEnd);
+
+ aStart++;
+ aEnd--;
+ }
+
+ if(aStart == aEnd)
+ {
+ // swap Prev and Next at middle element (if exists)
+ aStart->flip();
+ }
+
+ if(bIsClosed)
+ {
+ // swap Prev and Next at start element
+ maVector.begin()->flip();
+ }
+ }
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class ImplBufferedData
+{
+private:
+ // Possibility to hold the last subdivision
+ boost::scoped_ptr< basegfx::B2DPolygon > mpDefaultSubdivision;
+
+ // Possibility to hold the last B2DRange calculation
+ boost::scoped_ptr< basegfx::B2DRange > mpB2DRange;
+
+public:
+ ImplBufferedData()
+ : mpDefaultSubdivision(),
+ mpB2DRange()
+ {}
+
+ const basegfx::B2DPolygon& getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon& rSource) const
+ {
+ if(!mpDefaultSubdivision)
+ {
+ const_cast< ImplBufferedData* >(this)->mpDefaultSubdivision.reset(new basegfx::B2DPolygon(basegfx::tools::adaptiveSubdivideByCount(rSource, 9)));
+ }
+
+ return *mpDefaultSubdivision;
+ }
+
+ const basegfx::B2DRange& getB2DRange(const basegfx::B2DPolygon& rSource) const
+ {
+ if(!mpB2DRange)
+ {
+ basegfx::B2DRange aNewRange;
+ const sal_uInt32 nPointCount(rSource.count());
+
+ if(nPointCount)
+ {
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ aNewRange.expand(rSource.getB2DPoint(a));
+ }
+
+ if(rSource.areControlPointsUsed())
+ {
+ const sal_uInt32 nEdgeCount(rSource.isClosed() ? nPointCount : nPointCount - 1);
+
+ if(nEdgeCount)
+ {
+ basegfx::B2DCubicBezier aEdge;
+ aEdge.setStartPoint(rSource.getB2DPoint(0));
+
+ for(sal_uInt32 b(0); b < nEdgeCount; b++)
+ {
+ const sal_uInt32 nNextIndex((b + 1) % nPointCount);
+ aEdge.setControlPointA(rSource.getNextControlPoint(b));
+ aEdge.setControlPointB(rSource.getPrevControlPoint(nNextIndex));
+ aEdge.setEndPoint(rSource.getB2DPoint(nNextIndex));
+
+ if(aEdge.isBezier())
+ {
+ const basegfx::B2DRange aBezierRangeWithControlPoints(aEdge.getRange());
+
+ if(!aNewRange.isInside(aBezierRangeWithControlPoints))
+ {
+ // the range with control points of the current edge is not completely
+ // inside the current range without control points. Expand current range by
+ // subdividing the bezier segment.
+ // Ideal here is a subdivision at the extreme values, so use
+ // getAllExtremumPositions to get all extremas in one run
+ ::std::vector< double > aExtremas;
+
+ aExtremas.reserve(4);
+ aEdge.getAllExtremumPositions(aExtremas);
+
+ const sal_uInt32 nExtremaCount(aExtremas.size());
+
+ for(sal_uInt32 c(0); c < nExtremaCount; c++)
+ {
+ aNewRange.expand(aEdge.interpolatePoint(aExtremas[c]));
+ }
+ }
+ }
+
+ // prepare next edge
+ aEdge.setStartPoint(aEdge.getEndPoint());
+ }
+ }
+ }
+ }
+
+ const_cast< ImplBufferedData* >(this)->mpB2DRange.reset(new basegfx::B2DRange(aNewRange));
+ }
+
+ return *mpB2DRange;
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class ImplB2DPolygon
+{
+private:
+ // The point vector. This vector exists always and defines the
+ // count of members.
+ CoordinateDataArray2D maPoints;
+
+ // The control point vectors. This vectors are created on demand
+ // and may be zero.
+ boost::scoped_ptr< ControlVectorArray2D > mpControlVector;
+
+ // buffered data for e.g. default subdivision and range
+ boost::scoped_ptr< ImplBufferedData > mpBufferedData;
+
+ // flag which decides if this polygon is opened or closed
+ bool mbIsClosed;
+
+public:
+ const basegfx::B2DPolygon& getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon& rSource) const
+ {
+ if(!mpControlVector || !mpControlVector->isUsed())
+ {
+ return rSource;
+ }
+
+ if(!mpBufferedData)
+ {
+ const_cast< ImplB2DPolygon* >(this)->mpBufferedData.reset(new ImplBufferedData);
+ }
+
+ return mpBufferedData->getDefaultAdaptiveSubdivision(rSource);
+ }
+
+ const basegfx::B2DRange& getB2DRange(const basegfx::B2DPolygon& rSource) const
+ {
+ if(!mpBufferedData)
+ {
+ const_cast< ImplB2DPolygon* >(this)->mpBufferedData.reset(new ImplBufferedData);
+ }
+
+ return mpBufferedData->getB2DRange(rSource);
+ }
+
+ ImplB2DPolygon()
+ : maPoints(0),
+ mpControlVector(),
+ mpBufferedData(),
+ mbIsClosed(false)
+ {}
+
+ ImplB2DPolygon(const ImplB2DPolygon& rToBeCopied)
+ : maPoints(rToBeCopied.maPoints),
+ mpControlVector(),
+ mpBufferedData(),
+ mbIsClosed(rToBeCopied.mbIsClosed)
+ {
+ // complete initialization using copy
+ if(rToBeCopied.mpControlVector && rToBeCopied.mpControlVector->isUsed())
+ {
+ mpControlVector.reset( new ControlVectorArray2D(*rToBeCopied.mpControlVector) );
+ }
+ }
+
+ ImplB2DPolygon(const ImplB2DPolygon& rToBeCopied, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : maPoints(rToBeCopied.maPoints, nIndex, nCount),
+ mpControlVector(),
+ mpBufferedData(),
+ mbIsClosed(rToBeCopied.mbIsClosed)
+ {
+ // complete initialization using partly copy
+ if(rToBeCopied.mpControlVector && rToBeCopied.mpControlVector->isUsed())
+ {
+ mpControlVector.reset( new ControlVectorArray2D(*rToBeCopied.mpControlVector, nIndex, nCount) );
+
+ if(!mpControlVector->isUsed())
+ mpControlVector.reset();
+ }
+ }
+
+ ImplB2DPolygon& operator=( const ImplB2DPolygon& rToBeCopied )
+ {
+ maPoints = rToBeCopied.maPoints;
+ mpControlVector.reset();
+ mpBufferedData.reset();
+ mbIsClosed = rToBeCopied.mbIsClosed;
+
+ // complete initialization using copy
+ if(rToBeCopied.mpControlVector && rToBeCopied.mpControlVector->isUsed())
+ mpControlVector.reset( new ControlVectorArray2D(*rToBeCopied.mpControlVector) );
+
+ return *this;
+ }
+
+ sal_uInt32 count() const
+ {
+ return maPoints.count();
+ }
+
+ bool isClosed() const
+ {
+ return mbIsClosed;
+ }
+
+ void setClosed(bool bNew)
+ {
+ if(bNew != mbIsClosed)
+ {
+ mpBufferedData.reset();
+ mbIsClosed = bNew;
+ }
+ }
+
+ bool operator==(const ImplB2DPolygon& rCandidate) const
+ {
+ if(mbIsClosed == rCandidate.mbIsClosed)
+ {
+ if(maPoints == rCandidate.maPoints)
+ {
+ bool bControlVectorsAreEqual(true);
+
+ if(mpControlVector)
+ {
+ if(rCandidate.mpControlVector)
+ {
+ bControlVectorsAreEqual = ((*mpControlVector) == (*rCandidate.mpControlVector));
+ }
+ else
+ {
+ // candidate has no control vector, so it's assumed all unused.
+ bControlVectorsAreEqual = !mpControlVector->isUsed();
+ }
+ }
+ else
+ {
+ if(rCandidate.mpControlVector)
+ {
+ // we have no control vector, so it's assumed all unused.
+ bControlVectorsAreEqual = !rCandidate.mpControlVector->isUsed();
+ }
+ }
+
+ if(bControlVectorsAreEqual)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ const basegfx::B2DPoint& getPoint(sal_uInt32 nIndex) const
+ {
+ return maPoints.getCoordinate(nIndex);
+ }
+
+ void setPoint(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue)
+ {
+ mpBufferedData.reset();
+ maPoints.setCoordinate(nIndex, rValue);
+ }
+
+ void reserve(sal_uInt32 nCount)
+ {
+ maPoints.reserve(nCount);
+ }
+
+ void append(const basegfx::B2DPoint& rPoint)
+ {
+ mpBufferedData.reset(); // TODO: is this needed?
+ const CoordinateData2D aCoordinate(rPoint);
+ maPoints.append(aCoordinate);
+
+ if(mpControlVector)
+ {
+ const ControlVectorPair2D aVectorPair;
+ mpControlVector->append(aVectorPair);
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const basegfx::B2DPoint& rPoint, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ mpBufferedData.reset();
+ CoordinateData2D aCoordinate(rPoint);
+ maPoints.insert(nIndex, aCoordinate, nCount);
+
+ if(mpControlVector)
+ {
+ ControlVectorPair2D aVectorPair;
+ mpControlVector->insert(nIndex, aVectorPair, nCount);
+ }
+ }
+ }
+
+ const basegfx::B2DVector& getPrevControlVector(sal_uInt32 nIndex) const
+ {
+ if(mpControlVector)
+ {
+ return mpControlVector->getPrevVector(nIndex);
+ }
+ else
+ {
+ return basegfx::B2DVector::getEmptyVector();
+ }
+ }
+
+ void setPrevControlVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
+ {
+ if(!mpControlVector)
+ {
+ if(!rValue.equalZero())
+ {
+ mpBufferedData.reset();
+ mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
+ mpControlVector->setPrevVector(nIndex, rValue);
+ }
+ }
+ else
+ {
+ mpBufferedData.reset();
+ mpControlVector->setPrevVector(nIndex, rValue);
+
+ if(!mpControlVector->isUsed())
+ mpControlVector.reset();
+ }
+ }
+
+ const basegfx::B2DVector& getNextControlVector(sal_uInt32 nIndex) const
+ {
+ if(mpControlVector)
+ {
+ return mpControlVector->getNextVector(nIndex);
+ }
+ else
+ {
+ return basegfx::B2DVector::getEmptyVector();
+ }
+ }
+
+ void setNextControlVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
+ {
+ if(!mpControlVector)
+ {
+ if(!rValue.equalZero())
+ {
+ mpBufferedData.reset();
+ mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
+ mpControlVector->setNextVector(nIndex, rValue);
+ }
+ }
+ else
+ {
+ mpBufferedData.reset();
+ mpControlVector->setNextVector(nIndex, rValue);
+
+ if(!mpControlVector->isUsed())
+ mpControlVector.reset();
+ }
+ }
+
+ bool areControlPointsUsed() const
+ {
+ return (mpControlVector && mpControlVector->isUsed());
+ }
+
+ void resetControlVectors(sal_uInt32 nIndex)
+ {
+ setPrevControlVector(nIndex, basegfx::B2DVector::getEmptyVector());
+ setNextControlVector(nIndex, basegfx::B2DVector::getEmptyVector());
+ }
+
+ void resetControlVectors()
+ {
+ mpBufferedData.reset();
+ mpControlVector.reset();
+ }
+
+ void setControlVectors(sal_uInt32 nIndex, const basegfx::B2DVector& rPrev, const basegfx::B2DVector& rNext)
+ {
+ setPrevControlVector(nIndex, rPrev);
+ setNextControlVector(nIndex, rNext);
+ }
+
+ void appendBezierSegment(const basegfx::B2DVector& rNext, const basegfx::B2DVector& rPrev, const basegfx::B2DPoint& rPoint)
+ {
+ mpBufferedData.reset();
+ const sal_uInt32 nCount(maPoints.count());
+
+ if(nCount)
+ {
+ setNextControlVector(nCount - 1, rNext);
+ }
+
+ insert(nCount, rPoint, 1);
+ setPrevControlVector(nCount, rPrev);
+ }
+
+ void insert(sal_uInt32 nIndex, const ImplB2DPolygon& rSource)
+ {
+ const sal_uInt32 nCount(rSource.maPoints.count());
+
+ if(nCount)
+ {
+ mpBufferedData.reset();
+
+ if(rSource.mpControlVector && rSource.mpControlVector->isUsed() && !mpControlVector)
+ {
+ mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
+ }
+
+ maPoints.insert(nIndex, rSource.maPoints);
+
+ if(rSource.mpControlVector)
+ {
+ mpControlVector->insert(nIndex, *rSource.mpControlVector);
+
+ if(!mpControlVector->isUsed())
+ mpControlVector.reset();
+ }
+ else if(mpControlVector)
+ {
+ ControlVectorPair2D aVectorPair;
+ mpControlVector->insert(nIndex, aVectorPair, nCount);
+ }
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ mpBufferedData.reset();
+ maPoints.remove(nIndex, nCount);
+
+ if(mpControlVector)
+ {
+ mpControlVector->remove(nIndex, nCount);
+
+ if(!mpControlVector->isUsed())
+ mpControlVector.reset();
+ }
+ }
+ }
+
+ void flip()
+ {
+ if(maPoints.count() > 1)
+ {
+ mpBufferedData.reset();
+
+ // flip points
+ maPoints.flip(mbIsClosed);
+
+ if(mpControlVector)
+ {
+ // flip control vector
+ mpControlVector->flip(mbIsClosed);
+ }
+ }
+ }
+
+ bool hasDoublePoints() const
+ {
+ if(mbIsClosed)
+ {
+ // check for same start and end point
+ const sal_uInt32 nIndex(maPoints.count() - 1);
+
+ if(maPoints.getCoordinate(0) == maPoints.getCoordinate(nIndex))
+ {
+ if(mpControlVector)
+ {
+ if(mpControlVector->getNextVector(nIndex).equalZero() && mpControlVector->getPrevVector(0).equalZero())
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+
+ // test for range
+ for(sal_uInt32 a(0); a < maPoints.count() - 1; a++)
+ {
+ if(maPoints.getCoordinate(a) == maPoints.getCoordinate(a + 1))
+ {
+ if(mpControlVector)
+ {
+ if(mpControlVector->getNextVector(a).equalZero() && mpControlVector->getPrevVector(a + 1).equalZero())
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ void removeDoublePointsAtBeginEnd()
+ {
+ // Only remove DoublePoints at Begin and End when poly is closed
+ if(mbIsClosed)
+ {
+ mpBufferedData.reset();
+
+ if(mpControlVector)
+ {
+ bool bRemove;
+
+ do
+ {
+ bRemove = false;
+
+ if(maPoints.count() > 1)
+ {
+ const sal_uInt32 nIndex(maPoints.count() - 1);
+
+ if(maPoints.getCoordinate(0) == maPoints.getCoordinate(nIndex))
+ {
+ if(mpControlVector)
+ {
+ if(mpControlVector->getNextVector(nIndex).equalZero() && mpControlVector->getPrevVector(0).equalZero())
+ {
+ bRemove = true;
+ }
+ }
+ else
+ {
+ bRemove = true;
+ }
+ }
+ }
+
+ if(bRemove)
+ {
+ const sal_uInt32 nIndex(maPoints.count() - 1);
+
+ if(mpControlVector && !mpControlVector->getPrevVector(nIndex).equalZero())
+ {
+ mpControlVector->setPrevVector(0, mpControlVector->getPrevVector(nIndex));
+ }
+
+ remove(nIndex, 1);
+ }
+ }
+ while(bRemove);
+ }
+ else
+ {
+ maPoints.removeDoublePointsAtBeginEnd();
+ }
+ }
+ }
+
+ void removeDoublePointsWholeTrack()
+ {
+ mpBufferedData.reset();
+
+ if(mpControlVector)
+ {
+ sal_uInt32 nIndex(0);
+
+ // test as long as there are at least two points and as long as the index
+ // is smaller or equal second last point
+ while((maPoints.count() > 1) && (nIndex <= maPoints.count() - 2))
+ {
+ bool bRemove(maPoints.getCoordinate(nIndex) == maPoints.getCoordinate(nIndex + 1));
+
+ if(bRemove)
+ {
+ if(mpControlVector)
+ {
+ if(!mpControlVector->getNextVector(nIndex).equalZero() || !mpControlVector->getPrevVector(nIndex + 1).equalZero())
+ {
+ bRemove = false;
+ }
+ }
+ }
+
+ if(bRemove)
+ {
+ if(mpControlVector && !mpControlVector->getPrevVector(nIndex).equalZero())
+ {
+ mpControlVector->setPrevVector(nIndex + 1, mpControlVector->getPrevVector(nIndex));
+ }
+
+ // if next is same as index and the control vectors are unused, delete index
+ remove(nIndex, 1);
+ }
+ else
+ {
+ // if different, step forward
+ nIndex++;
+ }
+ }
+ }
+ else
+ {
+ maPoints.removeDoublePointsWholeTrack();
+ }
+ }
+
+ void transform(const basegfx::B2DHomMatrix& rMatrix)
+ {
+ mpBufferedData.reset();
+
+ if(mpControlVector)
+ {
+ for(sal_uInt32 a(0); a < maPoints.count(); a++)
+ {
+ basegfx::B2DPoint aCandidate = maPoints.getCoordinate(a);
+
+ if(mpControlVector->isUsed())
+ {
+ const basegfx::B2DVector& rPrevVector(mpControlVector->getPrevVector(a));
+ const basegfx::B2DVector& rNextVector(mpControlVector->getNextVector(a));
+
+ if(!rPrevVector.equalZero())
+ {
+ basegfx::B2DVector aPrevVector(rMatrix * rPrevVector);
+ mpControlVector->setPrevVector(a, aPrevVector);
+ }
+
+ if(!rNextVector.equalZero())
+ {
+ basegfx::B2DVector aNextVector(rMatrix * rNextVector);
+ mpControlVector->setNextVector(a, aNextVector);
+ }
+ }
+
+ aCandidate *= rMatrix;
+ maPoints.setCoordinate(a, aCandidate);
+ }
+
+ if(!mpControlVector->isUsed())
+ mpControlVector.reset();
+ }
+ else
+ {
+ maPoints.transform(rMatrix);
+ }
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace
+ {
+ struct DefaultPolygon: public rtl::Static<B2DPolygon::ImplType, DefaultPolygon> {};
+ }
+
+ B2DPolygon::B2DPolygon()
+ : mpPolygon(DefaultPolygon::get())
+ {}
+
+ B2DPolygon::B2DPolygon(const B2DPolygon& rPolygon)
+ : mpPolygon(rPolygon.mpPolygon)
+ {}
+
+ B2DPolygon::B2DPolygon(const B2DPolygon& rPolygon, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : mpPolygon(ImplB2DPolygon(*rPolygon.mpPolygon, nIndex, nCount))
+ {
+ // TODO(P2): one extra temporary here (cow_wrapper copies
+ // given ImplB2DPolygon into its internal impl_t wrapper type)
+ OSL_ENSURE(nIndex + nCount <= rPolygon.mpPolygon->count(), "B2DPolygon constructor outside range (!)");
+ }
+
+ B2DPolygon::~B2DPolygon()
+ {
+ }
+
+ B2DPolygon& B2DPolygon::operator=(const B2DPolygon& rPolygon)
+ {
+ mpPolygon = rPolygon.mpPolygon;
+ return *this;
+ }
+
+ void B2DPolygon::makeUnique()
+ {
+ mpPolygon.make_unique();
+ }
+
+ bool B2DPolygon::operator==(const B2DPolygon& rPolygon) const
+ {
+ if(mpPolygon.same_object(rPolygon.mpPolygon))
+ return true;
+
+ return ((*mpPolygon) == (*rPolygon.mpPolygon));
+ }
+
+ bool B2DPolygon::operator!=(const B2DPolygon& rPolygon) const
+ {
+ return !(*this == rPolygon);
+ }
+
+ sal_uInt32 B2DPolygon::count() const
+ {
+ return mpPolygon->count();
+ }
+
+ B2DPoint B2DPolygon::getB2DPoint(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ return mpPolygon->getPoint(nIndex);
+ }
+
+ void B2DPolygon::setB2DPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ if(getB2DPoint(nIndex) != rValue)
+ {
+ mpPolygon->setPoint(nIndex, rValue);
+ }
+ }
+
+ void B2DPolygon::reserve(sal_uInt32 nCount)
+ {
+ mpPolygon->reserve(nCount);
+ }
+
+ void B2DPolygon::insert(sal_uInt32 nIndex, const B2DPoint& rPoint, sal_uInt32 nCount)
+ {
+ OSL_ENSURE(nIndex <= mpPolygon->count(), "B2DPolygon Insert outside range (!)");
+
+ if(nCount)
+ {
+ mpPolygon->insert(nIndex, rPoint, nCount);
+ }
+ }
+
+ void B2DPolygon::append(const B2DPoint& rPoint, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ mpPolygon->insert(mpPolygon->count(), rPoint, nCount);
+ }
+ }
+
+ void B2DPolygon::append(const B2DPoint& rPoint)
+ {
+ mpPolygon->append(rPoint);
+ }
+
+ B2DPoint B2DPolygon::getPrevControlPoint(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ if(mpPolygon->areControlPointsUsed())
+ {
+ return mpPolygon->getPoint(nIndex) + mpPolygon->getPrevControlVector(nIndex);
+ }
+ else
+ {
+ return mpPolygon->getPoint(nIndex);
+ }
+ }
+
+ B2DPoint B2DPolygon::getNextControlPoint(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ if(mpPolygon->areControlPointsUsed())
+ {
+ return mpPolygon->getPoint(nIndex) + mpPolygon->getNextControlVector(nIndex);
+ }
+ else
+ {
+ return mpPolygon->getPoint(nIndex);
+ }
+ }
+
+ void B2DPolygon::setPrevControlPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+ const basegfx::B2DVector aNewVector(rValue - mpPolygon->getPoint(nIndex));
+
+ if(mpPolygon->getPrevControlVector(nIndex) != aNewVector)
+ {
+ mpPolygon->setPrevControlVector(nIndex, aNewVector);
+ }
+ }
+
+ void B2DPolygon::setNextControlPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+ const basegfx::B2DVector aNewVector(rValue - mpPolygon->getPoint(nIndex));
+
+ if(mpPolygon->getNextControlVector(nIndex) != aNewVector)
+ {
+ mpPolygon->setNextControlVector(nIndex, aNewVector);
+ }
+ }
+
+ void B2DPolygon::setControlPoints(sal_uInt32 nIndex, const basegfx::B2DPoint& rPrev, const basegfx::B2DPoint& rNext)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+ const B2DPoint aPoint(mpPolygon->getPoint(nIndex));
+ const basegfx::B2DVector aNewPrev(rPrev - aPoint);
+ const basegfx::B2DVector aNewNext(rNext - aPoint);
+
+ if(mpPolygon->getPrevControlVector(nIndex) != aNewPrev || mpPolygon->getNextControlVector(nIndex) != aNewNext)
+ {
+ mpPolygon->setControlVectors(nIndex, aNewPrev, aNewNext);
+ }
+ }
+
+ void B2DPolygon::resetPrevControlPoint(sal_uInt32 nIndex)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ if(mpPolygon->areControlPointsUsed() && !mpPolygon->getPrevControlVector(nIndex).equalZero())
+ {
+ mpPolygon->setPrevControlVector(nIndex, B2DVector::getEmptyVector());
+ }
+ }
+
+ void B2DPolygon::resetNextControlPoint(sal_uInt32 nIndex)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ if(mpPolygon->areControlPointsUsed() && !mpPolygon->getNextControlVector(nIndex).equalZero())
+ {
+ mpPolygon->setNextControlVector(nIndex, B2DVector::getEmptyVector());
+ }
+ }
+
+ void B2DPolygon::resetControlPoints(sal_uInt32 nIndex)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ if(mpPolygon->areControlPointsUsed() &&
+ (!mpPolygon->getPrevControlVector(nIndex).equalZero() || !mpPolygon->getNextControlVector(nIndex).equalZero()))
+ {
+ mpPolygon->resetControlVectors(nIndex);
+ }
+ }
+
+ void B2DPolygon::resetControlPoints()
+ {
+ if(mpPolygon->areControlPointsUsed())
+ {
+ mpPolygon->resetControlVectors();
+ }
+ }
+
+ void B2DPolygon::appendBezierSegment(
+ const B2DPoint& rNextControlPoint,
+ const B2DPoint& rPrevControlPoint,
+ const B2DPoint& rPoint)
+ {
+ const B2DVector aNewNextVector(mpPolygon->count() ? B2DVector(rNextControlPoint - mpPolygon->getPoint(mpPolygon->count() - 1)) : B2DVector::getEmptyVector());
+ const B2DVector aNewPrevVector(rPrevControlPoint - rPoint);
+
+ if(aNewNextVector.equalZero() && aNewPrevVector.equalZero())
+ {
+ mpPolygon->insert(mpPolygon->count(), rPoint, 1);
+ }
+ else
+ {
+ mpPolygon->appendBezierSegment(aNewNextVector, aNewPrevVector, rPoint);
+ }
+ }
+
+ bool B2DPolygon::areControlPointsUsed() const
+ {
+ return mpPolygon->areControlPointsUsed();
+ }
+
+ bool B2DPolygon::isPrevControlPointUsed(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ return (mpPolygon->areControlPointsUsed() && !mpPolygon->getPrevControlVector(nIndex).equalZero());
+ }
+
+ bool B2DPolygon::isNextControlPointUsed(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ return (mpPolygon->areControlPointsUsed() && !mpPolygon->getNextControlVector(nIndex).equalZero());
+ }
+
+ B2VectorContinuity B2DPolygon::getContinuityInPoint(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ if(mpPolygon->areControlPointsUsed())
+ {
+ const B2DVector& rPrev(mpPolygon->getPrevControlVector(nIndex));
+ const B2DVector& rNext(mpPolygon->getNextControlVector(nIndex));
+
+ return getContinuity(rPrev, rNext);
+ }
+ else
+ {
+ return CONTINUITY_NONE;
+ }
+ }
+
+ bool B2DPolygon::isBezierSegment(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+
+ if(mpPolygon->areControlPointsUsed())
+ {
+ // Check if the edge exists
+ const bool bNextIndexValidWithoutClose(nIndex + 1 < mpPolygon->count());
+
+ if(bNextIndexValidWithoutClose || mpPolygon->isClosed())
+ {
+ const sal_uInt32 nNextIndex(bNextIndexValidWithoutClose ? nIndex + 1 : 0);
+ return (!mpPolygon->getPrevControlVector(nNextIndex).equalZero()
+ || !mpPolygon->getNextControlVector(nIndex).equalZero());
+ }
+ else
+ {
+ // no valid edge -> no bezier segment, even when local next
+ // vector may be used
+ return false;
+ }
+ }
+ else
+ {
+ // no control points -> no bezier segment
+ return false;
+ }
+ }
+
+ void B2DPolygon::getBezierSegment(sal_uInt32 nIndex, B2DCubicBezier& rTarget) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
+ const bool bNextIndexValidWithoutClose(nIndex + 1 < mpPolygon->count());
+
+ if(bNextIndexValidWithoutClose || mpPolygon->isClosed())
+ {
+ const sal_uInt32 nNextIndex(bNextIndexValidWithoutClose ? nIndex + 1 : 0);
+ rTarget.setStartPoint(mpPolygon->getPoint(nIndex));
+ rTarget.setEndPoint(mpPolygon->getPoint(nNextIndex));
+
+ if(mpPolygon->areControlPointsUsed())
+ {
+ rTarget.setControlPointA(rTarget.getStartPoint() + mpPolygon->getNextControlVector(nIndex));
+ rTarget.setControlPointB(rTarget.getEndPoint() + mpPolygon->getPrevControlVector(nNextIndex));
+ }
+ else
+ {
+ // no bezier, reset control poins at rTarget
+ rTarget.setControlPointA(rTarget.getStartPoint());
+ rTarget.setControlPointB(rTarget.getEndPoint());
+ }
+ }
+ else
+ {
+ // no valid edge at all, reset rTarget to current point
+ const B2DPoint aPoint(mpPolygon->getPoint(nIndex));
+ rTarget.setStartPoint(aPoint);
+ rTarget.setEndPoint(aPoint);
+ rTarget.setControlPointA(aPoint);
+ rTarget.setControlPointB(aPoint);
+ }
+ }
+
+ B2DPolygon B2DPolygon::getDefaultAdaptiveSubdivision() const
+ {
+ return mpPolygon->getDefaultAdaptiveSubdivision(*this);
+ }
+
+ B2DRange B2DPolygon::getB2DRange() const
+ {
+ return mpPolygon->getB2DRange(*this);
+ }
+
+ void B2DPolygon::insert(sal_uInt32 nIndex, const B2DPolygon& rPoly, sal_uInt32 nIndex2, sal_uInt32 nCount)
+ {
+ OSL_ENSURE(nIndex <= mpPolygon->count(), "B2DPolygon Insert outside range (!)");
+
+ if(rPoly.count())
+ {
+ if(!nCount)
+ {
+ nCount = rPoly.count();
+ }
+
+ if(0 == nIndex2 && nCount == rPoly.count())
+ {
+ mpPolygon->insert(nIndex, *rPoly.mpPolygon);
+ }
+ else
+ {
+ OSL_ENSURE(nIndex2 + nCount <= rPoly.mpPolygon->count(), "B2DPolygon Insert outside range (!)");
+ ImplB2DPolygon aTempPoly(*rPoly.mpPolygon, nIndex2, nCount);
+ mpPolygon->insert(nIndex, aTempPoly);
+ }
+ }
+ }
+
+ void B2DPolygon::append(const B2DPolygon& rPoly, sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(rPoly.count())
+ {
+ if(!nCount)
+ {
+ nCount = rPoly.count();
+ }
+
+ if(0 == nIndex && nCount == rPoly.count())
+ {
+ mpPolygon->insert(mpPolygon->count(), *rPoly.mpPolygon);
+ }
+ else
+ {
+ OSL_ENSURE(nIndex + nCount <= rPoly.mpPolygon->count(), "B2DPolygon Append outside range (!)");
+ ImplB2DPolygon aTempPoly(*rPoly.mpPolygon, nIndex, nCount);
+ mpPolygon->insert(mpPolygon->count(), aTempPoly);
+ }
+ }
+ }
+
+ void B2DPolygon::remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ OSL_ENSURE(nIndex + nCount <= mpPolygon->count(), "B2DPolygon Remove outside range (!)");
+
+ if(nCount)
+ {
+ mpPolygon->remove(nIndex, nCount);
+ }
+ }
+
+ void B2DPolygon::clear()
+ {
+ mpPolygon = DefaultPolygon::get();
+ }
+
+ bool B2DPolygon::isClosed() const
+ {
+ return mpPolygon->isClosed();
+ }
+
+ void B2DPolygon::setClosed(bool bNew)
+ {
+ if(isClosed() != bNew)
+ {
+ mpPolygon->setClosed(bNew);
+ }
+ }
+
+ void B2DPolygon::flip()
+ {
+ if(count() > 1)
+ {
+ mpPolygon->flip();
+ }
+ }
+
+ bool B2DPolygon::hasDoublePoints() const
+ {
+ return (mpPolygon->count() > 1 && mpPolygon->hasDoublePoints());
+ }
+
+ void B2DPolygon::removeDoublePoints()
+ {
+ if(hasDoublePoints())
+ {
+ mpPolygon->removeDoublePointsAtBeginEnd();
+ mpPolygon->removeDoublePointsWholeTrack();
+ }
+ }
+
+ void B2DPolygon::transform(const B2DHomMatrix& rMatrix)
+ {
+ if(mpPolygon->count() && !rMatrix.isIdentity())
+ {
+ mpPolygon->transform(rMatrix);
+ }
+ }
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/polygon/b2dpolygonclipper.cxx b/basegfx/source/polygon/b2dpolygonclipper.cxx
new file mode 100644
index 000000000000..f0d325942c07
--- /dev/null
+++ b/basegfx/source/polygon/b2dpolygonclipper.cxx
@@ -0,0 +1,877 @@
+/*************************************************************************
+ *
+ * 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: b2dpolygonclipper.cxx,v $
+ * $Revision: 1.7 $
+ *
+ * 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/polygon/b2dpolygonclipper.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <basegfx/tools/rectcliptools.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ B2DPolyPolygon clipPolygonOnParallelAxis(const B2DPolygon& rCandidate, bool bParallelToXAxis, bool bAboveAxis, double fValueOnOtherAxis, bool bStroke)
+ {
+ B2DPolyPolygon aRetval;
+
+ if(rCandidate.count())
+ {
+ const B2DRange aCandidateRange(getRange(rCandidate));
+
+ if(bParallelToXAxis && fTools::moreOrEqual(aCandidateRange.getMinY(), fValueOnOtherAxis))
+ {
+ // completely above and on the clip line. also true for curves.
+ if(bAboveAxis)
+ {
+ // add completely
+ aRetval.append(rCandidate);
+ }
+ }
+ else if(bParallelToXAxis && fTools::lessOrEqual(aCandidateRange.getMaxY(), fValueOnOtherAxis))
+ {
+ // completely below and on the clip line. also true for curves.
+ if(!bAboveAxis)
+ {
+ // add completely
+ aRetval.append(rCandidate);
+ }
+ }
+ else if(!bParallelToXAxis && fTools::moreOrEqual(aCandidateRange.getMinX(), fValueOnOtherAxis))
+ {
+ // completely right of and on the clip line. also true for curves.
+ if(bAboveAxis)
+ {
+ // add completely
+ aRetval.append(rCandidate);
+ }
+ }
+ else if(!bParallelToXAxis && fTools::lessOrEqual(aCandidateRange.getMaxX(), fValueOnOtherAxis))
+ {
+ // completely left of and on the clip line. also true for curves.
+ if(!bAboveAxis)
+ {
+ // add completely
+ aRetval.append(rCandidate);
+ }
+ }
+ else
+ {
+ // add cuts with axis to polygon, including bezier segments
+ // Build edge to cut with. Make it a little big longer than needed for
+ // numerical stability. We want to cut against the edge seen as endless
+ // ray here, but addPointsAtCuts() will limit itself to the
+ // edge's range ]0.0 .. 1.0[.
+ const double fSmallExtension((aCandidateRange.getWidth() + aCandidateRange.getHeight()) * (0.5 * 0.1));
+ const B2DPoint aStart(
+ bParallelToXAxis ? aCandidateRange.getMinX() - fSmallExtension : fValueOnOtherAxis,
+ bParallelToXAxis ? fValueOnOtherAxis : aCandidateRange.getMinY() - fSmallExtension);
+ const B2DPoint aEnd(
+ bParallelToXAxis ? aCandidateRange.getMaxX() + fSmallExtension : fValueOnOtherAxis,
+ bParallelToXAxis ? fValueOnOtherAxis : aCandidateRange.getMaxY() + fSmallExtension);
+ const B2DPolygon aCandidate(addPointsAtCuts(rCandidate, aStart, aEnd));
+ const sal_uInt32 nPointCount(aCandidate.count());
+ const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+ B2DCubicBezier aEdge;
+ B2DPolygon aRun;
+
+ for(sal_uInt32 a(0L); a < nEdgeCount; a++)
+ {
+ aCandidate.getBezierSegment(a, aEdge);
+ const B2DPoint aTestPoint(aEdge.interpolatePoint(0.5));
+ const bool bInside(bParallelToXAxis ?
+ fTools::moreOrEqual(aTestPoint.getY(), fValueOnOtherAxis) == bAboveAxis :
+ fTools::moreOrEqual(aTestPoint.getX(), fValueOnOtherAxis) == bAboveAxis);
+
+ if(bInside)
+ {
+ if(!aRun.count() || !aRun.getB2DPoint(aRun.count() - 1).equal(aEdge.getStartPoint()))
+ {
+ aRun.append(aEdge.getStartPoint());
+ }
+
+ if(aEdge.isBezier())
+ {
+ aRun.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint());
+ }
+ else
+ {
+ aRun.append(aEdge.getEndPoint());
+ }
+ }
+ else
+ {
+ if(bStroke && aRun.count())
+ {
+ aRetval.append(aRun);
+ aRun.clear();
+ }
+ }
+ }
+
+ if(aRun.count())
+ {
+ if(bStroke)
+ {
+ // try to merge this last and first polygon; they may have been
+ // the former polygon's start/end point
+ if(aRetval.count())
+ {
+ const B2DPolygon aStartPolygon(aRetval.getB2DPolygon(0));
+
+ if(aStartPolygon.count() && aStartPolygon.getB2DPoint(0).equal(aRun.getB2DPoint(aRun.count() - 1)))
+ {
+ // append start polygon to aRun, remove from result set
+ aRun.append(aStartPolygon); aRun.removeDoublePoints();
+ aRetval.remove(0);
+ }
+ }
+
+ aRetval.append(aRun);
+ }
+ else
+ {
+ // set closed flag and correct last point (which is added double now).
+ closeWithGeometryChange(aRun);
+ aRetval.append(aRun);
+ }
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon clipPolyPolygonOnParallelAxis(const B2DPolyPolygon& rCandidate, bool bParallelToXAxis, bool bAboveAxis, double fValueOnOtherAxis, bool bStroke)
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolyPolygon aClippedPolyPolygon(clipPolygonOnParallelAxis(rCandidate.getB2DPolygon(a), bParallelToXAxis, bAboveAxis, fValueOnOtherAxis, bStroke));
+
+ if(aClippedPolyPolygon.count())
+ {
+ aRetval.append(aClippedPolyPolygon);
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon clipPolygonOnRange(const B2DPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke)
+ {
+ const sal_uInt32 nCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ if(!nCount)
+ {
+ // source is empty
+ return aRetval;
+ }
+
+ if(rRange.isEmpty())
+ {
+ if(bInside)
+ {
+ // nothing is inside an empty range
+ return aRetval;
+ }
+ else
+ {
+ // everything is outside an empty range
+ return B2DPolyPolygon(rCandidate);
+ }
+ }
+
+ const B2DRange aCandidateRange(getRange(rCandidate));
+
+ if(rRange.isInside(aCandidateRange))
+ {
+ // candidate is completely inside given range
+ if(bInside)
+ {
+ // nothing to do
+ return B2DPolyPolygon(rCandidate);
+ }
+ else
+ {
+ // nothing is outside, then
+ return aRetval;
+ }
+ }
+
+ if(!bInside)
+ {
+ // cutting off the outer parts of filled polygons at parallell
+ // lines to the axes is only possible for the inner part, not for
+ // the outer part which means cutting a hole into the original polygon.
+ // This is because the inner part is a logical AND-operation of
+ // the four implied half-planes, but the outer part is not.
+ // It is possible for strokes, but with creating unnecessary extra
+ // cuts, so using clipPolygonOnPolyPolygon is better there, too.
+ // This needs to be done with the topology knowlegde and is unfurtunately
+ // more expensive, too.
+ const B2DPolygon aClip(createPolygonFromRect(rRange));
+
+ return clipPolygonOnPolyPolygon(rCandidate, B2DPolyPolygon(aClip), bInside, bStroke);
+ }
+
+ // clip against the four axes of the range
+ // against X-Axis, lower value
+ aRetval = clipPolygonOnParallelAxis(rCandidate, true, bInside, rRange.getMinY(), bStroke);
+
+ if(aRetval.count())
+ {
+ // against Y-Axis, lower value
+ if(1L == aRetval.count())
+ {
+ aRetval = clipPolygonOnParallelAxis(aRetval.getB2DPolygon(0L), false, bInside, rRange.getMinX(), bStroke);
+ }
+ else
+ {
+ aRetval = clipPolyPolygonOnParallelAxis(aRetval, false, bInside, rRange.getMinX(), bStroke);
+ }
+
+ if(aRetval.count())
+ {
+ // against X-Axis, higher value
+ if(1L == aRetval.count())
+ {
+ aRetval = clipPolygonOnParallelAxis(aRetval.getB2DPolygon(0L), true, !bInside, rRange.getMaxY(), bStroke);
+ }
+ else
+ {
+ aRetval = clipPolyPolygonOnParallelAxis(aRetval, true, !bInside, rRange.getMaxY(), bStroke);
+ }
+
+ if(aRetval.count())
+ {
+ // against Y-Axis, higher value
+ if(1L == aRetval.count())
+ {
+ aRetval = clipPolygonOnParallelAxis(aRetval.getB2DPolygon(0L), false, !bInside, rRange.getMaxX(), bStroke);
+ }
+ else
+ {
+ aRetval = clipPolyPolygonOnParallelAxis(aRetval, false, !bInside, rRange.getMaxX(), bStroke);
+ }
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon clipPolyPolygonOnRange(const B2DPolyPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke)
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ if(!nPolygonCount)
+ {
+ // source is empty
+ return aRetval;
+ }
+
+ if(rRange.isEmpty())
+ {
+ if(bInside)
+ {
+ // nothing is inside an empty range
+ return aRetval;
+ }
+ else
+ {
+ // everything is outside an empty range
+ return rCandidate;
+ }
+ }
+
+ if(bInside)
+ {
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolyPolygon aClippedPolyPolygon(clipPolygonOnRange(rCandidate.getB2DPolygon(a), rRange, bInside, bStroke));
+
+ if(aClippedPolyPolygon.count())
+ {
+ aRetval.append(aClippedPolyPolygon);
+ }
+ }
+ }
+ else
+ {
+ // for details, see comment in clipPolygonOnRange for the "cutting off
+ // the outer parts of filled polygons at parallell lines" explanations
+ const B2DPolygon aClip(createPolygonFromRect(rRange));
+
+ return clipPolyPolygonOnPolyPolygon(rCandidate, B2DPolyPolygon(aClip), bInside, bStroke);
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon clipPolygonOnEdge(const B2DPolygon& rCandidate, const B2DPoint& rPointA, const B2DPoint& rPointB, bool bAbove, bool bStroke)
+ {
+ B2DPolyPolygon aRetval;
+
+ if(rPointA.equal(rPointB))
+ {
+ // edge has no length, return polygon
+ aRetval.append(rCandidate);
+ }
+ else if(rCandidate.count())
+ {
+ const B2DVector aEdge(rPointB - rPointA);
+ B2DHomMatrix aMatrixTransform;
+ B2DPolygon aCandidate(rCandidate);
+
+ // translate and rotate polygon so that given edge is on x axis
+ aMatrixTransform.translate(-rPointA.getX(), -rPointA.getY());
+ aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX()));
+ aCandidate.transform(aMatrixTransform);
+
+ // call clip method on X-Axis
+ aRetval = clipPolygonOnParallelAxis(aCandidate, true, bAbove, 0.0, bStroke);
+
+ if(aRetval.count())
+ {
+ // if there is a result, it needs to be transformed back
+ aMatrixTransform.invert();
+ aRetval.transform(aMatrixTransform);
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon clipPolyPolygonOnEdge(const B2DPolyPolygon& rCandidate, const B2DPoint& rPointA, const B2DPoint& rPointB, bool bAbove, bool bStroke)
+ {
+ B2DPolyPolygon aRetval;
+
+ if(rPointA.equal(rPointB))
+ {
+ // edge has no length, return polygon
+ aRetval = rCandidate;
+ }
+ else if(rCandidate.count())
+ {
+ const B2DVector aEdge(rPointB - rPointA);
+ B2DHomMatrix aMatrixTransform;
+ B2DPolyPolygon aCandidate(rCandidate);
+
+ // translate and rotate polygon so that given edge is on x axis
+ aMatrixTransform.translate(-rPointA.getX(), -rPointA.getY());
+ aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX()));
+ aCandidate.transform(aMatrixTransform);
+
+ // call clip method on X-Axis
+ aRetval = clipPolyPolygonOnParallelAxis(aCandidate, true, bAbove, 0.0, bStroke);
+
+ if(aRetval.count())
+ {
+ // if there is a result, it needs to be transformed back
+ aMatrixTransform.invert();
+ aRetval.transform(aMatrixTransform);
+ }
+ }
+
+ return aRetval;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ B2DPolyPolygon clipPolyPolygonOnPolyPolygon(const B2DPolyPolygon& rCandidate, const B2DPolyPolygon& rClip, bool bInside, bool bStroke)
+ {
+ B2DPolyPolygon aRetval;
+
+ if(rCandidate.count() && rClip.count())
+ {
+ if(bStroke)
+ {
+ // line clipping, create line snippets by first adding all cut points and
+ // then marching along the edges and detecting if they are inside or outside
+ // the clip polygon
+ for(sal_uInt32 a(0); a < rCandidate.count(); a++)
+ {
+ // add cuts with clip to polygon, including bezier segments
+ const B2DPolygon aCandidate(addPointsAtCuts(rCandidate.getB2DPolygon(a), rClip));
+ const sal_uInt32 nPointCount(aCandidate.count());
+ const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+ B2DCubicBezier aEdge;
+ B2DPolygon aRun;
+
+ for(sal_uInt32 b(0); b < nEdgeCount; b++)
+ {
+ aCandidate.getBezierSegment(b, aEdge);
+ const B2DPoint aTestPoint(aEdge.interpolatePoint(0.5));
+ const bool bIsInside(tools::isInside(rClip, aTestPoint) == bInside);
+
+ if(bIsInside)
+ {
+ if(!aRun.count())
+ {
+ aRun.append(aEdge.getStartPoint());
+ }
+
+ if(aEdge.isBezier())
+ {
+ aRun.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint());
+ }
+ else
+ {
+ aRun.append(aEdge.getEndPoint());
+ }
+ }
+ else
+ {
+ if(aRun.count())
+ {
+ aRetval.append(aRun);
+ aRun.clear();
+ }
+ }
+ }
+
+ if(aRun.count())
+ {
+ // try to merge this last and first polygon; they may have been
+ // the former polygon's start/end point
+ if(aRetval.count())
+ {
+ const B2DPolygon aStartPolygon(aRetval.getB2DPolygon(0));
+
+ if(aStartPolygon.count() && aStartPolygon.getB2DPoint(0).equal(aRun.getB2DPoint(aRun.count() - 1)))
+ {
+ // append start polygon to aRun, remove from result set
+ aRun.append(aStartPolygon); aRun.removeDoublePoints();
+ aRetval.remove(0);
+ }
+ }
+
+ aRetval.append(aRun);
+ }
+ }
+ }
+ else
+ {
+ // area clipping
+ B2DPolyPolygon aMergePolyPolygonA(rClip);
+
+ // First solve all polygon-self and polygon-polygon intersections.
+ // Also get rid of some not-needed polygons (neutral, no area -> when
+ // no intersections, these are tubes).
+ // Now it is possible to correct the orientations in the cut-free
+ // polygons to values corresponding to painting the PolyPolygon with
+ // a XOR-WindingRule.
+ aMergePolyPolygonA = solveCrossovers(aMergePolyPolygonA);
+ aMergePolyPolygonA = stripNeutralPolygons(aMergePolyPolygonA);
+ aMergePolyPolygonA = correctOrientations(aMergePolyPolygonA);
+
+ if(!bInside)
+ {
+ // if we want to get the outside of the clip polygon, make
+ // it a 'Hole' in topological sense
+ aMergePolyPolygonA.flip();
+ }
+
+ B2DPolyPolygon aMergePolyPolygonB(rCandidate);
+
+ // prepare 2nd source polygon in same way
+ aMergePolyPolygonB = solveCrossovers(aMergePolyPolygonB);
+ aMergePolyPolygonB = stripNeutralPolygons(aMergePolyPolygonB);
+ aMergePolyPolygonB = correctOrientations(aMergePolyPolygonB);
+
+ // to clip against each other, concatenate and solve all
+ // polygon-polygon crossovers. polygon-self do not need to
+ // be solved again, they were solved in the preparation.
+ aRetval.append(aMergePolyPolygonA);
+ aRetval.append(aMergePolyPolygonB);
+ aRetval = solveCrossovers(aRetval);
+
+ // now remove neutral polygons (closed, but no area). In a last
+ // step throw away all polygons which have a depth of less than 1
+ // which means there was no logical AND at their position. For the
+ // not-inside solution, the clip was flipped to define it as 'Hole',
+ // so the removal rule is different here; remove all with a depth
+ // of less than 0 (aka holes).
+ aRetval = stripNeutralPolygons(aRetval);
+ aRetval = stripDispensablePolygons(aRetval, bInside);
+ }
+ }
+
+ return aRetval;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ B2DPolyPolygon clipPolygonOnPolyPolygon(const B2DPolygon& rCandidate, const B2DPolyPolygon& rClip, bool bInside, bool bStroke)
+ {
+ B2DPolyPolygon aRetval;
+
+ if(rCandidate.count() && rClip.count())
+ {
+ aRetval = clipPolyPolygonOnPolyPolygon(B2DPolyPolygon(rCandidate), rClip, bInside, bStroke);
+ }
+
+ return aRetval;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ /*
+ * let a plane be defined as
+ *
+ * v.n+d=0
+ *
+ * and a ray be defined as
+ *
+ * a+(b-a)*t=0
+ *
+ * substitute and rearranging yields
+ *
+ * t = -(a.n+d)/(n.(b-a))
+ *
+ * if the denominator is zero, the line is either
+ * contained in the plane or parallel to the plane.
+ * in either case, there is no intersection.
+ * if numerator and denominator are both zero, the
+ * ray is contained in the plane.
+ *
+ */
+ struct scissor_plane {
+ double nx,ny; // plane normal
+ double d; // [-] minimum distance from origin
+ sal_uInt32 clipmask; // clipping mask, e.g. 1000 1000
+ };
+
+ /*
+ *
+ * polygon clipping rules (straight out of Foley and Van Dam)
+ * ===========================================================
+ * current |next |emit
+ * ____________________________________
+ * inside |inside |next
+ * inside |outside |intersect with clip plane
+ * outside |outside |nothing
+ * outside |inside |intersect with clip plane follwed by next
+ *
+ */
+ sal_uInt32 scissorLineSegment( ::basegfx::B2DPoint *in_vertex, // input buffer
+ sal_uInt32 in_count, // number of verts in input buffer
+ ::basegfx::B2DPoint *out_vertex, // output buffer
+ scissor_plane *pPlane, // scissoring plane
+ const ::basegfx::B2DRectangle &rR ) // clipping rectangle
+ {
+ ::basegfx::B2DPoint *curr;
+ ::basegfx::B2DPoint *next;
+
+ sal_uInt32 out_count=0;
+
+ // process all the verts
+ for(sal_uInt32 i=0; i<in_count; i++) {
+
+ // vertices are relative to the coordinate
+ // system defined by the rectangle.
+ curr = &in_vertex[i];
+ next = &in_vertex[(i+1)%in_count];
+
+ // perform clipping judgement & mask against current plane.
+ sal_uInt32 clip = pPlane->clipmask & ((getCohenSutherlandClipFlags(*curr,rR)<<4)|getCohenSutherlandClipFlags(*next,rR));
+
+ if(clip==0) { // both verts are inside
+ out_vertex[out_count++] = *next;
+ }
+ else if((clip&0x0f) && (clip&0xf0)) { // both verts are outside
+ }
+ else if((clip&0x0f) && (clip&0xf0)==0) { // curr is inside, next is outside
+
+ // direction vector from 'current' to 'next', *not* normalized
+ // to bring 't' into the [0<=x<=1] intervall.
+ ::basegfx::B2DPoint dir((*next)-(*curr));
+
+ double denominator = ( pPlane->nx*dir.getX() +
+ pPlane->ny*dir.getY() );
+ double numerator = ( pPlane->nx*curr->getX() +
+ pPlane->ny*curr->getY() +
+ pPlane->d );
+ double t = -numerator/denominator;
+
+ // calculate the actual point of intersection
+ ::basegfx::B2DPoint intersection( curr->getX()+t*dir.getX(),
+ curr->getY()+t*dir.getY() );
+
+ out_vertex[out_count++] = intersection;
+ }
+ else if((clip&0x0f)==0 && (clip&0xf0)) { // curr is outside, next is inside
+
+ // direction vector from 'current' to 'next', *not* normalized
+ // to bring 't' into the [0<=x<=1] intervall.
+ ::basegfx::B2DPoint dir((*next)-(*curr));
+
+ double denominator = ( pPlane->nx*dir.getX() +
+ pPlane->ny*dir.getY() );
+ double numerator = ( pPlane->nx*curr->getX() +
+ pPlane->ny*curr->getY() +
+ pPlane->d );
+ double t = -numerator/denominator;
+
+ // calculate the actual point of intersection
+ ::basegfx::B2DPoint intersection( curr->getX()+t*dir.getX(),
+ curr->getY()+t*dir.getY() );
+
+ out_vertex[out_count++] = intersection;
+ out_vertex[out_count++] = *next;
+ }
+ }
+
+ return out_count;
+ }
+
+ B2DPolygon clipTriangleListOnRange( const B2DPolygon& rCandidate,
+ const B2DRange& rRange )
+ {
+ B2DPolygon aResult;
+
+ if( !(rCandidate.count()%3) )
+ {
+ const int scissor_plane_count = 4;
+
+ scissor_plane sp[scissor_plane_count];
+
+ sp[0].nx = +1.0;
+ sp[0].ny = +0.0;
+ sp[0].d = -(rRange.getMinX());
+ sp[0].clipmask = (RectClipFlags::LEFT << 4) | RectClipFlags::LEFT; // 0001 0001
+ sp[1].nx = -1.0;
+ sp[1].ny = +0.0;
+ sp[1].d = +(rRange.getMaxX());
+ sp[1].clipmask = (RectClipFlags::RIGHT << 4) | RectClipFlags::RIGHT; // 0010 0010
+ sp[2].nx = +0.0;
+ sp[2].ny = +1.0;
+ sp[2].d = -(rRange.getMinY());
+ sp[2].clipmask = (RectClipFlags::TOP << 4) | RectClipFlags::TOP; // 0100 0100
+ sp[3].nx = +0.0;
+ sp[3].ny = -1.0;
+ sp[3].d = +(rRange.getMaxY());
+ sp[3].clipmask = (RectClipFlags::BOTTOM << 4) | RectClipFlags::BOTTOM; // 1000 1000
+
+ // retrieve the number of vertices of the triangulated polygon
+ const sal_uInt32 nVertexCount = rCandidate.count();
+
+ if(nVertexCount)
+ {
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Upper bound for the maximal number of vertices when intersecting an
+ // axis-aligned rectangle with a triangle in E2
+ //
+ // The rectangle and the triangle are in general position, and have 4 and 3
+ // vertices, respectively.
+ //
+ // Lemma: Since the rectangle is a convex polygon ( see
+ // http://mathworld.wolfram.com/ConvexPolygon.html for a definition), and
+ // has no holes, it follows that any straight line will intersect the
+ // rectangle's border line at utmost two times (with the usual
+ // tie-breaking rule, if the intersection exactly hits an already existing
+ // rectangle vertex, that this intersection is only attributed to one of
+ // the adjoining edges). Thus, having a rectangle intersected with
+ // a half-plane (one side of a straight line denotes 'inside', the
+ // other 'outside') will at utmost add _one_ vertex to the resulting
+ // intersection polygon (adding two intersection vertices, and removing at
+ // least one rectangle vertex):
+ //
+ // *
+ // +--+-----------------+
+ // | * |
+ // |* |
+ // + |
+ // *| |
+ // * | |
+ // +--------------------+
+ //
+ // Proof: If the straight line intersects the rectangle two
+ // times, it does so for distinct edges, i.e. the intersection has
+ // minimally one of the rectangle's vertices on either side of the straight
+ // line (but maybe more). Thus, the intersection with a half-plane has
+ // minimally _one_ rectangle vertex removed from the resulting clip
+ // polygon, and therefore, a clip against a half-plane has the net effect
+ // of adding at utmost _one_ vertex to the resulting clip polygon.
+ //
+ // Theorem: The intersection of a rectangle and a triangle results in a
+ // polygon with at utmost 7 vertices.
+ //
+ // Proof: The inside of the triangle can be described as the consecutive
+ // intersection with three half-planes. Together with the lemma above, this
+ // results in at utmost 3 additional vertices added to the already existing 4
+ // rectangle vertices.
+ //
+ // This upper bound is attained with the following example configuration:
+ //
+ // *
+ // ***
+ // ** *
+ // ** *
+ // ** *
+ // ** *
+ // ** *
+ // ** *
+ // ** *
+ // ** *
+ // ** *
+ // ----*2--------3 *
+ // | ** |*
+ // 1* 4
+ // **| *|
+ // ** | * |
+ // **| * |
+ // 7* * |
+ // --*6-----5-----
+ // ** *
+ // **
+ //
+ // As we need to scissor all triangles against the
+ // output rectangle we employ an output buffer for the
+ // resulting vertices. the question is how large this
+ // buffer needs to be compared to the number of
+ // incoming vertices. this buffer needs to hold at
+ // most the number of original vertices times '7'. see
+ // figure above for an example. scissoring triangles
+ // with the cohen-sutherland line clipping algorithm
+ // as implemented here will result in a triangle fan
+ // which will be rendered as separate triangles to
+ // avoid pipeline stalls for each scissored
+ // triangle. creating separate triangles from a
+ // triangle fan produces (n-2)*3 vertices where n is
+ // the number of vertices of the original triangle
+ // fan. for the maximum number of 7 vertices of
+ // resulting triangle fans we therefore need 15 times
+ // the number of original vertices.
+ //
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////
+
+ //const size_t nBufferSize = sizeof(vertex)*(nVertexCount*16);
+ //vertex *pVertices = (vertex*)alloca(nBufferSize);
+ //sal_uInt32 nNumOutput = 0;
+
+ // we need to clip this triangle against the output rectangle
+ // to ensure that the resulting texture coordinates are in
+ // the valid range from [0<=st<=1]. under normal circustances
+ // we could use the BORDERCOLOR renderstate but some cards
+ // seem to ignore this feature.
+ ::basegfx::B2DPoint stack[3];
+ unsigned int clipflag = 0;
+
+ for(sal_uInt32 nIndex=0; nIndex<nVertexCount; ++nIndex)
+ {
+ // rotate stack
+ stack[0] = stack[1];
+ stack[1] = stack[2];
+ stack[2] = rCandidate.getB2DPoint(nIndex);
+
+ // clipping judgement
+ clipflag |= !(rRange.isInside(stack[2]));
+
+ if(nIndex > 1)
+ {
+ // consume vertices until a single seperate triangle has been visited.
+ if(!((nIndex+1)%3))
+ {
+ // if any of the last three vertices was outside
+ // we need to scissor against the destination rectangle
+ if(clipflag & 7)
+ {
+ ::basegfx::B2DPoint buf0[16];
+ ::basegfx::B2DPoint buf1[16];
+
+ sal_uInt32 vertex_count = 3;
+
+ // clip against all 4 planes passing the result of
+ // each plane as the input to the next using a double buffer
+ vertex_count = scissorLineSegment(stack,vertex_count,buf1,&sp[0],rRange);
+ vertex_count = scissorLineSegment(buf1,vertex_count,buf0,&sp[1],rRange);
+ vertex_count = scissorLineSegment(buf0,vertex_count,buf1,&sp[2],rRange);
+ vertex_count = scissorLineSegment(buf1,vertex_count,buf0,&sp[3],rRange);
+
+ if(vertex_count >= 3)
+ {
+ // convert triangle fan back to triangle list.
+ ::basegfx::B2DPoint v0(buf0[0]);
+ ::basegfx::B2DPoint v1(buf0[1]);
+ for(sal_uInt32 i=2; i<vertex_count; ++i)
+ {
+ ::basegfx::B2DPoint v2(buf0[i]);
+ aResult.append(v0);
+ aResult.append(v1);
+ aResult.append(v2);
+ v1 = v2;
+ }
+ }
+ }
+ else
+ {
+ // the last triangle has not been altered, simply copy to result
+ for(sal_uInt32 i=0; i<3; ++i)
+ aResult.append(stack[i]);
+ }
+ }
+ }
+
+ clipflag <<= 1;
+ }
+ }
+ }
+
+ return aResult;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+// eof
diff --git a/basegfx/source/polygon/b2dpolygoncutandtouch.cxx b/basegfx/source/polygon/b2dpolygoncutandtouch.cxx
new file mode 100644
index 000000000000..da6ff8904725
--- /dev/null
+++ b/basegfx/source/polygon/b2dpolygoncutandtouch.cxx
@@ -0,0 +1,1211 @@
+/*************************************************************************
+ *
+ * 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: b2dpolygoncutandtouch.cxx,v $
+ *
+ * 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/polygon/b2dpolygoncutandtouch.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+
+#include <vector>
+#include <algorithm>
+
+//////////////////////////////////////////////////////////////////////////////
+// defines
+
+#define SUBDIVIDE_FOR_CUT_TEST_COUNT (50)
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace
+ {
+ ////////////////////////////////////////////////////////////////////////////////
+
+ class temporaryPoint
+ {
+ B2DPoint maPoint; // the new point
+ sal_uInt32 mnIndex; // index after which to insert
+ double mfCut; // parametric cut description [0.0 .. 1.0]
+
+ public:
+ temporaryPoint(const B2DPoint& rNewPoint, sal_uInt32 nIndex, double fCut)
+ : maPoint(rNewPoint),
+ mnIndex(nIndex),
+ mfCut(fCut)
+ {
+ }
+
+ bool operator<(const temporaryPoint& rComp) const
+ {
+ if(mnIndex == rComp.mnIndex)
+ {
+ return (mfCut < rComp.mfCut);
+ }
+
+ return (mnIndex < rComp.mnIndex);
+ }
+
+ const B2DPoint& getPoint() const { return maPoint; }
+ sal_uInt32 getIndex() const { return mnIndex; }
+ double getCut() const { return mfCut; }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ typedef ::std::vector< temporaryPoint > temporaryPointVector;
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ class temporaryPolygonData
+ {
+ B2DPolygon maPolygon;
+ B2DRange maRange;
+ temporaryPointVector maPoints;
+
+ public:
+ const B2DPolygon& getPolygon() const { return maPolygon; }
+ void setPolygon(const B2DPolygon& rNew) { maPolygon = rNew; maRange = tools::getRange(maPolygon); }
+ const B2DRange& getRange() const { return maRange; }
+ temporaryPointVector& getTemporaryPointVector() { return maPoints; }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ B2DPolygon mergeTemporaryPointsAndPolygon(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints)
+ {
+ // #i76891# mergeTemporaryPointsAndPolygon redesigned to be able to correctly handle
+ // single edges with/without control points
+ // #i101491# added counter for non-changing element count
+ const sal_uInt32 nTempPointCount(rTempPoints.size());
+
+ if(nTempPointCount)
+ {
+ B2DPolygon aRetval;
+ const sal_uInt32 nCount(rCandidate.count());
+
+ if(nCount)
+ {
+ // sort temp points to assure increasing fCut values and increasing indices
+ ::std::sort(rTempPoints.begin(), rTempPoints.end());
+
+ // prepare loop
+ B2DCubicBezier aEdge;
+ sal_uInt32 nNewInd(0L);
+
+ // add start point
+ aRetval.append(rCandidate.getB2DPoint(0));
+
+ for(sal_uInt32 a(0L); a < nCount; a++)
+ {
+ // get edge
+ rCandidate.getBezierSegment(a, aEdge);
+
+ if(aEdge.isBezier())
+ {
+ // control vectors involved for this edge
+ double fLeftStart(0.0);
+
+ // now add all points targeted to be at this index
+ while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a)
+ {
+ const temporaryPoint& rTempPoint = rTempPoints[nNewInd++];
+
+ // split curve segment. Splits need to come sorted and need to be < 1.0. Also,
+ // since original segment is consumed from left to right, the cut values need
+ // to be scaled to the remaining part
+ B2DCubicBezier aLeftPart;
+ const double fRelativeSplitPoint((rTempPoint.getCut() - fLeftStart) / (1.0 - fLeftStart));
+ aEdge.split(fRelativeSplitPoint, &aLeftPart, &aEdge);
+ fLeftStart = rTempPoint.getCut();
+
+ // add left bow
+ aRetval.appendBezierSegment(aLeftPart.getControlPointA(), aLeftPart.getControlPointB(), rTempPoint.getPoint());
+ }
+
+ // add remaining bow
+ aRetval.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint());
+ }
+ else
+ {
+ // add all points targeted to be at this index
+ while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a)
+ {
+ const temporaryPoint& rTempPoint = rTempPoints[nNewInd++];
+ const B2DPoint aNewPoint(rTempPoint.getPoint());
+
+ // do not add points double
+ if(!aRetval.getB2DPoint(aRetval.count() - 1L).equal(aNewPoint))
+ {
+ aRetval.append(aNewPoint);
+ }
+ }
+
+ // add edge end point
+ aRetval.append(aEdge.getEndPoint());
+ }
+ }
+ }
+
+ if(rCandidate.isClosed())
+ {
+ // set closed flag and correct last point (which is added double now).
+ tools::closeWithGeometryChange(aRetval);
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void adaptAndTransferCutsWithBezierSegment(
+ const temporaryPointVector& rPointVector, const B2DPolygon& rPolygon,
+ sal_uInt32 nInd, temporaryPointVector& rTempPoints)
+ {
+ // assuming that the subdivision to create rPolygon used aequidistant pieces
+ // (as in adaptiveSubdivideByCount) it is now possible to calculate back the
+ // cut positions in the polygon to relative cut positions on the original bezier
+ // segment.
+ const sal_uInt32 nTempPointCount(rPointVector.size());
+ const sal_uInt32 nEdgeCount(rPolygon.count() ? rPolygon.count() - 1L : 0L);
+
+ if(nTempPointCount && nEdgeCount)
+ {
+ for(sal_uInt32 a(0L); a < nTempPointCount; a++)
+ {
+ const temporaryPoint& rTempPoint = rPointVector[a];
+ const double fCutPosInPolygon((double)rTempPoint.getIndex() + rTempPoint.getCut());
+ const double fRelativeCutPos(fCutPosInPolygon / (double)nEdgeCount);
+ rTempPoints.push_back(temporaryPoint(rTempPoint.getPoint(), nInd, fRelativeCutPos));
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ } // end of anonymous namespace
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace
+ {
+ ////////////////////////////////////////////////////////////////////////////////
+ // predefines for calls to this methods before method implementation
+
+ void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints);
+ void findTouches(const B2DPolygon& rEdgePolygon, const B2DPolygon& rPointPolygon, temporaryPointVector& rTempPoints);
+ void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB);
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void findEdgeCutsTwoEdges(
+ const B2DPoint& rCurrA, const B2DPoint& rNextA,
+ const B2DPoint& rCurrB, const B2DPoint& rNextB,
+ sal_uInt32 nIndA, sal_uInt32 nIndB,
+ temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
+ {
+ // no null length edges
+ if(!(rCurrA.equal(rNextA) || rCurrB.equal(rNextB)))
+ {
+ // no common start/end points, this can be no cuts
+ if(!(rCurrB.equal(rCurrA) || rCurrB.equal(rNextA) || rNextB.equal(rCurrA) || rNextB.equal(rNextA)))
+ {
+ const B2DVector aVecA(rNextA - rCurrA);
+ const B2DVector aVecB(rNextB - rCurrB);
+ double fCut(aVecA.cross(aVecB));
+
+ if(!fTools::equalZero(fCut))
+ {
+ const double fZero(0.0);
+ const double fOne(1.0);
+ fCut = (aVecB.getY() * (rCurrB.getX() - rCurrA.getX()) + aVecB.getX() * (rCurrA.getY() - rCurrB.getY())) / fCut;
+
+ if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne))
+ {
+ // it's a candidate, but also need to test parameter value of cut on line 2
+ double fCut2;
+
+ // choose the more precise version
+ if(fabs(aVecB.getX()) > fabs(aVecB.getY()))
+ {
+ fCut2 = (rCurrA.getX() + (fCut * aVecA.getX()) - rCurrB.getX()) / aVecB.getX();
+ }
+ else
+ {
+ fCut2 = (rCurrA.getY() + (fCut * aVecA.getY()) - rCurrB.getY()) / aVecB.getY();
+ }
+
+ if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne))
+ {
+ // cut is in range, add point. Two edges can have only one cut, but
+ // add a cut point to each list. The lists may be the same for
+ // self intersections.
+ const B2DPoint aCutPoint(interpolate(rCurrA, rNextA, fCut));
+ rTempPointsA.push_back(temporaryPoint(aCutPoint, nIndA, fCut));
+ rTempPointsB.push_back(temporaryPoint(aCutPoint, nIndB, fCut2));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void findCutsAndTouchesAndCommonForBezier(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
+ {
+ // #i76891#
+ // This new method is necessary since in findEdgeCutsBezierAndEdge and in findEdgeCutsTwoBeziers
+ // it is not sufficient to use findCuts() recursively. This will indeed find the cuts between the
+ // segments of the two temporarily adaptive subdivided bezier segments, but not the touches or
+ // equal points of them.
+ // It would be possible to find the toches using findTouches(), but at last with commpn points
+ // the adding of cut points (temporary points) would fail. But for these temporarily adaptive
+ // subdivided bezier segments, common points may be not very likely, but the bug shows that it
+ // happens.
+ // Touch points are a little bit more likely than common points. All in all it is best to use
+ // a specialized method here which can profit from knowing that it is working on a special
+ // family of B2DPolygons: no curve segments included and not closed.
+ OSL_ENSURE(!rCandidateA.areControlPointsUsed() && !rCandidateB.areControlPointsUsed(), "findCutsAndTouchesAndCommonForBezier only works with subdivided polygons (!)");
+ OSL_ENSURE(!rCandidateA.isClosed() && !rCandidateB.isClosed(), "findCutsAndTouchesAndCommonForBezier only works with opened polygons (!)");
+ const sal_uInt32 nPointCountA(rCandidateA.count());
+ const sal_uInt32 nPointCountB(rCandidateB.count());
+
+ if(nPointCountA > 1 && nPointCountB > 1)
+ {
+ const sal_uInt32 nEdgeCountA(nPointCountA - 1);
+ const sal_uInt32 nEdgeCountB(nPointCountB - 1);
+ B2DPoint aCurrA(rCandidateA.getB2DPoint(0L));
+
+ for(sal_uInt32 a(0L); a < nEdgeCountA; a++)
+ {
+ const B2DPoint aNextA(rCandidateA.getB2DPoint(a + 1L));
+ const B2DRange aRangeA(aCurrA, aNextA);
+ B2DPoint aCurrB(rCandidateB.getB2DPoint(0L));
+
+ for(sal_uInt32 b(0L); b < nEdgeCountB; b++)
+ {
+ const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1L));
+ const B2DRange aRangeB(aCurrB, aNextB);
+
+ if(aRangeA.overlaps(aRangeB))
+ {
+ // no null length edges
+ if(!(aCurrA.equal(aNextA) || aCurrB.equal(aNextB)))
+ {
+ const B2DVector aVecA(aNextA - aCurrA);
+ const B2DVector aVecB(aNextB - aCurrB);
+ double fCutA(aVecA.cross(aVecB));
+
+ if(!fTools::equalZero(fCutA))
+ {
+ const double fZero(0.0);
+ const double fOne(1.0);
+ fCutA = (aVecB.getY() * (aCurrB.getX() - aCurrA.getX()) + aVecB.getX() * (aCurrA.getY() - aCurrB.getY())) / fCutA;
+
+ // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered
+ // as 0.0 cut. The 1.0 cut will be registered in the next loop step
+ if(fTools::moreOrEqual(fCutA, fZero) && fTools::less(fCutA, fOne))
+ {
+ // it's a candidate, but also need to test parameter value of cut on line 2
+ double fCutB;
+
+ // choose the more precise version
+ if(fabs(aVecB.getX()) > fabs(aVecB.getY()))
+ {
+ fCutB = (aCurrA.getX() + (fCutA * aVecA.getX()) - aCurrB.getX()) / aVecB.getX();
+ }
+ else
+ {
+ fCutB = (aCurrA.getY() + (fCutA * aVecA.getY()) - aCurrB.getY()) / aVecB.getY();
+ }
+
+ // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered
+ // as 0.0 cut. The 1.0 cut will be registered in the next loop step
+ if(fTools::moreOrEqual(fCutB, fZero) && fTools::less(fCutB, fOne))
+ {
+ // cut is in both ranges. Add points for A and B
+ if(fTools::equalZero(fCutA))
+ {
+ // ignore for start point in first edge; this is handled
+ // by outer methods and would just produce a double point
+ if(a)
+ {
+ rTempPointsA.push_back(temporaryPoint(aCurrA, a, 0.0));
+ }
+ }
+ else
+ {
+ const B2DPoint aCutPoint(interpolate(aCurrA, aNextA, fCutA));
+ rTempPointsA.push_back(temporaryPoint(aCutPoint, a, fCutA));
+ }
+
+ if(fTools::equalZero(fCutB))
+ {
+ // ignore for start point in first edge; this is handled
+ // by outer methods and would just produce a double point
+ if(b)
+ {
+ rTempPointsB.push_back(temporaryPoint(aCurrB, b, 0.0));
+ }
+ }
+ else
+ {
+ const B2DPoint aCutPoint(interpolate(aCurrB, aNextB, fCutB));
+ rTempPointsB.push_back(temporaryPoint(aCutPoint, b, fCutB));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // prepare next step
+ aCurrB = aNextB;
+ }
+
+ // prepare next step
+ aCurrA = aNextA;
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void findEdgeCutsBezierAndEdge(
+ const B2DCubicBezier& rCubicA,
+ const B2DPoint& rCurrB, const B2DPoint& rNextB,
+ sal_uInt32 nIndA, sal_uInt32 nIndB,
+ temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
+ {
+ // find all cuts between given bezier segment and edge. Add an entry to the tempPoints
+ // for each common point with the cut value describing the relative position on given
+ // bezier segment and edge.
+ B2DPolygon aTempPolygonA;
+ B2DPolygon aTempPolygonEdge;
+ temporaryPointVector aTempPointVectorA;
+ temporaryPointVector aTempPointVectorEdge;
+
+ // create subdivided polygons and find cuts between them
+ // Keep adaptiveSubdivideByCount due to needed quality
+ aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
+ aTempPolygonA.append(rCubicA.getStartPoint());
+ rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT);
+ aTempPolygonEdge.append(rCurrB);
+ aTempPolygonEdge.append(rNextB);
+
+ // #i76891# using findCuts recursively is not sufficient here
+ findCutsAndTouchesAndCommonForBezier(aTempPolygonA, aTempPolygonEdge, aTempPointVectorA, aTempPointVectorEdge);
+
+ if(aTempPointVectorA.size())
+ {
+ // adapt tempVector entries to segment
+ adaptAndTransferCutsWithBezierSegment(aTempPointVectorA, aTempPolygonA, nIndA, rTempPointsA);
+ }
+
+ // append remapped tempVector entries for edge to tempPoints for edge
+ for(sal_uInt32 a(0L); a < aTempPointVectorEdge.size(); a++)
+ {
+ const temporaryPoint& rTempPoint = aTempPointVectorEdge[a];
+ rTempPointsB.push_back(temporaryPoint(rTempPoint.getPoint(), nIndB, rTempPoint.getCut()));
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void findEdgeCutsTwoBeziers(
+ const B2DCubicBezier& rCubicA,
+ const B2DCubicBezier& rCubicB,
+ sal_uInt32 nIndA, sal_uInt32 nIndB,
+ temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
+ {
+ // find all cuts between the two given bezier segments. Add an entry to the tempPoints
+ // for each common point with the cut value describing the relative position on given
+ // bezier segments.
+ B2DPolygon aTempPolygonA;
+ B2DPolygon aTempPolygonB;
+ temporaryPointVector aTempPointVectorA;
+ temporaryPointVector aTempPointVectorB;
+
+ // create subdivided polygons and find cuts between them
+ // Keep adaptiveSubdivideByCount due to needed quality
+ aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
+ aTempPolygonA.append(rCubicA.getStartPoint());
+ rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT);
+ aTempPolygonB.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
+ aTempPolygonB.append(rCubicB.getStartPoint());
+ rCubicB.adaptiveSubdivideByCount(aTempPolygonB, SUBDIVIDE_FOR_CUT_TEST_COUNT);
+
+ // #i76891# using findCuts recursively is not sufficient here
+ findCutsAndTouchesAndCommonForBezier(aTempPolygonA, aTempPolygonB, aTempPointVectorA, aTempPointVectorB);
+
+ if(aTempPointVectorA.size())
+ {
+ // adapt tempVector entries to segment
+ adaptAndTransferCutsWithBezierSegment(aTempPointVectorA, aTempPolygonA, nIndA, rTempPointsA);
+ }
+
+ if(aTempPointVectorB.size())
+ {
+ // adapt tempVector entries to segment
+ adaptAndTransferCutsWithBezierSegment(aTempPointVectorB, aTempPolygonB, nIndB, rTempPointsB);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void findEdgeCutsOneBezier(
+ const B2DCubicBezier& rCubicA,
+ sal_uInt32 nInd, temporaryPointVector& rTempPoints)
+ {
+ // avoid expensive part of this method if possible
+ // TODO: use hasAnyExtremum() method instead when it becomes available
+ double fDummy;
+ const bool bHasAnyExtremum = rCubicA.getMinimumExtremumPosition( fDummy );
+ if( !bHasAnyExtremum )
+ return;
+
+ // find all self-intersections on the given bezier segment. Add an entry to the tempPoints
+ // for each self intersection point with the cut value describing the relative position on given
+ // bezier segment.
+ B2DPolygon aTempPolygon;
+ temporaryPointVector aTempPointVector;
+
+ // create subdivided polygon and find cuts on it
+ // Keep adaptiveSubdivideByCount due to needed quality
+ aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
+ aTempPolygon.append(rCubicA.getStartPoint());
+ rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT);
+ findCuts(aTempPolygon, aTempPointVector);
+
+ if(aTempPointVector.size())
+ {
+ // adapt tempVector entries to segment
+ adaptAndTransferCutsWithBezierSegment(aTempPointVector, aTempPolygon, nInd, rTempPoints);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints)
+ {
+ // find out if there are edges with intersections (self-cuts). If yes, add
+ // entries to rTempPoints accordingly
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+
+ if(nEdgeCount)
+ {
+ const bool bCurvesInvolved(rCandidate.areControlPointsUsed());
+
+ if(bCurvesInvolved)
+ {
+ B2DCubicBezier aCubicA;
+ B2DCubicBezier aCubicB;
+
+ for(sal_uInt32 a(0L); a < nEdgeCount - 1L; a++)
+ {
+ rCandidate.getBezierSegment(a, aCubicA);
+ aCubicA.testAndSolveTrivialBezier();
+ const bool bEdgeAIsCurve(aCubicA.isBezier());
+ const B2DRange aRangeA(aCubicA.getRange());
+
+ if(bEdgeAIsCurve)
+ {
+ // curved segments may have self-intersections, do not forget those (!)
+ findEdgeCutsOneBezier(aCubicA, a, rTempPoints);
+ }
+
+ for(sal_uInt32 b(a + 1L); b < nEdgeCount; b++)
+ {
+ rCandidate.getBezierSegment(b, aCubicB);
+ aCubicB.testAndSolveTrivialBezier();
+ const bool bEdgeBIsCurve(aCubicB.isBezier());
+ const B2DRange aRangeB(aCubicB.getRange());
+
+ // only overlapping segments need to be tested
+ // consecutive segments touch of course
+ bool bOverlap = false;
+ if( b > a+1)
+ bOverlap = aRangeA.overlaps(aRangeB);
+ else
+ bOverlap = aRangeA.overlapsMore(aRangeB);
+ if( bOverlap)
+ {
+ if(bEdgeAIsCurve && bEdgeBIsCurve)
+ {
+ // test for bezier-bezier cuts
+ findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPoints, rTempPoints);
+ }
+ else if(bEdgeAIsCurve)
+ {
+ // test for bezier-edge cuts
+ findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, rTempPoints, rTempPoints);
+ }
+ else if(bEdgeBIsCurve)
+ {
+ // test for bezier-edge cuts
+ findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, rTempPoints, rTempPoints);
+ }
+ else
+ {
+ // test for simple edge-edge cuts
+ findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(),
+ a, b, rTempPoints, rTempPoints);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ B2DPoint aCurrA(rCandidate.getB2DPoint(0L));
+
+ for(sal_uInt32 a(0L); a < nEdgeCount - 1L; a++)
+ {
+ const B2DPoint aNextA(rCandidate.getB2DPoint(a + 1L == nPointCount ? 0L : a + 1L));
+ const B2DRange aRangeA(aCurrA, aNextA);
+ B2DPoint aCurrB(rCandidate.getB2DPoint(a + 1L));
+
+ for(sal_uInt32 b(a + 1L); b < nEdgeCount; b++)
+ {
+ const B2DPoint aNextB(rCandidate.getB2DPoint(b + 1L == nPointCount ? 0L : b + 1L));
+ const B2DRange aRangeB(aCurrB, aNextB);
+
+ // consecutive segments touch of course
+ bool bOverlap = false;
+ if( b > a+1)
+ bOverlap = aRangeA.overlaps(aRangeB);
+ else
+ bOverlap = aRangeA.overlapsMore(aRangeB);
+ if( bOverlap)
+ {
+ findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPoints, rTempPoints);
+ }
+
+ // prepare next step
+ aCurrB = aNextB;
+ }
+
+ // prepare next step
+ aCurrA = aNextA;
+ }
+ }
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ } // end of anonymous namespace
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace
+ {
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void findTouchesOnEdge(
+ const B2DPoint& rCurr, const B2DPoint& rNext, const B2DPolygon& rPointPolygon,
+ sal_uInt32 nInd, temporaryPointVector& rTempPoints)
+ {
+ // find out if points from rPointPolygon are positioned on given edge. If Yes, add
+ // points there to represent touches (which may be enter or leave nodes later).
+ const sal_uInt32 nPointCount(rPointPolygon.count());
+
+ if(nPointCount)
+ {
+ const B2DRange aRange(rCurr, rNext);
+ const B2DVector aEdgeVector(rNext - rCurr);
+ B2DVector aNormalizedEdgeVector(aEdgeVector);
+ aNormalizedEdgeVector.normalize();
+ bool bTestUsingX(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY()));
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B2DPoint aTestPoint(rPointPolygon.getB2DPoint(a));
+
+ if(aRange.isInside(aTestPoint))
+ {
+ if(!aTestPoint.equal(rCurr) && !aTestPoint.equal(rNext))
+ {
+ const B2DVector aTestVector(aTestPoint - rCurr);
+
+ if(areParallel(aNormalizedEdgeVector, aTestVector))
+ {
+ const double fCut((bTestUsingX)
+ ? aTestVector.getX() / aEdgeVector.getX()
+ : aTestVector.getY() / aEdgeVector.getY());
+ const double fZero(0.0);
+ const double fOne(1.0);
+
+ if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne))
+ {
+ rTempPoints.push_back(temporaryPoint(aTestPoint, nInd, fCut));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void findTouchesOnCurve(
+ const B2DCubicBezier& rCubicA, const B2DPolygon& rPointPolygon,
+ sal_uInt32 nInd, temporaryPointVector& rTempPoints)
+ {
+ // find all points from rPointPolygon which touch the given bezier segment. Add an entry
+ // for each touch to the given pointVector. The cut for that entry is the relative position on
+ // the given bezier segment.
+ B2DPolygon aTempPolygon;
+ temporaryPointVector aTempPointVector;
+
+ // create subdivided polygon and find cuts on it
+ // Keep adaptiveSubdivideByCount due to needed quality
+ aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
+ aTempPolygon.append(rCubicA.getStartPoint());
+ rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT);
+ findTouches(aTempPolygon, rPointPolygon, aTempPointVector);
+
+ if(aTempPointVector.size())
+ {
+ // adapt tempVector entries to segment
+ adaptAndTransferCutsWithBezierSegment(aTempPointVector, aTempPolygon, nInd, rTempPoints);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void findTouches(const B2DPolygon& rEdgePolygon, const B2DPolygon& rPointPolygon, temporaryPointVector& rTempPoints)
+ {
+ // find out if points from rPointPolygon touch edges from rEdgePolygon. If yes,
+ // add entries to rTempPoints
+ const sal_uInt32 nPointCount(rPointPolygon.count());
+ const sal_uInt32 nEdgePointCount(rEdgePolygon.count());
+
+ if(nPointCount && nEdgePointCount)
+ {
+ const sal_uInt32 nEdgeCount(rEdgePolygon.isClosed() ? nEdgePointCount : nEdgePointCount - 1L);
+ B2DPoint aCurr(rEdgePolygon.getB2DPoint(0));
+
+ for(sal_uInt32 a(0L); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nEdgePointCount);
+ const B2DPoint aNext(rEdgePolygon.getB2DPoint(nNextIndex));
+
+ if(!aCurr.equal(aNext))
+ {
+ bool bHandleAsSimpleEdge(true);
+
+ if(rEdgePolygon.areControlPointsUsed())
+ {
+ const B2DPoint aNextControlPoint(rEdgePolygon.getNextControlPoint(a));
+ const B2DPoint aPrevControlPoint(rEdgePolygon.getPrevControlPoint(nNextIndex));
+ const bool bEdgeIsCurve(!aNextControlPoint.equal(aCurr) || !aPrevControlPoint.equal(aNext));
+
+ if(bEdgeIsCurve)
+ {
+ bHandleAsSimpleEdge = false;
+ const B2DCubicBezier aCubicA(aCurr, aNextControlPoint, aPrevControlPoint, aNext);
+ findTouchesOnCurve(aCubicA, rPointPolygon, a, rTempPoints);
+ }
+ }
+
+ if(bHandleAsSimpleEdge)
+ {
+ findTouchesOnEdge(aCurr, aNext, rPointPolygon, a, rTempPoints);
+ }
+ }
+
+ // next step
+ aCurr = aNext;
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ } // end of anonymous namespace
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace
+ {
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
+ {
+ // find out if edges from both polygons cut. If so, add entries to rTempPoints which
+ // should be added to the polygons accordingly
+ const sal_uInt32 nPointCountA(rCandidateA.count());
+ const sal_uInt32 nPointCountB(rCandidateB.count());
+
+ if(nPointCountA && nPointCountB)
+ {
+ const sal_uInt32 nEdgeCountA(rCandidateA.isClosed() ? nPointCountA : nPointCountA - 1L);
+ const sal_uInt32 nEdgeCountB(rCandidateB.isClosed() ? nPointCountB : nPointCountB - 1L);
+
+ if(nEdgeCountA && nEdgeCountB)
+ {
+ const bool bCurvesInvolved(rCandidateA.areControlPointsUsed() || rCandidateB.areControlPointsUsed());
+
+ if(bCurvesInvolved)
+ {
+ B2DCubicBezier aCubicA;
+ B2DCubicBezier aCubicB;
+
+ for(sal_uInt32 a(0L); a < nEdgeCountA; a++)
+ {
+ rCandidateA.getBezierSegment(a, aCubicA);
+ aCubicA.testAndSolveTrivialBezier();
+ const bool bEdgeAIsCurve(aCubicA.isBezier());
+ const B2DRange aRangeA(aCubicA.getRange());
+
+ for(sal_uInt32 b(0L); b < nEdgeCountB; b++)
+ {
+ rCandidateB.getBezierSegment(b, aCubicB);
+ aCubicB.testAndSolveTrivialBezier();
+ const bool bEdgeBIsCurve(aCubicB.isBezier());
+ const B2DRange aRangeB(aCubicB.getRange());
+
+ // consecutive segments touch of course
+ bool bOverlap = false;
+ if( b > a+1)
+ bOverlap = aRangeA.overlaps(aRangeB);
+ else
+ bOverlap = aRangeA.overlapsMore(aRangeB);
+ if( bOverlap)
+ {
+ if(bEdgeAIsCurve && bEdgeBIsCurve)
+ {
+ // test for bezier-bezier cuts
+ findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPointsA, rTempPointsB);
+ }
+ else if(bEdgeAIsCurve)
+ {
+ // test for bezier-edge cuts
+ findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, rTempPointsA, rTempPointsB);
+ }
+ else if(bEdgeBIsCurve)
+ {
+ // test for bezier-edge cuts
+ findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, rTempPointsB, rTempPointsA);
+ }
+ else
+ {
+ // test for simple edge-edge cuts
+ findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(),
+ a, b, rTempPointsA, rTempPointsB);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ B2DPoint aCurrA(rCandidateA.getB2DPoint(0L));
+
+ for(sal_uInt32 a(0L); a < nEdgeCountA; a++)
+ {
+ const B2DPoint aNextA(rCandidateA.getB2DPoint(a + 1L == nPointCountA ? 0L : a + 1L));
+ const B2DRange aRangeA(aCurrA, aNextA);
+ B2DPoint aCurrB(rCandidateB.getB2DPoint(0L));
+
+ for(sal_uInt32 b(0L); b < nEdgeCountB; b++)
+ {
+ const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1L == nPointCountB ? 0L : b + 1L));
+ const B2DRange aRangeB(aCurrB, aNextB);
+
+ // consecutive segments touch of course
+ bool bOverlap = false;
+ if( b > a+1)
+ bOverlap = aRangeA.overlaps(aRangeB);
+ else
+ bOverlap = aRangeA.overlapsMore(aRangeB);
+ if( bOverlap)
+ {
+ // test for simple edge-edge cuts
+ findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPointsA, rTempPointsB);
+ }
+
+ // prepare next step
+ aCurrB = aNextB;
+ }
+
+ // prepare next step
+ aCurrA = aNextA;
+ }
+ }
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ } // end of anonymous namespace
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ ////////////////////////////////////////////////////////////////////////////////
+
+ B2DPolygon addPointsAtCutsAndTouches(const B2DPolygon& rCandidate)
+ {
+ if(rCandidate.count())
+ {
+ temporaryPointVector aTempPoints;
+
+ findTouches(rCandidate, rCandidate, aTempPoints);
+ findCuts(rCandidate, aTempPoints);
+
+ return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rCandidate, bool bSelfIntersections)
+ {
+ const sal_uInt32 nCount(rCandidate.count());
+
+ if(nCount)
+ {
+ B2DPolyPolygon aRetval;
+
+ if(1L == nCount)
+ {
+ if(bSelfIntersections)
+ {
+ // remove self intersections
+ aRetval.append(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(0L)));
+ }
+ else
+ {
+ // copy source
+ aRetval = rCandidate;
+ }
+ }
+ else
+ {
+ // first solve self cuts and self touches for all contained single polygons
+ temporaryPolygonData *pTempData = new temporaryPolygonData[nCount];
+ sal_uInt32 a, b;
+
+ for(a = 0L; a < nCount; a++)
+ {
+ if(bSelfIntersections)
+ {
+ // use polygons with solved self intersections
+ pTempData[a].setPolygon(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(a)));
+ }
+ else
+ {
+ // copy given polygons
+ pTempData[a].setPolygon(rCandidate.getB2DPolygon(a));
+ }
+ }
+
+ // now cuts and touches between the polygons
+ for(a = 0L; a < nCount; a++)
+ {
+ for(b = 0L; b < nCount; b++)
+ {
+ if(a != b)
+ {
+ // look for touches, compare each edge polygon to all other points
+ if(pTempData[a].getRange().overlaps(pTempData[b].getRange()))
+ {
+ findTouches(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector());
+ }
+ }
+
+ if(a < b)
+ {
+ // look for cuts, compare each edge polygon to following ones
+ if(pTempData[a].getRange().overlaps(pTempData[b].getRange()))
+ {
+ findCuts(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector(), pTempData[b].getTemporaryPointVector());
+ }
+ }
+ }
+ }
+
+ // consolidate the result
+ for(a = 0L; a < nCount; a++)
+ {
+ aRetval.append(mergeTemporaryPointsAndPolygon(pTempData[a].getPolygon(), pTempData[a].getTemporaryPointVector()));
+ }
+
+ delete[] pTempData;
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ B2DPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rMask, const B2DPolygon& rCandidate)
+ {
+ if(rCandidate.count())
+ {
+ temporaryPointVector aTempPoints;
+ temporaryPointVector aTempPointsUnused;
+
+ for(sal_uInt32 a(0L); a < rMask.count(); a++)
+ {
+ const B2DPolygon aPartMask(rMask.getB2DPolygon(a));
+
+ findTouches(rCandidate, aPartMask, aTempPoints);
+ findCuts(rCandidate, aPartMask, aTempPoints, aTempPointsUnused);
+ }
+
+ return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rMask, const B2DPolyPolygon& rCandidate)
+ {
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ aRetval.append(addPointsAtCutsAndTouches(rMask, rCandidate.getB2DPolygon(a)));
+ }
+
+ return aRetval;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd)
+ {
+ const sal_uInt32 nCount(rCandidate.count());
+
+ if(nCount && !rStart.equal(rEnd))
+ {
+ const B2DRange aPolygonRange(rCandidate.getB2DRange());
+ const B2DRange aEdgeRange(rStart, rEnd);
+
+ if(aPolygonRange.overlaps(aEdgeRange))
+ {
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nCount : nCount - 1);
+ temporaryPointVector aTempPoints;
+ temporaryPointVector aUnusedTempPoints;
+ B2DCubicBezier aCubic;
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ rCandidate.getBezierSegment(a, aCubic);
+ B2DRange aCubicRange(aCubic.getStartPoint(), aCubic.getEndPoint());
+
+ if(aCubic.isBezier())
+ {
+ aCubicRange.expand(aCubic.getControlPointA());
+ aCubicRange.expand(aCubic.getControlPointB());
+
+ if(aCubicRange.overlaps(aEdgeRange))
+ {
+ findEdgeCutsBezierAndEdge(aCubic, rStart, rEnd, a, 0, aTempPoints, aUnusedTempPoints);
+ }
+ }
+ else
+ {
+ if(aCubicRange.overlaps(aEdgeRange))
+ {
+ findEdgeCutsTwoEdges(aCubic.getStartPoint(), aCubic.getEndPoint(), rStart, rEnd, a, 0, aTempPoints, aUnusedTempPoints);
+ }
+ }
+ }
+
+ return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
+ }
+ }
+
+ return rCandidate;
+ }
+
+ B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd)
+ {
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0); a < rCandidate.count(); a++)
+ {
+ aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(a), rStart, rEnd));
+ }
+
+ return aRetval;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPolyPolygon& rPolyMask)
+ {
+ const sal_uInt32 nCountA(rCandidate.count());
+ const sal_uInt32 nCountM(rPolyMask.count());
+
+ if(nCountA && nCountM)
+ {
+ const B2DRange aRangeA(rCandidate.getB2DRange());
+ const B2DRange aRangeM(rPolyMask.getB2DRange());
+
+ if(aRangeA.overlaps(aRangeM))
+ {
+ const sal_uInt32 nEdgeCountA(rCandidate.isClosed() ? nCountA : nCountA - 1);
+ temporaryPointVector aTempPointsA;
+ temporaryPointVector aUnusedTempPointsB;
+
+ for(sal_uInt32 m(0); m < nCountM; m++)
+ {
+ const B2DPolygon aMask(rPolyMask.getB2DPolygon(m));
+ const sal_uInt32 nCountB(aMask.count());
+
+ if(nCountB)
+ {
+ B2DCubicBezier aCubicA;
+ B2DCubicBezier aCubicB;
+
+ for(sal_uInt32 a(0); a < nEdgeCountA; a++)
+ {
+ rCandidate.getBezierSegment(a, aCubicA);
+ const bool bCubicAIsCurve(aCubicA.isBezier());
+ B2DRange aCubicRangeA(aCubicA.getStartPoint(), aCubicA.getEndPoint());
+
+ if(bCubicAIsCurve)
+ {
+ aCubicRangeA.expand(aCubicA.getControlPointA());
+ aCubicRangeA.expand(aCubicA.getControlPointB());
+ }
+
+ for(sal_uInt32 b(0); b < nCountB; b++)
+ {
+ aMask.getBezierSegment(b, aCubicB);
+ const bool bCubicBIsCurve(aCubicB.isBezier());
+ B2DRange aCubicRangeB(aCubicB.getStartPoint(), aCubicB.getEndPoint());
+
+ if(bCubicBIsCurve)
+ {
+ aCubicRangeB.expand(aCubicB.getControlPointA());
+ aCubicRangeB.expand(aCubicB.getControlPointB());
+ }
+
+ if(aCubicRangeA.overlaps(aCubicRangeB))
+ {
+ if(bCubicAIsCurve && bCubicBIsCurve)
+ {
+ findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, aTempPointsA, aUnusedTempPointsB);
+ }
+ else if(bCubicAIsCurve)
+ {
+ findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, aTempPointsA, aUnusedTempPointsB);
+ }
+ else if(bCubicBIsCurve)
+ {
+ findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, aUnusedTempPointsB, aTempPointsA);
+ }
+ else
+ {
+ findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, aTempPointsA, aUnusedTempPointsB);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return mergeTemporaryPointsAndPolygon(rCandidate, aTempPointsA);
+ }
+ }
+
+ return rCandidate;
+ }
+
+ B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, const B2DPolyPolygon& rMask)
+ {
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0); a < rCandidate.count(); a++)
+ {
+ aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(a), rMask));
+ }
+
+ return aRetval;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx
new file mode 100644
index 000000000000..7485387c6cb9
--- /dev/null
+++ b/basegfx/source/polygon/b2dpolygontools.cxx
@@ -0,0 +1,3631 @@
+/*************************************************************************
+ *
+ * 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: b2dpolygontools.cxx,v $
+ *
+ * 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/numeric/ftools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <osl/diagnose.h>
+#include <rtl/math.hxx>
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/curve/b2dbeziertools.hxx>
+
+#include <numeric>
+#include <limits>
+
+// #i37443#
+#define ANGLE_BOUND_START_VALUE (2.25)
+#define ANGLE_BOUND_MINIMUM_VALUE (0.1)
+#define COUNT_SUBDIVIDE_DEFAULT (4L)
+#ifdef DBG_UTIL
+static double fAngleBoundStartValue = ANGLE_BOUND_START_VALUE;
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ void openWithGeometryChange(B2DPolygon& rCandidate)
+ {
+ if(rCandidate.isClosed())
+ {
+ if(rCandidate.count())
+ {
+ rCandidate.append(rCandidate.getB2DPoint(0));
+
+ if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(0))
+ {
+ rCandidate.setPrevControlPoint(rCandidate.count() - 1, rCandidate.getPrevControlPoint(0));
+ rCandidate.resetPrevControlPoint(0);
+ }
+ }
+
+ rCandidate.setClosed(false);
+ }
+ }
+
+ void closeWithGeometryChange(B2DPolygon& rCandidate)
+ {
+ if(!rCandidate.isClosed())
+ {
+ while(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1))
+ {
+ if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(rCandidate.count() - 1))
+ {
+ rCandidate.setPrevControlPoint(0, rCandidate.getPrevControlPoint(rCandidate.count() - 1));
+ }
+
+ rCandidate.remove(rCandidate.count() - 1);
+ }
+
+ rCandidate.setClosed(true);
+ }
+ }
+
+ void checkClosed(B2DPolygon& rCandidate)
+ {
+ // #i80172# Removed unnecessary assertion
+ // OSL_ENSURE(!rCandidate.isClosed(), "checkClosed: already closed (!)");
+
+ if(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1))
+ {
+ closeWithGeometryChange(rCandidate);
+ }
+ }
+
+ // Get successor and predecessor indices. Returning the same index means there
+ // is none. Same for successor.
+ sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate)
+ {
+ OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
+
+ if(nIndex)
+ {
+ return nIndex - 1L;
+ }
+ else if(rCandidate.count())
+ {
+ return rCandidate.count() - 1L;
+ }
+ else
+ {
+ return nIndex;
+ }
+ }
+
+ sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate)
+ {
+ OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
+
+ if(nIndex + 1L < rCandidate.count())
+ {
+ return nIndex + 1L;
+ }
+ else if(nIndex + 1L == rCandidate.count())
+ {
+ return 0L;
+ }
+ else
+ {
+ return nIndex;
+ }
+ }
+
+ B2VectorOrientation getOrientation(const B2DPolygon& rCandidate)
+ {
+ B2VectorOrientation eRetval(ORIENTATION_NEUTRAL);
+
+ if(rCandidate.count() > 2L || rCandidate.areControlPointsUsed())
+ {
+ const double fSignedArea(getSignedArea(rCandidate));
+
+ if(fTools::equalZero(fSignedArea))
+ {
+ // ORIENTATION_NEUTRAL, already set
+ }
+ if(fSignedArea > 0.0)
+ {
+ eRetval = ORIENTATION_POSITIVE;
+ }
+ else if(fSignedArea < 0.0)
+ {
+ eRetval = ORIENTATION_NEGATIVE;
+ }
+ }
+
+ return eRetval;
+ }
+
+ B2VectorContinuity getContinuityInPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
+ {
+ return rCandidate.getContinuityInPoint(nIndex);
+ }
+
+ B2DPolygon adaptiveSubdivideByDistance(const B2DPolygon& rCandidate, double fDistanceBound)
+ {
+ if(rCandidate.areControlPointsUsed())
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+ B2DPolygon aRetval;
+
+ if(nPointCount)
+ {
+ // prepare edge-oriented loop
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+ B2DCubicBezier aBezier;
+ aBezier.setStartPoint(rCandidate.getB2DPoint(0));
+
+ // perf: try to avoid too many realloctions by guessing the result's pointcount
+ aRetval.reserve(nPointCount*4);
+
+ // add start point (always)
+ aRetval.append(aBezier.getStartPoint());
+
+ for(sal_uInt32 a(0L); a < nEdgeCount; a++)
+ {
+ // get next and control points
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+ aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
+ aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aBezier.testAndSolveTrivialBezier();
+
+ if(aBezier.isBezier())
+ {
+ // add curved edge and generate DistanceBound
+ double fBound(0.0);
+
+ if(0.0 == fDistanceBound)
+ {
+ // If not set, use B2DCubicBezier functionality to guess a rough value
+ const double fRoughLength((aBezier.getEdgeLength() + aBezier.getControlPolygonLength()) / 2.0);
+
+ // take 1/100th of the rough curve length
+ fBound = fRoughLength * 0.01;
+ }
+ else
+ {
+ // use given bound value
+ fBound = fDistanceBound;
+ }
+
+ // make sure bound value is not too small. The base units are 1/100th mm, thus
+ // just make sure it's not smaller then 1/100th of that
+ if(fBound < 0.01)
+ {
+ fBound = 0.01;
+ }
+
+ // call adaptive subdivide which adds edges to aRetval accordingly
+ aBezier.adaptiveSubdivideByDistance(aRetval, fBound);
+ }
+ else
+ {
+ // add non-curved edge
+ aRetval.append(aBezier.getEndPoint());
+ }
+
+ // prepare next step
+ aBezier.setStartPoint(aBezier.getEndPoint());
+ }
+
+ if(rCandidate.isClosed())
+ {
+ // set closed flag and correct last point (which is added double now).
+ closeWithGeometryChange(aRetval);
+ }
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon& rCandidate, double fAngleBound)
+ {
+ if(rCandidate.areControlPointsUsed())
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+ B2DPolygon aRetval;
+
+ if(nPointCount)
+ {
+ // prepare edge-oriented loop
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+ B2DCubicBezier aBezier;
+ aBezier.setStartPoint(rCandidate.getB2DPoint(0));
+
+ // perf: try to avoid too many realloctions by guessing the result's pointcount
+ aRetval.reserve(nPointCount*4);
+
+ // add start point (always)
+ aRetval.append(aBezier.getStartPoint());
+
+ // #i37443# prepare convenient AngleBound if none was given
+ if(0.0 == fAngleBound)
+ {
+#ifdef DBG_UTIL
+ fAngleBound = fAngleBoundStartValue;
+#else
+ fAngleBound = ANGLE_BOUND_START_VALUE;
+#endif
+ }
+ else if(fTools::less(fAngleBound, ANGLE_BOUND_MINIMUM_VALUE))
+ {
+ fAngleBound = 0.1;
+ }
+
+ for(sal_uInt32 a(0L); a < nEdgeCount; a++)
+ {
+ // get next and control points
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+ aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
+ aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aBezier.testAndSolveTrivialBezier();
+
+ if(aBezier.isBezier())
+ {
+ // call adaptive subdivide
+ aBezier.adaptiveSubdivideByAngle(aRetval, fAngleBound, true);
+ }
+ else
+ {
+ // add non-curved edge
+ aRetval.append(aBezier.getEndPoint());
+ }
+
+ // prepare next step
+ aBezier.setStartPoint(aBezier.getEndPoint());
+ }
+
+ if(rCandidate.isClosed())
+ {
+ // set closed flag and correct last point (which is added double now).
+ closeWithGeometryChange(aRetval);
+ }
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ B2DPolygon adaptiveSubdivideByCount(const B2DPolygon& rCandidate, sal_uInt32 nCount)
+ {
+ if(rCandidate.areControlPointsUsed())
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+ B2DPolygon aRetval;
+
+ if(nPointCount)
+ {
+ // prepare edge-oriented loop
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+ B2DCubicBezier aBezier;
+ aBezier.setStartPoint(rCandidate.getB2DPoint(0));
+
+ // perf: try to avoid too many realloctions by guessing the result's pointcount
+ aRetval.reserve(nPointCount*4);
+
+ // add start point (always)
+ aRetval.append(aBezier.getStartPoint());
+
+ // #i37443# prepare convenient count if none was given
+ if(0L == nCount)
+ {
+ nCount = COUNT_SUBDIVIDE_DEFAULT;
+ }
+
+ for(sal_uInt32 a(0L); a < nEdgeCount; a++)
+ {
+ // get next and control points
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+ aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
+ aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aBezier.testAndSolveTrivialBezier();
+
+ if(aBezier.isBezier())
+ {
+ // call adaptive subdivide
+ aBezier.adaptiveSubdivideByCount(aRetval, nCount);
+ }
+ else
+ {
+ // add non-curved edge
+ aRetval.append(aBezier.getEndPoint());
+ }
+
+ // prepare next step
+ aBezier.setStartPoint(aBezier.getEndPoint());
+ }
+
+ if(rCandidate.isClosed())
+ {
+ // set closed flag and correct last point (which is added double now).
+ closeWithGeometryChange(aRetval);
+ }
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ bool isInside(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder)
+ {
+ const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
+
+ if(bWithBorder && isPointOnPolygon(aCandidate, rPoint, true))
+ {
+ return true;
+ }
+ else
+ {
+ bool bRetval(false);
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount)
+ {
+ B2DPoint aCurrentPoint(aCandidate.getB2DPoint(nPointCount - 1L));
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B2DPoint aPreviousPoint(aCurrentPoint);
+ aCurrentPoint = aCandidate.getB2DPoint(a);
+
+ // cross-over in Y?
+ const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
+ const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
+
+ if(bCompYA != bCompYB)
+ {
+ // cross-over in X?
+ const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
+ const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
+
+ if(bCompXA == bCompXB)
+ {
+ if(bCompXA)
+ {
+ bRetval = !bRetval;
+ }
+ }
+ else
+ {
+ const double fCompare(
+ aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) *
+ (aPreviousPoint.getX() - aCurrentPoint.getX()) /
+ (aPreviousPoint.getY() - aCurrentPoint.getY()));
+
+ if(fTools::more(fCompare, rPoint.getX()))
+ {
+ bRetval = !bRetval;
+ }
+ }
+ }
+ }
+ }
+
+ return bRetval;
+ }
+ }
+
+ bool isInside(const B2DPolygon& rCandidate, const B2DPolygon& rPolygon, bool bWithBorder)
+ {
+ const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
+ const B2DPolygon aPolygon(rPolygon.areControlPointsUsed() ? rPolygon.getDefaultAdaptiveSubdivision() : rPolygon);
+ const sal_uInt32 nPointCount(aPolygon.count());
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B2DPoint aTestPoint(aPolygon.getB2DPoint(a));
+
+ if(!isInside(aCandidate, aTestPoint, bWithBorder))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ B2DRange getRangeWithControlPoints(const B2DPolygon& rCandidate)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+ B2DRange aRetval;
+
+ if(nPointCount)
+ {
+ const bool bControlPointsUsed(rCandidate.areControlPointsUsed());
+
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ aRetval.expand(rCandidate.getB2DPoint(a));
+
+ if(bControlPointsUsed)
+ {
+ aRetval.expand(rCandidate.getNextControlPoint(a));
+ aRetval.expand(rCandidate.getPrevControlPoint(a));
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DRange getRange(const B2DPolygon& rCandidate)
+ {
+ // changed to use internally buffered version at B2DPolygon
+ return rCandidate.getB2DRange();
+ }
+
+ double getSignedArea(const B2DPolygon& rCandidate)
+ {
+ const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
+ double fRetval(0.0);
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount > 2)
+ {
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B2DPoint aPreviousPoint(aCandidate.getB2DPoint((!a) ? nPointCount - 1L : a - 1L));
+ const B2DPoint aCurrentPoint(aCandidate.getB2DPoint(a));
+
+ fRetval += aPreviousPoint.getX() * aCurrentPoint.getY();
+ fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX();
+ }
+
+ fRetval /= 2.0;
+
+ // correct to zero if small enough. Also test the quadratic
+ // of the result since the precision is near quadratic due to
+ // the algorithm
+ if(fTools::equalZero(fRetval) || fTools::equalZero(fRetval * fRetval))
+ {
+ fRetval = 0.0;
+ }
+ }
+
+ return fRetval;
+ }
+
+ double getArea(const B2DPolygon& rCandidate)
+ {
+ double fRetval(0.0);
+
+ if(rCandidate.count() > 2 || rCandidate.areControlPointsUsed())
+ {
+ fRetval = getSignedArea(rCandidate);
+ const double fZero(0.0);
+
+ if(fTools::less(fRetval, fZero))
+ {
+ fRetval = -fRetval;
+ }
+ }
+
+ return fRetval;
+ }
+
+ double getEdgeLength(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+ OSL_ENSURE(nIndex < nPointCount, "getEdgeLength: Access to polygon out of range (!)");
+ double fRetval(0.0);
+
+ if(nPointCount)
+ {
+ const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ B2DCubicBezier aEdge;
+
+ aEdge.setStartPoint(rCandidate.getB2DPoint(nIndex));
+ aEdge.setControlPointA(rCandidate.getNextControlPoint(nIndex));
+ aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+
+ fRetval = aEdge.getLength();
+ }
+ else
+ {
+ const B2DPoint aCurrent(rCandidate.getB2DPoint(nIndex));
+ const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex));
+
+ fRetval = B2DVector(aNext - aCurrent).getLength();
+ }
+ }
+
+ return fRetval;
+ }
+
+ double getLength(const B2DPolygon& rCandidate)
+ {
+ double fRetval(0.0);
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ B2DCubicBezier aEdge;
+ aEdge.setStartPoint(rCandidate.getB2DPoint(0));
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
+ aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+
+ fRetval += aEdge.getLength();
+ aEdge.setStartPoint(aEdge.getEndPoint());
+ }
+ }
+ else
+ {
+ B2DPoint aCurrent(rCandidate.getB2DPoint(0));
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex));
+
+ fRetval += B2DVector(aNext - aCurrent).getLength();
+ aCurrent = aNext;
+ }
+ }
+ }
+
+ return fRetval;
+ }
+
+ B2DPoint getPositionAbsolute(const B2DPolygon& rCandidate, double fDistance, double fLength)
+ {
+ B2DPoint aRetval;
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if( 1L == nPointCount )
+ {
+ // only one point (i.e. no edge) - simply take that point
+ aRetval = rCandidate.getB2DPoint(0);
+ }
+ else if(nPointCount > 1L)
+ {
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+ sal_uInt32 nIndex(0L);
+ bool bIndexDone(false);
+
+ // get length if not given
+ if(fTools::equalZero(fLength))
+ {
+ fLength = getLength(rCandidate);
+ }
+
+ if(fTools::less(fDistance, 0.0))
+ {
+ // handle fDistance < 0.0
+ if(rCandidate.isClosed())
+ {
+ // if fDistance < 0.0 increment with multiple of fLength
+ sal_uInt32 nCount(sal_uInt32(-fDistance / fLength));
+ fDistance += double(nCount + 1L) * fLength;
+ }
+ else
+ {
+ // crop to polygon start
+ fDistance = 0.0;
+ bIndexDone = true;
+ }
+ }
+ else if(fTools::moreOrEqual(fDistance, fLength))
+ {
+ // handle fDistance >= fLength
+ if(rCandidate.isClosed())
+ {
+ // if fDistance >= fLength decrement with multiple of fLength
+ sal_uInt32 nCount(sal_uInt32(fDistance / fLength));
+ fDistance -= (double)(nCount) * fLength;
+ }
+ else
+ {
+ // crop to polygon end
+ fDistance = 0.0;
+ nIndex = nEdgeCount;
+ bIndexDone = true;
+ }
+ }
+
+ // look for correct index. fDistance is now [0.0 .. fLength[
+ double fEdgeLength(getEdgeLength(rCandidate, nIndex));
+
+ while(!bIndexDone)
+ {
+ // edge found must be on the half-open range
+ // [0,fEdgeLength).
+ // Note that in theory, we cannot move beyond
+ // the last polygon point, since fDistance>=fLength
+ // is checked above. Unfortunately, with floating-
+ // point calculations, this case might happen.
+ // Handled by nIndex check below
+ if(nIndex < nEdgeCount && fTools::moreOrEqual(fDistance, fEdgeLength))
+ {
+ // go to next edge
+ fDistance -= fEdgeLength;
+ fEdgeLength = getEdgeLength(rCandidate, ++nIndex);
+ }
+ else
+ {
+ // it's on this edge, stop
+ bIndexDone = true;
+ }
+ }
+
+ // get the point using nIndex
+ aRetval = rCandidate.getB2DPoint(nIndex);
+
+ // if fDistance != 0.0, move that length on the edge. The edge
+ // length is in fEdgeLength.
+ if(!fTools::equalZero(fDistance))
+ {
+ if(fTools::moreOrEqual(fDistance, fEdgeLength))
+ {
+ // end point of choosen edge
+ const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
+ aRetval = rCandidate.getB2DPoint(nNextIndex);
+ }
+ else if(fTools::equalZero(fDistance))
+ {
+ // start point of choosen edge
+ aRetval = aRetval;
+ }
+ else
+ {
+ // inside edge
+ const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
+ const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex));
+ bool bDone(false);
+
+ // add calculated average value to the return value
+ if(rCandidate.areControlPointsUsed())
+ {
+ // get as bezier segment
+ const B2DCubicBezier aBezierSegment(
+ aRetval, rCandidate.getNextControlPoint(nIndex),
+ rCandidate.getPrevControlPoint(nNextIndex), aNextPoint);
+
+ if(aBezierSegment.isBezier())
+ {
+ // use B2DCubicBezierHelper to bridge the non-linear gap between
+ // length and bezier distances
+ const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
+ const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fDistance));
+
+ aRetval = aBezierSegment.interpolatePoint(fBezierDistance);
+ bDone = true;
+ }
+ }
+
+ if(!bDone)
+ {
+ const double fRelativeInEdge(fDistance / fEdgeLength);
+ aRetval = interpolate(aRetval, aNextPoint, fRelativeInEdge);
+ }
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DPoint getPositionRelative(const B2DPolygon& rCandidate, double fDistance, double fLength)
+ {
+ // get length if not given
+ if(fTools::equalZero(fLength))
+ {
+ fLength = getLength(rCandidate);
+ }
+
+ // multiply fDistance with real length to get absolute position and
+ // use getPositionAbsolute
+ return getPositionAbsolute(rCandidate, fDistance * fLength, fLength);
+ }
+
+ B2DPolygon getSnippetAbsolute(const B2DPolygon& rCandidate, double fFrom, double fTo, double fLength)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount)
+ {
+ // get length if not given
+ if(fTools::equalZero(fLength))
+ {
+ fLength = getLength(rCandidate);
+ }
+
+ // test and correct fFrom
+ if(fTools::less(fFrom, 0.0))
+ {
+ fFrom = 0.0;
+ }
+
+ // test and correct fTo
+ if(fTools::more(fTo, fLength))
+ {
+ fTo = fLength;
+ }
+
+ // test and correct relationship of fFrom, fTo
+ if(fTools::more(fFrom, fTo))
+ {
+ fFrom = fTo = (fFrom + fTo) / 2.0;
+ }
+
+ if(fTools::equalZero(fFrom) && fTools::equal(fTo, fLength))
+ {
+ // no change, result is the whole polygon
+ return rCandidate;
+ }
+ else
+ {
+ B2DPolygon aRetval;
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+ double fPositionOfStart(0.0);
+ bool bStartDone(false);
+ bool bEndDone(false);
+
+ for(sal_uInt32 a(0L); !(bStartDone && bEndDone) && a < nEdgeCount; a++)
+ {
+ const double fEdgeLength(getEdgeLength(rCandidate, a));
+
+ if(!bStartDone)
+ {
+ if(fTools::equalZero(fFrom))
+ {
+ aRetval.append(rCandidate.getB2DPoint(a));
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a));
+ }
+
+ bStartDone = true;
+ }
+ else if(fTools::moreOrEqual(fFrom, fPositionOfStart) && fTools::less(fFrom, fPositionOfStart + fEdgeLength))
+ {
+ // calculate and add start point
+ if(fTools::equalZero(fEdgeLength))
+ {
+ aRetval.append(rCandidate.getB2DPoint(a));
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a));
+ }
+ }
+ else
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const B2DPoint aStart(rCandidate.getB2DPoint(a));
+ const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex));
+ bool bDone(false);
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ const B2DCubicBezier aBezierSegment(
+ aStart, rCandidate.getNextControlPoint(a),
+ rCandidate.getPrevControlPoint(nNextIndex), aEnd);
+
+ if(aBezierSegment.isBezier())
+ {
+ // use B2DCubicBezierHelper to bridge the non-linear gap between
+ // length and bezier distances
+ const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
+ const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fFrom - fPositionOfStart));
+ B2DCubicBezier aRight;
+
+ aBezierSegment.split(fBezierDistance, 0, &aRight);
+ aRetval.append(aRight.getStartPoint());
+ aRetval.setNextControlPoint(aRetval.count() - 1, aRight.getControlPointA());
+ bDone = true;
+ }
+ }
+
+ if(!bDone)
+ {
+ const double fRelValue((fFrom - fPositionOfStart) / fEdgeLength);
+ aRetval.append(interpolate(aStart, aEnd, fRelValue));
+ }
+ }
+
+ bStartDone = true;
+
+ // if same point, end is done, too.
+ if(fFrom == fTo)
+ {
+ bEndDone = true;
+ }
+ }
+ }
+
+ if(!bEndDone && fTools::moreOrEqual(fTo, fPositionOfStart) && fTools::less(fTo, fPositionOfStart + fEdgeLength))
+ {
+ // calculate and add end point
+ if(fTools::equalZero(fEdgeLength))
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aRetval.append(rCandidate.getB2DPoint(nNextIndex));
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex));
+ }
+ }
+ else
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const B2DPoint aStart(rCandidate.getB2DPoint(a));
+ const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex));
+ bool bDone(false);
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ const B2DCubicBezier aBezierSegment(
+ aStart, rCandidate.getNextControlPoint(a),
+ rCandidate.getPrevControlPoint(nNextIndex), aEnd);
+
+ if(aBezierSegment.isBezier())
+ {
+ // use B2DCubicBezierHelper to bridge the non-linear gap between
+ // length and bezier distances
+ const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
+ const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fTo - fPositionOfStart));
+ B2DCubicBezier aLeft;
+
+ aBezierSegment.split(fBezierDistance, &aLeft, 0);
+ aRetval.append(aLeft.getEndPoint());
+ aRetval.setPrevControlPoint(aRetval.count() - 1, aLeft.getControlPointB());
+ bDone = true;
+ }
+ }
+
+ if(!bDone)
+ {
+ const double fRelValue((fTo - fPositionOfStart) / fEdgeLength);
+ aRetval.append(interpolate(aStart, aEnd, fRelValue));
+ }
+ }
+
+ bEndDone = true;
+ }
+
+ if(!bEndDone)
+ {
+ if(bStartDone)
+ {
+ // add segments end point
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aRetval.append(rCandidate.getB2DPoint(nNextIndex));
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex));
+ aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(nNextIndex));
+ }
+ }
+
+ // increment fPositionOfStart
+ fPositionOfStart += fEdgeLength;
+ }
+ }
+ return aRetval;
+ }
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ B2DPolygon getSnippetRelative(const B2DPolygon& rCandidate, double fFrom, double fTo, double fLength)
+ {
+ // get length if not given
+ if(fTools::equalZero(fLength))
+ {
+ fLength = getLength(rCandidate);
+ }
+
+ // multiply distances with real length to get absolute position and
+ // use getSnippetAbsolute
+ return getSnippetAbsolute(rCandidate, fFrom * fLength, fTo * fLength, fLength);
+ }
+
+ CutFlagValue findCut(
+ const B2DPolygon& rCandidate,
+ sal_uInt32 nIndex1, sal_uInt32 nIndex2,
+ CutFlagValue aCutFlags,
+ double* pCut1, double* pCut2)
+ {
+ CutFlagValue aRetval(CUTFLAG_NONE);
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nIndex1 < nPointCount && nIndex2 < nPointCount && nIndex1 != nIndex2)
+ {
+ sal_uInt32 nEnd1(getIndexOfSuccessor(nIndex1, rCandidate));
+ sal_uInt32 nEnd2(getIndexOfSuccessor(nIndex2, rCandidate));
+
+ const B2DPoint aStart1(rCandidate.getB2DPoint(nIndex1));
+ const B2DPoint aEnd1(rCandidate.getB2DPoint(nEnd1));
+ const B2DVector aVector1(aEnd1 - aStart1);
+
+ const B2DPoint aStart2(rCandidate.getB2DPoint(nIndex2));
+ const B2DPoint aEnd2(rCandidate.getB2DPoint(nEnd2));
+ const B2DVector aVector2(aEnd2 - aStart2);
+
+ aRetval = findCut(
+ aStart1, aVector1, aStart2, aVector2,
+ aCutFlags, pCut1, pCut2);
+ }
+
+ return aRetval;
+ }
+
+ CutFlagValue findCut(
+ const B2DPolygon& rCandidate1, sal_uInt32 nIndex1,
+ const B2DPolygon& rCandidate2, sal_uInt32 nIndex2,
+ CutFlagValue aCutFlags,
+ double* pCut1, double* pCut2)
+ {
+ CutFlagValue aRetval(CUTFLAG_NONE);
+ const sal_uInt32 nPointCount1(rCandidate1.count());
+ const sal_uInt32 nPointCount2(rCandidate2.count());
+
+ if(nIndex1 < nPointCount1 && nIndex2 < nPointCount2)
+ {
+ sal_uInt32 nEnd1(getIndexOfSuccessor(nIndex1, rCandidate1));
+ sal_uInt32 nEnd2(getIndexOfSuccessor(nIndex2, rCandidate2));
+
+ const B2DPoint aStart1(rCandidate1.getB2DPoint(nIndex1));
+ const B2DPoint aEnd1(rCandidate1.getB2DPoint(nEnd1));
+ const B2DVector aVector1(aEnd1 - aStart1);
+
+ const B2DPoint aStart2(rCandidate2.getB2DPoint(nIndex2));
+ const B2DPoint aEnd2(rCandidate2.getB2DPoint(nEnd2));
+ const B2DVector aVector2(aEnd2 - aStart2);
+
+ aRetval = findCut(
+ aStart1, aVector1, aStart2, aVector2,
+ aCutFlags, pCut1, pCut2);
+ }
+
+ return aRetval;
+ }
+
+ CutFlagValue findCut(
+ const B2DPoint& rEdge1Start, const B2DVector& rEdge1Delta,
+ const B2DPoint& rEdge2Start, const B2DVector& rEdge2Delta,
+ CutFlagValue aCutFlags,
+ double* pCut1, double* pCut2)
+ {
+ CutFlagValue aRetval(CUTFLAG_NONE);
+ double fCut1(0.0);
+ double fCut2(0.0);
+ bool bFinished(!((bool)(aCutFlags & CUTFLAG_ALL)));
+
+ // test for same points?
+ if(!bFinished
+ && (aCutFlags & (CUTFLAG_START1|CUTFLAG_END1))
+ && (aCutFlags & (CUTFLAG_START2|CUTFLAG_END2)))
+ {
+ // same startpoint?
+ if(!bFinished && (aCutFlags & (CUTFLAG_START1|CUTFLAG_START2)) == (CUTFLAG_START1|CUTFLAG_START2))
+ {
+ if(rEdge1Start.equal(rEdge2Start))
+ {
+ bFinished = true;
+ aRetval = (CUTFLAG_START1|CUTFLAG_START2);
+ }
+ }
+
+ // same endpoint?
+ if(!bFinished && (aCutFlags & (CUTFLAG_END1|CUTFLAG_END2)) == (CUTFLAG_END1|CUTFLAG_END2))
+ {
+ const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
+ const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
+
+ if(aEnd1.equal(aEnd2))
+ {
+ bFinished = true;
+ aRetval = (CUTFLAG_END1|CUTFLAG_END2);
+ fCut1 = fCut2 = 1.0;
+ }
+ }
+
+ // startpoint1 == endpoint2?
+ if(!bFinished && (aCutFlags & (CUTFLAG_START1|CUTFLAG_END2)) == (CUTFLAG_START1|CUTFLAG_END2))
+ {
+ const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
+
+ if(rEdge1Start.equal(aEnd2))
+ {
+ bFinished = true;
+ aRetval = (CUTFLAG_START1|CUTFLAG_END2);
+ fCut1 = 0.0;
+ fCut2 = 1.0;
+ }
+ }
+
+ // startpoint2 == endpoint1?
+ if(!bFinished&& (aCutFlags & (CUTFLAG_START2|CUTFLAG_END1)) == (CUTFLAG_START2|CUTFLAG_END1))
+ {
+ const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
+
+ if(rEdge2Start.equal(aEnd1))
+ {
+ bFinished = true;
+ aRetval = (CUTFLAG_START2|CUTFLAG_END1);
+ fCut1 = 1.0;
+ fCut2 = 0.0;
+ }
+ }
+ }
+
+ if(!bFinished && (aCutFlags & CUTFLAG_LINE))
+ {
+ if(!bFinished && (aCutFlags & CUTFLAG_START1))
+ {
+ // start1 on line 2 ?
+ if(isPointOnEdge(rEdge1Start, rEdge2Start, rEdge2Delta, &fCut2))
+ {
+ bFinished = true;
+ aRetval = (CUTFLAG_LINE|CUTFLAG_START1);
+ }
+ }
+
+ if(!bFinished && (aCutFlags & CUTFLAG_START2))
+ {
+ // start2 on line 1 ?
+ if(isPointOnEdge(rEdge2Start, rEdge1Start, rEdge1Delta, &fCut1))
+ {
+ bFinished = true;
+ aRetval = (CUTFLAG_LINE|CUTFLAG_START2);
+ }
+ }
+
+ if(!bFinished && (aCutFlags & CUTFLAG_END1))
+ {
+ // end1 on line 2 ?
+ const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
+
+ if(isPointOnEdge(aEnd1, rEdge2Start, rEdge2Delta, &fCut2))
+ {
+ bFinished = true;
+ aRetval = (CUTFLAG_LINE|CUTFLAG_END1);
+ }
+ }
+
+ if(!bFinished && (aCutFlags & CUTFLAG_END2))
+ {
+ // end2 on line 1 ?
+ const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
+
+ if(isPointOnEdge(aEnd2, rEdge1Start, rEdge1Delta, &fCut1))
+ {
+ bFinished = true;
+ aRetval = (CUTFLAG_LINE|CUTFLAG_END2);
+ }
+ }
+
+ if(!bFinished)
+ {
+ // cut in line1, line2 ?
+ fCut1 = (rEdge1Delta.getX() * rEdge2Delta.getY()) - (rEdge1Delta.getY() * rEdge2Delta.getX());
+
+ if(!fTools::equalZero(fCut1))
+ {
+ fCut1 = (rEdge2Delta.getY() * (rEdge2Start.getX() - rEdge1Start.getX())
+ + rEdge2Delta.getX() * (rEdge1Start.getY() - rEdge2Start.getY())) / fCut1;
+
+ const double fZero(0.0);
+ const double fOne(1.0);
+
+ // inside parameter range edge1 AND fCut2 is calcable
+ if(fTools::more(fCut1, fZero) && fTools::less(fCut1, fOne)
+ && (!fTools::equalZero(rEdge2Delta.getX()) || !fTools::equalZero(rEdge2Delta.getY())))
+ {
+ // take the mopre precise calculation of the two possible
+ if(fabs(rEdge2Delta.getX()) > fabs(rEdge2Delta.getY()))
+ {
+ fCut2 = (rEdge1Start.getX() + fCut1
+ * rEdge1Delta.getX() - rEdge2Start.getX()) / rEdge2Delta.getX();
+ }
+ else
+ {
+ fCut2 = (rEdge1Start.getY() + fCut1
+ * rEdge1Delta.getY() - rEdge2Start.getY()) / rEdge2Delta.getY();
+ }
+
+ // inside parameter range edge2, too
+ if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne))
+ {
+ bFinished = true;
+ aRetval = CUTFLAG_LINE;
+ }
+ }
+ }
+ }
+ }
+
+ // copy values if wanted
+ if(pCut1)
+ {
+ *pCut1 = fCut1;
+ }
+
+ if(pCut2)
+ {
+ *pCut2 = fCut2;
+ }
+
+ return aRetval;
+ }
+
+ bool isPointOnEdge(
+ const B2DPoint& rPoint,
+ const B2DPoint& rEdgeStart,
+ const B2DVector& rEdgeDelta,
+ double* pCut)
+ {
+ bool bDeltaXIsZero(fTools::equalZero(rEdgeDelta.getX()));
+ bool bDeltaYIsZero(fTools::equalZero(rEdgeDelta.getY()));
+ const double fZero(0.0);
+ const double fOne(1.0);
+
+ if(bDeltaXIsZero && bDeltaYIsZero)
+ {
+ // no line, just a point
+ return false;
+ }
+ else if(bDeltaXIsZero)
+ {
+ // vertical line
+ if(fTools::equal(rPoint.getX(), rEdgeStart.getX()))
+ {
+ double fValue = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY();
+
+ if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
+ {
+ if(pCut)
+ {
+ *pCut = fValue;
+ }
+
+ return true;
+ }
+ }
+ }
+ else if(bDeltaYIsZero)
+ {
+ // horizontal line
+ if(fTools::equal(rPoint.getY(), rEdgeStart.getY()))
+ {
+ double fValue = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX();
+
+ if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
+ {
+ if(pCut)
+ {
+ *pCut = fValue;
+ }
+
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // any angle line
+ double fTOne = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX();
+ double fTTwo = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY();
+
+ if(fTools::equal(fTOne, fTTwo))
+ {
+ // same parameter representation, point is on line. Take
+ // middle value for better results
+ double fValue = (fTOne + fTTwo) / 2.0;
+
+ if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
+ {
+ // point is inside line bounds, too
+ if(pCut)
+ {
+ *pCut = fValue;
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ void applyLineDashing(const B2DPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fDotDashLength)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+ const sal_uInt32 nDotDashCount(rDotDashArray.size());
+
+ if(fTools::lessOrEqual(fDotDashLength, 0.0))
+ {
+ fDotDashLength = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
+ }
+
+ if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount)
+ {
+ // clear targets
+ if(pLineTarget)
+ {
+ pLineTarget->clear();
+ }
+
+ if(pGapTarget)
+ {
+ pGapTarget->clear();
+ }
+
+ // prepare current edge's start
+ B2DCubicBezier aCurrentEdge;
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+ aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0));
+
+ // prepare DotDashArray iteration and the line/gap switching bool
+ sal_uInt32 nDotDashIndex(0);
+ bool bIsLine(true);
+ double fDotDashMovingLength(rDotDashArray[0]);
+ B2DPolygon aSnippet;
+
+ // iterate over all edges
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ // update current edge (fill in C1, C2 and end point)
+ double fLastDotDashMovingLength(0.0);
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a));
+ aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+
+ // check if we have a trivial bezier segment -> possible fallback to edge
+ aCurrentEdge.testAndSolveTrivialBezier();
+
+ if(aCurrentEdge.isBezier())
+ {
+ // bezier segment
+ const B2DCubicBezierHelper aCubicBezierHelper(aCurrentEdge);
+ const double fEdgeLength(aCubicBezierHelper.getLength());
+
+ if(!fTools::equalZero(fEdgeLength))
+ {
+ while(fTools::less(fDotDashMovingLength, fEdgeLength))
+ {
+ // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
+ const bool bHandleLine(bIsLine && pLineTarget);
+ const bool bHandleGap(!bIsLine && pGapTarget);
+
+ if(bHandleLine || bHandleGap)
+ {
+ const double fBezierSplitStart(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
+ const double fBezierSplitEnd(aCubicBezierHelper.distanceToRelative(fDotDashMovingLength));
+ B2DCubicBezier aBezierSnippet(aCurrentEdge.snippet(fBezierSplitStart, fBezierSplitEnd));
+
+ if(!aSnippet.count())
+ {
+ aSnippet.append(aBezierSnippet.getStartPoint());
+ }
+
+ aSnippet.appendBezierSegment(aBezierSnippet.getControlPointA(), aBezierSnippet.getControlPointB(), aBezierSnippet.getEndPoint());
+
+ if(bHandleLine)
+ {
+ pLineTarget->append(aSnippet);
+ }
+ else
+ {
+ pGapTarget->append(aSnippet);
+ }
+
+ aSnippet.clear();
+ }
+
+ // prepare next DotDashArray step and flip line/gap flag
+ fLastDotDashMovingLength = fDotDashMovingLength;
+ fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
+ bIsLine = !bIsLine;
+ }
+
+ // append closing snippet [fLastDotDashMovingLength, fEdgeLength]
+ const bool bHandleLine(bIsLine && pLineTarget);
+ const bool bHandleGap(!bIsLine && pGapTarget);
+
+ if(bHandleLine || bHandleGap)
+ {
+ B2DCubicBezier aRight;
+ const double fBezierSplit(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
+
+ aCurrentEdge.split(fBezierSplit, 0, &aRight);
+
+ if(!aSnippet.count())
+ {
+ aSnippet.append(aRight.getStartPoint());
+ }
+
+ aSnippet.appendBezierSegment(aRight.getControlPointA(), aRight.getControlPointB(), aRight.getEndPoint());
+ }
+
+ // prepare move to next edge
+ fDotDashMovingLength -= fEdgeLength;
+ }
+ }
+ else
+ {
+ // simple edge
+ const double fEdgeLength(aCurrentEdge.getEdgeLength());
+
+ if(!fTools::equalZero(fEdgeLength))
+ {
+ while(fTools::less(fDotDashMovingLength, fEdgeLength))
+ {
+ // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
+ const bool bHandleLine(bIsLine && pLineTarget);
+ const bool bHandleGap(!bIsLine && pGapTarget);
+
+ if(bHandleLine || bHandleGap)
+ {
+ if(!aSnippet.count())
+ {
+ aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
+ }
+
+ aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fDotDashMovingLength / fEdgeLength));
+
+ if(bHandleLine)
+ {
+ pLineTarget->append(aSnippet);
+ }
+ else
+ {
+ pGapTarget->append(aSnippet);
+ }
+
+ aSnippet.clear();
+ }
+
+ // prepare next DotDashArray step and flip line/gap flag
+ fLastDotDashMovingLength = fDotDashMovingLength;
+ fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
+ bIsLine = !bIsLine;
+ }
+
+ // append snippet [fLastDotDashMovingLength, fEdgeLength]
+ const bool bHandleLine(bIsLine && pLineTarget);
+ const bool bHandleGap(!bIsLine && pGapTarget);
+
+ if(bHandleLine || bHandleGap)
+ {
+ if(!aSnippet.count())
+ {
+ aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
+ }
+
+ aSnippet.append(aCurrentEdge.getEndPoint());
+ }
+
+ // prepare move to next edge
+ fDotDashMovingLength -= fEdgeLength;
+ }
+ }
+
+ // prepare next edge step (end point gets new start point)
+ aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint());
+ }
+
+ // append last intermediate results (if exists)
+ if(aSnippet.count())
+ {
+ if(bIsLine && pLineTarget)
+ {
+ pLineTarget->append(aSnippet);
+ }
+ else if(!bIsLine && pGapTarget)
+ {
+ pGapTarget->append(aSnippet);
+ }
+ }
+
+ // check if start and end polygon may be merged
+ if(pLineTarget)
+ {
+ const sal_uInt32 nCount(pLineTarget->count());
+
+ if(nCount > 1)
+ {
+ // these polygons were created above, there exists none with less than two points,
+ // thus dircet point access below is allowed
+ const B2DPolygon aFirst(pLineTarget->getB2DPolygon(0));
+ B2DPolygon aLast(pLineTarget->getB2DPolygon(nCount - 1));
+
+ if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1)))
+ {
+ // start of first and end of last are the same -> merge them
+ aLast.append(aFirst);
+ aLast.removeDoublePoints();
+ pLineTarget->setB2DPolygon(0, aLast);
+ pLineTarget->remove(nCount - 1);
+ }
+ }
+ }
+
+ if(pGapTarget)
+ {
+ const sal_uInt32 nCount(pGapTarget->count());
+
+ if(nCount > 1)
+ {
+ // these polygons were created above, there exists none with less than two points,
+ // thus dircet point access below is allowed
+ const B2DPolygon aFirst(pGapTarget->getB2DPolygon(0));
+ B2DPolygon aLast(pGapTarget->getB2DPolygon(nCount - 1));
+
+ if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1)))
+ {
+ // start of first and end of last are the same -> merge them
+ aLast.append(aFirst);
+ aLast.removeDoublePoints();
+ pGapTarget->setB2DPolygon(0, aLast);
+ pGapTarget->remove(nCount - 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ // parameters make no sense, just add source to targets
+ if(pLineTarget)
+ {
+ pLineTarget->append(rCandidate);
+ }
+
+ if(pGapTarget)
+ {
+ pGapTarget->append(rCandidate);
+ }
+ }
+ }
+
+ // test if point is inside epsilon-range around an edge defined
+ // by the two given points. Can be used for HitTesting. The epsilon-range
+ // is defined to be the rectangle centered to the given edge, using height
+ // 2 x fDistance, and the circle around both points with radius fDistance.
+ bool isInEpsilonRange(const B2DPoint& rEdgeStart, const B2DPoint& rEdgeEnd, const B2DPoint& rTestPosition, double fDistance)
+ {
+ // build edge vector
+ const B2DVector aEdge(rEdgeEnd - rEdgeStart);
+ bool bDoDistanceTestStart(false);
+ bool bDoDistanceTestEnd(false);
+
+ if(aEdge.equalZero())
+ {
+ // no edge, just a point. Do one of the distance tests.
+ bDoDistanceTestStart = true;
+ }
+ else
+ {
+ // edge has a length. Create perpendicular vector.
+ const B2DVector aPerpend(getPerpendicular(aEdge));
+ double fCut(
+ (aPerpend.getY() * (rTestPosition.getX() - rEdgeStart.getX())
+ + aPerpend.getX() * (rEdgeStart.getY() - rTestPosition.getY())) /
+ (aEdge.getX() * aEdge.getX() + aEdge.getY() * aEdge.getY()));
+ const double fZero(0.0);
+ const double fOne(1.0);
+
+ if(fTools::less(fCut, fZero))
+ {
+ // left of rEdgeStart
+ bDoDistanceTestStart = true;
+ }
+ else if(fTools::more(fCut, fOne))
+ {
+ // right of rEdgeEnd
+ bDoDistanceTestEnd = true;
+ }
+ else
+ {
+ // inside line [0.0 .. 1.0]
+ const B2DPoint aCutPoint(interpolate(rEdgeStart, rEdgeEnd, fCut));
+ const B2DVector aDelta(rTestPosition - aCutPoint);
+ const double fDistanceSquare(aDelta.scalar(aDelta));
+
+ if(fDistanceSquare <= fDistance * fDistance)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ if(bDoDistanceTestStart)
+ {
+ const B2DVector aDelta(rTestPosition - rEdgeStart);
+ const double fDistanceSquare(aDelta.scalar(aDelta));
+
+ if(fDistanceSquare <= fDistance * fDistance)
+ {
+ return true;
+ }
+ }
+ else if(bDoDistanceTestEnd)
+ {
+ const B2DVector aDelta(rTestPosition - rEdgeEnd);
+ const double fDistanceSquare(aDelta.scalar(aDelta));
+
+ if(fDistanceSquare <= fDistance * fDistance)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // test if point is inside epsilon-range around the given Polygon. Can be used
+ // for HitTesting. The epsilon-range is defined to be the tube around the polygon
+ // with distance fDistance and rounded edges (start and end point).
+ bool isInEpsilonRange(const B2DPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance)
+ {
+ // force to non-bezier polygon
+ const B2DPolygon aCandidate(rCandidate.getDefaultAdaptiveSubdivision());
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+ B2DPoint aCurrent(aCandidate.getB2DPoint(0));
+
+ if(nEdgeCount)
+ {
+ // edges
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex));
+
+ if(isInEpsilonRange(aCurrent, aNext, rTestPosition, fDistance))
+ {
+ return true;
+ }
+
+ // prepare next step
+ aCurrent = aNext;
+ }
+ }
+ else
+ {
+ // no edges, but points -> not closed. Check single point. Just
+ // use isInEpsilonRange with twice the same point, it handles those well
+ if(isInEpsilonRange(aCurrent, aCurrent, rTestPosition, fDistance))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadius )
+ {
+ const double fZero(0.0);
+ const double fOne(1.0);
+
+ if(fTools::lessOrEqual(fRadius, fZero))
+ {
+ // no radius, use rectangle
+ return createPolygonFromRect( rRect );
+ }
+ else if(fTools::moreOrEqual(fRadius, fOne))
+ {
+ // full radius, use ellipse
+ const B2DPoint aCenter(rRect.getCenter());
+ const double fRadiusX(rRect.getWidth() / 2.0);
+ const double fRadiusY(rRect.getHeight() / 2.0);
+
+ return createPolygonFromEllipse( aCenter, fRadiusX, fRadiusY );
+ }
+ else
+ {
+ // create rectangle with two radii between ]0.0 .. 1.0[
+ return createPolygonFromRect( rRect, fRadius, fRadius );
+ }
+ }
+
+ B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadiusX, double fRadiusY )
+ {
+ const double fZero(0.0);
+ const double fOne(1.0);
+
+ // crop to useful values
+ if(fTools::less(fRadiusX, fZero))
+ {
+ fRadiusX = fZero;
+ }
+ else if(fTools::more(fRadiusX, fOne))
+ {
+ fRadiusX = fOne;
+ }
+
+ if(fTools::less(fRadiusY, fZero))
+ {
+ fRadiusY = fZero;
+ }
+ else if(fTools::more(fRadiusY, fOne))
+ {
+ fRadiusY = fOne;
+ }
+
+ if(fZero == fRadiusX || fZero == fRadiusY)
+ {
+ B2DPolygon aRetval;
+
+ // at least in one direction no radius, use rectangle.
+ // Do not use createPolygonFromRect() here since original
+ // creator (historical reasons) still creates a start point at the
+ // bottom center, so do the same here to get the same line patterns.
+ // Due to this the order of points is different, too.
+ const B2DPoint aBottomCenter(rRect.getCenter().getX(), rRect.getMaxY());
+ aRetval.append(aBottomCenter);
+
+ aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) );
+ aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) );
+ aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) );
+ aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) );
+
+ // close
+ aRetval.setClosed( true );
+
+ return aRetval;
+ }
+ else if(fOne == fRadiusX && fOne == fRadiusY)
+ {
+ // in both directions full radius, use ellipse
+ const B2DPoint aCenter(rRect.getCenter());
+ const double fRectRadiusX(rRect.getWidth() / 2.0);
+ const double fRectRadiusY(rRect.getHeight() / 2.0);
+
+ return createPolygonFromEllipse( aCenter, fRectRadiusX, fRectRadiusY );
+ }
+ else
+ {
+ B2DPolygon aRetval;
+ const double fBowX((rRect.getWidth() / 2.0) * fRadiusX);
+ const double fBowY((rRect.getHeight() / 2.0) * fRadiusY);
+ const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
+
+ // create start point at bottom center
+ if(fOne != fRadiusX)
+ {
+ const B2DPoint aBottomCenter(rRect.getCenter().getX(), rRect.getMaxY());
+ aRetval.append(aBottomCenter);
+ }
+
+ // create first bow
+ {
+ const B2DPoint aBottomRight(rRect.getMaxX(), rRect.getMaxY());
+ const B2DPoint aStart(aBottomRight + B2DPoint(-fBowX, 0.0));
+ const B2DPoint aStop(aBottomRight + B2DPoint(0.0, -fBowY));
+ aRetval.append(aStart);
+ aRetval.appendBezierSegment(interpolate(aStart, aBottomRight, fKappa), interpolate(aStop, aBottomRight, fKappa), aStop);
+ }
+
+ // create second bow
+ {
+ const B2DPoint aTopRight(rRect.getMaxX(), rRect.getMinY());
+ const B2DPoint aStart(aTopRight + B2DPoint(0.0, fBowY));
+ const B2DPoint aStop(aTopRight + B2DPoint(-fBowX, 0.0));
+ aRetval.append(aStart);
+ aRetval.appendBezierSegment(interpolate(aStart, aTopRight, fKappa), interpolate(aStop, aTopRight, fKappa), aStop);
+ }
+
+ // create third bow
+ {
+ const B2DPoint aTopLeft(rRect.getMinX(), rRect.getMinY());
+ const B2DPoint aStart(aTopLeft + B2DPoint(fBowX, 0.0));
+ const B2DPoint aStop(aTopLeft + B2DPoint(0.0, fBowY));
+ aRetval.append(aStart);
+ aRetval.appendBezierSegment(interpolate(aStart, aTopLeft, fKappa), interpolate(aStop, aTopLeft, fKappa), aStop);
+ }
+
+ // create forth bow
+ {
+ const B2DPoint aBottomLeft(rRect.getMinX(), rRect.getMaxY());
+ const B2DPoint aStart(aBottomLeft + B2DPoint(0.0, -fBowY));
+ const B2DPoint aStop(aBottomLeft + B2DPoint(fBowX, 0.0));
+ aRetval.append(aStart);
+ aRetval.appendBezierSegment(interpolate(aStart, aBottomLeft, fKappa), interpolate(aStop, aBottomLeft, fKappa), aStop);
+ }
+
+ // close
+ aRetval.setClosed( true );
+
+ // remove double created points if there are extreme radii envolved
+ if(fOne == fRadiusX || fOne == fRadiusY)
+ {
+ aRetval.removeDoublePoints();
+ }
+
+ return aRetval;
+ }
+ }
+
+ B2DPolygon createPolygonFromRect( const B2DRectangle& rRect )
+ {
+ B2DPolygon aRetval;
+
+ aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) );
+ aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) );
+ aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) );
+ aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) );
+
+ // close
+ aRetval.setClosed( true );
+
+ return aRetval;
+ }
+
+ B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius )
+ {
+ return createPolygonFromEllipse( rCenter, fRadius, fRadius );
+ }
+
+ void appendUnitCircleQuadrant(B2DPolygon& rPolygon, sal_uInt32 nQuadrant)
+ {
+ const double fZero(0.0);
+ const double fOne(1.0);
+ const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
+
+ // create closed unit-circle with 4 segments
+ switch(nQuadrant)
+ {
+ case 0 : // first quadrant
+ {
+ rPolygon.append(B2DPoint(fOne, fZero));
+ rPolygon.appendBezierSegment(B2DPoint(fOne, fKappa), B2DPoint(fKappa, fOne), B2DPoint(fZero, fOne));
+ break;
+ }
+ case 1 : // second quadrant
+ {
+ rPolygon.append(B2DPoint(fZero, fOne));
+ rPolygon.appendBezierSegment(B2DPoint(-fKappa, fOne), B2DPoint(-fOne, fKappa), B2DPoint(-fOne, fZero));
+ break;
+ }
+ case 2 : // third quadrant
+ {
+ rPolygon.append(B2DPoint(-fOne, fZero));
+ rPolygon.appendBezierSegment(B2DPoint(-fOne, -fKappa), B2DPoint(-fKappa, -fOne), B2DPoint(fZero, -fOne));
+ break;
+ }
+ default : // last quadrant
+ {
+ rPolygon.append(B2DPoint(fZero, -fOne));
+ rPolygon.appendBezierSegment(B2DPoint(fKappa, -fOne), B2DPoint(fOne, -fKappa), B2DPoint(fOne, fZero));
+ break;
+ }
+ }
+ }
+
+ B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant)
+ {
+ B2DPolygon aRetval;
+
+ // create unit-circle with all 4 segments, close it
+ appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++;
+ appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++;
+ appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++;
+ appendUnitCircleQuadrant(aRetval, nStartQuadrant % 4); nStartQuadrant++;
+ aRetval.setClosed(true);
+
+ // remove double points between segments created by segmented creation
+ aRetval.removeDoublePoints();
+
+ return aRetval;
+ }
+
+ B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY )
+ {
+ const double fOne(1.0);
+ B2DPolygon aRetval(createPolygonFromUnitCircle());
+
+ // transformation necessary?
+ const sal_Bool bScale(!fTools::equal(fRadiusX, fOne) || !fTools::equal(fRadiusY, fOne));
+ const sal_Bool bTranslate(!rCenter.equalZero());
+
+ if(bScale || bTranslate)
+ {
+ B2DHomMatrix aMatrix;
+
+ if(bScale)
+ {
+ aMatrix.scale(fRadiusX, fRadiusY);
+ }
+
+ if(bTranslate)
+ {
+ aMatrix.translate(rCenter.getX(), rCenter.getY());
+ }
+
+ aRetval.transform(aMatrix);
+ }
+
+ return aRetval;
+ }
+
+ void appendUnitCircleQuadrantSegment(B2DPolygon& rPolygon, sal_uInt32 nQuadrant, double fStart, double fEnd)
+ {
+ OSL_ENSURE(fStart >= 0.0 && fStart <= 1.0, "appendUnitCircleQuadrantSegment: Access out of range (!)");
+ OSL_ENSURE(fEnd >= 0.0 && fEnd <= 1.0, "appendUnitCircleQuadrantSegment: Access out of range (!)");
+ OSL_ENSURE(fEnd >= fStart, "appendUnitCircleQuadrantSegment: Access out of range (!)");
+ const double fOne(1.0);
+ const bool bStartIsZero(fTools::equalZero(fStart));
+ const bool bEndIsOne(fTools::equal(fEnd, fOne));
+
+ if(bStartIsZero && bEndIsOne)
+ {
+ // add completely
+ appendUnitCircleQuadrant(rPolygon, nQuadrant);
+ }
+ else
+ {
+ // split and add
+ B2DPolygon aQuadrant;
+ appendUnitCircleQuadrant(aQuadrant, nQuadrant);
+ const bool bStartEndEqual(fTools::equal(fStart, fEnd));
+
+ if(bStartEndEqual)
+ {
+ if(bStartIsZero)
+ {
+ // both zero, add start point
+ rPolygon.append(aQuadrant.getB2DPoint(0L));
+ }
+ else if(bEndIsOne)
+ {
+ // both one, add end point
+ rPolygon.append(aQuadrant.getB2DPoint(1L));
+ }
+ else
+ {
+ // both equal but not zero, add split point
+ B2DCubicBezier aCubicBezier(
+ aQuadrant.getB2DPoint(0L), aQuadrant.getNextControlPoint(0L),
+ aQuadrant.getPrevControlPoint(1L), aQuadrant.getB2DPoint(1L));
+
+ aCubicBezier.split(fStart, &aCubicBezier, 0);
+ rPolygon.append(aCubicBezier.getEndPoint());
+ }
+ }
+ else
+ {
+ B2DCubicBezier aCubicBezier(
+ aQuadrant.getB2DPoint(0L), aQuadrant.getNextControlPoint(0L),
+ aQuadrant.getPrevControlPoint(1L), aQuadrant.getB2DPoint(1L));
+
+ aCubicBezier = aCubicBezier.snippet(fStart, fEnd);
+ rPolygon.append(aCubicBezier.getStartPoint());
+ rPolygon.appendBezierSegment(aCubicBezier.getControlPointA(), aCubicBezier.getControlPointB(), aCubicBezier.getEndPoint());
+ }
+ }
+ }
+
+ B2DPolygon createPolygonFromUnitEllipseSegment( double fStart, double fEnd )
+ {
+ B2DPolygon aRetval;
+
+ // truncate fStart, fEnd to a range of [0.0 .. F_2PI[ where F_2PI
+ // falls back to 0.0 to ensure a unique definition
+ if(fTools::less(fStart, 0.0))
+ {
+ fStart = 0.0;
+ }
+
+ if(fTools::moreOrEqual(fStart, F_2PI))
+ {
+ fStart = 0.0;
+ }
+
+ if(fTools::less(fEnd, 0.0))
+ {
+ fEnd = 0.0;
+ }
+
+ if(fTools::moreOrEqual(fEnd, F_2PI))
+ {
+ fEnd = 0.0;
+ }
+
+ const sal_uInt32 nQuadrantStart(sal_uInt32(fStart / F_PI2) % 4L);
+ const sal_uInt32 nQuadrantEnd(sal_uInt32(fEnd / F_PI2) % 4L);
+ sal_uInt32 nCurrentQuadrant(nQuadrantStart);
+ bool bStartDone(false);
+ bool bEndDone(false);
+
+ do
+ {
+ if(!bStartDone && nQuadrantStart == nCurrentQuadrant)
+ {
+ if(nQuadrantStart == nQuadrantEnd && fTools::moreOrEqual(fEnd, fStart))
+ {
+ // both in one quadrant and defining the complete segment, create start to end
+ double fSplitOffsetStart((fStart - (nCurrentQuadrant * F_PI2)) / F_PI2);
+ double fSplitOffsetEnd((fEnd - (nCurrentQuadrant * F_PI2)) / F_PI2);
+ appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, fSplitOffsetStart, fSplitOffsetEnd);
+ bStartDone = bEndDone = true;
+ }
+ else
+ {
+ // create start to quadrant end
+ const double fSplitOffsetStart((fStart - (nCurrentQuadrant * F_PI2)) / F_PI2);
+ appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, fSplitOffsetStart, 1.0);
+ bStartDone = true;
+ }
+ }
+ else if(!bEndDone && nQuadrantEnd == nCurrentQuadrant)
+ {
+ // create quadrant start to end
+ const double fSplitOffsetEnd((fEnd - (nCurrentQuadrant * F_PI2)) / F_PI2);
+ appendUnitCircleQuadrantSegment(aRetval, nCurrentQuadrant, 0.0, fSplitOffsetEnd);
+ bEndDone = true;
+ }
+ else
+ {
+ // add quadrant completely
+ appendUnitCircleQuadrant(aRetval, nCurrentQuadrant);
+ }
+
+ // next step
+ nCurrentQuadrant = (nCurrentQuadrant + 1L) % 4L;
+ }
+ while(!(bStartDone && bEndDone));
+
+ // remove double points between segments created by segmented creation
+ aRetval.removeDoublePoints();
+
+ return aRetval;
+ }
+
+ B2DPolygon createPolygonFromEllipseSegment( const B2DPoint& rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd )
+ {
+ B2DPolygon aRetval(createPolygonFromUnitEllipseSegment(fStart, fEnd));
+
+ // transformation necessary?
+ const double fOne(1.0);
+ const sal_Bool bScale(!fTools::equal(fRadiusX, fOne) || !fTools::equal(fRadiusY, fOne));
+ const sal_Bool bTranslate(!rCenter.equalZero());
+
+ if(bScale || bTranslate)
+ {
+ B2DHomMatrix aMatrix;
+
+ if(bScale)
+ {
+ aMatrix.scale(fRadiusX, fRadiusY);
+ }
+
+ if(bTranslate)
+ {
+ aMatrix.translate(rCenter.getX(), rCenter.getY());
+ }
+
+ aRetval.transform(aMatrix);
+ }
+
+ return aRetval;
+ }
+
+ bool hasNeutralPoints(const B2DPolygon& rCandidate)
+ {
+ OSL_ENSURE(!rCandidate.areControlPointsUsed(), "hasNeutralPoints: ATM works not for curves (!)");
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 2L)
+ {
+ B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
+ B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
+ const B2DVector aPrevVec(aPrevPoint - aCurrPoint);
+ const B2DVector aNextVec(aNextPoint - aCurrPoint);
+ const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec));
+
+ if(ORIENTATION_NEUTRAL == aOrientation)
+ {
+ // current has neutral orientation
+ return true;
+ }
+ else
+ {
+ // prepare next
+ aPrevPoint = aCurrPoint;
+ aCurrPoint = aNextPoint;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ B2DPolygon removeNeutralPoints(const B2DPolygon& rCandidate)
+ {
+ if(hasNeutralPoints(rCandidate))
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+ B2DPolygon aRetval;
+ B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
+ B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
+ const B2DVector aPrevVec(aPrevPoint - aCurrPoint);
+ const B2DVector aNextVec(aNextPoint - aCurrPoint);
+ const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec));
+
+ if(ORIENTATION_NEUTRAL == aOrientation)
+ {
+ // current has neutral orientation, leave it out and prepare next
+ aCurrPoint = aNextPoint;
+ }
+ else
+ {
+ // add current point
+ aRetval.append(aCurrPoint);
+
+ // prepare next
+ aPrevPoint = aCurrPoint;
+ aCurrPoint = aNextPoint;
+ }
+ }
+
+ while(aRetval.count() && ORIENTATION_NEUTRAL == getOrientationForIndex(aRetval, 0L))
+ {
+ aRetval.remove(0L);
+ }
+
+ // copy closed state
+ aRetval.setClosed(rCandidate.isClosed());
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ bool isConvex(const B2DPolygon& rCandidate)
+ {
+ OSL_ENSURE(!rCandidate.areControlPointsUsed(), "isConvex: ATM works not for curves (!)");
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 2L)
+ {
+ const B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
+ B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
+ B2DVector aCurrVec(aPrevPoint - aCurrPoint);
+ B2VectorOrientation aOrientation(ORIENTATION_NEUTRAL);
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
+ const B2DVector aNextVec(aNextPoint - aCurrPoint);
+ const B2VectorOrientation aCurrentOrientation(getOrientation(aNextVec, aCurrVec));
+
+ if(ORIENTATION_NEUTRAL == aOrientation)
+ {
+ // set start value, maybe neutral again
+ aOrientation = aCurrentOrientation;
+ }
+ else
+ {
+ if(ORIENTATION_NEUTRAL != aCurrentOrientation && aCurrentOrientation != aOrientation)
+ {
+ // different orientations found, that's it
+ return false;
+ }
+ }
+
+ // prepare next
+ aCurrPoint = aNextPoint;
+ aCurrVec = -aNextVec;
+ }
+ }
+
+ return true;
+ }
+
+ B2VectorOrientation getOrientationForIndex(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
+ {
+ OSL_ENSURE(nIndex < rCandidate.count(), "getOrientationForIndex: index out of range (!)");
+ const B2DPoint aPrev(rCandidate.getB2DPoint(getIndexOfPredecessor(nIndex, rCandidate)));
+ const B2DPoint aCurr(rCandidate.getB2DPoint(nIndex));
+ const B2DPoint aNext(rCandidate.getB2DPoint(getIndexOfSuccessor(nIndex, rCandidate)));
+ const B2DVector aBack(aPrev - aCurr);
+ const B2DVector aForw(aNext - aCurr);
+
+ return getOrientation(aForw, aBack);
+ }
+
+ bool isPointOnLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidate, bool bWithPoints)
+ {
+ if(rCandidate.equal(rStart) || rCandidate.equal(rEnd))
+ {
+ // candidate is in epsilon around start or end -> inside
+ return bWithPoints;
+ }
+ else if(rStart.equal(rEnd))
+ {
+ // start and end are equal, but candidate is outside their epsilon -> outside
+ return false;
+ }
+ else
+ {
+ const B2DVector aEdgeVector(rEnd - rStart);
+ const B2DVector aTestVector(rCandidate - rStart);
+
+ if(areParallel(aEdgeVector, aTestVector))
+ {
+ const double fZero(0.0);
+ const double fOne(1.0);
+ const double fParamTestOnCurr(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY())
+ ? aTestVector.getX() / aEdgeVector.getX()
+ : aTestVector.getY() / aEdgeVector.getY());
+
+ if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ bool isPointOnPolygon(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithPoints)
+ {
+ const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount > 1L)
+ {
+ const sal_uInt32 nLoopCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+ B2DPoint aCurrentPoint(aCandidate.getB2DPoint(0L));
+
+ for(sal_uInt32 a(0L); a < nLoopCount; a++)
+ {
+ const B2DPoint aNextPoint(aCandidate.getB2DPoint((a + 1L) % nPointCount));
+
+ if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints))
+ {
+ return true;
+ }
+
+ aCurrentPoint = aNextPoint;
+ }
+ }
+ else if(nPointCount && bWithPoints)
+ {
+ return rPoint.equal(aCandidate.getB2DPoint(0L));
+ }
+
+ return false;
+ }
+
+ bool isPointInTriangle(const B2DPoint& rA, const B2DPoint& rB, const B2DPoint& rC, const B2DPoint& rCandidate, bool bWithBorder)
+ {
+ if(arePointsOnSameSideOfLine(rA, rB, rC, rCandidate, bWithBorder))
+ {
+ if(arePointsOnSameSideOfLine(rB, rC, rA, rCandidate, bWithBorder))
+ {
+ if(arePointsOnSameSideOfLine(rC, rA, rB, rCandidate, bWithBorder))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ bool arePointsOnSameSideOfLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidateA, const B2DPoint& rCandidateB, bool bWithLine)
+ {
+ const B2DVector aLineVector(rEnd - rStart);
+ const B2DVector aVectorToA(rEnd - rCandidateA);
+ const double fCrossA(aLineVector.cross(aVectorToA));
+
+ if(fTools::equalZero(fCrossA))
+ {
+ // one point on the line
+ return bWithLine;
+ }
+
+ const B2DVector aVectorToB(rEnd - rCandidateB);
+ const double fCrossB(aLineVector.cross(aVectorToB));
+
+ if(fTools::equalZero(fCrossB))
+ {
+ // one point on the line
+ return bWithLine;
+ }
+
+ // return true if they both have the same sign
+ return ((fCrossA > 0.0) == (fCrossB > 0.0));
+ }
+
+ void addTriangleFan(const B2DPolygon& rCandidate, B2DPolygon& rTarget)
+ {
+ const sal_uInt32 nCount(rCandidate.count());
+
+ if(nCount > 2L)
+ {
+ const B2DPoint aStart(rCandidate.getB2DPoint(0L));
+ B2DPoint aLast(rCandidate.getB2DPoint(1L));
+
+ for(sal_uInt32 a(2L); a < nCount; a++)
+ {
+ const B2DPoint aCurrent(rCandidate.getB2DPoint(a));
+ rTarget.append(aStart);
+ rTarget.append(aLast);
+ rTarget.append(aCurrent);
+
+ // prepare next
+ aLast = aCurrent;
+ }
+ }
+ }
+
+ namespace
+ {
+ /// return 0 for input of 0, -1 for negative and 1 for positive input
+ inline int lcl_sgn( const double n )
+ {
+ return n == 0.0 ? 0 : 1 - 2*::rtl::math::isSignBitSet(n);
+ }
+ }
+
+ bool isRectangle( const B2DPolygon& rPoly )
+ {
+ // polygon must be closed to resemble a rect, and contain
+ // at least four points.
+ if( !rPoly.isClosed() ||
+ rPoly.count() < 4 )
+ {
+ return false;
+ }
+
+ // number of 90 degree turns the polygon has taken
+ int nNumTurns(0);
+
+ int nVerticalEdgeType=0;
+ int nHorizontalEdgeType=0;
+ bool bNullVertex(true);
+ bool bCWPolygon(false); // when true, polygon is CW
+ // oriented, when false, CCW
+ bool bOrientationSet(false); // when false, polygon
+ // orientation has not yet
+ // been determined.
+
+ // scan all _edges_ (which involves coming back to point 0
+ // for the last edge - thus the modulo operation below)
+ const sal_Int32 nCount( rPoly.count() );
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ const B2DPoint& rPoint0( rPoly.getB2DPoint(i % nCount) );
+ const B2DPoint& rPoint1( rPoly.getB2DPoint((i+1) % nCount) );
+
+ // is 0 for zero direction vector, 1 for south edge and -1
+ // for north edge (standard screen coordinate system)
+ int nCurrVerticalEdgeType( lcl_sgn( rPoint1.getY() - rPoint0.getY() ) );
+
+ // is 0 for zero direction vector, 1 for east edge and -1
+ // for west edge (standard screen coordinate system)
+ int nCurrHorizontalEdgeType( lcl_sgn(rPoint1.getX() - rPoint0.getX()) );
+
+ if( nCurrVerticalEdgeType && nCurrHorizontalEdgeType )
+ return false; // oblique edge - for sure no rect
+
+ const bool bCurrNullVertex( !nCurrVerticalEdgeType && !nCurrHorizontalEdgeType );
+
+ // current vertex is equal to previous - just skip,
+ // until we have a real edge
+ if( bCurrNullVertex )
+ continue;
+
+ // if previous edge has two identical points, because
+ // no previous edge direction was available, simply
+ // take this first non-null edge as the start
+ // direction. That's what will happen here, if
+ // bNullVertex is false
+ if( !bNullVertex )
+ {
+ // 2D cross product - is 1 for CW and -1 for CCW turns
+ const int nCrossProduct( nHorizontalEdgeType*nCurrVerticalEdgeType -
+ nVerticalEdgeType*nCurrHorizontalEdgeType );
+
+ if( !nCrossProduct )
+ continue; // no change in orientation -
+ // collinear edges - just go on
+
+ // if polygon orientation is not set, we'll
+ // determine it now
+ if( !bOrientationSet )
+ {
+ bCWPolygon = nCrossProduct == 1;
+ bOrientationSet = true;
+ }
+ else
+ {
+ // if current turn orientation is not equal
+ // initial orientation, this is not a
+ // rectangle (as rectangles have consistent
+ // orientation).
+ if( (nCrossProduct == 1) != bCWPolygon )
+ return false;
+ }
+
+ ++nNumTurns;
+
+ // More than four 90 degree turns are an
+ // indication that this must not be a rectangle.
+ if( nNumTurns > 4 )
+ return false;
+ }
+
+ // store current state for the next turn
+ nVerticalEdgeType = nCurrVerticalEdgeType;
+ nHorizontalEdgeType = nCurrHorizontalEdgeType;
+ bNullVertex = false; // won't reach this line,
+ // if bCurrNullVertex is
+ // true - see above
+ }
+
+ return true;
+ }
+
+ B3DPolygon createB3DPolygonFromB2DPolygon(const B2DPolygon& rCandidate, double fZCoordinate)
+ {
+ if(rCandidate.areControlPointsUsed())
+ {
+ // call myself recursively with subdivided input
+ const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate));
+ return createB3DPolygonFromB2DPolygon(aCandidate, fZCoordinate);
+ }
+ else
+ {
+ B3DPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ B2DPoint aPoint(rCandidate.getB2DPoint(a));
+ aRetval.append(B3DPoint(aPoint.getX(), aPoint.getY(), fZCoordinate));
+ }
+
+ // copy closed state
+ aRetval.setClosed(rCandidate.isClosed());
+
+ return aRetval;
+ }
+ }
+
+ B2DPolygon createB2DPolygonFromB3DPolygon(const B3DPolygon& rCandidate, const B3DHomMatrix& rMat)
+ {
+ B2DPolygon aRetval;
+ const sal_uInt32 nCount(rCandidate.count());
+ const bool bIsIdentity(rMat.isIdentity());
+
+ for(sal_uInt32 a(0L); a < nCount; a++)
+ {
+ B3DPoint aCandidate(rCandidate.getB3DPoint(a));
+
+ if(!bIsIdentity)
+ {
+ aCandidate *= rMat;
+ }
+
+ aRetval.append(B2DPoint(aCandidate.getX(), aCandidate.getY()));
+ }
+
+ // copy closed state
+ aRetval.setClosed(rCandidate.isClosed());
+
+ return aRetval;
+ }
+
+ double getDistancePointToEndlessRay(const B2DPoint& rPointA, const B2DPoint& rPointB, const B2DPoint& rTestPoint, double& rCut)
+ {
+ if(rPointA.equal(rPointB))
+ {
+ rCut = 0.0;
+ const B2DVector aVector(rTestPoint - rPointA);
+ return aVector.getLength();
+ }
+ else
+ {
+ // get the relative cut value on line vector (Vector1) for cut with perpendicular through TestPoint
+ const B2DVector aVector1(rPointB - rPointA);
+ const B2DVector aVector2(rTestPoint - rPointA);
+ const double fDividend((aVector2.getX() * aVector1.getX()) + (aVector2.getY() * aVector1.getY()));
+ const double fDivisor((aVector1.getX() * aVector1.getX()) + (aVector1.getY() * aVector1.getY()));
+
+ rCut = fDividend / fDivisor;
+
+ const B2DPoint aCutPoint(rPointA + rCut * aVector1);
+ const B2DVector aVector(rTestPoint - aCutPoint);
+ return aVector.getLength();
+ }
+ }
+
+ double getSmallestDistancePointToEdge(const B2DPoint& rPointA, const B2DPoint& rPointB, const B2DPoint& rTestPoint, double& rCut)
+ {
+ if(rPointA.equal(rPointB))
+ {
+ rCut = 0.0;
+ const B2DVector aVector(rTestPoint - rPointA);
+ return aVector.getLength();
+ }
+ else
+ {
+ // get the relative cut value on line vector (Vector1) for cut with perpendicular through TestPoint
+ const B2DVector aVector1(rPointB - rPointA);
+ const B2DVector aVector2(rTestPoint - rPointA);
+ const double fDividend((aVector2.getX() * aVector1.getX()) + (aVector2.getY() * aVector1.getY()));
+ const double fDivisor((aVector1.getX() * aVector1.getX()) + (aVector1.getY() * aVector1.getY()));
+ const double fCut(fDividend / fDivisor);
+
+ if(fCut < 0.0)
+ {
+ // not in line range, get distance to PointA
+ rCut = 0.0;
+ return aVector2.getLength();
+ }
+ else if(fCut > 1.0)
+ {
+ // not in line range, get distance to PointB
+ rCut = 1.0;
+ const B2DVector aVector(rTestPoint - rPointB);
+ return aVector.getLength();
+ }
+ else
+ {
+ // in line range
+ const B2DPoint aCutPoint(rPointA + fCut * aVector1);
+ const B2DVector aVector(rTestPoint - aCutPoint);
+ rCut = fCut;
+ return aVector.getLength();
+ }
+ }
+ }
+
+ double getSmallestDistancePointToPolygon(const B2DPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rEdgeIndex, double& rCut)
+ {
+ double fRetval(DBL_MAX);
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 1L)
+ {
+ const double fZero(0.0);
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+ B2DCubicBezier aBezier;
+ aBezier.setStartPoint(rCandidate.getB2DPoint(0));
+
+ for(sal_uInt32 a(0L); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+ double fEdgeDist;
+ double fNewCut;
+ bool bEdgeIsCurve(false);
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
+ aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aBezier.testAndSolveTrivialBezier();
+ bEdgeIsCurve = aBezier.isBezier();
+ }
+
+ if(bEdgeIsCurve)
+ {
+ fEdgeDist = aBezier.getSmallestDistancePointToBezierSegment(rTestPoint, fNewCut);
+ }
+ else
+ {
+ fEdgeDist = getSmallestDistancePointToEdge(aBezier.getStartPoint(), aBezier.getEndPoint(), rTestPoint, fNewCut);
+ }
+
+ if(DBL_MAX == fRetval || fEdgeDist < fRetval)
+ {
+ fRetval = fEdgeDist;
+ rEdgeIndex = a;
+ rCut = fNewCut;
+
+ if(fTools::equal(fRetval, fZero))
+ {
+ // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
+ fRetval = 0.0;
+ break;
+ }
+ }
+
+ // prepare next step
+ aBezier.setStartPoint(aBezier.getEndPoint());
+ }
+
+ if(1.0 == rCut)
+ {
+ // correct rEdgeIndex when not last point
+ if(rCandidate.isClosed())
+ {
+ rEdgeIndex = getIndexOfSuccessor(rEdgeIndex, rCandidate);
+ rCut = 0.0;
+ }
+ else
+ {
+ if(rEdgeIndex != nEdgeCount - 1L)
+ {
+ rEdgeIndex++;
+ rCut = 0.0;
+ }
+ }
+ }
+ }
+
+ return fRetval;
+ }
+
+ B2DPoint distort(const B2DPoint& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
+ {
+ if(fTools::equalZero(rOriginal.getWidth()) || fTools::equalZero(rOriginal.getHeight()))
+ {
+ return rCandidate;
+ }
+ else
+ {
+ const double fRelativeX((rCandidate.getX() - rOriginal.getMinX()) / rOriginal.getWidth());
+ const double fRelativeY((rCandidate.getY() - rOriginal.getMinY()) / rOriginal.getHeight());
+ const double fOneMinusRelativeX(1.0 - fRelativeX);
+ const double fOneMinusRelativeY(1.0 - fRelativeY);
+ const double fNewX((fOneMinusRelativeY) * ((fOneMinusRelativeX) * rTopLeft.getX() + fRelativeX * rTopRight.getX()) +
+ fRelativeY * ((fOneMinusRelativeX) * rBottomLeft.getX() + fRelativeX * rBottomRight.getX()));
+ const double fNewY((fOneMinusRelativeX) * ((fOneMinusRelativeY) * rTopLeft.getY() + fRelativeY * rBottomLeft.getY()) +
+ fRelativeX * ((fOneMinusRelativeY) * rTopRight.getY() + fRelativeY * rBottomRight.getY()));
+
+ return B2DPoint(fNewX, fNewY);
+ }
+ }
+
+ B2DPolygon distort(const B2DPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount && 0.0 != rOriginal.getWidth() && 0.0 != rOriginal.getHeight())
+ {
+ B2DPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ aRetval.append(distort(rCandidate.getB2DPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ if(!rCandidate.getPrevControlPoint(a).equalZero())
+ {
+ aRetval.setPrevControlPoint(a, distort(rCandidate.getPrevControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
+ }
+
+ if(!rCandidate.getNextControlPoint(a).equalZero())
+ {
+ aRetval.setNextControlPoint(a, distort(rCandidate.getNextControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
+ }
+ }
+ }
+
+ aRetval.setClosed(rCandidate.isClosed());
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ B2DPolygon rotateAroundPoint(const B2DPolygon& rCandidate, const B2DPoint& rCenter, double fAngle)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+ B2DPolygon aRetval(rCandidate);
+
+ if(nPointCount)
+ {
+ B2DHomMatrix aMatrix;
+
+ aMatrix.translate(-rCenter.getX(), -rCenter.getY());
+ aMatrix.rotate(fAngle);
+ aMatrix.translate(rCenter.getX(), rCenter.getY());
+
+ aRetval.transform(aMatrix);
+ }
+
+ return aRetval;
+ }
+
+ B2DPolygon expandToCurve(const B2DPolygon& rCandidate)
+ {
+ B2DPolygon aRetval(rCandidate);
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ expandToCurveInPoint(aRetval, a);
+ }
+
+ return aRetval;
+ }
+
+ bool expandToCurveInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex)
+ {
+ OSL_ENSURE(nIndex < rCandidate.count(), "expandToCurveInPoint: Access to polygon out of range (!)");
+ bool bRetval(false);
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount)
+ {
+ // predecessor
+ if(!rCandidate.isPrevControlPointUsed(nIndex))
+ {
+ if(!rCandidate.isClosed() && 0 == nIndex)
+ {
+ // do not create previous vector for start point of open polygon
+ }
+ else
+ {
+ const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
+ rCandidate.setPrevControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0));
+ bRetval = true;
+ }
+ }
+
+ // successor
+ if(!rCandidate.isNextControlPointUsed(nIndex))
+ {
+ if(!rCandidate.isClosed() && nIndex + 1 == nPointCount)
+ {
+ // do not create next vector for end point of open polygon
+ }
+ else
+ {
+ const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
+ rCandidate.setNextControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0));
+ bRetval = true;
+ }
+ }
+ }
+
+ return bRetval;
+ }
+
+ B2DPolygon setContinuity(const B2DPolygon& rCandidate, B2VectorContinuity eContinuity)
+ {
+ B2DPolygon aRetval(rCandidate);
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ setContinuityInPoint(aRetval, a, eContinuity);
+ }
+
+ return aRetval;
+ }
+
+ bool setContinuityInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex, B2VectorContinuity eContinuity)
+ {
+ OSL_ENSURE(nIndex < rCandidate.count(), "setContinuityInPoint: Access to polygon out of range (!)");
+ bool bRetval(false);
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount)
+ {
+ const B2DPoint aCurrentPoint(rCandidate.getB2DPoint(nIndex));
+
+ switch(eContinuity)
+ {
+ case CONTINUITY_NONE :
+ {
+ if(rCandidate.isPrevControlPointUsed(nIndex))
+ {
+ if(!rCandidate.isClosed() && 0 == nIndex)
+ {
+ // remove existing previous vector for start point of open polygon
+ rCandidate.resetPrevControlPoint(nIndex);
+ }
+ else
+ {
+ const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
+ rCandidate.setPrevControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0));
+ }
+
+ bRetval = true;
+ }
+
+ if(rCandidate.isNextControlPointUsed(nIndex))
+ {
+ if(!rCandidate.isClosed() && nIndex == nPointCount + 1)
+ {
+ // remove next vector for end point of open polygon
+ rCandidate.resetNextControlPoint(nIndex);
+ }
+ else
+ {
+ const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
+ rCandidate.setNextControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0));
+ }
+
+ bRetval = true;
+ }
+
+ break;
+ }
+ case CONTINUITY_C1 :
+ {
+ if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex))
+ {
+ // lengths both exist since both are used
+ B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint);
+ B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint);
+ const double fLenPrev(aVectorPrev.getLength());
+ const double fLenNext(aVectorNext.getLength());
+ aVectorPrev.normalize();
+ aVectorNext.normalize();
+ const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext));
+
+ if(ORIENTATION_NEUTRAL == aOrientation && aVectorPrev.scalar(aVectorNext) < 0.0)
+ {
+ // parallel and opposite direction; check length
+ if(fTools::equal(fLenPrev, fLenNext))
+ {
+ // this would be even C2, but we want C1. Use the lengths of the corresponding edges.
+ const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
+ const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
+ const double fLenPrevEdge(B2DVector(rCandidate.getB2DPoint(nPrevIndex) - aCurrentPoint).getLength() * (1.0 / 3.0));
+ const double fLenNextEdge(B2DVector(rCandidate.getB2DPoint(nNextIndex) - aCurrentPoint).getLength() * (1.0 / 3.0));
+
+ rCandidate.setControlPoints(nIndex,
+ aCurrentPoint + (aVectorPrev * fLenPrevEdge),
+ aCurrentPoint + (aVectorNext * fLenNextEdge));
+ bRetval = true;
+ }
+ }
+ else
+ {
+ // not parallel or same direction, set vectors and length
+ const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext));
+
+ if(ORIENTATION_POSITIVE == aOrientation)
+ {
+ rCandidate.setControlPoints(nIndex,
+ aCurrentPoint - (aNormalizedPerpendicular * fLenPrev),
+ aCurrentPoint + (aNormalizedPerpendicular * fLenNext));
+ }
+ else
+ {
+ rCandidate.setControlPoints(nIndex,
+ aCurrentPoint + (aNormalizedPerpendicular * fLenPrev),
+ aCurrentPoint - (aNormalizedPerpendicular * fLenNext));
+ }
+
+ bRetval = true;
+ }
+ }
+ break;
+ }
+ case CONTINUITY_C2 :
+ {
+ if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex))
+ {
+ // lengths both exist since both are used
+ B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint);
+ B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint);
+ const double fCommonLength((aVectorPrev.getLength() + aVectorNext.getLength()) / 2.0);
+ aVectorPrev.normalize();
+ aVectorNext.normalize();
+ const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext));
+
+ if(ORIENTATION_NEUTRAL == aOrientation && aVectorPrev.scalar(aVectorNext) < 0.0)
+ {
+ // parallel and opposite direction; set length. Use one direction for better numerical correctness
+ const B2DVector aScaledDirection(aVectorPrev * fCommonLength);
+
+ rCandidate.setControlPoints(nIndex,
+ aCurrentPoint + aScaledDirection,
+ aCurrentPoint - aScaledDirection);
+ }
+ else
+ {
+ // not parallel or same direction, set vectors and length
+ const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext));
+ const B2DVector aPerpendicular(aNormalizedPerpendicular * fCommonLength);
+
+ if(ORIENTATION_POSITIVE == aOrientation)
+ {
+ rCandidate.setControlPoints(nIndex,
+ aCurrentPoint - aPerpendicular,
+ aCurrentPoint + aPerpendicular);
+ }
+ else
+ {
+ rCandidate.setControlPoints(nIndex,
+ aCurrentPoint + aPerpendicular,
+ aCurrentPoint - aPerpendicular);
+ }
+ }
+
+ bRetval = true;
+ }
+ break;
+ }
+ }
+ }
+
+ return bRetval;
+ }
+
+ B2DPolygon growInNormalDirection(const B2DPolygon& rCandidate, double fValue)
+ {
+ if(0.0 != fValue)
+ {
+ if(rCandidate.areControlPointsUsed())
+ {
+ // call myself recursively with subdivided input
+ const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate));
+ return growInNormalDirection(aCandidate, fValue);
+ }
+ else
+ {
+ B2DPolygon aRetval;
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount)
+ {
+ B2DPoint aPrev(rCandidate.getB2DPoint(nPointCount - 1L));
+ B2DPoint aCurrent(rCandidate.getB2DPoint(0L));
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B2DPoint aNext(rCandidate.getB2DPoint(a + 1L == nPointCount ? 0L : a + 1L));
+ const B2DVector aBack(aPrev - aCurrent);
+ const B2DVector aForw(aNext - aCurrent);
+ const B2DVector aPerpBack(getNormalizedPerpendicular(aBack));
+ const B2DVector aPerpForw(getNormalizedPerpendicular(aForw));
+ B2DVector aDirection(aPerpBack - aPerpForw);
+ aDirection.normalize();
+ aDirection *= fValue;
+ aRetval.append(aCurrent + aDirection);
+
+ // prepare next step
+ aPrev = aCurrent;
+ aCurrent = aNext;
+ }
+ }
+
+ // copy closed state
+ aRetval.setClosed(rCandidate.isClosed());
+
+ return aRetval;
+ }
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ B2DPolygon reSegmentPolygon(const B2DPolygon& rCandidate, sal_uInt32 nSegments)
+ {
+ B2DPolygon aRetval;
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount && nSegments)
+ {
+ // get current segment count
+ const sal_uInt32 nSegmentCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+
+ if(nSegmentCount == nSegments)
+ {
+ aRetval = rCandidate;
+ }
+ else
+ {
+ const double fLength(getLength(rCandidate));
+ const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nSegments : nSegments + 1L);
+
+ for(sal_uInt32 a(0L); a < nLoopCount; a++)
+ {
+ const double fRelativePos((double)a / (double)nSegments); // 0.0 .. 1.0
+ const B2DPoint aNewPoint(getPositionRelative(rCandidate, fRelativePos, fLength));
+ aRetval.append(aNewPoint);
+ }
+
+ // copy closed flag
+ aRetval.setClosed(rCandidate.isClosed());
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DPolygon reSegmentPolygonEdges(const B2DPolygon& rCandidate, sal_uInt32 nSubEdges, bool bHandleCurvedEdges, bool bHandleStraightEdges)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount < 2 || nSubEdges < 2 || (!bHandleCurvedEdges && !bHandleStraightEdges))
+ {
+ // nothing to do:
+ // - less than two points -> no edge at all
+ // - less than two nSubEdges -> no resegment necessary
+ // - neither bHandleCurvedEdges nor bHandleStraightEdges -> nothing to do
+ return rCandidate;
+ }
+ else
+ {
+ B2DPolygon aRetval;
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+ B2DCubicBezier aCurrentEdge;
+
+ // prepare first edge and add start point to target
+ aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0));
+ aRetval.append(aCurrentEdge.getStartPoint());
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ // fill edge
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a));
+ aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+
+ if(aCurrentEdge.isBezier())
+ {
+ if(bHandleCurvedEdges)
+ {
+ for(sal_uInt32 b(nSubEdges); b > 1; b--)
+ {
+ const double fSplitPoint(1.0 / b);
+ B2DCubicBezier aLeftPart;
+
+ aCurrentEdge.split(fSplitPoint, &aLeftPart, &aCurrentEdge);
+ aRetval.appendBezierSegment(aLeftPart.getControlPointA(), aLeftPart.getControlPointB(), aLeftPart.getEndPoint());
+ }
+ }
+
+ // copy remaining segment to target
+ aRetval.appendBezierSegment(aCurrentEdge.getControlPointA(), aCurrentEdge.getControlPointB(), aCurrentEdge.getEndPoint());
+ }
+ else
+ {
+ if(bHandleStraightEdges)
+ {
+ for(sal_uInt32 b(nSubEdges); b > 1; b--)
+ {
+ const double fSplitPoint(1.0 / b);
+ const B2DPoint aSplitPoint(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fSplitPoint));
+
+ aRetval.append(aSplitPoint);
+ aCurrentEdge.setStartPoint(aSplitPoint);
+ }
+ }
+
+ // copy remaining segment to target
+ aRetval.append(aCurrentEdge.getEndPoint());
+ }
+
+ // prepare next step
+ aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint());
+ }
+
+ // copy closed flag and return
+ aRetval.setClosed(rCandidate.isClosed());
+ return aRetval;
+ }
+ }
+
+ B2DPolygon interpolate(const B2DPolygon& rOld1, const B2DPolygon& rOld2, double t)
+ {
+ OSL_ENSURE(rOld1.count() == rOld2.count(), "B2DPolygon interpolate: Different geometry (!)");
+
+ if(fTools::lessOrEqual(t, 0.0) || rOld1 == rOld2)
+ {
+ return rOld1;
+ }
+ else if(fTools::moreOrEqual(t, 1.0))
+ {
+ return rOld2;
+ }
+ else
+ {
+ B2DPolygon aRetval;
+ const bool bInterpolateVectors(rOld1.areControlPointsUsed() || rOld2.areControlPointsUsed());
+ aRetval.setClosed(rOld1.isClosed() && rOld2.isClosed());
+
+ for(sal_uInt32 a(0L); a < rOld1.count(); a++)
+ {
+ aRetval.append(interpolate(rOld1.getB2DPoint(a), rOld2.getB2DPoint(a), t));
+
+ if(bInterpolateVectors)
+ {
+ aRetval.setPrevControlPoint(a, interpolate(rOld1.getPrevControlPoint(a), rOld2.getPrevControlPoint(a), t));
+ aRetval.setNextControlPoint(a, interpolate(rOld1.getNextControlPoint(a), rOld2.getNextControlPoint(a), t));
+ }
+ }
+
+ return aRetval;
+ }
+ }
+
+ bool isPolyPolygonEqualRectangle( const B2DPolyPolygon& rPolyPoly,
+ const B2DRange& rRect )
+ {
+ // exclude some cheap cases first
+ if( rPolyPoly.count() != 1 )
+ return false;
+
+ // fill array with rectangle vertices
+ const B2DPoint aPoints[] =
+ {
+ B2DPoint(rRect.getMinX(),rRect.getMinY()),
+ B2DPoint(rRect.getMaxX(),rRect.getMinY()),
+ B2DPoint(rRect.getMaxX(),rRect.getMaxY()),
+ B2DPoint(rRect.getMinX(),rRect.getMaxY())
+ };
+
+ const B2DPolygon& rPoly( rPolyPoly.getB2DPolygon(0) );
+ const sal_uInt32 nCount( rPoly.count() );
+ const double epsilon = ::std::numeric_limits<double>::epsilon();
+
+ for(unsigned int j=0; j<4; ++j)
+ {
+ const B2DPoint &p1 = aPoints[j];
+ const B2DPoint &p2 = aPoints[(j+1)%4];
+ bool bPointOnBoundary = false;
+ for( sal_uInt32 i=0; i<nCount; ++i )
+ {
+ const B2DPoint p(rPoly.getB2DPoint(i));
+
+ // 1 | x0 y0 1 |
+ // A = - | x1 y1 1 |
+ // 2 | x2 y2 1 |
+ double fDoubleArea = p2.getX()*p.getY() -
+ p2.getY()*p.getX() -
+ p1.getX()*p.getY() +
+ p1.getY()*p.getX() +
+ p1.getX()*p2.getY() -
+ p1.getY()*p2.getX();
+
+ if(fDoubleArea < epsilon)
+ {
+ bPointOnBoundary=true;
+ break;
+ }
+ }
+ if(!(bPointOnBoundary))
+ return false;
+ }
+
+ return true;
+ }
+
+
+ // create simplified version of the original polygon by
+ // replacing segments with spikes/loops and self intersections
+ // by several trivial sub-segments
+ B2DPolygon createSimplifiedPolygon( const B2DPolygon& rCandidate )
+ {
+ const sal_uInt32 nCount(rCandidate.count());
+
+ if(nCount && rCandidate.areControlPointsUsed())
+ {
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nCount : nCount - 1);
+ B2DPolygon aRetval;
+ B2DCubicBezier aSegment;
+
+ aSegment.setStartPoint(rCandidate.getB2DPoint(0));
+ aRetval.append(aSegment.getStartPoint());
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ // fill edge
+ const sal_uInt32 nNextIndex((a + 1) % nCount);
+ aSegment.setControlPointA(rCandidate.getNextControlPoint(a));
+ aSegment.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aSegment.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+
+ if(aSegment.isBezier())
+ {
+ double fExtremumPos(0.0);
+ sal_uInt32 nExtremumCounter(4);
+
+ while(nExtremumCounter-- && aSegment.isBezier() && aSegment.getMinimumExtremumPosition(fExtremumPos))
+ {
+ // split off left, now extremum-free part and append
+ B2DCubicBezier aLeft;
+
+ aSegment.split(fExtremumPos, &aLeft, &aSegment);
+ aLeft.testAndSolveTrivialBezier();
+ aSegment.testAndSolveTrivialBezier();
+
+ if(aLeft.isBezier())
+ {
+ aRetval.appendBezierSegment(aLeft.getControlPointA(), aLeft.getControlPointB(), aLeft.getEndPoint());
+ }
+ else
+ {
+ aRetval.append(aLeft.getEndPoint());
+ }
+ }
+
+ // append (evtl. reduced) rest of Segment
+ if(aSegment.isBezier())
+ {
+ aRetval.appendBezierSegment(aSegment.getControlPointA(), aSegment.getControlPointB(), aSegment.getEndPoint());
+ }
+ else
+ {
+ aRetval.append(aSegment.getEndPoint());
+ }
+ }
+ else
+ {
+ // simple edge, append end point
+ aRetval.append(aSegment.getEndPoint());
+ }
+
+ // prepare next edge
+ aSegment.setStartPoint(aSegment.getEndPoint());
+ }
+
+ // copy closed flag and check for double points
+ aRetval.setClosed(rCandidate.isClosed());
+ aRetval.removeDoublePoints();
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ // #i76891#
+ B2DPolygon simplifyCurveSegments(const B2DPolygon& rCandidate)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount && rCandidate.areControlPointsUsed())
+ {
+ // prepare loop
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+ B2DPolygon aRetval;
+ B2DCubicBezier aBezier;
+ aBezier.setStartPoint(rCandidate.getB2DPoint(0));
+
+ // try to avoid costly reallocations
+ aRetval.reserve( nEdgeCount+1);
+
+ // add start point
+ aRetval.append(aBezier.getStartPoint());
+
+ for(sal_uInt32 a(0L); a < nEdgeCount; a++)
+ {
+ // get values for edge
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
+ aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
+ aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
+ aBezier.testAndSolveTrivialBezier();
+
+ // still bezier?
+ if(aBezier.isBezier())
+ {
+ // add edge with control vectors
+ aRetval.appendBezierSegment(aBezier.getControlPointA(), aBezier.getControlPointB(), aBezier.getEndPoint());
+ }
+ else
+ {
+ // add edge
+ aRetval.append(aBezier.getEndPoint());
+ }
+
+ // next point
+ aBezier.setStartPoint(aBezier.getEndPoint());
+ }
+
+ if(rCandidate.isClosed())
+ {
+ // set closed flag, rescue control point and correct last double point
+ closeWithGeometryChange(aRetval);
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ // makes the given indexed point the new polygon start point. To do that, the points in the
+ // polygon will be rotated. This is only valid for closed polygons, for non-closed ones
+ // an assertion will be triggered
+ B2DPolygon makeStartPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndexOfNewStatPoint)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 2 && nIndexOfNewStatPoint != 0 && nIndexOfNewStatPoint < nPointCount)
+ {
+ OSL_ENSURE(rCandidate.isClosed(), "makeStartPoint: only valid for closed polygons (!)");
+ B2DPolygon aRetval;
+
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const sal_uInt32 nSourceIndex((a + nIndexOfNewStatPoint) % nPointCount);
+ aRetval.append(rCandidate.getB2DPoint(nSourceIndex));
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ aRetval.setPrevControlPoint(a, rCandidate.getPrevControlPoint(nSourceIndex));
+ aRetval.setNextControlPoint(a, rCandidate.getNextControlPoint(nSourceIndex));
+ }
+ }
+
+ return aRetval;
+ }
+
+ return rCandidate;
+ }
+
+ B2DPolygon createEdgesOfGivenLength(const B2DPolygon& rCandidate, double fLength, double fStart, double fEnd)
+ {
+ B2DPolygon aRetval;
+
+ if(fLength < 0.0)
+ {
+ fLength = 0.0;
+ }
+
+ if(!fTools::equalZero(fLength))
+ {
+ if(fStart < 0.0)
+ {
+ fStart = 0.0;
+ }
+
+ if(fEnd < 0.0)
+ {
+ fEnd = 0.0;
+ }
+
+ if(fEnd < fStart)
+ {
+ fEnd = fStart;
+ }
+
+ // iterate and consume pieces with fLength. First subdivide to reduce input to line segments
+ const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount > 1)
+ {
+ const bool bEndActive(!fTools::equalZero(fEnd));
+ const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1);
+ B2DPoint aCurrent(aCandidate.getB2DPoint(0));
+ double fPositionInEdge(fStart);
+ double fAbsolutePosition(fStart);
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex));
+ const B2DVector aEdge(aNext - aCurrent);
+ double fEdgeLength(aEdge.getLength());
+
+ if(!fTools::equalZero(fEdgeLength))
+ {
+ while(fTools::less(fPositionInEdge, fEdgeLength))
+ {
+ // move position on edge forward as long as on edge
+ const double fScalar(fPositionInEdge / fEdgeLength);
+ aRetval.append(aCurrent + (aEdge * fScalar));
+ fPositionInEdge += fLength;
+
+ if(bEndActive)
+ {
+ fAbsolutePosition += fLength;
+
+ if(fTools::more(fAbsolutePosition, fEnd))
+ {
+ break;
+ }
+ }
+ }
+
+ // substract length of current edge
+ fPositionInEdge -= fEdgeLength;
+ }
+
+ if(bEndActive && fTools::more(fAbsolutePosition, fEnd))
+ {
+ break;
+ }
+
+ // prepare next step
+ aCurrent = aNext;
+ }
+
+ // keep closed state
+ aRetval.setClosed(aCandidate.isClosed());
+ }
+ else
+ {
+ // source polygon has only one point, return unchanged
+ aRetval = aCandidate;
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DPolygon createWaveline(const B2DPolygon& rCandidate, double fWaveWidth, double fWaveHeight)
+ {
+ B2DPolygon aRetval;
+
+ if(fWaveWidth < 0.0)
+ {
+ fWaveWidth = 0.0;
+ }
+
+ if(fWaveHeight < 0.0)
+ {
+ fWaveHeight = 0.0;
+ }
+
+ const bool bHasWidth(!fTools::equalZero(fWaveWidth));
+ const bool bHasHeight(!fTools::equalZero(fWaveHeight));
+
+ if(bHasWidth)
+ {
+ if(bHasHeight)
+ {
+ // width and height, create waveline. First subdivide to reduce input to line segments
+ // of WaveWidth. Last segment may be missing. If this turns out to be a problem, it
+ // may be added here again using the original last point from rCandidate. It may
+ // also be the case that rCandidate was closed. To simplify things it is handled here
+ // as if it was opened.
+ // Result from createEdgesOfGivenLength contains no curved segments, handle as straight
+ // edges.
+ const B2DPolygon aEqualLenghEdges(createEdgesOfGivenLength(rCandidate, fWaveWidth));
+ const sal_uInt32 nPointCount(aEqualLenghEdges.count());
+
+ if(nPointCount > 1)
+ {
+ // iterate over straight edges, add start point
+ B2DPoint aCurrent(aEqualLenghEdges.getB2DPoint(0));
+ aRetval.append(aCurrent);
+
+ for(sal_uInt32 a(0); a < nPointCount - 1; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const B2DPoint aNext(aEqualLenghEdges.getB2DPoint(nNextIndex));
+ const B2DVector aEdge(aNext - aCurrent);
+ const B2DVector aPerpendicular(getNormalizedPerpendicular(aEdge));
+ const B2DVector aControlOffset((aEdge * 0.467308) - (aPerpendicular * fWaveHeight));
+
+ // add curve segment
+ aRetval.appendBezierSegment(
+ aCurrent + aControlOffset,
+ aNext - aControlOffset,
+ aNext);
+
+ // prepare next step
+ aCurrent = aNext;
+ }
+ }
+ }
+ else
+ {
+ // width but no height -> return original polygon
+ aRetval = rCandidate;
+ }
+ }
+ else
+ {
+ // no width -> no waveline, stay empty and return
+ }
+
+ return aRetval;
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // comparators with tolerance for 2D Polygons
+
+ bool equal(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, const double& rfSmallValue)
+ {
+ const sal_uInt32 nPointCount(rCandidateA.count());
+
+ if(nPointCount != rCandidateB.count())
+ return false;
+
+ const bool bClosed(rCandidateA.isClosed());
+
+ if(bClosed != rCandidateB.isClosed())
+ return false;
+
+ const bool bAreControlPointsUsed(rCandidateA.areControlPointsUsed());
+
+ if(bAreControlPointsUsed != rCandidateB.areControlPointsUsed())
+ return false;
+
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const B2DPoint aPoint(rCandidateA.getB2DPoint(a));
+
+ if(!aPoint.equal(rCandidateB.getB2DPoint(a), rfSmallValue))
+ return false;
+
+ if(bAreControlPointsUsed)
+ {
+ const basegfx::B2DPoint aPrev(rCandidateA.getPrevControlPoint(a));
+
+ if(!aPrev.equal(rCandidateB.getPrevControlPoint(a), rfSmallValue))
+ return false;
+
+ const basegfx::B2DPoint aNext(rCandidateA.getNextControlPoint(a));
+
+ if(!aNext.equal(rCandidateB.getNextControlPoint(a), rfSmallValue))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool equal(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB)
+ {
+ const double fSmallValue(fTools::getSmallValue());
+
+ return equal(rCandidateA, rCandidateB, fSmallValue);
+ }
+
+ // snap points of horizontal or vertical edges to discrete values
+ B2DPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolygon& rCandidate)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 1)
+ {
+ // Start by copying the source polygon to get a writeable copy. The closed state is
+ // copied by aRetval's initialisation, too, so no need to copy it in this method
+ B2DPolygon aRetval(rCandidate);
+
+ // prepare geometry data. Get rounded from original
+ B2ITuple aPrevTuple(basegfx::fround(rCandidate.getB2DPoint(nPointCount - 1)));
+ B2DPoint aCurrPoint(rCandidate.getB2DPoint(0));
+ B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
+
+ // loop over all points. This will also snap the implicit closing edge
+ // even when not closed, but that's no problem here
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ // get next point. Get rounded from original
+ const bool bLastRun(a + 1 == nPointCount);
+ const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1);
+ const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex));
+ const B2ITuple aNextTuple(basegfx::fround(aNextPoint));
+
+ // get the states
+ const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
+ const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
+ const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
+ const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
+ const bool bSnapX(bPrevVertical || bNextVertical);
+ const bool bSnapY(bPrevHorizontal || bNextHorizontal);
+
+ if(bSnapX || bSnapY)
+ {
+ const B2DPoint aSnappedPoint(
+ bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
+ bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
+
+ aRetval.setB2DPoint(a, aSnappedPoint);
+ }
+
+ // prepare next point
+ if(!bLastRun)
+ {
+ aPrevTuple = aCurrTuple;
+ aCurrPoint = aNextPoint;
+ aCurrTuple = aNextTuple;
+ }
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/polygon/b2dpolygontriangulator.cxx b/basegfx/source/polygon/b2dpolygontriangulator.cxx
new file mode 100644
index 000000000000..4f0a3dc1d8cc
--- /dev/null
+++ b/basegfx/source/polygon/b2dpolygontriangulator.cxx
@@ -0,0 +1,469 @@
+/*************************************************************************
+ *
+ * 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: b2dpolygontriangulator.cxx,v $
+ * $Revision: 1.7 $
+ *
+ * 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/polygon/b2dpolygontriangulator.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+#include <algorithm>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace
+ {
+ class EdgeEntry
+ {
+ EdgeEntry* mpNext;
+ B2DPoint maStart;
+ B2DPoint maEnd;
+ double mfAtan2;
+
+ public:
+ EdgeEntry(const B2DPoint& rStart, const B2DPoint& rEnd)
+ : mpNext(0L),
+ maStart(rStart),
+ maEnd(rEnd),
+ mfAtan2(0.0)
+ {
+ // make sure edge goes down. If horizontal, let it go to the right (left-handed).
+ bool bSwap(false);
+
+ if(::basegfx::fTools::equal(maStart.getY(), maEnd.getY()))
+ {
+ if(maStart.getX() > maEnd.getX())
+ {
+ bSwap = true;
+ }
+ }
+ else if(maStart.getY() > maEnd.getY())
+ {
+ bSwap = true;
+ }
+
+ if(bSwap)
+ {
+ maStart = rEnd;
+ maEnd = rStart;
+ }
+
+ mfAtan2 = atan2(maEnd.getY() - maStart.getY(), maEnd.getX() - maStart.getX());
+ }
+
+ ~EdgeEntry()
+ {
+ }
+
+ bool operator<(const EdgeEntry& rComp) const
+ {
+ if(::basegfx::fTools::equal(maStart.getY(), rComp.maStart.getY()))
+ {
+ if(::basegfx::fTools::equal(maStart.getX(), rComp.maStart.getX()))
+ {
+ // same in x and y -> same start point. Sort emitting vectors from left to right.
+ return (mfAtan2 > rComp.mfAtan2);
+ }
+
+ return (maStart.getX() < rComp.maStart.getX());
+ }
+
+ return (maStart.getY() < rComp.maStart.getY());
+ }
+
+ bool operator==(const EdgeEntry& rComp) const
+ {
+ return (maStart.equal(rComp.maStart) && maEnd.equal(rComp.maEnd));
+ }
+
+ bool operator!=(const EdgeEntry& rComp) const
+ {
+ return !(*this == rComp);
+ }
+
+ const B2DPoint& getStart() const { return maStart; }
+ const B2DPoint& getEnd() const { return maEnd; }
+
+ EdgeEntry* getNext() const { return mpNext; }
+ void setNext(EdgeEntry* pNext) { mpNext = pNext; }
+ };
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ typedef ::std::vector< EdgeEntry > EdgeEntries;
+ typedef ::std::vector< EdgeEntry* > EdgeEntryPointers;
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ class Triangulator
+ {
+ EdgeEntry* mpList;
+ EdgeEntries maStartEntries;
+ EdgeEntryPointers maNewEdgeEntries;
+ B2DPolygon maResult;
+
+ void handleClosingEdge(const B2DPoint& rStart, const B2DPoint& rEnd);
+ bool CheckPointInTriangle(EdgeEntry* pEdgeA, EdgeEntry* pEdgeB, const B2DPoint& rTestPoint);
+ void createTriangle(const B2DPoint& rA, const B2DPoint& rB, const B2DPoint& rC);
+
+ public:
+ Triangulator(const B2DPolyPolygon& rCandidate);
+ ~Triangulator();
+
+ const B2DPolygon getResult() const { return maResult; }
+ };
+
+ void Triangulator::handleClosingEdge(const B2DPoint& rStart, const B2DPoint& rEnd)
+ {
+ // create an entry, else the comparison might use the wrong edges
+ EdgeEntry aNew(rStart, rEnd);
+ EdgeEntry* pCurr = mpList;
+ EdgeEntry* pPrev = 0L;
+
+ while(pCurr
+ && pCurr->getStart().getY() <= aNew.getStart().getY()
+ && *pCurr != aNew)
+ {
+ pPrev = pCurr;
+ pCurr = pCurr->getNext();
+ }
+
+ if(pCurr && *pCurr == aNew)
+ {
+ // found closing edge, remove
+ if(pPrev)
+ {
+ pPrev->setNext(pCurr->getNext());
+ }
+ else
+ {
+ mpList = pCurr->getNext();
+ }
+ }
+ else
+ {
+ // insert closing edge
+ EdgeEntry* pNew = new EdgeEntry(aNew);
+ maNewEdgeEntries.push_back(pNew);
+ pCurr = mpList;
+ pPrev = 0L;
+
+ while(pCurr && *pCurr < *pNew)
+ {
+ pPrev = pCurr;
+ pCurr = pCurr->getNext();
+ }
+
+ if(pPrev)
+ {
+ pNew->setNext(pPrev->getNext());
+ pPrev->setNext(pNew);
+ }
+ else
+ {
+ pNew->setNext(mpList);
+ mpList = pNew;
+ }
+ }
+ }
+
+ bool Triangulator::CheckPointInTriangle(EdgeEntry* pEdgeA, EdgeEntry* pEdgeB, const B2DPoint& rTestPoint)
+ {
+ // inside triangle or on edge?
+ if(tools::isPointInTriangle(pEdgeA->getStart(), pEdgeA->getEnd(), pEdgeB->getEnd(), rTestPoint, true))
+ {
+ // but not on point
+ if(!rTestPoint.equal(pEdgeA->getEnd()) && !rTestPoint.equal(pEdgeB->getEnd()))
+ {
+ // found point in triangle -> split triangle inserting two edges
+ EdgeEntry* pStart = new EdgeEntry(pEdgeA->getStart(), rTestPoint);
+ EdgeEntry* pEnd = new EdgeEntry(*pStart);
+ maNewEdgeEntries.push_back(pStart);
+ maNewEdgeEntries.push_back(pEnd);
+
+ pStart->setNext(pEnd);
+ pEnd->setNext(pEdgeA->getNext());
+ pEdgeA->setNext(pStart);
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void Triangulator::createTriangle(const B2DPoint& rA, const B2DPoint& rB, const B2DPoint& rC)
+ {
+ maResult.append(rA);
+ maResult.append(rB);
+ maResult.append(rC);
+ }
+
+ // consume as long as there are edges
+ Triangulator::Triangulator(const B2DPolyPolygon& rCandidate)
+ : mpList(0L)
+ {
+ // add all available edges to the single linked local list which will be sorted
+ // by Y,X,atan2 when adding nodes
+ if(rCandidate.count())
+ {
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ const B2DPolygon aPolygonCandidate(rCandidate.getB2DPolygon(a));
+ const sal_uInt32 nCount(aPolygonCandidate.count());
+
+ if(nCount > 2L)
+ {
+ B2DPoint aPrevPnt(aPolygonCandidate.getB2DPoint(nCount - 1L));
+
+ for(sal_uInt32 b(0L); b < nCount; b++)
+ {
+ B2DPoint aNextPnt(aPolygonCandidate.getB2DPoint(b));
+
+ if( !aPrevPnt.equal(aNextPnt) )
+ {
+ maStartEntries.push_back(EdgeEntry(aPrevPnt, aNextPnt));
+ }
+
+ aPrevPnt = aNextPnt;
+ }
+ }
+ }
+
+ if(maStartEntries.size())
+ {
+ // sort initial list
+ ::std::sort(maStartEntries.begin(), maStartEntries.end());
+
+ // insert to own simply linked list
+ EdgeEntries::iterator aPos(maStartEntries.begin());
+ mpList = &(*aPos++);
+ EdgeEntry* pLast = mpList;
+
+ while(aPos != maStartEntries.end())
+ {
+ EdgeEntry* pEntry = &(*aPos++);
+ pLast->setNext(pEntry);
+ pLast = pEntry;
+ }
+ }
+ }
+
+ while(mpList)
+ {
+ if(mpList->getNext() && mpList->getNext()->getStart().equal(mpList->getStart()))
+ {
+ // next candidate. There are two edges and start point is equal.
+ // Length is not zero.
+ EdgeEntry* pEdgeA = mpList;
+ EdgeEntry* pEdgeB = pEdgeA->getNext();
+
+ if( pEdgeA->getEnd().equal(pEdgeB->getEnd()) )
+ {
+ // start and end equal -> neutral triangle, delete both
+ mpList = pEdgeB->getNext();
+ }
+ else
+ {
+ const B2DVector aLeft(pEdgeA->getEnd() - pEdgeA->getStart());
+ const B2DVector aRight(pEdgeB->getEnd() - pEdgeA->getStart());
+
+ if(ORIENTATION_NEUTRAL == getOrientation(aLeft, aRight))
+ {
+ // edges are parallel and have different length -> neutral triangle,
+ // delete both edges and handle closing edge
+ mpList = pEdgeB->getNext();
+ handleClosingEdge(pEdgeA->getEnd(), pEdgeB->getEnd());
+ }
+ else
+ {
+ // not parallel, look for points inside
+ B2DRange aRange(pEdgeA->getStart(), pEdgeA->getEnd());
+ aRange.expand(pEdgeB->getEnd());
+ EdgeEntry* pTestEdge = pEdgeB->getNext();
+ bool bNoPointInTriangle(true);
+
+ // look for start point in triangle
+ while(bNoPointInTriangle && pTestEdge)
+ {
+ if(aRange.getMaxY() < pTestEdge->getStart().getY())
+ {
+ // edge is below test range and edges are sorted -> stop looking
+ break;
+ }
+ else
+ {
+ // do not look for edges with same start point, they are sorted and cannot end inside.
+ if(!pTestEdge->getStart().equal(pEdgeA->getStart()))
+ {
+ if(aRange.isInside(pTestEdge->getStart()))
+ {
+ bNoPointInTriangle = CheckPointInTriangle(pEdgeA, pEdgeB, pTestEdge->getStart());
+ }
+ }
+ }
+
+ // next candidate
+ pTestEdge = pTestEdge->getNext();
+ }
+
+ if(bNoPointInTriangle)
+ {
+ // look for end point in triange
+ pTestEdge = pEdgeB->getNext();
+
+ while(bNoPointInTriangle && pTestEdge)
+ {
+ if(aRange.getMaxY() < pTestEdge->getStart().getY())
+ {
+ // edge is below test range and edges are sorted -> stop looking
+ break;
+ }
+ else
+ {
+ // do not look for edges with same end point, they are sorted and cannot end inside.
+ if(!pTestEdge->getEnd().equal(pEdgeA->getStart()))
+ {
+ if(aRange.isInside(pTestEdge->getEnd()))
+ {
+ bNoPointInTriangle = CheckPointInTriangle(pEdgeA, pEdgeB, pTestEdge->getEnd());
+ }
+ }
+ }
+
+ // next candidate
+ pTestEdge = pTestEdge->getNext();
+ }
+ }
+
+ if(bNoPointInTriangle)
+ {
+ // create triangle, remove edges, handle closing edge
+ mpList = pEdgeB->getNext();
+ createTriangle(pEdgeA->getStart(), pEdgeB->getEnd(), pEdgeA->getEnd());
+ handleClosingEdge(pEdgeA->getEnd(), pEdgeB->getEnd());
+ }
+ }
+ }
+ }
+ else
+ {
+ // only one entry at start point, delete it
+ mpList = mpList->getNext();
+ }
+ }
+ }
+
+ Triangulator::~Triangulator()
+ {
+ EdgeEntryPointers::iterator aIter(maNewEdgeEntries.begin());
+
+ while(aIter != maNewEdgeEntries.end())
+ {
+ delete (*aIter++);
+ }
+ }
+
+ } // end of anonymous namespace
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace triangulator
+ {
+ B2DPolygon triangulate(const B2DPolygon& rCandidate)
+ {
+ B2DPolygon aRetval;
+
+ // subdivide locally (triangulate does not work with beziers), remove double and neutral points
+ B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? tools::adaptiveSubdivideByAngle(rCandidate) : rCandidate);
+ aCandidate.removeDoublePoints();
+ aCandidate = tools::removeNeutralPoints(aCandidate);
+
+ if(2L == aCandidate.count())
+ {
+ // candidate IS a triangle, just append
+ aRetval.append(aCandidate);
+ }
+ else if(aCandidate.count() > 2L)
+ {
+ if(tools::isConvex(aCandidate))
+ {
+ // polygon is convex, just use a triangle fan
+ tools::addTriangleFan(aCandidate, aRetval);
+ }
+ else
+ {
+ // polygon is concave.
+ const B2DPolyPolygon aCandPolyPoly(aCandidate);
+ Triangulator aTriangulator(aCandPolyPoly);
+ aRetval = aTriangulator.getResult();
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DPolygon triangulate(const B2DPolyPolygon& rCandidate)
+ {
+ B2DPolygon aRetval;
+
+ // subdivide locally (triangulate does not work with beziers)
+ B2DPolyPolygon aCandidate(rCandidate.areControlPointsUsed() ? tools::adaptiveSubdivideByAngle(rCandidate) : rCandidate);
+
+ if(1L == aCandidate.count())
+ {
+ // single polygon -> single polygon triangulation
+ const B2DPolygon aSinglePolygon(aCandidate.getB2DPolygon(0L));
+ aRetval = triangulate(aSinglePolygon);
+ }
+ else
+ {
+ Triangulator aTriangulator(aCandidate);
+ aRetval = aTriangulator.getResult();
+ }
+
+ return aRetval;
+ }
+ } // end of namespace triangulator
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/polygon/b2dpolypolygon.cxx b/basegfx/source/polygon/b2dpolypolygon.cxx
new file mode 100644
index 000000000000..6467e7120c03
--- /dev/null
+++ b/basegfx/source/polygon/b2dpolypolygon.cxx
@@ -0,0 +1,383 @@
+/*************************************************************************
+ *
+ * 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: b2dpolypolygon.cxx,v $
+ * $Revision: 1.21.4.1 $
+ *
+ * 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/polygon/b2dpolypolygon.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <rtl/instance.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+
+#include <functional>
+#include <vector>
+#include <algorithm>
+
+//////////////////////////////////////////////////////////////////////////////
+
+class ImplB2DPolyPolygon
+{
+ typedef ::std::vector< basegfx::B2DPolygon > PolygonVector;
+
+ PolygonVector maPolygons;
+
+public:
+ ImplB2DPolyPolygon() : maPolygons()
+ {
+ }
+
+ ImplB2DPolyPolygon(const basegfx::B2DPolygon& rToBeCopied) :
+ maPolygons(1,rToBeCopied)
+ {
+ }
+
+ bool operator==(const ImplB2DPolyPolygon& rPolygonList) const
+ {
+ // same polygon count?
+ if(maPolygons.size() != rPolygonList.maPolygons.size())
+ return false;
+
+ // compare polygon content
+ if(!(maPolygons == rPolygonList.maPolygons))
+ return false;
+
+ return true;
+ }
+
+ const basegfx::B2DPolygon& getB2DPolygon(sal_uInt32 nIndex) const
+ {
+ return maPolygons[nIndex];
+ }
+
+ void setB2DPolygon(sal_uInt32 nIndex, const basegfx::B2DPolygon& rPolygon)
+ {
+ maPolygons[nIndex] = rPolygon;
+ }
+
+ void insert(sal_uInt32 nIndex, const basegfx::B2DPolygon& rPolygon, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // add nCount copies of rPolygon
+ PolygonVector::iterator aIndex(maPolygons.begin());
+ aIndex += nIndex;
+ maPolygons.insert(aIndex, nCount, rPolygon);
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const basegfx::B2DPolyPolygon& rPolyPolygon)
+ {
+ const sal_uInt32 nCount = rPolyPolygon.count();
+
+ if(nCount)
+ {
+ // add nCount polygons from rPolyPolygon
+ maPolygons.reserve(maPolygons.size() + nCount);
+ PolygonVector::iterator aIndex(maPolygons.begin());
+ aIndex += nIndex;
+
+ for(sal_uInt32 a(0L); a < nCount; a++)
+ {
+ aIndex = maPolygons.insert(aIndex, rPolyPolygon.getB2DPolygon(a));
+ aIndex++;
+ }
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // remove polygon data
+ PolygonVector::iterator aStart(maPolygons.begin());
+ aStart += nIndex;
+ const PolygonVector::iterator aEnd(aStart + nCount);
+
+ maPolygons.erase(aStart, aEnd);
+ }
+ }
+
+ sal_uInt32 count() const
+ {
+ return maPolygons.size();
+ }
+
+ void setClosed(bool bNew)
+ {
+ for(sal_uInt32 a(0L); a < maPolygons.size(); a++)
+ {
+ maPolygons[a].setClosed(bNew);
+ }
+ }
+
+ void flip()
+ {
+ std::for_each( maPolygons.begin(),
+ maPolygons.end(),
+ std::mem_fun_ref( &basegfx::B2DPolygon::flip ));
+ }
+
+ void removeDoublePoints()
+ {
+ std::for_each( maPolygons.begin(),
+ maPolygons.end(),
+ std::mem_fun_ref( &basegfx::B2DPolygon::removeDoublePoints ));
+ }
+
+ void transform(const basegfx::B2DHomMatrix& rMatrix)
+ {
+ for(sal_uInt32 a(0L); a < maPolygons.size(); a++)
+ {
+ maPolygons[a].transform(rMatrix);
+ }
+ }
+
+ void makeUnique()
+ {
+ std::for_each( maPolygons.begin(),
+ maPolygons.end(),
+ std::mem_fun_ref( &basegfx::B2DPolygon::makeUnique ));
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace { struct DefaultPolyPolygon: public rtl::Static<B2DPolyPolygon::ImplType,
+ DefaultPolyPolygon> {}; }
+
+ B2DPolyPolygon::B2DPolyPolygon() :
+ mpPolyPolygon(DefaultPolyPolygon::get())
+ {
+ }
+
+ B2DPolyPolygon::B2DPolyPolygon(const B2DPolyPolygon& rPolyPolygon) :
+ mpPolyPolygon(rPolyPolygon.mpPolyPolygon)
+ {
+ }
+
+ B2DPolyPolygon::B2DPolyPolygon(const B2DPolygon& rPolygon) :
+ mpPolyPolygon( ImplB2DPolyPolygon(rPolygon) )
+ {
+ }
+
+ B2DPolyPolygon::~B2DPolyPolygon()
+ {
+ }
+
+ B2DPolyPolygon& B2DPolyPolygon::operator=(const B2DPolyPolygon& rPolyPolygon)
+ {
+ mpPolyPolygon = rPolyPolygon.mpPolyPolygon;
+ return *this;
+ }
+
+ void B2DPolyPolygon::makeUnique()
+ {
+ mpPolyPolygon.make_unique();
+ mpPolyPolygon->makeUnique();
+ }
+
+ bool B2DPolyPolygon::operator==(const B2DPolyPolygon& rPolyPolygon) const
+ {
+ if(mpPolyPolygon.same_object(rPolyPolygon.mpPolyPolygon))
+ return true;
+
+ return ((*mpPolyPolygon) == (*rPolyPolygon.mpPolyPolygon));
+ }
+
+ bool B2DPolyPolygon::operator!=(const B2DPolyPolygon& rPolyPolygon) const
+ {
+ return !((*this) == rPolyPolygon);
+ }
+
+ sal_uInt32 B2DPolyPolygon::count() const
+ {
+ return mpPolyPolygon->count();
+ }
+
+ B2DPolygon B2DPolyPolygon::getB2DPolygon(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolyPolygon->count(), "B2DPolyPolygon access outside range (!)");
+
+ return mpPolyPolygon->getB2DPolygon(nIndex);
+ }
+
+ void B2DPolyPolygon::setB2DPolygon(sal_uInt32 nIndex, const B2DPolygon& rPolygon)
+ {
+ OSL_ENSURE(nIndex < mpPolyPolygon->count(), "B2DPolyPolygon access outside range (!)");
+
+ if(getB2DPolygon(nIndex) != rPolygon)
+ mpPolyPolygon->setB2DPolygon(nIndex, rPolygon);
+ }
+
+ bool B2DPolyPolygon::areControlPointsUsed() const
+ {
+ for(sal_uInt32 a(0L); a < mpPolyPolygon->count(); a++)
+ {
+ const B2DPolygon& rPolygon = mpPolyPolygon->getB2DPolygon(a);
+
+ if(rPolygon.areControlPointsUsed())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void B2DPolyPolygon::insert(sal_uInt32 nIndex, const B2DPolygon& rPolygon, sal_uInt32 nCount)
+ {
+ OSL_ENSURE(nIndex <= mpPolyPolygon->count(), "B2DPolyPolygon Insert outside range (!)");
+
+ if(nCount)
+ mpPolyPolygon->insert(nIndex, rPolygon, nCount);
+ }
+
+ void B2DPolyPolygon::append(const B2DPolygon& rPolygon, sal_uInt32 nCount)
+ {
+ if(nCount)
+ mpPolyPolygon->insert(mpPolyPolygon->count(), rPolygon, nCount);
+ }
+
+ B2DPolyPolygon B2DPolyPolygon::getDefaultAdaptiveSubdivision() const
+ {
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < mpPolyPolygon->count(); a++)
+ {
+ aRetval.append(mpPolyPolygon->getB2DPolygon(a).getDefaultAdaptiveSubdivision());
+ }
+
+ return aRetval;
+ }
+
+ B2DRange B2DPolyPolygon::getB2DRange() const
+ {
+ B2DRange aRetval;
+
+ for(sal_uInt32 a(0L); a < mpPolyPolygon->count(); a++)
+ {
+ aRetval.expand(mpPolyPolygon->getB2DPolygon(a).getB2DRange());
+ }
+
+ return aRetval;
+ }
+
+ void B2DPolyPolygon::insert(sal_uInt32 nIndex, const B2DPolyPolygon& rPolyPolygon)
+ {
+ OSL_ENSURE(nIndex <= mpPolyPolygon->count(), "B2DPolyPolygon Insert outside range (!)");
+
+ if(rPolyPolygon.count())
+ mpPolyPolygon->insert(nIndex, rPolyPolygon);
+ }
+
+ void B2DPolyPolygon::append(const B2DPolyPolygon& rPolyPolygon)
+ {
+ if(rPolyPolygon.count())
+ mpPolyPolygon->insert(mpPolyPolygon->count(), rPolyPolygon);
+ }
+
+ void B2DPolyPolygon::remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ OSL_ENSURE(nIndex + nCount <= mpPolyPolygon->count(), "B2DPolyPolygon Remove outside range (!)");
+
+ if(nCount)
+ mpPolyPolygon->remove(nIndex, nCount);
+ }
+
+ void B2DPolyPolygon::clear()
+ {
+ mpPolyPolygon = DefaultPolyPolygon::get();
+ }
+
+ bool B2DPolyPolygon::isClosed() const
+ {
+ bool bRetval(true);
+
+ // PolyPOlygon is closed when all contained Polygons are closed or
+ // no Polygon exists.
+ for(sal_uInt32 a(0L); bRetval && a < mpPolyPolygon->count(); a++)
+ {
+ if(!(mpPolyPolygon->getB2DPolygon(a)).isClosed())
+ {
+ bRetval = false;
+ }
+ }
+
+ return bRetval;
+ }
+
+ void B2DPolyPolygon::setClosed(bool bNew)
+ {
+ if(bNew != isClosed())
+ mpPolyPolygon->setClosed(bNew);
+ }
+
+ void B2DPolyPolygon::flip()
+ {
+ if(mpPolyPolygon->count())
+ {
+ mpPolyPolygon->flip();
+ }
+ }
+
+ bool B2DPolyPolygon::hasDoublePoints() const
+ {
+ bool bRetval(false);
+
+ for(sal_uInt32 a(0L); !bRetval && a < mpPolyPolygon->count(); a++)
+ {
+ if((mpPolyPolygon->getB2DPolygon(a)).hasDoublePoints())
+ {
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+ }
+
+ void B2DPolyPolygon::removeDoublePoints()
+ {
+ if(hasDoublePoints())
+ mpPolyPolygon->removeDoublePoints();
+ }
+
+ void B2DPolyPolygon::transform(const B2DHomMatrix& rMatrix)
+ {
+ if(mpPolyPolygon->count() && !rMatrix.isIdentity())
+ {
+ mpPolyPolygon->transform(rMatrix);
+ }
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/polygon/b2dpolypolygoncutter.cxx b/basegfx/source/polygon/b2dpolypolygoncutter.cxx
new file mode 100644
index 000000000000..0674bfe3953e
--- /dev/null
+++ b/basegfx/source/polygon/b2dpolypolygoncutter.cxx
@@ -0,0 +1,1017 @@
+/*************************************************************************
+ *
+ * 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: b2dpolypolygoncutter.cxx,v $
+ * $Revision: 1.17 $
+ *
+ * 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/numeric/ftools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <vector>
+#include <algorithm>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace
+ {
+ //////////////////////////////////////////////////////////////////////////////
+
+ struct StripHelper
+ {
+ B2DRange maRange;
+ sal_Int32 mnDepth;
+ B2VectorOrientation meOrinetation;
+ };
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ struct PN
+ {
+ public:
+ B2DPoint maPoint;
+ sal_uInt32 mnI;
+ sal_uInt32 mnIP;
+ sal_uInt32 mnIN;
+ };
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ struct VN
+ {
+ public:
+ B2DVector maPrev;
+ B2DVector maNext;
+
+ // to have the correct curve segments in the crossover checks,
+ // it is necessary to keep the original next vectors, too. Else,
+ // it may happen to use a already switched next vector which
+ // would interpolate the wrong comparison point
+ B2DVector maOriginalNext;
+ };
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ struct SN
+ {
+ public:
+ PN* mpPN;
+
+ bool operator<(const SN& rComp) const
+ {
+ if(fTools::equal(mpPN->maPoint.getX(), rComp.mpPN->maPoint.getX()))
+ {
+ if(fTools::equal(mpPN->maPoint.getY(), rComp.mpPN->maPoint.getY()))
+ {
+ return (mpPN->mnI < rComp.mpPN->mnI);
+ }
+ else
+ {
+ return fTools::less(mpPN->maPoint.getY(), rComp.mpPN->maPoint.getY());
+ }
+ }
+ else
+ {
+ return fTools::less(mpPN->maPoint.getX(), rComp.mpPN->maPoint.getX());
+ }
+ }
+ };
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ typedef ::std::vector< PN > PNV;
+ typedef ::std::vector< VN > VNV;
+ typedef ::std::vector< SN > SNV;
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ class solver
+ {
+ private:
+ const B2DPolyPolygon maOriginal;
+ PNV maPNV;
+ VNV maVNV;
+ SNV maSNV;
+
+ unsigned mbIsCurve : 1;
+ unsigned mbChanged : 1;
+
+ void impAddPolygon(const sal_uInt32 aPos, const B2DPolygon& rGeometry)
+ {
+ const sal_uInt32 nCount(rGeometry.count());
+ PN aNewPN;
+ VN aNewVN;
+ SN aNewSN;
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const B2DPoint aPoint(rGeometry.getB2DPoint(a));
+ aNewPN.maPoint = aPoint;
+ aNewPN.mnI = aPos + a;
+ aNewPN.mnIP = aPos + ((a != 0) ? a - 1 : nCount - 1);
+ aNewPN.mnIN = aPos + ((a + 1 == nCount) ? 0 : a + 1);
+ maPNV.push_back(aNewPN);
+
+ if(mbIsCurve)
+ {
+ aNewVN.maPrev = rGeometry.getPrevControlPoint(a) - aPoint;
+ aNewVN.maNext = rGeometry.getNextControlPoint(a) - aPoint;
+ aNewVN.maOriginalNext = aNewVN.maNext;
+ maVNV.push_back(aNewVN);
+ }
+
+ aNewSN.mpPN = &maPNV[maPNV.size() - 1];
+ maSNV.push_back(aNewSN);
+ }
+ }
+
+ bool impLeftOfEdges(const B2DVector& rVecA, const B2DVector& rVecB, const B2DVector& rTest)
+ {
+ // tests if rTest is left of both directed line segments along the line -rVecA, rVecB. Test is
+ // with border.
+ if(rVecA.cross(rVecB) > 0.0)
+ {
+ // b is left turn seen from a, test if Test is left of both and so inside (left is seeen as inside)
+ const bool bBoolA(fTools::moreOrEqual(rVecA.cross(rTest), 0.0));
+ const bool bBoolB(fTools::lessOrEqual(rVecB.cross(rTest), 0.0));
+
+ return (bBoolA && bBoolB);
+ }
+ else
+ {
+ // b is right turn seen from a, test if Test is right of both and so outside (left is seeen as inside)
+ const bool bBoolA(fTools::lessOrEqual(rVecA.cross(rTest), 0.0));
+ const bool bBoolB(fTools::moreOrEqual(rVecB.cross(rTest), 0.0));
+
+ return (!(bBoolA && bBoolB));
+ }
+ }
+
+ void impSwitchNext(PN& rPNa, PN& rPNb)
+ {
+ ::std::swap(rPNa.mnIN, rPNb.mnIN);
+
+ if(mbIsCurve)
+ {
+ VN& rVNa = maVNV[rPNa.mnI];
+ VN& rVNb = maVNV[rPNb.mnI];
+
+ ::std::swap(rVNa.maNext, rVNb.maNext);
+ }
+
+ if(!mbChanged)
+ {
+ mbChanged = true;
+ }
+ }
+
+ B2DCubicBezier createSegment(const PN& rPN, bool bPrev) const
+ {
+ const B2DPoint& rStart(rPN.maPoint);
+ const B2DPoint& rEnd(maPNV[bPrev ? rPN.mnIP : rPN.mnIN].maPoint);
+ const B2DVector& rCPA(bPrev ? maVNV[rPN.mnI].maPrev : maVNV[rPN.mnI].maNext);
+ // Use maOriginalNext, not maNext to create the original (yet unchanged)
+ // curve segment. Otherwise, this segment would NOT ne correct.
+ const B2DVector& rCPB(bPrev ? maVNV[maPNV[rPN.mnIP].mnI].maOriginalNext : maVNV[maPNV[rPN.mnIN].mnI].maPrev);
+
+ return B2DCubicBezier(rStart, rStart + rCPA, rEnd + rCPB, rEnd);
+ }
+
+ void impHandleCommon(PN& rPNa, PN& rPNb)
+ {
+ if(mbIsCurve)
+ {
+ const B2DCubicBezier aNextA(createSegment(rPNa, false));
+ const B2DCubicBezier aPrevA(createSegment(rPNa, true));
+
+ if(aNextA.equal(aPrevA))
+ {
+ // deadend on A (identical edge)
+ return;
+ }
+
+ const B2DCubicBezier aNextB(createSegment(rPNb, false));
+ const B2DCubicBezier aPrevB(createSegment(rPNb, true));
+
+ if(aNextB.equal(aPrevB))
+ {
+ // deadend on B (identical edge)
+ return;
+ }
+
+ if(aPrevA.equal(aPrevB))
+ {
+ // common edge in same direction
+ if(aNextA.equal(aNextB))
+ {
+ // common edge in same direction continues
+ return;
+ }
+ else
+ {
+ // common edge in same direction leave
+ // action is done on enter
+ return;
+ }
+ }
+ else if(aPrevA.equal(aNextB))
+ {
+ // common edge in opposite direction
+ if(aNextA.equal(aPrevB))
+ {
+ // common edge in opposite direction continues
+ return;
+ }
+ else
+ {
+ // common edge in opposite direction leave
+ impSwitchNext(rPNa, rPNb);
+ }
+ }
+ else if(aNextA.equal(aNextB))
+ {
+ // common edge in same direction enter
+ // search leave edge
+ PN* pPNa2 = &maPNV[rPNa.mnIN];
+ PN* pPNb2 = &maPNV[rPNb.mnIN];
+ bool bOnEdge(true);
+
+ do
+ {
+ const B2DCubicBezier aNextA2(createSegment(*pPNa2, false));
+ const B2DCubicBezier aNextB2(createSegment(*pPNb2, false));
+
+ if(aNextA2.equal(aNextB2))
+ {
+ pPNa2 = &maPNV[pPNa2->mnIN];
+ pPNb2 = &maPNV[pPNb2->mnIN];
+ }
+ else
+ {
+ bOnEdge = false;
+ }
+ }
+ while(bOnEdge && pPNa2 != &rPNa && pPNa2 != &rPNa);
+
+ if(bOnEdge)
+ {
+ // loop over two identical polygon paths
+ return;
+ }
+ else
+ {
+ // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
+ // at enter/leave. Check for crossover.
+ const B2DVector aPrevCA(aPrevA.interpolatePoint(0.5) - aPrevA.getStartPoint());
+ const B2DVector aNextCA(aNextA.interpolatePoint(0.5) - aNextA.getStartPoint());
+ const B2DVector aPrevCB(aPrevB.interpolatePoint(0.5) - aPrevB.getStartPoint());
+ const bool bEnter(impLeftOfEdges(aPrevCA, aNextCA, aPrevCB));
+
+ const B2DCubicBezier aNextA2(createSegment(*pPNa2, false));
+ const B2DCubicBezier aPrevA2(createSegment(*pPNa2, true));
+ const B2DCubicBezier aNextB2(createSegment(*pPNb2, false));
+ const B2DVector aPrevCA2(aPrevA2.interpolatePoint(0.5) - aPrevA2.getStartPoint());
+ const B2DVector aNextCA2(aNextA2.interpolatePoint(0.5) - aNextA2.getStartPoint());
+ const B2DVector aNextCB2(aNextB2.interpolatePoint(0.5) - aNextB2.getStartPoint());
+ const bool bLeave(impLeftOfEdges(aPrevCA2, aNextCA2, aNextCB2));
+
+ if(bEnter != bLeave)
+ {
+ // crossover
+ impSwitchNext(rPNa, rPNb);
+ }
+ }
+ }
+ else if(aNextA.equal(aPrevB))
+ {
+ // common edge in opposite direction enter
+ impSwitchNext(rPNa, rPNb);
+ }
+ else
+ {
+ // no common edges, check for crossover
+ const B2DVector aPrevCA(aPrevA.interpolatePoint(0.5) - aPrevA.getStartPoint());
+ const B2DVector aNextCA(aNextA.interpolatePoint(0.5) - aNextA.getStartPoint());
+ const B2DVector aPrevCB(aPrevB.interpolatePoint(0.5) - aPrevB.getStartPoint());
+ const B2DVector aNextCB(aNextB.interpolatePoint(0.5) - aNextB.getStartPoint());
+
+ const bool bEnter(impLeftOfEdges(aPrevCA, aNextCA, aPrevCB));
+ const bool bLeave(impLeftOfEdges(aPrevCA, aNextCA, aNextCB));
+
+ if(bEnter != bLeave)
+ {
+ // crossover
+ impSwitchNext(rPNa, rPNb);
+ }
+ }
+ }
+ else
+ {
+ const B2DPoint& rNextA(maPNV[rPNa.mnIN].maPoint);
+ const B2DPoint& rPrevA(maPNV[rPNa.mnIP].maPoint);
+
+ if(rNextA.equal(rPrevA))
+ {
+ // deadend on A
+ return;
+ }
+
+ const B2DPoint& rNextB(maPNV[rPNb.mnIN].maPoint);
+ const B2DPoint& rPrevB(maPNV[rPNb.mnIP].maPoint);
+
+ if(rNextB.equal(rPrevB))
+ {
+ // deadend on B
+ return;
+ }
+
+ if(rPrevA.equal(rPrevB))
+ {
+ // common edge in same direction
+ if(rNextA.equal(rNextB))
+ {
+ // common edge in same direction continues
+ return;
+ }
+ else
+ {
+ // common edge in same direction leave
+ // action is done on enter
+ return;
+ }
+ }
+ else if(rPrevA.equal(rNextB))
+ {
+ // common edge in opposite direction
+ if(rNextA.equal(rPrevB))
+ {
+ // common edge in opposite direction continues
+ return;
+ }
+ else
+ {
+ // common edge in opposite direction leave
+ impSwitchNext(rPNa, rPNb);
+ }
+ }
+ else if(rNextA.equal(rNextB))
+ {
+ // common edge in same direction enter
+ // search leave edge
+ PN* pPNa2 = &maPNV[rPNa.mnIN];
+ PN* pPNb2 = &maPNV[rPNb.mnIN];
+ bool bOnEdge(true);
+
+ do
+ {
+ const B2DPoint& rNextA2(maPNV[pPNa2->mnIN].maPoint);
+ const B2DPoint& rNextB2(maPNV[pPNb2->mnIN].maPoint);
+
+ if(rNextA2.equal(rNextB2))
+ {
+ pPNa2 = &maPNV[pPNa2->mnIN];
+ pPNb2 = &maPNV[pPNb2->mnIN];
+ }
+ else
+ {
+ bOnEdge = false;
+ }
+ }
+ while(bOnEdge && pPNa2 != &rPNa && pPNa2 != &rPNa);
+
+ if(bOnEdge)
+ {
+ // loop over two identical polygon paths
+ return;
+ }
+ else
+ {
+ // enter at rPNa, rPNb; leave at pPNa2, pPNb2. No common edges
+ // at enter/leave. Check for crossover.
+ const B2DPoint& aPointE(rPNa.maPoint);
+ const B2DVector aPrevAE(rPrevA - aPointE);
+ const B2DVector aNextAE(rNextA - aPointE);
+ const B2DVector aPrevBE(rPrevB - aPointE);
+
+ const B2DPoint& aPointL(pPNa2->maPoint);
+ const B2DVector aPrevAL(maPNV[pPNa2->mnIP].maPoint - aPointL);
+ const B2DVector aNextAL(maPNV[pPNa2->mnIN].maPoint - aPointL);
+ const B2DVector aNextBL(maPNV[pPNb2->mnIN].maPoint - aPointL);
+
+ const bool bEnter(impLeftOfEdges(aPrevAE, aNextAE, aPrevBE));
+ const bool bLeave(impLeftOfEdges(aPrevAL, aNextAL, aNextBL));
+
+ if(bEnter != bLeave)
+ {
+ // crossover; switch start or end
+ impSwitchNext(rPNa, rPNb);
+ }
+ }
+ }
+ else if(rNextA.equal(rPrevB))
+ {
+ // common edge in opposite direction enter
+ impSwitchNext(rPNa, rPNb);
+ }
+ else
+ {
+ // no common edges, check for crossover
+ const B2DPoint& aPoint(rPNa.maPoint);
+ const B2DVector aPrevA(rPrevA - aPoint);
+ const B2DVector aNextA(rNextA - aPoint);
+ const B2DVector aPrevB(rPrevB - aPoint);
+ const B2DVector aNextB(rNextB - aPoint);
+
+ const bool bEnter(impLeftOfEdges(aPrevA, aNextA, aPrevB));
+ const bool bLeave(impLeftOfEdges(aPrevA, aNextA, aNextB));
+
+ if(bEnter != bLeave)
+ {
+ // crossover
+ impSwitchNext(rPNa, rPNb);
+ }
+ }
+ }
+ }
+
+ void impSolve()
+ {
+ // sort by point to identify common nodes
+ ::std::sort(maSNV.begin(), maSNV.end());
+
+ // handle common nodes
+ const sal_uInt32 nNodeCount(maSNV.size());
+
+ for(sal_uInt32 a(0); a < nNodeCount - 1; a++)
+ {
+ // test a before using it, not after. Also use nPointCount instead of aSortNodes.size()
+ PN& rPNb = *(maSNV[a].mpPN);
+
+ for(sal_uInt32 b(a + 1); b < nNodeCount && rPNb.maPoint.equal(maSNV[b].mpPN->maPoint); b++)
+ {
+ impHandleCommon(rPNb, *maSNV[b].mpPN);
+ }
+ }
+ }
+
+ public:
+ solver(const B2DPolygon& rOriginal)
+ : maOriginal(B2DPolyPolygon(rOriginal)),
+ mbIsCurve(false),
+ mbChanged(false)
+ {
+ const sal_uInt32 nOriginalCount(rOriginal.count());
+
+ if(nOriginalCount)
+ {
+ B2DPolygon aGeometry(tools::addPointsAtCutsAndTouches(rOriginal));
+ aGeometry.removeDoublePoints();
+ aGeometry = tools::simplifyCurveSegments(aGeometry);
+ mbIsCurve = aGeometry.areControlPointsUsed();
+
+ const sal_uInt32 nPointCount(aGeometry.count());
+
+ // If it's not a pezier polygon, at least four points are needed to create
+ // a self-intersection. If it's a bezier polygon, the minimum point number
+ // is two, since with a single point You get a curve, but no self-intersection
+ if(nPointCount > 3 || (nPointCount > 1 && mbIsCurve))
+ {
+ // reserve space in point, control and sort vector.
+ maSNV.reserve(nPointCount);
+ maPNV.reserve(nPointCount);
+ maVNV.reserve(mbIsCurve ? nPointCount : 0);
+
+ // fill data
+ impAddPolygon(0, aGeometry);
+
+ // solve common nodes
+ impSolve();
+ }
+ }
+ }
+
+ solver(const B2DPolyPolygon& rOriginal)
+ : maOriginal(rOriginal),
+ mbIsCurve(false),
+ mbChanged(false)
+ {
+ sal_uInt32 nOriginalCount(maOriginal.count());
+
+ if(nOriginalCount)
+ {
+ B2DPolyPolygon aGeometry(tools::addPointsAtCutsAndTouches(maOriginal, true));
+ aGeometry.removeDoublePoints();
+ aGeometry = tools::simplifyCurveSegments(aGeometry);
+ mbIsCurve = aGeometry.areControlPointsUsed();
+ nOriginalCount = aGeometry.count();
+
+ if(nOriginalCount)
+ {
+ sal_uInt32 nPointCount(0);
+ sal_uInt32 a(0);
+
+ // count points
+ for(a = 0; a < nOriginalCount; a++)
+ {
+ const B2DPolygon aCandidate(aGeometry.getB2DPolygon(a));
+ const sal_uInt32 nCandCount(aCandidate.count());
+
+ // If it's not a bezier curve, at least three points would be needed to have a
+ // topological relevant (not empty) polygon. Since its not known here if trivial
+ // edges (dead ends) will be kept or sorted out, add non-bezier polygons with
+ // more than one point.
+ // For bezier curves, the minimum for defining an area is also one.
+ if(nCandCount)
+ {
+ nPointCount += nCandCount;
+ }
+ }
+
+ if(nPointCount)
+ {
+ // reserve space in point, control and sort vector.
+ maSNV.reserve(nPointCount);
+ maPNV.reserve(nPointCount);
+ maVNV.reserve(mbIsCurve ? nPointCount : 0);
+
+ // fill data
+ sal_uInt32 nInsertIndex(0);
+
+ for(a = 0; a < nOriginalCount; a++)
+ {
+ const B2DPolygon aCandidate(aGeometry.getB2DPolygon(a));
+ const sal_uInt32 nCandCount(aCandidate.count());
+
+ // use same condition as above, the data vector is
+ // pre-allocated
+ if(nCandCount)
+ {
+ impAddPolygon(nInsertIndex, aCandidate);
+ nInsertIndex += nCandCount;
+ }
+ }
+
+ // solve common nodes
+ impSolve();
+ }
+ }
+ }
+ }
+
+ B2DPolyPolygon getB2DPolyPolygon()
+ {
+ if(mbChanged)
+ {
+ B2DPolyPolygon aRetval;
+ const sal_uInt32 nCount(maPNV.size());
+ sal_uInt32 nCountdown(nCount);
+
+ for(sal_uInt32 a(0); nCountdown && a < nCount; a++)
+ {
+ PN& rPN = maPNV[a];
+
+ if(SAL_MAX_UINT32 != rPN.mnI)
+ {
+ // unused node, start new part polygon
+ B2DPolygon aNewPart;
+ PN* pPNCurr = &rPN;
+
+ do
+ {
+ const B2DPoint& rPoint = pPNCurr->maPoint;
+ aNewPart.append(rPoint);
+
+ if(mbIsCurve)
+ {
+ const VN& rVNCurr = maVNV[pPNCurr->mnI];
+
+ if(!rVNCurr.maPrev.equalZero())
+ {
+ aNewPart.setPrevControlPoint(aNewPart.count() - 1, rPoint + rVNCurr.maPrev);
+ }
+
+ if(!rVNCurr.maNext.equalZero())
+ {
+ aNewPart.setNextControlPoint(aNewPart.count() - 1, rPoint + rVNCurr.maNext);
+ }
+ }
+
+ pPNCurr->mnI = SAL_MAX_UINT32;
+ nCountdown--;
+ pPNCurr = &(maPNV[pPNCurr->mnIN]);
+ }
+ while(pPNCurr != &rPN && SAL_MAX_UINT32 != pPNCurr->mnI);
+
+ // close and add
+ aNewPart.setClosed(true);
+ aRetval.append(aNewPart);
+ }
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ // no change, return original
+ return maOriginal;
+ }
+ }
+ };
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ } // end of anonymous namespace
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ //////////////////////////////////////////////////////////////////////////////
+
+ B2DPolyPolygon solveCrossovers(const B2DPolyPolygon& rCandidate)
+ {
+ if(rCandidate.count() > 1L)
+ {
+ solver aSolver(rCandidate);
+ return aSolver.getB2DPolyPolygon();
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ B2DPolyPolygon solveCrossovers(const B2DPolygon& rCandidate)
+ {
+ solver aSolver(rCandidate);
+ return aSolver.getB2DPolyPolygon();
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ B2DPolyPolygon stripNeutralPolygons(const B2DPolyPolygon& rCandidate)
+ {
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ if(ORIENTATION_NEUTRAL != tools::getOrientation(aCandidate))
+ {
+ aRetval.append(aCandidate);
+ }
+ }
+
+ return aRetval;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ B2DPolyPolygon stripDispensablePolygons(const B2DPolyPolygon& rCandidate, bool bKeepAboveZero)
+ {
+ const sal_uInt32 nCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ if(nCount)
+ {
+ if(nCount == 1L)
+ {
+ if(!bKeepAboveZero && ORIENTATION_POSITIVE == tools::getOrientation(rCandidate.getB2DPolygon(0L)))
+ {
+ aRetval = rCandidate;
+ }
+ }
+ else
+ {
+ sal_uInt32 a, b;
+ ::std::vector< StripHelper > aHelpers;
+ aHelpers.resize(nCount);
+
+ for(a = 0L; a < nCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+ StripHelper* pNewHelper = &(aHelpers[a]);
+ pNewHelper->maRange = tools::getRange(aCandidate);
+ pNewHelper->meOrinetation = tools::getOrientation(aCandidate);
+ pNewHelper->mnDepth = (ORIENTATION_NEGATIVE == pNewHelper->meOrinetation ? -1L : 0L);
+ }
+
+ for(a = 0L; a < nCount - 1L; a++)
+ {
+ const B2DPolygon aCandA(rCandidate.getB2DPolygon(a));
+ StripHelper& rHelperA = aHelpers[a];
+
+ for(b = a + 1L; b < nCount; b++)
+ {
+ const B2DPolygon aCandB(rCandidate.getB2DPolygon(b));
+ StripHelper& rHelperB = aHelpers[b];
+ const bool bAInB(rHelperB.maRange.isInside(rHelperA.maRange) && tools::isInside(aCandB, aCandA, true));
+ const bool bBInA(rHelperA.maRange.isInside(rHelperB.maRange) && tools::isInside(aCandA, aCandB, true));
+
+ if(bAInB && bBInA)
+ {
+ // congruent
+ if(rHelperA.meOrinetation == rHelperB.meOrinetation)
+ {
+ // two polys or two holes. Lower one of them to get one of them out of the way.
+ // Since each will be contained in the other one, both will be increased, too.
+ // So, for lowering, increase only one of them
+ rHelperA.mnDepth++;
+ }
+ else
+ {
+ // poly and hole. They neutralize, so get rid of both. Move securely below zero.
+ rHelperA.mnDepth = -((sal_Int32)nCount);
+ rHelperB.mnDepth = -((sal_Int32)nCount);
+ }
+ }
+ else
+ {
+ if(bAInB)
+ {
+ if(ORIENTATION_NEGATIVE == rHelperB.meOrinetation)
+ {
+ rHelperA.mnDepth--;
+ }
+ else
+ {
+ rHelperA.mnDepth++;
+ }
+ }
+ else if(bBInA)
+ {
+ if(ORIENTATION_NEGATIVE == rHelperA.meOrinetation)
+ {
+ rHelperB.mnDepth--;
+ }
+ else
+ {
+ rHelperB.mnDepth++;
+ }
+ }
+ }
+ }
+ }
+
+ for(a = 0L; a < nCount; a++)
+ {
+ const StripHelper& rHelper = aHelpers[a];
+ bool bAcceptEntry(bKeepAboveZero ? 1L <= rHelper.mnDepth : 0L == rHelper.mnDepth);
+
+ if(bAcceptEntry)
+ {
+ aRetval.append(rCandidate.getB2DPolygon(a));
+ }
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ B2DPolyPolygon prepareForPolygonOperation(const B2DPolygon& rCandidate)
+ {
+ solver aSolver(rCandidate);
+ B2DPolyPolygon aRetval(stripNeutralPolygons(aSolver.getB2DPolyPolygon()));
+
+ return correctOrientations(aRetval);
+ }
+
+ B2DPolyPolygon prepareForPolygonOperation(const B2DPolyPolygon& rCandidate)
+ {
+ solver aSolver(rCandidate);
+ B2DPolyPolygon aRetval(stripNeutralPolygons(aSolver.getB2DPolyPolygon()));
+
+ return correctOrientations(aRetval);
+ }
+
+ B2DPolyPolygon solvePolygonOperationOr(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
+ {
+ if(!rCandidateA.count())
+ {
+ return rCandidateB;
+ }
+ else if(!rCandidateB.count())
+ {
+ return rCandidateA;
+ }
+ else
+ {
+ // concatenate polygons, solve crossovers and throw away all sub-polygons
+ // which have a depth other than 0.
+ B2DPolyPolygon aRetval(rCandidateA);
+
+ aRetval.append(rCandidateB);
+ aRetval = solveCrossovers(aRetval);
+ aRetval = stripNeutralPolygons(aRetval);
+
+ return stripDispensablePolygons(aRetval, false);
+ }
+ }
+
+ B2DPolyPolygon solvePolygonOperationXor(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
+ {
+ if(!rCandidateA.count())
+ {
+ return rCandidateB;
+ }
+ else if(!rCandidateB.count())
+ {
+ return rCandidateA;
+ }
+ else
+ {
+ // XOR is pretty simple: By definition it is the simple concatenation of
+ // the single polygons since we imply XOR fill rule. Make it intersection-free
+ // and correct orientations
+ B2DPolyPolygon aRetval(rCandidateA);
+
+ aRetval.append(rCandidateB);
+ aRetval = solveCrossovers(aRetval);
+ aRetval = stripNeutralPolygons(aRetval);
+
+ return correctOrientations(aRetval);
+ }
+ }
+
+ B2DPolyPolygon solvePolygonOperationAnd(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
+ {
+ if(!rCandidateA.count())
+ {
+ return B2DPolyPolygon();
+ }
+ else if(!rCandidateB.count())
+ {
+ return B2DPolyPolygon();
+ }
+ else
+ {
+ // concatenate polygons, solve crossovers and throw away all sub-polygons
+ // with a depth of < 1. This means to keep all polygons where at least two
+ // polygons do overlap.
+ B2DPolyPolygon aRetval(rCandidateA);
+
+ aRetval.append(rCandidateB);
+ aRetval = solveCrossovers(aRetval);
+ aRetval = stripNeutralPolygons(aRetval);
+
+ return stripDispensablePolygons(aRetval, true);
+ }
+ }
+
+ B2DPolyPolygon solvePolygonOperationDiff(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
+ {
+ if(!rCandidateA.count())
+ {
+ return B2DPolyPolygon();
+ }
+ else if(!rCandidateB.count())
+ {
+ return rCandidateA;
+ }
+ else
+ {
+ // Make B topologically to holes and append to A
+ B2DPolyPolygon aRetval(rCandidateB);
+
+ aRetval.flip();
+ aRetval.append(rCandidateA);
+
+ // solve crossovers and throw away all sub-polygons which have a
+ // depth other than 0.
+ aRetval = basegfx::tools::solveCrossovers(aRetval);
+ aRetval = basegfx::tools::stripNeutralPolygons(aRetval);
+
+ return basegfx::tools::stripDispensablePolygons(aRetval, false);
+ }
+ }
+
+ B2DPolyPolygon mergeToSinglePolyPolygon(const std::vector< basegfx::B2DPolyPolygon >& rInput)
+ {
+ std::vector< basegfx::B2DPolyPolygon > aInput(rInput);
+
+ // first step: prepareForPolygonOperation and simple merge of non-overlapping
+ // PolyPolygons for speedup; this is possible for the wanted OR-operation
+ if(aInput.size())
+ {
+ std::vector< basegfx::B2DPolyPolygon > aResult;
+ aResult.reserve(aInput.size());
+
+ for(sal_uInt32 a(0); a < aInput.size(); a++)
+ {
+ const basegfx::B2DPolyPolygon aCandidate(prepareForPolygonOperation(aInput[a]));
+
+ if(aResult.size())
+ {
+ const B2DRange aCandidateRange(aCandidate.getB2DRange());
+ bool bCouldMergeSimple(false);
+
+ for(sal_uInt32 b(0); !bCouldMergeSimple && b < aResult.size(); b++)
+ {
+ basegfx::B2DPolyPolygon aTarget(aResult[b]);
+ const B2DRange aTargetRange(aTarget.getB2DRange());
+
+ if(!aCandidateRange.overlaps(aTargetRange))
+ {
+ aTarget.append(aCandidate);
+ aResult[b] = aTarget;
+ bCouldMergeSimple = true;
+ }
+ }
+
+ if(!bCouldMergeSimple)
+ {
+ aResult.push_back(aCandidate);
+ }
+ }
+ else
+ {
+ aResult.push_back(aCandidate);
+ }
+ }
+
+ aInput = aResult;
+ }
+
+ // second step: melt pairwise to a single PolyPolygon
+ while(aInput.size() > 1)
+ {
+ std::vector< basegfx::B2DPolyPolygon > aResult;
+ aResult.reserve((aInput.size() / 2) + 1);
+
+ for(sal_uInt32 a(0); a < aInput.size(); a += 2)
+ {
+ if(a + 1 < aInput.size())
+ {
+ // a pair for processing
+ aResult.push_back(solvePolygonOperationOr(aInput[a], aInput[a + 1]));
+ }
+ else
+ {
+ // last single PolyPolygon; copy to target to not lose it
+ aResult.push_back(aInput[a]);
+ }
+ }
+
+ aInput = aResult;
+ }
+
+ // third step: get result
+ if(1 == aInput.size())
+ {
+ return aInput[0];
+ }
+
+ return B2DPolyPolygon();
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/polygon/b2dpolypolygonrasterconverter.cxx b/basegfx/source/polygon/b2dpolypolygonrasterconverter.cxx
new file mode 100644
index 000000000000..f72b9a6fb732
--- /dev/null
+++ b/basegfx/source/polygon/b2dpolypolygonrasterconverter.cxx
@@ -0,0 +1,705 @@
+/*************************************************************************
+ *
+ * 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: b2dpolypolygonrasterconverter.cxx,v $
+ * $Revision: 1.9 $
+ *
+ * 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/polygon/b2dpolypolygonrasterconverter.hxx>
+
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+
+#include <boost/mem_fn.hpp>
+
+#include <algorithm>
+
+namespace basegfx
+{
+ class radixSort {
+
+ //! public interface
+ public:
+
+ //! default constructor
+ radixSort( void );
+
+ //! destructor
+ ~radixSort( void );
+
+ bool sort( const float *pInput, sal_uInt32 nNumElements, sal_uInt32 dwStride );
+
+ inline sal_uInt32 *indices( void ) const { return m_indices1; }
+
+ //! private attributes
+ private:
+
+ // current size of index list
+ sal_uInt32 m_current_size;
+
+ // last known size of index list
+ sal_uInt32 m_previous_size;
+
+ // index lists
+ sal_uInt32 *m_indices1;
+ sal_uInt32 *m_indices2;
+
+ sal_uInt32 m_counter[256*4];
+ sal_uInt32 m_offset[256];
+
+ //! private methods
+ private:
+
+ bool resize( sal_uInt32 nNumElements );
+ inline void reset_indices( void );
+ bool prepareCounters( const float *pInput, sal_uInt32 nNumElements, sal_uInt32 dwStride );
+ };
+
+ inline radixSort::radixSort( void ) {
+
+ m_indices1 = NULL;
+ m_indices2 = NULL;
+ m_current_size = 0;
+ m_previous_size = 0;
+
+ reset_indices();
+ }
+
+ inline radixSort::~radixSort( void ) {
+
+ delete [] m_indices2;
+ delete [] m_indices1;
+ }
+
+ bool radixSort::resize( sal_uInt32 nNumElements ) {
+
+ if(nNumElements==m_previous_size)
+ return true;
+
+ if(nNumElements > m_current_size) {
+
+ // release index lists
+ if(m_indices2)
+ delete [] m_indices2;
+ if(m_indices1)
+ delete [] m_indices1;
+
+ // allocate new index lists
+ m_indices1 = new sal_uInt32[nNumElements];
+ m_indices2 = new sal_uInt32[nNumElements];
+
+ // check for out of memory situation
+ if(!m_indices1 || !m_indices2) {
+ delete [] m_indices1;
+ delete [] m_indices2;
+ m_indices1 = NULL;
+ m_indices2 = NULL;
+ m_current_size = 0;
+ return false;
+ }
+
+ m_current_size = nNumElements;
+ }
+
+ m_previous_size = nNumElements;
+
+ // initialize indices
+ reset_indices();
+
+ return true;
+ }
+
+ inline void radixSort::reset_indices( void ) {
+
+ for(sal_uInt32 i=0;i<m_current_size;i++)
+ m_indices1[i] = i;
+ }
+
+ bool radixSort::prepareCounters( const float *pInput, sal_uInt32 nNumElements, sal_uInt32 dwStride ) {
+
+ // clear counters
+ sal_uInt32 *ptr = m_counter;
+ for(int i=0; i<64; ++i)
+ {
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ }
+
+ // prepare pointers to relevant memory addresses
+ sal_uInt8 *p = (sal_uInt8*)pInput;
+ sal_uInt8 *pe = p+(nNumElements*dwStride);
+ sal_uInt32 *h0= &m_counter[0];
+ sal_uInt32 *h1= &m_counter[256];
+ sal_uInt32 *h2= &m_counter[512];
+ sal_uInt32 *h3= &m_counter[768];
+
+ sal_uInt32 *Indices = m_indices1;
+ float previous_value = *(float *)(((sal_uInt8 *)pInput)+(m_indices1[0]*dwStride));
+ bool bSorted = true;
+ while(p!=pe) {
+ float value = *(float *)(((sal_uInt8 *)pInput)+((*Indices++)*dwStride));
+ if(value<previous_value) {
+ bSorted = false;
+ break;
+ }
+ previous_value = value;
+ h0[*p++]++;
+ h1[*p++]++;
+ h2[*p++]++;
+ h3[*p++]++;
+ p += dwStride-4;
+ }
+ if(bSorted)
+ return true;
+ while(p!=pe) {
+ h0[*p++]++;
+ h1[*p++]++;
+ h2[*p++]++;
+ h3[*p++]++;
+ p += dwStride-4;
+ }
+ return false;
+ }
+
+ bool radixSort::sort( const float *pInput, sal_uInt32 nNumElements, sal_uInt32 dwStride ) {
+
+ if(!(pInput))
+ return false;
+ if(!(nNumElements))
+ return false;
+ if(!(resize(nNumElements)))
+ return false;
+
+ // prepare radix counters, return if already sorted
+ if(prepareCounters(pInput,nNumElements,dwStride))
+ return true;
+
+ // count number of negative values
+ sal_uInt32 num_negatives = 0;
+ sal_uInt32 *h3= &m_counter[768];
+ for(sal_uInt32 i=128;i<256;i++)
+ num_negatives += h3[i];
+
+ // perform passes, one for each byte
+ for(sal_uInt32 j=0;j<4;j++) {
+
+ // ignore this pass if all values have the same byte
+ bool bRun = true;
+ sal_uInt32 *current_counter = &m_counter[j<<8];
+ sal_uInt8 unique_value = *(((sal_uInt8*)pInput)+j);
+ if(current_counter[unique_value]==nNumElements)
+ bRun=false;
+
+ // does the incoming byte contain the sign bit?
+ sal_uInt32 i;
+ if(j!=3) {
+ if(bRun) {
+ m_offset[0] = 0;
+ for(i=1;i<256;i++)
+ m_offset[i] = m_offset[i-1] + current_counter[i-1];
+ sal_uInt8 *InputBytes = (sal_uInt8 *)pInput;
+ sal_uInt32 *Indices = m_indices1;
+ sal_uInt32 *IndicesEnd = &m_indices1[nNumElements];
+ InputBytes += j;
+ while(Indices!=IndicesEnd) {
+ sal_uInt32 id = *Indices++;
+ m_indices2[m_offset[InputBytes[id*dwStride]]++] = id;
+ }
+ sal_uInt32 *Tmp = m_indices1;
+ m_indices1 = m_indices2;
+ m_indices2 = Tmp;
+ }
+ }
+ else {
+ if(bRun) {
+ m_offset[0] = num_negatives;
+ for(i=1;i<128;i++)
+ m_offset[i] = m_offset[i-1] + current_counter[i-1];
+ m_offset[255] = 0;
+ for(i=0;i<127;i++)
+ m_offset[254-i] = m_offset[255-i] + current_counter[255-i];
+ for(i=128;i<256;i++)
+ m_offset[i] += current_counter[i];
+ for(i=0;i<nNumElements;i++) {
+ sal_uInt32 Radix = (*(sal_uInt32 *)(((sal_uInt8 *)pInput)+(m_indices1[i]*dwStride)))>>24;
+ if(Radix<128) m_indices2[m_offset[Radix]++] = m_indices1[i];
+ else m_indices2[--m_offset[Radix]] = m_indices1[i];
+ }
+ sal_uInt32 *Tmp = m_indices1;
+ m_indices1 = m_indices2;
+ m_indices2 = Tmp;
+ }
+ else {
+ if(unique_value>=128) {
+ for(i=0;i<nNumElements;i++)
+ m_indices2[i] = m_indices1[nNumElements-i-1];
+ sal_uInt32 *Tmp = m_indices1;
+ m_indices1 = m_indices2;
+ m_indices2 = Tmp;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ //************************************************************
+ // Internal vertex storage of B2DPolyPolygonRasterConverter
+ //************************************************************
+
+ inline B2DPolyPolygonRasterConverter::Vertex::Vertex() :
+ aP1(),
+ aP2(),
+ bDownwards( true )
+ {
+ }
+
+ inline B2DPolyPolygonRasterConverter::Vertex::Vertex( const B2DPoint& rP1, const B2DPoint& rP2, bool bDown ) :
+ aP1( rP1 ),
+ aP2( rP2 ),
+ bDownwards( bDown )
+ {
+ }
+
+
+ //************************************************************
+ // Helper class for holding horizontal line segments during raster
+ // conversion
+ //************************************************************
+
+ namespace
+ {
+ class ImplLineNode
+ {
+ public:
+ sal_Int32 mnYCounter;
+ float mfXPos;
+ float mfXDelta;
+ bool mbDownwards;
+
+ public:
+ /**rP1 and rP2 must not have equal y values, when rounded
+ to integer!
+ */
+ ImplLineNode(const B2DPoint& rP1, const B2DPoint& rP2, bool bDown) :
+ mnYCounter( fround(rP2.getY()) - fround(rP1.getY()) ),
+ mfXPos( (float)(rP1.getX()) ),
+ mfXDelta((float) ((rP2.getX() - rP1.getX()) / mnYCounter) ),
+ mbDownwards( bDown )
+ {
+ }
+
+ /// get current x position
+ const float& getXPos() const
+ {
+ return mfXPos;
+ }
+
+ /// returns true, if line ends on this Y value
+ float nextLine()
+ {
+ if(mnYCounter>=0)
+ {
+ // go one step in Y
+ mfXPos += mfXDelta;
+ --mnYCounter;
+ return mfXDelta;
+ }
+
+ return 0.0f;
+ }
+
+ bool isEnded()
+ {
+ return mnYCounter<=0;
+ }
+
+ bool isDownwards()
+ {
+ return mbDownwards;
+ }
+ };
+ }
+
+ typedef ::std::vector<ImplLineNode> VectorOfLineNodes;
+
+
+ //************************************************************
+ // Base2D PolyPolygon Raster Converter (Rasterizer)
+ //************************************************************
+
+ namespace
+ {
+ struct VertexComparator
+ {
+ bool operator()( const B2DPolyPolygonRasterConverter::Vertex& rLHS,
+ const B2DPolyPolygonRasterConverter::Vertex& rRHS )
+ {
+ return rLHS.aP1.getX() < rRHS.aP1.getX();
+ }
+ };
+ }
+
+ void B2DPolyPolygonRasterConverter::init()
+ {
+ if(!maPolyPolyRectangle.isEmpty())
+ {
+ const sal_Int32 nMinY( fround(maPolyPolyRectangle.getMinY()) );
+ const sal_Int32 nScanlines(fround(maPolyPolyRectangle.getMaxY()) - nMinY);
+
+ maScanlines.resize( nScanlines+1 );
+
+ // add all polygons
+ for( sal_uInt32 i(0), nCount(maPolyPolygon.count());
+ i < nCount;
+ ++i )
+ {
+ // add all vertices
+ const B2DPolygon& rPoly( maPolyPolygon.getB2DPolygon(i) );
+ for( sal_uInt32 k(0), nVertices(rPoly.count());
+ k<nVertices;
+ ++k )
+ {
+ const B2DPoint& rP1( rPoly.getB2DPoint(k) );
+ const B2DPoint& rP2( rPoly.getB2DPoint( (k + 1) % nVertices ) );
+
+ const sal_Int32 nVertexYP1( fround(rP1.getY()) );
+ const sal_Int32 nVertexYP2( fround(rP2.getY()) );
+
+ // insert only vertices which are not strictly
+ // horizontal. Note that the ImplLineNode relies on
+ // this.
+ if(nVertexYP1 != nVertexYP2)
+ {
+ if( nVertexYP2 < nVertexYP1 )
+ {
+ const sal_Int32 nStartScanline(nVertexYP2 - nMinY);
+
+ // swap edges
+ maScanlines[ nStartScanline ].push_back( Vertex(rP2, rP1, false) );
+ }
+ else
+ {
+ const sal_Int32 nStartScanline(nVertexYP1 - nMinY);
+
+ maScanlines[ nStartScanline ].push_back( Vertex(rP1, rP2, true) );
+ }
+ }
+ }
+ }
+
+ // now sort all scanlines, with increasing x coordinates
+ VectorOfVertexVectors::iterator aIter( maScanlines.begin() );
+ VectorOfVertexVectors::iterator aEnd( maScanlines.end() );
+ while( aIter != aEnd )
+ {
+ ::std::sort( aIter->begin(),
+ aIter->end(),
+ VertexComparator() );
+ ++aIter;
+ }
+ }
+ }
+
+ B2DPolyPolygonRasterConverter::B2DPolyPolygonRasterConverter( const B2DPolyPolygon& rPolyPoly ) :
+ maPolyPolygon( rPolyPoly ),
+ maPolyPolyRectangle( tools::getRange( rPolyPoly ) ),
+ maScanlines()
+ {
+ init();
+ }
+
+ namespace
+ {
+ B2DRectangle getCombinedBounds( const B2DPolyPolygon& rPolyPolyRaster,
+ const B2DRectangle& rRasterArea )
+ {
+ B2DRectangle aRect( tools::getRange( rPolyPolyRaster ) );
+ aRect.expand( rRasterArea );
+
+ return aRect;
+ }
+ }
+
+ B2DPolyPolygonRasterConverter::B2DPolyPolygonRasterConverter( const B2DPolyPolygon& rPolyPolyRaster,
+ const B2DRectangle& rRasterArea ) :
+ maPolyPolygon( rPolyPolyRaster ),
+ maPolyPolyRectangle(
+ getCombinedBounds( rPolyPolyRaster,
+ rRasterArea ) ),
+ maScanlines()
+ {
+ init();
+ }
+
+ B2DPolyPolygonRasterConverter::~B2DPolyPolygonRasterConverter()
+ {
+ }
+
+ namespace
+ {
+ class LineNodeGenerator
+ {
+ public:
+ LineNodeGenerator( VectorOfLineNodes& rActiveVertices ) :
+ mrActiveVertices( rActiveVertices )
+ {
+ }
+
+ void operator()( const B2DPolyPolygonRasterConverter::Vertex& rVertex )
+ {
+ mrActiveVertices.push_back( ImplLineNode(rVertex.aP1,
+ rVertex.aP2,
+ rVertex.bDownwards) );
+ }
+
+ private:
+ VectorOfLineNodes& mrActiveVertices;
+ };
+
+ struct LineNodeComparator
+ {
+ bool operator()( const ImplLineNode& rLHS, const ImplLineNode& rRHS )
+ {
+ return rLHS.getXPos() < rRHS.getXPos();
+ }
+ };
+ }
+
+ void B2DPolyPolygonRasterConverter::rasterConvert( FillRule eFillRule )
+ {
+ if( maScanlines.empty() )
+ return; // no scanlines at all -> bail out
+
+ const sal_Int32 nMinY( fround(maPolyPolyRectangle.getMinY()) );
+ const sal_Int32 nScanlines(fround(maPolyPolyRectangle.getMaxY()) - nMinY);
+
+ // Vector of currently active vertices. A vertex is active, if
+ // it crosses or touches the current scanline.
+ VectorOfLineNodes aActiveVertices;
+
+ // mickey's optimized version...
+ radixSort rs;
+ std::size_t nb(0);
+ std::size_t nb_previous(0);
+ bool bSort(false);
+
+ // process each scanline
+ for( sal_Int32 y(0); y <= nScanlines; ++y )
+ {
+ // add vertices which start at current scanline into
+ // active vertex vector
+ ::std::for_each( maScanlines[y].begin(),
+ maScanlines[y].end(),
+ LineNodeGenerator( aActiveVertices ) );
+ nb = aActiveVertices.size();
+ if(nb != nb_previous)
+ {
+ nb_previous = nb;
+ bSort = true;
+ }
+
+ // sort with increasing X
+ if(bSort)
+ {
+ bSort = false;
+
+ if( nb )
+ {
+ rs.sort(&aActiveVertices[0].mfXPos,
+ nb,
+ sizeof(ImplLineNode));
+ }
+ }
+
+ const std::size_t nLen( nb );
+ if( !nLen )
+ {
+ // empty scanline - call derived with an 'off' span
+ // for the full width
+ span( maPolyPolyRectangle.getMinX(),
+ maPolyPolyRectangle.getMaxX(),
+ nMinY + y,
+ false );
+ }
+ else
+ {
+ const sal_Int32 nCurrY( nMinY + y );
+
+ // scanline not empty - forward all scans to derived,
+ // according to selected fill rule
+
+ // TODO(P1): Maybe allow these 'off' span calls to be
+ // switched off (or all 'on' span calls, depending on
+ // use case scenario)
+
+ // sorting didn't change the order of the elements
+ // in memory but prepared a list of indices in sorted order.
+ // thus we now process the nodes with an additional indirection.
+ sal_uInt32 *sorted = rs.indices();
+
+ // call derived with 'off' span for everything left of first active span
+ if( aActiveVertices[sorted[0]].getXPos() > maPolyPolyRectangle.getMinX() )
+ {
+ span( maPolyPolyRectangle.getMinX(),
+ aActiveVertices[sorted[0]].getXPos(),
+ nCurrY,
+ false );
+ }
+
+ switch( eFillRule )
+ {
+ default:
+ OSL_ENSURE(false,
+ "B2DPolyPolygonRasterConverter::rasterConvert(): Unexpected fill rule");
+ return;
+
+ case FillRule_EVEN_ODD:
+ // process each span in current scanline, with
+ // even-odd fill rule
+ for( ::std::size_t i(0), nLength(aActiveVertices.size());
+ i+1 < nLength;
+ ++i )
+ {
+ sal_uInt32 nIndex = sorted[i];
+ sal_uInt32 nNextIndex = sorted[i+1];
+ span( aActiveVertices[nIndex].getXPos(),
+ aActiveVertices[nNextIndex].getXPos(),
+ nCurrY,
+ i % 2 == 0 );
+
+ float delta = aActiveVertices[nIndex].nextLine();
+ if(delta > 0.0f)
+ {
+ if(aActiveVertices[nIndex].getXPos() > aActiveVertices[nNextIndex].getXPos())
+ bSort = true;
+ }
+ else if(delta < 0.0f)
+ {
+ if(i)
+ {
+ sal_uInt32 nPrevIndex = sorted[i-1];
+ if(aActiveVertices[nIndex].getXPos() < aActiveVertices[nPrevIndex].getXPos())
+ bSort = true;
+ }
+ }
+ }
+ break;
+
+ case FillRule_NONZERO_WINDING_NUMBER:
+ // process each span in current scanline, with
+ // non-zero winding numbe fill rule
+ sal_Int32 nWindingNumber(0);
+ for( ::std::size_t i(0), nLength(aActiveVertices.size());
+ i+1 < nLength;
+ ++i )
+ {
+ sal_uInt32 nIndex = sorted[i];
+ sal_uInt32 nNextIndex = sorted[i+1];
+ nWindingNumber += -1 + 2*aActiveVertices[nIndex].isDownwards();
+
+ span( aActiveVertices[nIndex].getXPos(),
+ aActiveVertices[nNextIndex].getXPos(),
+ nCurrY,
+ nWindingNumber != 0 );
+
+ float delta = aActiveVertices[nIndex].nextLine();
+ if(delta > 0.0f)
+ {
+ if(aActiveVertices[nIndex].getXPos() > aActiveVertices[nNextIndex].getXPos())
+ bSort = true;
+ }
+ else if(delta < 0.0f)
+ {
+ if(i)
+ {
+ sal_uInt32 nPrevIndex = sorted[i-1];
+ if(aActiveVertices[nIndex].getXPos() < aActiveVertices[nPrevIndex].getXPos())
+ bSort = true;
+ }
+ }
+ }
+ break;
+ }
+
+ // call derived with 'off' span for everything right of last active span
+ if( aActiveVertices[sorted[nb-1]].getXPos()+1.0 < maPolyPolyRectangle.getMaxX() )
+ {
+ span( aActiveVertices[sorted[nb-1]].getXPos()+1.0,
+ maPolyPolyRectangle.getMaxX(),
+ nCurrY,
+ false );
+ }
+
+ // also call nextLine on very last line node
+ sal_uInt32 nIndex = sorted[nb-1];
+ float delta = aActiveVertices[nIndex].nextLine();
+ if(delta < 0.0f)
+ {
+ if(nb)
+ {
+ sal_uInt32 nPrevIndex = sorted[nb-2];
+ if(aActiveVertices[nIndex].getXPos() < aActiveVertices[nPrevIndex].getXPos())
+ bSort = true;
+ }
+ }
+ }
+
+ // remove line nodes which have ended on the current scanline
+ aActiveVertices.erase( ::std::remove_if( aActiveVertices.begin(),
+ aActiveVertices.end(),
+ ::boost::mem_fn( &ImplLineNode::isEnded ) ),
+ aActiveVertices.end() );
+ nb = aActiveVertices.size();
+ if(nb != nb_previous)
+ {
+ nb_previous = nb;
+ bSort = true;
+ }
+ }
+ }
+}
+// eof
diff --git a/basegfx/source/polygon/b2dpolypolygontools.cxx b/basegfx/source/polygon/b2dpolypolygontools.cxx
new file mode 100644
index 000000000000..e6b3a448530d
--- /dev/null
+++ b/basegfx/source/polygon/b2dpolypolygontools.cxx
@@ -0,0 +1,588 @@
+/*************************************************************************
+ *
+ * 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: b2dpolypolygontools.cxx,v $
+ * $Revision: 1.19.4.1 $
+ *
+ * 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/polygon/b2dpolypolygontools.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+
+#include <numeric>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ B2DPolyPolygon correctOrientations(const B2DPolyPolygon& rCandidate)
+ {
+ B2DPolyPolygon aRetval(rCandidate);
+ const sal_uInt32 nCount(aRetval.count());
+
+ for(sal_uInt32 a(0L); a < nCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+ const B2VectorOrientation aOrientation(tools::getOrientation(aCandidate));
+ sal_uInt32 nDepth(0L);
+
+ for(sal_uInt32 b(0L); b < nCount; b++)
+ {
+ if(b != a)
+ {
+ const B2DPolygon aCompare(rCandidate.getB2DPolygon(b));
+
+ if(tools::isInside(aCompare, aCandidate, true))
+ {
+ nDepth++;
+ }
+ }
+ }
+
+ const bool bShallBeHole(1L == (nDepth & 0x00000001));
+ const bool bIsHole(ORIENTATION_NEGATIVE == aOrientation);
+
+ if(bShallBeHole != bIsHole && ORIENTATION_NEUTRAL != aOrientation)
+ {
+ B2DPolygon aFlipped(aCandidate);
+ aFlipped.flip();
+ aRetval.setB2DPolygon(a, aFlipped);
+ }
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon correctOutmostPolygon(const B2DPolyPolygon& rCandidate)
+ {
+ const sal_uInt32 nCount(rCandidate.count());
+
+ if(nCount > 1L)
+ {
+ for(sal_uInt32 a(0L); a < nCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+ sal_uInt32 nDepth(0L);
+
+ for(sal_uInt32 b(0L); b < nCount; b++)
+ {
+ if(b != a)
+ {
+ const B2DPolygon aCompare(rCandidate.getB2DPolygon(b));
+
+ if(tools::isInside(aCompare, aCandidate, true))
+ {
+ nDepth++;
+ }
+ }
+ }
+
+ if(!nDepth)
+ {
+ B2DPolyPolygon aRetval(rCandidate);
+
+ if(a != 0L)
+ {
+ // exchange polygon a and polygon 0L
+ aRetval.setB2DPolygon(0L, aCandidate);
+ aRetval.setB2DPolygon(a, rCandidate.getB2DPolygon(0L));
+ }
+
+ // exit
+ return aRetval;
+ }
+ }
+ }
+
+ return rCandidate;
+ }
+
+ B2DPolyPolygon adaptiveSubdivideByDistance(const B2DPolyPolygon& rCandidate, double fDistanceBound)
+ {
+ if(rCandidate.areControlPointsUsed())
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ aRetval.append(tools::adaptiveSubdivideByDistance(aCandidate, fDistanceBound));
+ }
+ else
+ {
+ aRetval.append(aCandidate);
+ }
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ B2DPolyPolygon adaptiveSubdivideByAngle(const B2DPolyPolygon& rCandidate, double fAngleBound)
+ {
+ if(rCandidate.areControlPointsUsed())
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ aRetval.append(tools::adaptiveSubdivideByAngle(aCandidate, fAngleBound));
+ }
+ else
+ {
+ aRetval.append(aCandidate);
+ }
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ B2DPolyPolygon adaptiveSubdivideByCount(const B2DPolyPolygon& rCandidate, sal_uInt32 nCount)
+ {
+ if(rCandidate.areControlPointsUsed())
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ aRetval.append(tools::adaptiveSubdivideByCount(aCandidate, nCount));
+ }
+ else
+ {
+ aRetval.append(aCandidate);
+ }
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ bool isInside(const B2DPolyPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder)
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+
+ if(1L == nPolygonCount)
+ {
+ return isInside(rCandidate.getB2DPolygon(0L), rPoint, bWithBorder);
+ }
+ else
+ {
+ sal_Int32 nInsideCount(0L);
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolygon aPolygon(rCandidate.getB2DPolygon(a));
+ const bool bInside(isInside(aPolygon, rPoint, bWithBorder));
+
+ if(bInside)
+ {
+ nInsideCount++;
+ }
+ }
+
+ return (nInsideCount % 2L);
+ }
+ }
+
+ B2DRange getRangeWithControlPoints(const B2DPolyPolygon& rCandidate)
+ {
+ B2DRange aRetval;
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ B2DPolygon aCandidate = rCandidate.getB2DPolygon(a);
+ aRetval.expand(tools::getRangeWithControlPoints(aCandidate));
+ }
+
+ return aRetval;
+ }
+
+ B2DRange getRange(const B2DPolyPolygon& rCandidate)
+ {
+ B2DRange aRetval;
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ B2DPolygon aCandidate = rCandidate.getB2DPolygon(a);
+ aRetval.expand(tools::getRange(aCandidate));
+ }
+
+ return aRetval;
+ }
+
+ void applyLineDashing(const B2DPolyPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fFullDashDotLen)
+ {
+ if(0.0 == fFullDashDotLen && rDotDashArray.size())
+ {
+ // calculate fFullDashDotLen from rDotDashArray
+ fFullDashDotLen = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
+ }
+
+ if(rCandidate.count() && fFullDashDotLen > 0.0)
+ {
+ B2DPolyPolygon aLineTarget, aGapTarget;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ applyLineDashing(
+ aCandidate,
+ rDotDashArray,
+ pLineTarget ? &aLineTarget : 0,
+ pGapTarget ? &aGapTarget : 0,
+ fFullDashDotLen);
+
+ if(pLineTarget)
+ {
+ pLineTarget->append(aLineTarget);
+ }
+
+ if(pGapTarget)
+ {
+ pGapTarget->append(aGapTarget);
+ }
+ }
+ }
+ }
+
+ bool isInEpsilonRange(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance)
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ if(isInEpsilonRange(aCandidate, rTestPosition, fDistance))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ B3DPolyPolygon createB3DPolyPolygonFromB2DPolyPolygon(const B2DPolyPolygon& rCandidate, double fZCoordinate)
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B3DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ aRetval.append(createB3DPolygonFromB2DPolygon(aCandidate, fZCoordinate));
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon createB2DPolyPolygonFromB3DPolyPolygon(const B3DPolyPolygon& rCandidate, const B3DHomMatrix& rMat)
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ B3DPolygon aCandidate(rCandidate.getB3DPolygon(a));
+
+ aRetval.append(createB2DPolygonFromB3DPolygon(aCandidate, rMat));
+ }
+
+ return aRetval;
+ }
+
+ double getSmallestDistancePointToPolyPolygon(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rPolygonIndex, sal_uInt32& rEdgeIndex, double& rCut)
+ {
+ double fRetval(DBL_MAX);
+ const double fZero(0.0);
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+ sal_uInt32 nNewEdgeIndex;
+ double fNewCut;
+ const double fNewDistance(getSmallestDistancePointToPolygon(aCandidate, rTestPoint, nNewEdgeIndex, fNewCut));
+
+ if(DBL_MAX == fRetval || fNewDistance < fRetval)
+ {
+ fRetval = fNewDistance;
+ rPolygonIndex = a;
+ rEdgeIndex = nNewEdgeIndex;
+ rCut = fNewCut;
+
+ if(fTools::equal(fRetval, fZero))
+ {
+ // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
+ fRetval = 0.0;
+ break;
+ }
+ }
+ }
+
+ return fRetval;
+ }
+
+ B2DPolyPolygon distort(const B2DPolyPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ aRetval.append(distort(aCandidate, rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon rotateAroundPoint(const B2DPolyPolygon& rCandidate, const B2DPoint& rCenter, double fAngle)
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ aRetval.append(rotateAroundPoint(aCandidate, rCenter, fAngle));
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon expandToCurve(const B2DPolyPolygon& rCandidate)
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ aRetval.append(expandToCurve(aCandidate));
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon setContinuity(const B2DPolyPolygon& rCandidate, B2VectorContinuity eContinuity)
+ {
+ if(rCandidate.areControlPointsUsed())
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
+
+ aRetval.append(setContinuity(aCandidate, eContinuity));
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ B2DPolyPolygon growInNormalDirection(const B2DPolyPolygon& rCandidate, double fValue)
+ {
+ if(0.0 != fValue)
+ {
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ aRetval.append(growInNormalDirection(rCandidate.getB2DPolygon(a), fValue));
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ void correctGrowShrinkPolygonPair(B2DPolyPolygon& /*rOriginal*/, B2DPolyPolygon& /*rGrown*/)
+ {
+ }
+
+ B2DPolyPolygon reSegmentPolyPolygon(const B2DPolyPolygon& rCandidate, sal_uInt32 nSegments)
+ {
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ aRetval.append(reSegmentPolygon(rCandidate.getB2DPolygon(a), nSegments));
+ }
+
+ return aRetval;
+ }
+
+ B2DPolyPolygon interpolate(const B2DPolyPolygon& rOld1, const B2DPolyPolygon& rOld2, double t)
+ {
+ OSL_ENSURE(rOld1.count() == rOld2.count(), "B2DPolyPolygon interpolate: Different geometry (!)");
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rOld1.count(); a++)
+ {
+ aRetval.append(interpolate(rOld1.getB2DPolygon(a), rOld2.getB2DPolygon(a), t));
+ }
+
+ return aRetval;
+ }
+
+ bool isRectangle( const B2DPolyPolygon& rPoly )
+ {
+ // exclude some cheap cases first
+ if( rPoly.count() != 1 )
+ return false;
+
+ return isRectangle( rPoly.getB2DPolygon(0) );
+ }
+
+ // #i76891#
+ B2DPolyPolygon simplifyCurveSegments(const B2DPolyPolygon& rCandidate)
+ {
+ if(rCandidate.areControlPointsUsed())
+ {
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ aRetval.append(simplifyCurveSegments(rCandidate.getB2DPolygon(a)));
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ B2DPolyPolygon reSegmentPolyPolygonEdges(const B2DPolyPolygon& rCandidate, sal_uInt32 nSubEdges, bool bHandleCurvedEdges, bool bHandleStraightEdges)
+ {
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ aRetval.append(reSegmentPolygonEdges(rCandidate.getB2DPolygon(a), nSubEdges, bHandleCurvedEdges, bHandleStraightEdges));
+ }
+
+ return aRetval;
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // comparators with tolerance for 2D PolyPolygons
+
+ bool equal(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB, const double& rfSmallValue)
+ {
+ const sal_uInt32 nPolygonCount(rCandidateA.count());
+
+ if(nPolygonCount != rCandidateB.count())
+ return false;
+
+ for(sal_uInt32 a(0); a < nPolygonCount; a++)
+ {
+ const B2DPolygon aCandidate(rCandidateA.getB2DPolygon(a));
+
+ if(!equal(aCandidate, rCandidateB.getB2DPolygon(a), rfSmallValue))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool equal(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB)
+ {
+ const double fSmallValue(fTools::getSmallValue());
+
+ return equal(rCandidateA, rCandidateB, fSmallValue);
+ }
+
+ B2DPolyPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolyPolygon& rCandidate)
+ {
+ B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ aRetval.append(snapPointsOfHorizontalOrVerticalEdges(rCandidate.getB2DPolygon(a)));
+ }
+
+ return aRetval;
+ }
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/polygon/b2dsvgpolypolygon.cxx b/basegfx/source/polygon/b2dsvgpolypolygon.cxx
new file mode 100644
index 000000000000..2247c237d90f
--- /dev/null
+++ b/basegfx/source/polygon/b2dsvgpolypolygon.cxx
@@ -0,0 +1,1108 @@
+/*************************************************************************
+ *
+ * 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: b2dsvgpolypolygon.cxx,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/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/math.hxx>
+
+namespace basegfx
+{
+ namespace tools
+ {
+ namespace
+ {
+ void lcl_skipSpaces(sal_Int32& io_rPos,
+ const ::rtl::OUString& rStr,
+ const sal_Int32 nLen)
+ {
+ while( io_rPos < nLen &&
+ sal_Unicode(' ') == rStr[io_rPos] )
+ {
+ ++io_rPos;
+ }
+ }
+
+ void lcl_skipSpacesAndCommas(sal_Int32& io_rPos,
+ const ::rtl::OUString& rStr,
+ const sal_Int32 nLen)
+ {
+ while(io_rPos < nLen
+ && (sal_Unicode(' ') == rStr[io_rPos] || sal_Unicode(',') == rStr[io_rPos]))
+ {
+ ++io_rPos;
+ }
+ }
+
+ inline bool lcl_isOnNumberChar(const ::rtl::OUString& rStr, const sal_Int32 nPos, bool bSignAllowed = true)
+ {
+ const sal_Unicode aChar(rStr[nPos]);
+
+ const bool bPredicate( (sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
+ || (bSignAllowed && sal_Unicode('+') == aChar)
+ || (bSignAllowed && sal_Unicode('-') == aChar) );
+
+ return bPredicate;
+ }
+
+ bool lcl_getDoubleChar(double& o_fRetval,
+ sal_Int32& io_rPos,
+ const ::rtl::OUString& rStr,
+ const sal_Int32 /*nLen*/)
+ {
+ sal_Unicode aChar( rStr[io_rPos] );
+ ::rtl::OUStringBuffer sNumberString;
+
+ if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
+ {
+ sNumberString.append(rStr[io_rPos]);
+ aChar = rStr[++io_rPos];
+ }
+
+ while((sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
+ || sal_Unicode('.') == aChar)
+ {
+ sNumberString.append(rStr[io_rPos]);
+ aChar = rStr[++io_rPos];
+ }
+
+ if(sal_Unicode('e') == aChar || sal_Unicode('E') == aChar)
+ {
+ sNumberString.append(rStr[io_rPos]);
+ aChar = rStr[++io_rPos];
+
+ if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
+ {
+ sNumberString.append(rStr[io_rPos]);
+ aChar = rStr[++io_rPos];
+ }
+
+ while(sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
+ {
+ sNumberString.append(rStr[io_rPos]);
+ aChar = rStr[++io_rPos];
+ }
+ }
+
+ if(sNumberString.getLength())
+ {
+ rtl_math_ConversionStatus eStatus;
+ o_fRetval = ::rtl::math::stringToDouble( sNumberString.makeStringAndClear(),
+ (sal_Unicode)('.'),
+ (sal_Unicode)(','),
+ &eStatus,
+ NULL );
+ return ( eStatus == rtl_math_ConversionStatus_Ok );
+ }
+
+ return false;
+ }
+
+ bool lcl_importDoubleAndSpaces( double& o_fRetval,
+ sal_Int32& io_rPos,
+ const ::rtl::OUString& rStr,
+ const sal_Int32 nLen )
+ {
+ if( !lcl_getDoubleChar(o_fRetval, io_rPos, rStr, nLen) )
+ return false;
+
+ lcl_skipSpacesAndCommas(io_rPos, rStr, nLen);
+
+ return true;
+ }
+
+ bool lcl_importNumberAndSpaces(sal_Int32& o_nRetval,
+ sal_Int32& io_rPos,
+ const ::rtl::OUString& rStr,
+ const sal_Int32 nLen)
+ {
+ sal_Unicode aChar( rStr[io_rPos] );
+ ::rtl::OUStringBuffer sNumberString;
+
+ if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
+ {
+ sNumberString.append(rStr[io_rPos]);
+ aChar = rStr[++io_rPos];
+ }
+
+ while(sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
+ {
+ sNumberString.append(rStr[io_rPos]);
+ aChar = rStr[++io_rPos];
+ }
+
+ if(sNumberString.getLength())
+ {
+ o_nRetval = sNumberString.makeStringAndClear().toInt32();
+ lcl_skipSpacesAndCommas(io_rPos, rStr, nLen);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ void lcl_skipNumber(sal_Int32& io_rPos,
+ const ::rtl::OUString& rStr,
+ const sal_Int32 nLen)
+ {
+ bool bSignAllowed(true);
+
+ while(io_rPos < nLen && lcl_isOnNumberChar(rStr, io_rPos, bSignAllowed))
+ {
+ bSignAllowed = false;
+ ++io_rPos;
+ }
+ }
+
+ void lcl_skipDouble(sal_Int32& io_rPos,
+ const ::rtl::OUString& rStr,
+ const sal_Int32 /*nLen*/)
+ {
+ sal_Unicode aChar( rStr[io_rPos] );
+
+ if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
+ aChar = rStr[++io_rPos];
+
+ while((sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
+ || sal_Unicode('.') == aChar)
+ {
+ aChar = rStr[++io_rPos];
+ }
+
+ if(sal_Unicode('e') == aChar || sal_Unicode('E') == aChar)
+ {
+ aChar = rStr[++io_rPos];
+
+ if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar)
+ aChar = rStr[++io_rPos];
+
+ while(sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar)
+ {
+ aChar = rStr[++io_rPos];
+ }
+ }
+ }
+ void lcl_skipNumberAndSpacesAndCommas(sal_Int32& io_rPos,
+ const ::rtl::OUString& rStr,
+ const sal_Int32 nLen)
+ {
+ lcl_skipNumber(io_rPos, rStr, nLen);
+ lcl_skipSpacesAndCommas(io_rPos, rStr, nLen);
+ }
+
+ // #100617# Allow to skip doubles, too.
+ void lcl_skipDoubleAndSpacesAndCommas(sal_Int32& io_rPos,
+ const ::rtl::OUString& rStr,
+ const sal_Int32 nLen)
+ {
+ lcl_skipDouble(io_rPos, rStr, nLen);
+ lcl_skipSpacesAndCommas(io_rPos, rStr, nLen);
+ }
+
+ void lcl_putNumberChar( ::rtl::OUString& rStr,
+ double fValue )
+ {
+ rStr += ::rtl::OUString::valueOf( fValue );
+ }
+
+ void lcl_putNumberCharWithSpace( ::rtl::OUString& rStr,
+ double fValue,
+ double fOldValue,
+ bool bUseRelativeCoordinates )
+ {
+ if( bUseRelativeCoordinates )
+ fValue -= fOldValue;
+
+ const sal_Int32 aLen( rStr.getLength() );
+ if(aLen)
+ {
+ if( lcl_isOnNumberChar(rStr, aLen - 1, false) &&
+ fValue >= 0.0 )
+ {
+ rStr += ::rtl::OUString::valueOf(
+ sal_Unicode(' ') );
+ }
+ }
+
+ lcl_putNumberChar(rStr, fValue);
+ }
+
+ inline sal_Unicode lcl_getCommand( sal_Char cUpperCaseCommand,
+ sal_Char cLowerCaseCommand,
+ bool bUseRelativeCoordinates )
+ {
+ return bUseRelativeCoordinates ? cLowerCaseCommand : cUpperCaseCommand;
+ }
+ }
+
+ bool importFromSvgD(B2DPolyPolygon& o_rPolyPolygon, const ::rtl::OUString& rSvgDStatement)
+ {
+ o_rPolyPolygon.clear();
+ const sal_Int32 nLen(rSvgDStatement.getLength());
+ sal_Int32 nPos(0);
+ bool bIsClosed(false);
+ double nLastX( 0.0 );
+ double nLastY( 0.0 );
+ B2DPolygon aCurrPoly;
+
+ // skip initial whitespace
+ lcl_skipSpaces(nPos, rSvgDStatement, nLen);
+
+ while(nPos < nLen)
+ {
+ bool bRelative(false);
+ bool bMoveTo(false);
+ const sal_Unicode aCurrChar(rSvgDStatement[nPos]);
+
+ switch(aCurrChar)
+ {
+ case 'z' :
+ case 'Z' :
+ {
+ nPos++;
+ lcl_skipSpaces(nPos, rSvgDStatement, nLen);
+
+ // remember closed state of current polygon
+ bIsClosed = true;
+ break;
+ }
+
+ case 'm' :
+ case 'M' :
+ {
+ bMoveTo = true;
+ // FALLTHROUGH intended
+ }
+ case 'l' :
+ case 'L' :
+ {
+ if('m' == aCurrChar || 'l' == aCurrChar)
+ {
+ bRelative = true;
+ }
+
+ if(bMoveTo)
+ {
+ // new polygon start, finish old one
+ if(aCurrPoly.count())
+ {
+ // add current polygon
+ if(bIsClosed)
+ {
+ closeWithGeometryChange(aCurrPoly);
+ }
+
+ o_rPolyPolygon.append(aCurrPoly);
+
+ // reset import values
+ bIsClosed = false;
+ aCurrPoly.clear();
+ }
+ }
+
+ nPos++;
+ lcl_skipSpaces(nPos, rSvgDStatement, nLen);
+
+ while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
+ {
+ double nX, nY;
+
+ if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
+
+ if(bRelative)
+ {
+ nX += nLastX;
+ nY += nLastY;
+ }
+
+ // set last position
+ nLastX = nX;
+ nLastY = nY;
+
+ // add point
+ aCurrPoly.append(B2DPoint(nX, nY));
+ }
+ break;
+ }
+
+ case 'h' :
+ {
+ bRelative = true;
+ // FALLTHROUGH intended
+ }
+ case 'H' :
+ {
+ nPos++;
+ lcl_skipSpaces(nPos, rSvgDStatement, nLen);
+
+ while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
+ {
+ double nX, nY(nLastY);
+
+ if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
+
+ if(bRelative)
+ {
+ nX += nLastX;
+ }
+
+ // set last position
+ nLastX = nX;
+
+ // add point
+ aCurrPoly.append(B2DPoint(nX, nY));
+ }
+ break;
+ }
+
+ case 'v' :
+ {
+ bRelative = true;
+ // FALLTHROUGH intended
+ }
+ case 'V' :
+ {
+ nPos++;
+ lcl_skipSpaces(nPos, rSvgDStatement, nLen);
+
+ while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
+ {
+ double nX(nLastX), nY;
+
+ if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
+
+ if(bRelative)
+ {
+ nY += nLastY;
+ }
+
+ // set last position
+ nLastY = nY;
+
+ // add point
+ aCurrPoly.append(B2DPoint(nX, nY));
+ }
+ break;
+ }
+
+ case 's' :
+ {
+ bRelative = true;
+ // FALLTHROUGH intended
+ }
+ case 'S' :
+ {
+ nPos++;
+ lcl_skipSpaces(nPos, rSvgDStatement, nLen);
+
+ while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
+ {
+ double nX, nY;
+ double nX2, nY2;
+
+ if(!lcl_importDoubleAndSpaces(nX2, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY2, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
+
+ if(bRelative)
+ {
+ nX2 += nLastX;
+ nY2 += nLastY;
+ nX += nLastX;
+ nY += nLastY;
+ }
+
+ // ensure existance of start point
+ if(!aCurrPoly.count())
+ {
+ aCurrPoly.append(B2DPoint(nLastX, nLastY));
+ }
+
+ // get first control point. It's the reflection of the PrevControlPoint
+ // of the last point. If not existent, use current point (see SVG)
+ B2DPoint aPrevControl(B2DPoint(nLastX, nLastY));
+ const sal_uInt32 nIndex(aCurrPoly.count() - 1);
+
+ if(aCurrPoly.areControlPointsUsed() && aCurrPoly.isPrevControlPointUsed(nIndex))
+ {
+ const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(nIndex));
+ const B2DPoint aPrevControlPoint(aCurrPoly.getPrevControlPoint(nIndex));
+
+ // use mirrored previous control point
+ aPrevControl.setX((2.0 * aPrevPoint.getX()) - aPrevControlPoint.getX());
+ aPrevControl.setY((2.0 * aPrevPoint.getY()) - aPrevControlPoint.getY());
+ }
+
+ // append curved edge
+ aCurrPoly.appendBezierSegment(aPrevControl, B2DPoint(nX2, nY2), B2DPoint(nX, nY));
+
+ // set last position
+ nLastX = nX;
+ nLastY = nY;
+ }
+ break;
+ }
+
+ case 'c' :
+ {
+ bRelative = true;
+ // FALLTHROUGH intended
+ }
+ case 'C' :
+ {
+ nPos++;
+ lcl_skipSpaces(nPos, rSvgDStatement, nLen);
+
+ while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
+ {
+ double nX, nY;
+ double nX1, nY1;
+ double nX2, nY2;
+
+ if(!lcl_importDoubleAndSpaces(nX1, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY1, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nX2, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY2, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
+
+ if(bRelative)
+ {
+ nX1 += nLastX;
+ nY1 += nLastY;
+ nX2 += nLastX;
+ nY2 += nLastY;
+ nX += nLastX;
+ nY += nLastY;
+ }
+
+ // ensure existance of start point
+ if(!aCurrPoly.count())
+ {
+ aCurrPoly.append(B2DPoint(nLastX, nLastY));
+ }
+
+ // append curved edge
+ aCurrPoly.appendBezierSegment(B2DPoint(nX1, nY1), B2DPoint(nX2, nY2), B2DPoint(nX, nY));
+
+ // set last position
+ nLastX = nX;
+ nLastY = nY;
+ }
+ break;
+ }
+
+ // #100617# quadratic beziers are imported as cubic ones
+ case 'q' :
+ {
+ bRelative = true;
+ // FALLTHROUGH intended
+ }
+ case 'Q' :
+ {
+ nPos++;
+ lcl_skipSpaces(nPos, rSvgDStatement, nLen);
+
+ while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
+ {
+ double nX, nY;
+ double nX1, nY1;
+
+ if(!lcl_importDoubleAndSpaces(nX1, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY1, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
+
+ if(bRelative)
+ {
+ nX1 += nLastX;
+ nY1 += nLastY;
+ nX += nLastX;
+ nY += nLastY;
+ }
+
+ // calculate the cubic bezier coefficients from the quadratic ones
+ const double nX1Prime((nX1 * 2.0 + nLastX) / 3.0);
+ const double nY1Prime((nY1 * 2.0 + nLastY) / 3.0);
+ const double nX2Prime((nX1 * 2.0 + nX) / 3.0);
+ const double nY2Prime((nY1 * 2.0 + nY) / 3.0);
+
+ // ensure existance of start point
+ if(!aCurrPoly.count())
+ {
+ aCurrPoly.append(B2DPoint(nLastX, nLastY));
+ }
+
+ // append curved edge
+ aCurrPoly.appendBezierSegment(B2DPoint(nX1Prime, nY1Prime), B2DPoint(nX2Prime, nY2Prime), B2DPoint(nX, nY));
+
+ // set last position
+ nLastX = nX;
+ nLastY = nY;
+ }
+ break;
+ }
+
+ // #100617# relative quadratic beziers are imported as cubic
+ case 't' :
+ {
+ bRelative = true;
+ // FALLTHROUGH intended
+ }
+ case 'T' :
+ {
+ nPos++;
+ lcl_skipSpaces(nPos, rSvgDStatement, nLen);
+
+ while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
+ {
+ double nX, nY;
+
+ if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
+
+ if(bRelative)
+ {
+ nX += nLastX;
+ nY += nLastY;
+ }
+
+ // ensure existance of start point
+ if(!aCurrPoly.count())
+ {
+ aCurrPoly.append(B2DPoint(nLastX, nLastY));
+ }
+
+ // get first control point. It's the reflection of the PrevControlPoint
+ // of the last point. If not existent, use current point (see SVG)
+ B2DPoint aPrevControl(B2DPoint(nLastX, nLastY));
+ const sal_uInt32 nIndex(aCurrPoly.count() - 1);
+ const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(nIndex));
+
+ if(aCurrPoly.areControlPointsUsed() && aCurrPoly.isPrevControlPointUsed(nIndex))
+ {
+ const B2DPoint aPrevControlPoint(aCurrPoly.getPrevControlPoint(nIndex));
+
+ // use mirrored previous control point
+ aPrevControl.setX((2.0 * aPrevPoint.getX()) - aPrevControlPoint.getX());
+ aPrevControl.setY((2.0 * aPrevPoint.getY()) - aPrevControlPoint.getY());
+ }
+
+ if(!aPrevControl.equal(aPrevPoint))
+ {
+ // there is a prev control point, and we have the already mirrored one
+ // in aPrevControl. We also need the quadratic control point for this
+ // new quadratic segment to calculate the 2nd cubic control point
+ const B2DPoint aQuadControlPoint(
+ ((3.0 * aPrevControl.getX()) - aPrevPoint.getX()) / 2.0,
+ ((3.0 * aPrevControl.getY()) - aPrevPoint.getY()) / 2.0);
+
+ // calculate the cubic bezier coefficients from the quadratic ones.
+ const double nX2Prime((aQuadControlPoint.getX() * 2.0 + nX) / 3.0);
+ const double nY2Prime((aQuadControlPoint.getY() * 2.0 + nY) / 3.0);
+
+ // append curved edge, use mirrored cubic control point directly
+ aCurrPoly.appendBezierSegment(aPrevControl, B2DPoint(nX2Prime, nY2Prime), B2DPoint(nX, nY));
+ }
+ else
+ {
+ // when no previous control, SVG says to use current point -> straight line.
+ // Just add end point
+ aCurrPoly.append(B2DPoint(nX, nY));
+ }
+
+ // set last position
+ nLastX = nX;
+ nLastY = nY;
+ }
+ break;
+ }
+
+ case 'a' :
+ {
+ bRelative = true;
+ // FALLTHROUGH intended
+ }
+ case 'A' :
+ {
+ nPos++;
+ lcl_skipSpaces(nPos, rSvgDStatement, nLen);
+
+ while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos))
+ {
+ double nX, nY;
+ double fRX, fRY, fPhi;
+ sal_Int32 bLargeArcFlag, bSweepFlag;
+
+ if(!lcl_importDoubleAndSpaces(fRX, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(fRY, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(fPhi, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importNumberAndSpaces(bLargeArcFlag, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importNumberAndSpaces(bSweepFlag, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false;
+
+ if(bRelative)
+ {
+ nX += nLastX;
+ nY += nLastY;
+ }
+
+ const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(aCurrPoly.count() - 1));
+
+ if( nX == nLastX && nY == nLastY )
+ continue; // start==end -> skip according to SVG spec
+
+ if( fRX == 0.0 || fRY == 0.0 )
+ {
+ // straight line segment according to SVG spec
+ aCurrPoly.append(B2DPoint(nX, nY));
+ }
+ else
+ {
+ // normalize according to SVG spec
+ fRX=fabs(fRX); fRY=fabs(fRY);
+
+ // from the SVG spec, appendix F.6.4
+
+ // |x1'| |cos phi sin phi| |(x1 - x2)/2|
+ // |y1'| = |-sin phi cos phi| |(y1 - y2)/2|
+ const B2DPoint p1(nLastX, nLastY);
+ const B2DPoint p2(nX, nY);
+ B2DHomMatrix aTransform; aTransform.rotate(-fPhi*M_PI/180);
+
+ const B2DPoint p1_prime( aTransform * B2DPoint(((p1-p2)/2.0)) );
+
+ // ______________________________________ rx y1'
+ // |cx'| + / rx^2 ry^2 - rx^2 y1'^2 - ry^2 x1^2 ry
+ // |cy'| =-/ rx^2y1'^2 + ry^2 x1'^2 - ry x1'
+ // rx
+ // chose + if f_A != f_S
+ // chose - if f_A = f_S
+ B2DPoint aCenter_prime;
+ const double fRadicant(
+ (fRX*fRX*fRY*fRY - fRX*fRX*p1_prime.getY()*p1_prime.getY() - fRY*fRY*p1_prime.getX()*p1_prime.getX())/
+ (fRX*fRX*p1_prime.getY()*p1_prime.getY() + fRY*fRY*p1_prime.getX()*p1_prime.getX()));
+ if( fRadicant < 0.0 )
+ {
+ // no solution - according to SVG
+ // spec, scale up ellipse
+ // uniformly such that it passes
+ // through end points (denominator
+ // of radicant solved for fRY,
+ // with s=fRX/fRY)
+ const double fRatio(fRX/fRY);
+ const double fRadicant2(
+ p1_prime.getY()*p1_prime.getY() +
+ p1_prime.getX()*p1_prime.getX()/(fRatio*fRatio));
+ if( fRadicant2 < 0.0 )
+ {
+ // only trivial solution, one
+ // of the axes 0 -> straight
+ // line segment according to
+ // SVG spec
+ aCurrPoly.append(B2DPoint(nX, nY));
+ continue;
+ }
+
+ fRY=sqrt(fRadicant2);
+ fRX=fRatio*fRY;
+
+ // keep center_prime forced to (0,0)
+ }
+ else
+ {
+ const double fFactor(
+ (bLargeArcFlag==bSweepFlag ? -1.0 : 1.0) *
+ sqrt(fRadicant));
+
+ // actually calculate center_prime
+ aCenter_prime = B2DPoint(
+ fFactor*fRX*p1_prime.getY()/fRY,
+ -fFactor*fRY*p1_prime.getX()/fRX);
+ }
+
+ // + u - v
+ // angle(u,v) = arccos( ------------ ) (take the sign of (ux vy - uy vx))
+ // - ||u|| ||v||
+
+ // 1 | (x1' - cx')/rx |
+ // theta1 = angle(( ), | | )
+ // 0 | (y1' - cy')/ry |
+ const B2DPoint aRadii(fRX,fRY);
+ double fTheta1(
+ B2DVector(1.0,0.0).angle(
+ (p1_prime-aCenter_prime)/aRadii));
+
+ // |1| | (-x1' - cx')/rx |
+ // theta2 = angle( | | , | | )
+ // |0| | (-y1' - cy')/ry |
+ double fTheta2(
+ B2DVector(1.0,0.0).angle(
+ (-p1_prime-aCenter_prime)/aRadii));
+
+ // map both angles to [0,2pi)
+ fTheta1 = fmod(2*M_PI+fTheta1,2*M_PI);
+ fTheta2 = fmod(2*M_PI+fTheta2,2*M_PI);
+
+ // make sure the large arc is taken
+ // (since
+ // createPolygonFromEllipseSegment()
+ // normalizes to e.g. cw arc)
+ const bool bFlipSegment( (bLargeArcFlag!=0) ==
+ (fmod(fTheta2+2*M_PI-fTheta1,
+ 2*M_PI)<M_PI) );
+ if( bFlipSegment )
+ std::swap(fTheta1,fTheta2);
+
+ // finally, create bezier polygon from this
+ B2DPolygon aSegment(
+ tools::createPolygonFromUnitEllipseSegment(
+ fTheta1, fTheta2 ));
+
+ // transform ellipse by rotation & move to final center
+ aTransform.identity();
+ aTransform.scale(fRX,fRY);
+ aTransform.translate(aCenter_prime.getX(),
+ aCenter_prime.getY());
+ aTransform.rotate(fPhi*M_PI/180);
+ const B2DPoint aOffset((p1+p2)/2.0);
+ aTransform.translate(aOffset.getX(),
+ aOffset.getY());
+ aSegment.transform(aTransform);
+
+ // createPolygonFromEllipseSegment()
+ // always creates arcs that are
+ // positively oriented - flip polygon
+ // if we swapped angles above
+ if( bFlipSegment )
+ aSegment.flip();
+ aCurrPoly.append(aSegment);
+ }
+
+ // set last position
+ nLastX = nX;
+ nLastY = nY;
+ }
+ break;
+ }
+
+ default:
+ {
+ OSL_ENSURE(false, "importFromSvgD(): skipping tags in svg:d element (unknown)!");
+ OSL_TRACE("importFromSvgD(): skipping tags in svg:d element (unknown: \"%c\")!", aCurrChar);
+ ++nPos;
+ break;
+ }
+ }
+ }
+
+ if(aCurrPoly.count())
+ {
+ // end-process last poly
+ if(bIsClosed)
+ {
+ closeWithGeometryChange(aCurrPoly);
+ }
+
+ o_rPolyPolygon.append(aCurrPoly);
+ }
+
+ return true;
+ }
+
+ bool importFromSvgPoints( B2DPolygon& o_rPoly,
+ const ::rtl::OUString& rSvgPointsAttribute )
+ {
+ o_rPoly.clear();
+ const sal_Int32 nLen(rSvgPointsAttribute.getLength());
+ sal_Int32 nPos(0);
+ double nX, nY;
+
+ // skip initial whitespace
+ lcl_skipSpaces(nPos, rSvgPointsAttribute, nLen);
+
+ while(nPos < nLen)
+ {
+ if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgPointsAttribute, nLen)) return false;
+ if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgPointsAttribute, nLen)) return false;
+
+ // add point
+ o_rPoly.append(B2DPoint(nX, nY));
+
+ // skip to next number, or finish
+ lcl_skipSpaces(nPos, rSvgPointsAttribute, nLen);
+ }
+
+ return true;
+ }
+
+ ::rtl::OUString exportToSvgD(
+ const B2DPolyPolygon& rPolyPolygon,
+ bool bUseRelativeCoordinates,
+ bool bDetectQuadraticBeziers)
+ {
+ const sal_uInt32 nCount(rPolyPolygon.count());
+ ::rtl::OUString aResult;
+ B2DPoint aCurrentSVGPosition(0.0, 0.0); // SVG assumes (0,0) as the initial current point
+
+ for(sal_uInt32 i(0); i < nCount; i++)
+ {
+ const B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(i));
+ const sal_uInt32 nPointCount(aPolygon.count());
+
+ if(nPointCount)
+ {
+ const bool bPolyUsesControlPoints(aPolygon.areControlPointsUsed());
+ const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount : nPointCount - 1);
+ sal_Unicode aLastSVGCommand(' '); // last SVG command char
+ B2DPoint aLeft, aRight; // for quadratic bezier test
+
+ // handle polygon start point
+ B2DPoint aEdgeStart(aPolygon.getB2DPoint(0));
+ aResult += ::rtl::OUString::valueOf(lcl_getCommand('M', 'm', bUseRelativeCoordinates));
+ lcl_putNumberCharWithSpace(aResult, aEdgeStart.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aEdgeStart.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ aLastSVGCommand = lcl_getCommand('L', 'l', bUseRelativeCoordinates);
+ aCurrentSVGPosition = aEdgeStart;
+
+ for(sal_uInt32 nIndex(0); nIndex < nEdgeCount; nIndex++)
+ {
+ // prepare access to next point
+ const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
+ const B2DPoint aEdgeEnd(aPolygon.getB2DPoint(nNextIndex));
+
+ // handle edge from (aEdgeStart, aEdgeEnd) using indices (nIndex, nNextIndex)
+ const bool bEdgeIsBezier(bPolyUsesControlPoints
+ && (aPolygon.isNextControlPointUsed(nIndex) || aPolygon.isPrevControlPointUsed(nNextIndex)));
+
+ if(bEdgeIsBezier)
+ {
+ // handle bezier edge
+ const B2DPoint aControlEdgeStart(aPolygon.getNextControlPoint(nIndex));
+ const B2DPoint aControlEdgeEnd(aPolygon.getPrevControlPoint(nNextIndex));
+ bool bIsQuadraticBezier(false);
+
+ // check continuity at current edge's start point. For SVG, do NOT use an
+ // existing continuity since no 'S' or 's' statement should be written. At
+ // import, that 'previous' control vector is not available. SVG documentation
+ // says for interpretation:
+ //
+ // "(If there is no previous command or if the previous command was
+ // not an C, c, S or s, assume the first control point is coincident
+ // with the current point.)"
+ //
+ // That's what is done from our import, so avoid exporting it as first statement
+ // is necessary.
+ const bool bSymmetricAtEdgeStart(
+ 0 != nIndex
+ && CONTINUITY_C2 == aPolygon.getContinuityInPoint(nIndex));
+
+ if(bDetectQuadraticBeziers)
+ {
+ // check for quadratic beziers - that's
+ // the case if both control points are in
+ // the same place when they are prolonged
+ // to the common quadratic control point
+ //
+ // Left: P = (3P1 - P0) / 2
+ // Right: P = (3P2 - P3) / 2
+ aLeft = B2DPoint((3.0 * aControlEdgeStart - aEdgeStart) / 2.0);
+ aRight= B2DPoint((3.0 * aControlEdgeEnd - aEdgeEnd) / 2.0);
+ bIsQuadraticBezier = aLeft.equal(aRight);
+ }
+
+ if(bIsQuadraticBezier)
+ {
+ // approximately equal, export as quadratic bezier
+ if(bSymmetricAtEdgeStart)
+ {
+ const sal_Unicode aCommand(lcl_getCommand('T', 't', bUseRelativeCoordinates));
+
+ if(aLastSVGCommand != aCommand)
+ {
+ aResult += ::rtl::OUString::valueOf(aCommand);
+ aLastSVGCommand = aCommand;
+ }
+
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ aLastSVGCommand = aCommand;
+ aCurrentSVGPosition = aEdgeEnd;
+ }
+ else
+ {
+ const sal_Unicode aCommand(lcl_getCommand('Q', 'q', bUseRelativeCoordinates));
+
+ if(aLastSVGCommand != aCommand)
+ {
+ aResult += ::rtl::OUString::valueOf(aCommand);
+ aLastSVGCommand = aCommand;
+ }
+
+ lcl_putNumberCharWithSpace(aResult, aLeft.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aLeft.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ aLastSVGCommand = aCommand;
+ aCurrentSVGPosition = aEdgeEnd;
+ }
+ }
+ else
+ {
+ // export as cubic bezier
+ if(bSymmetricAtEdgeStart)
+ {
+ const sal_Unicode aCommand(lcl_getCommand('S', 's', bUseRelativeCoordinates));
+
+ if(aLastSVGCommand != aCommand)
+ {
+ aResult += ::rtl::OUString::valueOf(aCommand);
+ aLastSVGCommand = aCommand;
+ }
+
+ lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ aLastSVGCommand = aCommand;
+ aCurrentSVGPosition = aEdgeEnd;
+ }
+ else
+ {
+ const sal_Unicode aCommand(lcl_getCommand('C', 'c', bUseRelativeCoordinates));
+
+ if(aLastSVGCommand != aCommand)
+ {
+ aResult += ::rtl::OUString::valueOf(aCommand);
+ aLastSVGCommand = aCommand;
+ }
+
+ lcl_putNumberCharWithSpace(aResult, aControlEdgeStart.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aControlEdgeStart.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ aLastSVGCommand = aCommand;
+ aCurrentSVGPosition = aEdgeEnd;
+ }
+ }
+ }
+ else
+ {
+ // straight edge
+ if(0 == nNextIndex)
+ {
+ // it's a closed polygon's last edge and it's not a bezier edge, so there is
+ // no need to write it
+ }
+ else
+ {
+ const bool bXEqual(aEdgeStart.getX() == aEdgeEnd.getX());
+ const bool bYEqual(aEdgeStart.getY() == aEdgeEnd.getY());
+
+ if(bXEqual && bYEqual)
+ {
+ // point is a double point; do not export at all
+ }
+ else if(bXEqual)
+ {
+ // export as vertical line
+ const sal_Unicode aCommand(lcl_getCommand('V', 'v', bUseRelativeCoordinates));
+
+ if(aLastSVGCommand != aCommand)
+ {
+ aResult += ::rtl::OUString::valueOf(aCommand);
+ aLastSVGCommand = aCommand;
+ }
+
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ aCurrentSVGPosition = aEdgeEnd;
+ }
+ else if(bYEqual)
+ {
+ // export as horizontal line
+ const sal_Unicode aCommand(lcl_getCommand('H', 'h', bUseRelativeCoordinates));
+
+ if(aLastSVGCommand != aCommand)
+ {
+ aResult += ::rtl::OUString::valueOf(aCommand);
+ aLastSVGCommand = aCommand;
+ }
+
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ aCurrentSVGPosition = aEdgeEnd;
+ }
+ else
+ {
+ // export as line
+ const sal_Unicode aCommand(lcl_getCommand('L', 'l', bUseRelativeCoordinates));
+
+ if(aLastSVGCommand != aCommand)
+ {
+ aResult += ::rtl::OUString::valueOf(aCommand);
+ aLastSVGCommand = aCommand;
+ }
+
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates);
+ lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates);
+ aCurrentSVGPosition = aEdgeEnd;
+ }
+ }
+ }
+
+ // prepare edge start for next loop step
+ aEdgeStart = aEdgeEnd;
+ }
+
+ // close path if closed poly (Z and z are equivalent here, but looks nicer when case is matched)
+ if(aPolygon.isClosed())
+ {
+ aResult += ::rtl::OUString::valueOf(lcl_getCommand('Z', 'z', bUseRelativeCoordinates));
+ }
+ }
+ }
+
+ return aResult;
+ }
+ }
+}
+
+// eof
diff --git a/basegfx/source/polygon/b3dgeometry.cxx b/basegfx/source/polygon/b3dgeometry.cxx
new file mode 100644
index 000000000000..38b93b931f53
--- /dev/null
+++ b/basegfx/source/polygon/b3dgeometry.cxx
@@ -0,0 +1,59 @@
+/*************************************************************************
+ *
+ * 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: b3dgeometry.cxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#include <osl/diagnose.h>
+#include <basegfx/polygon/b3dgeometry.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ B3DGeometry::B3DGeometry()
+ : mbUnifiedVectorValid(false)
+ {
+ }
+
+ B3DGeometry::~B3DGeometry()
+ {
+ }
+
+ bool B3DGeometry::operator==(const B3DGeometry& rGeometry) const
+ {
+ return (maPolyPolygon == maPolyPolygon
+ && maPolygonTo3D == maPolygonTo3D
+ && maPolyNormal == maPolyNormal
+ && maPolyTexture == maPolyTexture
+ }
+
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/polygon/b3dpolygon.cxx b/basegfx/source/polygon/b3dpolygon.cxx
new file mode 100644
index 000000000000..84383bc55ec9
--- /dev/null
+++ b/basegfx/source/polygon/b3dpolygon.cxx
@@ -0,0 +1,1819 @@
+/*************************************************************************
+ *
+ * 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: b3dpolygon.cxx,v $
+ * $Revision: 1.13 $
+ *
+ * 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/polygon/b3dpolygon.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <rtl/instance.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <vector>
+#include <algorithm>
+
+//////////////////////////////////////////////////////////////////////////////
+
+class CoordinateData3D
+{
+ basegfx::B3DPoint maPoint;
+
+public:
+ CoordinateData3D()
+ : maPoint()
+ {
+ }
+
+ explicit CoordinateData3D(const basegfx::B3DPoint& rData)
+ : maPoint(rData)
+ {
+ }
+
+ const basegfx::B3DPoint& getCoordinate() const
+ {
+ return maPoint;
+ }
+
+ void setCoordinate(const basegfx::B3DPoint& rValue)
+ {
+ if(rValue != maPoint)
+ maPoint = rValue;
+ }
+
+ bool operator==(const CoordinateData3D& rData) const
+ {
+ return (maPoint == rData.getCoordinate());
+ }
+
+ void transform(const basegfx::B3DHomMatrix& rMatrix)
+ {
+ maPoint *= rMatrix;
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class CoordinateDataArray3D
+{
+ typedef ::std::vector< CoordinateData3D > CoordinateData3DVector;
+
+ CoordinateData3DVector maVector;
+
+public:
+ explicit CoordinateDataArray3D(sal_uInt32 nCount)
+ : maVector(nCount)
+ {
+ }
+
+ explicit CoordinateDataArray3D(const CoordinateDataArray3D& rOriginal)
+ : maVector(rOriginal.maVector)
+ {
+ }
+
+ CoordinateDataArray3D(const CoordinateDataArray3D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : maVector(rOriginal.maVector.begin() + nIndex, rOriginal.maVector.begin() + (nIndex + nCount))
+ {
+ }
+
+ ~CoordinateDataArray3D()
+ {
+ }
+
+ ::basegfx::B3DVector getNormal() const
+ {
+ ::basegfx::B3DVector aRetval;
+ const sal_uInt32 nPointCount(maVector.size());
+
+ if(nPointCount > 2)
+ {
+ sal_uInt32 nISmallest(0);
+ sal_uInt32 a(0);
+ const basegfx::B3DPoint* pSmallest(&maVector[0].getCoordinate());
+ const basegfx::B3DPoint* pNext(0);
+ const basegfx::B3DPoint* pPrev(0);
+
+ // To guarantee a correctly oriented point, choose an outmost one
+ // which then cannot be concave
+ for(a = 1; a < nPointCount; a++)
+ {
+ const basegfx::B3DPoint& rCandidate = maVector[a].getCoordinate();
+
+ if((rCandidate.getX() < pSmallest->getX())
+ || (rCandidate.getX() == pSmallest->getX() && rCandidate.getY() < pSmallest->getY())
+ || (rCandidate.getX() == pSmallest->getX() && rCandidate.getY() == pSmallest->getY() && rCandidate.getZ() < pSmallest->getZ()))
+ {
+ nISmallest = a;
+ pSmallest = &rCandidate;
+ }
+ }
+
+ // look for a next point different from minimal one
+ for(a = (nISmallest + 1) % nPointCount; a != nISmallest; a = (a + 1) % nPointCount)
+ {
+ const basegfx::B3DPoint& rCandidate = maVector[a].getCoordinate();
+
+ if(!rCandidate.equal(*pSmallest))
+ {
+ pNext = &rCandidate;
+ break;
+ }
+ }
+
+ // look for a previous point different from minimal one
+ for(a = (nISmallest + nPointCount - 1) % nPointCount; a != nISmallest; a = (a + nPointCount - 1) % nPointCount)
+ {
+ const basegfx::B3DPoint& rCandidate = maVector[a].getCoordinate();
+
+ if(!rCandidate.equal(*pSmallest))
+ {
+ pPrev = &rCandidate;
+ break;
+ }
+ }
+
+ // we always have a minimal point. If we also have a different next and previous,
+ // we can calculate the normal
+ if(pNext && pPrev)
+ {
+ const basegfx::B3DVector aPrev(*pPrev - *pSmallest);
+ const basegfx::B3DVector aNext(*pNext - *pSmallest);
+
+ aRetval = cross(aPrev, aNext);
+ aRetval.normalize();
+ }
+ }
+
+ return aRetval;
+ }
+
+ sal_uInt32 count() const
+ {
+ return maVector.size();
+ }
+
+ bool operator==(const CoordinateDataArray3D& rCandidate) const
+ {
+ return (maVector == rCandidate.maVector);
+ }
+
+ const basegfx::B3DPoint& getCoordinate(sal_uInt32 nIndex) const
+ {
+ return maVector[nIndex].getCoordinate();
+ }
+
+ void setCoordinate(sal_uInt32 nIndex, const basegfx::B3DPoint& rValue)
+ {
+ maVector[nIndex].setCoordinate(rValue);
+ }
+
+ void insert(sal_uInt32 nIndex, const CoordinateData3D& rValue, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // add nCount copies of rValue
+ CoordinateData3DVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ maVector.insert(aIndex, nCount, rValue);
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const CoordinateDataArray3D& rSource)
+ {
+ const sal_uInt32 nCount(rSource.maVector.size());
+
+ if(nCount)
+ {
+ // insert data
+ CoordinateData3DVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ CoordinateData3DVector::const_iterator aStart(rSource.maVector.begin());
+ CoordinateData3DVector::const_iterator aEnd(rSource.maVector.end());
+ maVector.insert(aIndex, aStart, aEnd);
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // remove point data
+ CoordinateData3DVector::iterator aStart(maVector.begin());
+ aStart += nIndex;
+ const CoordinateData3DVector::iterator aEnd(aStart + nCount);
+ maVector.erase(aStart, aEnd);
+ }
+ }
+
+ void flip()
+ {
+ if(maVector.size() > 1)
+ {
+ const sal_uInt32 nHalfSize(maVector.size() >> 1L);
+ CoordinateData3DVector::iterator aStart(maVector.begin());
+ CoordinateData3DVector::iterator aEnd(maVector.end() - 1L);
+
+ for(sal_uInt32 a(0); a < nHalfSize; a++)
+ {
+ ::std::swap(*aStart, *aEnd);
+ aStart++;
+ aEnd--;
+ }
+ }
+ }
+
+ void transform(const ::basegfx::B3DHomMatrix& rMatrix)
+ {
+ CoordinateData3DVector::iterator aStart(maVector.begin());
+ CoordinateData3DVector::iterator aEnd(maVector.end());
+
+ for(; aStart != aEnd; aStart++)
+ {
+ aStart->transform(rMatrix);
+ }
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class BColorArray
+{
+ typedef ::std::vector< ::basegfx::BColor > BColorDataVector;
+
+ BColorDataVector maVector;
+ sal_uInt32 mnUsedEntries;
+
+public:
+ explicit BColorArray(sal_uInt32 nCount)
+ : maVector(nCount),
+ mnUsedEntries(0L)
+ {
+ }
+
+ explicit BColorArray(const BColorArray& rOriginal)
+ : maVector(rOriginal.maVector),
+ mnUsedEntries(rOriginal.mnUsedEntries)
+ {
+ }
+
+ BColorArray(const BColorArray& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : maVector(),
+ mnUsedEntries(0L)
+ {
+ BColorDataVector::const_iterator aStart(rOriginal.maVector.begin());
+ aStart += nIndex;
+ BColorDataVector::const_iterator aEnd(aStart);
+ aEnd += nCount;
+ maVector.reserve(nCount);
+
+ for(; aStart != aEnd; aStart++)
+ {
+ if(!aStart->equalZero())
+ mnUsedEntries++;
+
+ maVector.push_back(*aStart);
+ }
+ }
+
+ ~BColorArray()
+ {
+ }
+
+ sal_uInt32 count() const
+ {
+ return maVector.size();
+ }
+
+ bool operator==(const BColorArray& rCandidate) const
+ {
+ return (maVector == rCandidate.maVector);
+ }
+
+ bool isUsed() const
+ {
+ return (0L != mnUsedEntries);
+ }
+
+ const ::basegfx::BColor& getBColor(sal_uInt32 nIndex) const
+ {
+ return maVector[nIndex];
+ }
+
+ void setBColor(sal_uInt32 nIndex, const ::basegfx::BColor& rValue)
+ {
+ bool bWasUsed(mnUsedEntries && !maVector[nIndex].equalZero());
+ bool bIsUsed(!rValue.equalZero());
+
+ if(bWasUsed)
+ {
+ if(bIsUsed)
+ {
+ maVector[nIndex] = rValue;
+ }
+ else
+ {
+ maVector[nIndex] = ::basegfx::BColor::getEmptyBColor();
+ mnUsedEntries--;
+ }
+ }
+ else
+ {
+ if(bIsUsed)
+ {
+ maVector[nIndex] = rValue;
+ mnUsedEntries++;
+ }
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const ::basegfx::BColor& rValue, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // add nCount copies of rValue
+ BColorDataVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ maVector.insert(aIndex, nCount, rValue);
+
+ if(!rValue.equalZero())
+ mnUsedEntries += nCount;
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const BColorArray& rSource)
+ {
+ const sal_uInt32 nCount(rSource.maVector.size());
+
+ if(nCount)
+ {
+ // insert data
+ BColorDataVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ BColorDataVector::const_iterator aStart(rSource.maVector.begin());
+ BColorDataVector::const_iterator aEnd(rSource.maVector.end());
+ maVector.insert(aIndex, aStart, aEnd);
+
+ for(; aStart != aEnd; aStart++)
+ {
+ if(!aStart->equalZero())
+ mnUsedEntries++;
+ }
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ const BColorDataVector::iterator aDeleteStart(maVector.begin() + nIndex);
+ const BColorDataVector::iterator aDeleteEnd(aDeleteStart + nCount);
+ BColorDataVector::const_iterator aStart(aDeleteStart);
+
+ for(; mnUsedEntries && aStart != aDeleteEnd; aStart++)
+ {
+ if(!aStart->equalZero())
+ mnUsedEntries--;
+ }
+
+ // remove point data
+ maVector.erase(aDeleteStart, aDeleteEnd);
+ }
+ }
+
+ void flip()
+ {
+ if(maVector.size() > 1)
+ {
+ const sal_uInt32 nHalfSize(maVector.size() >> 1L);
+ BColorDataVector::iterator aStart(maVector.begin());
+ BColorDataVector::iterator aEnd(maVector.end() - 1L);
+
+ for(sal_uInt32 a(0); a < nHalfSize; a++)
+ {
+ ::std::swap(*aStart, *aEnd);
+ aStart++;
+ aEnd--;
+ }
+ }
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class NormalsArray3D
+{
+ typedef ::std::vector< ::basegfx::B3DVector > NormalsData3DVector;
+
+ NormalsData3DVector maVector;
+ sal_uInt32 mnUsedEntries;
+
+public:
+ explicit NormalsArray3D(sal_uInt32 nCount)
+ : maVector(nCount),
+ mnUsedEntries(0L)
+ {
+ }
+
+ explicit NormalsArray3D(const NormalsArray3D& rOriginal)
+ : maVector(rOriginal.maVector),
+ mnUsedEntries(rOriginal.mnUsedEntries)
+ {
+ }
+
+ NormalsArray3D(const NormalsArray3D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : maVector(),
+ mnUsedEntries(0L)
+ {
+ NormalsData3DVector::const_iterator aStart(rOriginal.maVector.begin());
+ aStart += nIndex;
+ NormalsData3DVector::const_iterator aEnd(aStart);
+ aEnd += nCount;
+ maVector.reserve(nCount);
+
+ for(; aStart != aEnd; aStart++)
+ {
+ if(!aStart->equalZero())
+ mnUsedEntries++;
+
+ maVector.push_back(*aStart);
+ }
+ }
+
+ ~NormalsArray3D()
+ {
+ }
+
+ sal_uInt32 count() const
+ {
+ return maVector.size();
+ }
+
+ bool operator==(const NormalsArray3D& rCandidate) const
+ {
+ return (maVector == rCandidate.maVector);
+ }
+
+ bool isUsed() const
+ {
+ return (0L != mnUsedEntries);
+ }
+
+ const ::basegfx::B3DVector& getNormal(sal_uInt32 nIndex) const
+ {
+ return maVector[nIndex];
+ }
+
+ void setNormal(sal_uInt32 nIndex, const ::basegfx::B3DVector& rValue)
+ {
+ bool bWasUsed(mnUsedEntries && !maVector[nIndex].equalZero());
+ bool bIsUsed(!rValue.equalZero());
+
+ if(bWasUsed)
+ {
+ if(bIsUsed)
+ {
+ maVector[nIndex] = rValue;
+ }
+ else
+ {
+ maVector[nIndex] = ::basegfx::B3DVector::getEmptyVector();
+ mnUsedEntries--;
+ }
+ }
+ else
+ {
+ if(bIsUsed)
+ {
+ maVector[nIndex] = rValue;
+ mnUsedEntries++;
+ }
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const ::basegfx::B3DVector& rValue, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // add nCount copies of rValue
+ NormalsData3DVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ maVector.insert(aIndex, nCount, rValue);
+
+ if(!rValue.equalZero())
+ mnUsedEntries += nCount;
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const NormalsArray3D& rSource)
+ {
+ const sal_uInt32 nCount(rSource.maVector.size());
+
+ if(nCount)
+ {
+ // insert data
+ NormalsData3DVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ NormalsData3DVector::const_iterator aStart(rSource.maVector.begin());
+ NormalsData3DVector::const_iterator aEnd(rSource.maVector.end());
+ maVector.insert(aIndex, aStart, aEnd);
+
+ for(; aStart != aEnd; aStart++)
+ {
+ if(!aStart->equalZero())
+ mnUsedEntries++;
+ }
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ const NormalsData3DVector::iterator aDeleteStart(maVector.begin() + nIndex);
+ const NormalsData3DVector::iterator aDeleteEnd(aDeleteStart + nCount);
+ NormalsData3DVector::const_iterator aStart(aDeleteStart);
+
+ for(; mnUsedEntries && aStart != aDeleteEnd; aStart++)
+ {
+ if(!aStart->equalZero())
+ mnUsedEntries--;
+ }
+
+ // remove point data
+ maVector.erase(aDeleteStart, aDeleteEnd);
+ }
+ }
+
+ void flip()
+ {
+ if(maVector.size() > 1)
+ {
+ const sal_uInt32 nHalfSize(maVector.size() >> 1L);
+ NormalsData3DVector::iterator aStart(maVector.begin());
+ NormalsData3DVector::iterator aEnd(maVector.end() - 1L);
+
+ for(sal_uInt32 a(0); a < nHalfSize; a++)
+ {
+ ::std::swap(*aStart, *aEnd);
+ aStart++;
+ aEnd--;
+ }
+ }
+ }
+
+ void transform(const basegfx::B3DHomMatrix& rMatrix)
+ {
+ NormalsData3DVector::iterator aStart(maVector.begin());
+ NormalsData3DVector::iterator aEnd(maVector.end());
+
+ for(; aStart != aEnd; aStart++)
+ {
+ (*aStart) *= rMatrix;
+ }
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class TextureCoordinate2D
+{
+ typedef ::std::vector< ::basegfx::B2DPoint > TextureData2DVector;
+
+ TextureData2DVector maVector;
+ sal_uInt32 mnUsedEntries;
+
+public:
+ explicit TextureCoordinate2D(sal_uInt32 nCount)
+ : maVector(nCount),
+ mnUsedEntries(0L)
+ {
+ }
+
+ explicit TextureCoordinate2D(const TextureCoordinate2D& rOriginal)
+ : maVector(rOriginal.maVector),
+ mnUsedEntries(rOriginal.mnUsedEntries)
+ {
+ }
+
+ TextureCoordinate2D(const TextureCoordinate2D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : maVector(),
+ mnUsedEntries(0L)
+ {
+ TextureData2DVector::const_iterator aStart(rOriginal.maVector.begin());
+ aStart += nIndex;
+ TextureData2DVector::const_iterator aEnd(aStart);
+ aEnd += nCount;
+ maVector.reserve(nCount);
+
+ for(; aStart != aEnd; aStart++)
+ {
+ if(!aStart->equalZero())
+ mnUsedEntries++;
+
+ maVector.push_back(*aStart);
+ }
+ }
+
+ ~TextureCoordinate2D()
+ {
+ }
+
+ sal_uInt32 count() const
+ {
+ return maVector.size();
+ }
+
+ bool operator==(const TextureCoordinate2D& rCandidate) const
+ {
+ return (maVector == rCandidate.maVector);
+ }
+
+ bool isUsed() const
+ {
+ return (0L != mnUsedEntries);
+ }
+
+ const ::basegfx::B2DPoint& getTextureCoordinate(sal_uInt32 nIndex) const
+ {
+ return maVector[nIndex];
+ }
+
+ void setTextureCoordinate(sal_uInt32 nIndex, const ::basegfx::B2DPoint& rValue)
+ {
+ bool bWasUsed(mnUsedEntries && !maVector[nIndex].equalZero());
+ bool bIsUsed(!rValue.equalZero());
+
+ if(bWasUsed)
+ {
+ if(bIsUsed)
+ {
+ maVector[nIndex] = rValue;
+ }
+ else
+ {
+ maVector[nIndex] = ::basegfx::B2DPoint::getEmptyPoint();
+ mnUsedEntries--;
+ }
+ }
+ else
+ {
+ if(bIsUsed)
+ {
+ maVector[nIndex] = rValue;
+ mnUsedEntries++;
+ }
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const ::basegfx::B2DPoint& rValue, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // add nCount copies of rValue
+ TextureData2DVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ maVector.insert(aIndex, nCount, rValue);
+
+ if(!rValue.equalZero())
+ mnUsedEntries += nCount;
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const TextureCoordinate2D& rSource)
+ {
+ const sal_uInt32 nCount(rSource.maVector.size());
+
+ if(nCount)
+ {
+ // insert data
+ TextureData2DVector::iterator aIndex(maVector.begin());
+ aIndex += nIndex;
+ TextureData2DVector::const_iterator aStart(rSource.maVector.begin());
+ TextureData2DVector::const_iterator aEnd(rSource.maVector.end());
+ maVector.insert(aIndex, aStart, aEnd);
+
+ for(; aStart != aEnd; aStart++)
+ {
+ if(!aStart->equalZero())
+ mnUsedEntries++;
+ }
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ const TextureData2DVector::iterator aDeleteStart(maVector.begin() + nIndex);
+ const TextureData2DVector::iterator aDeleteEnd(aDeleteStart + nCount);
+ TextureData2DVector::const_iterator aStart(aDeleteStart);
+
+ for(; mnUsedEntries && aStart != aDeleteEnd; aStart++)
+ {
+ if(!aStart->equalZero())
+ mnUsedEntries--;
+ }
+
+ // remove point data
+ maVector.erase(aDeleteStart, aDeleteEnd);
+ }
+ }
+
+ void flip()
+ {
+ if(maVector.size() > 1)
+ {
+ const sal_uInt32 nHalfSize(maVector.size() >> 1L);
+ TextureData2DVector::iterator aStart(maVector.begin());
+ TextureData2DVector::iterator aEnd(maVector.end() - 1L);
+
+ for(sal_uInt32 a(0); a < nHalfSize; a++)
+ {
+ ::std::swap(*aStart, *aEnd);
+ aStart++;
+ aEnd--;
+ }
+ }
+ }
+
+ void transform(const ::basegfx::B2DHomMatrix& rMatrix)
+ {
+ TextureData2DVector::iterator aStart(maVector.begin());
+ TextureData2DVector::iterator aEnd(maVector.end());
+
+ for(; aStart != aEnd; aStart++)
+ {
+ (*aStart) *= rMatrix;
+ }
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class ImplB3DPolygon
+{
+ // The point vector. This vector exists always and defines the
+ // count of members.
+ CoordinateDataArray3D maPoints;
+
+ // The BColor vector. This vectors are created on demand
+ // and may be zero.
+ BColorArray* mpBColors;
+
+ // The Normals vector. This vectors are created on demand
+ // and may be zero.
+ NormalsArray3D* mpNormals;
+
+ // The TextureCoordinates vector. This vectors are created on demand
+ // and may be zero.
+ TextureCoordinate2D* mpTextureCoordiantes;
+
+ // The calculated plane normal. mbPlaneNormalValid says if it's valid.
+ ::basegfx::B3DVector maPlaneNormal;
+
+ // bitfield
+ // flag which decides if this polygon is opened or closed
+ unsigned mbIsClosed : 1;
+
+ // flag which says if maPlaneNormal is up-to-date
+ unsigned mbPlaneNormalValid : 1;
+
+protected:
+ void invalidatePlaneNormal()
+ {
+ if(mbPlaneNormalValid)
+ {
+ mbPlaneNormalValid = false;
+ }
+ }
+
+public:
+ // This constructor is only used from the static identity polygon, thus
+ // the RefCount is set to 1 to never 'delete' this static incarnation.
+ ImplB3DPolygon()
+ : maPoints(0L),
+ mpBColors(0L),
+ mpNormals(0L),
+ mpTextureCoordiantes(0L),
+ maPlaneNormal(::basegfx::B3DVector::getEmptyVector()),
+ mbIsClosed(false),
+ mbPlaneNormalValid(true)
+ {
+ // complete initialization with defaults
+ }
+
+ ImplB3DPolygon(const ImplB3DPolygon& rToBeCopied)
+ : maPoints(rToBeCopied.maPoints),
+ mpBColors(0L),
+ mpNormals(0L),
+ mpTextureCoordiantes(0L),
+ maPlaneNormal(rToBeCopied.maPlaneNormal),
+ mbIsClosed(rToBeCopied.mbIsClosed),
+ mbPlaneNormalValid(rToBeCopied.mbPlaneNormalValid)
+ {
+ // complete initialization using copy
+ if(rToBeCopied.mpBColors && rToBeCopied.mpBColors->isUsed())
+ {
+ mpBColors = new BColorArray(*rToBeCopied.mpBColors);
+ }
+
+ if(rToBeCopied.mpNormals && rToBeCopied.mpNormals->isUsed())
+ {
+ mpNormals = new NormalsArray3D(*rToBeCopied.mpNormals);
+ }
+
+ if(rToBeCopied.mpTextureCoordiantes && rToBeCopied.mpTextureCoordiantes->isUsed())
+ {
+ mpTextureCoordiantes = new TextureCoordinate2D(*rToBeCopied.mpTextureCoordiantes);
+ }
+ }
+
+ ImplB3DPolygon(const ImplB3DPolygon& rToBeCopied, sal_uInt32 nIndex, sal_uInt32 nCount)
+ : maPoints(rToBeCopied.maPoints, nIndex, nCount),
+ mpBColors(0L),
+ mpNormals(0L),
+ mpTextureCoordiantes(0L),
+ maPlaneNormal(::basegfx::B3DVector::getEmptyVector()),
+ mbIsClosed(rToBeCopied.mbIsClosed),
+ mbPlaneNormalValid(false)
+ {
+ // complete initialization using partly copy
+ if(rToBeCopied.mpBColors && rToBeCopied.mpBColors->isUsed())
+ {
+ mpBColors = new BColorArray(*rToBeCopied.mpBColors, nIndex, nCount);
+
+ if(!mpBColors->isUsed())
+ {
+ delete mpBColors;
+ mpBColors = 0L;
+ }
+ }
+
+ if(rToBeCopied.mpNormals && rToBeCopied.mpNormals->isUsed())
+ {
+ mpNormals = new NormalsArray3D(*rToBeCopied.mpNormals, nIndex, nCount);
+
+ if(!mpNormals->isUsed())
+ {
+ delete mpNormals;
+ mpNormals = 0L;
+ }
+ }
+
+ if(rToBeCopied.mpTextureCoordiantes && rToBeCopied.mpTextureCoordiantes->isUsed())
+ {
+ mpTextureCoordiantes = new TextureCoordinate2D(*rToBeCopied.mpTextureCoordiantes, nIndex, nCount);
+
+ if(!mpTextureCoordiantes->isUsed())
+ {
+ delete mpTextureCoordiantes;
+ mpTextureCoordiantes = 0L;
+ }
+ }
+ }
+
+ ~ImplB3DPolygon()
+ {
+ if(mpBColors)
+ {
+ delete mpBColors;
+ mpBColors = 0L;
+ }
+
+ if(mpNormals)
+ {
+ delete mpNormals;
+ mpNormals = 0L;
+ }
+
+ if(mpTextureCoordiantes)
+ {
+ delete mpTextureCoordiantes;
+ mpTextureCoordiantes = 0L;
+ }
+ }
+
+ sal_uInt32 count() const
+ {
+ return maPoints.count();
+ }
+
+ bool isClosed() const
+ {
+ return mbIsClosed;
+ }
+
+ void setClosed(bool bNew)
+ {
+ if(bNew != (bool)mbIsClosed)
+ {
+ mbIsClosed = bNew;
+ }
+ }
+
+ inline bool impBColorsAreEqual(const ImplB3DPolygon& rCandidate) const
+ {
+ bool bBColorsAreEqual(true);
+
+ if(mpBColors)
+ {
+ if(rCandidate.mpBColors)
+ {
+ bBColorsAreEqual = (*mpBColors == *rCandidate.mpBColors);
+ }
+ else
+ {
+ // candidate has no BColors, so it's assumed all unused.
+ bBColorsAreEqual = !mpBColors->isUsed();
+ }
+ }
+ else
+ {
+ if(rCandidate.mpBColors)
+ {
+ // we have no TextureCoordiantes, so it's assumed all unused.
+ bBColorsAreEqual = !rCandidate.mpBColors->isUsed();
+ }
+ }
+
+ return bBColorsAreEqual;
+ }
+
+ inline bool impNormalsAreEqual(const ImplB3DPolygon& rCandidate) const
+ {
+ bool bNormalsAreEqual(true);
+
+ if(mpNormals)
+ {
+ if(rCandidate.mpNormals)
+ {
+ bNormalsAreEqual = (*mpNormals == *rCandidate.mpNormals);
+ }
+ else
+ {
+ // candidate has no normals, so it's assumed all unused.
+ bNormalsAreEqual = !mpNormals->isUsed();
+ }
+ }
+ else
+ {
+ if(rCandidate.mpNormals)
+ {
+ // we have no normals, so it's assumed all unused.
+ bNormalsAreEqual = !rCandidate.mpNormals->isUsed();
+ }
+ }
+
+ return bNormalsAreEqual;
+ }
+
+ inline bool impTextureCoordinatesAreEqual(const ImplB3DPolygon& rCandidate) const
+ {
+ bool bTextureCoordinatesAreEqual(true);
+
+ if(mpTextureCoordiantes)
+ {
+ if(rCandidate.mpTextureCoordiantes)
+ {
+ bTextureCoordinatesAreEqual = (*mpTextureCoordiantes == *rCandidate.mpTextureCoordiantes);
+ }
+ else
+ {
+ // candidate has no TextureCoordinates, so it's assumed all unused.
+ bTextureCoordinatesAreEqual = !mpTextureCoordiantes->isUsed();
+ }
+ }
+ else
+ {
+ if(rCandidate.mpTextureCoordiantes)
+ {
+ // we have no TextureCoordiantes, so it's assumed all unused.
+ bTextureCoordinatesAreEqual = !rCandidate.mpTextureCoordiantes->isUsed();
+ }
+ }
+
+ return bTextureCoordinatesAreEqual;
+ }
+
+ bool operator==(const ImplB3DPolygon& rCandidate) const
+ {
+ if(mbIsClosed == rCandidate.mbIsClosed)
+ {
+ if(maPoints == rCandidate.maPoints)
+ {
+ if(impBColorsAreEqual(rCandidate))
+ {
+ if(impNormalsAreEqual(rCandidate))
+ {
+ if(impTextureCoordinatesAreEqual(rCandidate))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ const ::basegfx::B3DPoint& getPoint(sal_uInt32 nIndex) const
+ {
+ return maPoints.getCoordinate(nIndex);
+ }
+
+ void setPoint(sal_uInt32 nIndex, const ::basegfx::B3DPoint& rValue)
+ {
+ maPoints.setCoordinate(nIndex, rValue);
+ invalidatePlaneNormal();
+ }
+
+ void insert(sal_uInt32 nIndex, const ::basegfx::B3DPoint& rPoint, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ CoordinateData3D aCoordinate(rPoint);
+ maPoints.insert(nIndex, aCoordinate, nCount);
+ invalidatePlaneNormal();
+
+ if(mpBColors)
+ {
+ mpBColors->insert(nIndex, ::basegfx::BColor::getEmptyBColor(), nCount);
+ }
+
+ if(mpNormals)
+ {
+ mpNormals->insert(nIndex, ::basegfx::B3DVector::getEmptyVector(), nCount);
+ }
+
+ if(mpTextureCoordiantes)
+ {
+ mpTextureCoordiantes->insert(nIndex, ::basegfx::B2DPoint::getEmptyPoint(), nCount);
+ }
+ }
+ }
+
+ const ::basegfx::BColor& getBColor(sal_uInt32 nIndex) const
+ {
+ if(mpBColors)
+ {
+ return mpBColors->getBColor(nIndex);
+ }
+ else
+ {
+ return ::basegfx::BColor::getEmptyBColor();
+ }
+ }
+
+ void setBColor(sal_uInt32 nIndex, const ::basegfx::BColor& rValue)
+ {
+ if(!mpBColors)
+ {
+ if(!rValue.equalZero())
+ {
+ mpBColors = new BColorArray(maPoints.count());
+ mpBColors->setBColor(nIndex, rValue);
+ }
+ }
+ else
+ {
+ mpBColors->setBColor(nIndex, rValue);
+
+ if(!mpBColors->isUsed())
+ {
+ delete mpBColors;
+ mpBColors = 0L;
+ }
+ }
+ }
+
+ bool areBColorsUsed() const
+ {
+ return (mpBColors && mpBColors->isUsed());
+ }
+
+ void clearBColors()
+ {
+ if(mpBColors)
+ {
+ delete mpBColors;
+ mpBColors = 0L;
+ }
+ }
+
+ const ::basegfx::B3DVector& getNormal() const
+ {
+ if(!mbPlaneNormalValid)
+ {
+ const_cast< ImplB3DPolygon* >(this)->maPlaneNormal = maPoints.getNormal();
+ const_cast< ImplB3DPolygon* >(this)->mbPlaneNormalValid = true;
+ }
+
+ return maPlaneNormal;
+ }
+
+ const ::basegfx::B3DVector& getNormal(sal_uInt32 nIndex) const
+ {
+ if(mpNormals)
+ {
+ return mpNormals->getNormal(nIndex);
+ }
+ else
+ {
+ return ::basegfx::B3DVector::getEmptyVector();
+ }
+ }
+
+ void setNormal(sal_uInt32 nIndex, const ::basegfx::B3DVector& rValue)
+ {
+ if(!mpNormals)
+ {
+ if(!rValue.equalZero())
+ {
+ mpNormals = new NormalsArray3D(maPoints.count());
+ mpNormals->setNormal(nIndex, rValue);
+ }
+ }
+ else
+ {
+ mpNormals->setNormal(nIndex, rValue);
+
+ if(!mpNormals->isUsed())
+ {
+ delete mpNormals;
+ mpNormals = 0L;
+ }
+ }
+ }
+
+ void transformNormals(const ::basegfx::B3DHomMatrix& rMatrix)
+ {
+ if(mpNormals)
+ {
+ mpNormals->transform(rMatrix);
+ }
+ }
+
+ bool areNormalsUsed() const
+ {
+ return (mpNormals && mpNormals->isUsed());
+ }
+
+ void clearNormals()
+ {
+ if(mpNormals)
+ {
+ delete mpNormals;
+ mpNormals = 0L;
+ }
+ }
+
+ const ::basegfx::B2DPoint& getTextureCoordinate(sal_uInt32 nIndex) const
+ {
+ if(mpTextureCoordiantes)
+ {
+ return mpTextureCoordiantes->getTextureCoordinate(nIndex);
+ }
+ else
+ {
+ return ::basegfx::B2DPoint::getEmptyPoint();
+ }
+ }
+
+ void setTextureCoordinate(sal_uInt32 nIndex, const ::basegfx::B2DPoint& rValue)
+ {
+ if(!mpTextureCoordiantes)
+ {
+ if(!rValue.equalZero())
+ {
+ mpTextureCoordiantes = new TextureCoordinate2D(maPoints.count());
+ mpTextureCoordiantes->setTextureCoordinate(nIndex, rValue);
+ }
+ }
+ else
+ {
+ mpTextureCoordiantes->setTextureCoordinate(nIndex, rValue);
+
+ if(!mpTextureCoordiantes->isUsed())
+ {
+ delete mpTextureCoordiantes;
+ mpTextureCoordiantes = 0L;
+ }
+ }
+ }
+
+ bool areTextureCoordinatesUsed() const
+ {
+ return (mpTextureCoordiantes && mpTextureCoordiantes->isUsed());
+ }
+
+ void clearTextureCoordinates()
+ {
+ if(mpTextureCoordiantes)
+ {
+ delete mpTextureCoordiantes;
+ mpTextureCoordiantes = 0L;
+ }
+ }
+
+ void transformTextureCoordinates(const ::basegfx::B2DHomMatrix& rMatrix)
+ {
+ if(mpTextureCoordiantes)
+ {
+ mpTextureCoordiantes->transform(rMatrix);
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const ImplB3DPolygon& rSource)
+ {
+ const sal_uInt32 nCount(rSource.maPoints.count());
+
+ if(nCount)
+ {
+ maPoints.insert(nIndex, rSource.maPoints);
+ invalidatePlaneNormal();
+
+ if(rSource.mpBColors && rSource.mpBColors->isUsed())
+ {
+ if(!mpBColors)
+ {
+ mpBColors = new BColorArray(maPoints.count());
+ }
+
+ mpBColors->insert(nIndex, *rSource.mpBColors);
+ }
+ else
+ {
+ if(mpBColors)
+ {
+ mpBColors->insert(nIndex, ::basegfx::BColor::getEmptyBColor(), nCount);
+ }
+ }
+
+ if(rSource.mpNormals && rSource.mpNormals->isUsed())
+ {
+ if(!mpNormals)
+ {
+ mpNormals = new NormalsArray3D(maPoints.count());
+ }
+
+ mpNormals->insert(nIndex, *rSource.mpNormals);
+ }
+ else
+ {
+ if(mpNormals)
+ {
+ mpNormals->insert(nIndex, ::basegfx::B3DVector::getEmptyVector(), nCount);
+ }
+ }
+
+ if(rSource.mpTextureCoordiantes && rSource.mpTextureCoordiantes->isUsed())
+ {
+ if(!mpTextureCoordiantes)
+ {
+ mpTextureCoordiantes = new TextureCoordinate2D(maPoints.count());
+ }
+
+ mpTextureCoordiantes->insert(nIndex, *rSource.mpTextureCoordiantes);
+ }
+ else
+ {
+ if(mpTextureCoordiantes)
+ {
+ mpTextureCoordiantes->insert(nIndex, ::basegfx::B2DPoint::getEmptyPoint(), nCount);
+ }
+ }
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ maPoints.remove(nIndex, nCount);
+ invalidatePlaneNormal();
+
+ if(mpBColors)
+ {
+ mpBColors->remove(nIndex, nCount);
+
+ if(!mpBColors->isUsed())
+ {
+ delete mpBColors;
+ mpBColors = 0L;
+ }
+ }
+
+ if(mpNormals)
+ {
+ mpNormals->remove(nIndex, nCount);
+
+ if(!mpNormals->isUsed())
+ {
+ delete mpNormals;
+ mpNormals = 0L;
+ }
+ }
+
+ if(mpTextureCoordiantes)
+ {
+ mpTextureCoordiantes->remove(nIndex, nCount);
+
+ if(!mpTextureCoordiantes->isUsed())
+ {
+ delete mpTextureCoordiantes;
+ mpTextureCoordiantes = 0L;
+ }
+ }
+ }
+ }
+
+ void flip()
+ {
+ if(maPoints.count() > 1)
+ {
+ maPoints.flip();
+
+ if(mbPlaneNormalValid)
+ {
+ // mirror plane normal
+ maPlaneNormal = -maPlaneNormal;
+ }
+
+ if(mpBColors)
+ {
+ mpBColors->flip();
+ }
+
+ if(mpNormals)
+ {
+ mpNormals->flip();
+ }
+
+ if(mpTextureCoordiantes)
+ {
+ mpTextureCoordiantes->flip();
+ }
+ }
+ }
+
+ bool hasDoublePoints() const
+ {
+ if(mbIsClosed)
+ {
+ // check for same start and end point
+ const sal_uInt32 nIndex(maPoints.count() - 1L);
+
+ if(maPoints.getCoordinate(0L) == maPoints.getCoordinate(nIndex))
+ {
+ const bool bBColorEqual(!mpBColors || (mpBColors->getBColor(0L) == mpBColors->getBColor(nIndex)));
+
+ if(bBColorEqual)
+ {
+ const bool bNormalsEqual(!mpNormals || (mpNormals->getNormal(0L) == mpNormals->getNormal(nIndex)));
+
+ if(bNormalsEqual)
+ {
+ const bool bTextureCoordinatesEqual(!mpTextureCoordiantes || (mpTextureCoordiantes->getTextureCoordinate(0L) == mpTextureCoordiantes->getTextureCoordinate(nIndex)));
+
+ if(bTextureCoordinatesEqual)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ // test for range
+ for(sal_uInt32 a(0L); a < maPoints.count() - 1L; a++)
+ {
+ if(maPoints.getCoordinate(a) == maPoints.getCoordinate(a + 1L))
+ {
+ const bool bBColorEqual(!mpBColors || (mpBColors->getBColor(a) == mpBColors->getBColor(a + 1L)));
+
+ if(bBColorEqual)
+ {
+ const bool bNormalsEqual(!mpNormals || (mpNormals->getNormal(a) == mpNormals->getNormal(a + 1L)));
+
+ if(bNormalsEqual)
+ {
+ const bool bTextureCoordinatesEqual(!mpTextureCoordiantes || (mpTextureCoordiantes->getTextureCoordinate(a) == mpTextureCoordiantes->getTextureCoordinate(a + 1L)));
+
+ if(bTextureCoordinatesEqual)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ void removeDoublePointsAtBeginEnd()
+ {
+ // Only remove DoublePoints at Begin and End when poly is closed
+ if(mbIsClosed)
+ {
+ bool bRemove;
+
+ do
+ {
+ bRemove = false;
+
+ if(maPoints.count() > 1L)
+ {
+ const sal_uInt32 nIndex(maPoints.count() - 1L);
+ bRemove = (maPoints.getCoordinate(0L) == maPoints.getCoordinate(nIndex));
+
+ if(bRemove && mpBColors && !(mpBColors->getBColor(0L) == mpBColors->getBColor(nIndex)))
+ {
+ bRemove = false;
+ }
+
+ if(bRemove && mpNormals && !(mpNormals->getNormal(0L) == mpNormals->getNormal(nIndex)))
+ {
+ bRemove = false;
+ }
+
+ if(bRemove && mpTextureCoordiantes && !(mpTextureCoordiantes->getTextureCoordinate(0L) == mpTextureCoordiantes->getTextureCoordinate(nIndex)))
+ {
+ bRemove = false;
+ }
+ }
+
+ if(bRemove)
+ {
+ const sal_uInt32 nIndex(maPoints.count() - 1L);
+ remove(nIndex, 1L);
+ }
+ } while(bRemove);
+ }
+ }
+
+ void removeDoublePointsWholeTrack()
+ {
+ sal_uInt32 nIndex(0L);
+
+ // test as long as there are at least two points and as long as the index
+ // is smaller or equal second last point
+ while((maPoints.count() > 1L) && (nIndex <= maPoints.count() - 2L))
+ {
+ const sal_uInt32 nNextIndex(nIndex + 1L);
+ bool bRemove(maPoints.getCoordinate(nIndex) == maPoints.getCoordinate(nNextIndex));
+
+ if(bRemove && mpBColors && !(mpBColors->getBColor(nIndex) == mpBColors->getBColor(nNextIndex)))
+ {
+ bRemove = false;
+ }
+
+ if(bRemove && mpNormals && !(mpNormals->getNormal(nIndex) == mpNormals->getNormal(nNextIndex)))
+ {
+ bRemove = false;
+ }
+
+ if(bRemove && mpTextureCoordiantes && !(mpTextureCoordiantes->getTextureCoordinate(nIndex) == mpTextureCoordiantes->getTextureCoordinate(nNextIndex)))
+ {
+ bRemove = false;
+ }
+
+ if(bRemove)
+ {
+ // if next is same as index and the control vectors are unused, delete index
+ remove(nIndex, 1L);
+ }
+ else
+ {
+ // if different, step forward
+ nIndex++;
+ }
+ }
+ }
+
+ void transform(const ::basegfx::B3DHomMatrix& rMatrix)
+ {
+ maPoints.transform(rMatrix);
+
+ // Here, it seems to be possible to transform a valid plane normal and to avoid
+ // invalidation, but it's not true. If the transformation contains shears or e.g.
+ // perspective projection, the orthogonality to the transformed plane will not
+ // be preserved. It may be possible to test that at the matrix to not invalidate in
+ // all cases or to extract a matrix which does not 'shear' the vector which is
+ // a normal in this case. As long as this is not sure, i will just invalidate.
+ invalidatePlaneNormal();
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace { struct DefaultPolygon : public rtl::Static< B3DPolygon::ImplType,
+ DefaultPolygon > {}; }
+
+ B3DPolygon::B3DPolygon() :
+ mpPolygon(DefaultPolygon::get())
+ {
+ }
+
+ B3DPolygon::B3DPolygon(const B3DPolygon& rPolygon) :
+ mpPolygon(rPolygon.mpPolygon)
+ {
+ }
+
+ B3DPolygon::B3DPolygon(const B3DPolygon& rPolygon, sal_uInt32 nIndex, sal_uInt32 nCount) :
+ mpPolygon(ImplB3DPolygon(*rPolygon.mpPolygon, nIndex, nCount))
+ {
+ // TODO(P2): one extra temporary here (cow_wrapper copies
+ // given ImplB3DPolygon into its internal impl_t wrapper type)
+ OSL_ENSURE(nIndex + nCount > rPolygon.mpPolygon->count(), "B3DPolygon constructor outside range (!)");
+ }
+
+ B3DPolygon::~B3DPolygon()
+ {
+ }
+
+ B3DPolygon& B3DPolygon::operator=(const B3DPolygon& rPolygon)
+ {
+ mpPolygon = rPolygon.mpPolygon;
+ return *this;
+ }
+
+ void B3DPolygon::makeUnique()
+ {
+ mpPolygon.make_unique();
+ }
+
+ bool B3DPolygon::operator==(const B3DPolygon& rPolygon) const
+ {
+ if(mpPolygon.same_object(rPolygon.mpPolygon))
+ return true;
+
+ return (*mpPolygon == *rPolygon.mpPolygon);
+ }
+
+ bool B3DPolygon::operator!=(const B3DPolygon& rPolygon) const
+ {
+ return !(*this == rPolygon);
+ }
+
+ sal_uInt32 B3DPolygon::count() const
+ {
+ return mpPolygon->count();
+ }
+
+ basegfx::B3DPoint B3DPolygon::getB3DPoint(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
+
+ return mpPolygon->getPoint(nIndex);
+ }
+
+ void B3DPolygon::setB3DPoint(sal_uInt32 nIndex, const basegfx::B3DPoint& rValue)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
+
+ if(getB3DPoint(nIndex) != rValue)
+ mpPolygon->setPoint(nIndex, rValue);
+ }
+
+ BColor B3DPolygon::getBColor(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
+
+ return mpPolygon->getBColor(nIndex);
+ }
+
+ void B3DPolygon::setBColor(sal_uInt32 nIndex, const BColor& rValue)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
+
+ if(mpPolygon->getBColor(nIndex) != rValue)
+ mpPolygon->setBColor(nIndex, rValue);
+ }
+
+ bool B3DPolygon::areBColorsUsed() const
+ {
+ return mpPolygon->areBColorsUsed();
+ }
+
+ void B3DPolygon::clearBColors()
+ {
+ if(mpPolygon->areBColorsUsed())
+ mpPolygon->clearBColors();
+ }
+
+ B3DVector B3DPolygon::getNormal() const
+ {
+ return mpPolygon->getNormal();
+ }
+
+ B3DVector B3DPolygon::getNormal(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
+
+ return mpPolygon->getNormal(nIndex);
+ }
+
+ void B3DPolygon::setNormal(sal_uInt32 nIndex, const B3DVector& rValue)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
+
+ if(mpPolygon->getNormal(nIndex) != rValue)
+ mpPolygon->setNormal(nIndex, rValue);
+ }
+
+ void B3DPolygon::transformNormals(const B3DHomMatrix& rMatrix)
+ {
+ if(mpPolygon->areNormalsUsed() && !rMatrix.isIdentity())
+ mpPolygon->transformNormals(rMatrix);
+ }
+
+ bool B3DPolygon::areNormalsUsed() const
+ {
+ return mpPolygon->areNormalsUsed();
+ }
+
+ void B3DPolygon::clearNormals()
+ {
+ if(mpPolygon->areNormalsUsed())
+ mpPolygon->clearNormals();
+ }
+
+ B2DPoint B3DPolygon::getTextureCoordinate(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
+
+ return mpPolygon->getTextureCoordinate(nIndex);
+ }
+
+ void B3DPolygon::setTextureCoordinate(sal_uInt32 nIndex, const B2DPoint& rValue)
+ {
+ OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
+
+ if(mpPolygon->getTextureCoordinate(nIndex) != rValue)
+ mpPolygon->setTextureCoordinate(nIndex, rValue);
+ }
+
+ void B3DPolygon::transformTextureCoordiantes(const B2DHomMatrix& rMatrix)
+ {
+ if(mpPolygon->areTextureCoordinatesUsed() && !rMatrix.isIdentity())
+ mpPolygon->transformTextureCoordinates(rMatrix);
+ }
+
+ bool B3DPolygon::areTextureCoordinatesUsed() const
+ {
+ return mpPolygon->areTextureCoordinatesUsed();
+ }
+
+ void B3DPolygon::clearTextureCoordinates()
+ {
+ if(mpPolygon->areTextureCoordinatesUsed())
+ mpPolygon->clearTextureCoordinates();
+ }
+
+ void B3DPolygon::insert(sal_uInt32 nIndex, const ::basegfx::B3DPoint& rPoint, sal_uInt32 nCount)
+ {
+ OSL_ENSURE(nIndex <= mpPolygon->count(), "B3DPolygon Insert outside range (!)");
+
+ if(nCount)
+ mpPolygon->insert(nIndex, rPoint, nCount);
+ }
+
+ void B3DPolygon::append(const basegfx::B3DPoint& rPoint, sal_uInt32 nCount)
+ {
+ if(nCount)
+ mpPolygon->insert(mpPolygon->count(), rPoint, nCount);
+ }
+
+ void B3DPolygon::insert(sal_uInt32 nIndex, const B3DPolygon& rPoly, sal_uInt32 nIndex2, sal_uInt32 nCount)
+ {
+ OSL_ENSURE(nIndex <= mpPolygon->count(), "B3DPolygon Insert outside range (!)");
+
+ if(rPoly.count())
+ {
+ if(!nCount)
+ {
+ nCount = rPoly.count();
+ }
+
+ if(0L == nIndex2 && nCount == rPoly.count())
+ {
+ mpPolygon->insert(nIndex, *rPoly.mpPolygon);
+ }
+ else
+ {
+ OSL_ENSURE(nIndex2 + nCount <= rPoly.mpPolygon->count(), "B3DPolygon Insert outside range (!)");
+ ImplB3DPolygon aTempPoly(*rPoly.mpPolygon, nIndex2, nCount);
+ mpPolygon->insert(nIndex, aTempPoly);
+ }
+ }
+ }
+
+ void B3DPolygon::append(const B3DPolygon& rPoly, sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(rPoly.count())
+ {
+ if(!nCount)
+ {
+ nCount = rPoly.count();
+ }
+
+ if(0L == nIndex && nCount == rPoly.count())
+ {
+ mpPolygon->insert(mpPolygon->count(), *rPoly.mpPolygon);
+ }
+ else
+ {
+ OSL_ENSURE(nIndex + nCount <= rPoly.mpPolygon->count(), "B3DPolygon Append outside range (!)");
+ ImplB3DPolygon aTempPoly(*rPoly.mpPolygon, nIndex, nCount);
+ mpPolygon->insert(mpPolygon->count(), aTempPoly);
+ }
+ }
+ }
+
+ void B3DPolygon::remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ OSL_ENSURE(nIndex + nCount <= mpPolygon->count(), "B3DPolygon Remove outside range (!)");
+
+ if(nCount)
+ mpPolygon->remove(nIndex, nCount);
+ }
+
+ void B3DPolygon::clear()
+ {
+ mpPolygon = DefaultPolygon::get();
+ }
+
+ bool B3DPolygon::isClosed() const
+ {
+ return mpPolygon->isClosed();
+ }
+
+ void B3DPolygon::setClosed(bool bNew)
+ {
+ if(isClosed() != bNew)
+ mpPolygon->setClosed(bNew);
+ }
+
+ void B3DPolygon::flip()
+ {
+ if(count() > 1)
+ mpPolygon->flip();
+ }
+
+ bool B3DPolygon::hasDoublePoints() const
+ {
+ return (mpPolygon->count() > 1L && mpPolygon->hasDoublePoints());
+ }
+
+ void B3DPolygon::removeDoublePoints()
+ {
+ if(hasDoublePoints())
+ {
+ mpPolygon->removeDoublePointsAtBeginEnd();
+ mpPolygon->removeDoublePointsWholeTrack();
+ }
+ }
+
+ void B3DPolygon::transform(const basegfx::B3DHomMatrix& rMatrix)
+ {
+ if(mpPolygon->count() && !rMatrix.isIdentity())
+ {
+ mpPolygon->transform(rMatrix);
+ }
+ }
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/polygon/b3dpolygonclipper.cxx b/basegfx/source/polygon/b3dpolygonclipper.cxx
new file mode 100644
index 000000000000..93c446ff57a5
--- /dev/null
+++ b/basegfx/source/polygon/b3dpolygonclipper.cxx
@@ -0,0 +1,578 @@
+/*************************************************************************
+ *
+ * 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: b3dpolygonclipper.cxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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/polygon/b3dpolygonclipper.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <basegfx/range/b3drange.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+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
diff --git a/basegfx/source/polygon/b3dpolygontools.cxx b/basegfx/source/polygon/b3dpolygontools.cxx
new file mode 100644
index 000000000000..52e0f0fcc36f
--- /dev/null
+++ b/basegfx/source/polygon/b3dpolygontools.cxx
@@ -0,0 +1,1266 @@
+/*************************************************************************
+ *
+ * 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: b3dpolygontools.cxx,v $
+ * $Revision: 1.11.4.1 $
+ *
+ * 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/polygon/b3dpolygontools.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/range/b3drange.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/tuple/b3ituple.hxx>
+#include <numeric>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ // B3DPolygon tools
+ void checkClosed(B3DPolygon& rCandidate)
+ {
+ while(rCandidate.count() > 1L
+ && rCandidate.getB3DPoint(0L).equal(rCandidate.getB3DPoint(rCandidate.count() - 1L)))
+ {
+ rCandidate.setClosed(true);
+ rCandidate.remove(rCandidate.count() - 1L);
+ }
+ }
+
+ // Get successor and predecessor indices. Returning the same index means there
+ // is none. Same for successor.
+ sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B3DPolygon& rCandidate)
+ {
+ OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
+
+ if(nIndex)
+ {
+ return nIndex - 1L;
+ }
+ else if(rCandidate.count())
+ {
+ return rCandidate.count() - 1L;
+ }
+ else
+ {
+ return nIndex;
+ }
+ }
+
+ sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B3DPolygon& rCandidate)
+ {
+ OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
+
+ if(nIndex + 1L < rCandidate.count())
+ {
+ return nIndex + 1L;
+ }
+ else
+ {
+ return 0L;
+ }
+ }
+
+ B3DRange getRange(const B3DPolygon& rCandidate)
+ {
+ B3DRange aRetval;
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B3DPoint aTestPoint(rCandidate.getB3DPoint(a));
+ aRetval.expand(aTestPoint);
+ }
+
+ return aRetval;
+ }
+
+ B3DVector getNormal(const B3DPolygon& rCandidate)
+ {
+ return rCandidate.getNormal();
+ }
+
+ B3DVector getPositiveOrientedNormal(const B3DPolygon& rCandidate)
+ {
+ B3DVector aRetval(rCandidate.getNormal());
+
+ if(ORIENTATION_NEGATIVE == getOrientation(rCandidate))
+ {
+ aRetval = -aRetval;
+ }
+
+ return aRetval;
+ }
+
+ B2VectorOrientation getOrientation(const B3DPolygon& rCandidate)
+ {
+ B2VectorOrientation eRetval(ORIENTATION_NEUTRAL);
+
+ if(rCandidate.count() > 2L)
+ {
+ const double fSignedArea(getSignedArea(rCandidate));
+
+ if(fSignedArea > 0.0)
+ {
+ eRetval = ORIENTATION_POSITIVE;
+ }
+ else if(fSignedArea < 0.0)
+ {
+ eRetval = ORIENTATION_NEGATIVE;
+ }
+ }
+
+ return eRetval;
+ }
+
+ double getSignedArea(const B3DPolygon& rCandidate)
+ {
+ double fRetval(0.0);
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 2)
+ {
+ const B3DVector aAbsNormal(absolute(getNormal(rCandidate)));
+ sal_uInt16 nCase(3); // default: ignore z
+
+ if(aAbsNormal.getX() > aAbsNormal.getY())
+ {
+ if(aAbsNormal.getX() > aAbsNormal.getZ())
+ {
+ nCase = 1; // ignore x
+ }
+ }
+ else if(aAbsNormal.getY() > aAbsNormal.getZ())
+ {
+ nCase = 2; // ignore y
+ }
+
+ B3DPoint aPreviousPoint(rCandidate.getB3DPoint(nPointCount - 1L));
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(a));
+
+ switch(nCase)
+ {
+ case 1: // ignore x
+ fRetval += aPreviousPoint.getZ() * aCurrentPoint.getY();
+ fRetval -= aPreviousPoint.getY() * aCurrentPoint.getZ();
+ break;
+ case 2: // ignore y
+ fRetval += aPreviousPoint.getX() * aCurrentPoint.getZ();
+ fRetval -= aPreviousPoint.getZ() * aCurrentPoint.getX();
+ break;
+ case 3: // ignore z
+ fRetval += aPreviousPoint.getX() * aCurrentPoint.getY();
+ fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX();
+ break;
+ }
+
+ // prepare next step
+ aPreviousPoint = aCurrentPoint;
+ }
+
+ switch(nCase)
+ {
+ case 1: // ignore x
+ fRetval /= 2.0 * aAbsNormal.getX();
+ break;
+ case 2: // ignore y
+ fRetval /= 2.0 * aAbsNormal.getY();
+ break;
+ case 3: // ignore z
+ fRetval /= 2.0 * aAbsNormal.getZ();
+ break;
+ }
+ }
+
+ return fRetval;
+ }
+
+ double getArea(const B3DPolygon& rCandidate)
+ {
+ double fRetval(0.0);
+
+ if(rCandidate.count() > 2)
+ {
+ fRetval = getSignedArea(rCandidate);
+ const double fZero(0.0);
+
+ if(fTools::less(fRetval, fZero))
+ {
+ fRetval = -fRetval;
+ }
+ }
+
+ return fRetval;
+ }
+
+ double getEdgeLength(const B3DPolygon& rCandidate, sal_uInt32 nIndex)
+ {
+ OSL_ENSURE(nIndex < rCandidate.count(), "getEdgeLength: Access to polygon out of range (!)");
+ double fRetval(0.0);
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nIndex < nPointCount)
+ {
+ if(rCandidate.isClosed() || ((nIndex + 1L) != nPointCount))
+ {
+ const sal_uInt32 nNextIndex(getIndexOfSuccessor(nIndex, rCandidate));
+ const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(nIndex));
+ const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
+ const B3DVector aVector(aNextPoint - aCurrentPoint);
+ fRetval = aVector.getLength();
+ }
+ }
+
+ return fRetval;
+ }
+
+ double getLength(const B3DPolygon& rCandidate)
+ {
+ double fRetval(0.0);
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 1L)
+ {
+ const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+
+ for(sal_uInt32 a(0L); a < nLoopCount; a++)
+ {
+ const sal_uInt32 nNextIndex(getIndexOfSuccessor(a, rCandidate));
+ const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(a));
+ const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
+ const B3DVector aVector(aNextPoint - aCurrentPoint);
+ fRetval += aVector.getLength();
+ }
+ }
+
+ return fRetval;
+ }
+
+ B3DPoint getPositionAbsolute(const B3DPolygon& rCandidate, double fDistance, double fLength)
+ {
+ B3DPoint aRetval;
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 1L)
+ {
+ sal_uInt32 nIndex(0L);
+ bool bIndexDone(false);
+ const double fZero(0.0);
+ double fEdgeLength(fZero);
+
+ // get length if not given
+ if(fTools::equalZero(fLength))
+ {
+ fLength = getLength(rCandidate);
+ }
+
+ // handle fDistance < 0.0
+ if(fTools::less(fDistance, fZero))
+ {
+ if(rCandidate.isClosed())
+ {
+ // if fDistance < 0.0 increment with multiple of fLength
+ sal_uInt32 nCount(sal_uInt32(-fDistance / fLength));
+ fDistance += double(nCount + 1L) * fLength;
+ }
+ else
+ {
+ // crop to polygon start
+ fDistance = fZero;
+ bIndexDone = true;
+ }
+ }
+
+ // handle fDistance >= fLength
+ if(fTools::moreOrEqual(fDistance, fLength))
+ {
+ if(rCandidate.isClosed())
+ {
+ // if fDistance >= fLength decrement with multiple of fLength
+ sal_uInt32 nCount(sal_uInt32(fDistance / fLength));
+ fDistance -= (double)(nCount) * fLength;
+ }
+ else
+ {
+ // crop to polygon end
+ fDistance = fZero;
+ nIndex = nPointCount - 1L;
+ bIndexDone = true;
+ }
+ }
+
+ // look for correct index. fDistance is now [0.0 .. fLength[
+ if(!bIndexDone)
+ {
+ do
+ {
+ // get length of next edge
+ fEdgeLength = getEdgeLength(rCandidate, nIndex);
+
+ if(fTools::moreOrEqual(fDistance, fEdgeLength))
+ {
+ // go to next edge
+ fDistance -= fEdgeLength;
+ nIndex++;
+ }
+ else
+ {
+ // it's on this edge, stop
+ bIndexDone = true;
+ }
+ } while (!bIndexDone);
+ }
+
+ // get the point using nIndex
+ aRetval = rCandidate.getB3DPoint(nIndex);
+
+ // if fDistance != 0.0, move that length on the edge. The edge
+ // length is in fEdgeLength.
+ if(!fTools::equalZero(fDistance))
+ {
+ sal_uInt32 nNextIndex(getIndexOfSuccessor(nIndex, rCandidate));
+ const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
+ double fRelative(fZero);
+
+ if(!fTools::equalZero(fEdgeLength))
+ {
+ fRelative = fDistance / fEdgeLength;
+ }
+
+ // add calculated average value to the return value
+ aRetval += interpolate(aRetval, aNextPoint, fRelative);
+ }
+ }
+
+ return aRetval;
+ }
+
+ B3DPoint getPositionRelative(const B3DPolygon& rCandidate, double fDistance, double fLength)
+ {
+ // get length if not given
+ if(fTools::equalZero(fLength))
+ {
+ fLength = getLength(rCandidate);
+ }
+
+ // multiply fDistance with real length to get absolute position and
+ // use getPositionAbsolute
+ return getPositionAbsolute(rCandidate, fDistance * fLength, fLength);
+ }
+
+ void applyLineDashing(const B3DPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B3DPolyPolygon* pLineTarget, B3DPolyPolygon* pGapTarget, double fDotDashLength)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+ const sal_uInt32 nDotDashCount(rDotDashArray.size());
+
+ if(fTools::lessOrEqual(fDotDashLength, 0.0))
+ {
+ fDotDashLength = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
+ }
+
+ if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount)
+ {
+ // clear targets
+ if(pLineTarget)
+ {
+ pLineTarget->clear();
+ }
+
+ if(pGapTarget)
+ {
+ pGapTarget->clear();
+ }
+
+ // prepare current edge's start
+ B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+
+ // prepare DotDashArray iteration and the line/gap switching bool
+ sal_uInt32 nDotDashIndex(0);
+ bool bIsLine(true);
+ double fDotDashMovingLength(rDotDashArray[0]);
+ B3DPolygon aSnippet;
+
+ // iterate over all edges
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ // update current edge
+ double fLastDotDashMovingLength(0.0);
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
+ const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength());
+
+ while(fTools::less(fDotDashMovingLength, fEdgeLength))
+ {
+ // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
+ const bool bHandleLine(bIsLine && pLineTarget);
+ const bool bHandleGap(!bIsLine && pGapTarget);
+
+ if(bHandleLine || bHandleGap)
+ {
+ if(!aSnippet.count())
+ {
+ aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
+ }
+
+ aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength));
+
+ if(bHandleLine)
+ {
+ pLineTarget->append(aSnippet);
+ }
+ else
+ {
+ pGapTarget->append(aSnippet);
+ }
+
+ aSnippet.clear();
+ }
+
+ // prepare next DotDashArray step and flip line/gap flag
+ fLastDotDashMovingLength = fDotDashMovingLength;
+ fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
+ bIsLine = !bIsLine;
+ }
+
+ // append snippet [fLastDotDashMovingLength, fEdgeLength]
+ const bool bHandleLine(bIsLine && pLineTarget);
+ const bool bHandleGap(!bIsLine && pGapTarget);
+
+ if(bHandleLine || bHandleGap)
+ {
+ if(!aSnippet.count())
+ {
+ aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
+ }
+
+ aSnippet.append(aNextPoint);
+ }
+
+ // prepare move to next edge
+ fDotDashMovingLength -= fEdgeLength;
+
+ // prepare next edge step (end point gets new start point)
+ aCurrentPoint = aNextPoint;
+ }
+
+ // append last intermediate results (if exists)
+ if(aSnippet.count())
+ {
+ if(bIsLine && pLineTarget)
+ {
+ pLineTarget->append(aSnippet);
+ }
+ else if(!bIsLine && pGapTarget)
+ {
+ pGapTarget->append(aSnippet);
+ }
+ }
+
+ // check if start and end polygon may be merged
+ if(pLineTarget)
+ {
+ const sal_uInt32 nCount(pLineTarget->count());
+
+ if(nCount > 1)
+ {
+ // these polygons were created above, there exists none with less than two points,
+ // thus dircet point access below is allowed
+ const B3DPolygon aFirst(pLineTarget->getB3DPolygon(0));
+ B3DPolygon aLast(pLineTarget->getB3DPolygon(nCount - 1));
+
+ if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
+ {
+ // start of first and end of last are the same -> merge them
+ aLast.append(aFirst);
+ aLast.removeDoublePoints();
+ pLineTarget->setB3DPolygon(0, aLast);
+ pLineTarget->remove(nCount - 1);
+ }
+ }
+ }
+
+ if(pGapTarget)
+ {
+ const sal_uInt32 nCount(pGapTarget->count());
+
+ if(nCount > 1)
+ {
+ // these polygons were created above, there exists none with less than two points,
+ // thus dircet point access below is allowed
+ const B3DPolygon aFirst(pGapTarget->getB3DPolygon(0));
+ B3DPolygon aLast(pGapTarget->getB3DPolygon(nCount - 1));
+
+ if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
+ {
+ // start of first and end of last are the same -> merge them
+ aLast.append(aFirst);
+ aLast.removeDoublePoints();
+ pGapTarget->setB3DPolygon(0, aLast);
+ pGapTarget->remove(nCount - 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ // parameters make no sense, just add source to targets
+ if(pLineTarget)
+ {
+ pLineTarget->append(rCandidate);
+ }
+
+ if(pGapTarget)
+ {
+ pGapTarget->append(rCandidate);
+ }
+ }
+ }
+
+ B3DPolygon applyDefaultNormalsSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter)
+ {
+ B3DPolygon aRetval(rCandidate);
+
+ for(sal_uInt32 a(0L); a < aRetval.count(); a++)
+ {
+ B3DVector aVector(aRetval.getB3DPoint(a) - rCenter);
+ aVector.normalize();
+ aRetval.setNormal(a, aVector);
+ }
+
+ return aRetval;
+ }
+
+ B3DPolygon invertNormals( const B3DPolygon& rCandidate)
+ {
+ B3DPolygon aRetval(rCandidate);
+
+ if(aRetval.areNormalsUsed())
+ {
+ for(sal_uInt32 a(0L); a < aRetval.count(); a++)
+ {
+ aRetval.setNormal(a, -aRetval.getNormal(a));
+ }
+ }
+
+ return aRetval;
+ }
+
+ B3DPolygon applyDefaultTextureCoordinatesParallel( const B3DPolygon& rCandidate, const B3DRange& rRange, bool bChangeX, bool bChangeY)
+ {
+ B3DPolygon aRetval(rCandidate);
+
+ if(bChangeX || bChangeY)
+ {
+ // create projection of standard texture coordinates in (X, Y) onto
+ // the 3d coordinates straight
+ const double fWidth(rRange.getWidth());
+ const double fHeight(rRange.getHeight());
+ const bool bWidthSet(!fTools::equalZero(fWidth));
+ const bool bHeightSet(!fTools::equalZero(fHeight));
+ const double fOne(1.0);
+
+ for(sal_uInt32 a(0L); a < aRetval.count(); a++)
+ {
+ const B3DPoint aPoint(aRetval.getB3DPoint(a));
+ B2DPoint aTextureCoordinate(aRetval.getTextureCoordinate(a));
+
+ if(bChangeX)
+ {
+ if(bWidthSet)
+ {
+ aTextureCoordinate.setX((aPoint.getX() - rRange.getMinX()) / fWidth);
+ }
+ else
+ {
+ aTextureCoordinate.setX(0.0);
+ }
+ }
+
+ if(bChangeY)
+ {
+ if(bHeightSet)
+ {
+ aTextureCoordinate.setY(fOne - ((aPoint.getY() - rRange.getMinY()) / fHeight));
+ }
+ else
+ {
+ aTextureCoordinate.setY(fOne);
+ }
+ }
+
+ aRetval.setTextureCoordinate(a, aTextureCoordinate);
+ }
+ }
+
+ return aRetval;
+ }
+
+ B3DPolygon applyDefaultTextureCoordinatesSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter, bool bChangeX, bool bChangeY)
+ {
+ B3DPolygon aRetval(rCandidate);
+
+ if(bChangeX || bChangeY)
+ {
+ // create texture coordinates using sphere projection to cartesian coordinates,
+ // use object's center as base
+ const double fOne(1.0);
+ const sal_uInt32 nPointCount(aRetval.count());
+ bool bPolarPoints(false);
+ sal_uInt32 a;
+
+ // create center cartesian coordinates to have a possibility to decide if on boundary
+ // transitions which value to choose
+ const B3DRange aPlaneRange(getRange(rCandidate));
+ const B3DPoint aPlaneCenter(aPlaneRange.getCenter() - rCenter);
+ const double fXCenter(fOne - ((atan2(aPlaneCenter.getZ(), aPlaneCenter.getX()) + F_PI) / F_2PI));
+
+ for(a = 0L; a < nPointCount; a++)
+ {
+ const B3DVector aVector(aRetval.getB3DPoint(a) - rCenter);
+ const double fY(fOne - ((atan2(aVector.getY(), aVector.getXZLength()) + F_PI2) / F_PI));
+ B2DPoint aTexCoor(aRetval.getTextureCoordinate(a));
+
+ if(fTools::equalZero(fY))
+ {
+ // point is a north polar point, no useful X-coordinate can be created.
+ if(bChangeY)
+ {
+ aTexCoor.setY(0.0);
+
+ if(bChangeX)
+ {
+ bPolarPoints = true;
+ }
+ }
+ }
+ else if(fTools::equal(fY, fOne))
+ {
+ // point is a south polar point, no useful X-coordinate can be created. Set
+ // Y-coordinte, though
+ if(bChangeY)
+ {
+ aTexCoor.setY(fOne);
+
+ if(bChangeX)
+ {
+ bPolarPoints = true;
+ }
+ }
+ }
+ else
+ {
+ double fX(fOne - ((atan2(aVector.getZ(), aVector.getX()) + F_PI) / F_2PI));
+
+ // correct cartesinan point coordiante dependent from center value
+ if(fX > fXCenter + 0.5)
+ {
+ fX -= fOne;
+ }
+ else if(fX < fXCenter - 0.5)
+ {
+ fX += fOne;
+ }
+
+ if(bChangeX)
+ {
+ aTexCoor.setX(fX);
+ }
+
+ if(bChangeY)
+ {
+ aTexCoor.setY(fY);
+ }
+ }
+
+ aRetval.setTextureCoordinate(a, aTexCoor);
+ }
+
+ if(bPolarPoints)
+ {
+ // correct X-texture coordinates if polar points are contained. Those
+ // coordinates cannot be correct, so use prev or next X-coordinate
+ for(a = 0L; a < nPointCount; a++)
+ {
+ B2DPoint aTexCoor(aRetval.getTextureCoordinate(a));
+
+ if(fTools::equalZero(aTexCoor.getY()) || fTools::equal(aTexCoor.getY(), fOne))
+ {
+ // get prev, next TexCoor and test for pole
+ const B2DPoint aPrevTexCoor(aRetval.getTextureCoordinate(a ? a - 1L : nPointCount - 1L));
+ const B2DPoint aNextTexCoor(aRetval.getTextureCoordinate((a + 1L) % nPointCount));
+ const bool bPrevPole(fTools::equalZero(aPrevTexCoor.getY()) || fTools::equal(aPrevTexCoor.getY(), fOne));
+ const bool bNextPole(fTools::equalZero(aNextTexCoor.getY()) || fTools::equal(aNextTexCoor.getY(), fOne));
+
+ if(!bPrevPole && !bNextPole)
+ {
+ // both no poles, mix them
+ aTexCoor.setX((aPrevTexCoor.getX() + aNextTexCoor.getX()) / 2.0);
+ }
+ else if(!bNextPole)
+ {
+ // copy next
+ aTexCoor.setX(aNextTexCoor.getX());
+ }
+ else
+ {
+ // copy prev, even if it's a pole, hopefully it is already corrected
+ aTexCoor.setX(aPrevTexCoor.getX());
+ }
+
+ aRetval.setTextureCoordinate(a, aTexCoor);
+ }
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ bool isInEpsilonRange(const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, const B3DPoint& rTestPosition, double fDistance)
+ {
+ // build edge vector
+ const B3DVector aEdge(rEdgeEnd - rEdgeStart);
+ bool bDoDistanceTestStart(false);
+ bool bDoDistanceTestEnd(false);
+
+ if(aEdge.equalZero())
+ {
+ // no edge, just a point. Do one of the distance tests.
+ bDoDistanceTestStart = true;
+ }
+ else
+ {
+ // calculate fCut in aEdge
+ const B3DVector aTestEdge(rTestPosition - rEdgeStart);
+ const double fScalarTestEdge(aEdge.scalar(aTestEdge));
+ const double fScalarStartEdge(aEdge.scalar(rEdgeStart));
+ const double fScalarEdge(aEdge.scalar(aEdge));
+ const double fCut((fScalarTestEdge - fScalarStartEdge) / fScalarEdge);
+ const double fZero(0.0);
+ const double fOne(1.0);
+
+ if(fTools::less(fCut, fZero))
+ {
+ // left of rEdgeStart
+ bDoDistanceTestStart = true;
+ }
+ else if(fTools::more(fCut, fOne))
+ {
+ // right of rEdgeEnd
+ bDoDistanceTestEnd = true;
+ }
+ else
+ {
+ // inside line [0.0 .. 1.0]
+ const B3DPoint aCutPoint(interpolate(rEdgeStart, rEdgeEnd, fCut));
+ const B3DVector aDelta(rTestPosition - aCutPoint);
+ const double fDistanceSquare(aDelta.scalar(aDelta));
+
+ if(fDistanceSquare <= fDistance * fDistance * fDistance)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ if(bDoDistanceTestStart)
+ {
+ const B3DVector aDelta(rTestPosition - rEdgeStart);
+ const double fDistanceSquare(aDelta.scalar(aDelta));
+
+ if(fDistanceSquare <= fDistance * fDistance * fDistance)
+ {
+ return true;
+ }
+ }
+ else if(bDoDistanceTestEnd)
+ {
+ const B3DVector aDelta(rTestPosition - rEdgeEnd);
+ const double fDistanceSquare(aDelta.scalar(aDelta));
+
+ if(fDistanceSquare <= fDistance * fDistance * fDistance)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool isInEpsilonRange(const B3DPolygon& rCandidate, const B3DPoint& rTestPosition, double fDistance)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+ B3DPoint aCurrent(rCandidate.getB3DPoint(0));
+
+ if(nEdgeCount)
+ {
+ // edges
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const B3DPoint aNext(rCandidate.getB3DPoint(nNextIndex));
+
+ if(isInEpsilonRange(aCurrent, aNext, rTestPosition, fDistance))
+ {
+ return true;
+ }
+
+ // prepare next step
+ aCurrent = aNext;
+ }
+ }
+ else
+ {
+ // no edges, but points -> not closed. Check single point. Just
+ // use isInEpsilonRange with twice the same point, it handles those well
+ if(isInEpsilonRange(aCurrent, aCurrent, rTestPosition, fDistance))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ bool isInside(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder)
+ {
+ if(bWithBorder && isPointOnPolygon(rCandidate, rPoint, true))
+ {
+ return true;
+ }
+ else
+ {
+ bool bRetval(false);
+ const B3DVector aPlaneNormal(rCandidate.getNormal());
+
+ if(!aPlaneNormal.equalZero())
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount)
+ {
+ B3DPoint aCurrentPoint(rCandidate.getB3DPoint(nPointCount - 1));
+ const double fAbsX(fabs(aPlaneNormal.getX()));
+ const double fAbsY(fabs(aPlaneNormal.getY()));
+ const double fAbsZ(fabs(aPlaneNormal.getZ()));
+
+ if(fAbsX > fAbsY && fAbsX > fAbsZ)
+ {
+ // normal points mostly in X-Direction, use YZ-Polygon projection for check
+ // x -> y, y -> z
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const B3DPoint aPreviousPoint(aCurrentPoint);
+ aCurrentPoint = rCandidate.getB3DPoint(a);
+
+ // cross-over in Z?
+ const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ()));
+ const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ()));
+
+ if(bCompZA != bCompZB)
+ {
+ // cross-over in Y?
+ const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
+ const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
+
+ if(bCompYA == bCompYB)
+ {
+ if(bCompYA)
+ {
+ bRetval = !bRetval;
+ }
+ }
+ else
+ {
+ const double fCompare(
+ aCurrentPoint.getY() - (aCurrentPoint.getZ() - rPoint.getZ()) *
+ (aPreviousPoint.getY() - aCurrentPoint.getY()) /
+ (aPreviousPoint.getZ() - aCurrentPoint.getZ()));
+
+ if(fTools::more(fCompare, rPoint.getY()))
+ {
+ bRetval = !bRetval;
+ }
+ }
+ }
+ }
+ }
+ else if(fAbsY > fAbsX && fAbsY > fAbsZ)
+ {
+ // normal points mostly in Y-Direction, use XZ-Polygon projection for check
+ // x -> x, y -> z
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const B3DPoint aPreviousPoint(aCurrentPoint);
+ aCurrentPoint = rCandidate.getB3DPoint(a);
+
+ // cross-over in Z?
+ const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ()));
+ const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ()));
+
+ if(bCompZA != bCompZB)
+ {
+ // cross-over in X?
+ const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
+ const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
+
+ if(bCompXA == bCompXB)
+ {
+ if(bCompXA)
+ {
+ bRetval = !bRetval;
+ }
+ }
+ else
+ {
+ const double fCompare(
+ aCurrentPoint.getX() - (aCurrentPoint.getZ() - rPoint.getZ()) *
+ (aPreviousPoint.getX() - aCurrentPoint.getX()) /
+ (aPreviousPoint.getZ() - aCurrentPoint.getZ()));
+
+ if(fTools::more(fCompare, rPoint.getX()))
+ {
+ bRetval = !bRetval;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // normal points mostly in Z-Direction, use XY-Polygon projection for check
+ // x -> x, y -> y
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const B3DPoint aPreviousPoint(aCurrentPoint);
+ aCurrentPoint = rCandidate.getB3DPoint(a);
+
+ // cross-over in Y?
+ const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
+ const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
+
+ if(bCompYA != bCompYB)
+ {
+ // cross-over in X?
+ const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
+ const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
+
+ if(bCompXA == bCompXB)
+ {
+ if(bCompXA)
+ {
+ bRetval = !bRetval;
+ }
+ }
+ else
+ {
+ const double fCompare(
+ aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) *
+ (aPreviousPoint.getX() - aCurrentPoint.getX()) /
+ (aPreviousPoint.getY() - aCurrentPoint.getY()));
+
+ if(fTools::more(fCompare, rPoint.getX()))
+ {
+ bRetval = !bRetval;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return bRetval;
+ }
+ }
+
+ bool isInside(const B3DPolygon& rCandidate, const B3DPolygon& rPolygon, bool bWithBorder)
+ {
+ const sal_uInt32 nPointCount(rPolygon.count());
+
+ for(sal_uInt32 a(0L); a < nPointCount; a++)
+ {
+ const B3DPoint aTestPoint(rPolygon.getB3DPoint(a));
+
+ if(!isInside(rCandidate, aTestPoint, bWithBorder))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool isPointOnLine(const B3DPoint& rStart, const B3DPoint& rEnd, const B3DPoint& rCandidate, bool bWithPoints)
+ {
+ if(rCandidate.equal(rStart) || rCandidate.equal(rEnd))
+ {
+ // candidate is in epsilon around start or end -> inside
+ return bWithPoints;
+ }
+ else if(rStart.equal(rEnd))
+ {
+ // start and end are equal, but candidate is outside their epsilon -> outside
+ return false;
+ }
+ else
+ {
+ const B3DVector aEdgeVector(rEnd - rStart);
+ const B3DVector aTestVector(rCandidate - rStart);
+
+ if(areParallel(aEdgeVector, aTestVector))
+ {
+ const double fZero(0.0);
+ const double fOne(1.0);
+ double fParamTestOnCurr(0.0);
+
+ if(aEdgeVector.getX() > aEdgeVector.getY())
+ {
+ if(aEdgeVector.getX() > aEdgeVector.getZ())
+ {
+ // X is biggest
+ fParamTestOnCurr = aTestVector.getX() / aEdgeVector.getX();
+ }
+ else
+ {
+ // Z is biggest
+ fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
+ }
+ }
+ else
+ {
+ if(aEdgeVector.getY() > aEdgeVector.getZ())
+ {
+ // Y is biggest
+ fParamTestOnCurr = aTestVector.getY() / aEdgeVector.getY();
+ }
+ else
+ {
+ // Z is biggest
+ fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
+ }
+ }
+
+ if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ bool isPointOnPolygon(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithPoints)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 1L)
+ {
+ const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
+ B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
+
+ for(sal_uInt32 a(0); a < nLoopCount; a++)
+ {
+ const B3DPoint aNextPoint(rCandidate.getB3DPoint((a + 1) % nPointCount));
+
+ if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints))
+ {
+ return true;
+ }
+
+ aCurrentPoint = aNextPoint;
+ }
+ }
+ else if(nPointCount && bWithPoints)
+ {
+ return rPoint.equal(rCandidate.getB3DPoint(0));
+ }
+
+ return false;
+ }
+
+ bool getCutBetweenLineAndPlane(const B3DVector& rPlaneNormal, const B3DPoint& rPlanePoint, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut)
+ {
+ if(!rPlaneNormal.equalZero() && !rEdgeStart.equal(rEdgeEnd))
+ {
+ const B3DVector aTestEdge(rEdgeEnd - rEdgeStart);
+ const double fScalarEdge(rPlaneNormal.scalar(aTestEdge));
+
+ if(!fTools::equalZero(fScalarEdge))
+ {
+ const B3DVector aCompareEdge(rPlanePoint - rEdgeStart);
+ const double fScalarCompare(rPlaneNormal.scalar(aCompareEdge));
+
+ fCut = fScalarCompare / fScalarEdge;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool getCutBetweenLineAndPolygon(const B3DPolygon& rCandidate, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 2 && !rEdgeStart.equal(rEdgeEnd))
+ {
+ const B3DVector aPlaneNormal(rCandidate.getNormal());
+
+ if(!aPlaneNormal.equalZero())
+ {
+ const B3DPoint aPointOnPlane(rCandidate.getB3DPoint(0));
+
+ return getCutBetweenLineAndPlane(aPlaneNormal, aPointOnPlane, rEdgeStart, rEdgeEnd, fCut);
+ }
+ }
+
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // comparators with tolerance for 3D Polygons
+
+ bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB, const double& rfSmallValue)
+ {
+ const sal_uInt32 nPointCount(rCandidateA.count());
+
+ if(nPointCount != rCandidateB.count())
+ return false;
+
+ const bool bClosed(rCandidateA.isClosed());
+
+ if(bClosed != rCandidateB.isClosed())
+ return false;
+
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const B3DPoint aPoint(rCandidateA.getB3DPoint(a));
+
+ if(!aPoint.equal(rCandidateB.getB3DPoint(a), rfSmallValue))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB)
+ {
+ const double fSmallValue(fTools::getSmallValue());
+
+ return equal(rCandidateA, rCandidateB, fSmallValue);
+ }
+
+ // snap points of horizontal or vertical edges to discrete values
+ B3DPolygon snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon& rCandidate)
+ {
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount > 1)
+ {
+ // Start by copying the source polygon to get a writeable copy. The closed state is
+ // copied by aRetval's initialisation, too, so no need to copy it in this method
+ B3DPolygon aRetval(rCandidate);
+
+ // prepare geometry data. Get rounded from original
+ B3ITuple aPrevTuple(basegfx::fround(rCandidate.getB3DPoint(nPointCount - 1)));
+ B3DPoint aCurrPoint(rCandidate.getB3DPoint(0));
+ B3ITuple aCurrTuple(basegfx::fround(aCurrPoint));
+
+ // loop over all points. This will also snap the implicit closing edge
+ // even when not closed, but that's no problem here
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ // get next point. Get rounded from original
+ const bool bLastRun(a + 1 == nPointCount);
+ const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1);
+ const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
+ const B3ITuple aNextTuple(basegfx::fround(aNextPoint));
+
+ // get the states
+ const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
+ const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
+ const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
+ const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
+ const bool bSnapX(bPrevVertical || bNextVertical);
+ const bool bSnapY(bPrevHorizontal || bNextHorizontal);
+
+ if(bSnapX || bSnapY)
+ {
+ const B3DPoint aSnappedPoint(
+ bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
+ bSnapY ? aCurrTuple.getY() : aCurrPoint.getY(),
+ aCurrPoint.getZ());
+
+ aRetval.setB3DPoint(a, aSnappedPoint);
+ }
+
+ // prepare next point
+ if(!bLastRun)
+ {
+ aPrevTuple = aCurrTuple;
+ aCurrPoint = aNextPoint;
+ aCurrTuple = aNextTuple;
+ }
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return rCandidate;
+ }
+ }
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+// eof
diff --git a/basegfx/source/polygon/b3dpolypolygon.cxx b/basegfx/source/polygon/b3dpolypolygon.cxx
new file mode 100644
index 000000000000..9e004e67c35f
--- /dev/null
+++ b/basegfx/source/polygon/b3dpolypolygon.cxx
@@ -0,0 +1,449 @@
+/*************************************************************************
+ *
+ * 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: b3dpolypolygon.cxx,v $
+ * $Revision: 1.12 $
+ *
+ * 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/polygon/b3dpolypolygon.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <rtl/instance.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <functional>
+#include <vector>
+#include <algorithm>
+
+//////////////////////////////////////////////////////////////////////////////
+
+class ImplB3DPolyPolygon
+{
+ typedef ::std::vector< ::basegfx::B3DPolygon > PolygonVector;
+
+ PolygonVector maPolygons;
+
+public:
+ ImplB3DPolyPolygon() : maPolygons()
+ {
+ }
+
+ ImplB3DPolyPolygon(const ::basegfx::B3DPolygon& rToBeCopied) :
+ maPolygons(1,rToBeCopied)
+ {
+ }
+
+ bool operator==(const ImplB3DPolyPolygon& rPolygonList) const
+ {
+ // same polygon count?
+ if(maPolygons.size() != rPolygonList.maPolygons.size())
+ return false;
+
+ // compare polygon content
+ if(maPolygons != rPolygonList.maPolygons)
+ return false;
+
+ return true;
+ }
+
+ const ::basegfx::B3DPolygon& getB3DPolygon(sal_uInt32 nIndex) const
+ {
+ return maPolygons[nIndex];
+ }
+
+ void setB3DPolygon(sal_uInt32 nIndex, const ::basegfx::B3DPolygon& rPolygon)
+ {
+ maPolygons[nIndex] = rPolygon;
+ }
+
+ void insert(sal_uInt32 nIndex, const ::basegfx::B3DPolygon& rPolygon, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // add nCount copies of rPolygon
+ PolygonVector::iterator aIndex(maPolygons.begin());
+ aIndex += nIndex;
+ maPolygons.insert(aIndex, nCount, rPolygon);
+ }
+ }
+
+ void insert(sal_uInt32 nIndex, const ::basegfx::B3DPolyPolygon& rPolyPolygon)
+ {
+ const sal_uInt32 nCount = rPolyPolygon.count();
+
+ if(nCount)
+ {
+ // add nCount polygons from rPolyPolygon
+ maPolygons.reserve(maPolygons.size() + nCount);
+ PolygonVector::iterator aIndex(maPolygons.begin());
+ aIndex += nIndex;
+
+ for(sal_uInt32 a(0L); a < nCount; a++)
+ {
+ maPolygons.insert(aIndex, rPolyPolygon.getB3DPolygon(a));
+ aIndex++;
+ }
+ }
+ }
+
+ void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ if(nCount)
+ {
+ // remove polygon data
+ PolygonVector::iterator aStart(maPolygons.begin());
+ aStart += nIndex;
+ const PolygonVector::iterator aEnd(aStart + nCount);
+
+ maPolygons.erase(aStart, aEnd);
+ }
+ }
+
+ sal_uInt32 count() const
+ {
+ return maPolygons.size();
+ }
+
+ void setClosed(bool bNew)
+ {
+ for(sal_uInt32 a(0L); a < maPolygons.size(); a++)
+ {
+ maPolygons[a].setClosed(bNew);
+ }
+ }
+
+ void flip()
+ {
+ std::for_each( maPolygons.begin(),
+ maPolygons.end(),
+ std::mem_fun_ref( &::basegfx::B3DPolygon::flip ));
+ }
+
+ void removeDoublePoints()
+ {
+ std::for_each( maPolygons.begin(),
+ maPolygons.end(),
+ std::mem_fun_ref( &::basegfx::B3DPolygon::removeDoublePoints ));
+ }
+
+ void transform(const ::basegfx::B3DHomMatrix& rMatrix)
+ {
+ for(sal_uInt32 a(0L); a < maPolygons.size(); a++)
+ {
+ maPolygons[a].transform(rMatrix);
+ }
+ }
+
+ void clearBColors()
+ {
+ for(sal_uInt32 a(0L); a < maPolygons.size(); a++)
+ {
+ maPolygons[a].clearBColors();
+ }
+ }
+
+ void transformNormals(const ::basegfx::B3DHomMatrix& rMatrix)
+ {
+ for(sal_uInt32 a(0L); a < maPolygons.size(); a++)
+ {
+ maPolygons[a].transformNormals(rMatrix);
+ }
+ }
+
+ void clearNormals()
+ {
+ for(sal_uInt32 a(0L); a < maPolygons.size(); a++)
+ {
+ maPolygons[a].clearNormals();
+ }
+ }
+
+ void transformTextureCoordiantes(const ::basegfx::B2DHomMatrix& rMatrix)
+ {
+ for(sal_uInt32 a(0L); a < maPolygons.size(); a++)
+ {
+ maPolygons[a].transformTextureCoordiantes(rMatrix);
+ }
+ }
+
+ void clearTextureCoordinates()
+ {
+ for(sal_uInt32 a(0L); a < maPolygons.size(); a++)
+ {
+ maPolygons[a].clearTextureCoordinates();
+ }
+ }
+
+ void makeUnique()
+ {
+ std::for_each( maPolygons.begin(),
+ maPolygons.end(),
+ std::mem_fun_ref( &::basegfx::B3DPolygon::makeUnique ));
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace { struct DefaultPolyPolygon : public rtl::Static<B3DPolyPolygon::ImplType,
+ DefaultPolyPolygon> {}; }
+
+ B3DPolyPolygon::B3DPolyPolygon() :
+ mpPolyPolygon(DefaultPolyPolygon::get())
+ {
+ }
+
+ B3DPolyPolygon::B3DPolyPolygon(const B3DPolyPolygon& rPolyPolygon) :
+ mpPolyPolygon(rPolyPolygon.mpPolyPolygon)
+ {
+ }
+
+ B3DPolyPolygon::B3DPolyPolygon(const B3DPolygon& rPolygon) :
+ mpPolyPolygon( ImplB3DPolyPolygon(rPolygon) )
+ {
+ }
+
+ B3DPolyPolygon::~B3DPolyPolygon()
+ {
+ }
+
+ B3DPolyPolygon& B3DPolyPolygon::operator=(const B3DPolyPolygon& rPolyPolygon)
+ {
+ mpPolyPolygon = rPolyPolygon.mpPolyPolygon;
+ return *this;
+ }
+
+ void B3DPolyPolygon::makeUnique()
+ {
+ mpPolyPolygon.make_unique();
+ mpPolyPolygon->makeUnique();
+ }
+
+ bool B3DPolyPolygon::operator==(const B3DPolyPolygon& rPolyPolygon) const
+ {
+ if(mpPolyPolygon.same_object(rPolyPolygon.mpPolyPolygon))
+ return true;
+
+ return ((*mpPolyPolygon) == (*rPolyPolygon.mpPolyPolygon));
+ }
+
+ bool B3DPolyPolygon::operator!=(const B3DPolyPolygon& rPolyPolygon) const
+ {
+ return !(*this == rPolyPolygon);
+ }
+
+ sal_uInt32 B3DPolyPolygon::count() const
+ {
+ return mpPolyPolygon->count();
+ }
+
+ B3DPolygon B3DPolyPolygon::getB3DPolygon(sal_uInt32 nIndex) const
+ {
+ OSL_ENSURE(nIndex < mpPolyPolygon->count(), "B3DPolyPolygon access outside range (!)");
+
+ return mpPolyPolygon->getB3DPolygon(nIndex);
+ }
+
+ void B3DPolyPolygon::setB3DPolygon(sal_uInt32 nIndex, const B3DPolygon& rPolygon)
+ {
+ OSL_ENSURE(nIndex < mpPolyPolygon->count(), "B3DPolyPolygon access outside range (!)");
+
+ if(getB3DPolygon(nIndex) != rPolygon)
+ mpPolyPolygon->setB3DPolygon(nIndex, rPolygon);
+ }
+
+ bool B3DPolyPolygon::areBColorsUsed() const
+ {
+ for(sal_uInt32 a(0L); a < mpPolyPolygon->count(); a++)
+ {
+ if((mpPolyPolygon->getB3DPolygon(a)).areBColorsUsed())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void B3DPolyPolygon::clearBColors()
+ {
+ if(areBColorsUsed())
+ mpPolyPolygon->clearBColors();
+ }
+
+ void B3DPolyPolygon::transformNormals(const B3DHomMatrix& rMatrix)
+ {
+ if(!rMatrix.isIdentity())
+ mpPolyPolygon->transformNormals(rMatrix);
+ }
+
+ bool B3DPolyPolygon::areNormalsUsed() const
+ {
+ for(sal_uInt32 a(0L); a < mpPolyPolygon->count(); a++)
+ {
+ if((mpPolyPolygon->getB3DPolygon(a)).areNormalsUsed())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void B3DPolyPolygon::clearNormals()
+ {
+ if(areNormalsUsed())
+ mpPolyPolygon->clearNormals();
+ }
+
+ void B3DPolyPolygon::transformTextureCoordiantes(const B2DHomMatrix& rMatrix)
+ {
+ if(!rMatrix.isIdentity())
+ mpPolyPolygon->transformTextureCoordiantes(rMatrix);
+ }
+
+ bool B3DPolyPolygon::areTextureCoordinatesUsed() const
+ {
+ for(sal_uInt32 a(0L); a < mpPolyPolygon->count(); a++)
+ {
+ if((mpPolyPolygon->getB3DPolygon(a)).areTextureCoordinatesUsed())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void B3DPolyPolygon::clearTextureCoordinates()
+ {
+ if(areTextureCoordinatesUsed())
+ mpPolyPolygon->clearTextureCoordinates();
+ }
+
+ void B3DPolyPolygon::insert(sal_uInt32 nIndex, const B3DPolygon& rPolygon, sal_uInt32 nCount)
+ {
+ OSL_ENSURE(nIndex <= mpPolyPolygon->count(), "B3DPolyPolygon Insert outside range (!)");
+
+ if(nCount)
+ mpPolyPolygon->insert(nIndex, rPolygon, nCount);
+ }
+
+ void B3DPolyPolygon::append(const B3DPolygon& rPolygon, sal_uInt32 nCount)
+ {
+ if(nCount)
+ mpPolyPolygon->insert(mpPolyPolygon->count(), rPolygon, nCount);
+ }
+
+ void B3DPolyPolygon::insert(sal_uInt32 nIndex, const B3DPolyPolygon& rPolyPolygon)
+ {
+ OSL_ENSURE(nIndex <= mpPolyPolygon->count(), "B3DPolyPolygon Insert outside range (!)");
+
+ if(rPolyPolygon.count())
+ mpPolyPolygon->insert(nIndex, rPolyPolygon);
+ }
+
+ void B3DPolyPolygon::append(const B3DPolyPolygon& rPolyPolygon)
+ {
+ if(rPolyPolygon.count())
+ mpPolyPolygon->insert(mpPolyPolygon->count(), rPolyPolygon);
+ }
+
+ void B3DPolyPolygon::remove(sal_uInt32 nIndex, sal_uInt32 nCount)
+ {
+ OSL_ENSURE(nIndex + nCount <= mpPolyPolygon->count(), "B3DPolyPolygon Remove outside range (!)");
+
+ if(nCount)
+ mpPolyPolygon->remove(nIndex, nCount);
+ }
+
+ void B3DPolyPolygon::clear()
+ {
+ mpPolyPolygon = DefaultPolyPolygon::get();
+ }
+
+ bool B3DPolyPolygon::isClosed() const
+ {
+ bool bRetval(true);
+
+ // PolyPOlygon is closed when all contained Polygons are closed or
+ // no Polygon exists.
+ for(sal_uInt32 a(0L); bRetval && a < mpPolyPolygon->count(); a++)
+ {
+ if(!(mpPolyPolygon->getB3DPolygon(a)).isClosed())
+ {
+ bRetval = false;
+ }
+ }
+
+ return bRetval;
+ }
+
+ void B3DPolyPolygon::setClosed(bool bNew)
+ {
+ if(bNew != isClosed())
+ mpPolyPolygon->setClosed(bNew);
+ }
+
+ void B3DPolyPolygon::flip()
+ {
+ mpPolyPolygon->flip();
+ }
+
+ bool B3DPolyPolygon::hasDoublePoints() const
+ {
+ bool bRetval(false);
+
+ for(sal_uInt32 a(0L); !bRetval && a < mpPolyPolygon->count(); a++)
+ {
+ if((mpPolyPolygon->getB3DPolygon(a)).hasDoublePoints())
+ {
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+ }
+
+ void B3DPolyPolygon::removeDoublePoints()
+ {
+ if(hasDoublePoints())
+ mpPolyPolygon->removeDoublePoints();
+ }
+
+ void B3DPolyPolygon::transform(const B3DHomMatrix& rMatrix)
+ {
+ if(mpPolyPolygon->count() && !rMatrix.isIdentity())
+ {
+ mpPolyPolygon->transform(rMatrix);
+ }
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/polygon/b3dpolypolygontools.cxx b/basegfx/source/polygon/b3dpolypolygontools.cxx
new file mode 100644
index 000000000000..fe75e3919d21
--- /dev/null
+++ b/basegfx/source/polygon/b3dpolypolygontools.cxx
@@ -0,0 +1,559 @@
+/*************************************************************************
+ *
+ * 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: b3dpolypolygontools.cxx,v $
+ * $Revision: 1.8.4.1 $
+ *
+ * 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/polygon/b3dpolypolygontools.hxx>
+#include <basegfx/range/b3drange.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <numeric>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <osl/mutex.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ namespace tools
+ {
+ // B3DPolyPolygon tools
+ B3DRange getRange(const B3DPolyPolygon& rCandidate)
+ {
+ B3DRange aRetval;
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+
+ for(sal_uInt32 a(0L); a < nPolygonCount; a++)
+ {
+ B3DPolygon aCandidate = rCandidate.getB3DPolygon(a);
+ aRetval.expand(getRange(aCandidate));
+ }
+
+ return aRetval;
+ }
+
+ void applyLineDashing(const B3DPolyPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B3DPolyPolygon* pLineTarget, B3DPolyPolygon* pGapTarget, double fFullDashDotLen)
+ {
+ if(0.0 == fFullDashDotLen && rDotDashArray.size())
+ {
+ // calculate fFullDashDotLen from rDotDashArray
+ fFullDashDotLen = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
+ }
+
+ if(rCandidate.count() && fFullDashDotLen > 0.0)
+ {
+ B3DPolyPolygon aLineTarget, aGapTarget;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ const B3DPolygon aCandidate(rCandidate.getB3DPolygon(a));
+
+ applyLineDashing(
+ aCandidate,
+ rDotDashArray,
+ pLineTarget ? &aLineTarget : 0,
+ pGapTarget ? &aGapTarget : 0,
+ fFullDashDotLen);
+
+ if(pLineTarget)
+ {
+ pLineTarget->append(aLineTarget);
+ }
+
+ if(pGapTarget)
+ {
+ pGapTarget->append(aGapTarget);
+ }
+ }
+ }
+ }
+
+ B3DPolyPolygon createUnitCubePolyPolygon()
+ {
+ static B3DPolyPolygon aRetval;
+ ::osl::Mutex m_mutex;
+
+ if(!aRetval.count())
+ {
+ B3DPolygon aTemp;
+ aTemp.append(B3DPoint(0.0, 0.0, 1.0));
+ aTemp.append(B3DPoint(0.0, 1.0, 1.0));
+ aTemp.append(B3DPoint(1.0, 1.0, 1.0));
+ aTemp.append(B3DPoint(1.0, 0.0, 1.0));
+ aTemp.setClosed(true);
+ aRetval.append(aTemp);
+
+ aTemp.clear();
+ aTemp.append(B3DPoint(0.0, 0.0, 0.0));
+ aTemp.append(B3DPoint(0.0, 1.0, 0.0));
+ aTemp.append(B3DPoint(1.0, 1.0, 0.0));
+ aTemp.append(B3DPoint(1.0, 0.0, 0.0));
+ aTemp.setClosed(true);
+ aRetval.append(aTemp);
+
+ aTemp.clear();
+ aTemp.append(B3DPoint(0.0, 0.0, 0.0));
+ aTemp.append(B3DPoint(0.0, 0.0, 1.0));
+ aRetval.append(aTemp);
+
+ aTemp.clear();
+ aTemp.append(B3DPoint(0.0, 1.0, 0.0));
+ aTemp.append(B3DPoint(0.0, 1.0, 1.0));
+ aRetval.append(aTemp);
+
+ aTemp.clear();
+ aTemp.append(B3DPoint(1.0, 1.0, 0.0));
+ aTemp.append(B3DPoint(1.0, 1.0, 1.0));
+ aRetval.append(aTemp);
+
+ aTemp.clear();
+ aTemp.append(B3DPoint(1.0, 0.0, 0.0));
+ aTemp.append(B3DPoint(1.0, 0.0, 1.0));
+ aRetval.append(aTemp);
+ }
+
+ return aRetval;
+ }
+
+ B3DPolyPolygon createUnitCubeFillPolyPolygon()
+ {
+ static B3DPolyPolygon aRetval;
+ ::osl::Mutex m_mutex;
+
+ if(!aRetval.count())
+ {
+ B3DPolygon aTemp;
+
+ // all points
+ const B3DPoint A(0.0, 0.0, 0.0);
+ const B3DPoint B(0.0, 1.0, 0.0);
+ const B3DPoint C(1.0, 1.0, 0.0);
+ const B3DPoint D(1.0, 0.0, 0.0);
+ const B3DPoint E(0.0, 0.0, 1.0);
+ const B3DPoint F(0.0, 1.0, 1.0);
+ const B3DPoint G(1.0, 1.0, 1.0);
+ const B3DPoint H(1.0, 0.0, 1.0);
+
+ // create bottom
+ aTemp.append(D);
+ aTemp.append(A);
+ aTemp.append(E);
+ aTemp.append(H);
+ aTemp.setClosed(true);
+ aRetval.append(aTemp);
+
+ // create front
+ aTemp.clear();
+ aTemp.append(B);
+ aTemp.append(A);
+ aTemp.append(D);
+ aTemp.append(C);
+ aTemp.setClosed(true);
+ aRetval.append(aTemp);
+
+ // create left
+ aTemp.clear();
+ aTemp.append(E);
+ aTemp.append(A);
+ aTemp.append(B);
+ aTemp.append(F);
+ aTemp.setClosed(true);
+ aRetval.append(aTemp);
+
+ // create top
+ aTemp.clear();
+ aTemp.append(C);
+ aTemp.append(G);
+ aTemp.append(F);
+ aTemp.append(B);
+ aTemp.setClosed(true);
+ aRetval.append(aTemp);
+
+ // create right
+ aTemp.clear();
+ aTemp.append(H);
+ aTemp.append(G);
+ aTemp.append(C);
+ aTemp.append(D);
+ aTemp.setClosed(true);
+ aRetval.append(aTemp);
+
+ // create back
+ aTemp.clear();
+ aTemp.append(F);
+ aTemp.append(G);
+ aTemp.append(H);
+ aTemp.append(E);
+ aTemp.setClosed(true);
+ aRetval.append(aTemp);
+ }
+
+ return aRetval;
+ }
+
+ B3DPolyPolygon createCubePolyPolygonFromB3DRange( const B3DRange& rRange)
+ {
+ B3DPolyPolygon aRetval;
+
+ if(!rRange.isEmpty())
+ {
+ aRetval = createUnitCubePolyPolygon();
+ B3DHomMatrix aTrans;
+ aTrans.scale(rRange.getWidth(), rRange.getHeight(), rRange.getDepth());
+ aTrans.translate(rRange.getMinX(), rRange.getMinY(), rRange.getMinZ());
+ aRetval.transform(aTrans);
+ aRetval.removeDoublePoints();
+ }
+
+ return aRetval;
+ }
+
+ B3DPolyPolygon createCubeFillPolyPolygonFromB3DRange( const B3DRange& rRange)
+ {
+ B3DPolyPolygon aRetval;
+
+ if(!rRange.isEmpty())
+ {
+ aRetval = createUnitCubeFillPolyPolygon();
+ B3DHomMatrix aTrans;
+ aTrans.scale(rRange.getWidth(), rRange.getHeight(), rRange.getDepth());
+ aTrans.translate(rRange.getMinX(), rRange.getMinY(), rRange.getMinZ());
+ aRetval.transform(aTrans);
+ aRetval.removeDoublePoints();
+ }
+
+ return aRetval;
+ }
+
+ // helper for getting the 3D Point from given cartesian coordiantes. fVer is defined from
+ // [F_PI2 .. -F_PI2], fHor from [0.0 .. F_2PI]
+ inline B3DPoint getPointFromCartesian(double fVer, double fHor)
+ {
+ const double fCosHor(cos(fHor));
+ return B3DPoint(fCosHor * cos(fVer), sin(fHor), fCosHor * -sin(fVer));
+ }
+
+ B3DPolyPolygon createUnitSpherePolyPolygon(
+ sal_uInt32 nHorSeg, sal_uInt32 nVerSeg,
+ double fVerStart, double fVerStop,
+ double fHorStart, double fHorStop)
+ {
+ B3DPolyPolygon aRetval;
+ sal_uInt32 a, b;
+
+ if(!nHorSeg)
+ {
+ nHorSeg = fround(fabs(fHorStop - fHorStart) / (F_2PI / 24.0));
+ }
+
+ if(!nHorSeg)
+ {
+ nHorSeg = 1L;
+ }
+
+ if(!nVerSeg)
+ {
+ nVerSeg = fround(fabs(fVerStop - fVerStart) / (F_2PI / 24.0));
+ }
+
+ if(!nVerSeg)
+ {
+ nVerSeg = 1L;
+ }
+
+ // create constants
+ const double fVerDiffPerStep((fVerStop - fVerStart) / (double)nVerSeg);
+ const double fHorDiffPerStep((fHorStop - fHorStart) / (double)nHorSeg);
+ bool bHorClosed(fTools::equal(fHorStop - fHorStart, F_2PI));
+ bool bVerFromTop(fTools::equal(fVerStart, F_PI2));
+ bool bVerToBottom(fTools::equal(fVerStop, -F_PI2));
+
+ // create horizontal rings
+ const sal_uInt32 nLoopVerInit(bVerFromTop ? 1L : 0L);
+ const sal_uInt32 nLoopVerLimit(bVerToBottom ? nVerSeg : nVerSeg + 1L);
+ const sal_uInt32 nLoopHorLimit(bHorClosed ? nHorSeg : nHorSeg + 1L);
+
+ for(a = nLoopVerInit; a < nLoopVerLimit; a++)
+ {
+ const double fVer(fVerStart + ((double)(a) * fVerDiffPerStep));
+ B3DPolygon aNew;
+
+ for(b = 0L; b < nLoopHorLimit; b++)
+ {
+ const double fHor(fHorStart + ((double)(b) * fHorDiffPerStep));
+ aNew.append(getPointFromCartesian(fHor, fVer));
+ }
+
+ aNew.setClosed(bHorClosed);
+ aRetval.append(aNew);
+ }
+
+ // create vertical half-rings
+ for(a = 0L; a < nLoopHorLimit; a++)
+ {
+ const double fHor(fHorStart + ((double)(a) * fHorDiffPerStep));
+ B3DPolygon aNew;
+
+ if(bVerFromTop)
+ {
+ aNew.append(B3DPoint(0.0, 1.0, 0.0));
+ }
+
+ for(b = nLoopVerInit; b < nLoopVerLimit; b++)
+ {
+ const double fVer(fVerStart + ((double)(b) * fVerDiffPerStep));
+ aNew.append(getPointFromCartesian(fHor, fVer));
+ }
+
+ if(bVerToBottom)
+ {
+ aNew.append(B3DPoint(0.0, -1.0, 0.0));
+ }
+
+ aRetval.append(aNew);
+ }
+
+ return aRetval;
+ }
+
+ B3DPolyPolygon createSpherePolyPolygonFromB3DRange( const B3DRange& rRange,
+ sal_uInt32 nHorSeg, sal_uInt32 nVerSeg,
+ double fVerStart, double fVerStop,
+ double fHorStart, double fHorStop)
+ {
+ B3DPolyPolygon aRetval(createUnitSpherePolyPolygon(nHorSeg, nVerSeg, fVerStart, fVerStop, fHorStart, fHorStop));
+
+ if(aRetval.count())
+ {
+ // move and scale whole construct which is now in [-1.0 .. 1.0] in all directions
+ B3DHomMatrix aTrans;
+ aTrans.translate(1.0, 1.0, 1.0);
+ aTrans.scale(rRange.getWidth() / 2.0, rRange.getHeight() / 2.0, rRange.getDepth() / 2.0);
+ aTrans.translate(rRange.getMinX(), rRange.getMinY(), rRange.getMinZ());
+ aRetval.transform(aTrans);
+ }
+
+ return aRetval;
+ }
+
+ B3DPolyPolygon createUnitSphereFillPolyPolygon(
+ sal_uInt32 nHorSeg, sal_uInt32 nVerSeg,
+ bool bNormals,
+ double fVerStart, double fVerStop,
+ double fHorStart, double fHorStop)
+ {
+ B3DPolyPolygon aRetval;
+
+ if(!nHorSeg)
+ {
+ nHorSeg = fround(fabs(fHorStop - fHorStart) / (F_2PI / 24.0));
+ }
+
+ if(!nHorSeg)
+ {
+ nHorSeg = 1L;
+ }
+
+ if(!nVerSeg)
+ {
+ nVerSeg = fround(fabs(fVerStop - fVerStart) / (F_2PI / 24.0));
+ }
+
+ if(!nVerSeg)
+ {
+ nVerSeg = 1L;
+ }
+
+ // vertical loop
+ for(sal_uInt32 a(0L); a < nVerSeg; a++)
+ {
+ const double fVer(fVerStart + (((fVerStop - fVerStart) * a) / nVerSeg));
+ const double fVer2(fVerStart + (((fVerStop - fVerStart) * (a + 1)) / nVerSeg));
+
+ // horizontal loop
+ for(sal_uInt32 b(0L); b < nHorSeg; b++)
+ {
+ const double fHor(fHorStart + (((fHorStop - fHorStart) * b) / nHorSeg));
+ const double fHor2(fHorStart + (((fHorStop - fHorStart) * (b + 1)) / nHorSeg));
+ B3DPolygon aNew;
+
+ aNew.append(getPointFromCartesian(fHor, fVer));
+ aNew.append(getPointFromCartesian(fHor2, fVer));
+ aNew.append(getPointFromCartesian(fHor2, fVer2));
+ aNew.append(getPointFromCartesian(fHor, fVer2));
+
+ if(bNormals)
+ {
+ for(sal_uInt32 c(0L); c < aNew.count(); c++)
+ {
+ aNew.setNormal(c, ::basegfx::B3DVector(aNew.getB3DPoint(c)));
+ }
+ }
+
+ aNew.setClosed(true);
+ aRetval.append(aNew);
+ }
+ }
+
+ return aRetval;
+ }
+
+ B3DPolyPolygon createSphereFillPolyPolygonFromB3DRange( const B3DRange& rRange,
+ sal_uInt32 nHorSeg, sal_uInt32 nVerSeg,
+ bool bNormals,
+ double fVerStart, double fVerStop,
+ double fHorStart, double fHorStop)
+ {
+ B3DPolyPolygon aRetval(createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, bNormals, fVerStart, fVerStop, fHorStart, fHorStop));
+
+ if(aRetval.count())
+ {
+ // move and scale whole construct which is now in [-1.0 .. 1.0] in all directions
+ B3DHomMatrix aTrans;
+ aTrans.translate(1.0, 1.0, 1.0);
+ aTrans.scale(rRange.getWidth() / 2.0, rRange.getHeight() / 2.0, rRange.getDepth() / 2.0);
+ aTrans.translate(rRange.getMinX(), rRange.getMinY(), rRange.getMinZ());
+ aRetval.transform(aTrans);
+ }
+
+ return aRetval;
+ }
+
+ B3DPolyPolygon applyDefaultNormalsSphere( const B3DPolyPolygon& rCandidate, const B3DPoint& rCenter)
+ {
+ B3DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ aRetval.append(applyDefaultNormalsSphere(rCandidate.getB3DPolygon(a), rCenter));
+ }
+
+ return aRetval;
+ }
+
+ B3DPolyPolygon invertNormals( const B3DPolyPolygon& rCandidate)
+ {
+ B3DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ aRetval.append(invertNormals(rCandidate.getB3DPolygon(a)));
+ }
+
+ return aRetval;
+ }
+
+ B3DPolyPolygon applyDefaultTextureCoordinatesParallel( const B3DPolyPolygon& rCandidate, const B3DRange& rRange, bool bChangeX, bool bChangeY)
+ {
+ B3DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ aRetval.append(applyDefaultTextureCoordinatesParallel(rCandidate.getB3DPolygon(a), rRange, bChangeX, bChangeY));
+ }
+
+ return aRetval;
+ }
+
+ B3DPolyPolygon applyDefaultTextureCoordinatesSphere( const B3DPolyPolygon& rCandidate, const B3DPoint& rCenter, bool bChangeX, bool bChangeY)
+ {
+ B3DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
+ {
+ aRetval.append(applyDefaultTextureCoordinatesSphere(rCandidate.getB3DPolygon(a), rCenter, bChangeX, bChangeY));
+ }
+
+ return aRetval;
+ }
+
+ bool isInside(const B3DPolyPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder)
+ {
+ const sal_uInt32 nPolygonCount(rCandidate.count());
+
+ if(1L == nPolygonCount)
+ {
+ return isInside(rCandidate.getB3DPolygon(0), rPoint, bWithBorder);
+ }
+ else
+ {
+ sal_Int32 nInsideCount(0);
+
+ for(sal_uInt32 a(0); a < nPolygonCount; a++)
+ {
+ const B3DPolygon aPolygon(rCandidate.getB3DPolygon(a));
+ const bool bInside(isInside(aPolygon, rPoint, bWithBorder));
+
+ if(bInside)
+ {
+ nInsideCount++;
+ }
+ }
+
+ return (nInsideCount % 2L);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // comparators with tolerance for 3D PolyPolygons
+
+ bool equal(const B3DPolyPolygon& rCandidateA, const B3DPolyPolygon& rCandidateB, const double& rfSmallValue)
+ {
+ const sal_uInt32 nPolygonCount(rCandidateA.count());
+
+ if(nPolygonCount != rCandidateB.count())
+ return false;
+
+ for(sal_uInt32 a(0); a < nPolygonCount; a++)
+ {
+ const B3DPolygon aCandidate(rCandidateA.getB3DPolygon(a));
+
+ if(!equal(aCandidate, rCandidateB.getB3DPolygon(a), rfSmallValue))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool equal(const B3DPolyPolygon& rCandidateA, const B3DPolyPolygon& rCandidateB)
+ {
+ const double fSmallValue(fTools::getSmallValue());
+
+ return equal(rCandidateA, rCandidateB, fSmallValue);
+ }
+
+ } // end of namespace tools
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+
+// eof
diff --git a/basegfx/source/polygon/makefile.mk b/basegfx/source/polygon/makefile.mk
new file mode 100644
index 000000000000..b2da44250e66
--- /dev/null
+++ b/basegfx/source/polygon/makefile.mk
@@ -0,0 +1,66 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.12 $
+#
+# 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=polygon
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+ENABLE_EXCEPTIONS=TRUE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/b2dpolygon.obj \
+ $(SLO)$/b2dpolygontools.obj \
+ $(SLO)$/b2dpolypolygon.obj \
+ $(SLO)$/b2dpolypolygontools.obj \
+ $(SLO)$/b2dsvgpolypolygon.obj \
+ $(SLO)$/b2dlinegeometry.obj \
+ $(SLO)$/b2dpolypolygoncutter.obj \
+ $(SLO)$/b2dpolypolygonrasterconverter.obj \
+ $(SLO)$/b2dpolygonclipper.obj \
+ $(SLO)$/b2dpolygontriangulator.obj \
+ $(SLO)$/b2dpolygoncutandtouch.obj \
+ $(SLO)$/b3dpolygon.obj \
+ $(SLO)$/b3dpolygontools.obj \
+ $(SLO)$/b3dpolypolygon.obj \
+ $(SLO)$/b3dpolypolygontools.obj \
+ $(SLO)$/b3dpolygonclipper.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/range/b1drange.cxx b/basegfx/source/range/b1drange.cxx
new file mode 100644
index 000000000000..63f5e398a8be
--- /dev/null
+++ b/basegfx/source/range/b1drange.cxx
@@ -0,0 +1,59 @@
+/*************************************************************************
+ *
+ * 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: b1drange.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/range/b1drange.hxx>
+#include <basegfx/range/b1irange.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ B1DRange::B1DRange( const B1IRange& rRange ) :
+ maRange()
+ {
+ if( !rRange.isEmpty() )
+ {
+ maRange = rRange.getMinimum();
+ expand(rRange.getMaximum());
+ }
+ }
+
+ B1IRange fround(const B1DRange& rRange)
+ {
+ return rRange.isEmpty() ?
+ B1IRange() :
+ B1IRange( fround( rRange.getMinimum()),
+ fround( rRange.getMaximum()) );
+ }
+
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/range/b2dmultirange.cxx b/basegfx/source/range/b2dmultirange.cxx
new file mode 100644
index 000000000000..64c3cf5ab813
--- /dev/null
+++ b/basegfx/source/range/b2dmultirange.cxx
@@ -0,0 +1,282 @@
+/*************************************************************************
+ *
+ * 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/range/b2drange.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/range/b2dmultirange.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <boost/bind.hpp>
+#include <boost/mem_fn.hpp>
+#include <algorithm>
+#include <vector>
+
+
+namespace basegfx
+{
+ class ImplB2DMultiRange
+ {
+ public:
+ ImplB2DMultiRange() :
+ maBounds(),
+ maRanges()
+ {
+ }
+
+ explicit ImplB2DMultiRange( const B2DRange& rRange ) :
+ maBounds(),
+ maRanges( 1, rRange )
+ {
+ }
+
+ bool isEmpty() const
+ {
+ // no ranges at all, or all ranges empty
+ return maRanges.empty() ||
+ ::std::count_if( maRanges.begin(),
+ maRanges.end(),
+ ::boost::mem_fn( &B2DRange::isEmpty ) )
+ == static_cast<VectorOfRanges::difference_type>(maRanges.size());
+ }
+
+ void reset()
+ {
+ // swap in empty vector
+ VectorOfRanges aTmp;
+ maRanges.swap( aTmp );
+
+ maBounds.reset();
+ }
+
+ template< typename ValueType > bool isInside( const ValueType& rValue ) const
+ {
+ if( !maBounds.isInside( rValue ) )
+ return false;
+
+ // cannot use ::boost::bind here, since isInside is overloaded.
+ // It is currently not possible to resolve the overload
+ // by considering one of the other template arguments.
+ VectorOfRanges::const_iterator aCurr( maRanges.begin() );
+ const VectorOfRanges::const_iterator aEnd ( maRanges.end() );
+ while( aCurr != aEnd )
+ if( aCurr->isInside( rValue ) )
+ return true;
+
+ return false;
+ }
+
+ bool overlaps( const B2DRange& rRange ) const
+ {
+ if( !maBounds.overlaps( rRange ) )
+ return false;
+
+ const VectorOfRanges::const_iterator aEnd( maRanges.end() );
+ return ::std::find_if( maRanges.begin(),
+ aEnd,
+ ::boost::bind<bool>( ::boost::mem_fn( &B2DRange::overlaps ),
+ _1,
+ rRange ) ) != aEnd;
+ }
+
+ void addRange( const B2DRange& rRange )
+ {
+ maRanges.push_back( rRange );
+ maBounds.expand( rRange );
+ }
+
+ B2DRange getBounds() const
+ {
+ return maBounds;
+ }
+
+ B2DPolyPolygon getPolyPolygon() const
+ {
+ B2DPolyPolygon aRes;
+
+ // Make range vector unique ( have to avoid duplicate
+ // rectangles. The polygon clipper will return an empty
+ // result in this case).
+ VectorOfRanges aUniqueRanges;
+ aUniqueRanges.reserve( maRanges.size() );
+
+ VectorOfRanges::const_iterator aCurr( maRanges.begin() );
+ const VectorOfRanges::const_iterator aEnd ( maRanges.end() );
+ while( aCurr != aEnd )
+ {
+ // TODO(F3): It's plain wasted resources to apply a
+ // general clipping algorithm to the problem at
+ // hand. Go for a dedicated, scan-line-based approach.
+ VectorOfRanges::const_iterator aCurrScan( aCurr+1 );
+ VectorOfRanges::const_iterator aFound( aEnd );
+ while( aCurrScan != aEnd )
+ {
+ if( aCurrScan->equal( *aCurr ) ||
+ aCurrScan->isInside( *aCurr ) )
+ {
+ // current probe is equal to aCurr, or
+ // completely contains aCurr. Thus, stop
+ // searching, because aCurr is definitely not
+ // a member of the unique rect list
+ aFound = aCurrScan;
+ break;
+ }
+
+ ++aCurrScan;
+ }
+
+ if( aFound == aEnd )
+ {
+ // check whether aCurr is fully contained in one
+ // of the already added rects. If yes, we can skip
+ // it.
+ bool bUnique( true );
+ VectorOfRanges::const_iterator aCurrUnique( aUniqueRanges.begin() );
+ VectorOfRanges::const_iterator aEndUnique ( aUniqueRanges.end() );
+ while( aCurrUnique != aEndUnique )
+ {
+ if( aCurrUnique->isInside( *aCurr ) )
+ {
+ // fully contained, no need to add
+ bUnique = false;
+ break;
+ }
+
+ ++aCurrUnique;
+ }
+
+ if( bUnique )
+ aUniqueRanges.push_back( *aCurr );
+ }
+
+ ++aCurr;
+ }
+
+ VectorOfRanges::const_iterator aCurrUnique( aUniqueRanges.begin() );
+ const VectorOfRanges::const_iterator aEndUnique ( aUniqueRanges.end() );
+ while( aCurrUnique != aEndUnique )
+ {
+ // simply merge all unique parts (OR)
+ aRes.append( tools::createPolygonFromRect( *aCurrUnique++ ) );
+ }
+
+ // remove redundant intersections. Note: since all added
+ // rectangles are positively oriented, this method cannot
+ // generate any holes.
+ aRes = basegfx::tools::solveCrossovers(aRes);
+ aRes = basegfx::tools::stripNeutralPolygons(aRes);
+ aRes = basegfx::tools::stripDispensablePolygons(aRes, false);
+
+ return aRes;
+ }
+
+ private:
+ typedef ::std::vector< B2DRange > VectorOfRanges;
+
+ B2DRange maBounds;
+ VectorOfRanges maRanges;
+ };
+
+
+ // ====================================================================
+
+
+ B2DMultiRange::B2DMultiRange() :
+ mpImpl()
+ {
+ }
+
+ B2DMultiRange::B2DMultiRange( const B2DRange& rRange ) :
+ mpImpl( ImplB2DMultiRange( rRange ) )
+ {
+ }
+
+ B2DMultiRange::~B2DMultiRange()
+ {
+ // otherwise, ImplB2DMultiRange would be an incomplete type
+ }
+
+ B2DMultiRange::B2DMultiRange( const B2DMultiRange& rSrc ) :
+ mpImpl( rSrc.mpImpl )
+ {
+ }
+
+ B2DMultiRange& B2DMultiRange::operator=( const B2DMultiRange& rSrc )
+ {
+ mpImpl = rSrc.mpImpl;
+ return *this;
+ }
+
+ bool B2DMultiRange::isEmpty() const
+ {
+ return mpImpl->isEmpty();
+ }
+
+ void B2DMultiRange::reset()
+ {
+ mpImpl->reset();
+ }
+
+ bool B2DMultiRange::isInside( const B2DTuple& rTuple ) const
+ {
+ return mpImpl->isInside( rTuple );
+ }
+
+ bool B2DMultiRange::isInside( const B2DRange& rRange ) const
+ {
+ return mpImpl->isInside( rRange );
+ }
+
+ bool B2DMultiRange::overlaps( const B2DRange& rRange ) const
+ {
+ return mpImpl->overlaps( rRange );
+ }
+
+ void B2DMultiRange::addRange( const B2DRange& rRange )
+ {
+ mpImpl->addRange( rRange );
+ }
+
+ B2DRange B2DMultiRange::getBounds() const
+ {
+ return mpImpl->getBounds();
+ }
+
+ B2DPolyPolygon B2DMultiRange::getPolyPolygon() const
+ {
+ return mpImpl->getPolyPolygon();
+ }
+
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/range/b2drange.cxx b/basegfx/source/range/b2drange.cxx
new file mode 100644
index 000000000000..2508224bf2d8
--- /dev/null
+++ b/basegfx/source/range/b2drange.cxx
@@ -0,0 +1,77 @@
+/*************************************************************************
+ *
+ * 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: b2drange.cxx,v $
+ * $Revision: 1.11 $
+ *
+ * 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/range/b2drange.hxx>
+#include <basegfx/range/b2irange.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+
+namespace basegfx
+{
+ B2DRange::B2DRange( const B2IRange& rRange ) :
+ maRangeX(),
+ maRangeY()
+ {
+ if( !rRange.isEmpty() )
+ {
+ maRangeX = rRange.getMinX();
+ maRangeY = rRange.getMinY();
+
+ maRangeX.expand(rRange.getMaxX());
+ maRangeY.expand(rRange.getMaxY());
+ }
+ }
+
+ void B2DRange::transform(const B2DHomMatrix& rMatrix)
+ {
+ if(!isEmpty() && !rMatrix.isIdentity())
+ {
+ const B2DRange aSource(*this);
+ reset();
+ expand(rMatrix * B2DPoint(aSource.getMinX(), aSource.getMinY()));
+ expand(rMatrix * B2DPoint(aSource.getMaxX(), aSource.getMinY()));
+ expand(rMatrix * B2DPoint(aSource.getMinX(), aSource.getMaxY()));
+ expand(rMatrix * B2DPoint(aSource.getMaxX(), aSource.getMaxY()));
+ }
+ }
+
+ B2IRange fround(const B2DRange& rRange)
+ {
+ return rRange.isEmpty() ?
+ B2IRange() :
+ B2IRange(fround(rRange.getMinimum()),
+ fround(rRange.getMaximum()));
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/range/b2xrange.cxx b/basegfx/source/range/b2xrange.cxx
new file mode 100644
index 000000000000..aa373cc38005
--- /dev/null
+++ b/basegfx/source/range/b2xrange.cxx
@@ -0,0 +1,145 @@
+/*************************************************************************
+ *
+ * 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: b2xrange.cxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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/range/b2drange.hxx>
+#include <basegfx/range/b2irange.hxx>
+#include <basegfx/range/b2ibox.hxx>
+
+
+namespace basegfx
+{
+ namespace
+ {
+ /** Generic implementation of the difference set computation
+
+ @tpl RangeType
+ Type to operate on. Must provide ValueType and TraitsType
+ nested types.
+ */
+ template< class RangeType > void doComputeSetDifference(
+ ::std::vector< RangeType >& o_rRanges,
+ const RangeType& a,
+ const RangeType& b )
+ {
+ o_rRanges.clear();
+
+ // special-casing the empty rect case (this will fail most
+ // of the times below, because of the DBL_MIN/MAX special
+ // values denoting emptyness in the rectangle.
+ if( a.isEmpty() )
+ {
+ o_rRanges.push_back( b );
+ return;
+ }
+ if( b.isEmpty() )
+ {
+ o_rRanges.push_back( a );
+ return;
+ }
+
+ const typename RangeType::ValueType ax(a.getMinX());
+ const typename RangeType::ValueType ay(a.getMinY());
+ const typename RangeType::TraitsType::DifferenceType aw(a.getWidth());
+ const typename RangeType::TraitsType::DifferenceType ah(a.getHeight());
+ const typename RangeType::ValueType bx(b.getMinX());
+ const typename RangeType::ValueType by(b.getMinY());
+ const typename RangeType::TraitsType::DifferenceType bw(b.getWidth());
+ const typename RangeType::TraitsType::DifferenceType bh(b.getHeight());
+
+ const typename RangeType::TraitsType::DifferenceType h0( (by > ay) ? by - ay : 0 );
+ const typename RangeType::TraitsType::DifferenceType h3( (by + bh < ay + ah) ? ay + ah - by - bh : 0 );
+ const typename RangeType::TraitsType::DifferenceType w1( (bx > ax) ? bx - ax : 0 );
+ const typename RangeType::TraitsType::DifferenceType w2( (ax + aw > bx + bw) ? ax + aw - bx - bw : 0 );
+ const typename RangeType::TraitsType::DifferenceType h12( (h0 + h3 < ah) ? ah - h0 - h3 : 0 );
+
+ // TODO(E2): Use numeric_cast instead of static_cast here,
+ // need range checks!
+ if (h0 > 0)
+ o_rRanges.push_back(
+ RangeType(ax,ay,
+ static_cast<typename RangeType::ValueType>(ax+aw),
+ static_cast<typename RangeType::ValueType>(ay+h0)) );
+
+ if (w1 > 0 && h12 > 0)
+ o_rRanges.push_back(
+ RangeType(ax,
+ static_cast<typename RangeType::ValueType>(ay+h0),
+ static_cast<typename RangeType::ValueType>(ax+w1),
+ static_cast<typename RangeType::ValueType>(ay+h0+h12)) );
+
+ if (w2 > 0 && h12 > 0)
+ o_rRanges.push_back(
+ RangeType(static_cast<typename RangeType::ValueType>(bx+bw),
+ static_cast<typename RangeType::ValueType>(ay+h0),
+ static_cast<typename RangeType::ValueType>(bx+bw+w2),
+ static_cast<typename RangeType::ValueType>(ay+h0+h12)) );
+
+ if (h3 > 0)
+ o_rRanges.push_back(
+ RangeType(ax,
+ static_cast<typename RangeType::ValueType>(ay+h0+h12),
+ static_cast<typename RangeType::ValueType>(ax+aw),
+ static_cast<typename RangeType::ValueType>(ay+h0+h12+h3)) );
+ }
+ }
+
+ ::std::vector< B2IRange >& computeSetDifference( ::std::vector< B2IRange >& o_rResult,
+ const B2IRange& rFirst,
+ const B2IRange& rSecond )
+ {
+ doComputeSetDifference( o_rResult, rFirst, rSecond );
+
+ return o_rResult;
+ }
+
+ ::std::vector< B2DRange >& computeSetDifference( ::std::vector< B2DRange >& o_rResult,
+ const B2DRange& rFirst,
+ const B2DRange& rSecond )
+ {
+ doComputeSetDifference( o_rResult, rFirst, rSecond );
+
+ return o_rResult;
+ }
+
+ ::std::vector< B2IBox >& computeSetDifference( ::std::vector< B2IBox >& o_rResult,
+ const B2IBox& rFirst,
+ const B2IBox& rSecond )
+ {
+ doComputeSetDifference( o_rResult, rFirst, rSecond );
+
+ return o_rResult;
+ }
+
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/range/b3drange.cxx b/basegfx/source/range/b3drange.cxx
new file mode 100644
index 000000000000..6de46e981509
--- /dev/null
+++ b/basegfx/source/range/b3drange.cxx
@@ -0,0 +1,88 @@
+/*************************************************************************
+ *
+ * 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: b3drange.cxx,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/range/b3drange.hxx>
+#include <basegfx/range/b3irange.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+
+namespace basegfx
+{
+ B3DRange::B3DRange(const B3IRange& rRange) :
+ maRangeX(),
+ maRangeY(),
+ maRangeZ()
+ {
+ if( !rRange.isEmpty() )
+ {
+ maRangeX = rRange.getMinX();
+ maRangeY = rRange.getMinY();
+ maRangeZ = rRange.getMinZ();
+
+ maRangeX.expand( rRange.getMaxX() );
+ maRangeY.expand( rRange.getMaxY() );
+ maRangeZ.expand( rRange.getMaxZ() );
+ }
+ }
+
+ void B3DRange::transform(const B3DHomMatrix& rMatrix)
+ {
+ if(!isEmpty() && !rMatrix.isIdentity())
+ {
+ const B3DRange aSource(*this);
+ reset();
+ expand(rMatrix * B3DPoint(aSource.getMinX(), aSource.getMinY(), aSource.getMinZ()));
+ expand(rMatrix * B3DPoint(aSource.getMaxX(), aSource.getMinY(), aSource.getMinZ()));
+ expand(rMatrix * B3DPoint(aSource.getMinX(), aSource.getMaxY(), aSource.getMinZ()));
+ expand(rMatrix * B3DPoint(aSource.getMaxX(), aSource.getMaxY(), aSource.getMinZ()));
+ expand(rMatrix * B3DPoint(aSource.getMinX(), aSource.getMinY(), aSource.getMaxZ()));
+ expand(rMatrix * B3DPoint(aSource.getMaxX(), aSource.getMinY(), aSource.getMaxZ()));
+ expand(rMatrix * B3DPoint(aSource.getMinX(), aSource.getMaxY(), aSource.getMaxZ()));
+ expand(rMatrix * B3DPoint(aSource.getMaxX(), aSource.getMaxY(), aSource.getMaxZ()));
+ }
+ }
+
+ B3IRange fround(const B3DRange& rRange )
+ {
+ return rRange.isEmpty() ?
+ B3IRange() :
+ B3IRange(fround(rRange.getMinX()),
+ fround(rRange.getMinY()),
+ fround(rRange.getMinZ()),
+ fround(rRange.getMaxX()),
+ fround(rRange.getMaxY()),
+ fround(rRange.getMaxZ()));
+ }
+
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/range/makefile.mk b/basegfx/source/range/makefile.mk
new file mode 100644
index 000000000000..678bb16a9312
--- /dev/null
+++ b/basegfx/source/range/makefile.mk
@@ -0,0 +1,55 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.6 $
+#
+# 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=range
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+ENABLE_EXCEPTIONS=TRUE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/b1drange.obj \
+ $(SLO)$/b2drange.obj \
+ $(SLO)$/b2xrange.obj \
+ $(SLO)$/b2dmultirange.obj \
+ $(SLO)$/b3drange.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/raster/bpixelraster.cxx b/basegfx/source/raster/bpixelraster.cxx
new file mode 100644
index 000000000000..e08d0bce293e
--- /dev/null
+++ b/basegfx/source/raster/bpixelraster.cxx
@@ -0,0 +1,44 @@
+/*************************************************************************
+ *
+ * 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: bpixelraster.cxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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/raster/bpixelraster.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/raster/bzpixelraster.cxx b/basegfx/source/raster/bzpixelraster.cxx
new file mode 100644
index 000000000000..5c5e0a839e62
--- /dev/null
+++ b/basegfx/source/raster/bzpixelraster.cxx
@@ -0,0 +1,44 @@
+/*************************************************************************
+ *
+ * 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: bzpixelraster.cxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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/raster/bzpixelraster.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/raster/makefile.mk b/basegfx/source/raster/makefile.mk
new file mode 100644
index 000000000000..5f21a37ef247
--- /dev/null
+++ b/basegfx/source/raster/makefile.mk
@@ -0,0 +1,53 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.2 $
+#
+# 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=raster
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+ENABLE_EXCEPTIONS=TRUE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/bpixelraster.obj \
+ $(SLO)$/bzpixelraster.obj \
+ $(SLO)$/rasterconvert3d.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/raster/rasterconvert3d.cxx b/basegfx/source/raster/rasterconvert3d.cxx
new file mode 100644
index 000000000000..131a5f127cd8
--- /dev/null
+++ b/basegfx/source/raster/rasterconvert3d.cxx
@@ -0,0 +1,357 @@
+/*************************************************************************
+ *
+ * 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: rasterconvert3d.cxx,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * 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/raster/rasterconvert3d.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+// implementations of the 3D raster converter
+
+namespace basegfx
+{
+ void RasterConverter3D::addArea(const B3DPolygon& rFill, const B3DHomMatrix* pViewToEye)
+ {
+ const sal_uInt32 nPointCount(rFill.count());
+
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ addEdge(rFill, a, (a + 1) % nPointCount, pViewToEye);
+ }
+ }
+
+ void RasterConverter3D::addArea(const B3DPolyPolygon& rFill, const B3DHomMatrix* pViewToEye)
+ {
+ const sal_uInt32 nPolyCount(rFill.count());
+
+ for(sal_uInt32 a(0); a < nPolyCount; a++)
+ {
+ addArea(rFill.getB3DPolygon(a), pViewToEye);
+ }
+ }
+
+ RasterConverter3D::RasterConverter3D()
+ : InterpolatorProvider3D(),
+ maLineEntries()
+ {}
+
+ RasterConverter3D::~RasterConverter3D()
+ {}
+
+ void RasterConverter3D::rasterconvertB3DArea(sal_Int32 nStartLine, sal_Int32 nStopLine)
+ {
+ if(maLineEntries.size())
+ {
+ OSL_ENSURE(nStopLine >= nStartLine, "nStopLine is bigger than nStartLine (!)");
+
+ // sort global entries by Y, X once. After this, the vector
+ // is seen as frozen. Pointers to it's entries will be used in the following code.
+ ::std::sort(maLineEntries.begin(), maLineEntries.end());
+
+ // local parameters
+ ::std::vector< RasterConversionLineEntry3D >::iterator aCurrentEntry(maLineEntries.begin());
+ ::std::vector< RasterConversionLineEntry3D* > aCurrentLine;
+ ::std::vector< RasterConversionLineEntry3D* > aNextLine;
+ ::std::vector< RasterConversionLineEntry3D* >::iterator aRasterConversionLineEntry3D;
+ sal_uInt32 nPairCount(0);
+
+ // get scanlines first LineNumber as start
+ sal_Int32 nLineNumber(::std::max(aCurrentEntry->getY(), nStartLine));
+
+ while((aCurrentLine.size() || aCurrentEntry != maLineEntries.end()) && (nLineNumber < nStopLine))
+ {
+ // add all entries which start at current line to current scanline
+ while(aCurrentEntry != maLineEntries.end())
+ {
+ const sal_Int32 nCurrentLineNumber(aCurrentEntry->getY());
+
+ if(nCurrentLineNumber > nLineNumber)
+ {
+ // line is below current one, done (since array is sorted)
+ break;
+ }
+ else
+ {
+ // less or equal. Line is above or at current one. Advance it exactly to
+ // current line
+ const sal_uInt32 nStep(nLineNumber - nCurrentLineNumber);
+
+ if(!nStep || aCurrentEntry->decrementRasterConversionLineEntry3D(nStep))
+ {
+ // add when exactly on current line or when incremet to it did not
+ // completely consume it
+ if(nStep)
+ {
+ aCurrentEntry->incrementRasterConversionLineEntry3D(nStep, *this);
+ }
+
+ aCurrentLine.push_back(&(*(aCurrentEntry)));
+ }
+ }
+
+ aCurrentEntry++;
+ }
+
+ // sort current scanline using comparator. Only X is used there
+ // since all entries are already in one processed line. This needs to be done
+ // everytime since not only new spans may have benn added or old removed,
+ // but incrementing may also have changed the order
+ ::std::sort(aCurrentLine.begin(), aCurrentLine.end(), lineComparator());
+
+ // process current scanline
+ aRasterConversionLineEntry3D = aCurrentLine.begin();
+ aNextLine.clear();
+ nPairCount = 0;
+
+ while(aRasterConversionLineEntry3D != aCurrentLine.end())
+ {
+ RasterConversionLineEntry3D& rPrevScanRasterConversionLineEntry3D(**aRasterConversionLineEntry3D++);
+
+ // look for 2nd span
+ if(aRasterConversionLineEntry3D != aCurrentLine.end())
+ {
+ // work on span from rPrevScanRasterConversionLineEntry3D to aRasterConversionLineEntry3D, fLineNumber is valid
+ processLineSpan(rPrevScanRasterConversionLineEntry3D, **aRasterConversionLineEntry3D, nLineNumber, nPairCount++);
+ }
+
+ // increment to next line
+ if(rPrevScanRasterConversionLineEntry3D.decrementRasterConversionLineEntry3D(1))
+ {
+ rPrevScanRasterConversionLineEntry3D.incrementRasterConversionLineEntry3D(1, *this);
+ aNextLine.push_back(&rPrevScanRasterConversionLineEntry3D);
+ }
+ }
+
+ // copy back next scanline if count has changed
+ if(aNextLine.size() != aCurrentLine.size())
+ {
+ aCurrentLine = aNextLine;
+ }
+
+ // increment fLineNumber
+ nLineNumber++;
+ }
+ }
+ }
+
+ void RasterConverter3D::addEdge(const B3DPolygon& rFill, sal_uInt32 a, sal_uInt32 b, const B3DHomMatrix* pViewToEye)
+ {
+ B3DPoint aStart(rFill.getB3DPoint(a));
+ B3DPoint aEnd(rFill.getB3DPoint(b));
+ sal_Int32 nYStart(fround(aStart.getY()));
+ sal_Int32 nYEnd(fround(aEnd.getY()));
+
+ if(nYStart != nYEnd)
+ {
+ if(nYStart > nYEnd)
+ {
+ ::std::swap(aStart, aEnd);
+ ::std::swap(nYStart, nYEnd);
+ ::std::swap(a, b);
+ }
+
+ const sal_uInt32 nYDelta(nYEnd - nYStart);
+ const double fInvYDelta(1.0 / nYDelta);
+ maLineEntries.push_back(RasterConversionLineEntry3D(
+ aStart.getX(), (aEnd.getX() - aStart.getX()) * fInvYDelta,
+ aStart.getZ(), (aEnd.getZ() - aStart.getZ()) * fInvYDelta,
+ nYStart, nYDelta));
+
+ // if extra interpolation data is used, add it to the last created entry
+ RasterConversionLineEntry3D& rEntry = maLineEntries[maLineEntries.size() - 1];
+
+ if(rFill.areBColorsUsed())
+ {
+ rEntry.setColorIndex(addColorInterpolator(rFill.getBColor(a), rFill.getBColor(b), fInvYDelta));
+ }
+
+ if(rFill.areNormalsUsed())
+ {
+ rEntry.setNormalIndex(addNormalInterpolator(rFill.getNormal(a), rFill.getNormal(b), fInvYDelta));
+ }
+
+ if(rFill.areTextureCoordinatesUsed())
+ {
+ if(pViewToEye)
+ {
+ const double fEyeA(((*pViewToEye) * aStart).getZ());
+ const double fEyeB(((*pViewToEye) * aEnd).getZ());
+
+ rEntry.setInverseTextureIndex(addInverseTextureInterpolator(
+ rFill.getTextureCoordinate(a),
+ rFill.getTextureCoordinate(b),
+ fEyeA, fEyeB, fInvYDelta));
+ }
+ else
+ {
+ rEntry.setTextureIndex(addTextureInterpolator(
+ rFill.getTextureCoordinate(a),
+ rFill.getTextureCoordinate(b),
+ fInvYDelta));
+ }
+ }
+ }
+ }
+
+ void RasterConverter3D::rasterconvertB3DEdge(const B3DPolygon& rLine, sal_uInt32 nA, sal_uInt32 nB, sal_Int32 nStartLine, sal_Int32 nStopLine, sal_uInt16 nLineWidth)
+ {
+ B3DPoint aStart(rLine.getB3DPoint(nA));
+ B3DPoint aEnd(rLine.getB3DPoint(nB));
+ const double fZBufferLineAdd(0x00ff);
+ static bool bForceToPolygon(false);
+
+ if(nLineWidth > 1 || bForceToPolygon)
+ {
+ // this is not a hairline anymore, in most cases since it's an oversampled
+ // hairline to get e.g. AA for Z-Buffering. Create fill geometry.
+ if(!aStart.equal(aEnd))
+ {
+ reset();
+ maLineEntries.clear();
+
+ B2DVector aVector(aEnd.getX() - aStart.getX(), aEnd.getY() - aStart.getY());
+ aVector.normalize();
+ const B2DVector aPerpend(getPerpendicular(aVector) * ((static_cast<double>(nLineWidth) + 0.5) * 0.5));
+ const double fZStartWithAdd(aStart.getZ() + fZBufferLineAdd);
+ const double fZEndWithAdd(aEnd.getZ() + fZBufferLineAdd);
+
+ B3DPolygon aPolygon;
+ aPolygon.append(B3DPoint(aStart.getX() + aPerpend.getX(), aStart.getY() + aPerpend.getY(), fZStartWithAdd));
+ aPolygon.append(B3DPoint(aEnd.getX() + aPerpend.getX(), aEnd.getY() + aPerpend.getY(), fZEndWithAdd));
+ aPolygon.append(B3DPoint(aEnd.getX() - aPerpend.getX(), aEnd.getY() - aPerpend.getY(), fZEndWithAdd));
+ aPolygon.append(B3DPoint(aStart.getX() - aPerpend.getX(), aStart.getY() - aPerpend.getY(), fZStartWithAdd));
+ aPolygon.setClosed(true);
+
+ addArea(aPolygon, 0);
+ }
+ }
+ else
+ {
+ // it's a hairline. Use direct RasterConversionLineEntry creation to
+ // rasterconvert lines as similar to areas as possible to avoid Z-Fighting
+ sal_Int32 nYStart(fround(aStart.getY()));
+ sal_Int32 nYEnd(fround(aEnd.getY()));
+
+ if(nYStart == nYEnd)
+ {
+ // horizontal line, check X
+ const sal_Int32 nXStart(static_cast<sal_Int32>(aStart.getX()));
+ const sal_Int32 nXEnd(static_cast<sal_Int32>(aEnd.getX()));
+
+ if(nXStart != nXEnd)
+ {
+ reset();
+ maLineEntries.clear();
+
+ // horizontal line, create vertical entries. These will be sorted by
+ // X anyways, so no need to distinguish the case here
+ maLineEntries.push_back(RasterConversionLineEntry3D(
+ aStart.getX(), 0.0,
+ aStart.getZ() + fZBufferLineAdd, 0.0,
+ nYStart, 1));
+ maLineEntries.push_back(RasterConversionLineEntry3D(
+ aEnd.getX(), 0.0,
+ aEnd.getZ() + fZBufferLineAdd, 0.0,
+ nYStart, 1));
+ }
+ }
+ else
+ {
+ reset();
+ maLineEntries.clear();
+
+ if(nYStart > nYEnd)
+ {
+ ::std::swap(aStart, aEnd);
+ ::std::swap(nYStart, nYEnd);
+ }
+
+ const sal_uInt32 nYDelta(static_cast<sal_uInt32>(nYEnd - nYStart));
+ const double fInvYDelta(1.0 / nYDelta);
+
+ // non-horizontal line, create two parallell entries. These will be sorted by
+ // X anyways, so no need to distinguish the case here
+ maLineEntries.push_back(RasterConversionLineEntry3D(
+ aStart.getX(), (aEnd.getX() - aStart.getX()) * fInvYDelta,
+ aStart.getZ() + fZBufferLineAdd, (aEnd.getZ() - aStart.getZ()) * fInvYDelta,
+ nYStart, nYDelta));
+
+ RasterConversionLineEntry3D& rEntry = maLineEntries[maLineEntries.size() - 1];
+
+ // need to choose a X-Distance for the 2nd edge which guarantees all pixels
+ // of the line to be set. This is exactly the X-Increment for one Y-Step.
+ // Same is true for Z, so in both cases, add one increment to them. To also
+ // guarantee one pixel per line, add a minimum of one for X.
+ const double fDistanceX(fabs(rEntry.getX().getInc()) >= 1.0 ? rEntry.getX().getInc() : 1.0);
+
+ maLineEntries.push_back(RasterConversionLineEntry3D(
+ rEntry.getX().getVal() + fDistanceX, rEntry.getX().getInc(),
+ rEntry.getZ().getVal() + rEntry.getZ().getInc(), rEntry.getZ().getInc(),
+ nYStart, nYDelta));
+ }
+ }
+
+ if(maLineEntries.size())
+ {
+ rasterconvertB3DArea(nStartLine, nStopLine);
+ }
+ }
+
+ void RasterConverter3D::rasterconvertB3DPolyPolygon(const B3DPolyPolygon& rFill, const B3DHomMatrix* pViewToEye, sal_Int32 nStartLine, sal_Int32 nStopLine)
+ {
+ reset();
+ maLineEntries.clear();
+ addArea(rFill, pViewToEye);
+ rasterconvertB3DArea(nStartLine, nStopLine);
+ }
+
+ void RasterConverter3D::rasterconvertB3DPolygon(const B3DPolygon& rLine, sal_Int32 nStartLine, sal_Int32 nStopLine, sal_uInt16 nLineWidth)
+ {
+ const sal_uInt32 nPointCount(rLine.count());
+
+ if(nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(rLine.isClosed() ? nPointCount : nPointCount - 1);
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ rasterconvertB3DEdge(rLine, a, (a + 1) % nPointCount, nStartLine, nStopLine, nLineWidth);
+ }
+ }
+ }
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/tools/canvastools.cxx b/basegfx/source/tools/canvastools.cxx
new file mode 100755
index 000000000000..3aae34eaa3a8
--- /dev/null
+++ b/basegfx/source/tools/canvastools.cxx
@@ -0,0 +1,677 @@
+/*************************************************************************
+ *
+ * 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.cxx,v $
+ * $Revision: 1.12 $
+ *
+ * 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
diff --git a/basegfx/source/tools/debugplotter.cxx b/basegfx/source/tools/debugplotter.cxx
new file mode 100755
index 000000000000..b1576bb910d1
--- /dev/null
+++ b/basegfx/source/tools/debugplotter.cxx
@@ -0,0 +1,416 @@
+/*************************************************************************
+ *
+ * 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: debugplotter.cxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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>
+
+
+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 );
+ }
+}
diff --git a/basegfx/source/tools/gradienttools.cxx b/basegfx/source/tools/gradienttools.cxx
new file mode 100644
index 000000000000..9e78039cd590
--- /dev/null
+++ b/basegfx/source/tools/gradienttools.cxx
@@ -0,0 +1,366 @@
+/*************************************************************************
+ *
+ * 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.cxx,v $
+ * $Revision: 1.12 $
+ *
+ * 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>
+
+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;
+
+ 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;
+ }
+
+ // add object scale before rotate
+ o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY);
+
+ // add texture rotate after scale to keep perpendicular angles
+ if(0.0 != fAngle)
+ {
+ B2DPoint aCenter(0.5, 0.5);
+ aCenter *= o_rGradientInfo.maTextureTransform;
+
+ o_rGradientInfo.maTextureTransform.translate(-aCenter.getX(), -aCenter.getY());
+ o_rGradientInfo.maTextureTransform.rotate(fAngle);
+ o_rGradientInfo.maTextureTransform.translate(aCenter.getX(), aCenter.getY());
+ }
+
+ // 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();
+
+ double fSizeWithoutBorder=0;
+ if( bAxial )
+ {
+ fSizeWithoutBorder = (1.0 - fBorder) * 0.5;
+ o_rGradientInfo.maBackTextureTransform.translate(0.0, -0.5);
+ }
+ else
+ {
+ fSizeWithoutBorder = 1.0 - fBorder;
+ o_rGradientInfo.maBackTextureTransform.translate(0.0, -fBorder);
+ }
+
+ if(!fTools::equal(fSizeWithoutBorder, 0.0))
+ o_rGradientInfo.maBackTextureTransform.scale(1.0, 1.0 / fSizeWithoutBorder);
+ }
+
+ /** 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;
+
+ 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;
+ }
+
+ // add object scale before rotate
+ o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY);
+
+ if( !bCircular )
+ {
+ // add texture rotate after scale to keep perpendicular angles
+ if(0.0 != fAngle)
+ {
+ B2DPoint aCenter(0.5, 0.5);
+ aCenter *= o_rGradientInfo.maTextureTransform;
+
+ o_rGradientInfo.maTextureTransform.translate(-aCenter.getX(), -aCenter.getY());
+ o_rGradientInfo.maTextureTransform.rotate(fAngle);
+ o_rGradientInfo.maTextureTransform.translate(aCenter.getX(), aCenter.getY());
+ }
+ }
+
+ // 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]. As base, use inverse texture transform
+ o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform;
+ o_rGradientInfo.maBackTextureTransform.invert();
+ o_rGradientInfo.maBackTextureTransform.translate(-0.5, -0.5);
+ const double fHalfBorder((1.0 - fBorder) * 0.5);
+
+ if(!fTools::equal(fHalfBorder, 0.0))
+ {
+ const double fFactor(1.0 / fHalfBorder);
+ o_rGradientInfo.maBackTextureTransform.scale(fFactor, fFactor);
+ }
+ }
+
+ /** 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;
+
+ 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;
+ }
+
+ // add object scale before rotate
+ o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY);
+
+ // add texture rotate after scale to keep perpendicular angles
+ if(0.0 != fAngle)
+ {
+ B2DPoint aCenter(0.5, 0.5);
+ aCenter *= o_rGradientInfo.maTextureTransform;
+
+ o_rGradientInfo.maTextureTransform.translate(-aCenter.getX(), -aCenter.getY());
+ o_rGradientInfo.maTextureTransform.rotate(fAngle);
+ o_rGradientInfo.maTextureTransform.translate(aCenter.getX(), aCenter.getY());
+ }
+
+ // 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();
+ o_rGradientInfo.maBackTextureTransform.translate(-0.5, -0.5);
+ const double fHalfBorder((1.0 - fBorder) * 0.5);
+
+ if(!fTools::equal(fHalfBorder, 0.0))
+ {
+ const double fFactor(1.0 / fHalfBorder);
+ o_rGradientInfo.maBackTextureTransform.scale(fFactor, fFactor);
+ }
+ }
+
+ 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
diff --git a/basegfx/source/tools/liangbarsky.cxx b/basegfx/source/tools/liangbarsky.cxx
new file mode 100644
index 000000000000..1e1f4e23e9f8
--- /dev/null
+++ b/basegfx/source/tools/liangbarsky.cxx
@@ -0,0 +1,135 @@
+/*************************************************************************
+ *
+ * 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: liangbarsky.cxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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;
+ }
+ }
+}
diff --git a/basegfx/source/tools/makefile.mk b/basegfx/source/tools/makefile.mk
new file mode 100755
index 000000000000..1bede8b22d88
--- /dev/null
+++ b/basegfx/source/tools/makefile.mk
@@ -0,0 +1,53 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.6 $
+#
+# 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)$/canvastools.obj \
+ $(SLO)$/gradienttools.obj \
+ $(SLO)$/debugplotter.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..b81b52a1bd61
--- /dev/null
+++ b/basegfx/source/tools/tools.cxx
@@ -0,0 +1,127 @@
+/*************************************************************************
+ *
+ * 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: tools.cxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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 );
+ }
+ }
+}
diff --git a/basegfx/source/tools/unopolypolygon.cxx b/basegfx/source/tools/unopolypolygon.cxx
new file mode 100755
index 000000000000..6d8fcd83edb0
--- /dev/null
+++ b/basegfx/source/tools/unopolypolygon.cxx
@@ -0,0 +1,491 @@
+/*************************************************************************
+ *
+ * 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: unopolypolygon.cxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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>
+
+
+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() )
+ {
+ B2DHomMatrix aTranslate;
+ aTranslate.translate( aOffset.getX(), aOffset.getY() );
+
+ 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;
+ }
+
+}
+}
diff --git a/basegfx/source/tuple/b2dtuple.cxx b/basegfx/source/tuple/b2dtuple.cxx
new file mode 100644
index 000000000000..eae67f5e9244
--- /dev/null
+++ b/basegfx/source/tuple/b2dtuple.cxx
@@ -0,0 +1,87 @@
+/*************************************************************************
+ *
+ * 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: b2dtuple.cxx,v $
+ * $Revision: 1.14 $
+ *
+ * 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/tuple/b2dtuple.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <rtl/instance.hxx>
+
+namespace { struct EmptyTuple : public rtl::Static<basegfx::B2DTuple, EmptyTuple> {}; }
+#include <basegfx/tuple/b2ituple.hxx>
+
+namespace basegfx
+{
+ const B2DTuple& B2DTuple::getEmptyTuple()
+ {
+ return EmptyTuple::get();
+ }
+
+ B2DTuple::B2DTuple(const B2ITuple& rTup)
+ : mfX( rTup.getX() ),
+ mfY( rTup.getY() )
+ {}
+
+ void B2DTuple::correctValues(const double fCompareValue)
+ {
+ if(0.0 == fCompareValue)
+ {
+ if(::basegfx::fTools::equalZero(mfX))
+ {
+ mfX = 0.0;
+ }
+
+ if(::basegfx::fTools::equalZero(mfY))
+ {
+ mfY = 0.0;
+ }
+ }
+ else
+ {
+ if(::basegfx::fTools::equal(mfX, fCompareValue))
+ {
+ mfX = fCompareValue;
+ }
+
+ if(::basegfx::fTools::equal(mfY, fCompareValue))
+ {
+ mfY = fCompareValue;
+ }
+ }
+ }
+
+ B2ITuple fround(const B2DTuple& rTup)
+ {
+ return B2ITuple(fround(rTup.getX()), fround(rTup.getY()));
+ }
+
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/tuple/b2i64tuple.cxx b/basegfx/source/tuple/b2i64tuple.cxx
new file mode 100644
index 000000000000..3b5a60baf432
--- /dev/null
+++ b/basegfx/source/tuple/b2i64tuple.cxx
@@ -0,0 +1,47 @@
+/*************************************************************************
+ *
+ * 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: b2i64tuple.cxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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/tuple/b2i64tuple.hxx>
+#include <rtl/instance.hxx>
+
+namespace { struct EmptyTuple : public rtl::Static<basegfx::B2I64Tuple, EmptyTuple> {}; }
+
+namespace basegfx
+{
+ const B2I64Tuple& B2I64Tuple::getEmptyTuple()
+ {
+ return EmptyTuple::get();
+ }
+
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/tuple/b2ituple.cxx b/basegfx/source/tuple/b2ituple.cxx
new file mode 100644
index 000000000000..6b8149e7326c
--- /dev/null
+++ b/basegfx/source/tuple/b2ituple.cxx
@@ -0,0 +1,156 @@
+/*************************************************************************
+ *
+ * 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: b2ituple.cxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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/tuple/b2ituple.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <rtl/instance.hxx>
+
+namespace { struct EmptyTuple : public rtl::Static<basegfx::B2ITuple, EmptyTuple> {}; }
+
+namespace basegfx
+{
+ const B2ITuple& B2ITuple::getEmptyTuple()
+ {
+ return EmptyTuple::get();
+ }
+
+ // external operators
+ //////////////////////////////////////////////////////////////////////////
+
+ B2ITuple minimum(const B2ITuple& rTupA, const B2ITuple& rTupB)
+ {
+ B2ITuple aMin(
+ (rTupB.getX() < rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() < rTupA.getY()) ? rTupB.getY() : rTupA.getY());
+ return aMin;
+ }
+
+ B2ITuple maximum(const B2ITuple& rTupA, const B2ITuple& rTupB)
+ {
+ B2ITuple aMax(
+ (rTupB.getX() > rTupA.getX()) ? rTupB.getX() : rTupA.getX(),
+ (rTupB.getY() > rTupA.getY()) ? rTupB.getY() : rTupA.getY());
+ return aMax;
+ }
+
+ B2ITuple absolute(const B2ITuple& rTup)
+ {
+ B2ITuple aAbs(
+ (0 > rTup.getX()) ? -rTup.getX() : rTup.getX(),
+ (0 > rTup.getY()) ? -rTup.getY() : rTup.getY());
+ return aAbs;
+ }
+
+ B2DTuple interpolate(const B2ITuple& rOld1, const B2ITuple& rOld2, double t)
+ {
+ B2DTuple aInt(
+ ((rOld2.getX() - rOld1.getX()) * t) + rOld1.getX(),
+ ((rOld2.getY() - rOld1.getY()) * t) + rOld1.getY());
+ return aInt;
+ }
+
+ B2DTuple average(const B2ITuple& rOld1, const B2ITuple& rOld2)
+ {
+ B2DTuple aAvg(
+ (rOld1.getX() + rOld2.getX()) * 0.5,
+ (rOld1.getY() + rOld2.getY()) * 0.5);
+ return aAvg;
+ }
+
+ B2DTuple average(const B2ITuple& rOld1, const B2ITuple& rOld2, const B2ITuple& rOld3)
+ {
+ B2DTuple aAvg(
+ (rOld1.getX() + rOld2.getX() + rOld3.getX()) * (1.0 / 3.0),
+ (rOld1.getY() + rOld2.getY() + rOld3.getY()) * (1.0 / 3.0));
+ return aAvg;
+ }
+
+ B2ITuple operator+(const B2ITuple& rTupA, const B2ITuple& rTupB)
+ {
+ B2ITuple aSum(rTupA);
+ aSum += rTupB;
+ return aSum;
+ }
+
+ B2ITuple operator-(const B2ITuple& rTupA, const B2ITuple& rTupB)
+ {
+ B2ITuple aSub(rTupA);
+ aSub -= rTupB;
+ return aSub;
+ }
+
+ B2ITuple operator/(const B2ITuple& rTupA, const B2ITuple& rTupB)
+ {
+ B2ITuple aDiv(rTupA);
+ aDiv /= rTupB;
+ return aDiv;
+ }
+
+ B2ITuple operator*(const B2ITuple& rTupA, const B2ITuple& rTupB)
+ {
+ B2ITuple aMul(rTupA);
+ aMul *= rTupB;
+ return aMul;
+ }
+
+ B2ITuple operator*(const B2ITuple& rTup, sal_Int32 t)
+ {
+ B2ITuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ B2ITuple operator*(sal_Int32 t, const B2ITuple& rTup)
+ {
+ B2ITuple aNew(rTup);
+ aNew *= t;
+ return aNew;
+ }
+
+ B2ITuple operator/(const B2ITuple& rTup, sal_Int32 t)
+ {
+ B2ITuple aNew(rTup);
+ aNew /= t;
+ return aNew;
+ }
+
+ B2ITuple operator/(sal_Int32 t, const B2ITuple& rTup)
+ {
+ B2ITuple aNew(t, t);
+ B2ITuple aTmp(rTup);
+ aNew /= aTmp;
+ return aNew;
+ }
+
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/tuple/b3dtuple.cxx b/basegfx/source/tuple/b3dtuple.cxx
new file mode 100644
index 000000000000..3b887805132d
--- /dev/null
+++ b/basegfx/source/tuple/b3dtuple.cxx
@@ -0,0 +1,58 @@
+/*************************************************************************
+ *
+ * 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: b3dtuple.cxx,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/tuple/b3dtuple.hxx>
+#include <rtl/instance.hxx>
+
+namespace { struct EmptyTuple : public rtl::Static<basegfx::B3DTuple, EmptyTuple> {}; }
+#include <basegfx/tuple/b3ituple.hxx>
+
+namespace basegfx
+{
+ const B3DTuple& B3DTuple::getEmptyTuple()
+ {
+ return EmptyTuple::get();
+ }
+
+ B3DTuple::B3DTuple(const B3ITuple& rTup)
+ : mfX( rTup.getX() ),
+ mfY( rTup.getY() ),
+ mfZ( rTup.getZ() )
+ {}
+
+ B3ITuple fround(const B3DTuple& rTup)
+ {
+ return B3ITuple(fround(rTup.getX()), fround(rTup.getY()), fround(rTup.getZ()));
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/tuple/b3i64tuple.cxx b/basegfx/source/tuple/b3i64tuple.cxx
new file mode 100644
index 000000000000..8bba0fb14171
--- /dev/null
+++ b/basegfx/source/tuple/b3i64tuple.cxx
@@ -0,0 +1,46 @@
+/*************************************************************************
+ *
+ * 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: b3i64tuple.cxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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/tuple/b3i64tuple.hxx>
+#include <rtl/instance.hxx>
+
+namespace { struct EmptyTuple : public rtl::Static<basegfx::B3I64Tuple, EmptyTuple> {}; }
+
+namespace basegfx
+{
+ const B3I64Tuple& B3I64Tuple::getEmptyTuple()
+ {
+ return EmptyTuple::get();
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/tuple/b3ituple.cxx b/basegfx/source/tuple/b3ituple.cxx
new file mode 100644
index 000000000000..d9a87912a441
--- /dev/null
+++ b/basegfx/source/tuple/b3ituple.cxx
@@ -0,0 +1,46 @@
+/*************************************************************************
+ *
+ * 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: b3ituple.cxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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/tuple/b3ituple.hxx>
+#include <rtl/instance.hxx>
+
+namespace { struct EmptyTuple : public rtl::Static<basegfx::B3ITuple, EmptyTuple> {}; }
+
+namespace basegfx
+{
+ const B3ITuple& B3ITuple::getEmptyTuple()
+ {
+ return EmptyTuple::get();
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/tuple/makefile.mk b/basegfx/source/tuple/makefile.mk
new file mode 100644
index 000000000000..53ccba37dca3
--- /dev/null
+++ b/basegfx/source/tuple/makefile.mk
@@ -0,0 +1,56 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.4 $
+#
+# 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=tuple
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+#ENABLE_EXCEPTIONS=FALSE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/b2dtuple.obj \
+ $(SLO)$/b3dtuple.obj \
+ $(SLO)$/b2ituple.obj \
+ $(SLO)$/b3ituple.obj \
+ $(SLO)$/b2i64tuple.obj \
+ $(SLO)$/b3i64tuple.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/vector/b2dvector.cxx b/basegfx/source/vector/b2dvector.cxx
new file mode 100644
index 000000000000..311372348653
--- /dev/null
+++ b/basegfx/source/vector/b2dvector.cxx
@@ -0,0 +1,222 @@
+/*************************************************************************
+ *
+ * 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: b2dvector.cxx,v $
+ * $Revision: 1.18 $
+ *
+ * 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/vector/b2dvector.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ B2DVector& B2DVector::normalize()
+ {
+ double fLen(scalar(*this));
+
+ if(fTools::equalZero(fLen))
+ {
+ mfX = 0.0;
+ mfY = 0.0;
+ }
+ else
+ {
+ const double fOne(1.0);
+
+ if(!fTools::equal(fOne, fLen))
+ {
+ fLen = sqrt(fLen);
+
+ if(!fTools::equalZero(fLen))
+ {
+ mfX /= fLen;
+ mfY /= fLen;
+ }
+ }
+ }
+
+ return *this;
+ }
+
+ B2DVector& B2DVector::operator=( const B2DTuple& rVec )
+ {
+ mfX = rVec.getX();
+ mfY = rVec.getY();
+ return *this;
+ }
+
+
+ double B2DVector::getLength() const
+ {
+ if(fTools::equalZero(mfX))
+ {
+ return fabs(mfY);
+ }
+ else if(fTools::equalZero(mfY))
+ {
+ return fabs(mfX);
+ }
+
+ return hypot( mfX, mfY );
+ }
+
+ double B2DVector::scalar( const B2DVector& rVec ) const
+ {
+ return((mfX * rVec.mfX) + (mfY * rVec.mfY));
+ }
+
+ double B2DVector::cross( const B2DVector& rVec ) const
+ {
+ return(mfX * rVec.getY() - mfY * rVec.getX());
+ }
+
+ double B2DVector::angle( const B2DVector& rVec ) const
+ {
+ return atan2(mfX * rVec.getY() - mfY * rVec.getX(),
+ mfX * rVec.getX() + mfY * rVec.getY());
+ }
+
+ const B2DVector& B2DVector::getEmptyVector()
+ {
+ return (const B2DVector&) B2DTuple::getEmptyTuple();
+ }
+
+ B2DVector& B2DVector::operator*=( const B2DHomMatrix& rMat )
+ {
+ const double fTempX( rMat.get(0,0)*mfX +
+ rMat.get(0,1)*mfY );
+ const double fTempY( rMat.get(1,0)*mfX +
+ rMat.get(1,1)*mfY );
+ mfX = fTempX;
+ mfY = fTempY;
+
+ return *this;
+ }
+
+ B2DVector& B2DVector::setLength(double fLen)
+ {
+ double fLenNow(scalar(*this));
+
+ if(!fTools::equalZero(fLenNow))
+ {
+ const double fOne(10.0);
+
+ if(!fTools::equal(fOne, fLenNow))
+ {
+ fLen /= sqrt(fLenNow);
+ }
+
+ mfX *= fLen;
+ mfY *= fLen;
+ }
+
+ return *this;
+ }
+
+ bool B2DVector::isNormalized() const
+ {
+ const double fOne(1.0);
+ const double fScalar(scalar(*this));
+
+ return fTools::equal(fOne, fScalar);
+ }
+
+ bool areParallel( const B2DVector& rVecA, const B2DVector& rVecB )
+ {
+ const double fValA(rVecA.getX() * rVecB.getY());
+ const double fValB(rVecA.getY() * rVecB.getX());
+
+ return fTools::equal(fValA, fValB);
+ }
+
+ B2VectorOrientation getOrientation( const B2DVector& rVecA, const B2DVector& rVecB )
+ {
+ double fVal(rVecA.getX() * rVecB.getY() - rVecA.getY() * rVecB.getX());
+
+ if(fTools::equalZero(fVal))
+ {
+ return ORIENTATION_NEUTRAL;
+ }
+
+ if(fVal > 0.0)
+ {
+ return ORIENTATION_POSITIVE;
+ }
+ else
+ {
+ return ORIENTATION_NEGATIVE;
+ }
+ }
+
+ B2DVector getPerpendicular( const B2DVector& rNormalizedVec )
+ {
+ B2DVector aPerpendicular(-rNormalizedVec.getY(), rNormalizedVec.getX());
+ return aPerpendicular;
+ }
+
+ B2DVector getNormalizedPerpendicular( const B2DVector& rVec )
+ {
+ B2DVector aPerpendicular(rVec);
+ aPerpendicular.normalize();
+ const double aTemp(-aPerpendicular.getY());
+ aPerpendicular.setY(aPerpendicular.getX());
+ aPerpendicular.setX(aTemp);
+ return aPerpendicular;
+ }
+
+ B2DVector operator*( const B2DHomMatrix& rMat, const B2DVector& rVec )
+ {
+ B2DVector aRes( rVec );
+ return aRes*=rMat;
+ }
+
+ B2VectorContinuity getContinuity(const B2DVector& rBackVector, const B2DVector& rForwardVector )
+ {
+ if(rBackVector.equalZero() || rForwardVector.equalZero())
+ {
+ return CONTINUITY_NONE;
+ }
+
+ if(fTools::equal(rBackVector.getX(), -rForwardVector.getX()) && fTools::equal(rBackVector.getY(), -rForwardVector.getY()))
+ {
+ // same direction and same length -> C2
+ return CONTINUITY_C2;
+ }
+
+ if(areParallel(rBackVector, rForwardVector) && rBackVector.scalar(rForwardVector) < 0.0)
+ {
+ // parallel and opposite direction -> C1
+ return CONTINUITY_C1;
+ }
+
+ return CONTINUITY_NONE;
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/vector/b2ivector.cxx b/basegfx/source/vector/b2ivector.cxx
new file mode 100644
index 000000000000..aa5be7fef970
--- /dev/null
+++ b/basegfx/source/vector/b2ivector.cxx
@@ -0,0 +1,162 @@
+/*************************************************************************
+ *
+ * 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: b2ivector.cxx,v $
+ * $Revision: 1.7 $
+ *
+ * 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/vector/b2ivector.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace basegfx
+{
+ B2IVector& B2IVector::operator=( const ::basegfx::B2ITuple& rVec )
+ {
+ mnX = rVec.getX();
+ mnY = rVec.getY();
+ return *this;
+ }
+
+
+ double B2IVector::getLength() const
+ {
+ return hypot( mnX, mnY );
+ }
+
+ double B2IVector::scalar( const B2IVector& rVec ) const
+ {
+ return((mnX * rVec.mnX) + (mnY * rVec.mnY));
+ }
+
+ double B2IVector::cross( const B2IVector& rVec ) const
+ {
+ return(mnX * rVec.getY() - mnY * rVec.getX());
+ }
+
+ double B2IVector::angle( const B2IVector& rVec ) const
+ {
+ return atan2(double( mnX * rVec.getY() - mnY * rVec.getX()),
+ double( mnX * rVec.getX() + mnY * rVec.getY()));
+ }
+
+ const B2IVector& B2IVector::getEmptyVector()
+ {
+ return (const B2IVector&) ::basegfx::B2ITuple::getEmptyTuple();
+ }
+
+ B2IVector& B2IVector::operator*=( const B2DHomMatrix& rMat )
+ {
+ mnX = fround( rMat.get(0,0)*mnX +
+ rMat.get(0,1)*mnY );
+ mnY = fround( rMat.get(1,0)*mnX +
+ rMat.get(1,1)*mnY );
+
+ return *this;
+ }
+
+ B2IVector& B2IVector::setLength(double fLen)
+ {
+ double fLenNow(scalar(*this));
+
+ if(!::basegfx::fTools::equalZero(fLenNow))
+ {
+ const double fOne(10.0);
+
+ if(!::basegfx::fTools::equal(fOne, fLenNow))
+ {
+ fLen /= sqrt(fLenNow);
+ }
+
+ mnX = fround( mnX*fLen );
+ mnY = fround( mnY*fLen );
+ }
+
+ return *this;
+ }
+
+ bool areParallel( const B2IVector& rVecA, const B2IVector& rVecB )
+ {
+ double fVal(rVecA.getX() * rVecB.getY() - rVecA.getY() * rVecB.getX());
+ return ::basegfx::fTools::equalZero(fVal);
+ }
+
+ B2VectorOrientation getOrientation( const B2IVector& rVecA, const B2IVector& rVecB )
+ {
+ double fVal(rVecA.getX() * rVecB.getY() - rVecA.getY() * rVecB.getX());
+
+ if(fVal > 0.0)
+ {
+ return ORIENTATION_POSITIVE;
+ }
+
+ if(fVal < 0.0)
+ {
+ return ORIENTATION_NEGATIVE;
+ }
+
+ return ORIENTATION_NEUTRAL;
+ }
+
+ B2IVector getPerpendicular( const B2IVector& rNormalizedVec )
+ {
+ B2IVector aPerpendicular(-rNormalizedVec.getY(), rNormalizedVec.getX());
+ return aPerpendicular;
+ }
+
+ B2IVector operator*( const B2DHomMatrix& rMat, const B2IVector& rVec )
+ {
+ B2IVector aRes( rVec );
+ return aRes*=rMat;
+ }
+
+ B2VectorContinuity getContinuity(const B2IVector& rBackVector, const B2IVector& rForwardVector )
+ {
+ B2VectorContinuity eRetval(CONTINUITY_NONE);
+
+ if(!rBackVector.equalZero() && !rForwardVector.equalZero())
+ {
+ const B2IVector aInverseForwardVector(-rForwardVector.getX(), -rForwardVector.getY());
+
+ if(rBackVector == aInverseForwardVector)
+ {
+ // same direction and same length -> C2
+ eRetval = CONTINUITY_C2;
+ }
+ else if(areParallel(rBackVector, aInverseForwardVector))
+ {
+ // same direction -> C1
+ eRetval = CONTINUITY_C1;
+ }
+ }
+
+ return eRetval;
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/vector/b3dvector.cxx b/basegfx/source/vector/b3dvector.cxx
new file mode 100644
index 000000000000..3a7e04f4eea1
--- /dev/null
+++ b/basegfx/source/vector/b3dvector.cxx
@@ -0,0 +1,118 @@
+/*************************************************************************
+ *
+ * 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: b3dvector.cxx,v $
+ * $Revision: 1.9.4.1 $
+ *
+ * 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/vector/b3dvector.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace basegfx
+{
+ B3DVector& B3DVector::normalize()
+ {
+ double fLen(scalar(*this));
+
+ if(!::basegfx::fTools::equalZero(fLen))
+ {
+ const double fOne(1.0);
+
+ if(!::basegfx::fTools::equal(fOne, fLen))
+ {
+ fLen = sqrt(fLen);
+
+ if(!::basegfx::fTools::equalZero(fLen))
+ {
+ mfX /= fLen;
+ mfY /= fLen;
+ mfZ /= fLen;
+ }
+ }
+ }
+
+ return *this;
+ }
+
+ B3DVector B3DVector::getPerpendicular(const B3DVector& rNormalizedVec) const
+ {
+ B3DVector aNew(*this);
+ aNew = cross(aNew, rNormalizedVec);
+ aNew.normalize();
+ return aNew;
+ }
+
+ B3DVector B3DVector::getProjectionOnPlane(const B3DVector& rNormalizedPlane) const
+ {
+ B3DVector aNew(*this);
+ aNew = cross(aNew, rNormalizedPlane);
+ aNew = cross(aNew, rNormalizedPlane);
+
+ aNew.mfX = mfX - aNew.mfX;
+ aNew.mfY = mfY - aNew.mfY;
+ aNew.mfZ = mfZ - aNew.mfZ;
+
+ return aNew;
+ }
+
+ B3DVector& B3DVector::operator*=( const ::basegfx::B3DHomMatrix& rMat )
+ {
+ const double fTempX( rMat.get(0,0)*mfX + rMat.get(0,1)*mfY + rMat.get(0,2)*mfZ );
+ const double fTempY( rMat.get(1,0)*mfX + rMat.get(1,1)*mfY + rMat.get(1,2)*mfZ );
+ const double fTempZ( rMat.get(2,0)*mfX + rMat.get(2,1)*mfY + rMat.get(2,2)*mfZ );
+ mfX = fTempX;
+ mfY = fTempY;
+ mfZ = fTempZ;
+
+ return *this;
+ }
+
+ B3DVector operator*( const ::basegfx::B3DHomMatrix& rMat, const B3DVector& rVec )
+ {
+ B3DVector aRes( rVec );
+ return aRes*=rMat;
+ }
+
+ bool areParallel( const B3DVector& rVecA, const B3DVector& rVecB )
+ {
+ // i think fastest is to compare relations, need no square or division
+ if(!fTools::equal(rVecA.getX() * rVecB.getY(), rVecA.getY() * rVecB.getX()))
+ return false;
+
+ if(!fTools::equal(rVecA.getX() * rVecB.getZ(), rVecA.getZ() * rVecB.getX()))
+ return false;
+
+ return (fTools::equal(rVecA.getY() * rVecB.getZ(), rVecA.getZ() * rVecB.getY()));
+ }
+
+} // end of namespace basegfx
+
+//////////////////////////////////////////////////////////////////////////////
+// eof
diff --git a/basegfx/source/vector/b3ivector.cxx b/basegfx/source/vector/b3ivector.cxx
new file mode 100644
index 000000000000..be6d7e498592
--- /dev/null
+++ b/basegfx/source/vector/b3ivector.cxx
@@ -0,0 +1,54 @@
+/*************************************************************************
+ *
+ * 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: b3ivector.cxx,v $
+ * $Revision: 1.4 $
+ *
+ * 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/vector/b3ivector.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+
+namespace basegfx
+{
+ B3IVector& B3IVector::operator*=( const B3DHomMatrix& rMat )
+ {
+ mnX = fround( rMat.get(0,0)*mnX + rMat.get(0,1)*mnY + rMat.get(0,2)*mnZ );
+ mnY = fround( rMat.get(1,0)*mnX + rMat.get(1,1)*mnY + rMat.get(1,2)*mnZ );
+ mnZ = fround( rMat.get(2,0)*mnX + rMat.get(2,1)*mnY + rMat.get(2,2)*mnZ );
+
+ return *this;
+ }
+
+ B3IVector operator*( const B3DHomMatrix& rMat, const B3IVector& rVec )
+ {
+ B3IVector aRes( rVec );
+ return aRes*=rMat;
+ }
+} // end of namespace basegfx
+
+// eof
diff --git a/basegfx/source/vector/makefile.mk b/basegfx/source/vector/makefile.mk
new file mode 100644
index 000000000000..66c4264122be
--- /dev/null
+++ b/basegfx/source/vector/makefile.mk
@@ -0,0 +1,54 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.4 $
+#
+# 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=vector
+
+#UNOUCRRDB=$(SOLARBINDIR)$/applicat.rdb
+#ENABLE_EXCEPTIONS=FALSE
+#USE_DEFFILE=TRUE
+
+# --- Settings ----------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Files -------------------------------------
+
+SLOFILES= \
+ $(SLO)$/b2dvector.obj \
+ $(SLO)$/b3dvector.obj \
+ $(SLO)$/b2ivector.obj \
+ $(SLO)$/b3ivector.obj
+
+# --- Targets ----------------------------------
+
+.INCLUDE : target.mk
diff --git a/basegfx/source/workbench/Makefile b/basegfx/source/workbench/Makefile
new file mode 100644
index 000000000000..fe23a4d06708
--- /dev/null
+++ b/basegfx/source/workbench/Makefile
@@ -0,0 +1,16 @@
+# Testbuild
+
+#test : bezierclip.cxx convexhull.cxx
+# g++ -Wall -g \
+# -I. -I. -I../inc -I./inc -I./unx/inc -I./unxlngi4/inc -I. -I/develop4/update/SRX644/unxlngi4/inc.m4/stl -I/develop4/update/SRX644/unxlngi4/inc.m4/external -I/develop4/update/SRX644/unxlngi4/inc.m4 -I/develop4/update/SRX644/src.m4/solenv/unxlngi4/inc -I/net/grande/develop6/update/dev/gcc_3.0.1_linux_libc2.11_turbolinux/include -I/develop4/update/SRX644/src.m4/solenv/inc -I/develop4/update/SRX644/unxlngi4/inc.m4/stl -I/net/grande.germany/develop6/update/dev/gcc_3.0.1_linux_libc2.11_turbolinux/redhat60/usr/include -I/net/grande.germany/develop6/update/dev/gcc_3.0.1_linux_libc2.11_turbolinux/redhat60/usr/include/X11 -I/develop4/update/SRX644/src.m4/res -I/net/grande/develop6/update/dev/Linux_JDK_1.4.0/include -I/net/grande/develop6/update/dev/Linux_JDK_1.4.0/include/linux -I. -I./res -I. \
+# -include preinclude.h -D_USE_NAMESPACE -DGLIBC=2 -D_USE_NAMESPACE=1 -DSTLPORT_VERSION=400 -D_STLP_DEBUG \
+# bezierclip.cxx convexhull.cxx -o bezierclip
+
+prog : bezierclip.cxx convexhull.cxx
+ g++ -Wall -g bezierclip.cxx convexhull.cxx -o bezierclip
+
+test : testconvexhull.cxx
+ g++ -Wall -g testconvexhull.cxx -o testhull
+
+.cxx.o:
+ g++ -c $(LOCALDEFINES) $(CCFLAGS) $<
diff --git a/basegfx/source/workbench/bezierclip.cxx b/basegfx/source/workbench/bezierclip.cxx
new file mode 100644
index 000000000000..acd7e49f1b03
--- /dev/null
+++ b/basegfx/source/workbench/bezierclip.cxx
@@ -0,0 +1,2060 @@
+/*************************************************************************
+ *
+ * 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: bezierclip.cxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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 <algorithm>
+#include <iterator>
+#include <vector>
+#include <utility>
+
+#include <math.h>
+
+#include "bezierclip.hxx"
+#include "gauss.hxx"
+
+
+
+// what to test
+#define WITH_ASSERTIONS
+//#define WITH_CONVEXHULL_TEST
+//#define WITH_MULTISUBDIVIDE_TEST
+//#define WITH_FATLINE_TEST
+//#define WITH_CALCFOCUS_TEST
+//#define WITH_SAFEPARAMBASE_TEST
+//#define WITH_SAFEPARAMS_TEST
+//#define WITH_SAFEPARAM_DETAILED_TEST
+//#define WITH_SAFEFOCUSPARAM_CALCFOCUS
+//#define WITH_SAFEFOCUSPARAM_TEST
+//#define WITH_SAFEFOCUSPARAM_DETAILED_TEST
+#define WITH_BEZIERCLIP_TEST
+
+
+
+// -----------------------------------------------------------------------------
+
+/* Implementation of the so-called 'Fat-Line Bezier Clipping Algorithm' by Sederberg et al.
+ *
+ * Actual reference is: T. W. Sederberg and T Nishita: Curve
+ * intersection using Bezier clipping. In Computer Aided Design, 22
+ * (9), 1990, pp. 538--549
+ */
+
+// -----------------------------------------------------------------------------
+
+/* Misc helper
+ * ===========
+ */
+int fallFac( int n, int k )
+{
+#ifdef WITH_ASSERTIONS
+ assert(n>=k); // "For factorials, n must be greater or equal k"
+ assert(n>=0); // "For factorials, n must be positive"
+ assert(k>=0); // "For factorials, k must be positive"
+#endif
+
+ int res( 1 );
+
+ while( k-- && n ) res *= n--;
+
+ return res;
+}
+
+// -----------------------------------------------------------------------------
+
+int fac( int n )
+{
+ return fallFac(n, n);
+}
+
+// -----------------------------------------------------------------------------
+
+/* Bezier fat line clipping part
+ * =============================
+ */
+
+// -----------------------------------------------------------------------------
+
+void Impl_calcFatLine( FatLine& line, const Bezier& c )
+{
+ // Prepare normalized implicit line
+ // ================================
+
+ // calculate vector orthogonal to p1-p4:
+ line.a = -(c.p0.y - c.p3.y);
+ line.b = (c.p0.x - c.p3.x);
+
+ // normalize
+ const double len( sqrt( line.a*line.a + line.b*line.b ) );
+ if( !tolZero(len) )
+ {
+ line.a /= len;
+ line.b /= len;
+ }
+
+ line.c = -(line.a*c.p0.x + line.b*c.p0.y);
+
+
+ // Determine bounding fat line from it
+ // ===================================
+
+ // calc control point distances
+ const double dP2( calcLineDistance(line.a, line.b, line.c, c.p1.x, c.p1.y ) );
+ const double dP3( calcLineDistance(line.a, line.b, line.c, c.p2.x, c.p2.y ) );
+
+ // calc approximate bounding lines to curve (tight bounds are
+ // possible here, but more expensive to calculate and thus not
+ // worth the overhead)
+ if( dP2 * dP3 > 0.0 )
+ {
+ line.dMin = 3.0/4.0 * ::std::min(0.0, ::std::min(dP2, dP3));
+ line.dMax = 3.0/4.0 * ::std::max(0.0, ::std::max(dP2, dP3));
+ }
+ else
+ {
+ line.dMin = 4.0/9.0 * ::std::min(0.0, ::std::min(dP2, dP3));
+ line.dMax = 4.0/9.0 * ::std::max(0.0, ::std::max(dP2, dP3));
+ }
+}
+
+void Impl_calcBounds( Point2D& leftTop,
+ Point2D& rightBottom,
+ const Bezier& c1 )
+{
+ leftTop.x = ::std::min( c1.p0.x, ::std::min( c1.p1.x, ::std::min( c1.p2.x, c1.p3.x ) ) );
+ leftTop.y = ::std::min( c1.p0.y, ::std::min( c1.p1.y, ::std::min( c1.p2.y, c1.p3.y ) ) );
+ rightBottom.x = ::std::max( c1.p0.x, ::std::max( c1.p1.x, ::std::max( c1.p2.x, c1.p3.x ) ) );
+ rightBottom.y = ::std::max( c1.p0.y, ::std::max( c1.p1.y, ::std::max( c1.p2.y, c1.p3.y ) ) );
+}
+
+bool Impl_doBBoxIntersect( const Bezier& c1,
+ const Bezier& c2 )
+{
+ // calc rectangular boxes from c1 and c2
+ Point2D lt1;
+ Point2D rb1;
+ Point2D lt2;
+ Point2D rb2;
+
+ Impl_calcBounds( lt1, rb1, c1 );
+ Impl_calcBounds( lt2, rb2, c2 );
+
+ if( ::std::min(rb1.x, rb2.x) < ::std::max(lt1.x, lt2.x) ||
+ ::std::min(rb1.y, rb2.y) < ::std::max(lt1.y, lt2.y) )
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+/* calculates two t's for the given bernstein control polygon: the first is
+ * the intersection of the min value line with the convex hull from
+ * the left, the second is the intersection of the max value line with
+ * the convex hull from the right.
+ */
+bool Impl_calcSafeParams( double& t1,
+ double& t2,
+ const Polygon2D& rPoly,
+ double lowerYBound,
+ double upperYBound )
+{
+ // need the convex hull of the control polygon, as this is
+ // guaranteed to completely bound the curve
+ Polygon2D convHull( convexHull(rPoly) );
+
+ // init min and max buffers
+ t1 = 0.0 ;
+ double currLowerT( 1.0 );
+
+ t2 = 1.0;
+ double currHigherT( 0.0 );
+
+ if( convHull.size() <= 1 )
+ return false; // only one point? Then we're done with clipping
+
+ /* now, clip against lower and higher bounds */
+ Point2D p0;
+ Point2D p1;
+
+ bool bIntersection( false );
+
+ for( Polygon2D::size_type i=0; i<convHull.size(); ++i )
+ {
+ // have to check against convHull.size() segments, as the
+ // convex hull is, by definition, closed. Thus, for the
+ // last point, we take the first point as partner.
+ if( i+1 == convHull.size() )
+ {
+ // close the polygon
+ p0 = convHull[i];
+ p1 = convHull[0];
+ }
+ else
+ {
+ p0 = convHull[i];
+ p1 = convHull[i+1];
+ }
+
+ // is the segment in question within or crossing the
+ // horizontal band spanned by lowerYBound and upperYBound? If
+ // not, we've got no intersection. If yes, we maybe don't have
+ // an intersection, but we've got to update the permissible
+ // range, nevertheless. This is because inside lying segments
+ // leads to full range forbidden.
+ if( (tolLessEqual(p0.y, upperYBound) || tolLessEqual(p1.y, upperYBound)) &&
+ (tolGreaterEqual(p0.y, lowerYBound) || tolGreaterEqual(p1.y, lowerYBound)) )
+ {
+ // calc intersection of convex hull segment with
+ // one of the horizontal bounds lines
+ const double r_x( p1.x - p0.x );
+ const double r_y( p1.y - p0.y );
+
+ if( tolZero(r_y) )
+ {
+ // r_y is virtually zero, thus we've got a horizontal
+ // line. Now check whether we maybe coincide with lower or
+ // upper horizonal bound line.
+ if( tolEqual(p0.y, lowerYBound) ||
+ tolEqual(p0.y, upperYBound) )
+ {
+ // yes, simulate intersection then
+ currLowerT = ::std::min(currLowerT, ::std::min(p0.x, p1.x));
+ currHigherT = ::std::max(currHigherT, ::std::max(p0.x, p1.x));
+ }
+ }
+ else
+ {
+ // check against lower and higher bounds
+ // =====================================
+
+ // calc intersection with horizontal dMin line
+ const double currTLow( (lowerYBound - p0.y) * r_x / r_y + p0.x );
+
+ // calc intersection with horizontal dMax line
+ const double currTHigh( (upperYBound - p0.y) * r_x / r_y + p0.x );
+
+ currLowerT = ::std::min(currLowerT, ::std::min(currTLow, currTHigh));
+ currHigherT = ::std::max(currHigherT, ::std::max(currTLow, currTHigh));
+ }
+
+ // set flag that at least one segment is contained or
+ // intersects given horizontal band.
+ bIntersection = true;
+ }
+ }
+
+#ifndef WITH_SAFEPARAMBASE_TEST
+ // limit intersections found to permissible t parameter range
+ t1 = ::std::max(0.0, currLowerT);
+ t2 = ::std::min(1.0, currHigherT);
+#endif
+
+ return bIntersection;
+}
+
+
+/* calculates two t's for the given bernstein polynomial: the first is
+ * the intersection of the min value line with the convex hull from
+ * the left, the second is the intersection of the max value line with
+ * the convex hull from the right.
+ *
+ * The polynomial coefficients c0 to c3 given to this method
+ * must correspond to t values of 0, 1/3, 2/3 and 1, respectively.
+ */
+bool Impl_calcSafeParams_clip( double& t1,
+ double& t2,
+ const FatLine& bounds,
+ double c0,
+ double c1,
+ double c2,
+ double c3 )
+{
+ /* first of all, determine convex hull of c0-c3 */
+ Polygon2D poly(4);
+ poly[0] = Point2D(0, c0);
+ poly[1] = Point2D(1.0/3.0, c1);
+ poly[2] = Point2D(2.0/3.0, c2);
+ poly[3] = Point2D(1, c3);
+
+#ifndef WITH_SAFEPARAM_DETAILED_TEST
+
+ return Impl_calcSafeParams( t1, t2, poly, bounds.dMin, bounds.dMax );
+
+#else
+ bool bRet( Impl_calcSafeParams( t1, t2, poly, bounds.dMin, bounds.dMax ) );
+
+ Polygon2D convHull( convexHull( poly ) );
+
+ cout << "# convex hull testing" << endl
+ << "plot [t=0:1] ";
+ cout << " bez("
+ << poly[0].x << ","
+ << poly[1].x << ","
+ << poly[2].x << ","
+ << poly[3].x << ",t),bez("
+ << poly[0].y << ","
+ << poly[1].y << ","
+ << poly[2].y << ","
+ << poly[3].y << ",t), "
+ << "t, " << bounds.dMin << ", "
+ << "t, " << bounds.dMax << ", "
+ << t1 << ", t, "
+ << t2 << ", t, "
+ << "'-' using ($1):($2) title \"control polygon\" with lp, "
+ << "'-' using ($1):($2) title \"convex hull\" with lp" << endl;
+
+ unsigned int k;
+ for( k=0; k<poly.size(); ++k )
+ {
+ cout << poly[k].x << " " << poly[k].y << endl;
+ }
+ cout << poly[0].x << " " << poly[0].y << endl;
+ cout << "e" << endl;
+
+ for( k=0; k<convHull.size(); ++k )
+ {
+ cout << convHull[k].x << " " << convHull[k].y << endl;
+ }
+ cout << convHull[0].x << " " << convHull[0].y << endl;
+ cout << "e" << endl;
+
+ return bRet;
+#endif
+}
+
+// -----------------------------------------------------------------------------
+
+void Impl_deCasteljauAt( Bezier& part1,
+ Bezier& part2,
+ const Bezier& input,
+ double t )
+{
+ // deCasteljau bezier arc, scheme is:
+ //
+ // First row is C_0^n,C_1^n,...,C_n^n
+ // Second row is P_1^n,...,P_n^n
+ // etc.
+ // with P_k^r = (1 - x_s)P_{k-1}^{r-1} + x_s P_k{r-1}
+ //
+ // this results in:
+ //
+ // P1 P2 P3 P4
+ // L1 P2 P3 R4
+ // L2 H R3
+ // L3 R2
+ // L4/R1
+ if( tolZero(t) )
+ {
+ // t is zero -> part2 is input curve, part1 is empty (input.p0, that is)
+ part1.p0.x = part1.p1.x = part1.p2.x = part1.p3.x = input.p0.x;
+ part1.p0.y = part1.p1.y = part1.p2.y = part1.p3.y = input.p0.y;
+ part2 = input;
+ }
+ else if( tolEqual(t, 1.0) )
+ {
+ // t is one -> part1 is input curve, part2 is empty (input.p3, that is)
+ part1 = input;
+ part2.p0.x = part2.p1.x = part2.p2.x = part2.p3.x = input.p3.x;
+ part2.p0.y = part2.p1.y = part2.p2.y = part2.p3.y = input.p3.y;
+ }
+ else
+ {
+ part1.p0.x = input.p0.x; part1.p0.y = input.p0.y;
+ part1.p1.x = (1.0 - t)*part1.p0.x + t*input.p1.x; part1.p1.y = (1.0 - t)*part1.p0.y + t*input.p1.y;
+ const double Hx ( (1.0 - t)*input.p1.x + t*input.p2.x ), Hy ( (1.0 - t)*input.p1.y + t*input.p2.y );
+ part1.p2.x = (1.0 - t)*part1.p1.x + t*Hx; part1.p2.y = (1.0 - t)*part1.p1.y + t*Hy;
+ part2.p3.x = input.p3.x; part2.p3.y = input.p3.y;
+ part2.p2.x = (1.0 - t)*input.p2.x + t*input.p3.x; part2.p2.y = (1.0 - t)*input.p2.y + t*input.p3.y;
+ part2.p1.x = (1.0 - t)*Hx + t*part2.p2.x; part2.p1.y = (1.0 - t)*Hy + t*part2.p2.y;
+ part2.p0.x = (1.0 - t)*part1.p2.x + t*part2.p1.x; part2.p0.y = (1.0 - t)*part1.p2.y + t*part2.p1.y;
+ part1.p3.x = part2.p0.x; part1.p3.y = part2.p0.y;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void printCurvesWithSafeRange( const Bezier& c1, const Bezier& c2, double t1_c1, double t2_c1,
+ const Bezier& c2_part, const FatLine& bounds_c2 )
+{
+ static int offset = 0;
+
+ cout << "# safe param range testing" << endl
+ << "plot [t=0.0:1.0] ";
+
+ // clip safe ranges off c1
+ Bezier c1_part1;
+ Bezier c1_part2;
+ Bezier c1_part3;
+
+ // subdivide at t1_c1
+ Impl_deCasteljauAt( c1_part1, c1_part2, c1, t1_c1 );
+ // subdivide at t2_c1
+ Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, t2_c1 );
+
+ // output remaining segment (c1_part1)
+
+ cout << "bez("
+ << c1.p0.x+offset << ","
+ << c1.p1.x+offset << ","
+ << c1.p2.x+offset << ","
+ << c1.p3.x+offset << ",t),bez("
+ << c1.p0.y << ","
+ << c1.p1.y << ","
+ << c1.p2.y << ","
+ << c1.p3.y << ",t), bez("
+ << c2.p0.x+offset << ","
+ << c2.p1.x+offset << ","
+ << c2.p2.x+offset << ","
+ << c2.p3.x+offset << ",t),bez("
+ << c2.p0.y << ","
+ << c2.p1.y << ","
+ << c2.p2.y << ","
+ << c2.p3.y << ",t), "
+#if 1
+ << "bez("
+ << c1_part1.p0.x+offset << ","
+ << c1_part1.p1.x+offset << ","
+ << c1_part1.p2.x+offset << ","
+ << c1_part1.p3.x+offset << ",t),bez("
+ << c1_part1.p0.y << ","
+ << c1_part1.p1.y << ","
+ << c1_part1.p2.y << ","
+ << c1_part1.p3.y << ",t), "
+#endif
+#if 1
+ << "bez("
+ << c2_part.p0.x+offset << ","
+ << c2_part.p1.x+offset << ","
+ << c2_part.p2.x+offset << ","
+ << c2_part.p3.x+offset << ",t),bez("
+ << c2_part.p0.y << ","
+ << c2_part.p1.y << ","
+ << c2_part.p2.y << ","
+ << c2_part.p3.y << ",t), "
+#endif
+ << "linex("
+ << bounds_c2.a << ","
+ << bounds_c2.b << ","
+ << bounds_c2.c << ",t)+" << offset << ", liney("
+ << bounds_c2.a << ","
+ << bounds_c2.b << ","
+ << bounds_c2.c << ",t) title \"fat line (center)\", linex("
+ << bounds_c2.a << ","
+ << bounds_c2.b << ","
+ << bounds_c2.c-bounds_c2.dMin << ",t)+" << offset << ", liney("
+ << bounds_c2.a << ","
+ << bounds_c2.b << ","
+ << bounds_c2.c-bounds_c2.dMin << ",t) title \"fat line (min) \", linex("
+ << bounds_c2.a << ","
+ << bounds_c2.b << ","
+ << bounds_c2.c-bounds_c2.dMax << ",t)+" << offset << ", liney("
+ << bounds_c2.a << ","
+ << bounds_c2.b << ","
+ << bounds_c2.c-bounds_c2.dMax << ",t) title \"fat line (max) \"" << endl;
+
+ offset += 1;
+}
+
+// -----------------------------------------------------------------------------
+
+void printResultWithFinalCurves( const Bezier& c1, const Bezier& c1_part,
+ const Bezier& c2, const Bezier& c2_part,
+ double t1_c1, double t2_c1 )
+{
+ static int offset = 0;
+
+ cout << "# final result" << endl
+ << "plot [t=0.0:1.0] ";
+
+ cout << "bez("
+ << c1.p0.x+offset << ","
+ << c1.p1.x+offset << ","
+ << c1.p2.x+offset << ","
+ << c1.p3.x+offset << ",t),bez("
+ << c1.p0.y << ","
+ << c1.p1.y << ","
+ << c1.p2.y << ","
+ << c1.p3.y << ",t), bez("
+ << c1_part.p0.x+offset << ","
+ << c1_part.p1.x+offset << ","
+ << c1_part.p2.x+offset << ","
+ << c1_part.p3.x+offset << ",t),bez("
+ << c1_part.p0.y << ","
+ << c1_part.p1.y << ","
+ << c1_part.p2.y << ","
+ << c1_part.p3.y << ",t), "
+ << " pointmarkx(bez("
+ << c1.p0.x+offset << ","
+ << c1.p1.x+offset << ","
+ << c1.p2.x+offset << ","
+ << c1.p3.x+offset << ","
+ << t1_c1 << "),t), "
+ << " pointmarky(bez("
+ << c1.p0.y << ","
+ << c1.p1.y << ","
+ << c1.p2.y << ","
+ << c1.p3.y << ","
+ << t1_c1 << "),t), "
+ << " pointmarkx(bez("
+ << c1.p0.x+offset << ","
+ << c1.p1.x+offset << ","
+ << c1.p2.x+offset << ","
+ << c1.p3.x+offset << ","
+ << t2_c1 << "),t), "
+ << " pointmarky(bez("
+ << c1.p0.y << ","
+ << c1.p1.y << ","
+ << c1.p2.y << ","
+ << c1.p3.y << ","
+ << t2_c1 << "),t), "
+
+ << "bez("
+ << c2.p0.x+offset << ","
+ << c2.p1.x+offset << ","
+ << c2.p2.x+offset << ","
+ << c2.p3.x+offset << ",t),bez("
+ << c2.p0.y << ","
+ << c2.p1.y << ","
+ << c2.p2.y << ","
+ << c2.p3.y << ",t), "
+ << "bez("
+ << c2_part.p0.x+offset << ","
+ << c2_part.p1.x+offset << ","
+ << c2_part.p2.x+offset << ","
+ << c2_part.p3.x+offset << ",t),bez("
+ << c2_part.p0.y << ","
+ << c2_part.p1.y << ","
+ << c2_part.p2.y << ","
+ << c2_part.p3.y << ",t)" << endl;
+
+ offset += 1;
+}
+
+// -----------------------------------------------------------------------------
+
+/** determine parameter ranges [0,t1) and (t2,1] on c1, where c1 is guaranteed to lie outside c2.
+ Returns false, if the two curves don't even intersect.
+
+ @param t1
+ Range [0,t1) on c1 is guaranteed to lie outside c2
+
+ @param t2
+ Range (t2,1] on c1 is guaranteed to lie outside c2
+
+ @param c1_orig
+ Original curve c1
+
+ @param c1_part
+ Subdivided current part of c1
+
+ @param c2_orig
+ Original curve c2
+
+ @param c2_part
+ Subdivided current part of c2
+ */
+bool Impl_calcClipRange( double& t1,
+ double& t2,
+ const Bezier& c1_orig,
+ const Bezier& c1_part,
+ const Bezier& c2_orig,
+ const Bezier& c2_part )
+{
+ // TODO: Maybe also check fat line orthogonal to P0P3, having P0
+ // and P3 as the extremal points
+
+ if( Impl_doBBoxIntersect(c1_part, c2_part) )
+ {
+ // Calculate fat lines around c1
+ FatLine bounds_c2;
+
+ // must use the subdivided version of c2, since the fat line
+ // algorithm works implicitely with the convex hull bounding
+ // box.
+ Impl_calcFatLine(bounds_c2, c2_part);
+
+ // determine clip positions on c2. Can use original c1 (which
+ // is necessary anyway, to get the t's on the original curve),
+ // since the distance calculations work directly in the
+ // Bernstein polynom parameter domain.
+ if( Impl_calcSafeParams_clip( t1, t2, bounds_c2,
+ calcLineDistance( bounds_c2.a,
+ bounds_c2.b,
+ bounds_c2.c,
+ c1_orig.p0.x,
+ c1_orig.p0.y ),
+ calcLineDistance( bounds_c2.a,
+ bounds_c2.b,
+ bounds_c2.c,
+ c1_orig.p1.x,
+ c1_orig.p1.y ),
+ calcLineDistance( bounds_c2.a,
+ bounds_c2.b,
+ bounds_c2.c,
+ c1_orig.p2.x,
+ c1_orig.p2.y ),
+ calcLineDistance( bounds_c2.a,
+ bounds_c2.b,
+ bounds_c2.c,
+ c1_orig.p3.x,
+ c1_orig.p3.y ) ) )
+ {
+ //printCurvesWithSafeRange(c1_orig, c2_orig, t1, t2, c2_part, bounds_c2);
+
+ // they do intersect
+ return true;
+ }
+ }
+
+ // they don't intersect: nothing to do
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+/* Tangent intersection part
+ * =========================
+ */
+
+// -----------------------------------------------------------------------------
+
+void Impl_calcFocus( Bezier& res, const Bezier& c )
+{
+ // arbitrary small value, for now
+ // TODO: find meaningful value
+ const double minPivotValue( 1.0e-20 );
+
+ Point2D::value_type fMatrix[6];
+ Point2D::value_type fRes[2];
+
+ // calc new curve from hodograph, c and linear blend
+
+ // Coefficients for derivative of c are (C_i=n(C_{i+1} - C_i)):
+ //
+ // 3(P1 - P0), 3(P2 - P1), 3(P3 - P2) (bezier curve of degree 2)
+ //
+ // The hodograph is then (bezier curve of 2nd degree is P0(1-t)^2 + 2P1(1-t)t + P2t^2):
+ //
+ // 3(P1 - P0)(1-t)^2 + 6(P2 - P1)(1-t)t + 3(P3 - P2)t^2
+ //
+ // rotate by 90 degrees: x=-y, y=x and you get the normal vector function N(t):
+ //
+ // x(t) = -(3(P1.y - P0.y)(1-t)^2 + 6(P2.y - P1.y)(1-t)t + 3(P3.y - P2.y)t^2)
+ // y(t) = 3(P1.x - P0.x)(1-t)^2 + 6(P2.x - P1.x)(1-t)t + 3(P3.x - P2.x)t^2
+ //
+ // Now, the focus curve is defined to be F(t)=P(t) + c(t)N(t),
+ // where P(t) is the original curve, and c(t)=c0(1-t) + c1 t
+ //
+ // This results in the following expression for F(t):
+ //
+ // x(t) = P0.x (1-t)^3 + 3 P1.x (1-t)^2t + 3 P2.x (1.t)t^2 + P3.x t^3 -
+ // (c0(1-t) + c1 t)(3(P1.y - P0.y)(1-t)^2 + 6(P2.y - P1.y)(1-t)t + 3(P3.y - P2.y)t^2)
+ //
+ // y(t) = P0.y (1-t)^3 + 3 P1.y (1-t)^2t + 3 P2.y (1.t)t^2 + P3.y t^3 +
+ // (c0(1-t) + c1 t)(3(P1.x - P0.x)(1-t)^2 + 6(P2.x - P1.x)(1-t)t + 3(P3.x - P2.x)t^2)
+ //
+ // As a heuristic, we set F(0)=F(1) (thus, the curve is closed and _tends_ to be small):
+ //
+ // For F(0), the following results:
+ //
+ // x(0) = P0.x - c0 3(P1.y - P0.y)
+ // y(0) = P0.y + c0 3(P1.x - P0.x)
+ //
+ // For F(1), the following results:
+ //
+ // x(1) = P3.x - c1 3(P3.y - P2.y)
+ // y(1) = P3.y + c1 3(P3.x - P2.x)
+ //
+ // Reorder, collect and substitute into F(0)=F(1):
+ //
+ // P0.x - c0 3(P1.y - P0.y) = P3.x - c1 3(P3.y - P2.y)
+ // P0.y + c0 3(P1.x - P0.x) = P3.y + c1 3(P3.x - P2.x)
+ //
+ // which yields
+ //
+ // (P0.y - P1.y)c0 + (P3.y - P2.y)c1 = (P3.x - P0.x)/3
+ // (P1.x - P0.x)c0 + (P2.x - P3.x)c1 = (P3.y - P0.y)/3
+ //
+
+ // so, this is what we calculate here (determine c0 and c1):
+ fMatrix[0] = c.p1.x - c.p0.x;
+ fMatrix[1] = c.p2.x - c.p3.x;
+ fMatrix[2] = (c.p3.y - c.p0.y)/3.0;
+ fMatrix[3] = c.p0.y - c.p1.y;
+ fMatrix[4] = c.p3.y - c.p2.y;
+ fMatrix[5] = (c.p3.x - c.p0.x)/3.0;
+
+ // TODO: determine meaningful value for
+ if( !solve(fMatrix, 2, 3, fRes, minPivotValue) )
+ {
+ // TODO: generate meaningful values here
+ // singular or nearly singular system -- use arbitrary
+ // values for res
+ fRes[0] = 0.0;
+ fRes[1] = 1.0;
+
+ cerr << "Matrix singular!" << endl;
+ }
+
+ // now, the reordered and per-coefficient collected focus curve is
+ // the following third degree bezier curve F(t):
+ //
+ // x(t) = P0.x (1-t)^3 + 3 P1.x (1-t)^2t + 3 P2.x (1.t)t^2 + P3.x t^3 -
+ // (c0(1-t) + c1 t)(3(P1.y - P0.y)(1-t)^2 + 6(P2.y - P1.y)(1-t)t + 3(P3.y - P2.y)t^2)
+ // = P0.x (1-t)^3 + 3 P1.x (1-t)^2t + 3 P2.x (1.t)t^2 + P3.x t^3 -
+ // (3c0P1.y(1-t)^3 - 3c0P0.y(1-t)^3 + 6c0P2.y(1-t)^2t - 6c0P1.y(1-t)^2t +
+ // 3c0P3.y(1-t)t^2 - 3c0P2.y(1-t)t^2 +
+ // 3c1P1.y(1-t)^2t - 3c1P0.y(1-t)^2t + 6c1P2.y(1-t)t^2 - 6c1P1.y(1-t)t^2 +
+ // 3c1P3.yt^3 - 3c1P2.yt^3)
+ // = (P0.x - 3 c0 P1.y + 3 c0 P0.y)(1-t)^3 +
+ // 3(P1.x - c1 P1.y + c1 P0.y - 2 c0 P2.y + 2 c0 P1.y)(1-t)^2t +
+ // 3(P2.x - 2 c1 P2.y + 2 c1 P1.y - c0 P3.y + c0 P2.y)(1-t)t^2 +
+ // (P3.x - 3 c1 P3.y + 3 c1 P2.y)t^3
+ // = (P0.x - 3 c0(P1.y - P0.y))(1-t)^3 +
+ // 3(P1.x - c1(P1.y - P0.y) - 2c0(P2.y - P1.y))(1-t)^2t +
+ // 3(P2.x - 2 c1(P2.y - P1.y) - c0(P3.y - P2.y))(1-t)t^2 +
+ // (P3.x - 3 c1(P3.y - P2.y))t^3
+ //
+ // y(t) = P0.y (1-t)^3 + 3 P1.y (1-t)^2t + 3 P2.y (1-t)t^2 + P3.y t^3 +
+ // (c0(1-t) + c1 t)(3(P1.x - P0.x)(1-t)^2 + 6(P2.x - P1.x)(1-t)t + 3(P3.x - P2.x)t^2)
+ // = P0.y (1-t)^3 + 3 P1.y (1-t)^2t + 3 P2.y (1-t)t^2 + P3.y t^3 +
+ // 3c0(P1.x - P0.x)(1-t)^3 + 6c0(P2.x - P1.x)(1-t)^2t + 3c0(P3.x - P2.x)(1-t)t^2 +
+ // 3c1(P1.x - P0.x)(1-t)^2t + 6c1(P2.x - P1.x)(1-t)t^2 + 3c1(P3.x - P2.x)t^3
+ // = (P0.y + 3 c0 (P1.x - P0.x))(1-t)^3 +
+ // 3(P1.y + 2 c0 (P2.x - P1.x) + c1 (P1.x - P0.x))(1-t)^2t +
+ // 3(P2.y + c0 (P3.x - P2.x) + 2 c1 (P2.x - P1.x))(1-t)t^2 +
+ // (P3.y + 3 c1 (P3.x - P2.x))t^3
+ //
+ // Therefore, the coefficients F0 to F3 of the focus curve are:
+ //
+ // F0.x = (P0.x - 3 c0(P1.y - P0.y)) F0.y = (P0.y + 3 c0 (P1.x - P0.x))
+ // F1.x = (P1.x - c1(P1.y - P0.y) - 2c0(P2.y - P1.y)) F1.y = (P1.y + 2 c0 (P2.x - P1.x) + c1 (P1.x - P0.x))
+ // F2.x = (P2.x - 2 c1(P2.y - P1.y) - c0(P3.y - P2.y)) F2.y = (P2.y + c0 (P3.x - P2.x) + 2 c1 (P2.x - P1.x))
+ // F3.x = (P3.x - 3 c1(P3.y - P2.y)) F3.y = (P3.y + 3 c1 (P3.x - P2.x))
+ //
+ res.p0.x = c.p0.x - 3*fRes[0]*(c.p1.y - c.p0.y);
+ res.p1.x = c.p1.x - fRes[1]*(c.p1.y - c.p0.y) - 2*fRes[0]*(c.p2.y - c.p1.y);
+ res.p2.x = c.p2.x - 2*fRes[1]*(c.p2.y - c.p1.y) - fRes[0]*(c.p3.y - c.p2.y);
+ res.p3.x = c.p3.x - 3*fRes[1]*(c.p3.y - c.p2.y);
+
+ res.p0.y = c.p0.y + 3*fRes[0]*(c.p1.x - c.p0.x);
+ res.p1.y = c.p1.y + 2*fRes[0]*(c.p2.x - c.p1.x) + fRes[1]*(c.p1.x - c.p0.x);
+ res.p2.y = c.p2.y + fRes[0]*(c.p3.x - c.p2.x) + 2*fRes[1]*(c.p2.x - c.p1.x);
+ res.p3.y = c.p3.y + 3*fRes[1]*(c.p3.x - c.p2.x);
+}
+
+// -----------------------------------------------------------------------------
+
+bool Impl_calcSafeParams_focus( double& t1,
+ double& t2,
+ const Bezier& curve,
+ const Bezier& focus )
+{
+ // now, we want to determine which normals of the original curve
+ // P(t) intersect with the focus curve F(t). The condition for
+ // this statement is P'(t)(P(t) - F) = 0, i.e. hodograph P'(t) and
+ // line through P(t) and F are perpendicular.
+ // If you expand this equation, you end up with something like
+ //
+ // (\sum_{i=0}^n (P_i - F)B_i^n(t))^T (\sum_{j=0}^{n-1} n(P_{j+1} - P_j)B_j^{n-1}(t))
+ //
+ // Multiplying that out (as the scalar product is linear, we can
+ // extract some terms) yields:
+ //
+ // (P_i - F)^T n(P_{j+1} - P_j) B_i^n(t)B_j^{n-1}(t) + ...
+ //
+ // If we combine the B_i^n(t)B_j^{n-1}(t) product, we arrive at a
+ // Bernstein polynomial of degree 2n-1, as
+ //
+ // \binom{n}{i}(1-t)^{n-i}t^i) \binom{n-1}{j}(1-t)^{n-1-j}t^j) =
+ // \binom{n}{i}\binom{n-1}{j}(1-t)^{2n-1-i-j}t^{i+j}
+ //
+ // Thus, with the defining equation for a 2n-1 degree Bernstein
+ // polynomial
+ //
+ // \sum_{i=0}^{2n-1} d_i B_i^{2n-1}(t)
+ //
+ // the d_i are calculated as follows:
+ //
+ // d_i = \sum_{j+k=i, j\in\{0,...,n\}, k\in\{0,...,n-1\}} \frac{\binom{n}{j}\binom{n-1}{k}}{\binom{2n-1}{i}} n (P_{k+1} - P_k)^T(P_j - F)
+ //
+ //
+ // Okay, but F is now not a single point, but itself a curve
+ // F(u). Thus, for every value of u, we get a different 2n-1
+ // bezier curve from the above equation. Therefore, we have a
+ // tensor product bezier patch, with the following defining
+ // equation:
+ //
+ // d(t,u) = \sum_{i=0}^{2n-1} \sum_{j=0}^m B_i^{2n-1}(t) B_j^{m}(u) d_{ij}, where
+ // d_{ij} = \sum_{k+l=i, l\in\{0,...,n\}, k\in\{0,...,n-1\}} \frac{\binom{n}{l}\binom{n-1}{k}}{\binom{2n-1}{i}} n (P_{k+1} - P_k)^T(P_l - F_j)
+ //
+ // as above, only that now F is one of the focus' control points.
+ //
+ // Note the difference in the binomial coefficients to the
+ // reference paper, these formulas most probably contained a typo.
+ //
+ // To determine, where D(t,u) is _not_ zero (these are the parts
+ // of the curve that don't share normals with the focus and can
+ // thus be safely clipped away), we project D(u,t) onto the
+ // (d(t,u), t) plane, determine the convex hull there and proceed
+ // as for the curve intersection part (projection is orthogonal to
+ // u axis, thus simply throw away u coordinate).
+ //
+ // \fallfac are so-called falling factorials (see Concrete
+ // Mathematics, p. 47 for a definition).
+ //
+
+ // now, for tensor product bezier curves, the convex hull property
+ // holds, too. Thus, we simply project the control points (t_{ij},
+ // u_{ij}, d_{ij}) onto the (t,d) plane and calculate the
+ // intersections of the convex hull with the t axis, as for the
+ // bezier clipping case.
+
+ //
+ // calc polygon of control points (t_{ij}, d_{ij}):
+ //
+ const int n( 3 ); // cubic bezier curves, as a matter of fact
+ const int i_card( 2*n );
+ const int j_card( n + 1 );
+ const int k_max( n-1 );
+ Polygon2D controlPolygon( i_card*j_card ); // vector of (t_{ij}, d_{ij}) in row-major order
+
+ int i, j, k, l; // variable notation from formulas above and Sederberg article
+ Point2D::value_type d;
+ for( i=0; i<i_card; ++i )
+ {
+ for( j=0; j<j_card; ++j )
+ {
+ // calc single d_{ij} sum:
+ for( d=0.0, k=::std::max(0,i-n); k<=k_max && k<=i; ++k )
+ {
+ l = i - k; // invariant: k + l = i
+ assert(k>=0 && k<=n-1); // k \in {0,...,n-1}
+ assert(l>=0 && l<=n); // l \in {0,...,n}
+
+ // TODO: find, document and assert proper limits for n and int's max_val.
+ // This becomes important should anybody wants to use
+ // this code for higher-than-cubic beziers
+ d += static_cast<double>(fallFac(n,l)*fallFac(n-1,k)*fac(i)) /
+ static_cast<double>(fac(l)*fac(k) * fallFac(2*n-1,i)) * n *
+ ( (curve[k+1].x - curve[k].x)*(curve[l].x - focus[j].x) + // dot product here
+ (curve[k+1].y - curve[k].y)*(curve[l].y - focus[j].y) );
+ }
+
+ // Note that the t_{ij} values are evenly spaced on the
+ // [0,1] interval, thus t_{ij}=i/(2n-1)
+ controlPolygon[ i*j_card + j ] = Point2D( i/(2.0*n-1.0), d );
+ }
+ }
+
+#ifndef WITH_SAFEFOCUSPARAM_DETAILED_TEST
+
+ // calc safe parameter range, to determine [0,t1] and [t2,1] where
+ // no zero crossing is guaranteed.
+ return Impl_calcSafeParams( t1, t2, controlPolygon, 0.0, 0.0 );
+
+#else
+ bool bRet( Impl_calcSafeParams( t1, t2, controlPolygon, 0.0, 0.0 ) );
+
+ Polygon2D convHull( convexHull( controlPolygon ) );
+
+ cout << "# convex hull testing (focus)" << endl
+ << "plot [t=0:1] ";
+ cout << "'-' using ($1):($2) title \"control polygon\" with lp, "
+ << "'-' using ($1):($2) title \"convex hull\" with lp" << endl;
+
+ unsigned int count;
+ for( count=0; count<controlPolygon.size(); ++count )
+ {
+ cout << controlPolygon[count].x << " " << controlPolygon[count].y << endl;
+ }
+ cout << controlPolygon[0].x << " " << controlPolygon[0].y << endl;
+ cout << "e" << endl;
+
+ for( count=0; count<convHull.size(); ++count )
+ {
+ cout << convHull[count].x << " " << convHull[count].y << endl;
+ }
+ cout << convHull[0].x << " " << convHull[0].y << endl;
+ cout << "e" << endl;
+
+ return bRet;
+#endif
+}
+
+// -----------------------------------------------------------------------------
+
+/** Calc all values t_i on c1, for which safeRanges functor does not
+ give a safe range on c1 and c2.
+
+ This method is the workhorse of the bezier clipping. Because c1
+ and c2 must be alternatingly tested against each other (first
+ determine safe parameter interval on c1 with regard to c2, then
+ the other way around), we call this method recursively with c1 and
+ c2 swapped.
+
+ @param result
+ Output iterator where the final t values are added to. If curves
+ don't intersect, nothing is added.
+
+ @param delta
+ Maximal allowed distance to true critical point (measured in the
+ original curve's coordinate system)
+
+ @param safeRangeFunctor
+ Functor object, that must provide the following operator():
+ bool safeRangeFunctor( double& t1,
+ double& t2,
+ const Bezier& c1_orig,
+ const Bezier& c1_part,
+ const Bezier& c2_orig,
+ const Bezier& c2_part );
+ This functor must calculate the safe ranges [0,t1] and [t2,1] on
+ c1_orig, where c1_orig is 'safe' from c2_part. If the whole
+ c1_orig is safe, false must be returned, true otherwise.
+ */
+template <class Functor> void Impl_applySafeRanges_rec( ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > >& result,
+ double delta,
+ const Functor& safeRangeFunctor,
+ int recursionLevel,
+ const Bezier& c1_orig,
+ const Bezier& c1_part,
+ double last_t1_c1,
+ double last_t2_c1,
+ const Bezier& c2_orig,
+ const Bezier& c2_part,
+ double last_t1_c2,
+ double last_t2_c2 )
+{
+ // check end condition
+ // ===================
+
+ // TODO: tidy up recursion handling. maybe put everything in a
+ // struct and swap that here at method entry
+
+ // TODO: Implement limit on recursion depth. Should that limit be
+ // reached, chances are that we're on a higher-order tangency. For
+ // this case, AW proposed to take the middle of the current
+ // interval, and to correct both curve's tangents at that new
+ // endpoint to be equal. That virtually generates a first-order
+ // tangency, and justifies to return a single intersection
+ // point. Otherwise, inside/outside test might fail here.
+
+ for( int i=0; i<recursionLevel; ++i ) cerr << " ";
+ if( recursionLevel % 2 )
+ {
+ cerr << "level: " << recursionLevel
+ << " t: "
+ << last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0
+ << ", c1: " << last_t1_c2 << " " << last_t2_c2
+ << ", c2: " << last_t1_c1 << " " << last_t2_c1
+ << endl;
+ }
+ else
+ {
+ cerr << "level: " << recursionLevel
+ << " t: "
+ << last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0
+ << ", c1: " << last_t1_c1 << " " << last_t2_c1
+ << ", c2: " << last_t1_c2 << " " << last_t2_c2
+ << endl;
+ }
+
+ // refine solution
+ // ===============
+
+ double t1_c1, t2_c1;
+
+ // Note: we first perform the clipping and only test for precision
+ // sufficiency afterwards, since we want to exploit the fact that
+ // Impl_calcClipRange returns false if the curves don't
+ // intersect. We would have to check that separately for the end
+ // condition, otherwise.
+
+ // determine safe range on c1_orig
+ if( safeRangeFunctor( t1_c1, t2_c1, c1_orig, c1_part, c2_orig, c2_part ) )
+ {
+ // now, t1 and t2 are calculated on the original curve
+ // (but against a fat line calculated from the subdivided
+ // c2, namely c2_part). If the [t1,t2] range is outside
+ // our current [last_t1,last_t2] range, we're done in this
+ // branch - the curves no longer intersect.
+ if( tolLessEqual(t1_c1, last_t2_c1) && tolGreaterEqual(t2_c1, last_t1_c1) )
+ {
+ // As noted above, t1 and t2 are calculated on the
+ // original curve, but against a fat line
+ // calculated from the subdivided c2, namely
+ // c2_part. Our domain to work on is
+ // [last_t1,last_t2], on the other hand, so values
+ // of [t1,t2] outside that range are irrelevant
+ // here. Clip range appropriately.
+ t1_c1 = ::std::max(t1_c1, last_t1_c1);
+ t2_c1 = ::std::min(t2_c1, last_t2_c1);
+
+ // TODO: respect delta
+ // for now, end condition is just a fixed threshold on the t's
+
+ // check end condition
+ // ===================
+
+#if 1
+ if( fabs(last_t2_c1 - last_t1_c1) < 0.0001 &&
+ fabs(last_t2_c2 - last_t1_c2) < 0.0001 )
+#else
+ if( fabs(last_t2_c1 - last_t1_c1) < 0.01 &&
+ fabs(last_t2_c2 - last_t1_c2) < 0.01 )
+#endif
+ {
+ // done. Add to result
+ if( recursionLevel % 2 )
+ {
+ // uneven level: have to swap the t's, since curves are swapped, too
+ *result++ = ::std::make_pair( last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0,
+ last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0 );
+ }
+ else
+ {
+ *result++ = ::std::make_pair( last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0,
+ last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0 );
+ }
+
+#if 0
+ //printResultWithFinalCurves( c1_orig, c1_part, c2_orig, c2_part, last_t1_c1, last_t2_c1 );
+ printResultWithFinalCurves( c1_orig, c1_part, c2_orig, c2_part, t1_c1, t2_c1 );
+#else
+ // calc focus curve of c2
+ Bezier focus;
+ Impl_calcFocus(focus, c2_part); // need to use subdivided c2
+
+ safeRangeFunctor( t1_c1, t2_c1, c1_orig, c1_part, c2_orig, c2_part );
+
+ //printResultWithFinalCurves( c1_orig, c1_part, c2_orig, focus, t1_c1, t2_c1 );
+ printResultWithFinalCurves( c1_orig, c1_part, c2_orig, focus, last_t1_c1, last_t2_c1 );
+#endif
+ }
+ else
+ {
+ // heuristic: if parameter range is not reduced by at least
+ // 20%, subdivide longest curve, and clip shortest against
+ // both parts of longest
+// if( (last_t2_c1 - last_t1_c1 - t2_c1 + t1_c1) / (last_t2_c1 - last_t1_c1) < 0.2 )
+ if( false )
+ {
+ // subdivide and descend
+ // =====================
+
+ Bezier part1;
+ Bezier part2;
+
+ double intervalMiddle;
+
+ if( last_t2_c1 - last_t1_c1 > last_t2_c2 - last_t1_c2 )
+ {
+ // subdivide c1
+ // ============
+
+ intervalMiddle = last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0;
+
+ // subdivide at the middle of the interval (as
+ // we're not subdividing on the original
+ // curve, this simply amounts to subdivision
+ // at 0.5)
+ Impl_deCasteljauAt( part1, part2, c1_part, 0.5 );
+
+ // and descend recursively with swapped curves
+ Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
+ c2_orig, c2_part, last_t1_c2, last_t2_c2,
+ c1_orig, part1, last_t1_c1, intervalMiddle );
+
+ Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
+ c2_orig, c2_part, last_t1_c2, last_t2_c2,
+ c1_orig, part2, intervalMiddle, last_t2_c1 );
+ }
+ else
+ {
+ // subdivide c2
+ // ============
+
+ intervalMiddle = last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0;
+
+ // subdivide at the middle of the interval (as
+ // we're not subdividing on the original
+ // curve, this simply amounts to subdivision
+ // at 0.5)
+ Impl_deCasteljauAt( part1, part2, c2_part, 0.5 );
+
+ // and descend recursively with swapped curves
+ Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
+ c2_orig, part1, last_t1_c2, intervalMiddle,
+ c1_orig, c1_part, last_t1_c1, last_t2_c1 );
+
+ Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
+ c2_orig, part2, intervalMiddle, last_t2_c2,
+ c1_orig, c1_part, last_t1_c1, last_t2_c1 );
+ }
+ }
+ else
+ {
+ // apply calculated clip
+ // =====================
+
+ // clip safe ranges off c1_orig
+ Bezier c1_part1;
+ Bezier c1_part2;
+ Bezier c1_part3;
+
+ // subdivide at t1_c1
+ Impl_deCasteljauAt( c1_part1, c1_part2, c1_orig, t1_c1 );
+
+ // subdivide at t2_c1. As we're working on
+ // c1_part2 now, we have to adapt t2_c1 since
+ // we're no longer in the original parameter
+ // interval. This is based on the following
+ // assumption: t2_new = (t2-t1)/(1-t1), which
+ // relates the t2 value into the new parameter
+ // range [0,1] of c1_part2.
+ Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2_c1-t1_c1)/(1.0-t1_c1) );
+
+ // descend with swapped curves and c1_part1 as the
+ // remaining (middle) segment
+ Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
+ c2_orig, c2_part, last_t1_c2, last_t2_c2,
+ c1_orig, c1_part1, t1_c1, t2_c1 );
+ }
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+struct ClipBezierFunctor
+{
+ bool operator()( double& t1_c1,
+ double& t2_c1,
+ const Bezier& c1_orig,
+ const Bezier& c1_part,
+ const Bezier& c2_orig,
+ const Bezier& c2_part ) const
+ {
+ return Impl_calcClipRange( t1_c1, t2_c1, c1_orig, c1_part, c2_orig, c2_part );
+ }
+};
+
+// -----------------------------------------------------------------------------
+
+struct BezierTangencyFunctor
+{
+ bool operator()( double& t1_c1,
+ double& t2_c1,
+ const Bezier& c1_orig,
+ const Bezier& c1_part,
+ const Bezier& c2_orig,
+ const Bezier& c2_part ) const
+ {
+ // calc focus curve of c2
+ Bezier focus;
+ Impl_calcFocus(focus, c2_part); // need to use subdivided c2
+ // here, as the whole curve is
+ // used for focus calculation
+
+ // determine safe range on c1_orig
+ bool bRet( Impl_calcSafeParams_focus( t1_c1, t2_c1,
+ c1_orig, // use orig curve here, need t's on original curve
+ focus ) );
+
+ cerr << "range: " << t2_c1 - t1_c1 << ", ret: " << bRet << endl;
+
+ return bRet;
+ }
+};
+
+// -----------------------------------------------------------------------------
+
+/** Perform a bezier clip (curve against curve)
+
+ @param result
+ Output iterator where the final t values are added to. This
+ iterator will remain empty, if there are no intersections.
+
+ @param delta
+ Maximal allowed distance to true intersection (measured in the
+ original curve's coordinate system)
+ */
+void clipBezier( ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > >& result,
+ double delta,
+ const Bezier& c1,
+ const Bezier& c2 )
+{
+#if 0
+ // first of all, determine list of collinear normals. Collinear
+ // normals typically separate two intersections, thus, subdivide
+ // at all collinear normal's t values beforehand. This will cater
+ // for tangent intersections, where two or more intersections are
+ // infinitesimally close together.
+
+ // TODO: evaluate effects of higher-than-second-order
+ // tangencies. Sederberg et al. state that collinear normal
+ // algorithm then degrades quickly.
+
+ ::std::vector< ::std::pair<double,double> > results;
+ ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > > ii(results);
+
+ Impl_calcCollinearNormals( ii, delta, 0, c1, c1, 0.0, 1.0, c2, c2, 0.0, 1.0 );
+
+ // As Sederberg's collinear normal theorem is only sufficient, not
+ // necessary for two intersections left and right, we've to test
+ // all segments generated by the collinear normal algorithm
+ // against each other. In other words, if the two curves are both
+ // divided in a left and a right part, the collinear normal
+ // theorem does _not_ state that the left part of curve 1 does not
+ // e.g. intersect with the right part of curve 2.
+
+ // divide c1 and c2 at collinear normal intersection points
+ ::std::vector< Bezier > c1_segments( results.size()+1 );
+ ::std::vector< Bezier > c2_segments( results.size()+1 );
+ Bezier c1_remainder( c1 );
+ Bezier c2_remainder( c2 );
+ unsigned int i;
+ for( i=0; i<results.size(); ++i )
+ {
+ Bezier c1_part2;
+ Impl_deCasteljauAt( c1_segments[i], c1_part2, c1_remainder, results[i].first );
+ c1_remainder = c1_part2;
+
+ Bezier c2_part2;
+ Impl_deCasteljauAt( c2_segments[i], c2_part2, c2_remainder, results[i].second );
+ c2_remainder = c2_part2;
+ }
+ c1_segments[i] = c1_remainder;
+ c2_segments[i] = c2_remainder;
+
+ // now, c1/c2_segments contain all segments, then
+ // clip every resulting segment against every other
+ unsigned int c1_curr, c2_curr;
+ for( c1_curr=0; c1_curr<c1_segments.size(); ++c1_curr )
+ {
+ for( c2_curr=0; c2_curr<c2_segments.size(); ++c2_curr )
+ {
+ if( c1_curr != c2_curr )
+ {
+ Impl_clipBezier_rec(result, delta, 0,
+ c1_segments[c1_curr], c1_segments[c1_curr],
+ 0.0, 1.0,
+ c2_segments[c2_curr], c2_segments[c2_curr],
+ 0.0, 1.0);
+ }
+ }
+ }
+#else
+ Impl_applySafeRanges_rec( result, delta, BezierTangencyFunctor(), 0, c1, c1, 0.0, 1.0, c2, c2, 0.0, 1.0 );
+ //Impl_applySafeRanges_rec( result, delta, ClipBezierFunctor(), 0, c1, c1, 0.0, 1.0, c2, c2, 0.0, 1.0 );
+#endif
+ // that's it, boys'n'girls!
+}
+
+int main(int argc, const char *argv[])
+{
+ double curr_Offset( 0 );
+ unsigned int i,j,k;
+
+ Bezier someCurves[] =
+ {
+// {Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0),Point2D(1.0,0.0)},
+// {Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0),Point2D(1.0,0.5)},
+// {Point2D(1.0,0.0),Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0)}
+// {Point2D(0.25+1,0.5),Point2D(0.25+1,0.708333),Point2D(0.423611+1,0.916667),Point2D(0.770833+1,0.980324)},
+// {Point2D(0.0+1,0.0),Point2D(0.0+1,1.0),Point2D(1.0+1,1.0),Point2D(1.0+1,0.5)}
+
+// tangency1
+// {Point2D(0.627124+1,0.828427),Point2D(0.763048+1,0.828507),Point2D(0.885547+1,0.77312),Point2D(0.950692+1,0.67325)},
+// {Point2D(0.0,1.0),Point2D(0.1,1.0),Point2D(0.4,1.0),Point2D(0.5,1.0)}
+
+// {Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0),Point2D(1.0,0.5)},
+// {Point2D(0.60114,0.933091),Point2D(0.69461,0.969419),Point2D(0.80676,0.992976),Point2D(0.93756,0.998663)}
+// {Point2D(1.0,0.0),Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0)},
+// {Point2D(0.62712,0.828427),Point2D(0.76305,0.828507),Point2D(0.88555,0.77312),Point2D(0.95069,0.67325)}
+
+// clipping1
+// {Point2D(0.0,0.0),Point2D(0.0,3.5),Point2D(1.0,-2.5),Point2D(1.0,1.0)},
+// {Point2D(0.0,1.0),Point2D(3.5,1.0),Point2D(-2.5,0.0),Point2D(1.0,0.0)}
+
+// tangency2
+// {Point2D(0.0,1.0),Point2D(3.5,1.0),Point2D(-2.5,0.0),Point2D(1.0,0.0)},
+// {Point2D(15.3621,0.00986464),Point2D(15.3683,0.0109389),Point2D(15.3682,0.0109315),Point2D(15.3621,0.00986464)}
+
+// tangency3
+// {Point2D(1.0,0.0),Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0)},
+// {Point2D(-0.5,0.0),Point2D(0.5,0.0),Point2D(0.5,1.0),Point2D(-0.5,1.0)}
+
+// tangency4
+// {Point2D(-0.5,0.0),Point2D(0.5,0.0),Point2D(0.5,1.0),Point2D(-0.5,1.0)},
+// {Point2D(0.26,0.4),Point2D(0.25,0.5),Point2D(0.25,0.5),Point2D(0.26,0.6)}
+
+// tangency5
+// {Point2D(0.0,0.0),Point2D(0.0,3.5),Point2D(1.0,-2.5),Point2D(1.0,1.0)},
+// {Point2D(15.3621,0.00986464),Point2D(15.3683,0.0109389),Point2D(15.3682,0.0109315),Point2D(15.3621,0.00986464)}
+
+// tangency6
+// {Point2D(0.0,0.0),Point2D(0.0,3.5),Point2D(1.0,-2.5),Point2D(1.0,1.0)},
+// {Point2D(15.3621,10.00986464),Point2D(15.3683,10.0109389),Point2D(15.3682,10.0109315),Point2D(15.3621,10.00986464)}
+
+// tangency7
+// {Point2D(2.505,0.0),Point2D(2.505+4.915,4.300),Point2D(2.505+3.213,10.019),Point2D(2.505-2.505,10.255)},
+// {Point2D(15.3621,10.00986464),Point2D(15.3683,10.0109389),Point2D(15.3682,10.0109315),Point2D(15.3621,10.00986464)}
+
+// tangency Sederberg example
+ {Point2D(2.505,0.0),Point2D(2.505+4.915,4.300),Point2D(2.505+3.213,10.019),Point2D(2.505-2.505,10.255)},
+ {Point2D(5.33+9.311,0.0),Point2D(5.33+9.311-13.279,4.205),Point2D(5.33+9.311-10.681,9.119),Point2D(5.33+9.311-2.603,10.254)}
+
+// clipping2
+// {Point2D(-0.5,0.0),Point2D(0.5,0.0),Point2D(0.5,1.0),Point2D(-0.5,1.0)},
+// {Point2D(0.2575,0.4),Point2D(0.2475,0.5),Point2D(0.2475,0.5),Point2D(0.2575,0.6)}
+
+// {Point2D(0.0,0.1),Point2D(0.2,3.5),Point2D(1.0,-2.5),Point2D(1.1,1.2)},
+// {Point2D(0.0,1.0),Point2D(3.5,0.9),Point2D(-2.5,0.1),Point2D(1.1,0.2)}
+// {Point2D(0.0,0.1),Point2D(0.2,3.0),Point2D(1.0,-2.0),Point2D(1.1,1.2)},
+// {Point2D(0.627124+1,0.828427),Point2D(0.763048+1,0.828507),Point2D(0.885547+1,0.77312),Point2D(0.950692+1,0.67325)}
+// {Point2D(0.0,1.0),Point2D(3.0,0.9),Point2D(-2.0,0.1),Point2D(1.1,0.2)}
+// {Point2D(0.0,4.0),Point2D(0.1,5.0),Point2D(0.9,5.0),Point2D(1.0,4.0)},
+// {Point2D(0.0,0.0),Point2D(0.1,0.5),Point2D(0.9,0.5),Point2D(1.0,0.0)},
+// {Point2D(0.0,0.1),Point2D(0.1,1.5),Point2D(0.9,1.5),Point2D(1.0,0.1)},
+// {Point2D(0.0,-4.0),Point2D(0.1,-5.0),Point2D(0.9,-5.0),Point2D(1.0,-4.0)}
+ };
+
+ // output gnuplot setup
+ cout << "#!/usr/bin/gnuplot -persist" << endl
+ << "#" << endl
+ << "# automatically generated by bezierclip, don't change!" << endl
+ << "#" << endl
+ << "set parametric" << endl
+ << "bez(p,q,r,s,t) = p*(1-t)**3+q*3*(1-t)**2*t+r*3*(1-t)*t**2+s*t**3" << endl
+ << "bezd(p,q,r,s,t) = 3*(q-p)*(1-t)**2+6*(r-q)*(1-t)*t+3*(s-r)*t**2" << endl
+ << "pointmarkx(c,t) = c-0.03*t" << endl
+ << "pointmarky(c,t) = c+0.03*t" << endl
+ << "linex(a,b,c,t) = a*-c + t*-b" << endl
+ << "liney(a,b,c,t) = b*-c + t*a" << endl << endl
+ << "# end of setup" << endl << endl;
+
+#ifdef WITH_CONVEXHULL_TEST
+ // test convex hull algorithm
+ const double convHull_xOffset( curr_Offset );
+ curr_Offset += 20;
+ cout << "# convex hull testing" << endl
+ << "plot [t=0:1] ";
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ Polygon2D aTestPoly(4);
+ aTestPoly[0] = someCurves[i].p0;
+ aTestPoly[1] = someCurves[i].p1;
+ aTestPoly[2] = someCurves[i].p2;
+ aTestPoly[3] = someCurves[i].p3;
+
+ aTestPoly[0].x += convHull_xOffset;
+ aTestPoly[1].x += convHull_xOffset;
+ aTestPoly[2].x += convHull_xOffset;
+ aTestPoly[3].x += convHull_xOffset;
+
+ cout << " bez("
+ << aTestPoly[0].x << ","
+ << aTestPoly[1].x << ","
+ << aTestPoly[2].x << ","
+ << aTestPoly[3].x << ",t),bez("
+ << aTestPoly[0].y << ","
+ << aTestPoly[1].y << ","
+ << aTestPoly[2].y << ","
+ << aTestPoly[3].y << ",t), '-' using ($1):($2) title \"convex hull " << i << "\" with lp";
+
+ if( i+1<sizeof(someCurves)/sizeof(Bezier) )
+ cout << ",\\" << endl;
+ else
+ cout << endl;
+ }
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ Polygon2D aTestPoly(4);
+ aTestPoly[0] = someCurves[i].p0;
+ aTestPoly[1] = someCurves[i].p1;
+ aTestPoly[2] = someCurves[i].p2;
+ aTestPoly[3] = someCurves[i].p3;
+
+ aTestPoly[0].x += convHull_xOffset;
+ aTestPoly[1].x += convHull_xOffset;
+ aTestPoly[2].x += convHull_xOffset;
+ aTestPoly[3].x += convHull_xOffset;
+
+ Polygon2D convHull( convexHull(aTestPoly) );
+
+ for( k=0; k<convHull.size(); ++k )
+ {
+ cout << convHull[k].x << " " << convHull[k].y << endl;
+ }
+ cout << convHull[0].x << " " << convHull[0].y << endl;
+ cout << "e" << endl;
+ }
+#endif
+
+#ifdef WITH_MULTISUBDIVIDE_TEST
+ // test convex hull algorithm
+ const double multiSubdivide_xOffset( curr_Offset );
+ curr_Offset += 20;
+ cout << "# multi subdivide testing" << endl
+ << "plot [t=0:1] ";
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ Bezier c( someCurves[i] );
+ Bezier c1_part1;
+ Bezier c1_part2;
+ Bezier c1_part3;
+
+ c.p0.x += multiSubdivide_xOffset;
+ c.p1.x += multiSubdivide_xOffset;
+ c.p2.x += multiSubdivide_xOffset;
+ c.p3.x += multiSubdivide_xOffset;
+
+ const double t1( 0.1+i/(3.0*sizeof(someCurves)/sizeof(Bezier)) );
+ const double t2( 0.9-i/(3.0*sizeof(someCurves)/sizeof(Bezier)) );
+
+ // subdivide at t1
+ Impl_deCasteljauAt( c1_part1, c1_part2, c, t1 );
+
+ // subdivide at t2_c1. As we're working on
+ // c1_part2 now, we have to adapt t2_c1 since
+ // we're no longer in the original parameter
+ // interval. This is based on the following
+ // assumption: t2_new = (t2-t1)/(1-t1), which
+ // relates the t2 value into the new parameter
+ // range [0,1] of c1_part2.
+ Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2-t1)/(1.0-t1) );
+
+ // subdivide at t2
+ Impl_deCasteljauAt( c1_part3, c1_part2, c, t2 );
+
+ cout << " bez("
+ << c1_part1.p0.x << ","
+ << c1_part1.p1.x << ","
+ << c1_part1.p2.x << ","
+ << c1_part1.p3.x << ",t), bez("
+ << c1_part1.p0.y+0.01 << ","
+ << c1_part1.p1.y+0.01 << ","
+ << c1_part1.p2.y+0.01 << ","
+ << c1_part1.p3.y+0.01 << ",t) title \"middle " << i << "\", "
+ << " bez("
+ << c1_part2.p0.x << ","
+ << c1_part2.p1.x << ","
+ << c1_part2.p2.x << ","
+ << c1_part2.p3.x << ",t), bez("
+ << c1_part2.p0.y << ","
+ << c1_part2.p1.y << ","
+ << c1_part2.p2.y << ","
+ << c1_part2.p3.y << ",t) title \"right " << i << "\", "
+ << " bez("
+ << c1_part3.p0.x << ","
+ << c1_part3.p1.x << ","
+ << c1_part3.p2.x << ","
+ << c1_part3.p3.x << ",t), bez("
+ << c1_part3.p0.y << ","
+ << c1_part3.p1.y << ","
+ << c1_part3.p2.y << ","
+ << c1_part3.p3.y << ",t) title \"left " << i << "\"";
+
+
+ if( i+1<sizeof(someCurves)/sizeof(Bezier) )
+ cout << ",\\" << endl;
+ else
+ cout << endl;
+ }
+#endif
+
+#ifdef WITH_FATLINE_TEST
+ // test fatline algorithm
+ const double fatLine_xOffset( curr_Offset );
+ curr_Offset += 20;
+ cout << "# fat line testing" << endl
+ << "plot [t=0:1] ";
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ Bezier c( someCurves[i] );
+
+ c.p0.x += fatLine_xOffset;
+ c.p1.x += fatLine_xOffset;
+ c.p2.x += fatLine_xOffset;
+ c.p3.x += fatLine_xOffset;
+
+ FatLine line;
+
+ Impl_calcFatLine(line, c);
+
+ cout << " bez("
+ << c.p0.x << ","
+ << c.p1.x << ","
+ << c.p2.x << ","
+ << c.p3.x << ",t), bez("
+ << c.p0.y << ","
+ << c.p1.y << ","
+ << c.p2.y << ","
+ << c.p3.y << ",t) title \"bezier " << i << "\", linex("
+ << line.a << ","
+ << line.b << ","
+ << line.c << ",t), liney("
+ << line.a << ","
+ << line.b << ","
+ << line.c << ",t) title \"fat line (center) on " << i << "\", linex("
+ << line.a << ","
+ << line.b << ","
+ << line.c-line.dMin << ",t), liney("
+ << line.a << ","
+ << line.b << ","
+ << line.c-line.dMin << ",t) title \"fat line (min) on " << i << "\", linex("
+ << line.a << ","
+ << line.b << ","
+ << line.c-line.dMax << ",t), liney("
+ << line.a << ","
+ << line.b << ","
+ << line.c-line.dMax << ",t) title \"fat line (max) on " << i << "\"";
+
+ if( i+1<sizeof(someCurves)/sizeof(Bezier) )
+ cout << ",\\" << endl;
+ else
+ cout << endl;
+ }
+#endif
+
+#ifdef WITH_CALCFOCUS_TEST
+ // test focus curve algorithm
+ const double focus_xOffset( curr_Offset );
+ curr_Offset += 20;
+ cout << "# focus line testing" << endl
+ << "plot [t=0:1] ";
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ Bezier c( someCurves[i] );
+
+ c.p0.x += focus_xOffset;
+ c.p1.x += focus_xOffset;
+ c.p2.x += focus_xOffset;
+ c.p3.x += focus_xOffset;
+
+ // calc focus curve
+ Bezier focus;
+ Impl_calcFocus(focus, c);
+
+ cout << " bez("
+ << c.p0.x << ","
+ << c.p1.x << ","
+ << c.p2.x << ","
+ << c.p3.x << ",t), bez("
+ << c.p0.y << ","
+ << c.p1.y << ","
+ << c.p2.y << ","
+ << c.p3.y << ",t) title \"bezier " << i << "\", bez("
+ << focus.p0.x << ","
+ << focus.p1.x << ","
+ << focus.p2.x << ","
+ << focus.p3.x << ",t), bez("
+ << focus.p0.y << ","
+ << focus.p1.y << ","
+ << focus.p2.y << ","
+ << focus.p3.y << ",t) title \"focus " << i << "\"";
+
+
+ if( i+1<sizeof(someCurves)/sizeof(Bezier) )
+ cout << ",\\" << endl;
+ else
+ cout << endl;
+ }
+#endif
+
+#ifdef WITH_SAFEPARAMBASE_TEST
+ // test safe params base method
+ double safeParamsBase_xOffset( curr_Offset );
+ cout << "# safe param base method testing" << endl
+ << "plot [t=0:1] ";
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ Bezier c( someCurves[i] );
+
+ c.p0.x += safeParamsBase_xOffset;
+ c.p1.x += safeParamsBase_xOffset;
+ c.p2.x += safeParamsBase_xOffset;
+ c.p3.x += safeParamsBase_xOffset;
+
+ Polygon2D poly(4);
+ poly[0] = c.p0;
+ poly[1] = c.p1;
+ poly[2] = c.p2;
+ poly[3] = c.p3;
+
+ double t1, t2;
+
+ bool bRet( Impl_calcSafeParams( t1, t2, poly, 0, 1 ) );
+
+ Polygon2D convHull( convexHull( poly ) );
+
+ cout << " bez("
+ << poly[0].x << ","
+ << poly[1].x << ","
+ << poly[2].x << ","
+ << poly[3].x << ",t),bez("
+ << poly[0].y << ","
+ << poly[1].y << ","
+ << poly[2].y << ","
+ << poly[3].y << ",t), "
+ << "t+" << safeParamsBase_xOffset << ", 0, "
+ << "t+" << safeParamsBase_xOffset << ", 1, ";
+ if( bRet )
+ {
+ cout << t1+safeParamsBase_xOffset << ", t, "
+ << t2+safeParamsBase_xOffset << ", t, ";
+ }
+ cout << "'-' using ($1):($2) title \"control polygon\" with lp, "
+ << "'-' using ($1):($2) title \"convex hull\" with lp";
+
+ if( i+1<sizeof(someCurves)/sizeof(Bezier) )
+ cout << ",\\" << endl;
+ else
+ cout << endl;
+
+ safeParamsBase_xOffset += 2;
+ }
+
+ safeParamsBase_xOffset = curr_Offset;
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ Bezier c( someCurves[i] );
+
+ c.p0.x += safeParamsBase_xOffset;
+ c.p1.x += safeParamsBase_xOffset;
+ c.p2.x += safeParamsBase_xOffset;
+ c.p3.x += safeParamsBase_xOffset;
+
+ Polygon2D poly(4);
+ poly[0] = c.p0;
+ poly[1] = c.p1;
+ poly[2] = c.p2;
+ poly[3] = c.p3;
+
+ double t1, t2;
+
+ Impl_calcSafeParams( t1, t2, poly, 0, 1 );
+
+ Polygon2D convHull( convexHull( poly ) );
+
+ unsigned int k;
+ for( k=0; k<poly.size(); ++k )
+ {
+ cout << poly[k].x << " " << poly[k].y << endl;
+ }
+ cout << poly[0].x << " " << poly[0].y << endl;
+ cout << "e" << endl;
+
+ for( k=0; k<convHull.size(); ++k )
+ {
+ cout << convHull[k].x << " " << convHull[k].y << endl;
+ }
+ cout << convHull[0].x << " " << convHull[0].y << endl;
+ cout << "e" << endl;
+
+ safeParamsBase_xOffset += 2;
+ }
+ curr_Offset += 20;
+#endif
+
+#ifdef WITH_SAFEPARAMS_TEST
+ // test safe parameter range algorithm
+ const double safeParams_xOffset( curr_Offset );
+ curr_Offset += 20;
+ cout << "# safe param range testing" << endl
+ << "plot [t=0.0:1.0] ";
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
+ {
+ Bezier c1( someCurves[i] );
+ Bezier c2( someCurves[j] );
+
+ c1.p0.x += safeParams_xOffset;
+ c1.p1.x += safeParams_xOffset;
+ c1.p2.x += safeParams_xOffset;
+ c1.p3.x += safeParams_xOffset;
+ c2.p0.x += safeParams_xOffset;
+ c2.p1.x += safeParams_xOffset;
+ c2.p2.x += safeParams_xOffset;
+ c2.p3.x += safeParams_xOffset;
+
+ double t1, t2;
+
+ if( Impl_calcClipRange(t1, t2, c1, c1, c2, c2) )
+ {
+ // clip safe ranges off c1
+ Bezier c1_part1;
+ Bezier c1_part2;
+ Bezier c1_part3;
+
+ // subdivide at t1_c1
+ Impl_deCasteljauAt( c1_part1, c1_part2, c1, t1 );
+ // subdivide at t2_c1
+ Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2-t1)/(1.0-t1) );
+
+ // output remaining segment (c1_part1)
+
+ cout << " bez("
+ << c1.p0.x << ","
+ << c1.p1.x << ","
+ << c1.p2.x << ","
+ << c1.p3.x << ",t),bez("
+ << c1.p0.y << ","
+ << c1.p1.y << ","
+ << c1.p2.y << ","
+ << c1.p3.y << ",t), bez("
+ << c2.p0.x << ","
+ << c2.p1.x << ","
+ << c2.p2.x << ","
+ << c2.p3.x << ",t),bez("
+ << c2.p0.y << ","
+ << c2.p1.y << ","
+ << c2.p2.y << ","
+ << c2.p3.y << ",t), bez("
+ << c1_part1.p0.x << ","
+ << c1_part1.p1.x << ","
+ << c1_part1.p2.x << ","
+ << c1_part1.p3.x << ",t),bez("
+ << c1_part1.p0.y << ","
+ << c1_part1.p1.y << ","
+ << c1_part1.p2.y << ","
+ << c1_part1.p3.y << ",t)";
+
+ if( i+2<sizeof(someCurves)/sizeof(Bezier) )
+ cout << ",\\" << endl;
+ else
+ cout << endl;
+ }
+ }
+ }
+#endif
+
+#ifdef WITH_SAFEPARAM_DETAILED_TEST
+ // test safe parameter range algorithm
+ const double safeParams2_xOffset( curr_Offset );
+ curr_Offset += 20;
+ if( sizeof(someCurves)/sizeof(Bezier) > 1 )
+ {
+ Bezier c1( someCurves[0] );
+ Bezier c2( someCurves[1] );
+
+ c1.p0.x += safeParams2_xOffset;
+ c1.p1.x += safeParams2_xOffset;
+ c1.p2.x += safeParams2_xOffset;
+ c1.p3.x += safeParams2_xOffset;
+ c2.p0.x += safeParams2_xOffset;
+ c2.p1.x += safeParams2_xOffset;
+ c2.p2.x += safeParams2_xOffset;
+ c2.p3.x += safeParams2_xOffset;
+
+ double t1, t2;
+
+ // output happens here
+ Impl_calcClipRange(t1, t2, c1, c1, c2, c2);
+ }
+#endif
+
+#ifdef WITH_SAFEFOCUSPARAM_TEST
+ // test safe parameter range from focus algorithm
+ const double safeParamsFocus_xOffset( curr_Offset );
+ curr_Offset += 20;
+ cout << "# safe param range from focus testing" << endl
+ << "plot [t=0.0:1.0] ";
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
+ {
+ Bezier c1( someCurves[i] );
+ Bezier c2( someCurves[j] );
+
+ c1.p0.x += safeParamsFocus_xOffset;
+ c1.p1.x += safeParamsFocus_xOffset;
+ c1.p2.x += safeParamsFocus_xOffset;
+ c1.p3.x += safeParamsFocus_xOffset;
+ c2.p0.x += safeParamsFocus_xOffset;
+ c2.p1.x += safeParamsFocus_xOffset;
+ c2.p2.x += safeParamsFocus_xOffset;
+ c2.p3.x += safeParamsFocus_xOffset;
+
+ double t1, t2;
+
+ Bezier focus;
+#ifdef WITH_SAFEFOCUSPARAM_CALCFOCUS
+#if 0
+ {
+ // clip safe ranges off c1_orig
+ Bezier c1_part1;
+ Bezier c1_part2;
+ Bezier c1_part3;
+
+ // subdivide at t1_c1
+ Impl_deCasteljauAt( c1_part1, c1_part2, c2, 0.30204 );
+
+ // subdivide at t2_c1. As we're working on
+ // c1_part2 now, we have to adapt t2_c1 since
+ // we're no longer in the original parameter
+ // interval. This is based on the following
+ // assumption: t2_new = (t2-t1)/(1-t1), which
+ // relates the t2 value into the new parameter
+ // range [0,1] of c1_part2.
+ Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (0.57151-0.30204)/(1.0-0.30204) );
+
+ c2 = c1_part1;
+ Impl_calcFocus( focus, c2 );
+ }
+#else
+ Impl_calcFocus( focus, c2 );
+#endif
+#else
+ focus = c2;
+#endif
+ // determine safe range on c1
+ bool bRet( Impl_calcSafeParams_focus( t1, t2,
+ c1, focus ) );
+
+ cerr << "t1: " << t1 << ", t2: " << t2 << endl;
+
+ // clip safe ranges off c1
+ Bezier c1_part1;
+ Bezier c1_part2;
+ Bezier c1_part3;
+
+ // subdivide at t1_c1
+ Impl_deCasteljauAt( c1_part1, c1_part2, c1, t1 );
+ // subdivide at t2_c1
+ Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2-t1)/(1.0-t1) );
+
+ // output remaining segment (c1_part1)
+
+ cout << " bez("
+ << c1.p0.x << ","
+ << c1.p1.x << ","
+ << c1.p2.x << ","
+ << c1.p3.x << ",t),bez("
+ << c1.p0.y << ","
+ << c1.p1.y << ","
+ << c1.p2.y << ","
+ << c1.p3.y << ",t) title \"c1\", "
+#ifdef WITH_SAFEFOCUSPARAM_CALCFOCUS
+ << "bez("
+ << c2.p0.x << ","
+ << c2.p1.x << ","
+ << c2.p2.x << ","
+ << c2.p3.x << ",t),bez("
+ << c2.p0.y << ","
+ << c2.p1.y << ","
+ << c2.p2.y << ","
+ << c2.p3.y << ",t) title \"c2\", "
+ << "bez("
+ << focus.p0.x << ","
+ << focus.p1.x << ","
+ << focus.p2.x << ","
+ << focus.p3.x << ",t),bez("
+ << focus.p0.y << ","
+ << focus.p1.y << ","
+ << focus.p2.y << ","
+ << focus.p3.y << ",t) title \"focus\"";
+#else
+ << "bez("
+ << c2.p0.x << ","
+ << c2.p1.x << ","
+ << c2.p2.x << ","
+ << c2.p3.x << ",t),bez("
+ << c2.p0.y << ","
+ << c2.p1.y << ","
+ << c2.p2.y << ","
+ << c2.p3.y << ",t) title \"focus\"";
+#endif
+ if( bRet )
+ {
+ cout << ", bez("
+ << c1_part1.p0.x << ","
+ << c1_part1.p1.x << ","
+ << c1_part1.p2.x << ","
+ << c1_part1.p3.x << ",t),bez("
+ << c1_part1.p0.y+0.01 << ","
+ << c1_part1.p1.y+0.01 << ","
+ << c1_part1.p2.y+0.01 << ","
+ << c1_part1.p3.y+0.01 << ",t) title \"part\"";
+ }
+
+ if( i+2<sizeof(someCurves)/sizeof(Bezier) )
+ cout << ",\\" << endl;
+ else
+ cout << endl;
+ }
+ }
+#endif
+
+#ifdef WITH_SAFEFOCUSPARAM_DETAILED_TEST
+ // test safe parameter range algorithm
+ const double safeParams3_xOffset( curr_Offset );
+ curr_Offset += 20;
+ if( sizeof(someCurves)/sizeof(Bezier) > 1 )
+ {
+ Bezier c1( someCurves[0] );
+ Bezier c2( someCurves[1] );
+
+ c1.p0.x += safeParams3_xOffset;
+ c1.p1.x += safeParams3_xOffset;
+ c1.p2.x += safeParams3_xOffset;
+ c1.p3.x += safeParams3_xOffset;
+ c2.p0.x += safeParams3_xOffset;
+ c2.p1.x += safeParams3_xOffset;
+ c2.p2.x += safeParams3_xOffset;
+ c2.p3.x += safeParams3_xOffset;
+
+ double t1, t2;
+
+ Bezier focus;
+#ifdef WITH_SAFEFOCUSPARAM_CALCFOCUS
+ Impl_calcFocus( focus, c2 );
+#else
+ focus = c2;
+#endif
+
+ // determine safe range on c1, output happens here
+ Impl_calcSafeParams_focus( t1, t2,
+ c1, focus );
+ }
+#endif
+
+#ifdef WITH_BEZIERCLIP_TEST
+ ::std::vector< ::std::pair<double, double> > result;
+ ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > > ii(result);
+
+ // test full bezier clipping
+ const double bezierClip_xOffset( curr_Offset );
+ curr_Offset += 20;
+ cout << endl << endl << "# bezier clip testing" << endl
+ << "plot [t=0:1] ";
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
+ {
+ Bezier c1( someCurves[i] );
+ Bezier c2( someCurves[j] );
+
+ c1.p0.x += bezierClip_xOffset;
+ c1.p1.x += bezierClip_xOffset;
+ c1.p2.x += bezierClip_xOffset;
+ c1.p3.x += bezierClip_xOffset;
+ c2.p0.x += bezierClip_xOffset;
+ c2.p1.x += bezierClip_xOffset;
+ c2.p2.x += bezierClip_xOffset;
+ c2.p3.x += bezierClip_xOffset;
+
+ cout << " bez("
+ << c1.p0.x << ","
+ << c1.p1.x << ","
+ << c1.p2.x << ","
+ << c1.p3.x << ",t),bez("
+ << c1.p0.y << ","
+ << c1.p1.y << ","
+ << c1.p2.y << ","
+ << c1.p3.y << ",t), bez("
+ << c2.p0.x << ","
+ << c2.p1.x << ","
+ << c2.p2.x << ","
+ << c2.p3.x << ",t),bez("
+ << c2.p0.y << ","
+ << c2.p1.y << ","
+ << c2.p2.y << ","
+ << c2.p3.y << ",t), '-' using (bez("
+ << c1.p0.x << ","
+ << c1.p1.x << ","
+ << c1.p2.x << ","
+ << c1.p3.x
+ << ",$1)):(bez("
+ << c1.p0.y << ","
+ << c1.p1.y << ","
+ << c1.p2.y << ","
+ << c1.p3.y << ",$1)) title \"bezier " << i << " clipped against " << j << " (t on " << i << ")\", "
+ << " '-' using (bez("
+ << c2.p0.x << ","
+ << c2.p1.x << ","
+ << c2.p2.x << ","
+ << c2.p3.x
+ << ",$1)):(bez("
+ << c2.p0.y << ","
+ << c2.p1.y << ","
+ << c2.p2.y << ","
+ << c2.p3.y << ",$1)) title \"bezier " << i << " clipped against " << j << " (t on " << j << ")\"";
+
+ if( i+2<sizeof(someCurves)/sizeof(Bezier) )
+ cout << ",\\" << endl;
+ else
+ cout << endl;
+ }
+ }
+ for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
+ {
+ for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
+ {
+ result.clear();
+ Bezier c1( someCurves[i] );
+ Bezier c2( someCurves[j] );
+
+ c1.p0.x += bezierClip_xOffset;
+ c1.p1.x += bezierClip_xOffset;
+ c1.p2.x += bezierClip_xOffset;
+ c1.p3.x += bezierClip_xOffset;
+ c2.p0.x += bezierClip_xOffset;
+ c2.p1.x += bezierClip_xOffset;
+ c2.p2.x += bezierClip_xOffset;
+ c2.p3.x += bezierClip_xOffset;
+
+ clipBezier( ii, 0.00001, c1, c2 );
+
+ for( k=0; k<result.size(); ++k )
+ {
+ cout << result[k].first << endl;
+ }
+ cout << "e" << endl;
+
+ for( k=0; k<result.size(); ++k )
+ {
+ cout << result[k].second << endl;
+ }
+ cout << "e" << endl;
+ }
+ }
+#endif
+
+ return 0;
+}
diff --git a/basegfx/source/workbench/bezierclip.hxx b/basegfx/source/workbench/bezierclip.hxx
new file mode 100644
index 000000000000..3efd74f06f49
--- /dev/null
+++ b/basegfx/source/workbench/bezierclip.hxx
@@ -0,0 +1,96 @@
+/*************************************************************************
+ *
+ * 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: bezierclip.hxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef BASEGFX_BEZIERCLIP_HXX
+#define BASEGFX_BEZIERCLIP_HXX
+
+#include <vector>
+
+struct Point2D
+{
+ typedef double value_type;
+ Point2D( double _x, double _y ) : x(_x), y(_y) {}
+ Point2D() : x(), y() {}
+ double x;
+ double y;
+};
+
+struct Bezier
+{
+ Point2D p0;
+ Point2D p1;
+ Point2D p2;
+ Point2D p3;
+
+ Point2D& operator[]( int i ) { return reinterpret_cast<Point2D*>(this)[i]; }
+ const Point2D& operator[]( int i ) const { return reinterpret_cast<const Point2D*>(this)[i]; }
+};
+
+struct FatLine
+{
+ // line L through p1 and p4 in normalized implicit form
+ double a;
+ double b;
+ double c;
+
+ // the upper and lower distance from this line
+ double dMin;
+ double dMax;
+};
+
+template <typename DataType> DataType calcLineDistance( const DataType& a,
+ const DataType& b,
+ const DataType& c,
+ const DataType& x,
+ const DataType& y )
+{
+ return a*x + b*y + c;
+}
+
+typedef ::std::vector< Point2D > Polygon2D;
+
+/* little abs template */
+template <typename NumType> NumType absval( NumType x )
+{
+ return x<0 ? -x : x;
+}
+
+Polygon2D convexHull( const Polygon2D& rPoly );
+
+// TODO: find proper epsilon here (try ::std::numeric_limits<NumType>::epsilon()?)!
+#define DBL_EPSILON 1.0e-100
+
+/* little approximate comparions */
+template <typename NumType> bool tolZero( NumType n ) { return fabs(n) < DBL_EPSILON; }
+template <typename NumType> bool tolEqual( NumType n1, NumType n2 ) { return tolZero(n1-n2); }
+template <typename NumType> bool tolLessEqual( NumType n1, NumType n2 ) { return tolEqual(n1,n2) || n1<n2; }
+template <typename NumType> bool tolGreaterEqual( NumType n1, NumType n2 ) { return tolEqual(n1,n2) || n1>n2; }
+
+#endif /* BASEGFX_BEZIERCLIP_HXX */
diff --git a/basegfx/source/workbench/convexhull.cxx b/basegfx/source/workbench/convexhull.cxx
new file mode 100644
index 000000000000..6c27221ee734
--- /dev/null
+++ b/basegfx/source/workbench/convexhull.cxx
@@ -0,0 +1,216 @@
+/*************************************************************************
+ *
+ * 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: convexhull.cxx,v $
+ * $Revision: 1.6 $
+ *
+ * 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 <algorithm>
+#include <functional>
+#include <vector>
+
+#include "bezierclip.hxx"
+
+// -----------------------------------------------------------------------------
+
+/* Implements the theta function from Sedgewick: Algorithms in XXX, chapter 24 */
+template <class PointType> double theta( const PointType& p1, const PointType& p2 )
+{
+ typename PointType::value_type dx, dy, ax, ay;
+ double t;
+
+ dx = p2.x - p1.x; ax = absval( dx );
+ dy = p2.y - p1.y; ay = absval( dy );
+ t = (ax+ay == 0) ? 0 : (double) dy/(ax+ay);
+ if( dx < 0 )
+ t = 2-t;
+ else if( dy < 0 )
+ t = 4+t;
+
+ return t*90.0;
+}
+
+/* Model of LessThanComparable for theta sort.
+ * Uses the theta function from Sedgewick: Algorithms in XXX, chapter 24
+ */
+template <class PointType> class ThetaCompare : public ::std::binary_function< const PointType&, const PointType&, bool >
+{
+public:
+ ThetaCompare( const PointType& rRefPoint ) : maRefPoint( rRefPoint ) {}
+
+ bool operator() ( const PointType& p1, const PointType& p2 )
+ {
+ // return true, if p1 precedes p2 in the angle relative to maRefPoint
+ return theta(maRefPoint, p1) < theta(maRefPoint, p2);
+ }
+
+ double operator() ( const PointType& p ) const
+ {
+ return theta(maRefPoint, p);
+ }
+
+private:
+ PointType maRefPoint;
+};
+
+/* Implementation of the predicate 'counter-clockwise' for three points, from Sedgewick: Algorithms in XXX, chapter 24 */
+template <class PointType, class CmpFunctor> typename PointType::value_type ccw( const PointType& p0, const PointType& p1, const PointType& p2, CmpFunctor& thetaCmp )
+{
+ typename PointType::value_type dx1, dx2, dy1, dy2;
+ typename PointType::value_type theta0( thetaCmp(p0) );
+ typename PointType::value_type theta1( thetaCmp(p1) );
+ typename PointType::value_type theta2( thetaCmp(p2) );
+
+#if 0
+ if( theta0 == theta1 ||
+ theta0 == theta2 ||
+ theta1 == theta2 )
+ {
+ // cannot reliably compare, as at least two points are
+ // theta-equal. See bug description below
+ return 0;
+ }
+#endif
+
+ dx1 = p1.x - p0.x; dy1 = p1.y - p0.y;
+ dx2 = p2.x - p0.x; dy2 = p2.y - p0.y;
+
+ if( dx1*dy2 > dy1*dx2 )
+ return +1;
+
+ if( dx1*dy2 < dy1*dx2 )
+ return -1;
+
+ if( (dx1*dx2 < 0) || (dy1*dy2 < 0) )
+ return -1;
+
+ if( (dx1*dx1 + dy1*dy1) < (dx2*dx2 + dy2*dy2) )
+ return +1;
+
+ return 0;
+}
+
+/*
+ Bug
+ ===
+
+ Sometimes, the resulting polygon is not the convex hull (see below
+ for an edge configuration to reproduce that problem)
+
+ Problem analysis:
+ =================
+
+ The root cause of this bug is the fact that the second part of
+ the algorithm (the 'wrapping' of the point set) relies on the
+ previous theta sorting. Namely, it is required that the
+ generated point ordering, when interpreted as a polygon, is not
+ self-intersecting. This property, although, cannot be
+ guaranteed due to limited floating point accuracy. For example,
+ for two points very close together, and at the same time very
+ far away from the theta reference point p1, can appear on the
+ same theta value (because floating point accuracy is limited),
+ although on different rays to p1 when inspected locally.
+
+ Example:
+
+ /
+ P3 /
+ |\ /
+ | /
+ |/ \
+ P2 \
+ \
+ ...____________\
+ P1
+
+ Here, P2 and P3 are theta-equal relative to P1, but the local
+ ccw measure always says 'left turn'. Thus, the convex hull is
+ wrong at this place.
+
+ Solution:
+ =========
+
+ If two points are theta-equal and checked via ccw, ccw must
+ also classify them as 'equal'. Thus, the second stage of the
+ convex hull algorithm sorts the first one out, effectively
+ reducing a cluster of theta-equal points to only one. This
+ single point can then be treated correctly.
+*/
+
+
+/* Implementation of Graham's convex hull algorithm, see Sedgewick: Algorithms in XXX, chapter 25 */
+Polygon2D convexHull( const Polygon2D& rPoly )
+{
+ const Polygon2D::size_type N( rPoly.size() );
+ Polygon2D result( N + 1 );
+ ::std::copy(rPoly.begin(), rPoly.end(), result.begin()+1 );
+ Polygon2D::size_type min, i;
+
+ // determine safe point on hull (smallest y value)
+ for( min=1, i=2; i<=N; ++i )
+ {
+ if( result[i].y < result[min].y )
+ min = i;
+ }
+
+ // determine safe point on hull (largest x value)
+ for( i=1; i<=N; ++i )
+ {
+ if( result[i].y == result[min].y &&
+ result[i].x > result[min].x )
+ {
+ min = i;
+ }
+ }
+
+ // TODO: add inner elimination optimization from Sedgewick: Algorithms in XXX, chapter 25
+ // TODO: use radix sort instead of ::std::sort(), calc theta only once and store
+
+ // setup first point and sort
+ ::std::swap( result[1], result[min] );
+ ThetaCompare<Point2D> cmpFunc(result[1]);
+ ::std::sort( result.begin()+1, result.end(), cmpFunc );
+
+ // setup sentinel
+ result[0] = result[N];
+
+ // generate convex hull
+ Polygon2D::size_type M;
+ for( M=3, i=4; i<=N; ++i )
+ {
+ while( ccw(result[M], result[M-1], result[i], cmpFunc) >= 0 )
+ --M;
+
+ ++M;
+ ::std::swap( result[M], result[i] );
+ }
+
+ // copy range [1,M] to output
+ return Polygon2D( result.begin()+1, result.begin()+M+1 );
+}
diff --git a/basegfx/source/workbench/gauss.hxx b/basegfx/source/workbench/gauss.hxx
new file mode 100644
index 000000000000..9df12ecc88d6
--- /dev/null
+++ b/basegfx/source/workbench/gauss.hxx
@@ -0,0 +1,175 @@
+/*************************************************************************
+ *
+ * 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: gauss.hxx,v $
+ * $Revision: 1.5 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+/** This method eliminates elements below main diagonal in the given
+ matrix by gaussian elimination.
+
+ @param matrix
+ The matrix to operate on. Last column is the result vector (right
+ hand side of the linear equation). After successful termination,
+ the matrix is upper triangular. The matrix is expected to be in
+ row major order.
+
+ @param rows
+ Number of rows in matrix
+
+ @param cols
+ Number of columns in matrix
+
+ @param minPivot
+ If the pivot element gets lesser than minPivot, this method fails,
+ otherwise, elimination succeeds and true is returned.
+
+ @return true, if elimination succeeded.
+ */
+template <class Matrix, typename BaseType>
+bool eliminate( Matrix& matrix,
+ int rows,
+ int cols,
+ const BaseType& minPivot )
+{
+ BaseType temp;
+ int max, i, j, k; /* *must* be signed, when looping like: j>=0 ! */
+
+ /* eliminate below main diagonal */
+ for(i=0; i<cols-1; ++i)
+ {
+ /* find best pivot */
+ max = i;
+ for(j=i+1; j<rows; ++j)
+ if( fabs(matrix[ j*cols + i ]) > fabs(matrix[ max*cols + i ]) )
+ max = j;
+
+ /* check pivot value */
+ if( fabs(matrix[ max*cols + i ]) < minPivot )
+ return false; /* pivot too small! */
+
+ /* interchange rows 'max' and 'i' */
+ for(k=0; k<cols; ++k)
+ {
+ temp = matrix[ i*cols + k ];
+ matrix[ i*cols + k ] = matrix[ max*cols + k ];
+ matrix[ max*cols + k ] = temp;
+ }
+
+ /* eliminate column */
+ for(j=i+1; j<rows; ++j)
+ for(k=cols-1; k>=i; --k)
+ matrix[ j*cols + k ] -= matrix[ i*cols + k ] *
+ matrix[ j*cols + i ] / matrix[ i*cols + i ];
+ }
+
+ /* everything went well */
+ return true;
+}
+
+
+/** Retrieve solution vector of linear system by substituting backwards.
+
+ This operation _relies_ on the previous successful
+ application of eliminate()!
+
+ @param matrix
+ Matrix in upper diagonal form, as e.g. generated by eliminate()
+
+ @param rows
+ Number of rows in matrix
+
+ @param cols
+ Number of columns in matrix
+
+ @param result
+ Result vector. Given matrix must have space for one column (rows entries).
+
+ @return true, if back substitution was possible (i.e. no division
+ by zero occured).
+ */
+template <class Matrix, class Vector, typename BaseType>
+bool substitute( const Matrix& matrix,
+ int rows,
+ int cols,
+ Vector& result )
+{
+ BaseType temp;
+ int j,k; /* *must* be signed, when looping like: j>=0 ! */
+
+ /* substitute backwards */
+ for(j=rows-1; j>=0; --j)
+ {
+ temp = 0.0;
+ for(k=j+1; k<cols-1; ++k)
+ temp += matrix[ j*cols + k ] * result[k];
+
+ if( matrix[ j*cols + j ] == 0.0 )
+ return false; /* imminent division by zero! */
+
+ result[j] = (matrix[ j*cols + cols-1 ] - temp) / matrix[ j*cols + j ];
+ }
+
+ /* everything went well */
+ return true;
+}
+
+
+/** This method determines solution of given linear system, if any
+
+ This is a wrapper for eliminate and substitute, given matrix must
+ contain right side of equation as the last column.
+
+ @param matrix
+ The matrix to operate on. Last column is the result vector (right
+ hand side of the linear equation). After successful termination,
+ the matrix is upper triangular. The matrix is expected to be in
+ row major order.
+
+ @param rows
+ Number of rows in matrix
+
+ @param cols
+ Number of columns in matrix
+
+ @param minPivot
+ If the pivot element gets lesser than minPivot, this method fails,
+ otherwise, elimination succeeds and true is returned.
+
+ @return true, if elimination succeeded.
+ */
+template <class Matrix, class Vector, typename BaseType>
+bool solve( Matrix& matrix,
+ int rows,
+ int cols,
+ Vector& result,
+ BaseType minPivot )
+{
+ if( eliminate<Matrix,BaseType>(matrix, rows, cols, minPivot) )
+ return substitute<Matrix,Vector,BaseType>(matrix, rows, cols, result);
+
+ return false;
+}
diff --git a/basegfx/test/basegfx1d.cxx b/basegfx/test/basegfx1d.cxx
new file mode 100644
index 000000000000..f058b0034fa7
--- /dev/null
+++ b/basegfx/test/basegfx1d.cxx
@@ -0,0 +1,80 @@
+/*************************************************************************
+ *
+ * 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: basegfx1d.cxx,v $
+ * $Revision: 1.3 $
+ *
+ * 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"
+// autogenerated file with codegen.pl
+
+#include <cppunit/simpleheader.hxx>
+
+namespace basegfx1d
+{
+
+class b1drange : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ // this is only demonstration code
+ void EmptyMethod()
+ {
+ // CPPUNIT_ASSERT_MESSAGE("a message", 1 == 1);
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b1drange);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b1drange
+
+// -----------------------------------------------------------------------------
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx1d::b1drange, "basegfx1d");
+} // namespace basegfx1d
+
+
+// -----------------------------------------------------------------------------
+
+// this macro creates an empty function, which will called by the RegisterAllFunctions()
+// to let the user the possibility to also register some functions by hand.
+NOADDITIONAL;
+
diff --git a/basegfx/test/basegfx2d.cxx b/basegfx/test/basegfx2d.cxx
new file mode 100644
index 000000000000..4f0305df953f
--- /dev/null
+++ b/basegfx/test/basegfx2d.cxx
@@ -0,0 +1,1644 @@
+/*************************************************************************
+ *
+ * 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: basegfx2d.cxx,v $
+ * $Revision: 1.14 $
+ *
+ * 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"
+// autogenerated file with codegen.pl
+
+#include <cppunit/simpleheader.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <basegfx/curve/b2dbeziertools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/range/b2dmultirange.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/color/bcolortools.hxx>
+
+#include <basegfx/tools/debugplotter.hxx>
+
+#include <iostream>
+#include <fstream>
+
+using namespace ::basegfx;
+
+
+namespace basegfx2d
+{
+/// Gets a random ordinal [0,n)
+inline double getRandomOrdinal( const ::std::size_t n )
+{
+ return double(n) * rand() / (RAND_MAX + 1.0);
+}
+
+class b2dmultirange : public CppUnit::TestFixture
+{
+private:
+ B2DMultiRange aDisjunctRanges;
+ B2DMultiRange aEqualRanges;
+ B2DMultiRange aIntersectionN;
+ B2DMultiRange aIntersectionE;
+ B2DMultiRange aIntersectionS;
+ B2DMultiRange aIntersectionW;
+ B2DMultiRange aIntersectionNE;
+ B2DMultiRange aIntersectionSE;
+ B2DMultiRange aIntersectionSW;
+ B2DMultiRange aIntersectionNW;
+ B2DMultiRange aRingIntersection;
+ B2DMultiRange aComplexIntersections;
+ B2DMultiRange aRandomIntersections;
+
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ B2DRange aCenter(1.0, 1.0, -1.0, -1.0);
+ B2DRange aOffside(9.0, 9.0, 11.0, 11.0);
+ B2DRange aNorth(1.0, 0.0, -1.0, -2.0);
+ B2DRange aSouth(1.0, 2.0, -1.0, 0.0);
+ B2DRange aEast(0.0, 1.0, 2.0, -1.0);
+ B2DRange aWest(-2.0, 1.0, 0.0, -1.0);
+ B2DRange aNorthEast(0.0, 0.0, 2.0, -2.0);
+ B2DRange aSouthEast(0.0, 0.0, 2.0, 2.0);
+ B2DRange aSouthWest(0.0, 0.0, -2.0, 2.0);
+ B2DRange aNorthWest(0.0, 0.0, -2.0, -2.0);
+
+ B2DRange aNorth2(-1.5, 0.5, 1.5, 3.5);
+ B2DRange aSouth2(-1.5, -0.5, 1.5, -3.5);
+ B2DRange aEast2 (0.5, -1.5, 3.5, 1.5);
+ B2DRange aWest2 (-0.5, -1.5,-3.5, 1.5);
+
+ ::std::ofstream output("multirange_testcases.gnuplot");
+ DebugPlotter aPlotter( "multirange testcases",
+ output );
+
+ aPlotter.plot( aCenter, "center" );
+ aPlotter.plot( aOffside, "offside" );
+ aPlotter.plot( aNorth, "north" );
+ aPlotter.plot( aSouth, "south" );
+ aPlotter.plot( aEast, "east" );
+ aPlotter.plot( aWest, "west" );
+ aPlotter.plot( aNorthEast, "northeast" );
+ aPlotter.plot( aSouthEast, "southeast" );
+ aPlotter.plot( aSouthWest, "southwest" );
+ aPlotter.plot( aNorthWest, "northwest" );
+
+ aDisjunctRanges.addRange( aCenter );
+ aDisjunctRanges.addRange( aOffside );
+
+ aEqualRanges.addRange( aCenter );
+ aEqualRanges.addRange( aCenter );
+
+ aIntersectionN.addRange( aCenter );
+ aIntersectionN.addRange( aNorth );
+
+ aIntersectionE.addRange( aCenter );
+ aIntersectionE.addRange( aEast );
+
+ aIntersectionS.addRange( aCenter );
+ aIntersectionS.addRange( aSouth );
+
+ aIntersectionW.addRange( aCenter );
+ aIntersectionW.addRange( aWest );
+
+ aIntersectionNE.addRange( aCenter );
+ aIntersectionNE.addRange( aNorthEast );
+
+ aIntersectionSE.addRange( aCenter );
+ aIntersectionSE.addRange( aSouthEast );
+
+ aIntersectionSW.addRange( aCenter );
+ aIntersectionSW.addRange( aSouthWest );
+
+ aIntersectionNW.addRange( aCenter );
+ aIntersectionNW.addRange( aNorthWest );
+
+ aRingIntersection.addRange( aNorth2 );
+ aRingIntersection.addRange( aEast2 );
+ aRingIntersection.addRange( aSouth2 );
+ aRingIntersection.addRange( aWest2 );
+
+ aComplexIntersections.addRange( aCenter );
+ aComplexIntersections.addRange( aOffside );
+ aComplexIntersections.addRange( aCenter );
+ aComplexIntersections.addRange( aNorth );
+ aComplexIntersections.addRange( aEast );
+ aComplexIntersections.addRange( aSouth );
+ aComplexIntersections.addRange( aWest );
+ aComplexIntersections.addRange( aNorthEast );
+ aComplexIntersections.addRange( aSouthEast );
+ aComplexIntersections.addRange( aSouthWest );
+ aComplexIntersections.addRange( aNorthWest );
+
+/*
+ for( int i=0; i<10; ++i )
+ {
+ B2DRange aRandomRange(
+ getRandomOrdinal( 10 ),
+ getRandomOrdinal( 10 ),
+ getRandomOrdinal( 10 ),
+ getRandomOrdinal( 10 ) );
+
+ aRandomIntersections.addRange( aRandomRange );
+ }
+*/
+ }
+
+ void tearDown()
+ {
+ }
+
+ ::basegfx::B2DPolyPolygon shiftPoly( int nCount,
+ const ::basegfx::B2DPolyPolygon& rPoly )
+ {
+ B2DHomMatrix aMatrix;
+ aMatrix.translate( nCount*4.0,
+ 10.0-nCount*2.0 );
+
+ ::basegfx::B2DPolyPolygon aRes( rPoly );
+ aRes.transform( aMatrix );
+
+ return aRes;
+ }
+
+ void getPolyPolygon()
+ {
+ ::std::ofstream output("multirange_getpolypolygon.gnuplot");
+ DebugPlotter aPlotter( "multirange getPolyPolygon",
+ output );
+
+ B2DPolyPolygon result;
+
+ aPlotter.plot( shiftPoly(
+ 0,
+ aDisjunctRanges.getPolyPolygon() ),
+ "disjunct" );
+ aPlotter.plot( shiftPoly(
+ 1,
+ aEqualRanges.getPolyPolygon() ),
+ "equal" );
+ aPlotter.plot( shiftPoly(
+ 2,
+ aIntersectionN.getPolyPolygon() ),
+ "intersectionN" );
+ aPlotter.plot( shiftPoly(
+ 3,
+ aIntersectionE.getPolyPolygon() ),
+ "intersectionE" );
+ aPlotter.plot( shiftPoly(
+ 4,
+ aIntersectionS.getPolyPolygon() ),
+ "intersectionS" );
+ aPlotter.plot( shiftPoly(
+ 5,
+ aIntersectionW.getPolyPolygon() ),
+ "intersectionW" );
+ aPlotter.plot( shiftPoly(
+ 6,
+ aIntersectionNE.getPolyPolygon() ),
+ "intersectionNE" );
+ aPlotter.plot( shiftPoly(
+ 7,
+ aIntersectionSE.getPolyPolygon() ),
+ "intersectionSE" );
+ aPlotter.plot( shiftPoly(
+ 8,
+ aIntersectionSW.getPolyPolygon() ),
+ "intersectionSW" );
+ aPlotter.plot( shiftPoly(
+ 9,
+ aIntersectionNW.getPolyPolygon() ),
+ "intersectionNW" );
+ aPlotter.plot( shiftPoly(
+ 10,
+ aRingIntersection.getPolyPolygon() ),
+ "intersection ring" );
+ aPlotter.plot( shiftPoly(
+ 11,
+ aComplexIntersections.getPolyPolygon() ),
+ "intersection complex" );
+ aPlotter.plot( shiftPoly(
+ 12,
+ aRandomIntersections.getPolyPolygon() ),
+ "intersection random" );
+
+ CPPUNIT_ASSERT_MESSAGE("getPolyPolygon", true );
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dmultirange);
+ CPPUNIT_TEST(getPolyPolygon);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dmultirange
+
+class b2dsvgdimpex : public CppUnit::TestFixture
+{
+private:
+ ::rtl::OUString aPath0;
+ ::rtl::OUString aPath1;
+ ::rtl::OUString aPath2;
+ ::rtl::OUString aPath3;
+
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ // simple rectangle
+ aPath0 = ::rtl::OUString::createFromAscii(
+ "M 10 10-10 10-10-10 10-10Z" );
+
+ // simple bezier polygon
+ aPath1 = ::rtl::OUString::createFromAscii(
+ "m11430 0c-8890 3810 5715 6985 5715 6985 "
+ "0 0-17145-1905-17145-1905 0 0 22860-10160 "
+ "16510 6350-6350 16510-3810-11430-3810-11430z" );
+
+ // '@' as a bezier polygon
+ aPath2 = ::rtl::OUString::createFromAscii(
+ "m1917 1114c-89-189-233-284-430-284-167 "
+ "0-306 91-419 273-113 182-170 370-170 564 "
+ "0 145 33 259 98 342 65 84 150 126 257 126 "
+ "77 0 154-19 231-57 77-38 147-97 210-176 63"
+ "-79 99-143 109-190 38-199 76-398 114-598z"
+ "m840 1646c-133 73-312 139-537 197-225 57"
+ "-440 86-644 87-483-1-866-132-1150-392-284"
+ "-261-426-619-426-1076 0-292 67-560 200-803 "
+ "133-243 321-433 562-569 241-136 514-204 821"
+ "-204 405 0 739 125 1003 374 264 250 396 550 "
+ "396 899 0 313-88 576-265 787-177 212-386 318"
+ "-627 318-191 0-308-94-352-281-133 187-315 281"
+ "-546 281-172 0-315-67-428-200-113-133-170-301"
+ "-170-505 0-277 90-527 271-751 181-223 394"
+ "-335 640-335 196 0 353 83 470 250 13-68 26"
+ "-136 41-204 96 0 192 0 288 0-74 376-148 752"
+ "-224 1128-21 101-31 183-31 245 0 39 9 70 26 "
+ "93 17 24 39 36 67 36 145 0 279-80 400-240 121"
+ "-160 182-365 182-615 0-288-107-533-322-734"
+ "-215-201-487-301-816-301-395 0-715 124-960 "
+ "373-245 249-368 569-368 958 0 385 119 685 "
+ "357 900 237 216 557 324 958 325 189-1 389-27 "
+ "600-77 211-52 378-110 503-174 27 70 54 140 81 210z" );
+
+ // first part of 'Hello World' as a line polygon
+ aPath3 = ::rtl::OUString::createFromAscii(
+ "m1598 125h306v2334h-306v-1105h-1293v1105h-305v"
+ "-2334h305v973h1293zm2159 1015 78-44 85 235-91 "
+ "47-91 40-90 34-90 29-89 21-88 16-88 10-88 3-102"
+ "-4-97-12-91-19-85-26-40-16-39-18-38-20-36-22-34"
+ "-24-33-26-32-27-30-30-29-31-27-33-25-33-23-36-21"
+ "-36-19-38-18-40-16-40-26-86-18-91-11-97-4-103 3"
+ "-98 11-94 17-89 24-84 31-79 37-75 22-35 23-34 24"
+ "-33 27-32 28-30 29-28 31-27 31-24 33-22 34-21 35"
+ "-18 37-17 38-14 38-13 41-11 41-8 86-12 91-4 82 4 "
+ "78 10 37 9 37 9 36 12 35 14 33 15 33 17 32 19 31 "
+ "21 30 22 30 25 55 54 26 29 24 31 22 32 21 33 19 "
+ "34 18 36 30 74 23 80 17 84 10 89 3 94v78h-1277l6 "
+ "75 10 70 14 66 19 62 23 57 13 26 14 26 15 25 17 "
+ "23 17 22 19 21 19 20 21 18 21 18 23 16 23 14 24 "
+ "14 26 12 26 11 27 10 28 8 59 13 63 7 67 3 80-3 81"
+ "-9 79-14 80-21 78-26 79-32zm-1049-808-12 53h963l"
+ "-7-51-11-49-14-46-17-43-21-40-24-38-27-36-31-32"
+ "-33-29-35-25-37-22-38-17-40-14-41-9-42-6-44-2-48 "
+ "2-46 6-44 9-42 13-40 17-38 21-36 24-34 28-32 32"
+ "-29 34-26 38-23 41-20 44-17 47zm1648-1293h288v"
+ "2459h-288zm752-2459h288v2459h-288zm1286-1750 86-11 "
+ "91-4 91 4 85 12 42 8 39 11 39 13 38 14 36 17 35 18 "
+ "34 20 33 23 31 24 30 26 29 28 28 30 26 32 25 33 23 "
+ "34 21 35 37 75 31 80 24 84 16 90 11 94 3 100-3 100"
+ "-11 95-16 89-24 85-31 80-37 74-21 35-23 35-25 32-26 "
+ "32-28 30-29 28-30 26-31 24-33 22-34 21-35 18-36 17"
+ "-38 14-39 13-39 10-42 9-85 12-91 4-91-4-86-12-41-9"
+ "-40-10-39-13-37-14-36-17-35-18-34-21-33-22-31-24-30"
+ "-26-29-28-28-30-26-32-25-32-23-35-21-35-38-74-30-80"
+ "-24-85-17-89-11-95-3-100 3-101 11-95 17-90 24-85 30"
+ "-79 38-75 21-35 23-35 25-32 26-32 28-30 29-28 30-26 "
+ "31-24 33-22 34-20 35-18 36-16 37-15 39-12 40-11z" );
+ }
+
+ void tearDown()
+ {
+ }
+
+ void impex()
+ {
+ B2DPolyPolygon aPoly;
+ ::rtl::OUString aExport;
+
+ CPPUNIT_ASSERT_MESSAGE("importing simple rectangle from SVG-D",
+ tools::importFromSvgD( aPoly,
+ aPath0 ));
+ aExport = tools::exportToSvgD( aPoly );
+ const char* sExportString = "m10 10h-20v-20h20z";
+ CPPUNIT_ASSERT_MESSAGE("exporting rectangle to SVG-D",
+ !aExport.compareToAscii(sExportString) );
+ CPPUNIT_ASSERT_MESSAGE("importing simple rectangle from SVG-D (round-trip",
+ tools::importFromSvgD( aPoly,
+ aExport ));
+ aExport = tools::exportToSvgD( aPoly );
+ CPPUNIT_ASSERT_MESSAGE("exporting rectangle to SVG-D (round-trip)",
+ !aExport.compareToAscii(sExportString));
+
+ CPPUNIT_ASSERT_MESSAGE("importing simple bezier polygon from SVG-D",
+ tools::importFromSvgD( aPoly,
+ aPath1 ));
+ aExport = tools::exportToSvgD( aPoly );
+
+ // Adaptions for B2DPolygon bezier change (see #i77162#):
+ //
+ // The import/export of aPath1 does not reproduce aExport again. This is
+ // correct since aPath1 contains a segment with non-used control points
+ // which gets exported now correctly as 'l' and also a point (#4, index 3)
+ // with C2 continuity which produces a 's' staement now.
+ //
+ // The old SVGexport identified nun-used ControlVectors erraneously as bezier segments
+ // because the 2nd vector at the start point was used, even when added
+ // with start point was identical to end point. Exactly for that reason
+ // i reworked the B2DPolygon to use prev, next control points.
+ //
+ // so for correct unit test i add the new exported string here as sExportStringSimpleBezier
+ // and compare to it.
+ const char* sExportStringSimpleBezier =
+ "m11430 0c-8890 3810 5715 6985 5715 6985"
+ "l-17145-1905c0 0 22860-10160 16510 6350"
+ "s-3810-11430-3810-11430z";
+ CPPUNIT_ASSERT_MESSAGE("exporting bezier polygon to SVG-D", !aExport.compareToAscii(sExportStringSimpleBezier));
+
+ // Adaptions for B2DPolygon bezier change (see #i77162#):
+ //
+ // a 2nd good test is that re-importing of aExport has to create the same
+ // B2DPolPolygon again:
+ B2DPolyPolygon aReImport;
+ CPPUNIT_ASSERT_MESSAGE("importing simple bezier polygon from SVG-D", tools::importFromSvgD( aReImport, aExport));
+ CPPUNIT_ASSERT_MESSAGE("re-imported polygon needs to be identical", aReImport == aPoly);
+
+ CPPUNIT_ASSERT_MESSAGE("importing '@' from SVG-D", tools::importFromSvgD( aPoly, aPath2 ));
+ aExport = tools::exportToSvgD( aPoly );
+
+ // Adaptions for B2DPolygon bezier change (see #i77162#):
+ //
+ // same here, the corrected export with the corrected B2DPolygon is simply more efficient,
+ // so i needed to change the compare string. Also adding the re-import comparison below.
+ const char* sExportString1 =
+ "m1917 1114c-89-189-233-284-430-284-167 0-306 91-419 273s-170 370-17"
+ "0 564c0 145 33 259 98 342 65 84 150 126 257 126q115.5 0 231-57s147-97 210-176 99-143 109-190c38-199 76-398 114"
+ "-598zm840 1646c-133 73-312 139-537 197-225 57-440 86-644 87-483-1-866-132-1150-392-284-261-426-619-426-1076 0-"
+ "292 67-560 200-803s321-433 562-569 514-204 821-204c405 0 739 125 1003 374 264 250 396 550 396 899 0 313-88 576"
+ "-265 787q-265.5 318-627 318c-191 0-308-94-352-281-133 187-315 281-546 281-172 0-315-67-428-200s-170-301-170-50"
+ "5c0-277 90-527 271-751 181-223 394-335 640-335 196 0 353 83 470 250 13-68 26-136 41-204q144 0 288 0c-74 376-14"
+ "8 752-224 1128-21 101-31 183-31 245 0 39 9 70 26 93 17 24 39 36 67 36 145 0 279-80 400-240s182-365 182-615c0-2"
+ "88-107-533-322-734s-487-301-816-301c-395 0-715 124-960 373s-368 569-368 958q0 577.5 357 900c237 216 557 324 95"
+ "8 325 189-1 389-27 600-77 211-52 378-110 503-174q40.5 105 81 210z";
+ CPPUNIT_ASSERT_MESSAGE("re-importing '@' from SVG-D", tools::importFromSvgD( aReImport, aExport));
+ CPPUNIT_ASSERT_MESSAGE("re-imported '@' needs to be identical", aReImport == aPoly);
+
+ CPPUNIT_ASSERT_MESSAGE("exporting '@' to SVG-D", !aExport.compareToAscii(sExportString1));
+ CPPUNIT_ASSERT_MESSAGE("importing '@' from SVG-D (round-trip",
+ tools::importFromSvgD( aPoly,
+ aExport ));
+ aExport = tools::exportToSvgD( aPoly );
+ CPPUNIT_ASSERT_MESSAGE("exporting '@' to SVG-D (round-trip)",
+ !aExport.compareToAscii(sExportString1));
+
+
+ CPPUNIT_ASSERT_MESSAGE("importing complex polygon from SVG-D",
+ tools::importFromSvgD( aPoly,
+ aPath3 ));
+ aExport = tools::exportToSvgD( aPoly );
+ const char* sExportString2 =
+ "m1598 125h306v2334h-306v-1105h-1293v1105h-305v-2334h305v973h1293"
+ "zm2159 1015 78-44 85 235-91 47-91 40-90 34-90 29-89 21-88 16-88 10-88 3-102-4-97"
+ "-12-91-19-85-26-40-16-39-18-38-20-36-22-34-24-33-26-32-27-30-30-29-31-27-33-25-3"
+ "3-23-36-21-36-19-38-18-40-16-40-26-86-18-91-11-97-4-103 3-98 11-94 17-89 24-84 3"
+ "1-79 37-75 22-35 23-34 24-33 27-32 28-30 29-28 31-27 31-24 33-22 34-21 35-18 37-"
+ "17 38-14 38-13 41-11 41-8 86-12 91-4 82 4 78 10 37 9 37 9 36 12 35 14 33 15 33 1"
+ "7 32 19 31 21 30 22 30 25 55 54 26 29 24 31 22 32 21 33 19 34 18 36 30 74 23 80 "
+ "17 84 10 89 3 94v78h-1277l6 75 10 70 14 66 19 62 23 57 13 26 14 26 15 25 17 23 1"
+ "7 22 19 21 19 20 21 18 21 18 23 16 23 14 24 14 26 12 26 11 27 10 28 8 59 13 63 7"
+ " 67 3 80-3 81-9 79-14 80-21 78-26 79-32zm-1049-808-12 53h963l-7-51-11-49-14-46-1"
+ "7-43-21-40-24-38-27-36-31-32-33-29-35-25-37-22-38-17-40-14-41-9-42-6-44-2-48 2-4"
+ "6 6-44 9-42 13-40 17-38 21-36 24-34 28-32 32-29 34-26 38-23 41-20 44-17 47zm1648"
+ "-1293h288v2459h-288zm752-2459h288v2459h-288zm1286-1750 86-11 91-4 91 4 85 12 42 "
+ "8 39 11 39 13 38 14 36 17 35 18 34 20 33 23 31 24 30 26 29 28 28 30 26 32 25 33 "
+ "23 34 21 35 37 75 31 80 24 84 16 90 11 94 3 100-3 100-11 95-16 89-24 85-31 80-37"
+ " 74-21 35-23 35-25 32-26 32-28 30-29 28-30 26-31 24-33 22-34 21-35 18-36 17-38 1"
+ "4-39 13-39 10-42 9-85 12-91 4-91-4-86-12-41-9-40-10-39-13-37-14-36-17-35-18-34-2"
+ "1-33-22-31-24-30-26-29-28-28-30-26-32-25-32-23-35-21-35-38-74-30-80-24-85-17-89-"
+ "11-95-3-100 3-101 11-95 17-90 24-85 30-79 38-75 21-35 23-35 25-32 26-32 28-30 29"
+ "-28 30-26 31-24 33-22 34-20 35-18 36-16 37-15 39-12 40-11z";
+ CPPUNIT_ASSERT_MESSAGE("exporting complex polygon to SVG-D",
+ !aExport.compareToAscii(sExportString2));
+ CPPUNIT_ASSERT_MESSAGE("importing complex polygon from SVG-D (round-trip",
+ tools::importFromSvgD( aPoly,
+ aExport ));
+ aExport = tools::exportToSvgD( aPoly );
+ CPPUNIT_ASSERT_MESSAGE("exporting complex polygon to SVG-D (round-trip)",
+ !aExport.compareToAscii(sExportString2));
+
+ const B2DPolygon aCircle(
+ tools::createPolygonFromEllipse( B2DPoint(4000,4000),
+ 1000.0, 2000.0 ));
+ aExport = tools::exportToSvgD( B2DPolyPolygon(aCircle), false, false);
+
+ // count number of spaces, in lieu of a better way - no real
+ // point in comparing with a gold standard, as fractional
+ // parts of the coordinates will differ between systems.
+ sal_Int32 nIndex=0, nCount=0;
+ do
+ {
+ rtl::OUString aToken = aExport.getToken( 0, ' ', nIndex );
+ ++nCount;
+ }
+ while ( nIndex >= 0 );
+
+ CPPUNIT_ASSERT_MESSAGE("exporting to circle does not produce the expected number of coordinates",
+ nCount==18);
+
+ const B2DPolygon aRect(
+ tools::createPolygonFromRect( B2DRange(0.0,0.0,4000.0,4000.0) ));
+ aExport = tools::exportToSvgD( B2DPolyPolygon(aRect), false, false);
+
+ const char* sExportStringRect = "M0 0H4000V4000H0Z";
+ CPPUNIT_ASSERT_MESSAGE("exporting to rectangle svg-d string",
+ !aExport.compareToAscii(sExportStringRect));
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dsvgdimpex);
+ CPPUNIT_TEST(impex);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dsvgdimpex
+
+class b2dbeziertools : public CppUnit::TestFixture
+{
+private:
+ B2DCubicBezier aHalfCircle; // not exactly, but a look-alike
+ B2DCubicBezier aQuarterCircle; // not exactly, but a look-alike
+ B2DCubicBezier aLoop; // identical endpoints, curve goes back to where it started
+ B2DCubicBezier aStraightLineDistinctEndPoints; // truly a line
+ B2DCubicBezier aStraightLineDistinctEndPoints2; // truly a line, with slightly different control points
+ B2DCubicBezier aStraightLineIdenticalEndPoints; // degenerate case of aLoop
+ B2DCubicBezier aStraightLineIdenticalEndPoints2;// degenerate case of aLoop, with slightly different control points
+ B2DCubicBezier aCrossing; // curve self-intersects somewhere in the middle
+ B2DCubicBezier aCusp; // curve has a point of undefined tangency
+
+
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ const B2DPoint a00(0.0, 0.0);
+ const B2DPoint a10(1.0, 0.0);
+ const B2DPoint a11(1.0, 1.0);
+ const B2DPoint a01(0.0, 1.0);
+ const B2DPoint middle( 0.5, 0.5 );
+ const B2DPoint quarterDown( 0.25, 0.25 );
+ const B2DPoint quarterUp( 0.75, 0.75 );
+
+ aHalfCircle = B2DCubicBezier(a00, a01, a11, a10);
+
+ // The spline control points become
+ //
+ // (R * cos(A), R * sin(A))
+ // (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A))
+ // (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B))
+ // (R * cos(B), R * sin(B))
+ //
+ // where h = 4/3 * R * tan ((B-A)/4)
+ //
+ // with R being the radius, A start angle and B end angle (A < B).
+ //
+ // (This calculation courtesy Carl Worth, himself based on
+ // Michael Goldapp and Dokken/Daehlen)
+
+ // Choosing R=1, A=0, B=pi/2
+ const double h( 4.0/3.0 * tan(M_PI/8.0) );
+ aQuarterCircle = B2DCubicBezier(a10 + B2DPoint(1.0,0.0),
+ B2DPoint(B2DPoint( 1.0, h ) + B2DPoint(1.0,0.0)),
+ B2DPoint(B2DPoint( h, 1.0) + B2DPoint(1.0,0.0)),
+ a01 + B2DPoint(1.0,0.0));
+
+ aCusp = B2DCubicBezier(a00 + B2DPoint(2.0,0.0),
+ B2DPoint(a11 + B2DPoint(2.0,0.0)),
+ B2DPoint(a01 + B2DPoint(2.0,0.0)),
+ a10 + B2DPoint(2.0,0.0));
+
+ aLoop = B2DCubicBezier(a00 + B2DPoint(3.0,0.0),
+ B2DPoint(a01 + B2DPoint(3.0,0.0)),
+ B2DPoint(a10 + B2DPoint(3.0,0.0)),
+ a00 + B2DPoint(3.0,0.0));
+
+ aStraightLineDistinctEndPoints = B2DCubicBezier(a00 + B2DPoint(4.0,0.0),
+ B2DPoint(middle + B2DPoint(4.0,0.0)),
+ B2DPoint(middle + B2DPoint(4.0,0.0)),
+ a11 + B2DPoint(4.0,0.0));
+
+ aStraightLineDistinctEndPoints2 = B2DCubicBezier(a00 + B2DPoint(5.0,0.0),
+ B2DPoint(quarterDown + B2DPoint(5.0,0.0)),
+ B2DPoint(quarterUp + B2DPoint(5.0,0.0)),
+ a11 + B2DPoint(5.0,0.0));
+
+ aStraightLineIdenticalEndPoints = B2DCubicBezier(a00 + B2DPoint(6.0,0.0),
+ B2DPoint(a11 + B2DPoint(6.0,0.0)),
+ B2DPoint(a11 + B2DPoint(6.0,0.0)),
+ a00 + B2DPoint(6.0,0.0));
+
+ aStraightLineIdenticalEndPoints2 = B2DCubicBezier(a00 + B2DPoint(7.0,0.0),
+ B2DPoint(quarterDown + B2DPoint(7.0,0.0)),
+ B2DPoint(quarterUp + B2DPoint(7.0,0.0)),
+ a00 + B2DPoint(7.0,0.0));
+
+ aCrossing = B2DCubicBezier(a00 + B2DPoint(8.0,0.0),
+ B2DPoint(B2DPoint(2.0,2.0) + B2DPoint(8.0,0.0)),
+ B2DPoint(B2DPoint(-1.0,2.0) + B2DPoint(8.0,0.0)),
+ a10 + B2DPoint(8.0,0.0));
+
+ ::std::ofstream output("bez_testcases.gnuplot");
+ DebugPlotter aPlotter( "Original curves",
+ output );
+
+ aPlotter.plot( aHalfCircle,
+ "half circle" );
+ aPlotter.plot( aQuarterCircle,
+ "quarter circle" );
+ aPlotter.plot( aCusp,
+ "cusp" );
+ aPlotter.plot( aLoop,
+ "loop" );
+ aPlotter.plot( aStraightLineDistinctEndPoints,
+ "straight line 0" );
+ aPlotter.plot( aStraightLineDistinctEndPoints2,
+ "straight line 1" );
+ aPlotter.plot( aStraightLineIdenticalEndPoints,
+ "straight line 2" );
+ aPlotter.plot( aStraightLineIdenticalEndPoints2,
+ "straight line 3" );
+ aPlotter.plot( aCrossing,
+ "crossing" );
+
+ // break up a complex bezier (loopy, spiky or self intersecting)
+ // into simple segments (left to right)
+ B2DCubicBezier aSegment = aCrossing;
+ double fExtremePos(0.0);
+
+ aPlotter.plot( aSegment, "segment" );
+ while(aSegment.getMinimumExtremumPosition(fExtremePos))
+ {
+ aSegment.split(fExtremePos, 0, &aSegment);
+ aPlotter.plot( aSegment, "segment" );
+ }
+ }
+
+ void tearDown()
+ {
+ }
+
+ void adaptiveByDistance()
+ {
+ ::std::ofstream output("bez_adaptiveByDistance.gnuplot");
+ DebugPlotter aPlotter( "distance-adaptive subdivision",
+ output );
+
+ const double fBound( 0.0001 );
+ B2DPolygon result;
+
+ aHalfCircle.adaptiveSubdivideByDistance(result, fBound);
+ aPlotter.plot(result,
+ "half circle"); result.clear();
+
+ aQuarterCircle.adaptiveSubdivideByDistance(result, fBound);
+ aPlotter.plot(result,
+ "quarter circle"); result.clear();
+
+ aLoop.adaptiveSubdivideByDistance(result, fBound);
+ aPlotter.plot(result,
+ "loop"); result.clear();
+
+ aStraightLineDistinctEndPoints.adaptiveSubdivideByDistance(result, fBound);
+ aPlotter.plot(result,
+ "straight line 0"); result.clear();
+
+ aStraightLineDistinctEndPoints2.adaptiveSubdivideByDistance(result, fBound);
+ aPlotter.plot(result,
+ "straight line 1"); result.clear();
+
+ aStraightLineIdenticalEndPoints.adaptiveSubdivideByDistance(result, fBound);
+ aPlotter.plot(result,
+ "straight line 2"); result.clear();
+
+ aStraightLineIdenticalEndPoints2.adaptiveSubdivideByDistance(result, fBound);
+ aPlotter.plot(result,
+ "straight line 3"); result.clear();
+
+ aCrossing.adaptiveSubdivideByDistance(result, fBound);
+ aPlotter.plot(result,
+ "straight line 4"); result.clear();
+
+ aCusp.adaptiveSubdivideByDistance(result, fBound);
+ aPlotter.plot(result,
+ "straight line 5"); result.clear();
+
+ CPPUNIT_ASSERT_MESSAGE("adaptiveByDistance", true );
+ }
+
+ void adaptiveByAngle()
+ {
+ const double fBound( 5.0 );
+ B2DPolygon result;
+
+ ::std::ofstream output("bez_adaptiveByAngle.gnuplot");
+ DebugPlotter aPlotter( "angle-adaptive subdivision",
+ output );
+
+ aHalfCircle.adaptiveSubdivideByAngle(result, fBound, true);
+ aPlotter.plot(result,
+ "half circle"); result.clear();
+
+ aQuarterCircle.adaptiveSubdivideByAngle(result, fBound, true);
+ aPlotter.plot(result,
+ "quarter cirle"); result.clear();
+
+ aLoop.adaptiveSubdivideByAngle(result, fBound, true);
+ aPlotter.plot(result,
+ "loop"); result.clear();
+
+ aStraightLineDistinctEndPoints.adaptiveSubdivideByAngle(result, fBound, true);
+ aPlotter.plot(result,
+ "straight line 0"); result.clear();
+
+ aStraightLineDistinctEndPoints2.adaptiveSubdivideByAngle(result, fBound, true);
+ aPlotter.plot(result,
+ "straight line 1"); result.clear();
+
+ aStraightLineIdenticalEndPoints.adaptiveSubdivideByAngle(result, fBound, true);
+ aPlotter.plot(result,
+ "straight line 2"); result.clear();
+
+ aStraightLineIdenticalEndPoints2.adaptiveSubdivideByAngle(result, fBound, true);
+ aPlotter.plot(result,
+ "straight line 3"); result.clear();
+
+ aCrossing.adaptiveSubdivideByAngle(result, fBound, true);
+ aPlotter.plot(result,
+ "straight line 4"); result.clear();
+
+ aCusp.adaptiveSubdivideByAngle(result, fBound, true);
+ aPlotter.plot(result,
+ "straight line 5"); result.clear();
+
+ CPPUNIT_ASSERT_MESSAGE("adaptiveByAngle", true );
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dbeziertools);
+ CPPUNIT_TEST(adaptiveByDistance); // TODO: add tests for quadratic bezier (subdivide and degree reduction)
+ CPPUNIT_TEST(adaptiveByAngle);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dcubicbezier
+
+
+class b2dcubicbezier : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ void EmptyMethod()
+ {
+ // this is demonstration code
+ // CPPUNIT_ASSERT_MESSAGE("a message", 1 == 1);
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dcubicbezier);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dcubicbezier
+
+
+class b2dhommatrix : public CppUnit::TestFixture
+{
+private:
+ B2DHomMatrix maIdentity;
+ B2DHomMatrix maScale;
+ B2DHomMatrix maTranslate;
+ B2DHomMatrix maShear;
+ B2DHomMatrix maAffine;
+ B2DHomMatrix maPerspective;
+
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ // setup some test matrices
+ maIdentity.identity(); // force compact layout
+ maIdentity.set(0,0, 1.0);
+ maIdentity.set(0,1, 0.0);
+ maIdentity.set(0,2, 0.0);
+ maIdentity.set(1,0, 0.0);
+ maIdentity.set(1,1, 1.0);
+ maIdentity.set(1,2, 0.0);
+
+ maScale.identity(); // force compact layout
+ maScale.set(0,0, 2.0);
+ maScale.set(1,1, 20.0);
+
+ maTranslate.identity(); // force compact layout
+ maTranslate.set(0,2, 20.0);
+ maTranslate.set(1,2, 2.0);
+
+ maShear.identity(); // force compact layout
+ maShear.set(0,1, 3.0);
+ maShear.set(1,0, 7.0);
+ maShear.set(1,1, 22.0);
+
+ maAffine.identity(); // force compact layout
+ maAffine.set(0,0, 1.0);
+ maAffine.set(0,1, 2.0);
+ maAffine.set(0,2, 3.0);
+ maAffine.set(1,0, 4.0);
+ maAffine.set(1,1, 5.0);
+ maAffine.set(1,2, 6.0);
+
+ maPerspective.set(0,0, 1.0);
+ maPerspective.set(0,1, 2.0);
+ maPerspective.set(0,2, 3.0);
+ maPerspective.set(1,0, 4.0);
+ maPerspective.set(1,1, 5.0);
+ maPerspective.set(1,2, 6.0);
+ maPerspective.set(2,0, 7.0);
+ maPerspective.set(2,1, 8.0);
+ maPerspective.set(2,2, 9.0);
+ }
+
+ void tearDown()
+ {
+ }
+
+ void equal()
+ {
+ B2DHomMatrix aIdentity;
+ B2DHomMatrix aScale;
+ B2DHomMatrix aTranslate;
+ B2DHomMatrix aShear;
+ B2DHomMatrix aAffine;
+ B2DHomMatrix aPerspective;
+
+ // setup some test matrices
+ aIdentity.identity(); // force compact layout
+ aIdentity.set(0,0, 1.0);
+ aIdentity.set(0,1, 0.0);
+ aIdentity.set(0,2, 0.0);
+ aIdentity.set(1,0, 0.0);
+ aIdentity.set(1,1, 1.0);
+ aIdentity.set(1,2, 0.0);
+
+ aScale.identity(); // force compact layout
+ aScale.set(0,0, 2.0);
+ aScale.set(1,1, 20.0);
+
+ aTranslate.identity(); // force compact layout
+ aTranslate.set(0,2, 20.0);
+ aTranslate.set(1,2, 2.0);
+
+ aShear.identity(); // force compact layout
+ aShear.set(0,1, 3.0);
+ aShear.set(1,0, 7.0);
+ aShear.set(1,1, 22.0);
+
+ aAffine.identity(); // force compact layout
+ aAffine.set(0,0, 1.0);
+ aAffine.set(0,1, 2.0);
+ aAffine.set(0,2, 3.0);
+ aAffine.set(1,0, 4.0);
+ aAffine.set(1,1, 5.0);
+ aAffine.set(1,2, 6.0);
+
+ aPerspective.set(0,0, 1.0);
+ aPerspective.set(0,1, 2.0);
+ aPerspective.set(0,2, 3.0);
+ aPerspective.set(1,0, 4.0);
+ aPerspective.set(1,1, 5.0);
+ aPerspective.set(1,2, 6.0);
+ aPerspective.set(2,0, 7.0);
+ aPerspective.set(2,1, 8.0);
+ aPerspective.set(2,2, 9.0);
+
+ CPPUNIT_ASSERT_MESSAGE("operator==: identity matrix", aIdentity == maIdentity);
+ CPPUNIT_ASSERT_MESSAGE("operator==: scale matrix", aScale == maScale);
+ CPPUNIT_ASSERT_MESSAGE("operator==: translate matrix", aTranslate == maTranslate);
+ CPPUNIT_ASSERT_MESSAGE("operator==: shear matrix", aShear == maShear);
+ CPPUNIT_ASSERT_MESSAGE("operator==: affine matrix", aAffine == maAffine);
+ CPPUNIT_ASSERT_MESSAGE("operator==: perspective matrix", aPerspective == maPerspective);
+ }
+
+ void identity()
+ {
+ B2DHomMatrix ident;
+
+ CPPUNIT_ASSERT_MESSAGE("identity", maIdentity == ident);
+ }
+
+ void scale()
+ {
+ B2DHomMatrix mat;
+ mat.scale(2.0,20.0);
+ CPPUNIT_ASSERT_MESSAGE("scale", maScale == mat);
+ }
+
+ void translate()
+ {
+ B2DHomMatrix mat;
+ mat.translate(20.0,2.0);
+ CPPUNIT_ASSERT_MESSAGE("translate", maTranslate == mat);
+ }
+
+ void shear()
+ {
+ B2DHomMatrix mat;
+ mat.shearX(3.0);
+ mat.shearY(7.0);
+ CPPUNIT_ASSERT_MESSAGE("translate", maShear == mat);
+ }
+
+ void multiply()
+ {
+ B2DHomMatrix affineAffineProd;
+
+ affineAffineProd.set(0,0, 9);
+ affineAffineProd.set(0,1, 12);
+ affineAffineProd.set(0,2, 18);
+ affineAffineProd.set(1,0, 24);
+ affineAffineProd.set(1,1, 33);
+ affineAffineProd.set(1,2, 48);
+
+ B2DHomMatrix affinePerspectiveProd;
+
+ affinePerspectiveProd.set(0,0, 30);
+ affinePerspectiveProd.set(0,1, 36);
+ affinePerspectiveProd.set(0,2, 42);
+ affinePerspectiveProd.set(1,0, 66);
+ affinePerspectiveProd.set(1,1, 81);
+ affinePerspectiveProd.set(1,2, 96);
+ affinePerspectiveProd.set(2,0, 7);
+ affinePerspectiveProd.set(2,1, 8);
+ affinePerspectiveProd.set(2,2, 9);
+
+ B2DHomMatrix perspectiveAffineProd;
+
+ perspectiveAffineProd.set(0,0, 9);
+ perspectiveAffineProd.set(0,1, 12);
+ perspectiveAffineProd.set(0,2, 18);
+ perspectiveAffineProd.set(1,0, 24);
+ perspectiveAffineProd.set(1,1, 33);
+ perspectiveAffineProd.set(1,2, 48);
+ perspectiveAffineProd.set(2,0, 39);
+ perspectiveAffineProd.set(2,1, 54);
+ perspectiveAffineProd.set(2,2, 78);
+
+ B2DHomMatrix perspectivePerspectiveProd;
+
+ perspectivePerspectiveProd.set(0,0, 30);
+ perspectivePerspectiveProd.set(0,1, 36);
+ perspectivePerspectiveProd.set(0,2, 42);
+ perspectivePerspectiveProd.set(1,0, 66);
+ perspectivePerspectiveProd.set(1,1, 81);
+ perspectivePerspectiveProd.set(1,2, 96);
+ perspectivePerspectiveProd.set(2,0, 102);
+ perspectivePerspectiveProd.set(2,1, 126);
+ perspectivePerspectiveProd.set(2,2, 150);
+
+ B2DHomMatrix temp;
+
+ temp = maAffine;
+ temp*=maAffine;
+ CPPUNIT_ASSERT_MESSAGE("multiply: both compact", temp == affineAffineProd);
+
+ temp = maPerspective;
+ temp*=maAffine;
+ CPPUNIT_ASSERT_MESSAGE("multiply: first compact", temp == affinePerspectiveProd);
+
+ temp = maAffine;
+ temp*=maPerspective;
+ CPPUNIT_ASSERT_MESSAGE("multiply: second compact", temp == perspectiveAffineProd);
+
+ temp = maPerspective;
+ temp*=maPerspective;
+ CPPUNIT_ASSERT_MESSAGE("multiply: none compact", temp == perspectivePerspectiveProd);
+ }
+
+ void impFillMatrix(B2DHomMatrix& rSource, double fScaleX, double fScaleY, double fShearX, double fRotate)
+ {
+ // fill rSource with a linear combination of scale, shear and rotate
+ rSource.identity();
+ rSource.scale(fScaleX, fScaleY);
+ rSource.shearX(fShearX);
+ rSource.rotate(fRotate);
+ }
+
+ bool impDecomposeComposeTest(double fScaleX, double fScaleY, double fShearX, double fRotate)
+ {
+ // linear combine matrix with given values
+ B2DHomMatrix aSource;
+ impFillMatrix(aSource, fScaleX, fScaleY, fShearX, fRotate);
+
+ // decompose that matrix
+ B2DTuple aDScale;
+ B2DTuple aDTrans;
+ double fDRot;
+ double fDShX;
+ bool bWorked = aSource.decompose(aDScale, aDTrans, fDRot, fDShX);
+
+ // linear combine another matrix with decomposition results
+ B2DHomMatrix aRecombined;
+ impFillMatrix(aRecombined, aDScale.getX(), aDScale.getY(), fDShX, fDRot);
+
+ // if decomposition worked, matrices need to be the same
+ return bWorked && aSource == aRecombined;
+ }
+
+ void decompose()
+ {
+ // test matrix decompositions. Each matrix decomposed and rebuilt
+ // using the decompose result should be the same as before. Test
+ // with all ranges of values. Translations are not tested since these
+ // are just the two rightmost values and uncritical
+ static double fSX(10.0);
+ static double fSY(12.0);
+ static double fR(45.0 * F_PI180);
+ static double fS(15.0 * F_PI180);
+
+ // check all possible scaling combinations
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test A1", impDecomposeComposeTest(fSX, fSY, 0.0, 0.0));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test A2", impDecomposeComposeTest(-fSX, fSY, 0.0, 0.0));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test A3", impDecomposeComposeTest(fSX, -fSY, 0.0, 0.0));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test A4", impDecomposeComposeTest(-fSX, -fSY, 0.0, 0.0));
+
+ // check all possible scaling combinations with positive rotation
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test B1", impDecomposeComposeTest(fSX, fSY, 0.0, fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test B2", impDecomposeComposeTest(-fSX, fSY, 0.0, fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test B3", impDecomposeComposeTest(fSX, -fSY, 0.0, fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test B4", impDecomposeComposeTest(-fSX, -fSY, 0.0, fR));
+
+ // check all possible scaling combinations with negative rotation
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test C1", impDecomposeComposeTest(fSX, fSY, 0.0, -fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test C2", impDecomposeComposeTest(-fSX, fSY, 0.0, -fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test C3", impDecomposeComposeTest(fSX, -fSY, 0.0, -fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test C4", impDecomposeComposeTest(-fSX, -fSY, 0.0, -fR));
+
+ // check all possible scaling combinations with positive shear
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test D1", impDecomposeComposeTest(fSX, fSY, tan(fS), 0.0));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test D2", impDecomposeComposeTest(-fSX, fSY, tan(fS), 0.0));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test D3", impDecomposeComposeTest(fSX, -fSY, tan(fS), 0.0));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test D4", impDecomposeComposeTest(-fSX, -fSY, tan(fS), 0.0));
+
+ // check all possible scaling combinations with negative shear
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test E1", impDecomposeComposeTest(fSX, fSY, tan(-fS), 0.0));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test E2", impDecomposeComposeTest(-fSX, fSY, tan(-fS), 0.0));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test E3", impDecomposeComposeTest(fSX, -fSY, tan(-fS), 0.0));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test E4", impDecomposeComposeTest(-fSX, -fSY, tan(-fS), 0.0));
+
+ // check all possible scaling combinations with positive rotate and positive shear
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test F1", impDecomposeComposeTest(fSX, fSY, tan(fS), fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test F2", impDecomposeComposeTest(-fSX, fSY, tan(fS), fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test F3", impDecomposeComposeTest(fSX, -fSY, tan(fS), fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test F4", impDecomposeComposeTest(-fSX, -fSY, tan(fS), fR));
+
+ // check all possible scaling combinations with negative rotate and positive shear
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test G1", impDecomposeComposeTest(fSX, fSY, tan(fS), -fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test G2", impDecomposeComposeTest(-fSX, fSY, tan(fS), -fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test G3", impDecomposeComposeTest(fSX, -fSY, tan(fS), -fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test G4", impDecomposeComposeTest(-fSX, -fSY, tan(fS), -fR));
+
+ // check all possible scaling combinations with positive rotate and negative shear
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test H1", impDecomposeComposeTest(fSX, fSY, tan(-fS), fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test H2", impDecomposeComposeTest(-fSX, fSY, tan(-fS), fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test H3", impDecomposeComposeTest(fSX, -fSY, tan(-fS), fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test H4", impDecomposeComposeTest(-fSX, -fSY, tan(-fS), fR));
+
+ // check all possible scaling combinations with negative rotate and negative shear
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test I1", impDecomposeComposeTest(fSX, fSY, tan(-fS), -fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test I2", impDecomposeComposeTest(-fSX, fSY, tan(-fS), -fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test I3", impDecomposeComposeTest(fSX, -fSY, tan(-fS), -fR));
+ CPPUNIT_ASSERT_MESSAGE("decompose: error test I4", impDecomposeComposeTest(-fSX, -fSY, tan(-fS), -fR));
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dhommatrix);
+ CPPUNIT_TEST(equal);
+ CPPUNIT_TEST(identity);
+ CPPUNIT_TEST(scale);
+ CPPUNIT_TEST(translate);
+ CPPUNIT_TEST(shear);
+ CPPUNIT_TEST(multiply);
+ CPPUNIT_TEST(decompose);
+ CPPUNIT_TEST_SUITE_END();
+
+}; // class b2dhommatrix
+
+
+class b2dhompoint : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ void EmptyMethod()
+ {
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dhompoint);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dhompoint
+
+
+class b2dpoint : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ // this is only demonstration code
+ void EmptyMethod()
+ {
+ // CPPUNIT_ASSERT_MESSAGE("a message", 1 == 1);
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dpoint);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dpoint
+
+
+class b2dpolygon : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ void testBasics()
+ {
+ B2DPolygon aPoly;
+
+ aPoly.appendBezierSegment(B2DPoint(1,1),B2DPoint(2,2),B2DPoint(3,3));
+
+ CPPUNIT_ASSERT_MESSAGE("#1 first polygon point wrong",
+ aPoly.getB2DPoint(0) == B2DPoint(3,3));
+ CPPUNIT_ASSERT_MESSAGE("#1 first control point wrong",
+ aPoly.getPrevControlPoint(0) == B2DPoint(2,2));
+ CPPUNIT_ASSERT_MESSAGE("#1 second control point wrong",
+ aPoly.getNextControlPoint(0) == B2DPoint(3,3));
+ CPPUNIT_ASSERT_MESSAGE("next control point not used",
+ aPoly.isNextControlPointUsed(0) == false);
+
+ aPoly.setNextControlPoint(0,B2DPoint(4,4));
+ CPPUNIT_ASSERT_MESSAGE("#1.1 second control point wrong",
+ aPoly.getNextControlPoint(0) == B2DPoint(4,4));
+ CPPUNIT_ASSERT_MESSAGE("next control point used",
+ aPoly.isNextControlPointUsed(0) == true);
+ CPPUNIT_ASSERT_MESSAGE("areControlPointsUsed() wrong",
+ aPoly.areControlPointsUsed() == true);
+ CPPUNIT_ASSERT_MESSAGE("getContinuityInPoint() wrong",
+ aPoly.getContinuityInPoint(0) == CONTINUITY_C2);
+
+ aPoly.resetControlPoints();
+ CPPUNIT_ASSERT_MESSAGE("resetControlPoints() did not clear",
+ aPoly.getB2DPoint(0) == B2DPoint(3,3));
+ CPPUNIT_ASSERT_MESSAGE("resetControlPoints() did not clear",
+ aPoly.getPrevControlPoint(0) == B2DPoint(3,3));
+ CPPUNIT_ASSERT_MESSAGE("resetControlPoints() did not clear",
+ aPoly.getNextControlPoint(0) == B2DPoint(3,3));
+ CPPUNIT_ASSERT_MESSAGE("areControlPointsUsed() wrong #2",
+ aPoly.areControlPointsUsed() == false);
+
+ aPoly.clear();
+ aPoly.append(B2DPoint(0,0));
+ aPoly.appendBezierSegment(B2DPoint(1,1),B2DPoint(2,2),B2DPoint(3,3));
+
+ CPPUNIT_ASSERT_MESSAGE("#2 first polygon point wrong",
+ aPoly.getB2DPoint(0) == B2DPoint(0,0));
+ CPPUNIT_ASSERT_MESSAGE("#2 first control point wrong",
+ aPoly.getPrevControlPoint(0) == B2DPoint(0,0));
+ CPPUNIT_ASSERT_MESSAGE("#2 second control point wrong",
+ aPoly.getNextControlPoint(0) == B2DPoint(1,1));
+ CPPUNIT_ASSERT_MESSAGE("#2 third control point wrong",
+ aPoly.getPrevControlPoint(1) == B2DPoint(2,2));
+ CPPUNIT_ASSERT_MESSAGE("#2 fourth control point wrong",
+ aPoly.getNextControlPoint(1) == B2DPoint(3,3));
+ CPPUNIT_ASSERT_MESSAGE("#2 second polygon point wrong",
+ aPoly.getB2DPoint(1) == B2DPoint(3,3));
+ }
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dpolygon);
+ CPPUNIT_TEST(testBasics);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dpolygon
+
+
+class b2dpolygontools : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ // this is only demonstration code
+ void testIsRectangle()
+ {
+ B2DPolygon aRect1(
+ tools::createPolygonFromRect(
+ B2DRange(0,0,1,1) ) );
+
+ B2DPolygon aRect2;
+ aRect2.append( B2DPoint(0,0) );
+ aRect2.append( B2DPoint(1,0) );
+ aRect2.append( B2DPoint(1,.5));
+ aRect2.append( B2DPoint(1,1) );
+ aRect2.append( B2DPoint(0,1) );
+ aRect2.setClosed(true);
+
+ B2DPolygon aNonRect1;
+ aNonRect1.append( B2DPoint(0,0) );
+ aNonRect1.append( B2DPoint(1,0) );
+ aNonRect1.append( B2DPoint(1,1) );
+ aNonRect1.append( B2DPoint(0.5,1) );
+ aNonRect1.append( B2DPoint(0.5,0) );
+ aNonRect1.setClosed(true);
+
+ B2DPolygon aNonRect2;
+ aNonRect2.append( B2DPoint(0,0) );
+ aNonRect2.append( B2DPoint(1,1) );
+ aNonRect2.append( B2DPoint(1,0) );
+ aNonRect2.append( B2DPoint(0,1) );
+ aNonRect2.setClosed(true);
+
+ B2DPolygon aNonRect3;
+ aNonRect3.append( B2DPoint(0,0) );
+ aNonRect3.append( B2DPoint(1,0) );
+ aNonRect3.append( B2DPoint(1,1) );
+ aNonRect3.setClosed(true);
+
+ B2DPolygon aNonRect4;
+ aNonRect4.append( B2DPoint(0,0) );
+ aNonRect4.append( B2DPoint(1,0) );
+ aNonRect4.append( B2DPoint(1,1) );
+ aNonRect4.append( B2DPoint(0,1) );
+
+ CPPUNIT_ASSERT_MESSAGE("checking rectangle-ness of rectangle 1",
+ tools::isRectangle( aRect1 ));
+ CPPUNIT_ASSERT_MESSAGE("checking rectangle-ness of rectangle 2",
+ tools::isRectangle( aRect2 ));
+ CPPUNIT_ASSERT_MESSAGE("checking non-rectangle-ness of polygon 1",
+ !tools::isRectangle( aNonRect1 ));
+ CPPUNIT_ASSERT_MESSAGE("checking non-rectangle-ness of polygon 2",
+ !tools::isRectangle( aNonRect2 ));
+ CPPUNIT_ASSERT_MESSAGE("checking non-rectangle-ness of polygon 3",
+ !tools::isRectangle( aNonRect3 ));
+ CPPUNIT_ASSERT_MESSAGE("checking non-rectangle-ness of polygon 4",
+ !tools::isRectangle( aNonRect4 ));
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dpolygontools);
+ CPPUNIT_TEST(testIsRectangle);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dpolygontools
+
+
+class b2dpolypolygon : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ void EmptyMethod()
+ {
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dpolypolygon);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dpolypolygon
+
+
+class b2dquadraticbezier : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ // this is only demonstration code
+ void EmptyMethod()
+ {
+ // CPPUNIT_ASSERT_MESSAGE("a message", 1 == 1);
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dquadraticbezier);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dquadraticbezier
+
+
+class b2drange : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ void EmptyMethod()
+ {
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2drange);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2drange
+
+
+class b2dtuple : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ // this is only demonstration code
+ void EmptyMethod()
+ {
+ // CPPUNIT_ASSERT_MESSAGE("a message", 1 == 1);
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dtuple);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dtuple
+
+
+class b2dvector : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ void EmptyMethod()
+ {
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b2dvector);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dvector
+
+class bcolor : public CppUnit::TestFixture
+{
+ BColor maWhite;
+ BColor maBlack;
+ BColor maRed;
+ BColor maGreen;
+ BColor maBlue;
+ BColor maYellow;
+ BColor maMagenta;
+ BColor maCyan;
+
+public:
+ bcolor() :
+ maWhite(1,1,1),
+ maBlack(0,0,0),
+ maRed(1,0,0),
+ maGreen(0,1,0),
+ maBlue(0,0,1),
+ maYellow(1,1,0),
+ maMagenta(1,0,1),
+ maCyan(0,1,1)
+ {}
+
+
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ void hslTest()
+ {
+ CPPUNIT_ASSERT_MESSAGE("white",
+ tools::rgb2hsl(maWhite) == BColor(0,0,1));
+ CPPUNIT_ASSERT_MESSAGE("black",
+ tools::rgb2hsl(maBlack) == BColor(0,0,0));
+ CPPUNIT_ASSERT_MESSAGE("red",
+ tools::rgb2hsl(maRed) == BColor(0,1,0.5));
+ CPPUNIT_ASSERT_MESSAGE("green",
+ tools::rgb2hsl(maGreen) == BColor(120,1,0.5));
+ CPPUNIT_ASSERT_MESSAGE("blue",
+ tools::rgb2hsl(maBlue) == BColor(240,1,0.5));
+ CPPUNIT_ASSERT_MESSAGE("yellow",
+ tools::rgb2hsl(maYellow) == BColor(60,1,0.5));
+ CPPUNIT_ASSERT_MESSAGE("magenta",
+ tools::rgb2hsl(maMagenta) == BColor(300,1,0.5));
+ CPPUNIT_ASSERT_MESSAGE("cyan",
+ tools::rgb2hsl(maCyan) == BColor(180,1,0.5));
+
+ CPPUNIT_ASSERT_MESSAGE("roundtrip white",
+ tools::hsl2rgb(tools::rgb2hsl(maWhite)) == maWhite);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip black",
+ tools::hsl2rgb(tools::rgb2hsl(maBlack)) == maBlack);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip red",
+ tools::hsl2rgb(tools::rgb2hsl(maRed)) == maRed);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip green",
+ tools::hsl2rgb(tools::rgb2hsl(maGreen)) == maGreen);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip blue",
+ tools::hsl2rgb(tools::rgb2hsl(maBlue)) == maBlue);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip yellow",
+ tools::hsl2rgb(tools::rgb2hsl(maYellow)) == maYellow);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip magenta",
+ tools::hsl2rgb(tools::rgb2hsl(maMagenta)) == maMagenta);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip cyan",
+ tools::hsl2rgb(tools::rgb2hsl(maCyan)) == maCyan);
+
+ CPPUNIT_ASSERT_MESSAGE("grey10",
+ tools::rgb2hsl(maWhite*.1) == BColor(0,0,.1));
+ CPPUNIT_ASSERT_MESSAGE("grey90",
+ tools::rgb2hsl(maWhite*.9) == BColor(0,0,.9));
+ CPPUNIT_ASSERT_MESSAGE("red/2",
+ tools::rgb2hsl(maRed*.5) == BColor(0,1,0.25));
+ CPPUNIT_ASSERT_MESSAGE("green/2",
+ tools::rgb2hsl(maGreen*.5) == BColor(120,1,0.25));
+ CPPUNIT_ASSERT_MESSAGE("blue/2",
+ tools::rgb2hsl(maBlue*.5) == BColor(240,1,0.25));
+ CPPUNIT_ASSERT_MESSAGE("yellow/2",
+ tools::rgb2hsl(maYellow*.5) == BColor(60,1,0.25));
+ CPPUNIT_ASSERT_MESSAGE("magenta/2",
+ tools::rgb2hsl(maMagenta*.5) == BColor(300,1,0.25));
+ CPPUNIT_ASSERT_MESSAGE("cyan/2",
+ tools::rgb2hsl(maCyan*.5) == BColor(180,1,0.25));
+
+ CPPUNIT_ASSERT_MESSAGE("pastel",
+ tools::rgb2hsl(BColor(.75,.25,.25)) == BColor(0,.5,.5));
+ }
+
+ // insert your test code here.
+ void hsvTest()
+ {
+ CPPUNIT_ASSERT_MESSAGE("white",
+ tools::rgb2hsv(maWhite) == BColor(0,0,1));
+ CPPUNIT_ASSERT_MESSAGE("black",
+ tools::rgb2hsv(maBlack) == BColor(0,0,0));
+ CPPUNIT_ASSERT_MESSAGE("red",
+ tools::rgb2hsv(maRed) == BColor(0,1,1));
+ CPPUNIT_ASSERT_MESSAGE("green",
+ tools::rgb2hsv(maGreen) == BColor(120,1,1));
+ CPPUNIT_ASSERT_MESSAGE("blue",
+ tools::rgb2hsv(maBlue) == BColor(240,1,1));
+ CPPUNIT_ASSERT_MESSAGE("yellow",
+ tools::rgb2hsv(maYellow) == BColor(60,1,1));
+ CPPUNIT_ASSERT_MESSAGE("magenta",
+ tools::rgb2hsv(maMagenta) == BColor(300,1,1));
+ CPPUNIT_ASSERT_MESSAGE("cyan",
+ tools::rgb2hsv(maCyan) == BColor(180,1,1));
+
+ CPPUNIT_ASSERT_MESSAGE("roundtrip white",
+ tools::hsv2rgb(tools::rgb2hsv(maWhite)) == maWhite);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip black",
+ tools::hsv2rgb(tools::rgb2hsv(maBlack)) == maBlack);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip red",
+ tools::hsv2rgb(tools::rgb2hsv(maRed)) == maRed);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip green",
+ tools::hsv2rgb(tools::rgb2hsv(maGreen)) == maGreen);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip blue",
+ tools::hsv2rgb(tools::rgb2hsv(maBlue)) == maBlue);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip yellow",
+ tools::hsv2rgb(tools::rgb2hsv(maYellow)) == maYellow);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip magenta",
+ tools::hsv2rgb(tools::rgb2hsv(maMagenta)) == maMagenta);
+ CPPUNIT_ASSERT_MESSAGE("roundtrip cyan",
+ tools::hsv2rgb(tools::rgb2hsv(maCyan)) == maCyan);
+
+ CPPUNIT_ASSERT_MESSAGE("grey10",
+ tools::rgb2hsv(maWhite*.1) == BColor(0,0,.1));
+ CPPUNIT_ASSERT_MESSAGE("grey90",
+ tools::rgb2hsv(maWhite*.9) == BColor(0,0,.9));
+ CPPUNIT_ASSERT_MESSAGE("red/2",
+ tools::rgb2hsv(maRed*.5) == BColor(0,1,0.5));
+ CPPUNIT_ASSERT_MESSAGE("green/2",
+ tools::rgb2hsv(maGreen*.5) == BColor(120,1,0.5));
+ CPPUNIT_ASSERT_MESSAGE("blue/2",
+ tools::rgb2hsv(maBlue*.5) == BColor(240,1,0.5));
+ CPPUNIT_ASSERT_MESSAGE("yellow/2",
+ tools::rgb2hsv(maYellow*.5) == BColor(60,1,0.5));
+ CPPUNIT_ASSERT_MESSAGE("magenta/2",
+ tools::rgb2hsv(maMagenta*.5) == BColor(300,1,0.5));
+ CPPUNIT_ASSERT_MESSAGE("cyan/2",
+ tools::rgb2hsv(maCyan*.5) == BColor(180,1,0.5));
+
+ CPPUNIT_ASSERT_MESSAGE("pastel",
+ tools::rgb2hsv(BColor(.5,.25,.25)) == BColor(0,.5,.5));
+ }
+
+ void ciexyzTest()
+ {
+ tools::rgb2ciexyz(maWhite);
+ tools::rgb2ciexyz(maBlack);
+ tools::rgb2ciexyz(maRed);
+ tools::rgb2ciexyz(maGreen);
+ tools::rgb2ciexyz(maBlue);
+ tools::rgb2ciexyz(maYellow);
+ tools::rgb2ciexyz(maMagenta);
+ tools::rgb2ciexyz(maCyan);
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(bcolor);
+ CPPUNIT_TEST(hslTest);
+ CPPUNIT_TEST(hsvTest);
+ CPPUNIT_TEST(ciexyzTest);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b2dvector
+
+// -----------------------------------------------------------------------------
+//CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dmultirange, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dsvgdimpex, "basegfx2d");
+//CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dbeziertools, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dcubicbezier, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dhommatrix, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dhompoint, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dpoint, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dpolygon, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dpolygontools, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dpolypolygon, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dquadraticbezier, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2drange, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dtuple, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::b2dvector, "basegfx2d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx2d::bcolor, "basegfx2d");
+} // namespace basegfx2d
+
+
+// -----------------------------------------------------------------------------
+
+// this macro creates an empty function, which will called by the RegisterAllFunctions()
+// to let the user the possibility to also register some functions by hand.
+// NOADDITIONAL;
+
diff --git a/basegfx/test/basegfx3d.cxx b/basegfx/test/basegfx3d.cxx
new file mode 100644
index 000000000000..fc59ffbced4e
--- /dev/null
+++ b/basegfx/test/basegfx3d.cxx
@@ -0,0 +1,229 @@
+/*************************************************************************
+ *
+ * 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: basegfx3d.cxx,v $
+ * $Revision: 1.3 $
+ *
+ * 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"
+// autogenerated file with codegen.pl
+
+#include <cppunit/simpleheader.hxx>
+
+namespace basegfx3d
+{
+
+class b3dhommatrix : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ // this is only demonstration code
+ void EmptyMethod()
+ {
+ // CPPUNIT_ASSERT_MESSAGE("a message", 1 == 1);
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b3dhommatrix);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b3dhommatrix
+
+
+class b3dhompoint : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ void EmptyMethod()
+ {
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b3dhompoint);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b3dhompoint
+
+
+class b3dpoint : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ // this is only demonstration code
+ void EmptyMethod()
+ {
+ // CPPUNIT_ASSERT_MESSAGE("a message", 1 == 1);
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b3dpoint);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b3dpoint
+
+
+class b3drange : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ void EmptyMethod()
+ {
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b3drange);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b3drange
+
+
+class b3dtuple : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ // this is only demonstration code
+ void EmptyMethod()
+ {
+ // CPPUNIT_ASSERT_MESSAGE("a message", 1 == 1);
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b3dtuple);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b3dtuple
+
+
+class b3dvector : public CppUnit::TestFixture
+{
+public:
+ // initialise your test code values here.
+ void setUp()
+ {
+ }
+
+ void tearDown()
+ {
+ }
+
+ // insert your test code here.
+ void EmptyMethod()
+ {
+ CPPUNIT_ASSERT_STUB();
+ }
+
+ // Change the following lines only, if you add, remove or rename
+ // member functions of the current class,
+ // because these macros are need by auto register mechanism.
+
+ CPPUNIT_TEST_SUITE(b3dvector);
+ CPPUNIT_TEST(EmptyMethod);
+ CPPUNIT_TEST_SUITE_END();
+}; // class b3dvector
+
+// -----------------------------------------------------------------------------
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx3d::b3dhommatrix, "basegfx3d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx3d::b3dhompoint, "basegfx3d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx3d::b3dpoint, "basegfx3d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx3d::b3drange, "basegfx3d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx3d::b3dtuple, "basegfx3d");
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(basegfx3d::b3dvector, "basegfx3d");
+} // namespace basegfx3d
+
+
+// -----------------------------------------------------------------------------
+
+// this macro creates an empty function, which will called by the RegisterAllFunctions()
+// to let the user the possibility to also register some functions by hand.
+// NOADDITIONAL;
+
diff --git a/basegfx/test/export.map b/basegfx/test/export.map
new file mode 100644
index 000000000000..37f3bf2c55d2
--- /dev/null
+++ b/basegfx/test/export.map
@@ -0,0 +1,38 @@
+#*************************************************************************
+#
+# 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: export.map,v $
+#
+# $Revision: 1.4 $
+#
+# 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.
+#
+#*************************************************************************
+
+UDK_3.1 {
+ global:
+ registerAllTestFunction;
+
+ local:
+ *;
+};
diff --git a/basegfx/test/makefile.mk b/basegfx/test/makefile.mk
new file mode 100644
index 000000000000..1bd4d59b98d5
--- /dev/null
+++ b/basegfx/test/makefile.mk
@@ -0,0 +1,96 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.7 $
+#
+# 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=tests
+
+ENABLE_EXCEPTIONS=TRUE
+
+# --- Settings -----------------------------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Common ----------------------------------------------------------
+
+SHL1OBJS= \
+ $(SLO)$/basegfx1d.obj \
+ $(SLO)$/basegfx2d.obj \
+ $(SLO)$/basegfx3d.obj \
+ $(SLO)$/testtools.obj
+
+# linking statically against basegfx parts
+SHL1LIBS=\
+ $(SLB)$/curve.lib \
+ $(SLB)$/matrix.lib \
+ $(SLB)$/numeric.lib \
+ $(SLB)$/point.lib \
+ $(SLB)$/polygon.lib \
+ $(SLB)$/range.lib \
+ $(SLB)$/tuple.lib \
+ $(SLB)$/tools.lib \
+ $(SLB)$/color.lib \
+ $(SLB)$/vector.lib
+
+SHL1TARGET= basegfx_tests
+SHL1STDLIBS= \
+ $(SALLIB) \
+ $(CPPUHELPERLIB) \
+ $(CPPULIB) \
+ $(CPPUNITLIB)
+
+SHL1IMPLIB= i$(SHL1TARGET)
+
+DEF1NAME =$(SHL1TARGET)
+SHL1VERSIONMAP = export.map
+
+# END ------------------------------------------------------------------
+
+#------------------------------- All object files -------------------------------
+# do this here, so we get right dependencies
+SLOFILES=$(SHL1OBJS)
+
+# --- Targets ------------------------------------------------------
+
+.INCLUDE : target.mk
+.INCLUDE : _cppunit.mk
+
+# --- Enable testshl2 execution in normal build ------------------------
+
+$(MISC)$/unittest_succeeded : $(SHL1TARGETN)
+ @echo ----------------------------------------------------------
+ @echo - start unit test on library $(SHL1TARGETN)
+ @echo ----------------------------------------------------------
+ $(TESTSHL2) -sf $(mktmp ) -forward $(BIN)$/ $(SHL1TARGETN)
+ $(TOUCH) $@
+
+ALLTAR : $(MISC)$/unittest_succeeded
diff --git a/basegfx/test/tests.txt b/basegfx/test/tests.txt
new file mode 100644
index 000000000000..fc2d33ab51d5
--- /dev/null
+++ b/basegfx/test/tests.txt
@@ -0,0 +1,20 @@
+# Jobfile
+basegfx1d.b1drange
+basegfx2d.b2dcubicbezier
+basegfx2d.b2dhommatrix
+basegfx2d.b2dhompoint
+basegfx2d.b2dpoint
+basegfx2d.b2dpolygon
+basegfx2d.b2dpolygontools
+basegfx2d.b2dpolypolygon
+basegfx2d.b2dquadraticbezier
+basegfx2d.b2drange
+basegfx2d.b2dtuple
+basegfx2d.b2dvector
+basegfx3d.b3dhommatrix
+basegfx3d.b3dhompoint
+basegfx3d.b3dhompoint
+basegfx3d.b3dpoint
+basegfx3d.b3drange
+basegfx3d.b3dtuple
+basegfx3d.b3dvector
diff --git a/basegfx/test/testtools.cxx b/basegfx/test/testtools.cxx
new file mode 100644
index 000000000000..5dfa37131445
--- /dev/null
+++ b/basegfx/test/testtools.cxx
@@ -0,0 +1,238 @@
+/*************************************************************************
+ *
+ * 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: testtools.cxx,v $
+ * $Revision: 1.3 $
+ *
+ * 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 "testtools.hxx"
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+#include <algorithm>
+
+
+namespace basegfx
+{
+ namespace testtools
+ {
+ Plotter::Plotter( ::std::ostream& rOutputStream ) :
+ mrOutputStream(rOutputStream),
+ maPoints(),
+ mbFirstElement( true )
+ {
+ // output gnuplot setup. We switch gnuplot to parametric
+ // mode, therefore every plot has at least _two_
+ // functions: one for the x and one for the y value, both
+ // depending on t.
+ mrOutputStream << "#!/usr/bin/gnuplot -persist" << ::std::endl
+ << "#" << ::std::endl
+ << "# automatically generated by basegfx::testtools::Plotter, don't change!" << ::std::endl
+ << "#" << ::std::endl
+ << "set parametric" << ::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 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
+
+ // 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(a,b,t) = a*(1-t) + b*t" << ::std::endl << ::std::endl
+ << "# end of setup" << ::std::endl << ::std::endl
+
+ // Start the actual plot line
+ << "plot [t=0:1] ";
+ }
+
+ namespace
+ {
+ class PointWriter
+ {
+ public:
+ PointWriter( ::std::ostream& rOutputStream ) :
+ mrOutputStream( rOutputStream )
+ {
+ }
+
+ void operator()( const B2DPoint& rPoint ) const
+ {
+ mrOutputStream << rPoint.getX() << "\t" << rPoint.getY() << ::std::endl;
+ mrOutputStream << "e" << ::std::endl;
+ }
+
+ private:
+ ::std::ostream& mrOutputStream;
+ };
+ }
+
+ Plotter::~Plotter()
+ {
+ // End the plot line
+ mrOutputStream << ::std::endl;
+
+ // write stored data points. Cannot write before, since
+ // this is an inline dataset, which must be after the plot <...>
+ // line
+ ::std::for_each( maPoints.begin(), maPoints.end(), PointWriter(mrOutputStream) );
+ }
+
+ void Plotter::plot( const B2DPolygon& rPoly )
+ {
+ const sal_uInt32 pointCount( rPoly.count() );
+
+ if( pointCount < 1 )
+ return;
+
+ if( pointCount == 1 )
+ {
+ plot( rPoly.getB2DPoint(0) );
+ return;
+ }
+
+ sal_uInt32 i;
+ for( i=0; i<pointCount-1; ++i )
+ {
+ if(rPoly.isNextControlPointUsed(i) || rPoly.isPrevControlPointUsed(i + 1))
+ {
+ const B2DCubicBezier aBezierPlot(
+ rPoly.getB2DPoint(i), rPoly.getNextControlPoint(i),
+ rPoly.getPrevControlPoint(i + 1), rPoly.getB2DPoint(i + 1));
+
+ plot(aBezierPlot);
+ }
+ else
+ {
+ plot( rPoly.getB2DPoint(i), rPoly.getB2DPoint(i+1) );
+ }
+ }
+ }
+
+ void Plotter::plot( const B2DPolyPolygon& rPolyPoly )
+ {
+ const sal_uInt32 nPolyCount( rPolyPoly.count() );
+
+ sal_uInt32 i;
+ for( i=0; i<nPolyCount; ++i )
+ {
+ plot( rPolyPoly.getB2DPolygon(i) );
+ }
+ }
+
+ void Plotter::plot( const B2DPoint& rPoint )
+ {
+ maPoints.push_back( rPoint );
+ writeSeparator();
+ mrOutputStream << "'-' using ($1):($2) title \"Point " << maPoints.size() << "\" with points";
+ }
+
+ void Plotter::plot( const B2DRange& rRect )
+ {
+ // TODO: do that also as a data file plot. maPoints must
+ // then become polymorph, but WTF.
+
+ // decompose into four lines
+ plot( B2DPoint(rRect.getMinX(),
+ rRect.getMinY()),
+ B2DPoint(rRect.getMaxX(),
+ rRect.getMinY()) );
+ plot( B2DPoint(rRect.getMaxX(),
+ rRect.getMinY()),
+ B2DPoint(rRect.getMaxX(),
+ rRect.getMaxY()) );
+ plot( B2DPoint(rRect.getMaxX(),
+ rRect.getMaxY()),
+ B2DPoint(rRect.getMinX(),
+ rRect.getMaxY()) );
+ plot( B2DPoint(rRect.getMinX(),
+ rRect.getMaxY()),
+ B2DPoint(rRect.getMinX(),
+ rRect.getMinY()) );
+ }
+
+ void Plotter::plot( const B2DPoint& rStartPoint, const B2DPoint& rEndPoint )
+ {
+ writeSeparator();
+ mrOutputStream << "line(" << rStartPoint.getX()
+ << "," << rEndPoint.getX()
+ << ",t), "
+ << "line(" << rStartPoint.getY()
+ << "," << rEndPoint.getY()
+ << ",t)";
+ }
+
+ void Plotter::plot( const B2DCubicBezier& rCurve )
+ {
+ writeSeparator();
+ mrOutputStream << "cubicBezier(" << rCurve.getStartPoint().getX()
+ << "," << rCurve.getControlPointA().getX()
+ << "," << rCurve.getControlPointB().getX()
+ << "," << rCurve.getEndPoint().getX()
+ << ",t), "
+ << "cubicBezier(" << rCurve.getStartPoint().getY()
+ << "," << rCurve.getControlPointA().getY()
+ << "," << rCurve.getControlPointB().getY()
+ << "," << rCurve.getEndPoint().getY()
+ << ",t)";
+ }
+
+ void Plotter::writeSeparator()
+ {
+ if( mbFirstElement )
+ {
+ mbFirstElement = false;
+ }
+ else
+ {
+ mrOutputStream << ", ";
+ }
+ }
+
+ }
+}
diff --git a/basegfx/test/testtools.hxx b/basegfx/test/testtools.hxx
new file mode 100644
index 000000000000..5dfff34cf8a9
--- /dev/null
+++ b/basegfx/test/testtools.hxx
@@ -0,0 +1,101 @@
+/*************************************************************************
+ *
+ * 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: testtools.hxx,v $
+ * $Revision: 1.3 $
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+#ifndef _BASEGFX_TESTTOOLS_HXX
+#define _BASEGFX_TESTTOOLS_HXX
+
+#include <basegfx/point/b2dpoint.hxx>
+
+#include <vector>
+#include <iostream>
+
+
+namespace basegfx
+{
+ class B2DCubicBezier;
+ class B2DPolygon;
+ class B2DPolyPolygon;
+ class B2DRange;
+
+ namespace testtools
+ {
+ class Plotter
+ {
+ public:
+ /** Create a plotter for the given output stream
+
+ This class can be used to generate gnuplot scripts for
+ a number of basegfx graphics primitives, useful for
+ debugging, regression-testing and comparing basegfx.
+ */
+ Plotter( ::std::ostream& rOutputStream );
+
+ /** Delete the plotter
+
+ This implicitely flushes all potential pending writes
+ to the output stream
+ */
+ ~Plotter();
+
+ /** Plot a 2d polygon into the current graph
+ */
+ void plot( const B2DPolygon& rPoly );
+
+ /** Plot a 2d polyPolygon into the current graph
+ */
+ void plot( const B2DPolyPolygon& rPolyPoly );
+
+ /** Plot a 2d point into the current graph
+ */
+ void plot( const B2DPoint& rPoint );
+
+ /** Plot a 2d rectangle into the current graph
+ */
+ void plot( const B2DRange& rRect );
+
+ /** Plot a 2d line into the current graph
+ */
+ void plot( const B2DPoint& rStartPoint, const B2DPoint& rEndPoint );
+
+ /** Plot a 2d cubic bezier curve into the current graph
+ */
+ void plot( const B2DCubicBezier& rCurve );
+
+ private:
+ void writeSeparator();
+
+ ::std::ostream& mrOutputStream;
+ ::std::vector< B2DPoint > maPoints;
+ bool mbFirstElement;
+ };
+ }
+}
+
+#endif /* _BASEGFX_TESTTOOLS_HXX */
diff --git a/basegfx/util/basegfx.flt b/basegfx/util/basegfx.flt
new file mode 100644
index 000000000000..57bb2e2ff324
--- /dev/null
+++ b/basegfx/util/basegfx.flt
@@ -0,0 +1,6 @@
+__CT
+__real
++getImplementation
+Impl
+IMP
+internal \ No newline at end of file
diff --git a/basegfx/util/makefile.mk b/basegfx/util/makefile.mk
new file mode 100644
index 000000000000..a595fdafa48a
--- /dev/null
+++ b/basegfx/util/makefile.mk
@@ -0,0 +1,89 @@
+#*************************************************************************
+#
+# 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: makefile.mk,v $
+#
+# $Revision: 1.15 $
+#
+# 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=basegfx
+
+# --- Settings ---------------------------------------------------
+
+.INCLUDE : settings.mk
+
+# --- Allgemein ---------------------------------------------------
+
+LIB1TARGET=$(SLB)$/basegfx.lib
+LIB1FILES=\
+ $(SLB)$/curve.lib \
+ $(SLB)$/matrix.lib \
+ $(SLB)$/numeric.lib \
+ $(SLB)$/point.lib \
+ $(SLB)$/polygon.lib \
+ $(SLB)$/range.lib \
+ $(SLB)$/tuple.lib \
+ $(SLB)$/tools.lib \
+ $(SLB)$/vector.lib \
+ $(SLB)$/color.lib \
+ $(SLB)$/pixel.lib \
+ $(SLB)$/raster.lib
+
+SHL1TARGET= basegfx$(DLLPOSTFIX)
+.IF "$(GUI)" == "OS2"
+SHL1TARGET= bgfx
+.ENDIF
+SHL1IMPLIB= ibasegfx
+
+SHL1STDLIBS=\
+ $(SALLIB) \
+ $(CPPUHELPERLIB) \
+ $(CPPULIB)
+
+SHL1DEF= $(MISC)$/$(SHL1TARGET).def
+SHL1LIBS= $(SLB)$/basegfx.lib
+
+DEF1NAME =$(SHL1TARGET)
+DEF1DEPN =$(MISC)$/$(SHL1TARGET).flt \
+ $(LIB1TARGET)
+
+DEF1DES =BaseGFX
+DEFLIB1NAME =basegfx
+
+LIB2TARGET=$(LB)$/$(TARGET)_s.lib
+LIB2ARCHIV=$(LB)$/lib$(TARGET)_s.a
+LIB2FILES=$(LIB1FILES)
+
+# --- Targets -----------------------------------------------------------
+
+.INCLUDE : target.mk
+
+$(MISC)$/$(SHL1TARGET).flt : makefile.mk
+ @$(TYPE) $(TARGET).flt > $@
+