| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_drawinglayer.hxx" |
| |
| #include <drawinglayer/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> |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| namespace drawinglayer |
| { |
| namespace primitive3d |
| { |
| namespace // anonymous namespace |
| { |
| Primitive3DSequence getLineTubeSegments( |
| sal_uInt32 nSegments, |
| const attribute::MaterialAttribute3D& rMaterial) |
| { |
| // static data for buffered tube primitives |
| static Primitive3DSequence aLineTubeList; |
| static sal_uInt32 nLineTubeSegments(0L); |
| static attribute::MaterialAttribute3D aLineMaterial; |
| |
| // may exclusively change static data, use mutex |
| ::osl::Mutex m_mutex; |
| |
| if(nSegments != nLineTubeSegments || !(rMaterial == aLineMaterial)) |
| { |
| nLineTubeSegments = nSegments; |
| aLineMaterial = rMaterial; |
| aLineTubeList = Primitive3DSequence(); |
| } |
| |
| if(!aLineTubeList.hasElements() && 0L != nLineTubeSegments) |
| { |
| 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 / (double)nLineTubeSegments, 0.0, 0.0); |
| aLineTubeList.realloc(nLineTubeSegments); |
| |
| for(sal_uInt32 a(0L); a < nLineTubeSegments; a++) |
| { |
| const basegfx::B3DPoint aNextLeft(aRot * aLastLeft); |
| const basegfx::B3DPoint aNextRight(aRot * aLastRight); |
| basegfx::B3DPolygon aNewPolygon; |
| |
| aNewPolygon.append(aNextLeft); |
| aNewPolygon.setNormal(0L, basegfx::B3DVector(aNextLeft - aLeft)); |
| |
| aNewPolygon.append(aLastLeft); |
| aNewPolygon.setNormal(1L, basegfx::B3DVector(aLastLeft - aLeft)); |
| |
| aNewPolygon.append(aLastRight); |
| aNewPolygon.setNormal(2L, basegfx::B3DVector(aLastRight - aRight)); |
| |
| aNewPolygon.append(aNextRight); |
| aNewPolygon.setNormal(3L, basegfx::B3DVector(aNextRight - aRight)); |
| |
| aNewPolygon.setClosed(true); |
| |
| const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); |
| const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, aLineMaterial, false)); |
| aLineTubeList[a] = xRef; |
| |
| aLastLeft = aNextLeft; |
| aLastRight = aNextRight; |
| } |
| } |
| |
| return aLineTubeList; |
| } |
| |
| Primitive3DSequence getLineCapSegments( |
| sal_uInt32 nSegments, |
| const attribute::MaterialAttribute3D& rMaterial) |
| { |
| // static data for buffered tube primitives |
| static Primitive3DSequence aLineCapList; |
| static sal_uInt32 nLineCapSegments(0L); |
| static attribute::MaterialAttribute3D aLineMaterial; |
| |
| // may exclusively change static data, use mutex |
| ::osl::Mutex m_mutex; |
| |
| if(nSegments != nLineCapSegments || !(rMaterial == aLineMaterial)) |
| { |
| nLineCapSegments = nSegments; |
| aLineMaterial = rMaterial; |
| aLineCapList = Primitive3DSequence(); |
| } |
| |
| if(!aLineCapList.hasElements() && 0L != nLineCapSegments) |
| { |
| 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 / (double)nLineCapSegments, 0.0, 0.0); |
| aLineCapList.realloc(nLineCapSegments); |
| |
| for(sal_uInt32 a(0L); a < nLineCapSegments; a++) |
| { |
| const basegfx::B3DPoint aNext(aRot * aLast); |
| basegfx::B3DPolygon aNewPolygon; |
| |
| aNewPolygon.append(aLast); |
| aNewPolygon.setNormal(0L, basegfx::B3DVector(aLast - aNull)); |
| |
| aNewPolygon.append(aNext); |
| aNewPolygon.setNormal(1L, basegfx::B3DVector(aNext - aNull)); |
| |
| aNewPolygon.append(aNull); |
| aNewPolygon.setNormal(2L, basegfx::B3DVector(-1.0, 0.0, 0.0)); |
| |
| aNewPolygon.setClosed(true); |
| |
| const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); |
| const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, aLineMaterial, false)); |
| aLineCapList[a] = xRef; |
| |
| aLast = aNext; |
| } |
| } |
| |
| return aLineCapList; |
| } |
| |
| Primitive3DSequence getLineCapRoundSegments( |
| sal_uInt32 nSegments, |
| const attribute::MaterialAttribute3D& rMaterial) |
| { |
| // static data for buffered tube primitives |
| static Primitive3DSequence aLineCapRoundList; |
| static sal_uInt32 nLineCapRoundSegments(0); |
| static attribute::MaterialAttribute3D aLineMaterial; |
| |
| // may exclusively change static data, use mutex |
| ::osl::Mutex m_mutex; |
| |
| if(nSegments != nLineCapRoundSegments || !(rMaterial == aLineMaterial)) |
| { |
| nLineCapRoundSegments = nSegments; |
| aLineMaterial = rMaterial; |
| aLineCapRoundList = Primitive3DSequence(); |
| } |
| |
| if(!aLineCapRoundList.hasElements() && 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::tools::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 orientned 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 |
| aLineCapRoundList.realloc(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 |
| aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D( |
| aPartPolyPolygon, |
| rMaterial, |
| false); |
| } |
| } |
| } |
| |
| return aLineCapRoundList; |
| } |
| |
| Primitive3DSequence getLineJoinSegments( |
| sal_uInt32 nSegments, |
| const attribute::MaterialAttribute3D& rMaterial, |
| double fAngle, |
| double /*fDegreeStepWidth*/, |
| double fMiterMinimumAngle, |
| basegfx::B2DLineJoin aLineJoin) |
| { |
| // nSegments is for whole circle, adapt to half circle |
| const sal_uInt32 nVerSeg(nSegments >> 1L); |
| std::vector< BasePrimitive3D* > aResultVector; |
| |
| if(nVerSeg) |
| { |
| if(basegfx::B2DLINEJOIN_ROUND == aLineJoin) |
| { |
| // calculate new horizontal segments |
| const sal_uInt32 nHorSeg(basegfx::fround((fAngle / F_2PI) * (double)nSegments)); |
| |
| if(nHorSeg) |
| { |
| // create half-sphere |
| const basegfx::B3DPolyPolygon aSphere(basegfx::tools::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, F_PI2, -F_PI2, 0.0, fAngle)); |
| |
| for(sal_uInt32 a(0L); 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_MIDDLE == aLineJoin |
| || 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 / (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(0L); a < nVerSeg; a++) |
| { |
| const bool bFirst(0L == a); |
| const bool bLast(a + 1L == 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(0L); 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(0L); 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; |
| } |
| } |
| } |
| } |
| } |
| |
| Primitive3DSequence aRetval(aResultVector.size()); |
| |
| for(sal_uInt32 a(0L); 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 |
| } // end of namespace primitive3d |
| } // end of namespace drawinglayer |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| using namespace com::sun::star; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| namespace drawinglayer |
| { |
| namespace primitive3d |
| { |
| Primitive3DSequence 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 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 && com::sun::star::drawing::LineCap_ROUND == getLineCap()); |
| const bool bLineCapSquare(bLineCapPossible && com::sun::star::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 |
| Primitive3DSequence 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 |
| Primitive3DSequence aNewList( |
| getLineJoinSegments( |
| nSegments, |
| aMaterial, |
| fAngle, |
| getDegreeStepWidth(), |
| 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 |
| Primitive3DSequence 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 |
| Primitive3DSequence aRetval(aResultVector.size()); |
| |
| for(sal_uInt32 a(0L); 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, |
| com::sun::star::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 = (PolygonTubePrimitive3D&)rPrimitive; |
| |
| return (getRadius() == rCompare.getRadius() |
| && getDegreeStepWidth() == rCompare.getDegreeStepWidth() |
| && getMiterMinimumAngle() == rCompare.getMiterMinimumAngle() |
| && getLineJoin() == rCompare.getLineJoin() |
| && getLineCap() == rCompare.getLineCap()); |
| } |
| |
| return false; |
| } |
| |
| Primitive3DSequence PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const |
| { |
| ::osl::MutexGuard aGuard( m_aMutex ); |
| |
| if(!getLast3DDecomposition().hasElements()) |
| { |
| const Primitive3DSequence aNewSequence(impCreate3DDecomposition(rViewInformation)); |
| const_cast< PolygonTubePrimitive3D* >(this)->setLast3DDecomposition(aNewSequence); |
| } |
| |
| return getLast3DDecomposition(); |
| } |
| |
| // provide unique ID |
| ImplPrimitrive3DIDBlock(PolygonTubePrimitive3D, PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D) |
| |
| } // end of namespace primitive3d |
| } // end of namespace drawinglayer |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // eof |