summaryrefslogtreecommitdiffstats
path: root/drawinglayer/source/primitive3d
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drawinglayer/source/primitive3d/Tools.cxx67
-rw-r--r--drawinglayer/source/primitive3d/baseprimitive3d.cxx183
-rw-r--r--drawinglayer/source/primitive3d/groupprimitive3d.cxx61
-rw-r--r--drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx310
-rw-r--r--drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx51
-rw-r--r--drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx64
-rw-r--r--drawinglayer/source/primitive3d/polygonprimitive3d.cxx148
-rw-r--r--drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx769
-rw-r--r--drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx64
-rw-r--r--drawinglayer/source/primitive3d/sdrcubeprimitive3d.cxx199
-rw-r--r--drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx324
-rw-r--r--drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx982
-rw-r--r--drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx502
-rw-r--r--drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx360
-rw-r--r--drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx176
-rw-r--r--drawinglayer/source/primitive3d/sdrprimitive3d.cxx106
-rw-r--r--drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx205
-rw-r--r--drawinglayer/source/primitive3d/shadowprimitive3d.cxx63
-rw-r--r--drawinglayer/source/primitive3d/textureprimitive3d.cxx183
-rw-r--r--drawinglayer/source/primitive3d/transformprimitive3d.cxx61
20 files changed, 4878 insertions, 0 deletions
diff --git a/drawinglayer/source/primitive3d/Tools.cxx b/drawinglayer/source/primitive3d/Tools.cxx
new file mode 100644
index 000000000..c27896b04
--- /dev/null
+++ b/drawinglayer/source/primitive3d/Tools.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <drawinglayer/primitive3d/Tools.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+using namespace css;
+
+namespace drawinglayer::primitive3d
+{
+OUString idToString(sal_uInt32 nId)
+{
+ switch (nId)
+ {
+ case PRIMITIVE3D_ID_GROUPPRIMITIVE3D:
+ return "GROUPPRIMITIVE3D";
+ case PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D:
+ return "HATCHTEXTUREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_MODIFIEDCOLORPRIMITIVE3D:
+ return "MODIFIEDCOLORPRIMITIVE3D";
+ case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D:
+ return "POLYGONHAIRLINEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_POLYGONSTROKEPRIMITIVE3D:
+ return "POLYGONSTROKEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D:
+ return "POLYGONTUBEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D:
+ return "POLYPOLYGONMATERIALPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SDRCUBEPRIMITIVE3D:
+ return "SDRCUBEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D:
+ return "SDREXTRUDEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SDRLATHEPRIMITIVE3D:
+ return "SDRLATHEPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SDRPOLYPOLYGONPRIMITIVE3D:
+ return "SDRPOLYPOLYGONPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SDRSPHEREPRIMITIVE3D:
+ return "SDRSPHEREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_SHADOWPRIMITIVE3D:
+ return "SHADOWPRIMITIVE3D";
+ case PRIMITIVE3D_ID_UNIFIEDTRANSPARENCETEXTUREPRIMITIVE3D:
+ return "UNIFIEDTRANSPARENCETEXTUREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_GRADIENTTEXTUREPRIMITIVE3D:
+ return "GRADIENTTEXTUREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_BITMAPTEXTUREPRIMITIVE3D:
+ return "BITMAPTEXTUREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_TRANSPARENCETEXTUREPRIMITIVE3D:
+ return "TRANSPARENCETEXTUREPRIMITIVE3D";
+ case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D:
+ return "TRANSFORMPRIMITIVE3D";
+ case PRIMITIVE3D_ID_HIDDENGEOMETRYPRIMITIVE3D:
+ return "HIDDENGEOMETRYPRIMITIVE3D";
+ default:
+ return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF);
+ }
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/baseprimitive3d.cxx b/drawinglayer/source/primitive3d/baseprimitive3d.cxx
new file mode 100644
index 000000000..5bd980012
--- /dev/null
+++ b/drawinglayer/source/primitive3d/baseprimitive3d.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/baseprimitive3d.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <comphelper/sequence.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ BasePrimitive3D::BasePrimitive3D()
+ : BasePrimitive3DImplBase(m_aMutex)
+ {
+ }
+
+ BasePrimitive3D::~BasePrimitive3D()
+ {
+ }
+
+ bool BasePrimitive3D::operator==( const BasePrimitive3D& rPrimitive ) const
+ {
+ return (getPrimitive3DID() == rPrimitive.getPrimitive3DID());
+ }
+
+ basegfx::B3DRange BasePrimitive3D::getB3DRange(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ return get3DDecomposition(rViewInformation).getB3DRange(rViewInformation);
+ }
+
+ Primitive3DContainer BasePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ return Primitive3DContainer();
+ }
+
+ css::uno::Sequence< ::css::uno::Reference< ::css::graphic::XPrimitive3D > > SAL_CALL BasePrimitive3D::getDecomposition( const uno::Sequence< beans::PropertyValue >& rViewParameters )
+ {
+ const geometry::ViewInformation3D aViewInformation(rViewParameters);
+ return comphelper::containerToSequence(get3DDecomposition(aViewInformation));
+ }
+
+ css::geometry::RealRectangle3D SAL_CALL BasePrimitive3D::getRange( const uno::Sequence< beans::PropertyValue >& rViewParameters )
+ {
+ const geometry::ViewInformation3D aViewInformation(rViewParameters);
+ return basegfx::unotools::rectangle3DFromB3DRectangle(getB3DRange(aViewInformation));
+ }
+
+
+ Primitive3DContainer BufferedDecompositionPrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ return Primitive3DContainer();
+ }
+
+ BufferedDecompositionPrimitive3D::BufferedDecompositionPrimitive3D()
+ {
+ }
+
+ Primitive3DContainer BufferedDecompositionPrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if(getBuffered3DDecomposition().empty())
+ {
+ const Primitive3DContainer aNewSequence(create3DDecomposition(rViewInformation));
+ const_cast< BufferedDecompositionPrimitive3D* >(this)->setBuffered3DDecomposition(aNewSequence);
+ }
+
+ return getBuffered3DDecomposition();
+ }
+
+// tooling
+
+ // get range3D from a given Primitive3DReference
+ basegfx::B3DRange getB3DRangeFromPrimitive3DReference(const Primitive3DReference& rCandidate, const geometry::ViewInformation3D& aViewInformation)
+ {
+ basegfx::B3DRange aRetval;
+
+ if(rCandidate.is())
+ {
+ const BasePrimitive3D* pCandidate(static_cast< BasePrimitive3D* >(rCandidate.get()));
+ aRetval.expand(pCandidate->getB3DRange(aViewInformation));
+ }
+
+ return aRetval;
+ }
+
+ // get range3D from a given Primitive3DContainer
+ basegfx::B3DRange Primitive3DContainer::getB3DRange(const geometry::ViewInformation3D& aViewInformation) const
+ {
+ basegfx::B3DRange aRetval;
+
+ if(!empty())
+ {
+ const size_t nCount(size());
+
+ for(size_t a(0); a < nCount; a++)
+ {
+ aRetval.expand(getB3DRangeFromPrimitive3DReference((*this)[a], aViewInformation));
+ }
+ }
+
+ return aRetval;
+ }
+
+ bool arePrimitive3DReferencesEqual(const Primitive3DReference& rxA, const Primitive3DReference& rxB)
+ {
+ const bool bAIs(rxA.is());
+
+ if(bAIs != rxB.is())
+ {
+ return false;
+ }
+
+ if(!bAIs)
+ {
+ return true;
+ }
+
+ const BasePrimitive3D* pA(static_cast< const BasePrimitive3D* >(rxA.get()));
+ const BasePrimitive3D* pB(static_cast< const BasePrimitive3D* >(rxB.get()));
+
+ return pA->operator==(*pB);
+ }
+
+ bool Primitive3DContainer::operator==(const Primitive3DContainer& rB) const
+ {
+ const bool bAHasElements(!empty());
+
+ if(bAHasElements != !rB.empty())
+ {
+ return false;
+ }
+
+ if(!bAHasElements)
+ {
+ return true;
+ }
+
+ const size_t nCount(size());
+
+ if(nCount != rB.size())
+ {
+ return false;
+ }
+
+ for(size_t a(0); a < nCount; a++)
+ {
+ if(!arePrimitive3DReferencesEqual((*this)[a], rB[a]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void Primitive3DContainer::append(const Primitive3DContainer& rSource)
+ {
+ insert(end(), rSource.begin(), rSource.end());
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/groupprimitive3d.cxx b/drawinglayer/source/primitive3d/groupprimitive3d.cxx
new file mode 100644
index 000000000..69291e768
--- /dev/null
+++ b/drawinglayer/source/primitive3d/groupprimitive3d.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/groupprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ GroupPrimitive3D::GroupPrimitive3D( const Primitive3DContainer& rChildren )
+ : maChildren(rChildren)
+ {
+ }
+
+ /** The compare opertator uses the Sequence::==operator, so only checking if
+ the references are equal. All non-equal references are interpreted as
+ non-equal.
+ */
+ bool GroupPrimitive3D::operator==( const BasePrimitive3D& rPrimitive ) const
+ {
+ if(BasePrimitive3D::operator==(rPrimitive))
+ {
+ const GroupPrimitive3D& rCompare = static_cast< const GroupPrimitive3D& >(rPrimitive);
+
+ return getChildren() == rCompare.getChildren();
+ }
+
+ return false;
+ }
+
+ /// default: just return children, so all renderers not supporting group will use its content
+ Primitive3DContainer GroupPrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ return getChildren();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(GroupPrimitive3D, PRIMITIVE3D_ID_GROUPPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx b/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx
new file mode 100644
index 000000000..a699f7170
--- /dev/null
+++ b/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/hatchtextureprimitive3d.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <texture/texture.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer HatchTexturePrimitive3D::impCreate3DDecomposition() const
+ {
+ Primitive3DContainer aRetval;
+
+ if(!getChildren().empty())
+ {
+ const Primitive3DContainer aSource(getChildren());
+ const size_t nSourceCount(aSource.size());
+ std::vector< Primitive3DReference > aDestination;
+
+ for(size_t a(0); a < nSourceCount; a++)
+ {
+ // get reference
+ const Primitive3DReference xReference(aSource[a]);
+
+ if(xReference.is())
+ {
+ const BasePrimitive3D* pBasePrimitive = static_cast< const BasePrimitive3D* >(xReference.get());
+
+ // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch
+ // not all content is needed, remove transparencies and ModifiedColorPrimitives
+ switch(pBasePrimitive->getPrimitive3DID())
+ {
+ case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D :
+ {
+ // polyPolygonMaterialPrimitive3D, check texturing and hatching
+ const PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const PolyPolygonMaterialPrimitive3D& >(*pBasePrimitive);
+ const basegfx::B3DPolyPolygon& aFillPolyPolygon(rPrimitive.getB3DPolyPolygon());
+
+ if(maHatch.isFillBackground())
+ {
+ // add original primitive for background
+ aDestination.push_back(xReference);
+ }
+
+ if(aFillPolyPolygon.areTextureCoordinatesUsed())
+ {
+ const sal_uInt32 nPolyCount(aFillPolyPolygon.count());
+ basegfx::B2DPolyPolygon aTexPolyPolygon;
+ basegfx::B2DPoint a2N;
+ basegfx::B2DVector a2X, a2Y;
+ basegfx::B3DPoint a3N;
+ basegfx::B3DVector a3X, a3Y;
+ bool b2N(false), b2X(false), b2Y(false);
+
+ for(sal_uInt32 b(0); b < nPolyCount; b++)
+ {
+ const basegfx::B3DPolygon& aPartPoly(aFillPolyPolygon.getB3DPolygon(b));
+ const sal_uInt32 nPointCount(aPartPoly.count());
+ basegfx::B2DPolygon aTexPolygon;
+
+ for(sal_uInt32 c(0); c < nPointCount; c++)
+ {
+ const basegfx::B2DPoint a2Candidate(aPartPoly.getTextureCoordinate(c));
+
+ if(!b2N)
+ {
+ a2N = a2Candidate;
+ a3N = aPartPoly.getB3DPoint(c);
+ b2N = true;
+ }
+ else if(!b2X && !a2N.equal(a2Candidate))
+ {
+ a2X = a2Candidate - a2N;
+ a3X = aPartPoly.getB3DPoint(c) - a3N;
+ b2X = true;
+ }
+ else if(!b2Y && !a2N.equal(a2Candidate) && !a2X.equal(a2Candidate))
+ {
+ a2Y = a2Candidate - a2N;
+
+ const double fCross(a2X.cross(a2Y));
+
+ if(!basegfx::fTools::equalZero(fCross))
+ {
+ a3Y = aPartPoly.getB3DPoint(c) - a3N;
+ b2Y = true;
+ }
+ }
+
+ aTexPolygon.append(a2Candidate);
+ }
+
+ aTexPolygon.setClosed(true);
+ aTexPolyPolygon.append(aTexPolygon);
+ }
+
+ if(b2N && b2X && b2Y)
+ {
+ // found two linearly independent 2D vectors
+ // get 2d range of texture coordinates
+ const basegfx::B2DRange aOutlineRange(basegfx::utils::getRange(aTexPolyPolygon));
+ const basegfx::BColor aHatchColor(getHatch().getColor());
+ const double fAngle(getHatch().getAngle());
+ std::vector< basegfx::B2DHomMatrix > aMatrices;
+
+ // get hatch transformations
+ switch(getHatch().getStyle())
+ {
+ case attribute::HatchStyle::Triple:
+ {
+ // rotated 45 degrees
+ texture::GeoTexSvxHatch aHatch(
+ aOutlineRange,
+ aOutlineRange,
+ getHatch().getDistance(),
+ fAngle - M_PI_4);
+
+ aHatch.appendTransformations(aMatrices);
+
+ [[fallthrough]];
+ }
+ case attribute::HatchStyle::Double:
+ {
+ // rotated 90 degrees
+ texture::GeoTexSvxHatch aHatch(
+ aOutlineRange,
+ aOutlineRange,
+ getHatch().getDistance(),
+ fAngle - M_PI_2);
+
+ aHatch.appendTransformations(aMatrices);
+
+ [[fallthrough]];
+ }
+ case attribute::HatchStyle::Single:
+ {
+ // angle as given
+ texture::GeoTexSvxHatch aHatch(
+ aOutlineRange,
+ aOutlineRange,
+ getHatch().getDistance(),
+ fAngle);
+
+ aHatch.appendTransformations(aMatrices);
+ }
+ }
+
+ // create geometry from unit line
+ basegfx::B2DPolyPolygon a2DHatchLines;
+ basegfx::B2DPolygon a2DUnitLine;
+ a2DUnitLine.append(basegfx::B2DPoint(0.0, 0.0));
+ a2DUnitLine.append(basegfx::B2DPoint(1.0, 0.0));
+
+ for(const basegfx::B2DHomMatrix & rMatrix : aMatrices)
+ {
+ basegfx::B2DPolygon aNewLine(a2DUnitLine);
+ aNewLine.transform(rMatrix);
+ a2DHatchLines.append(aNewLine);
+ }
+
+ if(a2DHatchLines.count())
+ {
+ // clip against texture polygon
+ a2DHatchLines = basegfx::utils::clipPolyPolygonOnPolyPolygon(a2DHatchLines, aTexPolyPolygon, true, true);
+ }
+
+ if(a2DHatchLines.count())
+ {
+ // create 2d matrix with 2d vectors as column vectors and 2d point as offset, this represents
+ // a coordinate system transformation from unit coordinates to the new coordinate system
+ basegfx::B2DHomMatrix a2D;
+ a2D.set(0, 0, a2X.getX());
+ a2D.set(1, 0, a2X.getY());
+ a2D.set(0, 1, a2Y.getX());
+ a2D.set(1, 1, a2Y.getY());
+ a2D.set(0, 2, a2N.getX());
+ a2D.set(1, 2, a2N.getY());
+
+ // invert that transformation, so we have a back-transformation from texture coordinates
+ // to unit coordinates
+ a2D.invert();
+ a2DHatchLines.transform(a2D);
+
+ // expand back-transformed geometry to 3D
+ basegfx::B3DPolyPolygon a3DHatchLines(basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(a2DHatchLines, 0.0));
+
+ // create 3d matrix with 3d vectors as column vectors (0,0,1 as Z) and 3d point as offset, this represents
+ // a coordinate system transformation from unit coordinates to the object's 3d coordinate system
+ basegfx::B3DHomMatrix a3D;
+ a3D.set(0, 0, a3X.getX());
+ a3D.set(1, 0, a3X.getY());
+ a3D.set(2, 0, a3X.getZ());
+ a3D.set(0, 1, a3Y.getX());
+ a3D.set(1, 1, a3Y.getY());
+ a3D.set(2, 1, a3Y.getZ());
+ a3D.set(0, 3, a3N.getX());
+ a3D.set(1, 3, a3N.getY());
+ a3D.set(2, 3, a3N.getZ());
+
+ // transform hatch lines to 3D object coordinates
+ a3DHatchLines.transform(a3D);
+
+ // build primitives from this geometry
+ const sal_uInt32 nHatchLines(a3DHatchLines.count());
+
+ for(sal_uInt32 d(0); d < nHatchLines; d++)
+ {
+ const Primitive3DReference xRef(new PolygonHairlinePrimitive3D(a3DHatchLines.getB3DPolygon(d), aHatchColor));
+ aDestination.push_back(xRef);
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ default :
+ {
+ // add reference to result
+ aDestination.push_back(xReference);
+ break;
+ }
+ }
+ }
+ }
+
+ // prepare return value
+ const sal_uInt32 nDestSize(aDestination.size());
+ aRetval.resize(nDestSize);
+
+ for(sal_uInt32 b(0); b < nDestSize; b++)
+ {
+ aRetval[b] = aDestination[b];
+ }
+ }
+
+ return aRetval;
+ }
+
+ HatchTexturePrimitive3D::HatchTexturePrimitive3D(
+ const attribute::FillHatchAttribute& rHatch,
+ const Primitive3DContainer& rChildren,
+ const basegfx::B2DVector& rTextureSize,
+ bool bModulate,
+ bool bFilter)
+ : TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter),
+ maHatch(rHatch)
+ {
+ }
+
+ bool HatchTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(TexturePrimitive3D::operator==(rPrimitive))
+ {
+ const HatchTexturePrimitive3D& rCompare = static_cast<const HatchTexturePrimitive3D&>(rPrimitive);
+
+ return (getHatch() == rCompare.getHatch());
+ }
+
+ return false;
+ }
+
+ Primitive3DContainer HatchTexturePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if(getBuffered3DDecomposition().empty())
+ {
+ const Primitive3DContainer aNewSequence(impCreate3DDecomposition());
+ const_cast< HatchTexturePrimitive3D* >(this)->maBuffered3DDecomposition = aNewSequence;
+ }
+
+ return getBuffered3DDecomposition();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(HatchTexturePrimitive3D, PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx b/drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx
new file mode 100644
index 000000000..1a367e501
--- /dev/null
+++ b/drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/hiddengeometryprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ HiddenGeometryPrimitive3D::HiddenGeometryPrimitive3D(
+ const Primitive3DContainer& rChildren)
+ : GroupPrimitive3D(rChildren)
+ {
+ }
+
+ basegfx::B3DRange HiddenGeometryPrimitive3D::getB3DRange(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ return getChildren().getB3DRange(rViewInformation);
+ }
+
+ Primitive3DContainer HiddenGeometryPrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // return empty sequence
+ return Primitive3DContainer();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(HiddenGeometryPrimitive3D, PRIMITIVE3D_ID_HIDDENGEOMETRYPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx b/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx
new file mode 100644
index 000000000..255f0235c
--- /dev/null
+++ b/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ ModifiedColorPrimitive3D::ModifiedColorPrimitive3D(
+ const Primitive3DContainer& rChildren,
+ const basegfx::BColorModifierSharedPtr& rColorModifier)
+ : GroupPrimitive3D(rChildren),
+ maColorModifier(rColorModifier)
+ {
+ }
+
+ bool ModifiedColorPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(GroupPrimitive3D::operator==(rPrimitive))
+ {
+ const ModifiedColorPrimitive3D& rCompare = static_cast<const ModifiedColorPrimitive3D&>(rPrimitive);
+
+ if(getColorModifier().get() == rCompare.getColorModifier().get())
+ {
+ return true;
+ }
+
+ if(!getColorModifier() || !rCompare.getColorModifier())
+ {
+ return false;
+ }
+
+ return *getColorModifier() == *rCompare.getColorModifier();
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(ModifiedColorPrimitive3D, PRIMITIVE3D_ID_MODIFIEDCOLORPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/polygonprimitive3d.cxx b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx
new file mode 100644
index 000000000..8fc2b4d31
--- /dev/null
+++ b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <primitive3d/polygontubeprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ PolygonHairlinePrimitive3D::PolygonHairlinePrimitive3D(
+ const basegfx::B3DPolygon& rPolygon,
+ const basegfx::BColor& rBColor)
+ : maPolygon(rPolygon),
+ maBColor(rBColor)
+ {
+ }
+
+ bool PolygonHairlinePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(BasePrimitive3D::operator==(rPrimitive))
+ {
+ const PolygonHairlinePrimitive3D& rCompare = static_cast<const PolygonHairlinePrimitive3D&>(rPrimitive);
+
+ return (getB3DPolygon() == rCompare.getB3DPolygon()
+ && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange PolygonHairlinePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ return basegfx::utils::getRange(getB3DPolygon());
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(PolygonHairlinePrimitive3D, PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D)
+
+
+
+ Primitive3DContainer PolygonStrokePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ Primitive3DContainer aRetval;
+
+ if(getB3DPolygon().count())
+ {
+ basegfx::B3DPolyPolygon aHairLinePolyPolygon;
+
+ if(0.0 == getStrokeAttribute().getFullDotDashLen())
+ {
+ aHairLinePolyPolygon = basegfx::B3DPolyPolygon(getB3DPolygon());
+ }
+ else
+ {
+ // apply LineStyle
+ basegfx::utils::applyLineDashing(getB3DPolygon(), getStrokeAttribute().getDotDashArray(), &aHairLinePolyPolygon, getStrokeAttribute().getFullDotDashLen());
+ }
+
+ // prepare result
+ aRetval.resize(aHairLinePolyPolygon.count());
+
+ if(getLineAttribute().getWidth())
+ {
+ // create fat line data
+ const double fRadius(getLineAttribute().getWidth() / 2.0);
+ const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin());
+ const css::drawing::LineCap aLineCap(getLineAttribute().getLineCap());
+
+ for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++)
+ {
+ // create tube primitives
+ const Primitive3DReference xRef(
+ new PolygonTubePrimitive3D(
+ aHairLinePolyPolygon.getB3DPolygon(a),
+ getLineAttribute().getColor(),
+ fRadius,
+ aLineJoin,
+ aLineCap));
+ aRetval[a] = xRef;
+ }
+ }
+ else
+ {
+ // create hair line data for all sub polygons
+ for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++)
+ {
+ const basegfx::B3DPolygon& aCandidate = aHairLinePolyPolygon.getB3DPolygon(a);
+ const Primitive3DReference xRef(new PolygonHairlinePrimitive3D(aCandidate, getLineAttribute().getColor()));
+ aRetval[a] = xRef;
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ PolygonStrokePrimitive3D::PolygonStrokePrimitive3D(
+ const basegfx::B3DPolygon& rPolygon,
+ const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute)
+ : maPolygon(rPolygon),
+ maLineAttribute(rLineAttribute),
+ maStrokeAttribute(rStrokeAttribute)
+ {
+ }
+
+ bool PolygonStrokePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive3D::operator==(rPrimitive))
+ {
+ const PolygonStrokePrimitive3D& rCompare = static_cast<const PolygonStrokePrimitive3D&>(rPrimitive);
+
+ return (getB3DPolygon() == rCompare.getB3DPolygon()
+ && getLineAttribute() == rCompare.getLineAttribute()
+ && getStrokeAttribute() == rCompare.getStrokeAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(PolygonStrokePrimitive3D, PRIMITIVE3D_ID_POLYGONSTROKEPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx
new file mode 100644
index 000000000..d8d3cc9c8
--- /dev/null
+++ b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx
@@ -0,0 +1,769 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/polygontubeprimitive3d.hxx>
+#include <drawinglayer/attribute/materialattribute3d.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <mutex>
+
+namespace drawinglayer::primitive3d
+{
+ namespace // anonymous namespace
+ {
+ class TubeBuffer
+ {
+ private:
+ // data for buffered tube primitives
+ Primitive3DContainer m_aLineTubeList;
+ sal_uInt32 m_nLineTubeSegments;
+ attribute::MaterialAttribute3D m_aLineMaterial;
+ std::mutex m_aMutex;
+ public:
+ TubeBuffer()
+ : m_nLineTubeSegments(0)
+ {
+ }
+
+ TubeBuffer(const TubeBuffer&) = delete;
+ const TubeBuffer& operator=(const TubeBuffer&) = delete;
+
+ Primitive3DContainer getLineTubeSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // may exclusively change cached data, use mutex
+ std::unique_lock aGuard(m_aMutex);
+
+ if (nSegments != m_nLineTubeSegments || !(rMaterial == m_aLineMaterial))
+ {
+ m_nLineTubeSegments = nSegments;
+ m_aLineMaterial = rMaterial;
+ m_aLineTubeList = Primitive3DContainer();
+ }
+
+ if (m_aLineTubeList.empty() && m_nLineTubeSegments != 0)
+ {
+ const basegfx::B3DPoint aLeft(0.0, 0.0, 0.0);
+ const basegfx::B3DPoint aRight(1.0, 0.0, 0.0);
+ basegfx::B3DPoint aLastLeft(0.0, 1.0, 0.0);
+ basegfx::B3DPoint aLastRight(1.0, 1.0, 0.0);
+ basegfx::B3DHomMatrix aRot;
+ aRot.rotate(2 * M_PI / static_cast<double>(m_nLineTubeSegments), 0.0, 0.0);
+ m_aLineTubeList.resize(m_nLineTubeSegments);
+
+ for(sal_uInt32 a = 0; a < m_nLineTubeSegments; ++a)
+ {
+ const basegfx::B3DPoint aNextLeft(aRot * aLastLeft);
+ const basegfx::B3DPoint aNextRight(aRot * aLastRight);
+ basegfx::B3DPolygon aNewPolygon;
+
+ aNewPolygon.append(aNextLeft);
+ aNewPolygon.setNormal(0, basegfx::B3DVector(aNextLeft - aLeft));
+
+ aNewPolygon.append(aLastLeft);
+ aNewPolygon.setNormal(1, basegfx::B3DVector(aLastLeft - aLeft));
+
+ aNewPolygon.append(aLastRight);
+ aNewPolygon.setNormal(2, basegfx::B3DVector(aLastRight - aRight));
+
+ aNewPolygon.append(aNextRight);
+ aNewPolygon.setNormal(3, basegfx::B3DVector(aNextRight - aRight));
+
+ aNewPolygon.setClosed(true);
+
+ const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
+ const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
+ m_aLineTubeList[a] = xRef;
+
+ aLastLeft = aNextLeft;
+ aLastRight = aNextRight;
+ }
+ }
+ return m_aLineTubeList;
+ }
+ };
+
+ Primitive3DContainer getLineTubeSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // static data for buffered tube primitives
+ static TubeBuffer theTubeBuffer;
+ return theTubeBuffer.getLineTubeSegments(nSegments, rMaterial);
+ }
+
+ class CapBuffer
+ {
+ private:
+ // data for buffered cap primitives
+ Primitive3DContainer m_aLineCapList;
+ sal_uInt32 m_nLineCapSegments;
+ attribute::MaterialAttribute3D m_aLineMaterial;
+ std::mutex m_aMutex;
+ public:
+ CapBuffer()
+ : m_nLineCapSegments(0)
+ {
+ }
+ CapBuffer(const CapBuffer&) = delete;
+ const CapBuffer& operator=(const CapBuffer&) = delete;
+
+ Primitive3DContainer getLineCapSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // may exclusively change cached data, use mutex
+ std::unique_lock aGuard(m_aMutex);
+
+ if (nSegments != m_nLineCapSegments || !(rMaterial == m_aLineMaterial))
+ {
+ m_nLineCapSegments = nSegments;
+ m_aLineMaterial = rMaterial;
+ m_aLineCapList = Primitive3DContainer();
+ }
+
+ if (m_aLineCapList.empty() && m_nLineCapSegments != 0)
+ {
+ const basegfx::B3DPoint aNull(0.0, 0.0, 0.0);
+ basegfx::B3DPoint aLast(0.0, 1.0, 0.0);
+ basegfx::B3DHomMatrix aRot;
+ aRot.rotate(2 * M_PI / static_cast<double>(m_nLineCapSegments), 0.0, 0.0);
+ m_aLineCapList.resize(m_nLineCapSegments);
+
+ for(sal_uInt32 a = 0; a < m_nLineCapSegments; ++a)
+ {
+ const basegfx::B3DPoint aNext(aRot * aLast);
+ basegfx::B3DPolygon aNewPolygon;
+
+ aNewPolygon.append(aLast);
+ aNewPolygon.setNormal(0, basegfx::B3DVector(aLast - aNull));
+
+ aNewPolygon.append(aNext);
+ aNewPolygon.setNormal(1, basegfx::B3DVector(aNext - aNull));
+
+ aNewPolygon.append(aNull);
+ aNewPolygon.setNormal(2, basegfx::B3DVector(-1.0, 0.0, 0.0));
+
+ aNewPolygon.setClosed(true);
+
+ const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
+ const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
+ m_aLineCapList[a] = xRef;
+
+ aLast = aNext;
+ }
+ }
+
+ return m_aLineCapList;
+ }
+ };
+
+ Primitive3DContainer getLineCapSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // static data for buffered cap primitives
+ static CapBuffer theCapBuffer;
+ return theCapBuffer.getLineCapSegments(nSegments, rMaterial);
+ }
+
+ class CapRoundBuffer
+ {
+ private:
+ // data for buffered capround primitives
+ Primitive3DContainer m_aLineCapRoundList;
+ sal_uInt32 m_nLineCapRoundSegments;
+ attribute::MaterialAttribute3D m_aLineMaterial;
+ std::mutex m_aMutex;
+ public:
+ CapRoundBuffer()
+ : m_nLineCapRoundSegments(0)
+ {
+ }
+ CapRoundBuffer(const CapRoundBuffer&) = delete;
+ const CapRoundBuffer& operator=(const CapRoundBuffer&) = delete;
+
+ Primitive3DContainer getLineCapRoundSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // may exclusively change cached data, use mutex
+ std::unique_lock aGuard(m_aMutex);
+
+ if (nSegments != m_nLineCapRoundSegments || !(rMaterial == m_aLineMaterial))
+ {
+ m_nLineCapRoundSegments = nSegments;
+ m_aLineMaterial = rMaterial;
+ m_aLineCapRoundList = Primitive3DContainer();
+ }
+
+ if (m_aLineCapRoundList.empty() && m_nLineCapRoundSegments)
+ {
+ // calculate new horizontal segments
+ sal_uInt32 nVerSeg(nSegments / 2);
+
+ if (nVerSeg < 1)
+ {
+ nVerSeg = 1;
+ }
+
+ // create half-sphere; upper half of unit sphere
+ basegfx::B3DPolyPolygon aSphere(
+ basegfx::utils::createUnitSphereFillPolyPolygon(
+ nSegments,
+ nVerSeg,
+ true,
+ M_PI_2, 0.0,
+ 0.0, 2 * M_PI));
+ const sal_uInt32 nCount(aSphere.count());
+
+ if (nCount)
+ {
+ // rotate to have sphere cap oriented to negative X-Axis; do not
+ // forget to transform normals, too
+ basegfx::B3DHomMatrix aSphereTrans;
+
+ aSphereTrans.rotate(0.0, 0.0, M_PI_2);
+ aSphere.transform(aSphereTrans);
+ aSphere.transformNormals(aSphereTrans);
+
+ // realloc for primitives and create based on polygon snippets
+ m_aLineCapRoundList.resize(nCount);
+
+ for (sal_uInt32 a = 0; a < nCount; ++a)
+ {
+ const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
+ const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
+
+ // need to create one primitive per Polygon since the primitive
+ // is for planar PolyPolygons which is definitely not the case here
+ m_aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D(
+ aPartPolyPolygon,
+ rMaterial,
+ false);
+ }
+ }
+ }
+
+ return m_aLineCapRoundList;
+ }
+
+ };
+
+ Primitive3DContainer getLineCapRoundSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial)
+ {
+ // static data for buffered cap primitives
+ static CapRoundBuffer theCapRoundBuffer;
+ return theCapRoundBuffer.getLineCapRoundSegments(nSegments, rMaterial);
+ }
+
+ Primitive3DContainer getLineJoinSegments(
+ sal_uInt32 nSegments,
+ const attribute::MaterialAttribute3D& rMaterial,
+ double fAngle,
+ double fMiterMinimumAngle,
+ basegfx::B2DLineJoin aLineJoin)
+ {
+ // nSegments is for whole circle, adapt to half circle
+ const sal_uInt32 nVerSeg(nSegments >> 1);
+ std::vector< BasePrimitive3D* > aResultVector;
+
+ if(nVerSeg)
+ {
+ if(basegfx::B2DLineJoin::Round == aLineJoin)
+ {
+ // calculate new horizontal segments
+ const sal_uInt32 nHorSeg(basegfx::fround((fAngle / (2 * M_PI)) * static_cast<double>(nSegments)));
+
+ if(nHorSeg)
+ {
+ // create half-sphere
+ const basegfx::B3DPolyPolygon aSphere(basegfx::utils::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, M_PI_2, -M_PI_2, 0.0, fAngle));
+
+ for(sal_uInt32 a(0); a < aSphere.count(); a++)
+ {
+ const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
+ const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
+ aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false));
+ }
+ }
+ else
+ {
+ // fallback to bevel when there is not at least one segment hor and ver
+ aLineJoin = basegfx::B2DLineJoin::Bevel;
+ }
+ }
+
+ if (basegfx::B2DLineJoin::Bevel == aLineJoin ||
+ basegfx::B2DLineJoin::Miter == aLineJoin)
+ {
+ if(basegfx::B2DLineJoin::Miter == aLineJoin)
+ {
+ const double fMiterAngle(fAngle/2.0);
+
+ if(fMiterAngle < fMiterMinimumAngle)
+ {
+ // fallback to bevel when miter's angle is too small
+ aLineJoin = basegfx::B2DLineJoin::Bevel;
+ }
+ }
+
+ const double fInc(M_PI / static_cast<double>(nVerSeg));
+ const double fSin(sin(-fAngle));
+ const double fCos(cos(-fAngle));
+ const bool bMiter(basegfx::B2DLineJoin::Miter == aLineJoin);
+ const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0);
+ const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0);
+ double fPos(-M_PI_2);
+ basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY;
+ basegfx::B3DPoint aCurrMiter, aNextMiter;
+ basegfx::B3DPolygon aNewPolygon, aMiterPolygon;
+
+ // close polygon
+ aNewPolygon.setClosed(true);
+ aMiterPolygon.setClosed(true);
+
+ for(sal_uInt32 a(0); a < nVerSeg; a++)
+ {
+ const bool bFirst(0 == a);
+ const bool bLast(a + 1 == nVerSeg);
+
+ if(bFirst || !bLast)
+ {
+ fPos += fInc;
+
+ aNextPointOnXY = basegfx::B3DPoint(
+ cos(fPos),
+ sin(fPos),
+ 0.0);
+
+ aNextPointRotY = basegfx::B3DPoint(
+ aNextPointOnXY.getX() * fCos,
+ aNextPointOnXY.getY(),
+ aNextPointOnXY.getX() * fSin);
+
+ if(bMiter)
+ {
+ aNextMiter = basegfx::B3DPoint(
+ aNextPointOnXY.getX(),
+ aNextPointOnXY.getY(),
+ fMiterSin * (aNextPointOnXY.getX() / fMiterCos));
+ }
+ }
+
+ if(bFirst)
+ {
+ aNewPolygon.clear();
+
+ if(bMiter)
+ {
+ aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
+ aNewPolygon.append(aNextPointOnXY);
+ aNewPolygon.append(aNextMiter);
+
+ aMiterPolygon.clear();
+ aMiterPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
+ aMiterPolygon.append(aNextMiter);
+ aMiterPolygon.append(aNextPointRotY);
+ }
+ else
+ {
+ aNewPolygon.append(basegfx::B3DPoint(0.0, -1.0, 0.0));
+ aNewPolygon.append(aNextPointOnXY);
+ aNewPolygon.append(aNextPointRotY);
+ }
+ }
+ else if(bLast)
+ {
+ aNewPolygon.clear();
+
+ if(bMiter)
+ {
+ aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
+ aNewPolygon.append(aCurrMiter);
+ aNewPolygon.append(aPointOnXY);
+
+ aMiterPolygon.clear();
+ aMiterPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
+ aMiterPolygon.append(aPointRotY);
+ aMiterPolygon.append(aCurrMiter);
+ }
+ else
+ {
+ aNewPolygon.append(basegfx::B3DPoint(0.0, 1.0, 0.0));
+ aNewPolygon.append(aPointRotY);
+ aNewPolygon.append(aPointOnXY);
+ }
+ }
+ else
+ {
+ aNewPolygon.clear();
+
+ if(bMiter)
+ {
+ aNewPolygon.append(aPointOnXY);
+ aNewPolygon.append(aNextPointOnXY);
+ aNewPolygon.append(aNextMiter);
+ aNewPolygon.append(aCurrMiter);
+
+ aMiterPolygon.clear();
+ aMiterPolygon.append(aCurrMiter);
+ aMiterPolygon.append(aNextMiter);
+ aMiterPolygon.append(aNextPointRotY);
+ aMiterPolygon.append(aPointRotY);
+ }
+ else
+ {
+ aNewPolygon.append(aPointRotY);
+ aNewPolygon.append(aPointOnXY);
+ aNewPolygon.append(aNextPointOnXY);
+ aNewPolygon.append(aNextPointRotY);
+ }
+ }
+
+ // set normals
+ for(sal_uInt32 b(0); b < aNewPolygon.count(); b++)
+ {
+ aNewPolygon.setNormal(b, basegfx::B3DVector(aNewPolygon.getB3DPoint(b)));
+ }
+
+ // create primitive
+ if(aNewPolygon.count())
+ {
+ const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
+ aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false));
+ }
+
+ if(bMiter && aMiterPolygon.count())
+ {
+ // set normals
+ for(sal_uInt32 c(0); c < aMiterPolygon.count(); c++)
+ {
+ aMiterPolygon.setNormal(c, basegfx::B3DVector(aMiterPolygon.getB3DPoint(c)));
+ }
+
+ // create primitive
+ const basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
+ aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false));
+ }
+
+ // prepare next step
+ if(bFirst || !bLast)
+ {
+ aPointOnXY = aNextPointOnXY;
+ aPointRotY = aNextPointRotY;
+
+ if(bMiter)
+ {
+ aCurrMiter = aNextMiter;
+ }
+ }
+ }
+ }
+ }
+
+ Primitive3DContainer aRetval(aResultVector.size());
+
+ for(size_t a(0); a < aResultVector.size(); a++)
+ {
+ aRetval[a] = Primitive3DReference(aResultVector[a]);
+ }
+
+ return aRetval;
+ }
+
+ basegfx::B3DHomMatrix getRotationFromVector(const basegfx::B3DVector& rVector)
+ {
+ // build transformation from unit vector to vector
+ basegfx::B3DHomMatrix aRetval;
+
+ // get applied rotations from angles in XY and in XZ (cartesian)
+ const double fRotInXY(atan2(rVector.getY(), rVector.getXZLength()));
+ const double fRotInXZ(atan2(-rVector.getZ(), rVector.getX()));
+
+ // apply rotations. Rot around Z needs to be done first, so apply in two steps
+ aRetval.rotate(0.0, 0.0, fRotInXY);
+ aRetval.rotate(0.0, fRotInXZ, 0.0);
+
+ return aRetval;
+ }
+ } // end of anonymous namespace
+
+
+using namespace com::sun::star;
+
+ Primitive3DContainer PolygonTubePrimitive3D::impCreate3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ const sal_uInt32 nPointCount(getB3DPolygon().count());
+ std::vector< BasePrimitive3D* > aResultVector;
+
+ if(nPointCount)
+ {
+ if(basegfx::fTools::more(getRadius(), 0.0))
+ {
+ const attribute::MaterialAttribute3D aMaterial(getBColor());
+ static const sal_uInt32 nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps)
+ const bool bClosed(getB3DPolygon().isClosed());
+ const bool bNoLineJoin(basegfx::B2DLineJoin::NONE == getLineJoin());
+ const sal_uInt32 nLoopCount(bClosed ? nPointCount : nPointCount - 1);
+ basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1));
+ basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0));
+
+ for(sal_uInt32 a(0); a < nLoopCount; a++)
+ {
+ // get next data
+ const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1) % nPointCount));
+ const basegfx::B3DVector aForw(aNext - aCurr);
+ const double fForwLen(aForw.getLength());
+
+ if(basegfx::fTools::more(fForwLen, 0.0))
+ {
+ // find out if linecap is active
+ const bool bFirst(!a);
+ const bool bLast(a + 1 == nLoopCount);
+ const bool bLineCapPossible(!bClosed && (bFirst || bLast));
+ const bool bLineCapRound(bLineCapPossible && css::drawing::LineCap_ROUND == getLineCap());
+ const bool bLineCapSquare(bLineCapPossible && css::drawing::LineCap_SQUARE == getLineCap());
+
+ // get rotation from vector, this describes rotation from (1, 0, 0) to aForw
+ basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
+
+ // prepare transformations for tube and cap
+ basegfx::B3DHomMatrix aTubeTrans;
+ basegfx::B3DHomMatrix aCapTrans;
+
+ // cap gets radius size
+ aCapTrans.scale(getRadius(), getRadius(), getRadius());
+
+ if(bLineCapSquare)
+ {
+ // when square line cap just prolong line segment in X, maybe 2 x radius when
+ // first and last (simple line segment)
+ const double fExtraLength(bFirst && bLast ? getRadius() * 2.0 : getRadius());
+
+ aTubeTrans.scale(fForwLen + fExtraLength, getRadius(), getRadius());
+
+ if(bFirst)
+ {
+ // correct start positions for tube and cap when first and square prolonged
+ aTubeTrans.translate(-getRadius(), 0.0, 0.0);
+ aCapTrans.translate(-getRadius(), 0.0, 0.0);
+ }
+ }
+ else
+ {
+ // normal tube size
+ aTubeTrans.scale(fForwLen, getRadius(), getRadius());
+ }
+
+ // rotate and translate tube and cap
+ aTubeTrans *= aRotVector;
+ aTubeTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
+ aCapTrans *= aRotVector;
+ aCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
+
+ if(bNoLineJoin || (!bClosed && bFirst))
+ {
+ // line start edge, build transformed primitiveVector3D
+ Primitive3DContainer aSequence;
+
+ if(bLineCapRound && bFirst)
+ {
+ // LineCapRound used
+ aSequence = getLineCapRoundSegments(nSegments, aMaterial);
+ }
+ else
+ {
+ // simple closing cap
+ aSequence = getLineCapSegments(nSegments, aMaterial);
+ }
+
+ aResultVector.push_back(new TransformPrimitive3D(aCapTrans, aSequence));
+ }
+ else
+ {
+ const basegfx::B3DVector aBack(aCurr - aLast);
+ const double fCross(basegfx::cross(aBack, aForw).getLength());
+
+ if(!basegfx::fTools::equalZero(fCross))
+ {
+ // line connect non-parallel, aBack, aForw, use getLineJoin()
+ const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. M_PI_2
+ Primitive3DContainer aNewList(
+ getLineJoinSegments(
+ nSegments,
+ aMaterial,
+ fAngle,
+ getMiterMinimumAngle(),
+ getLineJoin()));
+
+ // calculate transformation. First, get angle in YZ between nForw projected on (1, 0, 0) and nBack
+ basegfx::B3DHomMatrix aInvRotVector(aRotVector);
+ aInvRotVector.invert();
+ basegfx::B3DVector aTransBack(aInvRotVector * aBack);
+ const double fRotInYZ(atan2(aTransBack.getY(), aTransBack.getZ()));
+
+ // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X.
+ // Also apply usual scaling and translation
+ basegfx::B3DHomMatrix aSphereTrans;
+ aSphereTrans.rotate(0.0, M_PI_2, 0.0);
+ aSphereTrans.rotate(M_PI - fRotInYZ, 0.0, 0.0);
+ aSphereTrans *= aRotVector;
+ aSphereTrans.scale(getRadius(), getRadius(), getRadius());
+ aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
+
+ // line start edge, build transformed primitiveVector3D
+ aResultVector.push_back(
+ new TransformPrimitive3D(
+ aSphereTrans,
+ aNewList));
+ }
+ }
+
+ // create line segments, build transformed primitiveVector3D
+ aResultVector.push_back(
+ new TransformPrimitive3D(
+ aTubeTrans,
+ getLineTubeSegments(nSegments, aMaterial)));
+
+ if(bNoLineJoin || (!bClosed && bLast))
+ {
+ // line end edge
+ basegfx::B3DHomMatrix aBackCapTrans;
+
+ // Mirror (line end) and radius scale
+ aBackCapTrans.rotate(0.0, M_PI, 0.0);
+ aBackCapTrans.scale(getRadius(), getRadius(), getRadius());
+
+ if(bLineCapSquare && bLast)
+ {
+ // correct position when square and prolonged
+ aBackCapTrans.translate(fForwLen + getRadius(), 0.0, 0.0);
+ }
+ else
+ {
+ // standard position
+ aBackCapTrans.translate(fForwLen, 0.0, 0.0);
+ }
+
+ // rotate and translate to destination
+ aBackCapTrans *= aRotVector;
+ aBackCapTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
+
+ // get primitiveVector3D
+ Primitive3DContainer aSequence;
+
+ if(bLineCapRound && bLast)
+ {
+ // LineCapRound used
+ aSequence = getLineCapRoundSegments(nSegments, aMaterial);
+ }
+ else
+ {
+ // simple closing cap
+ aSequence = getLineCapSegments(nSegments, aMaterial);
+ }
+
+ aResultVector.push_back(
+ new TransformPrimitive3D(
+ aBackCapTrans,
+ aSequence));
+ }
+ }
+
+ // prepare next loop step
+ aLast = aCurr;
+ aCurr = aNext;
+ }
+ }
+ else
+ {
+ // create hairline
+ aResultVector.push_back(new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor()));
+ }
+ }
+
+ // prepare return value
+ Primitive3DContainer aRetval(aResultVector.size());
+
+ for(size_t a(0); a < aResultVector.size(); a++)
+ {
+ aRetval[a] = Primitive3DReference(aResultVector[a]);
+ }
+
+ return aRetval;
+ }
+
+ PolygonTubePrimitive3D::PolygonTubePrimitive3D(
+ const basegfx::B3DPolygon& rPolygon,
+ const basegfx::BColor& rBColor,
+ double fRadius, basegfx::B2DLineJoin aLineJoin,
+ css::drawing::LineCap aLineCap,
+ double fDegreeStepWidth,
+ double fMiterMinimumAngle)
+ : PolygonHairlinePrimitive3D(rPolygon, rBColor),
+ mfRadius(fRadius),
+ mfDegreeStepWidth(fDegreeStepWidth),
+ mfMiterMinimumAngle(fMiterMinimumAngle),
+ maLineJoin(aLineJoin),
+ maLineCap(aLineCap)
+ {
+ }
+
+ bool PolygonTubePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(PolygonHairlinePrimitive3D::operator==(rPrimitive))
+ {
+ const PolygonTubePrimitive3D& rCompare = static_cast<const PolygonTubePrimitive3D&>(rPrimitive);
+
+ return (getRadius() == rCompare.getRadius()
+ && getDegreeStepWidth() == rCompare.getDegreeStepWidth()
+ && getMiterMinimumAngle() == rCompare.getMiterMinimumAngle()
+ && getLineJoin() == rCompare.getLineJoin()
+ && getLineCap() == rCompare.getLineCap());
+ }
+
+ return false;
+ }
+
+ Primitive3DContainer PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if(getLast3DDecomposition().empty())
+ {
+ const Primitive3DContainer aNewSequence(impCreate3DDecomposition(rViewInformation));
+ const_cast< PolygonTubePrimitive3D* >(this)->maLast3DDecomposition = aNewSequence;
+ }
+
+ return getLast3DDecomposition();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(PolygonTubePrimitive3D, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D)
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx b/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx
new file mode 100644
index 000000000..41e62b611
--- /dev/null
+++ b/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ PolyPolygonMaterialPrimitive3D::PolyPolygonMaterialPrimitive3D(
+ const basegfx::B3DPolyPolygon& rPolyPolygon,
+ const attribute::MaterialAttribute3D& rMaterial,
+ bool bDoubleSided)
+ : maPolyPolygon(rPolyPolygon),
+ maMaterial(rMaterial),
+ mbDoubleSided(bDoubleSided)
+ {
+ }
+
+ bool PolyPolygonMaterialPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(BasePrimitive3D::operator==(rPrimitive))
+ {
+ const PolyPolygonMaterialPrimitive3D& rCompare = static_cast<const PolyPolygonMaterialPrimitive3D&>(rPrimitive);
+
+ return (getB3DPolyPolygon() == rCompare.getB3DPolyPolygon()
+ && getMaterial() == rCompare.getMaterial()
+ && getDoubleSided() == rCompare.getDoubleSided());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange PolyPolygonMaterialPrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ return basegfx::utils::getRange(getB3DPolyPolygon());
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(PolyPolygonMaterialPrimitive3D, PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrcubeprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrcubeprimitive3d.cxx
new file mode 100644
index 000000000..6297c7df0
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrcubeprimitive3d.cxx
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrcubeprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer SdrCubePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ const basegfx::B3DRange aUnitRange(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
+ Primitive3DContainer aRetval;
+ basegfx::B3DPolyPolygon aFill(basegfx::utils::createCubeFillPolyPolygonFromB3DRange(aUnitRange));
+
+ // normal creation
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ if(css::drawing::NormalsKind_SPECIFIC == getSdr3DObjectAttribute().getNormalsKind()
+ || css::drawing::NormalsKind_SPHERE == getSdr3DObjectAttribute().getNormalsKind())
+ {
+ // create sphere normals
+ const basegfx::B3DPoint aCenter(basegfx::utils::getRange(aFill).getCenter());
+ aFill = basegfx::utils::applyDefaultNormalsSphere(aFill, aCenter);
+ }
+
+ if(getSdr3DObjectAttribute().getNormalsInvert())
+ {
+ // invert normals
+ aFill = basegfx::utils::invertNormals(aFill);
+ }
+ }
+
+ // texture coordinates
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // handle texture coordinates X
+ const bool bParallelX(css::drawing::TextureProjectionMode_PARALLEL == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bObjectSpecificX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bSphereX(!bParallelX && (css::drawing::TextureProjectionMode_SPHERE == getSdr3DObjectAttribute().getTextureProjectionX()));
+
+ // handle texture coordinates Y
+ const bool bParallelY(css::drawing::TextureProjectionMode_PARALLEL == getSdr3DObjectAttribute().getTextureProjectionY());
+ const bool bObjectSpecificY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionY());
+ const bool bSphereY(!bParallelY && (css::drawing::TextureProjectionMode_SPHERE == getSdr3DObjectAttribute().getTextureProjectionY()));
+
+ if(bParallelX || bParallelY)
+ {
+ // apply parallel texture coordinates in X and/or Y
+ const basegfx::B3DRange aRange(basegfx::utils::getRange(aFill));
+ aFill = basegfx::utils::applyDefaultTextureCoordinatesParallel(aFill, aRange, bParallelX, bParallelY);
+ }
+
+ if(bSphereX || bSphereY)
+ {
+ // apply spherical texture coordinates in X and/or Y
+ const basegfx::B3DRange aRange(basegfx::utils::getRange(aFill));
+ const basegfx::B3DPoint aCenter(aRange.getCenter());
+ aFill = basegfx::utils::applyDefaultTextureCoordinatesSphere(aFill, aCenter, bSphereX, bSphereY);
+ }
+
+ if(bObjectSpecificX || bObjectSpecificY)
+ {
+ // object-specific
+ for(sal_uInt32 a(0); a < aFill.count(); a++)
+ {
+ basegfx::B3DPolygon aTmpPoly(aFill.getB3DPolygon(a));
+
+ if(aTmpPoly.count() >= 4)
+ {
+ for(sal_uInt32 b(0); b < 4; b++)
+ {
+ basegfx::B2DPoint aPoint(aTmpPoly.getTextureCoordinate(b));
+
+ if(bObjectSpecificX)
+ {
+ aPoint.setX((1 == b || 2 == b) ? 1.0 : 0.0);
+ }
+
+ if(bObjectSpecificY)
+ {
+ aPoint.setY((2 == b || 3 == b) ? 1.0 : 0.0);
+ }
+
+ aTmpPoly.setTextureCoordinate(b, aPoint);
+ }
+
+ aFill.setB3DPolygon(a, aTmpPoly);
+ }
+ }
+ }
+
+ // transform texture coordinates to texture size
+ basegfx::B2DHomMatrix aTexMatrix;
+ aTexMatrix.scale(getTextureSize().getX(), getTextureSize().getY());
+ aFill.transformTextureCoordinates(aTexMatrix);
+ }
+
+ // build vector of PolyPolygons
+ std::vector< basegfx::B3DPolyPolygon > a3DPolyPolygonVector;
+
+ for(sal_uInt32 a(0); a < aFill.count(); a++)
+ {
+ a3DPolyPolygonVector.emplace_back(aFill.getB3DPolygon(a));
+ }
+
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // add fill
+ aRetval = create3DPolyPolygonFillPrimitives(
+ a3DPolyPolygonVector,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute(),
+ getSdrLFSAttribute().getFill(),
+ getSdrLFSAttribute().getFillFloatTransGradient());
+ }
+ else
+ {
+ // create simplified 3d hit test geometry
+ aRetval = createHiddenGeometryPrimitives3D(
+ a3DPolyPolygonVector,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute());
+ }
+
+ // add line
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ basegfx::B3DPolyPolygon aLine(basegfx::utils::createCubePolyPolygonFromB3DRange(aUnitRange));
+ const Primitive3DContainer aLines(create3DPolyPolygonLinePrimitives(
+ aLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aLines);
+ }
+
+ // add shadow
+ if(!getSdrLFSAttribute().getShadow().isDefault() && !aRetval.empty())
+ {
+ const Primitive3DContainer aShadow(createShadowPrimitive3D(
+ aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
+ aRetval.append(aShadow);
+ }
+
+ return aRetval;
+ }
+
+ SdrCubePrimitive3D::SdrCubePrimitive3D(
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute)
+ : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute)
+ {
+ }
+
+ basegfx::B3DRange SdrCubePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // use default from sdrPrimitive3D which uses transformation expanded by line width/2.
+ // The parent implementation which uses the ranges of the decomposition would be more
+ // correct, but for historical reasons it is necessary to do the old method: To get
+ // the range of the non-transformed geometry and transform it then. This leads to different
+ // ranges where the new method is more correct, but the need to keep the old behaviour
+ // has priority here.
+ return getStandard3DRange();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(SdrCubePrimitive3D, PRIMITIVE3D_ID_SDRCUBEPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx
new file mode 100644
index 000000000..ec6bfaf3c
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx
@@ -0,0 +1,324 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/primitive3d/baseprimitive3d.hxx>
+#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygon.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <drawinglayer/attribute/fillgraphicattribute.hxx>
+#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <primitive3d/textureprimitive3d.hxx>
+#include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx>
+#include <primitive3d/hatchtextureprimitive3d.hxx>
+#include <primitive3d/shadowprimitive3d.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrobjectattribute3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+#include <primitive3d/hiddengeometryprimitive3d.hxx>
+#include <rtl/ref.hxx>
+
+
+namespace drawinglayer::primitive3d
+{
+ basegfx::B3DRange getRangeFrom3DGeometry(std::vector< basegfx::B3DPolyPolygon >& rFill)
+ {
+ basegfx::B3DRange aRetval;
+
+ for(const basegfx::B3DPolyPolygon & a : rFill)
+ {
+ aRetval.expand(basegfx::utils::getRange(a));
+ }
+
+ return aRetval;
+ }
+
+ void applyNormalsKindSphereTo3DGeometry(std::vector< basegfx::B3DPolyPolygon >& rFill, const basegfx::B3DRange& rRange)
+ {
+ // create sphere normals
+ const basegfx::B3DPoint aCenter(rRange.getCenter());
+
+ for(basegfx::B3DPolyPolygon & a : rFill)
+ {
+ a = basegfx::utils::applyDefaultNormalsSphere(a, aCenter);
+ }
+ }
+
+ void applyNormalsKindFlatTo3DGeometry(std::vector< basegfx::B3DPolyPolygon >& rFill)
+ {
+ for(basegfx::B3DPolyPolygon & a : rFill)
+ {
+ a.clearNormals();
+ }
+ }
+
+ void applyNormalsInvertTo3DGeometry(std::vector< basegfx::B3DPolyPolygon >& rFill)
+ {
+ // invert normals
+ for(basegfx::B3DPolyPolygon & a : rFill)
+ {
+ a = basegfx::utils::invertNormals(a);
+ }
+ }
+
+ void applyTextureTo3DGeometry(
+ css::drawing::TextureProjectionMode eModeX,
+ css::drawing::TextureProjectionMode eModeY,
+ std::vector< basegfx::B3DPolyPolygon >& rFill,
+ const basegfx::B3DRange& rRange,
+ const basegfx::B2DVector& rTextureSize)
+ {
+ // handle texture coordinates X
+ const bool bParallelX(css::drawing::TextureProjectionMode_PARALLEL == eModeX);
+ const bool bSphereX(!bParallelX && (css::drawing::TextureProjectionMode_SPHERE == eModeX));
+
+ // handle texture coordinates Y
+ const bool bParallelY(css::drawing::TextureProjectionMode_PARALLEL == eModeY);
+ const bool bSphereY(!bParallelY && (css::drawing::TextureProjectionMode_SPHERE == eModeY));
+
+ if(bParallelX || bParallelY)
+ {
+ // apply parallel texture coordinates in X and/or Y
+ for(auto & a: rFill)
+ {
+ a = basegfx::utils::applyDefaultTextureCoordinatesParallel(a, rRange, bParallelX, bParallelY);
+ }
+ }
+
+ if(bSphereX || bSphereY)
+ {
+ // apply spherical texture coordinates in X and/or Y
+ const basegfx::B3DPoint aCenter(rRange.getCenter());
+
+ for(auto & a: rFill)
+ {
+ a = basegfx::utils::applyDefaultTextureCoordinatesSphere(a, aCenter, bSphereX, bSphereY);
+ }
+ }
+
+ // transform texture coordinates to texture size
+ basegfx::B2DHomMatrix aTexMatrix;
+ aTexMatrix.scale(rTextureSize.getX(), rTextureSize.getY());
+
+ for(auto & a: rFill)
+ {
+ a.transformTextureCoordinates(aTexMatrix);
+ }
+ }
+
+ Primitive3DContainer create3DPolyPolygonLinePrimitives(
+ const basegfx::B3DPolyPolygon& rUnitPolyPolygon,
+ const basegfx::B3DHomMatrix& rObjectTransform,
+ const attribute::SdrLineAttribute& rLine)
+ {
+ // prepare fully scaled polyPolygon
+ basegfx::B3DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon);
+ aScaledPolyPolygon.transform(rObjectTransform);
+
+ // create line and stroke attribute
+ const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap());
+ const attribute::StrokeAttribute aStrokeAttribute(std::vector(rLine.getDotDashArray()), rLine.getFullDotDashLen());
+
+ // create primitives
+ Primitive3DContainer aRetval(aScaledPolyPolygon.count());
+
+ for(sal_uInt32 a(0); a < aScaledPolyPolygon.count(); a++)
+ {
+ const Primitive3DReference xRef(new PolygonStrokePrimitive3D(aScaledPolyPolygon.getB3DPolygon(a), aLineAttribute, aStrokeAttribute));
+ aRetval[a] = xRef;
+ }
+
+ if(0.0 != rLine.getTransparence())
+ {
+ // create UnifiedTransparenceTexturePrimitive3D, add created primitives and exchange
+ const Primitive3DReference xRef(new UnifiedTransparenceTexturePrimitive3D(rLine.getTransparence(), aRetval));
+ aRetval = { xRef };
+ }
+
+ return aRetval;
+ }
+
+ Primitive3DContainer create3DPolyPolygonFillPrimitives(
+ const std::vector< basegfx::B3DPolyPolygon >& r3DPolyPolygonVector,
+ const basegfx::B3DHomMatrix& rObjectTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::Sdr3DObjectAttribute& aSdr3DObjectAttribute,
+ const attribute::SdrFillAttribute& rFill,
+ const attribute::FillGradientAttribute& rFillGradient)
+ {
+ Primitive3DContainer aRetval;
+
+ if(!r3DPolyPolygonVector.empty())
+ {
+ // create list of simple fill primitives
+ aRetval.resize(r3DPolyPolygonVector.size());
+
+ for(size_t a(0); a < r3DPolyPolygonVector.size(); a++)
+ {
+ // get scaled PolyPolygon
+ basegfx::B3DPolyPolygon aScaledPolyPolygon(r3DPolyPolygonVector[a]);
+ aScaledPolyPolygon.transform(rObjectTransform);
+
+ if(aScaledPolyPolygon.areNormalsUsed())
+ {
+ aScaledPolyPolygon.transformNormals(rObjectTransform);
+ }
+
+ const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(
+ aScaledPolyPolygon,
+ aSdr3DObjectAttribute.getMaterial(),
+ aSdr3DObjectAttribute.getDoubleSided()));
+ aRetval[a] = xRef;
+ }
+
+ // look for and evtl. build texture sub-group primitive
+ if(!rFill.getGradient().isDefault()
+ || !rFill.getHatch().isDefault()
+ || !rFill.getFillGraphic().isDefault())
+ {
+ bool bModulate(css::drawing::TextureMode_MODULATE == aSdr3DObjectAttribute.getTextureMode());
+ bool bFilter(aSdr3DObjectAttribute.getTextureFilter());
+ rtl::Reference<BasePrimitive3D> pNewTexturePrimitive3D;
+
+ if(!rFill.getGradient().isDefault())
+ {
+ // create gradientTexture3D with sublist, add to local aRetval
+ pNewTexturePrimitive3D = new GradientTexturePrimitive3D(
+ rFill.getGradient(),
+ aRetval,
+ rTextureSize,
+ bModulate,
+ bFilter);
+ }
+ else if(!rFill.getHatch().isDefault())
+ {
+ // create hatchTexture3D with sublist, add to local aRetval
+ pNewTexturePrimitive3D = new HatchTexturePrimitive3D(
+ rFill.getHatch(),
+ aRetval,
+ rTextureSize,
+ bModulate,
+ bFilter);
+ }
+ else // if(!rFill.getFillGraphic().isDefault())
+ {
+ // create bitmapTexture3D with sublist, add to local aRetval
+ const basegfx::B2DRange aTexRange(0.0, 0.0, rTextureSize.getX(), rTextureSize.getY());
+
+ pNewTexturePrimitive3D = new BitmapTexturePrimitive3D(
+ rFill.getFillGraphic().createFillGraphicAttribute(aTexRange),
+ aRetval,
+ rTextureSize,
+ bModulate,
+ bFilter);
+ }
+
+ // exchange aRetval content with texture group
+ const Primitive3DReference xRef(pNewTexturePrimitive3D);
+ aRetval = { xRef };
+
+ if(css::drawing::TextureKind2_LUMINANCE == aSdr3DObjectAttribute.getTextureKind())
+ {
+ // use modified color primitive to force textures to gray
+ const basegfx::BColorModifierSharedPtr aBColorModifier =
+ std::make_shared<basegfx::BColorModifier_gray>();
+ const Primitive3DReference xRef2(
+ new ModifiedColorPrimitive3D(
+ aRetval,
+ aBColorModifier));
+
+ aRetval = { xRef2 };
+ }
+ }
+
+ if(0.0 != rFill.getTransparence())
+ {
+ // create UnifiedTransparenceTexturePrimitive3D with sublist and exchange
+ const Primitive3DReference xRef(new UnifiedTransparenceTexturePrimitive3D(rFill.getTransparence(), aRetval));
+ aRetval = { xRef };
+ }
+ else if(!rFillGradient.isDefault())
+ {
+ // create TransparenceTexturePrimitive3D with sublist and exchange
+ const Primitive3DReference xRef(new TransparenceTexturePrimitive3D(rFillGradient, aRetval, rTextureSize));
+ aRetval = { xRef };
+ }
+ }
+
+ return aRetval;
+ }
+
+ Primitive3DContainer createShadowPrimitive3D(
+ const Primitive3DContainer& rSource,
+ const attribute::SdrShadowAttribute& rShadow,
+ bool bShadow3D)
+ {
+ // create Shadow primitives. Uses already created primitives
+ if(!rSource.empty() && !basegfx::fTools::moreOrEqual(rShadow.getTransparence(), 1.0))
+ {
+ // prepare new list for shadow geometry
+ basegfx::B2DHomMatrix aShadowOffset;
+ aShadowOffset.set(0, 2, rShadow.getOffset().getX());
+ aShadowOffset.set(1, 2, rShadow.getOffset().getY());
+
+ // create shadow primitive and add primitives
+ const Primitive3DReference xRef(new ShadowPrimitive3D(aShadowOffset, rShadow.getColor(), rShadow.getTransparence(), bShadow3D, rSource));
+ return { xRef };
+ }
+ else
+ {
+ return Primitive3DContainer();
+ }
+ }
+
+ Primitive3DContainer createHiddenGeometryPrimitives3D(
+ const std::vector< basegfx::B3DPolyPolygon >& r3DPolyPolygonVector,
+ const basegfx::B3DHomMatrix& rObjectTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::Sdr3DObjectAttribute& aSdr3DObjectAttribute)
+ {
+ // create hidden sub-geometry which can be used for HitTest
+ // and BoundRect calculations, but will not be visualized
+ const attribute::SdrFillAttribute aSimplifiedFillAttribute(
+ 0.0,
+ basegfx::BColor(),
+ attribute::FillGradientAttribute(),
+ attribute::FillHatchAttribute(),
+ attribute::SdrFillGraphicAttribute());
+
+ const Primitive3DReference aHidden(
+ new HiddenGeometryPrimitive3D(
+ create3DPolyPolygonFillPrimitives(
+ r3DPolyPolygonVector,
+ rObjectTransform,
+ rTextureSize,
+ aSdr3DObjectAttribute,
+ aSimplifiedFillAttribute,
+ attribute::FillGradientAttribute())));
+
+ return { aHidden };
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx b/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx
new file mode 100644
index 000000000..a7015ff8e
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx
@@ -0,0 +1,982 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrextrudelathetools3d.hxx>
+
+#include <osl/diagnose.h>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <basegfx/range/b3drange.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <numeric>
+
+
+// decomposition helpers for extrude/lathe (rotation) objects
+
+namespace
+{
+
+ // common helpers
+
+ basegfx::B2DPolyPolygon impScalePolyPolygonOnCenter(
+ const basegfx::B2DPolyPolygon& rSource,
+ double fScale)
+ {
+ basegfx::B2DPolyPolygon aRetval(rSource);
+
+ if(!basegfx::fTools::equalZero(fScale))
+ {
+ const basegfx::B2DRange aRange(basegfx::utils::getRange(rSource));
+ const basegfx::B2DPoint aCenter(aRange.getCenter());
+ basegfx::B2DHomMatrix aTrans;
+
+ aTrans.translate(-aCenter.getX(), -aCenter.getY());
+ aTrans.scale(fScale, fScale);
+ aTrans.translate(aCenter.getX(), aCenter.getY());
+ aRetval.transform(aTrans);
+ }
+
+ return aRetval;
+ }
+
+ void impGetOuterPolyPolygon(
+ basegfx::B2DPolyPolygon& rPolygon,
+ basegfx::B2DPolyPolygon& rOuterPolyPolygon,
+ double fOffset,
+ bool bCharacterMode)
+ {
+ rOuterPolyPolygon = rPolygon;
+
+ if(!basegfx::fTools::more(fOffset, 0.0))
+ return;
+
+ if(bCharacterMode)
+ {
+ // grow the outside polygon and scale all polygons to original size. This is done
+ // to avoid a shrink which potentially would lead to self-intersections, but changes
+ // the original polygon -> not a precision step, so e.g. not usable for charts
+ const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolygon));
+ rPolygon = basegfx::utils::growInNormalDirection(rPolygon, fOffset);
+ const basegfx::B2DRange aGrownRange(basegfx::utils::getRange(rPolygon));
+ const double fScaleX(basegfx::fTools::equalZero(aGrownRange.getWidth()) ? 1.0 : aRange.getWidth() / aGrownRange.getWidth());
+ const double fScaleY(basegfx::fTools::equalZero(aGrownRange.getHeight())? 1.0 : aRange.getHeight() / aGrownRange.getHeight());
+ basegfx::B2DHomMatrix aScaleTrans;
+
+ aScaleTrans.translate(-aGrownRange.getMinX(), -aGrownRange.getMinY());
+ aScaleTrans.scale(fScaleX, fScaleY);
+ aScaleTrans.translate(aRange.getMinX(), aRange.getMinY());
+ rPolygon.transform(aScaleTrans);
+ rOuterPolyPolygon.transform(aScaleTrans);
+ }
+ else
+ {
+ // use more precision, shrink the outer polygons. Since this may lead to self-intersections,
+ // some kind of correction should be applied here after that step
+ rOuterPolyPolygon = basegfx::utils::growInNormalDirection(rPolygon, -fOffset);
+ // basegfx::utils::correctGrowShrinkPolygonPair(rPolygon, rOuterPolyPolygon);
+ }
+ }
+
+ void impAddInBetweenFill(
+ basegfx::B3DPolyPolygon& rTarget,
+ const basegfx::B3DPolyPolygon& rPolA,
+ const basegfx::B3DPolyPolygon& rPolB,
+ double fTexVerStart,
+ double fTexVerStop,
+ bool bCreateNormals,
+ bool bCreateTextureCoordinates)
+ {
+ OSL_ENSURE(rPolA.count() == rPolB.count(), "impAddInBetweenFill: unequally sized polygons (!)");
+ const sal_uInt32 nPolygonCount(std::min(rPolA.count(), rPolB.count()));
+
+ for(sal_uInt32 a(0); a < nPolygonCount; a++)
+ {
+ const basegfx::B3DPolygon& aSubA(rPolA.getB3DPolygon(a));
+ const basegfx::B3DPolygon& aSubB(rPolB.getB3DPolygon(a));
+ OSL_ENSURE(aSubA.count() == aSubB.count(), "impAddInBetweenFill: unequally sized polygons (!)");
+ const sal_uInt32 nPointCount(std::min(aSubA.count(), aSubB.count()));
+
+ if(nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(aSubA.isClosed() ? nPointCount : nPointCount - 1);
+ double fTexHorMultiplicatorA(0.0), fTexHorMultiplicatorB(0.0);
+ double fPolygonPosA(0.0), fPolygonPosB(0.0);
+
+ if(bCreateTextureCoordinates)
+ {
+ const double fPolygonLengthA(basegfx::utils::getLength(aSubA));
+ fTexHorMultiplicatorA = basegfx::fTools::equalZero(fPolygonLengthA) ? 1.0 : 1.0 / fPolygonLengthA;
+
+ const double fPolygonLengthB(basegfx::utils::getLength(aSubB));
+ fTexHorMultiplicatorB = basegfx::fTools::equalZero(fPolygonLengthB) ? 1.0 : 1.0 / fPolygonLengthB;
+ }
+
+ for(sal_uInt32 b(0); b < nEdgeCount; b++)
+ {
+ const sal_uInt32 nIndexA(b);
+ const sal_uInt32 nIndexB((b + 1) % nPointCount);
+
+ const basegfx::B3DPoint aStartA(aSubA.getB3DPoint(nIndexA));
+ const basegfx::B3DPoint aEndA(aSubA.getB3DPoint(nIndexB));
+ const basegfx::B3DPoint aStartB(aSubB.getB3DPoint(nIndexA));
+ const basegfx::B3DPoint aEndB(aSubB.getB3DPoint(nIndexB));
+
+ basegfx::B3DPolygon aNew;
+ aNew.setClosed(true);
+
+ aNew.append(aStartA);
+ aNew.append(aStartB);
+ aNew.append(aEndB);
+ aNew.append(aEndA);
+
+ if(bCreateNormals)
+ {
+ aNew.setNormal(0, aSubA.getNormal(nIndexA));
+ aNew.setNormal(1, aSubB.getNormal(nIndexA));
+ aNew.setNormal(2, aSubB.getNormal(nIndexB));
+ aNew.setNormal(3, aSubA.getNormal(nIndexB));
+ }
+
+ if(bCreateTextureCoordinates)
+ {
+ const double fRelTexAL(fPolygonPosA * fTexHorMultiplicatorA);
+ const double fEdgeLengthA(basegfx::B3DVector(aEndA - aStartA).getLength());
+ fPolygonPosA += fEdgeLengthA;
+ const double fRelTexAR(fPolygonPosA * fTexHorMultiplicatorA);
+
+ const double fRelTexBL(fPolygonPosB * fTexHorMultiplicatorB);
+ const double fEdgeLengthB(basegfx::B3DVector(aEndB - aStartB).getLength());
+ fPolygonPosB += fEdgeLengthB;
+ const double fRelTexBR(fPolygonPosB * fTexHorMultiplicatorB);
+
+ aNew.setTextureCoordinate(0, basegfx::B2DPoint(fRelTexAL, fTexVerStart));
+ aNew.setTextureCoordinate(1, basegfx::B2DPoint(fRelTexBL, fTexVerStop));
+ aNew.setTextureCoordinate(2, basegfx::B2DPoint(fRelTexBR, fTexVerStop));
+ aNew.setTextureCoordinate(3, basegfx::B2DPoint(fRelTexAR, fTexVerStart));
+ }
+
+ rTarget.append(aNew);
+ }
+ }
+ }
+ }
+
+ void impSetNormal(
+ basegfx::B3DPolyPolygon& rCandidate,
+ const basegfx::B3DVector& rNormal)
+ {
+ for(sal_uInt32 a(0); a < rCandidate.count(); a++)
+ {
+ basegfx::B3DPolygon aSub(rCandidate.getB3DPolygon(a));
+
+ for(sal_uInt32 b(0); b < aSub.count(); b++)
+ {
+ aSub.setNormal(b, rNormal);
+ }
+
+ rCandidate.setB3DPolygon(a, aSub);
+ }
+ }
+
+ void impCreateInBetweenNormals(
+ basegfx::B3DPolyPolygon& rPolA,
+ basegfx::B3DPolyPolygon& rPolB)
+ {
+ OSL_ENSURE(rPolA.count() == rPolB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
+ const sal_uInt32 nPolygonCount(std::min(rPolA.count(), rPolB.count()));
+
+ for(sal_uInt32 a(0); a < nPolygonCount; a++)
+ {
+ basegfx::B3DPolygon aSubA(rPolA.getB3DPolygon(a));
+ basegfx::B3DPolygon aSubB(rPolB.getB3DPolygon(a));
+ OSL_ENSURE(aSubA.count() == aSubB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
+ const sal_uInt32 nPointCount(std::min(aSubA.count(), aSubB.count()));
+
+ if(nPointCount)
+ {
+ basegfx::B3DPoint aPrevA(aSubA.getB3DPoint(nPointCount - 1));
+ basegfx::B3DPoint aCurrA(aSubA.getB3DPoint(0));
+ const bool bClosed(aSubA.isClosed());
+
+ for(sal_uInt32 b(0); b < nPointCount; b++)
+ {
+ const sal_uInt32 nIndNext((b + 1) % nPointCount);
+ const basegfx::B3DPoint aNextA(aSubA.getB3DPoint(nIndNext));
+ const basegfx::B3DPoint aCurrB(aSubB.getB3DPoint(b));
+
+ // vector to back
+ basegfx::B3DVector aDepth(aCurrB - aCurrA);
+ aDepth.normalize();
+
+ if(aDepth.equalZero())
+ {
+ // no difference, try to get depth from next point
+ const basegfx::B3DPoint aNextB(aSubB.getB3DPoint(nIndNext));
+ aDepth = aNextB - aNextA;
+ aDepth.normalize();
+ }
+
+ // vector to left (correct for non-closed lines)
+ const bool bFirstAndNotClosed(!bClosed && 0 == b);
+ basegfx::B3DVector aLeft(bFirstAndNotClosed ? aCurrA - aNextA : aPrevA - aCurrA);
+ aLeft.normalize();
+
+ // create left normal
+ const basegfx::B3DVector aNormalLeft(aDepth.getPerpendicular(aLeft));
+
+ // smooth horizontal normals
+ {
+ // vector to right (correct for non-closed lines)
+ const bool bLastAndNotClosed(!bClosed && b + 1 == nPointCount);
+ basegfx::B3DVector aRight(bLastAndNotClosed ? aCurrA - aPrevA : aNextA - aCurrA);
+ aRight.normalize();
+
+ // create right normal
+ const basegfx::B3DVector aNormalRight(aRight.getPerpendicular(aDepth));
+
+ // create smoothed in-between normal
+ basegfx::B3DVector aNewNormal(aNormalLeft + aNormalRight);
+ aNewNormal.normalize();
+
+ // set as new normal at polygons
+ aSubA.setNormal(b, aNewNormal);
+ aSubB.setNormal(b, aNewNormal);
+ }
+
+ // prepare next step
+ aPrevA = aCurrA;
+ aCurrA = aNextA;
+ }
+
+ rPolA.setB3DPolygon(a, aSubA);
+ rPolB.setB3DPolygon(a, aSubB);
+ }
+ }
+ }
+
+ void impMixNormals(
+ basegfx::B3DPolyPolygon& rPolA,
+ const basegfx::B3DPolyPolygon& rPolB,
+ double fWeightA)
+ {
+ const double fWeightB(1.0 - fWeightA);
+ OSL_ENSURE(rPolA.count() == rPolB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
+ const sal_uInt32 nPolygonCount(std::min(rPolA.count(), rPolB.count()));
+
+ for(sal_uInt32 a(0); a < nPolygonCount; a++)
+ {
+ basegfx::B3DPolygon aSubA(rPolA.getB3DPolygon(a));
+ const basegfx::B3DPolygon& aSubB(rPolB.getB3DPolygon(a));
+ OSL_ENSURE(aSubA.count() == aSubB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
+ const sal_uInt32 nPointCount(std::min(aSubA.count(), aSubB.count()));
+
+ for(sal_uInt32 b(0); b < nPointCount; b++)
+ {
+ const basegfx::B3DVector aVA(aSubA.getNormal(b) * fWeightA);
+ const basegfx::B3DVector aVB(aSubB.getNormal(b) * fWeightB);
+ basegfx::B3DVector aVNew(aVA + aVB);
+ aVNew.normalize();
+ aSubA.setNormal(b, aVNew);
+ }
+
+ rPolA.setB3DPolygon(a, aSubA);
+ }
+ }
+
+ bool impHasCutWith(const basegfx::B2DPolygon& rPoly, const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd)
+ {
+ // polygon is closed, one of the points is a member
+ const sal_uInt32 nPointCount(rPoly.count());
+
+ if(!nPointCount)
+ return false;
+
+ basegfx::B2DPoint aCurrent(rPoly.getB2DPoint(0));
+ const basegfx::B2DVector aVector(rEnd - rStart);
+
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const basegfx::B2DPoint aNext(rPoly.getB2DPoint(nNextIndex));
+ const basegfx::B2DVector aEdgeVector(aNext - aCurrent);
+
+ if(basegfx::utils::findCut(
+ rStart, aVector,
+ aCurrent, aEdgeVector) != CutFlagValue::NONE)
+ {
+ return true;
+ }
+
+ aCurrent = aNext;
+ }
+
+ return false;
+ }
+} // end of anonymous namespace
+
+
+namespace drawinglayer::primitive3d
+{
+ void createLatheSlices(
+ Slice3DVector& rSliceVector,
+ const basegfx::B2DPolyPolygon& rSource,
+ double fBackScale,
+ double fDiagonal,
+ double fRotation,
+ sal_uInt32 nSteps,
+ bool bCharacterMode,
+ bool bCloseFront,
+ bool bCloseBack)
+ {
+ if(basegfx::fTools::equalZero(fRotation) || 0 == nSteps)
+ {
+ // no rotation or no steps, just one plane
+ rSliceVector.emplace_back(rSource, basegfx::B3DHomMatrix());
+ }
+ else
+ {
+ const bool bBackScale(!basegfx::fTools::equal(fBackScale, 1.0));
+ const bool bClosedRotation(!bBackScale && basegfx::fTools::equal(fRotation, 2 * M_PI));
+ basegfx::B2DPolyPolygon aFront(rSource);
+ basegfx::B2DPolyPolygon aBack(rSource);
+ basegfx::B3DHomMatrix aTransformBack;
+ basegfx::B2DPolyPolygon aOuterBack;
+
+ if(bClosedRotation)
+ {
+ bCloseFront = bCloseBack = false;
+ }
+
+ if(bBackScale)
+ {
+ // avoid null zoom
+ if(basegfx::fTools::equalZero(fBackScale))
+ {
+ fBackScale = 0.000001;
+ }
+
+ // back is scaled compared to front, create scaled version
+ aBack = impScalePolyPolygonOnCenter(aBack, fBackScale);
+ }
+
+ if(bCloseFront || bCloseBack)
+ {
+ const basegfx::B2DRange aBaseRange(basegfx::utils::getRange(aFront));
+ const double fOuterLength(aBaseRange.getMaxX() * fRotation);
+ const double fInnerLength(aBaseRange.getMinX() * fRotation);
+ const double fAverageLength((fOuterLength + fInnerLength) * 0.5);
+
+ if(bCloseFront)
+ {
+ const double fOffsetLen((fAverageLength / 12.0) * fDiagonal);
+ basegfx::B2DPolyPolygon aOuterFront;
+ impGetOuterPolyPolygon(aFront, aOuterFront, fOffsetLen, bCharacterMode);
+ basegfx::B3DHomMatrix aTransform;
+ aTransform.translate(0.0, 0.0, fOffsetLen);
+ rSliceVector.emplace_back(aOuterFront, aTransform, SLICETYPE3D_FRONTCAP);
+ }
+
+ if(bCloseBack)
+ {
+ const double fOffsetLen((fAverageLength / 12.0) * fDiagonal);
+ impGetOuterPolyPolygon(aBack, aOuterBack, fOffsetLen, bCharacterMode);
+ aTransformBack.translate(0.0, 0.0, -fOffsetLen);
+ aTransformBack.rotate(0.0, fRotation, 0.0);
+ }
+ }
+
+ // add start polygon (a = 0)
+ if(!bClosedRotation)
+ {
+ rSliceVector.emplace_back(aFront, basegfx::B3DHomMatrix());
+ }
+
+ // create segments (a + 1 .. nSteps)
+ const double fStepSize(1.0 / static_cast<double>(nSteps));
+
+ for(sal_uInt32 a(0); a < nSteps; a++)
+ {
+ const double fStep(static_cast<double>(a + 1) * fStepSize);
+ basegfx::B2DPolyPolygon aNewPoly(bBackScale ? basegfx::utils::interpolate(aFront, aBack, fStep) : aFront);
+ basegfx::B3DHomMatrix aNewMat;
+ aNewMat.rotate(0.0, fRotation * fStep, 0.0);
+ rSliceVector.emplace_back(aNewPoly, aNewMat);
+ }
+
+ if(bCloseBack)
+ {
+ rSliceVector.emplace_back(aOuterBack, aTransformBack, SLICETYPE3D_BACKCAP);
+ }
+ }
+ }
+
+ void createExtrudeSlices(
+ Slice3DVector& rSliceVector,
+ const basegfx::B2DPolyPolygon& rSource,
+ double fBackScale,
+ double fDiagonal,
+ double fDepth,
+ bool bCharacterMode,
+ bool bCloseFront,
+ bool bCloseBack)
+ {
+ if(basegfx::fTools::equalZero(fDepth))
+ {
+ // no depth, just one plane
+ rSliceVector.emplace_back(rSource, basegfx::B3DHomMatrix());
+ }
+ else
+ {
+ // there is depth, create Polygons for front,back and their default depth positions
+ basegfx::B2DPolyPolygon aFront(rSource);
+ basegfx::B2DPolyPolygon aBack(rSource);
+ const bool bBackScale(!basegfx::fTools::equal(fBackScale, 1.0));
+ double fZFront(fDepth); // default depth for aFront
+ double fZBack(0.0); // default depth for aBack
+ basegfx::B2DPolyPolygon aOuterBack;
+
+ if(bBackScale)
+ {
+ // avoid null zoom
+ if(basegfx::fTools::equalZero(fBackScale))
+ {
+ fBackScale = 0.000001;
+ }
+
+ // aFront is scaled compared to aBack, create scaled version
+ aFront = impScalePolyPolygonOnCenter(aFront, fBackScale);
+ }
+
+ if(bCloseFront)
+ {
+ const double fOffset(fDepth * fDiagonal * 0.5);
+ fZFront = fDepth - fOffset;
+ basegfx::B2DPolyPolygon aOuterFront;
+ impGetOuterPolyPolygon(aFront, aOuterFront, fOffset, bCharacterMode);
+ basegfx::B3DHomMatrix aTransformFront;
+ aTransformFront.translate(0.0, 0.0, fDepth);
+ rSliceVector.emplace_back(aOuterFront, aTransformFront, SLICETYPE3D_FRONTCAP);
+ }
+
+ if(bCloseBack)
+ {
+ const double fOffset(fDepth * fDiagonal * 0.5);
+ fZBack = fOffset;
+ impGetOuterPolyPolygon(aBack, aOuterBack, fOffset, bCharacterMode);
+ }
+
+ // add front and back polygons at evtl. changed depths
+ {
+ basegfx::B3DHomMatrix aTransformA, aTransformB;
+
+ aTransformA.translate(0.0, 0.0, fZFront);
+ rSliceVector.emplace_back(aFront, aTransformA);
+
+ aTransformB.translate(0.0, 0.0, fZBack);
+ rSliceVector.emplace_back(aBack, aTransformB);
+ }
+
+ if(bCloseBack)
+ {
+ rSliceVector.emplace_back(aOuterBack, basegfx::B3DHomMatrix(), SLICETYPE3D_BACKCAP);
+ }
+ }
+ }
+
+ basegfx::B3DPolyPolygon extractHorizontalLinesFromSlice(const Slice3DVector& rSliceVector, bool bCloseHorLines)
+ {
+ basegfx::B3DPolyPolygon aRetval;
+ const sal_uInt32 nNumSlices(rSliceVector.size());
+
+ if(nNumSlices)
+ {
+ const sal_uInt32 nSlideSubPolygonCount(rSliceVector[0].getB3DPolyPolygon().count());
+
+ for(sal_uInt32 b(0); b < nSlideSubPolygonCount; b++)
+ {
+ const sal_uInt32 nSubPolygonPointCount(rSliceVector[0].getB3DPolyPolygon().getB3DPolygon(b).count());
+
+ for(sal_uInt32 c(0); c < nSubPolygonPointCount; c++)
+ {
+ basegfx::B3DPolygon aNew;
+
+ for(sal_uInt32 d(0); d < nNumSlices; d++)
+ {
+ const bool bSamePolygonCount(nSlideSubPolygonCount == rSliceVector[d].getB3DPolyPolygon().count());
+ const bool bSamePointCount(nSubPolygonPointCount == rSliceVector[d].getB3DPolyPolygon().getB3DPolygon(b).count());
+
+ if(bSamePolygonCount && bSamePointCount)
+ {
+ aNew.append(rSliceVector[d].getB3DPolyPolygon().getB3DPolygon(b).getB3DPoint(c));
+ }
+ else
+ {
+ OSL_ENSURE(bSamePolygonCount, "Slice tools::PolyPolygon with different Polygon count (!)");
+ OSL_ENSURE(bSamePointCount, "Slice Polygon with different point count (!)");
+ }
+ }
+
+ aNew.setClosed(bCloseHorLines);
+ aRetval.append(aNew);
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ basegfx::B3DPolyPolygon extractVerticalLinesFromSlice(const Slice3DVector& rSliceVector)
+ {
+ basegfx::B3DPolyPolygon aRetval;
+ const sal_uInt32 nNumSlices(rSliceVector.size());
+
+ for(sal_uInt32 a(0); a < nNumSlices; a++)
+ {
+ aRetval.append(rSliceVector[a].getB3DPolyPolygon());
+ }
+
+ return aRetval;
+ }
+
+ void extractPlanesFromSlice(
+ std::vector< basegfx::B3DPolyPolygon >& rFill,
+ const Slice3DVector& rSliceVector,
+ bool bCreateNormals,
+ bool bSmoothNormals,
+ bool bSmoothLids,
+ bool bClosed,
+ double fSmoothNormalsMix,
+ double fSmoothLidsMix,
+ bool bCreateTextureCoordinates,
+ const basegfx::B2DHomMatrix& rTexTransform)
+ {
+ const sal_uInt32 nNumSlices(rSliceVector.size());
+
+ if(!nNumSlices)
+ return;
+
+ // common parameters
+ const sal_uInt32 nLoopCount(bClosed ? nNumSlices : nNumSlices - 1);
+ basegfx::B3DPolyPolygon aEdgeRounding;
+ sal_uInt32 a;
+
+ // texture parameters
+ double fInvTexHeight(1.0);
+ std::vector<double> aTexHeightArray;
+ basegfx::B3DRange aTexRangeFront;
+ basegfx::B3DRange aTexRangeBack;
+
+ if(bCreateTextureCoordinates)
+ {
+ aTexRangeFront = basegfx::utils::getRange(rSliceVector[0].getB3DPolyPolygon());
+ aTexRangeBack = basegfx::utils::getRange(rSliceVector[nNumSlices - 1].getB3DPolyPolygon());
+
+ if(aTexRangeBack.getDepth() > aTexRangeBack.getWidth())
+ {
+ // last polygon is rotated so that depth is bigger than width, exchange X and Z
+ // for making applyDefaultTextureCoordinatesParallel use Z instead of X for
+ // horizontal texture coordinate
+ aTexRangeBack = basegfx::B3DRange(
+ aTexRangeBack.getMinZ(), aTexRangeBack.getMinY(), aTexRangeBack.getMinX(),
+ aTexRangeBack.getMaxZ(), aTexRangeBack.getMaxY(), aTexRangeBack.getMaxX());
+ }
+
+ basegfx::B3DPoint aCenter(basegfx::utils::getRange(rSliceVector[0].getB3DPolyPolygon()).getCenter());
+
+ for(a = 0; a < nLoopCount; a++)
+ {
+ const basegfx::B3DPoint aNextCenter(basegfx::utils::getRange(rSliceVector[(a + 1) % nNumSlices].getB3DPolyPolygon()).getCenter());
+ const double fLength(basegfx::B3DVector(aNextCenter - aCenter).getLength());
+ aTexHeightArray.push_back(fLength);
+ aCenter = aNextCenter;
+ }
+
+ const double fTexHeight(std::accumulate(aTexHeightArray.begin(), aTexHeightArray.end(), 0.0));
+
+ if(!basegfx::fTools::equalZero(fTexHeight))
+ {
+ fInvTexHeight = 1.0 / fTexHeight;
+ }
+ }
+
+ if(nLoopCount)
+ {
+ double fTexHeightPos(0.0);
+ for(a = 0; a < nLoopCount; a++)
+ {
+ const Slice3D& rSliceA(rSliceVector[a]);
+ const Slice3D& rSliceB(rSliceVector[(a + 1) % nNumSlices]);
+ const bool bAcceptPair(SLICETYPE3D_REGULAR == rSliceA.getSliceType() && SLICETYPE3D_REGULAR == rSliceB.getSliceType());
+ basegfx::B3DPolyPolygon aPolA(rSliceA.getB3DPolyPolygon());
+ basegfx::B3DPolyPolygon aPolB(rSliceB.getB3DPolyPolygon());
+
+ if(bAcceptPair)
+ {
+ if(bCreateNormals)
+ {
+ impCreateInBetweenNormals(aPolB, aPolA);
+ }
+
+ {
+ const sal_uInt32 nIndPrev((a + nNumSlices - 1) % nNumSlices);
+ const Slice3D& rSlicePrev(rSliceVector[nIndPrev]);
+ basegfx::B3DPolyPolygon aPrev(rSlicePrev.getB3DPolyPolygon());
+ basegfx::B3DPolyPolygon aPolAA(rSliceA.getB3DPolyPolygon());
+
+ if(SLICETYPE3D_FRONTCAP == rSlicePrev.getSliceType())
+ {
+ basegfx::B3DPolyPolygon aFront(rSlicePrev.getB3DPolyPolygon());
+ const bool bHasSlant(aPolAA != aPrev);
+
+ if(bCreateTextureCoordinates)
+ {
+ aFront = basegfx::utils::applyDefaultTextureCoordinatesParallel(aFront, aTexRangeFront);
+ }
+
+ if(bCreateNormals)
+ {
+ basegfx::B3DVector aNormal(0.0, 0.0, -1.0);
+
+ if(aFront.count())
+ {
+ aNormal = -aFront.getB3DPolygon(0).getNormal();
+ }
+
+ impSetNormal(aFront, aNormal);
+
+ if(bHasSlant)
+ {
+ impCreateInBetweenNormals(aPolAA, aPrev);
+
+ if(bSmoothNormals)
+ {
+ // smooth and copy
+ impMixNormals(aPolA, aPolAA, fSmoothNormalsMix);
+ aPolAA = aPolA;
+ }
+ else
+ {
+ // take over from surface
+ aPolAA = aPolA;
+ }
+
+ if(bSmoothLids)
+ {
+ // smooth and copy
+ impMixNormals(aFront, aPrev, fSmoothLidsMix);
+ aPrev = aFront;
+ }
+ else
+ {
+ // take over from front
+ aPrev = aFront;
+ }
+ }
+ else
+ {
+ if(bSmoothNormals)
+ {
+ // smooth
+ impMixNormals(aPolA, aFront, fSmoothNormalsMix);
+ }
+
+ if(bSmoothLids)
+ {
+ // smooth and copy
+ impMixNormals(aFront, aPolA, fSmoothLidsMix);
+ aPolA = aFront;
+ }
+ }
+ }
+
+ if(bHasSlant)
+ {
+ double fTexStart{};
+ double fTexStop{};
+ if(bCreateTextureCoordinates)
+ {
+ fTexStart = fTexHeightPos * fInvTexHeight;
+ fTexStop = (fTexHeightPos - aTexHeightArray[(a + nLoopCount - 1) % nLoopCount]) * fInvTexHeight;
+ }
+
+ impAddInBetweenFill(aEdgeRounding, aPolAA, aPrev, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates);
+ }
+
+ aFront.flip();
+ rFill.push_back(aFront);
+ }
+ else
+ {
+ if(bCreateNormals && bSmoothNormals && (nIndPrev != a + 1))
+ {
+ impCreateInBetweenNormals(aPolAA, aPrev);
+ impMixNormals(aPolA, aPolAA, 0.5);
+ }
+ }
+ }
+
+ {
+ const sal_uInt32 nIndNext((a + 2) % nNumSlices);
+ const Slice3D& rSliceNext(rSliceVector[nIndNext]);
+ basegfx::B3DPolyPolygon aNext(rSliceNext.getB3DPolyPolygon());
+ basegfx::B3DPolyPolygon aPolBB(rSliceB.getB3DPolyPolygon());
+
+ if(SLICETYPE3D_BACKCAP == rSliceNext.getSliceType())
+ {
+ basegfx::B3DPolyPolygon aBack(rSliceNext.getB3DPolyPolygon());
+ const bool bHasSlant(aPolBB != aNext);
+
+ if(bCreateTextureCoordinates)
+ {
+ aBack = basegfx::utils::applyDefaultTextureCoordinatesParallel(aBack, aTexRangeBack);
+ }
+
+ if(bCreateNormals)
+ {
+ const basegfx::B3DVector aNormal(aBack.count() ? aBack.getB3DPolygon(0).getNormal() : basegfx::B3DVector(0.0, 0.0, 1.0));
+ impSetNormal(aBack, aNormal);
+
+ if(bHasSlant)
+ {
+ impCreateInBetweenNormals(aNext, aPolBB);
+
+ if(bSmoothNormals)
+ {
+ // smooth and copy
+ impMixNormals(aPolB, aPolBB, fSmoothNormalsMix);
+ aPolBB = aPolB;
+ }
+ else
+ {
+ // take over from surface
+ aPolBB = aPolB;
+ }
+
+ if(bSmoothLids)
+ {
+ // smooth and copy
+ impMixNormals(aBack, aNext, fSmoothLidsMix);
+ aNext = aBack;
+ }
+ else
+ {
+ // take over from back
+ aNext = aBack;
+ }
+ }
+ else
+ {
+ if(bSmoothNormals)
+ {
+ // smooth
+ impMixNormals(aPolB, aBack, fSmoothNormalsMix);
+ }
+
+ if(bSmoothLids)
+ {
+ // smooth and copy
+ impMixNormals(aBack, aPolB, fSmoothLidsMix);
+ aPolB = aBack;
+ }
+ }
+ }
+
+ if(bHasSlant)
+ {
+ double fTexStart{};
+ double fTexStop{};
+ if(bCreateTextureCoordinates)
+ {
+ fTexStart = (fTexHeightPos + aTexHeightArray[a] + aTexHeightArray[(a + 1) % nLoopCount]) * fInvTexHeight;
+ fTexStop = (fTexHeightPos + aTexHeightArray[a]) * fInvTexHeight;
+ }
+
+ impAddInBetweenFill(aEdgeRounding, aNext, aPolBB, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates);
+ }
+
+ rFill.push_back(aBack);
+ }
+ else
+ {
+ if(bCreateNormals && bSmoothNormals && (nIndNext != a))
+ {
+ impCreateInBetweenNormals(aNext, aPolBB);
+ impMixNormals(aPolB, aPolBB, 0.5);
+ }
+ }
+ }
+
+ double fTexStart{};
+ double fTexStop{};
+ if(bCreateTextureCoordinates)
+ {
+ fTexStart = (fTexHeightPos + aTexHeightArray[a]) * fInvTexHeight;
+ fTexStop = fTexHeightPos * fInvTexHeight;
+ }
+
+ impAddInBetweenFill(aEdgeRounding, aPolB, aPolA, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates);
+ }
+
+ if(bCreateTextureCoordinates)
+ {
+ fTexHeightPos += aTexHeightArray[a];
+ }
+ }
+ }
+ else
+ {
+ // no loop, but a single slice (1 == nNumSlices), create a filling from the single
+ // front plane
+ const Slice3D& rSlice(rSliceVector[0]);
+ basegfx::B3DPolyPolygon aFront(rSlice.getB3DPolyPolygon());
+
+ if(bCreateTextureCoordinates)
+ {
+ aFront = basegfx::utils::applyDefaultTextureCoordinatesParallel(aFront, aTexRangeFront);
+ }
+
+ if(bCreateNormals)
+ {
+ basegfx::B3DVector aNormal(0.0, 0.0, -1.0);
+
+ if(aFront.count())
+ {
+ aNormal = -aFront.getB3DPolygon(0).getNormal();
+ }
+
+ impSetNormal(aFront, aNormal);
+ }
+
+ aFront.flip();
+ rFill.push_back(aFront);
+ }
+
+ if(bCreateTextureCoordinates)
+ {
+ aEdgeRounding.transformTextureCoordinates(rTexTransform);
+ }
+
+ for(a = 0; a < aEdgeRounding.count(); a++)
+ {
+ rFill.emplace_back(aEdgeRounding.getB3DPolygon(a));
+ }
+ }
+
+ void createReducedOutlines(
+ const geometry::ViewInformation3D& rViewInformation,
+ const basegfx::B3DHomMatrix& rObjectTransform,
+ const basegfx::B3DPolygon& rLoopA,
+ const basegfx::B3DPolygon& rLoopB,
+ basegfx::B3DPolyPolygon& rTarget)
+ {
+ const sal_uInt32 nPointCount(rLoopA.count());
+
+ // with identical polygons there are no outlines
+ if(rLoopA == rLoopB)
+ return;
+
+ if(!(nPointCount && nPointCount == rLoopB.count()))
+ return;
+
+ const basegfx::B3DHomMatrix aObjectTransform(rViewInformation.getObjectToView() * rObjectTransform);
+ const basegfx::B2DPolygon a2DLoopA(basegfx::utils::createB2DPolygonFromB3DPolygon(rLoopA, aObjectTransform));
+ const basegfx::B2DPolygon a2DLoopB(basegfx::utils::createB2DPolygonFromB3DPolygon(rLoopB, aObjectTransform));
+ const basegfx::B2DPoint a2DCenterA(a2DLoopA.getB2DRange().getCenter());
+ const basegfx::B2DPoint a2DCenterB(a2DLoopB.getB2DRange().getCenter());
+
+ // without detectable Y-Axis there are no outlines
+ if(a2DCenterA.equal(a2DCenterB))
+ return;
+
+ // search for outmost left and right inter-loop-edges which do not cut the loops
+ const basegfx::B2DPoint aCommonCenter(basegfx::average(a2DCenterA, a2DCenterB));
+ const basegfx::B2DVector aAxisVector(a2DCenterA - a2DCenterB);
+ double fMaxLeft(0.0);
+ double fMaxRight(0.0);
+ sal_uInt32 nIndexLeft(0);
+ sal_uInt32 nIndexRight(0);
+
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const basegfx::B2DPoint aStart(a2DLoopA.getB2DPoint(a));
+ const basegfx::B2DPoint aEnd(a2DLoopB.getB2DPoint(a));
+ const basegfx::B2DPoint aMiddle(basegfx::average(aStart, aEnd));
+
+ if(!basegfx::utils::isInside(a2DLoopA, aMiddle))
+ {
+ if(!basegfx::utils::isInside(a2DLoopB, aMiddle))
+ {
+ if(!impHasCutWith(a2DLoopA, aStart, aEnd))
+ {
+ if(!impHasCutWith(a2DLoopB, aStart, aEnd))
+ {
+ const basegfx::B2DVector aCandidateVector(aMiddle - aCommonCenter);
+ const double fCross(aCandidateVector.cross(aAxisVector));
+ const double fDistance(aCandidateVector.getLength());
+
+ if(fCross > 0.0)
+ {
+ if(fDistance > fMaxLeft)
+ {
+ fMaxLeft = fDistance;
+ nIndexLeft = a;
+ }
+ }
+ else if(fCross < 0.0)
+ {
+ if(fDistance > fMaxRight)
+ {
+ fMaxRight = fDistance;
+ nIndexRight = a;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(fMaxLeft != 0.0)
+ {
+ basegfx::B3DPolygon aToBeAdded;
+ aToBeAdded.append(rLoopA.getB3DPoint(nIndexLeft));
+ aToBeAdded.append(rLoopB.getB3DPoint(nIndexLeft));
+ rTarget.append(aToBeAdded);
+ }
+
+ if(fMaxRight != 0.0)
+ {
+ basegfx::B3DPolygon aToBeAdded;
+ aToBeAdded.append(rLoopA.getB3DPoint(nIndexRight));
+ aToBeAdded.append(rLoopB.getB3DPoint(nIndexRight));
+ rTarget.append(aToBeAdded);
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx
new file mode 100644
index 000000000..919fbcf5a
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx
@@ -0,0 +1,502 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cmath>
+
+#include <drawinglayer/primitive3d/sdrextrudeprimitive3d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer SdrExtrudePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ Primitive3DContainer aRetval;
+
+ // get slices
+ const Slice3DVector& rSliceVector = getSlices();
+
+ if(!rSliceVector.empty())
+ {
+ sal_uInt32 a;
+
+ // decide what to create
+ const css::drawing::NormalsKind eNormalsKind(getSdr3DObjectAttribute().getNormalsKind());
+ const bool bCreateNormals(css::drawing::NormalsKind_SPECIFIC == eNormalsKind);
+ const bool bCreateTextureCoordinatesX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bCreateTextureCoordinatesY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionY());
+ basegfx::B2DHomMatrix aTexTransform;
+
+ if(!getSdrLFSAttribute().getFill().isDefault() && (bCreateTextureCoordinatesX || bCreateTextureCoordinatesY))
+ {
+ const basegfx::B2DPolygon aFirstPolygon(maCorrectedPolyPolygon.getB2DPolygon(0));
+ const double fFrontLength(basegfx::utils::getLength(aFirstPolygon));
+ const double fFrontArea(basegfx::utils::getArea(aFirstPolygon));
+ const double fSqrtFrontArea(sqrt(fFrontArea));
+ double fRelativeTextureWidth = basegfx::fTools::equalZero(fSqrtFrontArea) ? 1.0 : fFrontLength / fSqrtFrontArea;
+ fRelativeTextureWidth = std::trunc(fRelativeTextureWidth - 0.5);
+
+ if(fRelativeTextureWidth < 1.0)
+ {
+ fRelativeTextureWidth = 1.0;
+ }
+
+ aTexTransform.translate(-0.5, -0.5);
+ aTexTransform.scale(-1.0, -1.0);
+ aTexTransform.translate(0.5, 0.5);
+ aTexTransform.scale(fRelativeTextureWidth, 1.0);
+ }
+
+ // create geometry
+ std::vector< basegfx::B3DPolyPolygon > aFill;
+ extractPlanesFromSlice(aFill, rSliceVector,
+ bCreateNormals, getSmoothNormals(), getSmoothLids(), false,
+ 0.5, 0.6, bCreateTextureCoordinatesX || bCreateTextureCoordinatesY, aTexTransform);
+
+ // get full range
+ const basegfx::B3DRange aRange(getRangeFrom3DGeometry(aFill));
+
+ // normal creation
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ if(css::drawing::NormalsKind_SPHERE == eNormalsKind)
+ {
+ applyNormalsKindSphereTo3DGeometry(aFill, aRange);
+ }
+ else if(css::drawing::NormalsKind_FLAT == eNormalsKind)
+ {
+ applyNormalsKindFlatTo3DGeometry(aFill);
+ }
+
+ if(getSdr3DObjectAttribute().getNormalsInvert())
+ {
+ applyNormalsInvertTo3DGeometry(aFill);
+ }
+ }
+
+ // texture coordinates
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ applyTextureTo3DGeometry(
+ getSdr3DObjectAttribute().getTextureProjectionX(),
+ getSdr3DObjectAttribute().getTextureProjectionY(),
+ aFill,
+ aRange,
+ getTextureSize());
+ }
+
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // add fill
+ aRetval = create3DPolyPolygonFillPrimitives(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute(),
+ getSdrLFSAttribute().getFill(),
+ getSdrLFSAttribute().getFillFloatTransGradient());
+ }
+ else
+ {
+ // create simplified 3d hit test geometry
+ aRetval = createHiddenGeometryPrimitives3D(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute());
+ }
+
+ // add line
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ if(getSdr3DObjectAttribute().getReducedLineGeometry())
+ {
+ // create geometric outlines with reduced line geometry for chart.
+ const basegfx::B3DPolyPolygon aVerLine(extractVerticalLinesFromSlice(rSliceVector));
+ const sal_uInt32 nCount(aVerLine.count());
+ basegfx::B3DPolyPolygon aReducedLoops;
+ basegfx::B3DPolyPolygon aNewLineGeometry;
+
+ // sort out doubles (front and back planes when no edge rounding is done). Since
+ // this is a line geometry merged from PolyPolygons, loop over all Polygons
+ for(a = 0; a < nCount; a++)
+ {
+ const sal_uInt32 nReducedCount(aReducedLoops.count());
+ const basegfx::B3DPolygon& aCandidate(aVerLine.getB3DPolygon(a));
+ bool bAdd(true);
+
+ if(nReducedCount)
+ {
+ for(sal_uInt32 b(0); bAdd && b < nReducedCount; b++)
+ {
+ if(aCandidate == aReducedLoops.getB3DPolygon(b))
+ {
+ bAdd = false;
+ }
+ }
+ }
+
+ if(bAdd)
+ {
+ aReducedLoops.append(aCandidate);
+ }
+ }
+
+ // from here work with reduced loops and reduced count without changing them
+ const sal_uInt32 nReducedCount(aReducedLoops.count());
+
+ if(nReducedCount > 1)
+ {
+ for(sal_uInt32 b(1); b < nReducedCount; b++)
+ {
+ // get loop pair
+ const basegfx::B3DPolygon& aCandA(aReducedLoops.getB3DPolygon(b - 1));
+ const basegfx::B3DPolygon& aCandB(aReducedLoops.getB3DPolygon(b));
+
+ // for each loop pair create the connection edges
+ createReducedOutlines(
+ rViewInformation,
+ getTransform(),
+ aCandA,
+ aCandB,
+ aNewLineGeometry);
+ }
+ }
+
+ // add reduced loops themselves
+ aNewLineGeometry.append(aReducedLoops);
+
+ // to create vertical edges at non-C1/C2 steady loops, use maCorrectedPolyPolygon
+ // directly since the 3D Polygons do not support this.
+ //
+ // Unfortunately there is no bezier polygon provided by the chart module; one reason is
+ // that the API for extrude wants a 3D polygon geometry (for historical reasons, i guess)
+ // and those have no beziers. Another reason is that he chart module uses self-created
+ // stuff to create the 2D geometry (in ShapeFactory::createPieSegment), but this geometry
+ // does not contain bezier infos, either. The only way which is possible for now is to 'detect'
+ // candidates for vertical edges of pie segments by looking for the angles in the polygon.
+ //
+ // This is all not very well designed ATM. Ideally, the ReducedLineGeometry is responsible
+ // for creating the outer geometry edges (createReducedOutlines), but for special edges
+ // like the vertical ones for pie center and both start/end, the incarnation with the
+ // knowledge about that it needs to create those and IS a pie segment -> in this case,
+ // the chart itself.
+ const sal_uInt32 nPolyCount(maCorrectedPolyPolygon.count());
+
+ for(sal_uInt32 c(0); c < nPolyCount; c++)
+ {
+ const basegfx::B2DPolygon aCandidate(maCorrectedPolyPolygon.getB2DPolygon(c));
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount > 2)
+ {
+ sal_uInt32 nIndexA(nPointCount);
+ sal_uInt32 nIndexB(nPointCount);
+ sal_uInt32 nIndexC(nPointCount);
+
+ for(sal_uInt32 d(0); d < nPointCount; d++)
+ {
+ const sal_uInt32 nPrevInd((d + nPointCount - 1) % nPointCount);
+ const sal_uInt32 nNextInd((d + 1) % nPointCount);
+ const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(d));
+ const basegfx::B2DVector aPrev(aCandidate.getB2DPoint(nPrevInd) - aPoint);
+ const basegfx::B2DVector aNext(aCandidate.getB2DPoint(nNextInd) - aPoint);
+ const double fAngle(aPrev.angle(aNext));
+
+ // take each angle which deviates more than 10% from going straight as
+ // special edge. This will detect the two outer edges of pie segments,
+ // but not always the center one (think about a near 180 degree pie)
+ if(M_PI - fabs(fAngle) > M_PI * 0.1)
+ {
+ if(nPointCount == nIndexA)
+ {
+ nIndexA = d;
+ }
+ else if(nPointCount == nIndexB)
+ {
+ nIndexB = d;
+ }
+ else if(nPointCount == nIndexC)
+ {
+ nIndexC = d;
+ d = nPointCount;
+ }
+ }
+ }
+
+ const bool bIndexAUsed(nIndexA != nPointCount);
+ const bool bIndexBUsed(nIndexB != nPointCount);
+ bool bIndexCUsed(nIndexC != nPointCount);
+
+ if(bIndexCUsed)
+ {
+ // already three special edges found, so the center one was already detected
+ // and does not need to be searched
+ }
+ else if(bIndexAUsed && bIndexBUsed)
+ {
+ // outer edges detected (they are approx. 90 degrees), but center one not.
+ // Look with the knowledge that it's in-between the two found ones
+ if(((nIndexA + 2) % nPointCount) == nIndexB)
+ {
+ nIndexC = (nIndexA + 1) % nPointCount;
+ }
+ else if(((nIndexA + nPointCount - 2) % nPointCount) == nIndexB)
+ {
+ nIndexC = (nIndexA + nPointCount - 1) % nPointCount;
+ }
+
+ bIndexCUsed = (nIndexC != nPointCount);
+ }
+
+ if(bIndexAUsed)
+ {
+ const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexA));
+ const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0);
+ const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth());
+ basegfx::B3DPolygon aToBeAdded;
+
+ aToBeAdded.append(aStart);
+ aToBeAdded.append(aEnd);
+ aNewLineGeometry.append(aToBeAdded);
+ }
+
+ if(bIndexBUsed)
+ {
+ const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexB));
+ const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0);
+ const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth());
+ basegfx::B3DPolygon aToBeAdded;
+
+ aToBeAdded.append(aStart);
+ aToBeAdded.append(aEnd);
+ aNewLineGeometry.append(aToBeAdded);
+ }
+
+ if(bIndexCUsed)
+ {
+ const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexC));
+ const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0);
+ const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth());
+ basegfx::B3DPolygon aToBeAdded;
+
+ aToBeAdded.append(aStart);
+ aToBeAdded.append(aEnd);
+ aNewLineGeometry.append(aToBeAdded);
+ }
+ }
+ }
+
+ // append loops themselves
+ aNewLineGeometry.append(aReducedLoops);
+
+ if(aNewLineGeometry.count())
+ {
+ const Primitive3DContainer aLines(create3DPolyPolygonLinePrimitives(
+ aNewLineGeometry, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aLines);
+ }
+ }
+ else
+ {
+ // extract line geometry from slices
+ const basegfx::B3DPolyPolygon aHorLine(extractHorizontalLinesFromSlice(rSliceVector, false));
+ const basegfx::B3DPolyPolygon aVerLine(extractVerticalLinesFromSlice(rSliceVector));
+
+ // add horizontal lines
+ const Primitive3DContainer aHorLines(create3DPolyPolygonLinePrimitives(
+ aHorLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aHorLines);
+
+ // add vertical lines
+ const Primitive3DContainer aVerLines(create3DPolyPolygonLinePrimitives(
+ aVerLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aVerLines);
+ }
+ }
+
+ // add shadow
+ if(!getSdrLFSAttribute().getShadow().isDefault() && !aRetval.empty())
+ {
+ const Primitive3DContainer aShadow(createShadowPrimitive3D(
+ aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
+ aRetval.append(aShadow);
+ }
+ }
+
+ return aRetval;
+ }
+
+ void SdrExtrudePrimitive3D::impCreateSlices()
+ {
+ // prepare the polygon. No double points, correct orientations and a correct
+ // outmost polygon are needed
+ // Also important: subdivide here to ensure equal point count for all slices (!)
+ maCorrectedPolyPolygon = basegfx::utils::adaptiveSubdivideByAngle(getPolyPolygon());
+ maCorrectedPolyPolygon.removeDoublePoints();
+ maCorrectedPolyPolygon = basegfx::utils::correctOrientations(maCorrectedPolyPolygon);
+ maCorrectedPolyPolygon = basegfx::utils::correctOutmostPolygon(maCorrectedPolyPolygon);
+
+ // prepare slices as geometry
+ createExtrudeSlices(maSlices, maCorrectedPolyPolygon, getBackScale(), getDiagonal(), getDepth(), getCharacterMode(), getCloseFront(), getCloseBack());
+ }
+
+ const Slice3DVector& SdrExtrudePrimitive3D::getSlices() const
+ {
+ // This can be made dependent of getSdrLFSAttribute().getFill() and getSdrLFSAttribute().getLine()
+ // again when no longer geometry is needed for non-visible 3D objects as it is now for chart
+ if(getPolyPolygon().count() && maSlices.empty())
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ const_cast< SdrExtrudePrimitive3D& >(*this).impCreateSlices();
+ }
+
+ return maSlices;
+ }
+
+ SdrExtrudePrimitive3D::SdrExtrudePrimitive3D(
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fDepth,
+ double fDiagonal,
+ double fBackScale,
+ bool bSmoothNormals,
+ bool bSmoothLids,
+ bool bCharacterMode,
+ bool bCloseFront,
+ bool bCloseBack)
+ : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
+ maPolyPolygon(rPolyPolygon),
+ mfDepth(fDepth),
+ mfDiagonal(fDiagonal),
+ mfBackScale(fBackScale),
+ mbSmoothNormals(bSmoothNormals),
+ mbSmoothLids(bSmoothLids),
+ mbCharacterMode(bCharacterMode),
+ mbCloseFront(bCloseFront),
+ mbCloseBack(bCloseBack)
+ {
+ // make sure depth is positive
+ if(basegfx::fTools::lessOrEqual(getDepth(), 0.0))
+ {
+ mfDepth = 0.0;
+ }
+
+ // make sure the percentage value getDiagonal() is between 0.0 and 1.0
+ if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0))
+ {
+ mfDiagonal = 0.0;
+ }
+ else if(basegfx::fTools::moreOrEqual(getDiagonal(), 1.0))
+ {
+ mfDiagonal = 1.0;
+ }
+
+ // no close front/back when polygon is not closed
+ if(getPolyPolygon().count() && !getPolyPolygon().getB2DPolygon(0).isClosed())
+ {
+ mbCloseFront = mbCloseBack = false;
+ }
+
+ // no edge rounding when not closing
+ if(!getCloseFront() && !getCloseBack())
+ {
+ mfDiagonal = 0.0;
+ }
+ }
+
+ SdrExtrudePrimitive3D::~SdrExtrudePrimitive3D()
+ {
+ }
+
+ bool SdrExtrudePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(SdrPrimitive3D::operator==(rPrimitive))
+ {
+ const SdrExtrudePrimitive3D& rCompare = static_cast< const SdrExtrudePrimitive3D& >(rPrimitive);
+
+ return (getPolyPolygon() == rCompare.getPolyPolygon()
+ && getDepth() == rCompare.getDepth()
+ && getDiagonal() == rCompare.getDiagonal()
+ && getBackScale() == rCompare.getBackScale()
+ && getSmoothNormals() == rCompare.getSmoothNormals()
+ && getSmoothLids() == rCompare.getSmoothLids()
+ && getCharacterMode() == rCompare.getCharacterMode()
+ && getCloseFront() == rCompare.getCloseFront()
+ && getCloseBack() == rCompare.getCloseBack());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange SdrExtrudePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // use default from sdrPrimitive3D which uses transformation expanded by line width/2
+ // The parent implementation which uses the ranges of the decomposition would be more
+ // correct, but for historical reasons it is necessary to do the old method: To get
+ // the range of the non-transformed geometry and transform it then. This leads to different
+ // ranges where the new method is more correct, but the need to keep the old behaviour
+ // has priority here.
+ return get3DRangeFromSlices(getSlices());
+ }
+
+ Primitive3DContainer SdrExtrudePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ if(getSdr3DObjectAttribute().getReducedLineGeometry())
+ {
+ if(!mpLastRLGViewInformation ||
+ (!getBuffered3DDecomposition().empty()
+ && *mpLastRLGViewInformation != rViewInformation))
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // conditions of last local decomposition with reduced lines have changed. Remember
+ // new one and clear current decompositiopn
+ SdrExtrudePrimitive3D* pThat = const_cast< SdrExtrudePrimitive3D* >(this);
+ pThat->setBuffered3DDecomposition(Primitive3DContainer());
+ pThat->mpLastRLGViewInformation = rViewInformation;
+ }
+ }
+
+ // no test for buffering needed, call parent
+ return SdrPrimitive3D::get3DDecomposition(rViewInformation);
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(SdrExtrudePrimitive3D, PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx
new file mode 100644
index 000000000..2115ebc3a
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx
@@ -0,0 +1,360 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrlatheprimitive3d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer SdrLathePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ Primitive3DContainer aRetval;
+
+ // get slices
+ const Slice3DVector& rSliceVector = getSlices();
+
+ if(!rSliceVector.empty())
+ {
+ const bool bBackScale(!basegfx::fTools::equal(getBackScale(), 1.0));
+ const bool bClosedRotation(!bBackScale && getHorizontalSegments() && basegfx::fTools::equal(getRotation(), 2 * M_PI));
+ sal_uInt32 a;
+
+ // decide what to create
+ const css::drawing::NormalsKind eNormalsKind(getSdr3DObjectAttribute().getNormalsKind());
+ const bool bCreateNormals(css::drawing::NormalsKind_SPECIFIC == eNormalsKind);
+ const bool bCreateTextureCoordinatesX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bCreateTextureCoordinatesY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionY());
+ basegfx::B2DHomMatrix aTexTransform;
+
+ if(!getSdrLFSAttribute().getFill().isDefault()
+ && (bCreateTextureCoordinatesX || bCreateTextureCoordinatesY))
+ {
+ aTexTransform.set(0, 0, 0.0);
+ aTexTransform.set(0, 1, 1.0);
+ aTexTransform.set(1, 0, 1.0);
+ aTexTransform.set(1, 1, 0.0);
+
+ aTexTransform.translate(0.0, -0.5);
+ aTexTransform.scale(1.0, -1.0);
+ aTexTransform.translate(0.0, 0.5);
+ }
+
+ // create geometry
+ std::vector< basegfx::B3DPolyPolygon > aFill;
+ extractPlanesFromSlice(aFill, rSliceVector,
+ bCreateNormals, getSmoothNormals(), getSmoothLids(), bClosedRotation,
+ 0.85, 0.6, bCreateTextureCoordinatesX || bCreateTextureCoordinatesY, aTexTransform);
+
+ // get full range
+ const basegfx::B3DRange aRange(getRangeFrom3DGeometry(aFill));
+
+ // normal creation
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ if(css::drawing::NormalsKind_SPHERE == eNormalsKind)
+ {
+ applyNormalsKindSphereTo3DGeometry(aFill, aRange);
+ }
+ else if(css::drawing::NormalsKind_FLAT == eNormalsKind)
+ {
+ applyNormalsKindFlatTo3DGeometry(aFill);
+ }
+
+ if(getSdr3DObjectAttribute().getNormalsInvert())
+ {
+ applyNormalsInvertTo3DGeometry(aFill);
+ }
+ }
+
+ // texture coordinates
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ applyTextureTo3DGeometry(
+ getSdr3DObjectAttribute().getTextureProjectionX(),
+ getSdr3DObjectAttribute().getTextureProjectionY(),
+ aFill,
+ aRange,
+ getTextureSize());
+ }
+
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // add fill
+ aRetval = create3DPolyPolygonFillPrimitives(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute(),
+ getSdrLFSAttribute().getFill(),
+ getSdrLFSAttribute().getFillFloatTransGradient());
+ }
+ else
+ {
+ // create simplified 3d hit test geometry
+ aRetval = createHiddenGeometryPrimitives3D(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute());
+ }
+
+ // add line
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ if(getSdr3DObjectAttribute().getReducedLineGeometry())
+ {
+ // create geometric outlines with reduced line geometry for chart
+ const basegfx::B3DPolyPolygon aHorLine(extractHorizontalLinesFromSlice(rSliceVector, bClosedRotation));
+ const sal_uInt32 nCount(aHorLine.count());
+ basegfx::B3DPolyPolygon aNewLineGeometry;
+
+ for(a = 1; a < nCount; a++)
+ {
+ // for each loop pair create the connection edges
+ createReducedOutlines(
+ rViewInformation,
+ getTransform(),
+ aHorLine.getB3DPolygon(a - 1),
+ aHorLine.getB3DPolygon(a),
+ aNewLineGeometry);
+ }
+
+ for(a = 0; a < nCount; a++)
+ {
+ // filter hor lines for empty loops (those who have their defining point on the Y-Axis)
+ basegfx::B3DPolygon aCandidate(aHorLine.getB3DPolygon(a));
+ aCandidate.removeDoublePoints();
+
+ if(aCandidate.count())
+ {
+ aNewLineGeometry.append(aCandidate);
+ }
+ }
+
+ if(aNewLineGeometry.count())
+ {
+ const Primitive3DContainer aLines(create3DPolyPolygonLinePrimitives(
+ aNewLineGeometry, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aLines);
+ }
+ }
+ else
+ {
+ // extract line geometry from slices
+ const basegfx::B3DPolyPolygon aHorLine(extractHorizontalLinesFromSlice(rSliceVector, bClosedRotation));
+ const basegfx::B3DPolyPolygon aVerLine(extractVerticalLinesFromSlice(rSliceVector));
+
+ // add horizontal lines
+ const Primitive3DContainer aHorLines(create3DPolyPolygonLinePrimitives(
+ aHorLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aHorLines);
+
+ // add vertical lines
+ const Primitive3DContainer aVerLines(create3DPolyPolygonLinePrimitives(
+ aVerLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aVerLines);
+ }
+ }
+
+ // add shadow
+ if(!getSdrLFSAttribute().getShadow().isDefault()
+ && !aRetval.empty())
+ {
+ const Primitive3DContainer aShadow(createShadowPrimitive3D(
+ aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
+ aRetval.append(aShadow);
+ }
+ }
+
+ return aRetval;
+ }
+
+ void SdrLathePrimitive3D::impCreateSlices()
+ {
+ // prepare the polygon. No double points, correct orientations and a correct
+ // outmost polygon are needed
+ // Also important: subdivide here to ensure equal point count for all slices (!)
+ maCorrectedPolyPolygon = basegfx::utils::adaptiveSubdivideByAngle(getPolyPolygon());
+ maCorrectedPolyPolygon.removeDoublePoints();
+ maCorrectedPolyPolygon = basegfx::utils::correctOrientations(maCorrectedPolyPolygon);
+ maCorrectedPolyPolygon = basegfx::utils::correctOutmostPolygon(maCorrectedPolyPolygon);
+
+ // check edge count of first sub-polygon. If different, reSegment polyPolygon. This ensures
+ // that for polyPolygons, the subPolys 1..n only get reSegmented when polygon 0 is different
+ // at all (and not always)
+ const basegfx::B2DPolygon aSubCandidate(maCorrectedPolyPolygon.getB2DPolygon(0));
+ const sal_uInt32 nSubEdgeCount(aSubCandidate.isClosed() ? aSubCandidate.count() : (aSubCandidate.count() ? aSubCandidate.count() - 1 : 0));
+
+ if(nSubEdgeCount != getVerticalSegments())
+ {
+ maCorrectedPolyPolygon = basegfx::utils::reSegmentPolyPolygon(maCorrectedPolyPolygon, getVerticalSegments());
+ }
+
+ // prepare slices as geometry
+ createLatheSlices(maSlices, maCorrectedPolyPolygon, getBackScale(), getDiagonal(), getRotation(), getHorizontalSegments(), getCharacterMode(), getCloseFront(), getCloseBack());
+ }
+
+ const Slice3DVector& SdrLathePrimitive3D::getSlices() const
+ {
+ // This can be made dependent of getSdrLFSAttribute().getFill() and getSdrLFSAttribute().getLine()
+ // again when no longer geometry is needed for non-visible 3D objects as it is now for chart
+ if(getPolyPolygon().count() && maSlices.empty())
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ const_cast< SdrLathePrimitive3D& >(*this).impCreateSlices();
+ }
+
+ return maSlices;
+ }
+
+ SdrLathePrimitive3D::SdrLathePrimitive3D(
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ sal_uInt32 nHorizontalSegments,
+ sal_uInt32 nVerticalSegments,
+ double fDiagonal,
+ double fBackScale,
+ double fRotation,
+ bool bSmoothNormals,
+ bool bSmoothLids,
+ bool bCharacterMode,
+ bool bCloseFront,
+ bool bCloseBack)
+ : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
+ maPolyPolygon(rPolyPolygon),
+ mnHorizontalSegments(nHorizontalSegments),
+ mnVerticalSegments(nVerticalSegments),
+ mfDiagonal(fDiagonal),
+ mfBackScale(fBackScale),
+ mfRotation(fRotation),
+ mbSmoothNormals(bSmoothNormals),
+ mbSmoothLids(bSmoothLids),
+ mbCharacterMode(bCharacterMode),
+ mbCloseFront(bCloseFront),
+ mbCloseBack(bCloseBack)
+ {
+ // make sure Rotation is positive
+ if(basegfx::fTools::lessOrEqual(getRotation(), 0.0))
+ {
+ mfRotation = 0.0;
+ }
+
+ // make sure the percentage value getDiagonal() is between 0.0 and 1.0
+ if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0))
+ {
+ mfDiagonal = 0.0;
+ }
+ else if(basegfx::fTools::moreOrEqual(getDiagonal(), 1.0))
+ {
+ mfDiagonal = 1.0;
+ }
+
+ // no close front/back when polygon is not closed
+ if(getPolyPolygon().count() && !getPolyPolygon().getB2DPolygon(0).isClosed())
+ {
+ mbCloseFront = mbCloseBack = false;
+ }
+
+ // no edge rounding when not closing
+ if(!getCloseFront() && !getCloseBack())
+ {
+ mfDiagonal = 0.0;
+ }
+ }
+
+ SdrLathePrimitive3D::~SdrLathePrimitive3D()
+ {
+ }
+
+ bool SdrLathePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(SdrPrimitive3D::operator==(rPrimitive))
+ {
+ const SdrLathePrimitive3D& rCompare = static_cast< const SdrLathePrimitive3D& >(rPrimitive);
+
+ return (getPolyPolygon() == rCompare.getPolyPolygon()
+ && getHorizontalSegments() == rCompare.getHorizontalSegments()
+ && getVerticalSegments() == rCompare.getVerticalSegments()
+ && getDiagonal() == rCompare.getDiagonal()
+ && getBackScale() == rCompare.getBackScale()
+ && getRotation() == rCompare.getRotation()
+ && getSmoothNormals() == rCompare.getSmoothNormals()
+ && getSmoothLids() == rCompare.getSmoothLids()
+ && getCharacterMode() == rCompare.getCharacterMode()
+ && getCloseFront() == rCompare.getCloseFront()
+ && getCloseBack() == rCompare.getCloseBack());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange SdrLathePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // use default from sdrPrimitive3D which uses transformation expanded by line width/2
+ // The parent implementation which uses the ranges of the decomposition would be more
+ // correct, but for historical reasons it is necessary to do the old method: To get
+ // the range of the non-transformed geometry and transform it then. This leads to different
+ // ranges where the new method is more correct, but the need to keep the old behaviour
+ // has priority here.
+ return get3DRangeFromSlices(getSlices());
+ }
+
+ Primitive3DContainer SdrLathePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ if(getSdr3DObjectAttribute().getReducedLineGeometry())
+ {
+ if(!mpLastRLGViewInformation ||
+ (!getBuffered3DDecomposition().empty()
+ && *mpLastRLGViewInformation != rViewInformation))
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // conditions of last local decomposition with reduced lines have changed. Remember
+ // new one and clear current decompositiopn
+ SdrLathePrimitive3D* pThat = const_cast< SdrLathePrimitive3D* >(this);
+ pThat->setBuffered3DDecomposition(Primitive3DContainer());
+ pThat->mpLastRLGViewInformation = rViewInformation;
+ }
+ }
+
+ // no test for buffering needed, call parent
+ return SdrPrimitive3D::get3DDecomposition(rViewInformation);
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(SdrLathePrimitive3D, PRIMITIVE3D_ID_SDRLATHEPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx
new file mode 100644
index 000000000..010083b42
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrpolypolygonprimitive3d.hxx>
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer SdrPolyPolygonPrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ Primitive3DContainer aRetval;
+
+ if(getPolyPolygon3D().count())
+ {
+ std::vector< basegfx::B3DPolyPolygon > aFill { getPolyPolygon3D() };
+
+ // get full range
+ const basegfx::B3DRange aRange(getRangeFrom3DGeometry(aFill));
+
+ // #i98295# normal creation
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ if(css::drawing::NormalsKind_SPHERE == getSdr3DObjectAttribute().getNormalsKind())
+ {
+ applyNormalsKindSphereTo3DGeometry(aFill, aRange);
+ }
+ else if(css::drawing::NormalsKind_FLAT == getSdr3DObjectAttribute().getNormalsKind())
+ {
+ applyNormalsKindFlatTo3DGeometry(aFill);
+ }
+
+ if(getSdr3DObjectAttribute().getNormalsInvert())
+ {
+ applyNormalsInvertTo3DGeometry(aFill);
+ }
+ }
+
+ // #i98314# texture coordinates
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ applyTextureTo3DGeometry(
+ getSdr3DObjectAttribute().getTextureProjectionX(),
+ getSdr3DObjectAttribute().getTextureProjectionY(),
+ aFill,
+ aRange,
+ getTextureSize());
+ }
+
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // add fill
+ aRetval = create3DPolyPolygonFillPrimitives(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute(),
+ getSdrLFSAttribute().getFill(),
+ getSdrLFSAttribute().getFillFloatTransGradient());
+ }
+ else
+ {
+ // create simplified 3d hit test geometry
+ aRetval = createHiddenGeometryPrimitives3D(
+ aFill,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute());
+ }
+
+ // add line
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ basegfx::B3DPolyPolygon aLine(getPolyPolygon3D());
+ aLine.clearNormals();
+ aLine.clearTextureCoordinates();
+ const Primitive3DContainer aLines(create3DPolyPolygonLinePrimitives(
+ aLine, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aLines);
+ }
+
+ // add shadow
+ if(!getSdrLFSAttribute().getShadow().isDefault()
+ && !aRetval.empty())
+ {
+ const Primitive3DContainer aShadow(createShadowPrimitive3D(
+ aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
+ aRetval.append(aShadow);
+ }
+ }
+
+ return aRetval;
+ }
+
+ SdrPolyPolygonPrimitive3D::SdrPolyPolygonPrimitive3D(
+ const basegfx::B3DPolyPolygon& rPolyPolygon3D,
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute)
+ : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
+ maPolyPolygon3D(rPolyPolygon3D)
+ {
+ }
+
+ bool SdrPolyPolygonPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(SdrPrimitive3D::operator==(rPrimitive))
+ {
+ const SdrPolyPolygonPrimitive3D& rCompare = static_cast< const SdrPolyPolygonPrimitive3D& >(rPrimitive);
+
+ return (getPolyPolygon3D() == rCompare.getPolyPolygon3D());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange SdrPolyPolygonPrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // added this implementation to make sure that non-visible objects of this
+ // kind will deliver their expansion. If not implemented, it would never deliver
+ // the used space for non-visible objects since the decomposition for that
+ // case will be empty (what is correct). To support chart ATM which relies on
+ // non-visible objects occupying space in 3D, this method was added
+ basegfx::B3DRange aRetval;
+
+ if(getPolyPolygon3D().count())
+ {
+ aRetval = basegfx::utils::getRange(getPolyPolygon3D());
+ aRetval.transform(getTransform());
+
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ const attribute::SdrLineAttribute& rLine = getSdrLFSAttribute().getLine();
+
+ if(!rLine.isDefault() && !basegfx::fTools::equalZero(rLine.getWidth()))
+ {
+ // expand by half LineWidth as tube radius
+ aRetval.grow(rLine.getWidth() / 2.0);
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(SdrPolyPolygonPrimitive3D, PRIMITIVE3D_ID_SDRPOLYPOLYGONPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrprimitive3d.cxx
new file mode 100644
index 000000000..54fd2cf4b
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrprimitive3d.cxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ basegfx::B3DRange SdrPrimitive3D::getStandard3DRange() const
+ {
+ basegfx::B3DRange aUnitRange(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
+ aUnitRange.transform(getTransform());
+
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ const attribute::SdrLineAttribute& rLine = getSdrLFSAttribute().getLine();
+
+ if(!rLine.isDefault() && !basegfx::fTools::equalZero(rLine.getWidth()))
+ {
+ // expand by held LineWidth as tube radius
+ aUnitRange.grow(rLine.getWidth() / 2.0);
+ }
+ }
+
+ return aUnitRange;
+ }
+
+ basegfx::B3DRange SdrPrimitive3D::get3DRangeFromSlices(const Slice3DVector& rSlices) const
+ {
+ basegfx::B3DRange aRetval;
+
+ if(!rSlices.empty())
+ {
+ for(const auto & rSlice : rSlices)
+ {
+ aRetval.expand(basegfx::utils::getRange(rSlice.getB3DPolyPolygon()));
+ }
+
+ aRetval.transform(getTransform());
+
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ const attribute::SdrLineAttribute& rLine = getSdrLFSAttribute().getLine();
+
+ if(!rLine.isDefault() && !basegfx::fTools::equalZero(rLine.getWidth()))
+ {
+ // expand by half LineWidth as tube radius
+ aRetval.grow(rLine.getWidth() / 2.0);
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ SdrPrimitive3D::SdrPrimitive3D(
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute)
+ : maTransform(rTransform),
+ maTextureSize(rTextureSize),
+ maSdrLFSAttribute(rSdrLFSAttribute),
+ maSdr3DObjectAttribute(rSdr3DObjectAttribute)
+ {
+ }
+
+ bool SdrPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive3D::operator==(rPrimitive))
+ {
+ const SdrPrimitive3D& rCompare = static_cast< const SdrPrimitive3D& >(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getTextureSize() == rCompare.getTextureSize()
+ && getSdrLFSAttribute() == rCompare.getSdrLFSAttribute()
+ && getSdr3DObjectAttribute() == rCompare.getSdr3DObjectAttribute());
+ }
+
+ return false;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx
new file mode 100644
index 000000000..c3127261f
--- /dev/null
+++ b/drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/sdrsphereprimitive3d.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <primitive3d/sdrdecompositiontools3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ Primitive3DContainer SdrSpherePrimitive3D::create3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ Primitive3DContainer aRetval;
+ const basegfx::B3DRange aUnitRange(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
+ const bool bCreateNormals(css::drawing::NormalsKind_SPECIFIC == getSdr3DObjectAttribute().getNormalsKind()
+ || css::drawing::NormalsKind_SPHERE == getSdr3DObjectAttribute().getNormalsKind());
+
+ // create unit geometry
+ basegfx::B3DPolyPolygon aFill(basegfx::utils::createSphereFillPolyPolygonFromB3DRange(aUnitRange,
+ getHorizontalSegments(), getVerticalSegments(), bCreateNormals));
+
+ // normal inversion
+ if(!getSdrLFSAttribute().getFill().isDefault()
+ && bCreateNormals
+ && getSdr3DObjectAttribute().getNormalsInvert()
+ && aFill.areNormalsUsed())
+ {
+ // invert normals
+ aFill = basegfx::utils::invertNormals(aFill);
+ }
+
+ // texture coordinates
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // handle texture coordinates X
+ const bool bParallelX(css::drawing::TextureProjectionMode_PARALLEL == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bObjectSpecificX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionX());
+ const bool bSphereX(css::drawing::TextureProjectionMode_SPHERE == getSdr3DObjectAttribute().getTextureProjectionX());
+
+ // handle texture coordinates Y
+ const bool bParallelY(css::drawing::TextureProjectionMode_PARALLEL == getSdr3DObjectAttribute().getTextureProjectionY());
+ const bool bObjectSpecificY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionY());
+ const bool bSphereY(css::drawing::TextureProjectionMode_SPHERE == getSdr3DObjectAttribute().getTextureProjectionY());
+
+ if(bParallelX || bParallelY)
+ {
+ // apply parallel texture coordinates in X and/or Y
+ const basegfx::B3DRange aRange(basegfx::utils::getRange(aFill));
+ aFill = basegfx::utils::applyDefaultTextureCoordinatesParallel(aFill, aRange, bParallelX, bParallelY);
+ }
+
+ if(bSphereX || bObjectSpecificX || bSphereY || bObjectSpecificY)
+ {
+ double fRelativeAngle(0.0);
+
+ if(bObjectSpecificX)
+ {
+ // Since the texture coordinates are (for historical reasons)
+ // different from forced to sphere texture coordinates,
+ // create a old version from it by rotating to old state before applying
+ // the texture coordinates to emulate old behaviour
+ fRelativeAngle = 2 * M_PI * (static_cast<double>((getHorizontalSegments() >> 1) - 1) / static_cast<double>(getHorizontalSegments()));
+ basegfx::B3DHomMatrix aRot;
+ aRot.rotate(0.0, fRelativeAngle, 0.0);
+ aFill.transform(aRot);
+ }
+
+ // apply spherical texture coordinates in X and/or Y
+ const basegfx::B3DRange aRange(basegfx::utils::getRange(aFill));
+ const basegfx::B3DPoint aCenter(aRange.getCenter());
+ aFill = basegfx::utils::applyDefaultTextureCoordinatesSphere(aFill, aCenter,
+ bSphereX || bObjectSpecificX, bSphereY || bObjectSpecificY);
+
+ if(bObjectSpecificX)
+ {
+ // rotate back again
+ basegfx::B3DHomMatrix aRot;
+ aRot.rotate(0.0, -fRelativeAngle, 0.0);
+ aFill.transform(aRot);
+ }
+ }
+
+ // transform texture coordinates to texture size
+ basegfx::B2DHomMatrix aTexMatrix;
+ aTexMatrix.scale(getTextureSize().getX(), getTextureSize().getY());
+ aFill.transformTextureCoordinates(aTexMatrix);
+ }
+
+ // build vector of PolyPolygons
+ std::vector< basegfx::B3DPolyPolygon > a3DPolyPolygonVector;
+
+ for(sal_uInt32 a(0); a < aFill.count(); a++)
+ {
+ a3DPolyPolygonVector.emplace_back(aFill.getB3DPolygon(a));
+ }
+
+ if(!getSdrLFSAttribute().getFill().isDefault())
+ {
+ // add fill
+ aRetval = create3DPolyPolygonFillPrimitives(
+ a3DPolyPolygonVector,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute(),
+ getSdrLFSAttribute().getFill(),
+ getSdrLFSAttribute().getFillFloatTransGradient());
+ }
+ else
+ {
+ // create simplified 3d hit test geometry
+ aRetval = createHiddenGeometryPrimitives3D(
+ a3DPolyPolygonVector,
+ getTransform(),
+ getTextureSize(),
+ getSdr3DObjectAttribute());
+ }
+
+ // add line
+ if(!getSdrLFSAttribute().getLine().isDefault())
+ {
+ basegfx::B3DPolyPolygon aSphere(basegfx::utils::createSpherePolyPolygonFromB3DRange(aUnitRange, getHorizontalSegments(), getVerticalSegments()));
+ const Primitive3DContainer aLines(create3DPolyPolygonLinePrimitives(
+ aSphere, getTransform(), getSdrLFSAttribute().getLine()));
+ aRetval.append(aLines);
+ }
+
+ // add shadow
+ if(!getSdrLFSAttribute().getShadow().isDefault()
+ && !aRetval.empty())
+ {
+ const Primitive3DContainer aShadow(createShadowPrimitive3D(
+ aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
+ aRetval.append(aShadow);
+ }
+
+ return aRetval;
+ }
+
+ SdrSpherePrimitive3D::SdrSpherePrimitive3D(
+ const basegfx::B3DHomMatrix& rTransform,
+ const basegfx::B2DVector& rTextureSize,
+ const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute,
+ sal_uInt32 nHorizontalSegments,
+ sal_uInt32 nVerticalSegments)
+ : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
+ mnHorizontalSegments(nHorizontalSegments),
+ mnVerticalSegments(nVerticalSegments)
+ {
+ }
+
+ bool SdrSpherePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(SdrPrimitive3D::operator==(rPrimitive))
+ {
+ const SdrSpherePrimitive3D& rCompare = static_cast< const SdrSpherePrimitive3D& >(rPrimitive);
+
+ return (getHorizontalSegments() == rCompare.getHorizontalSegments()
+ && getVerticalSegments() == rCompare.getVerticalSegments());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange SdrSpherePrimitive3D::getB3DRange(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ // use default from sdrPrimitive3D which uses transformation expanded by line width/2
+ // The parent implementation which uses the ranges of the decomposition would be more
+ // correct, but for historical reasons it is necessary to do the old method: To get
+ // the range of the non-transformed geometry and transform it then. This leads to different
+ // ranges where the new method is more correct, but the need to keep the old behaviour
+ // has priority here.
+ return getStandard3DRange();
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(SdrSpherePrimitive3D, PRIMITIVE3D_ID_SDRSPHEREPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/shadowprimitive3d.cxx b/drawinglayer/source/primitive3d/shadowprimitive3d.cxx
new file mode 100644
index 000000000..cca2e3c6f
--- /dev/null
+++ b/drawinglayer/source/primitive3d/shadowprimitive3d.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/shadowprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ ShadowPrimitive3D::ShadowPrimitive3D(
+ const basegfx::B2DHomMatrix& rShadowTransform,
+ const basegfx::BColor& rShadowColor,
+ double fShadowTransparence,
+ bool bShadow3D,
+ const Primitive3DContainer& rChildren)
+ : GroupPrimitive3D(rChildren),
+ maShadowTransform(rShadowTransform),
+ maShadowColor(rShadowColor),
+ mfShadowTransparence(fShadowTransparence),
+ mbShadow3D(bShadow3D)
+ {
+ }
+
+ bool ShadowPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(GroupPrimitive3D::operator==(rPrimitive))
+ {
+ const ShadowPrimitive3D& rCompare = static_cast<const ShadowPrimitive3D&>(rPrimitive);
+
+ return (getShadowTransform() == rCompare.getShadowTransform()
+ && getShadowColor() == rCompare.getShadowColor()
+ && getShadowTransparence() == rCompare.getShadowTransparence()
+ && getShadow3D() == rCompare.getShadow3D());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(ShadowPrimitive3D, PRIMITIVE3D_ID_SHADOWPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/textureprimitive3d.cxx b/drawinglayer/source/primitive3d/textureprimitive3d.cxx
new file mode 100644
index 000000000..7c9226bf4
--- /dev/null
+++ b/drawinglayer/source/primitive3d/textureprimitive3d.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <primitive3d/textureprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ TexturePrimitive3D::TexturePrimitive3D(
+ const Primitive3DContainer& rChildren,
+ const basegfx::B2DVector& rTextureSize,
+ bool bModulate, bool bFilter)
+ : GroupPrimitive3D(rChildren),
+ maTextureSize(rTextureSize),
+ mbModulate(bModulate),
+ mbFilter(bFilter)
+ {
+ }
+
+ bool TexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(GroupPrimitive3D::operator==(rPrimitive))
+ {
+ const TexturePrimitive3D& rCompare = static_cast<const TexturePrimitive3D&>(rPrimitive);
+
+ return (getModulate() == rCompare.getModulate()
+ && getFilter() == rCompare.getFilter());
+ }
+
+ return false;
+ }
+
+
+
+ UnifiedTransparenceTexturePrimitive3D::UnifiedTransparenceTexturePrimitive3D(
+ double fTransparence,
+ const Primitive3DContainer& rChildren)
+ : TexturePrimitive3D(rChildren, basegfx::B2DVector(), false, false),
+ mfTransparence(fTransparence)
+ {
+ }
+
+ bool UnifiedTransparenceTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(TexturePrimitive3D::operator==(rPrimitive))
+ {
+ const UnifiedTransparenceTexturePrimitive3D& rCompare = static_cast<const UnifiedTransparenceTexturePrimitive3D&>(rPrimitive);
+
+ return (getTransparence() == rCompare.getTransparence());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange UnifiedTransparenceTexturePrimitive3D::getB3DRange(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ // do not use the fallback to decomposition here since for a correct BoundRect we also
+ // need invisible (1.0 == getTransparence()) geometry; these would be deleted in the decomposition
+ return getChildren().getB3DRange(rViewInformation);
+ }
+
+ Primitive3DContainer UnifiedTransparenceTexturePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
+ {
+ if(0.0 == getTransparence())
+ {
+ // no transparence used, so just use content
+ return getChildren();
+ }
+ else if(getTransparence() > 0.0 && getTransparence() < 1.0)
+ {
+ // create TransparenceTexturePrimitive3D with fixed transparence as replacement
+ const basegfx::BColor aGray(getTransparence(), getTransparence(), getTransparence());
+ const attribute::FillGradientAttribute aFillGradient(attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, 0.0, aGray, aGray);
+ const Primitive3DReference xRef(new TransparenceTexturePrimitive3D(aFillGradient, getChildren(), getTextureSize()));
+ return { xRef };
+ }
+ else
+ {
+ // completely transparent or invalid definition, add nothing
+ return Primitive3DContainer();
+ }
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(UnifiedTransparenceTexturePrimitive3D, PRIMITIVE3D_ID_UNIFIEDTRANSPARENCETEXTUREPRIMITIVE3D)
+
+
+
+ GradientTexturePrimitive3D::GradientTexturePrimitive3D(
+ const attribute::FillGradientAttribute& rGradient,
+ const Primitive3DContainer& rChildren,
+ const basegfx::B2DVector& rTextureSize,
+ bool bModulate,
+ bool bFilter)
+ : TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter),
+ maGradient(rGradient)
+ {
+ }
+
+ bool GradientTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(TexturePrimitive3D::operator==(rPrimitive))
+ {
+ const GradientTexturePrimitive3D& rCompare = static_cast<const GradientTexturePrimitive3D&>(rPrimitive);
+
+ return (getGradient() == rCompare.getGradient());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(GradientTexturePrimitive3D, PRIMITIVE3D_ID_GRADIENTTEXTUREPRIMITIVE3D)
+
+
+
+ BitmapTexturePrimitive3D::BitmapTexturePrimitive3D(
+ const attribute::FillGraphicAttribute& rFillGraphicAttribute,
+ const Primitive3DContainer& rChildren,
+ const basegfx::B2DVector& rTextureSize,
+ bool bModulate, bool bFilter)
+ : TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter),
+ maFillGraphicAttribute(rFillGraphicAttribute)
+ {
+ }
+
+ bool BitmapTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(TexturePrimitive3D::operator==(rPrimitive))
+ {
+ const BitmapTexturePrimitive3D& rCompare = static_cast<const BitmapTexturePrimitive3D&>(rPrimitive);
+
+ return (getFillGraphicAttribute() == rCompare.getFillGraphicAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(BitmapTexturePrimitive3D, PRIMITIVE3D_ID_BITMAPTEXTUREPRIMITIVE3D)
+
+
+
+ TransparenceTexturePrimitive3D::TransparenceTexturePrimitive3D(
+ const attribute::FillGradientAttribute& rGradient,
+ const Primitive3DContainer& rChildren,
+ const basegfx::B2DVector& rTextureSize)
+ : GradientTexturePrimitive3D(rGradient, rChildren, rTextureSize, false, false)
+ {
+ }
+
+ bool TransparenceTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ return GradientTexturePrimitive3D::operator==(rPrimitive);
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(TransparenceTexturePrimitive3D, PRIMITIVE3D_ID_TRANSPARENCETEXTUREPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive3d/transformprimitive3d.cxx b/drawinglayer/source/primitive3d/transformprimitive3d.cxx
new file mode 100644
index 000000000..1ddb919bf
--- /dev/null
+++ b/drawinglayer/source/primitive3d/transformprimitive3d.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive3d
+{
+ TransformPrimitive3D::TransformPrimitive3D(
+ const basegfx::B3DHomMatrix& rTransformation,
+ const Primitive3DContainer& rChildren)
+ : GroupPrimitive3D(rChildren),
+ maTransformation(rTransformation)
+ {
+ }
+
+ bool TransformPrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
+ {
+ if(GroupPrimitive3D::operator==(rPrimitive))
+ {
+ const TransformPrimitive3D& rCompare = static_cast< const TransformPrimitive3D& >(rPrimitive);
+
+ return (getTransformation() == rCompare.getTransformation());
+ }
+
+ return false;
+ }
+
+ basegfx::B3DRange TransformPrimitive3D::getB3DRange(const geometry::ViewInformation3D& rViewInformation) const
+ {
+ basegfx::B3DRange aRetval(getChildren().getB3DRange(rViewInformation));
+ aRetval.transform(getTransformation());
+ return aRetval;
+ }
+
+ // provide unique ID
+ ImplPrimitive3DIDBlock(TransformPrimitive3D, PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */