diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /drawinglayer/source/primitive3d | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drawinglayer/source/primitive3d')
19 files changed, 4866 insertions, 0 deletions
diff --git a/drawinglayer/source/primitive3d/baseprimitive3d.cxx b/drawinglayer/source/primitive3d/baseprimitive3d.cxx new file mode 100644 index 000000000..4a69c7cc0 --- /dev/null +++ b/drawinglayer/source/primitive3d/baseprimitive3d.cxx @@ -0,0 +1,202 @@ +/* -*- 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() + : BasePrimitive3D(), + maBuffered3DDecomposition() + { + } + + 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()) + { + // try to get C++ implementation base + const BasePrimitive3D* pCandidate(dynamic_cast< BasePrimitive3D* >(rCandidate.get())); + + if(pCandidate) + { + // use it if possible + aRetval.expand(pCandidate->getB3DRange(aViewInformation)); + } + else + { + // use UNO API call instead + const uno::Sequence< beans::PropertyValue >& rViewParameters(aViewInformation.getViewInformationSequence()); + aRetval.expand(basegfx::unotools::b3DRectangleFromRealRectangle3D(rCandidate->getRange(rViewParameters))); + } + } + + 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(dynamic_cast< const BasePrimitive3D* >(rxA.get())); + const BasePrimitive3D* pB(dynamic_cast< const BasePrimitive3D* >(rxB.get())); + + if(!pA || !pB) + { + return false; + } + + 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..7a6254b00 --- /dev/null +++ b/drawinglayer/source/primitive3d/groupprimitive3d.cxx @@ -0,0 +1,62 @@ +/* -*- 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 ) + : BasePrimitive3D(), + 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..2ea280b68 --- /dev/null +++ b/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx @@ -0,0 +1,320 @@ +/* -*- 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()) + { + // try to cast to BasePrimitive2D implementation + const BasePrimitive3D* pBasePrimitive = dynamic_cast< const BasePrimitive3D* >(xReference.get()); + + if(pBasePrimitive) + { + // 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 - F_PI4); + + aHatch.appendTransformations(aMatrices); + + [[fallthrough]]; + } + case attribute::HatchStyle::Double: + { + // rotated 90 degrees + texture::GeoTexSvxHatch aHatch( + aOutlineRange, + aOutlineRange, + getHatch().getDistance(), + fAngle - F_PI2); + + 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; + } + } + } + else + { + // unknown implementation, add to result + aDestination.push_back(xReference); + } + } + } + + // 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), + maBuffered3DDecomposition() + { + } + + 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..ac820546b --- /dev/null +++ b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx @@ -0,0 +1,150 @@ +/* -*- 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) + : BasePrimitive3D(), + 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) + : BufferedDecompositionPrimitive3D(), + 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..177d829ef --- /dev/null +++ b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx @@ -0,0 +1,786 @@ +/* -*- 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 <rtl/instance.hxx> + + +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; + ::osl::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 + ::osl::MutexGuard 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(F_2PI / 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; + } + }; + + struct theTubeBuffer : + public rtl::Static< TubeBuffer, theTubeBuffer > {}; + + Primitive3DContainer getLineTubeSegments( + sal_uInt32 nSegments, + const attribute::MaterialAttribute3D& rMaterial) + { + // static data for buffered tube primitives + TubeBuffer &rTheBuffer = theTubeBuffer::get(); + return rTheBuffer.getLineTubeSegments(nSegments, rMaterial); + } + + class CapBuffer + { + private: + // data for buffered cap primitives + Primitive3DContainer m_aLineCapList; + sal_uInt32 m_nLineCapSegments; + attribute::MaterialAttribute3D m_aLineMaterial; + ::osl::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 + ::osl::MutexGuard 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(F_2PI / 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; + } + }; + + struct theCapBuffer : + public rtl::Static< CapBuffer, theCapBuffer > {}; + + Primitive3DContainer getLineCapSegments( + sal_uInt32 nSegments, + const attribute::MaterialAttribute3D& rMaterial) + { + // static data for buffered cap primitives + CapBuffer &rTheBuffer = theCapBuffer::get(); + return rTheBuffer.getLineCapSegments(nSegments, rMaterial); + } + + class CapRoundBuffer + { + private: + // data for buffered capround primitives + Primitive3DContainer m_aLineCapRoundList; + sal_uInt32 m_nLineCapRoundSegments; + attribute::MaterialAttribute3D m_aLineMaterial; + ::osl::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 + ::osl::MutexGuard 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, + F_PI2, 0.0, + 0.0, F_2PI)); + 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, F_PI2); + 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; + } + + }; + + struct theCapRoundBuffer : + public rtl::Static< CapRoundBuffer, theCapRoundBuffer > {}; + + + Primitive3DContainer getLineCapRoundSegments( + sal_uInt32 nSegments, + const attribute::MaterialAttribute3D& rMaterial) + { + // static data for buffered cap primitives + CapRoundBuffer &rTheBuffer = theCapRoundBuffer::get(); + return rTheBuffer.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 / F_2PI) * static_cast<double>(nSegments))); + + if(nHorSeg) + { + // create half-sphere + const basegfx::B3DPolyPolygon aSphere(basegfx::utils::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, F_PI2, -F_PI2, 0.0, fAngle)); + + for(sal_uInt32 a(0); a < aSphere.count(); a++) + { + const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a)); + const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon); + BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false); + aResultVector.push_back(pNew); + } + } + 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(F_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(-F_PI2); + 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); + BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false); + aResultVector.push_back(pNew); + } + + 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); + BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false); + aResultVector.push_back(pNew); + } + + // 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); + } + + TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aCapTrans, aSequence); + aResultVector.push_back(pNewTransformedA); + } + 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 .. F_PI2 + 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, F_PI2, 0.0); + aSphereTrans.rotate(F_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, F_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 + PolygonHairlinePrimitive3D* pNew = new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor()); + aResultVector.push_back(pNew); + } + } + + // 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), + maLast3DDecomposition(), + 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..db137b47e --- /dev/null +++ b/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx @@ -0,0 +1,65 @@ +/* -*- 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) + : BasePrimitive3D(), + 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..826583b14 --- /dev/null +++ b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx @@ -0,0 +1,323 @@ +/* -*- 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> + + +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(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()); + BasePrimitive3D* pNewTexturePrimitive3D = nullptr; + + 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..afe030ac7 --- /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) + { + 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, F_2PI)); + 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..b7d1c8abd --- /dev/null +++ b/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx @@ -0,0 +1,504 @@ +/* -*- 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(F_PI - fabs(fAngle) > F_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), + maCorrectedPolyPolygon(), + maSlices(), + 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.reset( new geometry::ViewInformation3D(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..1d5b918a2 --- /dev/null +++ b/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx @@ -0,0 +1,362 @@ +/* -*- 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(), F_2PI)); + 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), + maCorrectedPolyPolygon(), + maSlices(), + 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.reset( new geometry::ViewInformation3D(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..4887802e1 --- /dev/null +++ b/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx @@ -0,0 +1,177 @@ +/* -*- 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; + aFill.push_back(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..ad8b9daca --- /dev/null +++ b/drawinglayer/source/primitive3d/sdrprimitive3d.cxx @@ -0,0 +1,107 @@ +/* -*- 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) + : BufferedDecompositionPrimitive3D(), + 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..1e0dd7454 --- /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 = F_2PI * (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..a053a7c21 --- /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, 1); + 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: */ |