You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openoffice.apache.org by al...@apache.org on 2012/01/10 12:00:49 UTC

svn commit: r1229503 - in /incubator/ooo/branches/alg/linecap/main: basegfx/inc/basegfx/polygon/ basegfx/source/polygon/ drawinglayer/inc/drawinglayer/primitive3d/ drawinglayer/source/primitive3d/

Author: alg
Date: Tue Jan 10 11:00:48 2012
New Revision: 1229503

URL: http://svn.apache.org/viewvc?rev=1229503&view=rev
Log:
linecaps: Adapted and partially overhauled 2D line geometry creation; Adapted 3D line geometry creation (tubes) to support LineCap styles

Modified:
    incubator/ooo/branches/alg/linecap/main/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx
    incubator/ooo/branches/alg/linecap/main/basegfx/source/polygon/b2dlinegeometry.cxx
    incubator/ooo/branches/alg/linecap/main/basegfx/source/polygon/b2dpolygontools.cxx
    incubator/ooo/branches/alg/linecap/main/drawinglayer/inc/drawinglayer/primitive3d/polygontubeprimitive3d.hxx
    incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/polygonprimitive3d.cxx
    incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx
    incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx

Modified: incubator/ooo/branches/alg/linecap/main/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx
URL: http://svn.apache.org/viewvc/incubator/ooo/branches/alg/linecap/main/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx?rev=1229503&r1=1229502&r2=1229503&view=diff
==============================================================================
--- incubator/ooo/branches/alg/linecap/main/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx (original)
+++ incubator/ooo/branches/alg/linecap/main/basegfx/inc/basegfx/polygon/b2dpolygontools.hxx Tue Jan 10 11:00:48 2012
@@ -285,6 +285,9 @@ namespace basegfx
          */
         B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius );
 
+        /// create half circle centered on (0,0) from [0 .. F_PI]
+        B2DPolygon createHalfUnitCircle();
+
 		/** create a polygon which describes the unit circle and close it
 
 			@param nStartQuadrant

Modified: incubator/ooo/branches/alg/linecap/main/basegfx/source/polygon/b2dlinegeometry.cxx
URL: http://svn.apache.org/viewvc/incubator/ooo/branches/alg/linecap/main/basegfx/source/polygon/b2dlinegeometry.cxx?rev=1229503&r1=1229502&r2=1229503&view=diff
==============================================================================
--- incubator/ooo/branches/alg/linecap/main/basegfx/source/polygon/b2dlinegeometry.cxx (original)
+++ incubator/ooo/branches/alg/linecap/main/basegfx/source/polygon/b2dlinegeometry.cxx Tue Jan 10 11:00:48 2012
@@ -339,7 +339,13 @@ namespace basegfx
             }
         }
 
-		B2DPolygon createAreaGeometryForEdge(const B2DCubicBezier& rEdge, double fHalfLineWidth)
+        B2DPolyPolygon createAreaGeometryForEdge(
+            const B2DCubicBezier& rEdge, 
+            double fHalfLineWidth,
+            bool bStartRound,
+            bool bEndRound,
+            bool bStartSquare,
+            bool bEndSquare)
         {
             // create polygon for edge
             // Unfortunately, while it would be geometrically correct to not add
@@ -348,35 +354,102 @@ namespace basegfx
             if(rEdge.isBezier())
             {
                 // prepare target and data common for upper and lower
+                B2DPolyPolygon aRetval;
                 B2DPolygon aBezierPolygon;
-	            const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
+                const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
                 const double fEdgeLength(aPureEdgeVector.getLength());
                 const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
-                const B2DVector aTangentA(rEdge.getTangent(0.0));
-                const B2DVector aTangentB(rEdge.getTangent(1.0));
+                B2DVector aTangentA(rEdge.getTangent(0.0)); aTangentA.normalize();
+                B2DVector aTangentB(rEdge.getTangent(1.0)); aTangentB.normalize();
+                const B2DVector aNormalizedPerpendicularA(getPerpendicular(aTangentA));
+                const B2DVector aNormalizedPerpendicularB(getPerpendicular(aTangentB));
+
+                // create upper displacement vectors and check if they cut
+                const B2DVector aPerpendStartA(aNormalizedPerpendicularA * -fHalfLineWidth);
+                const B2DVector aPerpendEndA(aNormalizedPerpendicularB * -fHalfLineWidth);
+                double fCutA(0.0);
+                const tools::CutFlagValue aCutA(tools::findCut(
+                    rEdge.getStartPoint(), aPerpendStartA,
+                    rEdge.getEndPoint(), aPerpendEndA, 
+                    CUTFLAG_ALL, &fCutA));
+                const bool bCutA(CUTFLAG_NONE != aCutA);
+
+                // create lower displacement vectors and check if they cut
+                const B2DVector aPerpendStartB(aNormalizedPerpendicularA * fHalfLineWidth);
+                const B2DVector aPerpendEndB(aNormalizedPerpendicularB * fHalfLineWidth);
+                double fCutB(0.0);
+                const tools::CutFlagValue aCutB(tools::findCut(
+                    rEdge.getEndPoint(), aPerpendEndB,
+                    rEdge.getStartPoint(), aPerpendStartB, 
+                    CUTFLAG_ALL, &fCutB));
+                const bool bCutB(CUTFLAG_NONE != aCutB);
 
-                // create upper edge.
+                // check if cut happens
+                const bool bCut(bCutA || bCutB);
+
+                // create left edge
+                if(bStartRound || bStartSquare)
                 {
-                    // create displacement vectors and check if they cut
-                    const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * -fHalfLineWidth);
-                    const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * -fHalfLineWidth);
-                    double fCut(0.0);
-                    const tools::CutFlagValue aCut(tools::findCut(
-                        rEdge.getStartPoint(), aPerpendStart,
-                        rEdge.getEndPoint(), aPerpendEnd, 
-                        CUTFLAG_ALL, &fCut));
+                    basegfx::B2DPolygon aStartPolygon;
 
-                    if(CUTFLAG_NONE != aCut)
+                    if(bStartRound)
+                    {
+                        aStartPolygon = tools::createHalfUnitCircle();
+                        aStartPolygon.transform(
+                            tools::createScaleShearXRotateTranslateB2DHomMatrix(
+                                fHalfLineWidth, fHalfLineWidth,
+                                0.0,
+                                atan2(aTangentA.getY(), aTangentA.getX()) + F_PI2,
+                                rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY()));
+                    }
+                    else // bStartSquare
+                    {
+                        const basegfx::B2DPoint aStart(rEdge.getStartPoint() - (aTangentA * fHalfLineWidth));
+
+                        if(bCut)
+                        {
+                            aStartPolygon.append(rEdge.getStartPoint() + aPerpendStartB);
+                        }
+
+                        aStartPolygon.append(aStart + aPerpendStartB);
+                        aStartPolygon.append(aStart + aPerpendStartA);
+
+                        if(bCut)
+                        {
+                            aStartPolygon.append(rEdge.getStartPoint() + aPerpendStartA);
+                        }
+                    }
+
+                    if(bCut)
+                    {
+                        aStartPolygon.append(rEdge.getStartPoint());
+                        aStartPolygon.setClosed(true);
+                        aRetval.append(aStartPolygon);
+                    }
+                    else
+                    {
+                        aBezierPolygon.append(aStartPolygon);
+                    }
+                }
+                else
+                {
+                    // append original in-between point
+                    aBezierPolygon.append(rEdge.getStartPoint());
+                }
+
+                // create upper edge.
+                {
+                    if(bCutA)
                     {
                         // calculate cut point and add
-                        const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStart * fCut));
+                        const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStartA * fCutA));
                         aBezierPolygon.append(aCutPoint);
                     }
                     else
                     {
                         // create scaled bezier segment
-                        const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStart);
-                        const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEnd);
+                        const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStartA);
+                        const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEndA);
                         const B2DVector aEdge(aEnd - aStart);
                         const double fLength(aEdge.getLength());
                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
@@ -388,31 +461,69 @@ namespace basegfx
                     }
                 }
 
-                // append original in-between point
-                aBezierPolygon.append(rEdge.getEndPoint());
+                // create right edge
+                if(bEndRound || bEndSquare)
+                {
+                    basegfx::B2DPolygon aEndPolygon;
 
-                // create lower edge. 
+                    if(bEndRound)
+                    {
+                        aEndPolygon = tools::createHalfUnitCircle();
+                        aEndPolygon.transform(
+                            tools::createScaleShearXRotateTranslateB2DHomMatrix(
+                                fHalfLineWidth, fHalfLineWidth,
+                                0.0,
+                                atan2(aTangentB.getY(), aTangentB.getX()) - F_PI2,
+                                rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY()));
+                    }
+                    else // bEndSquare
+                    {
+                        const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + (aTangentB * fHalfLineWidth));
+
+                        if(bCut)
+                        {
+                            aEndPolygon.append(rEdge.getEndPoint() + aPerpendEndA);
+                        }
+
+                        aEndPolygon.append(aEnd + aPerpendEndA);
+                        aEndPolygon.append(aEnd + aPerpendEndB);
+
+                        if(bCut)
+                        {
+                            aEndPolygon.append(rEdge.getEndPoint() + aPerpendEndB);
+                        }
+                    }
+
+                    if(bCut)
+                    {
+                        aEndPolygon.append(rEdge.getEndPoint());
+                        aEndPolygon.setClosed(true);
+                        aRetval.append(aEndPolygon);
+                    }
+                    else
+                    {
+                        aBezierPolygon.append(aEndPolygon);
+                    }
+                }
+                else
                 {
-                    // create displacement vectors and check if they cut
-                    const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * fHalfLineWidth);
-                    const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * fHalfLineWidth);
-                    double fCut(0.0);
-                    const tools::CutFlagValue aCut(tools::findCut(
-                        rEdge.getEndPoint(), aPerpendEnd,
-                        rEdge.getStartPoint(), aPerpendStart, 
-                        CUTFLAG_ALL, &fCut));
+                    // append original in-between point
+                    aBezierPolygon.append(rEdge.getEndPoint());
+                }
 
-                    if(CUTFLAG_NONE != aCut)
+                // create lower edge. 
+                {
+                    if(bCutB)
                     {
                         // calculate cut point and add
-                        const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEnd * fCut));
+                        const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEndB * fCutB));
                         aBezierPolygon.append(aCutPoint);
                     }
                     else
                     {
                         // create scaled bezier segment
-                        const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEnd);
-                        const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStart);
+                        const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEndB);
+                        const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStartB);
                         const B2DVector aEdge(aEnd - aStart);
                         const double fLength(aEdge.getLength());
                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
@@ -424,39 +535,108 @@ namespace basegfx
                     }
                 }
 
-                // append original in-between point
-                aBezierPolygon.append(rEdge.getStartPoint());
-
                 // close and return
                 aBezierPolygon.setClosed(true);
-                return aBezierPolygon;
+                aRetval.append(aBezierPolygon);
+                
+                return aRetval;
             }
             else
             {
-				// #i101491# emulate rEdge.getTangent call which applies a factor of 0.3 to the
-				// full-length edge vector to have numerically exactly the same results as in the
-				// createAreaGeometryForJoin implementation
-	            const B2DVector aEdgeTangent((rEdge.getEndPoint() - rEdge.getStartPoint()) * 0.3);
-                const B2DVector aPerpendEdgeVector(getNormalizedPerpendicular(aEdgeTangent) * fHalfLineWidth);
-                B2DPolygon aEdgePolygon;
+                // Get start and  end point, create tangent and set to needed length
+                B2DVector aTangent(rEdge.getEndPoint() - rEdge.getStartPoint());
+                aTangent.setLength(fHalfLineWidth);
                 
-                // create upper edge
-                aEdgePolygon.append(rEdge.getStartPoint() - aPerpendEdgeVector);
-                aEdgePolygon.append(rEdge.getEndPoint() - aPerpendEdgeVector);
-                
-                // append original in-between point
-                aEdgePolygon.append(rEdge.getEndPoint());
+                // prepare return value
+                B2DPolygon aEdgePolygon;
+
+                // buffered angle
+                double fAngle(0.0);
+                bool bAngle(false);
+
+                // buffered perpendicular
+                B2DVector aPerpend;
+                bool bPerpend(false);
+
+                // create left vertical
+                if(bStartRound)
+                {
+                    aEdgePolygon = tools::createHalfUnitCircle();
+                    fAngle = atan2(aTangent.getY(), aTangent.getX());
+                    bAngle = true;
+                    aEdgePolygon.transform(
+                        tools::createScaleShearXRotateTranslateB2DHomMatrix(
+                            fHalfLineWidth, fHalfLineWidth,
+                            0.0,
+                            fAngle + F_PI2,
+                            rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY()));
+                }
+                else
+                {
+                    aPerpend.setX(-aTangent.getY());
+                    aPerpend.setY(aTangent.getX());
+                    bPerpend = true;
+
+                    if(bStartSquare)
+                    {
+                        const basegfx::B2DPoint aStart(rEdge.getStartPoint() - aTangent);
+
+                        aEdgePolygon.append(aStart + aPerpend);
+                        aEdgePolygon.append(aStart - aPerpend);
+                    }
+                    else
+                    {
+                        aEdgePolygon.append(rEdge.getStartPoint() + aPerpend);
+                        aEdgePolygon.append(rEdge.getStartPoint()); // keep the in-between point for numerical reasons
+                        aEdgePolygon.append(rEdge.getStartPoint() - aPerpend);
+                    }
+                }
 
-                // create lower edge
-                aEdgePolygon.append(rEdge.getEndPoint() + aPerpendEdgeVector);
-                aEdgePolygon.append(rEdge.getStartPoint() + aPerpendEdgeVector);
+                // create right vertical
+                if(bEndRound)
+                {
+                    basegfx::B2DPolygon aEndPolygon(tools::createHalfUnitCircle());
 
-                // append original in-between point
-                aEdgePolygon.append(rEdge.getStartPoint());
+                    if(!bAngle)
+                    {
+                        fAngle = atan2(aTangent.getY(), aTangent.getX());
+                    }
+
+                    aEndPolygon.transform(
+                        tools::createScaleShearXRotateTranslateB2DHomMatrix(
+                            fHalfLineWidth, fHalfLineWidth,
+                            0.0,
+                            fAngle - F_PI2,
+                            rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY()));
+                    aEdgePolygon.append(aEndPolygon);
+                }
+                else
+                {
+                    if(!bPerpend)
+                    {
+                        aPerpend.setX(-aTangent.getY());
+                        aPerpend.setY(aTangent.getX());
+                    }
+
+                    if(bEndSquare)
+                    {
+                        const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + aTangent);
+
+                        aEdgePolygon.append(aEnd - aPerpend);
+                        aEdgePolygon.append(aEnd + aPerpend);
+                    }
+                    else
+                    {
+                        aEdgePolygon.append(rEdge.getEndPoint() - aPerpend);
+                        aEdgePolygon.append(rEdge.getEndPoint()); // keep the in-between point for numerical reasons
+                        aEdgePolygon.append(rEdge.getEndPoint() + aPerpend);
+                    }
+                }
 
                 // close and return
                 aEdgePolygon.setClosed(true);
-                return aEdgePolygon;
+
+                return B2DPolyPolygon(aEdgePolygon);
             }
         }
 
@@ -567,82 +747,6 @@ namespace basegfx
 
             return aEdgePolygon;
         }
-
-        B2DPolygon createAreaGeometryForCap(
-            const B2DVector& rTangent,
-            const B2DPoint& rPoint,
-            double fHalfLineWidth,
-            com::sun::star::drawing::LineCap eCap,
-            bool bIsStart)
-        {
-            OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)");
-
-            const B2DVector aScaledPerpend(getNormalizedPerpendicular(rTangent)
-                                 * (bIsStart ? fHalfLineWidth : -fHalfLineWidth));
-            // Vector from rPoint towards cap
-            B2DVector aScaledTangent(rTangent);
-            aScaledTangent.normalize();
-            aScaledTangent *= (bIsStart ? -fHalfLineWidth : fHalfLineWidth);
-
-            B2DPolygon aCapPolygon;
-            const B2DPoint aStartPoint(rPoint + aScaledPerpend);
-            const B2DPoint aEndPoint(rPoint - aScaledPerpend);
-
-            switch(eCap)
-            {
-                case com::sun::star::drawing::LineCap_ROUND :
-                {
-                    // use tooling to add needed EllipseSegment
-                    double fAngleStart(atan2(aScaledPerpend.getY(), aScaledPerpend.getX()));
-
-                    // atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI]
-                    if(fAngleStart < 0.0)
-                    {
-                        fAngleStart += F_2PI;
-                    }
-
-                    double fAngleEnd = fAngleStart + F_PI;
-                    if(fAngleEnd > F_2PI)
-                    {
-                        fAngleEnd -= F_2PI;
-                    }
-
-                    const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd));
-                    // #i101491#
-                    // use the original start/end positions; the ones from bow creation may be numerically
-                    // different due to their different creation. To guarantee good merging quality with edges
-                    // and edge roundings (and to reduce point count)
-                    
-                    // ToDo Are there at least two points in aBow ?
-                    aCapPolygon = aBow;
-                    aCapPolygon.setB2DPoint(0, aStartPoint);
-                    aCapPolygon.setB2DPoint(aBow.count() - 1, aEndPoint);
-                    aCapPolygon.append(rPoint);
-                    break;
-                }
-                case com::sun::star::drawing::LineCap_SQUARE :
-                {
-                    aCapPolygon.append(aStartPoint);
-                    aCapPolygon.append(aStartPoint + aScaledTangent);
-                    aCapPolygon.append(aEndPoint + aScaledTangent);
-                    aCapPolygon.append(aEndPoint);
-                    aCapPolygon.append(rPoint);
-                    break;
-                }
-                default: // com::sun::star::drawing::LineCap_BUTT
-                {
-                    // No cap.
-                    // ToDo. Is actually not called with BUTT. What setting is
-                    // useful to prevend errors, if called with BUTT anyway?
-                    break;
-                }
-            }
-
-            // create last polygon part for cap
-            aCapPolygon.setClosed(true);
-
-            return aCapPolygon;
-        }
     } // end of anonymus namespace
 
 	namespace tools
@@ -697,7 +801,7 @@ namespace basegfx
 				const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin);
                 const bool bIsClosed(aCandidate.isClosed());
                 const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
-                const bool bCreateLineCap(com::sun::star::drawing::LineCap_BUTT != eCap);
+                const bool bLineCap(!bIsClosed && com::sun::star::drawing::LineCap_BUTT != eCap);
 
                 if(nEdgeCount)
                 {
@@ -707,18 +811,6 @@ namespace basegfx
                     // prepare edge
                     aEdge.setStartPoint(aCandidate.getB2DPoint(0));
 
-                    // eventually create start line cap
-                    if (!bIsClosed && bCreateLineCap)
-                    {
-                      aEdge.setControlPointA(aCandidate.getNextControlPoint(0));
-                      aEdge.setControlPointB(aCandidate.getPrevControlPoint(1 % nPointCount));
-                      aEdge.setEndPoint(aCandidate.getB2DPoint(1 % nPointCount ));
-                      const B2DVector aTangentStart(aEdge.getTangent(0.0));
-                      aRetval.append(createAreaGeometryForCap(
-                                    aTangentStart, aEdge.getStartPoint(),
-                                    fHalfLineWidth, eCap, true /*IsStart*/));
-                    }
-
                     if(bIsClosed && bEventuallyCreateLineJoin)
                     {
                         // prepare previous edge
@@ -740,85 +832,110 @@ namespace basegfx
                         // check and create linejoin
                         if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a))
                         {
-				            const B2DVector aTangentPrev(aPrev.getTangent(1.0));
-				            const B2DVector aTangentEdge(aEdge.getTangent(0.0));
+                            B2DVector aTangentPrev(aPrev.getTangent(1.0)); aTangentPrev.normalize();
+                            B2DVector aTangentEdge(aEdge.getTangent(0.0)); aTangentEdge.normalize();
                             B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge));
 
-							if(ORIENTATION_NEUTRAL == aOrientation)
-							{
-								// they are parallell or empty; if they are both not zero and point 
-								// in opposite direction, a half-circle is needed
-								if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
-								{
-									const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
-
-									if(fTools::equal(fAngle, F_PI))
-									{
+                            if(ORIENTATION_NEUTRAL == aOrientation)
+                            {
+                                   // they are parallell or empty; if they are both not zero and point 
+                                   // in opposite direction, a half-circle is needed
+                                   if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
+                                   {
+                                    const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
+        
+                                    if(fTools::equal(fAngle, F_PI))
+                                    {
                                         // for half-circle production, fallback to positive
                                         // orientation
-										aOrientation = ORIENTATION_POSITIVE;
-									}
-								}
-							}
+                                        aOrientation = ORIENTATION_POSITIVE;
+                                    }
+                                }
+                            }
 
                             if(ORIENTATION_POSITIVE == aOrientation)
                             {
-								const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * -fHalfLineWidth);
-								const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * -fHalfLineWidth);
+                                const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * -fHalfLineWidth);
+                                const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * -fHalfLineWidth);
                                 
-								aRetval.append(createAreaGeometryForJoin(
-                                    aTangentPrev, aTangentEdge,
-                                    aPerpendPrev, aPerpendEdge, 
-									aEdge.getStartPoint(), fHalfLineWidth, 
-                                    eJoin, fMiterMinimumAngle));
+                                aRetval.append(
+                                    createAreaGeometryForJoin(
+                                        aTangentPrev, 
+                                        aTangentEdge,
+                                        aPerpendPrev, 
+                                        aPerpendEdge, 
+                                        aEdge.getStartPoint(), 
+                                        fHalfLineWidth, 
+                                        eJoin, 
+                                        fMiterMinimumAngle));
                             }
                             else if(ORIENTATION_NEGATIVE == aOrientation)
                             {
-								const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * fHalfLineWidth);
-								const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * fHalfLineWidth);
+                                const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * fHalfLineWidth);
+                                const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * fHalfLineWidth);
 
-								aRetval.append(createAreaGeometryForJoin(
-                                    aTangentEdge, aTangentPrev,
-                                    aPerpendEdge, aPerpendPrev, 
-									aEdge.getStartPoint(), fHalfLineWidth, 
-                                    eJoin, fMiterMinimumAngle));
+                                aRetval.append(
+                                    createAreaGeometryForJoin(
+                                        aTangentEdge, 
+                                        aTangentPrev,
+                                        aPerpendEdge, 
+                                        aPerpendPrev, 
+                                        aEdge.getStartPoint(), 
+                                        fHalfLineWidth, 
+                                        eJoin, 
+                                        fMiterMinimumAngle));
                             }
                         }
 
                         // create geometry for edge
-                		aRetval.append(createAreaGeometryForEdge(aEdge, fHalfLineWidth));
+                        const bool bLast(a + 1 == nEdgeCount);
 
-                        // prepare next step
-                        if(bEventuallyCreateLineJoin)
+                        if(bLineCap)
+                        {
+                            const bool bFirst(!a);
+
+                            aRetval.append(
+                                createAreaGeometryForEdge(
+                                    aEdge, 
+                                    fHalfLineWidth,
+                                    bFirst && com::sun::star::drawing::LineCap_ROUND == eCap,
+                                    bLast && com::sun::star::drawing::LineCap_ROUND == eCap,
+                                    bFirst && com::sun::star::drawing::LineCap_SQUARE == eCap,
+                                    bLast && com::sun::star::drawing::LineCap_SQUARE == eCap));
+                        }
+                        else
                         {
-                            aPrev = aEdge;
+                            aRetval.append(
+                                createAreaGeometryForEdge(
+                                    aEdge, 
+                                    fHalfLineWidth,
+                                    false,
+                                    false,
+                                    false,
+                                    false));
                         }
 
-                        aEdge.setStartPoint(aEdge.getEndPoint());
-                    }
+                        // prepare next step
+                        if(!bLast)
+                        {
+                            if(bEventuallyCreateLineJoin)
+                            {
+                                aPrev = aEdge;
+                            }
 
-                    // eventually create end line cap
-                    if (!bIsClosed && bCreateLineCap)
-                    {
-                        aEdge.setStartPoint(aCandidate.getB2DPoint(nEdgeCount -1));
-                        aEdge.setControlPointA(aCandidate.getNextControlPoint(nEdgeCount -1));
-                        aEdge.setControlPointB(aCandidate.getPrevControlPoint(nEdgeCount % nPointCount));
-                        aEdge.setEndPoint(aCandidate.getB2DPoint(nEdgeCount % nPointCount ));
-                        const B2DVector aTangentEnd(aEdge.getTangent(1.0));
-                        aRetval.append(createAreaGeometryForCap(
-                                    aTangentEnd, aEdge.getEndPoint(),
-                                    fHalfLineWidth, eCap, false /*IsStart*/));
+                            aEdge.setStartPoint(aEdge.getEndPoint());
+                        }
                     }
                 }
-                // ToDo Returns empty PolyPolygon if rCandidate is a single point?
+
                 return aRetval;
-			}
+            }
             else
             {
                 return B2DPolyPolygon(rCandidate);
             }
-		}
-	} // end of namespace tools
+        }
+    } // end of namespace tools
 } // end of namespace basegfx
 
 //////////////////////////////////////////////////////////////////////////////

Modified: incubator/ooo/branches/alg/linecap/main/basegfx/source/polygon/b2dpolygontools.cxx
URL: http://svn.apache.org/viewvc/incubator/ooo/branches/alg/linecap/main/basegfx/source/polygon/b2dpolygontools.cxx?rev=1229503&r1=1229502&r2=1229503&view=diff
==============================================================================
--- incubator/ooo/branches/alg/linecap/main/basegfx/source/polygon/b2dpolygontools.cxx (original)
+++ incubator/ooo/branches/alg/linecap/main/basegfx/source/polygon/b2dpolygontools.cxx Tue Jan 10 11:00:48 2012
@@ -1889,6 +1889,33 @@ namespace basegfx
             return aUnitCircle;
         }
 
+        B2DPolygon createHalfUnitCircle()
+        {
+            static B2DPolygon aUnitHalfCircle;
+
+            if(!aUnitHalfCircle.count())
+            {
+                const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
+                const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER));
+                const B2DHomMatrix aRotateMatrix(createRotateB2DHomMatrix(F_PI2 / STEPSPERQUARTER));
+                B2DPoint aPoint(1.0, 0.0);
+                B2DPoint aForward(1.0, fScaledKappa);
+                B2DPoint aBackward(1.0, -fScaledKappa);
+
+                aUnitHalfCircle.append(aPoint);
+
+                for(sal_uInt32 a(0); a < STEPSPERQUARTER * 2; a++)
+                {
+                    aPoint *= aRotateMatrix;
+                    aBackward *= aRotateMatrix;
+                    aUnitHalfCircle.appendBezierSegment(aForward, aBackward, aPoint);
+                    aForward *= aRotateMatrix;
+                }
+            }
+
+            return aUnitHalfCircle;
+        }
+
         B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant)
 		{
             switch(nStartQuadrant % 4)

Modified: incubator/ooo/branches/alg/linecap/main/drawinglayer/inc/drawinglayer/primitive3d/polygontubeprimitive3d.hxx
URL: http://svn.apache.org/viewvc/incubator/ooo/branches/alg/linecap/main/drawinglayer/inc/drawinglayer/primitive3d/polygontubeprimitive3d.hxx?rev=1229503&r1=1229502&r2=1229503&view=diff
==============================================================================
--- incubator/ooo/branches/alg/linecap/main/drawinglayer/inc/drawinglayer/primitive3d/polygontubeprimitive3d.hxx (original)
+++ incubator/ooo/branches/alg/linecap/main/drawinglayer/inc/drawinglayer/primitive3d/polygontubeprimitive3d.hxx Tue Jan 10 11:00:48 2012
@@ -59,6 +59,7 @@ namespace drawinglayer
 			double										mfDegreeStepWidth;
 			double										mfMiterMinimumAngle;
 			basegfx::B2DLineJoin					    maLineJoin;
+            com::sun::star::drawing::LineCap            maLineCap;
 
 		protected:
 			/** access methods to maLast3DDecomposition. The usage of this methods may allow
@@ -76,7 +77,9 @@ namespace drawinglayer
 			PolygonTubePrimitive3D(
 				const basegfx::B3DPolygon& rPolygon, 
 				const basegfx::BColor& rBColor,
-				double fRadius, basegfx::B2DLineJoin aLineJoin,
+				double fRadius, 
+                basegfx::B2DLineJoin aLineJoin,
+                com::sun::star::drawing::LineCap aLineCap,
 				double fDegreeStepWidth = 10.0 * F_PI180,
 				double fMiterMinimumAngle = 15.0 * F_PI180);
 
@@ -85,6 +88,7 @@ namespace drawinglayer
 			double getDegreeStepWidth() const { return mfDegreeStepWidth; }
 			double getMiterMinimumAngle() const { return mfMiterMinimumAngle; }
 			basegfx::B2DLineJoin getLineJoin() const { return maLineJoin; }
+            com::sun::star::drawing::LineCap getLineCap() const { return maLineCap; }
 
 			/// compare operator
 			virtual bool operator==(const BasePrimitive3D& rPrimitive) const;

Modified: incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/polygonprimitive3d.cxx
URL: http://svn.apache.org/viewvc/incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/polygonprimitive3d.cxx?rev=1229503&r1=1229502&r2=1229503&view=diff
==============================================================================
--- incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/polygonprimitive3d.cxx (original)
+++ incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/polygonprimitive3d.cxx Tue Jan 10 11:00:48 2012
@@ -106,11 +106,18 @@ namespace drawinglayer
 					// create fat line data
 					const double fRadius(getLineAttribute().getWidth() / 2.0);
 					const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin());
+                    const com::sun::star::drawing::LineCap aLineCap(getLineAttribute().getLineCap());
 
 					for(sal_uInt32 a(0L); a < aHairLinePolyPolygon.count(); a++)
 					{
 						// create tube primitives
-						const Primitive3DReference xRef(new PolygonTubePrimitive3D(aHairLinePolyPolygon.getB3DPolygon(a), getLineAttribute().getColor(), fRadius, aLineJoin));
+						const Primitive3DReference xRef(
+                            new PolygonTubePrimitive3D(
+                                aHairLinePolyPolygon.getB3DPolygon(a), 
+                                getLineAttribute().getColor(), 
+                                fRadius, 
+                                aLineJoin,
+                                aLineCap));
 						aRetval[a] = xRef;
 					}
 				}

Modified: incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx
URL: http://svn.apache.org/viewvc/incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx?rev=1229503&r1=1229502&r2=1229503&view=diff
==============================================================================
--- incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx (original)
+++ incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx Tue Jan 10 11:00:48 2012
@@ -156,6 +156,76 @@ namespace drawinglayer
 				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, 
@@ -173,7 +243,7 @@ namespace drawinglayer
 					if(basegfx::B2DLINEJOIN_ROUND == aLineJoin)
 					{
 						// calculate new horizontal segments
-						const sal_uInt32 nHorSeg((sal_uInt32)((fAngle / F_2PI) * (double)nSegments));
+						const sal_uInt32 nHorSeg(basegfx::fround((fAngle / F_2PI) * (double)nSegments));
 
 						if(nHorSeg)
 						{
@@ -403,100 +473,188 @@ 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(0L != nPointCount)
-			{
-				if(basegfx::fTools::more(getRadius(), 0.0))
-				{
-					const attribute::MaterialAttribute3D aMaterial(getBColor());
-					static sal_uInt32 nSegments(8L); // 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 - 1L);
-					basegfx::B3DPoint aLast(getB3DPolygon().getB3DPoint(nPointCount - 1L));
-					basegfx::B3DPoint aCurr(getB3DPolygon().getB3DPoint(0L));
-
-					for(sal_uInt32 a(0L); a < nLoopCount; a++)
-					{
-						// get next data
-						const basegfx::B3DPoint aNext(getB3DPolygon().getB3DPoint((a + 1L) % nPointCount));
-						const basegfx::B3DVector aForw(aNext - aCurr);
-						const double fForwLen(aForw.getLength());
-
-						if(basegfx::fTools::more(fForwLen, 0.0))
-						{
-							// get rotation from vector, this describes rotation from (1, 0, 0) to aForw
-							basegfx::B3DHomMatrix aRotVector(getRotationFromVector(aForw));
-
-							// create default transformation with scale and rotate
-							basegfx::B3DHomMatrix aVectorTrans;
-							aVectorTrans.scale(fForwLen, getRadius(), getRadius());
-							aVectorTrans *= aRotVector;
-							aVectorTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
-
-							if(bNoLineJoin || (!bClosed && !a))
-							{
-								// line start edge, build transformed primitiveVector3D
-								TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aVectorTrans, getLineCapSegments(nSegments, aMaterial));
-								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
-									TransformPrimitive3D* pNewTransformedB = new TransformPrimitive3D(aSphereTrans, aNewList);
-									aResultVector.push_back(pNewTransformedB);
-								}
-							}
-
-							// create line segments, build transformed primitiveVector3D
-							TransformPrimitive3D* pNewTransformedC = new TransformPrimitive3D(aVectorTrans, getLineTubeSegments(nSegments, aMaterial));
-							aResultVector.push_back(pNewTransformedC);
-
-							if(bNoLineJoin || (!bClosed && ((a + 1L) == nLoopCount)))
-							{
-								// line end edge, first rotate (mirror) and translate, then use use aRotVector
-								basegfx::B3DHomMatrix aBackTrans;
-								aBackTrans.rotate(0.0, F_PI, 0.0);
-								aBackTrans.translate(1.0, 0.0, 0.0);
-								aBackTrans.scale(fForwLen, getRadius(), getRadius());
-								aBackTrans *= aRotVector;
-								aBackTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ());
-								
-								// line end edge, build transformed primitiveVector3D
-								TransformPrimitive3D* pNewTransformedD = new TransformPrimitive3D(aBackTrans, getLineCapSegments(nSegments, aMaterial));
-								aResultVector.push_back(pNewTransformedD);
-							}
-						}
+    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;
@@ -526,6 +684,7 @@ namespace drawinglayer
 			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),
@@ -533,7 +692,8 @@ namespace drawinglayer
             mfRadius(fRadius),
 			mfDegreeStepWidth(fDegreeStepWidth),
 			mfMiterMinimumAngle(fMiterMinimumAngle),
-			maLineJoin(aLineJoin)
+			maLineJoin(aLineJoin),
+            maLineCap(aLineCap)
 		{
 		}
 
@@ -546,7 +706,8 @@ namespace drawinglayer
 				return (getRadius() == rCompare.getRadius()
 					&& getDegreeStepWidth() == rCompare.getDegreeStepWidth()
 					&& getMiterMinimumAngle() == rCompare.getMiterMinimumAngle()
-					&& getLineJoin() == rCompare.getLineJoin());
+					&& getLineJoin() == rCompare.getLineJoin()
+                    && getLineCap() == rCompare.getLineCap());
 			}
 
 			return false;

Modified: incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx
URL: http://svn.apache.org/viewvc/incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx?rev=1229503&r1=1229502&r2=1229503&view=diff
==============================================================================
--- incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx (original)
+++ incubator/ooo/branches/alg/linecap/main/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx Tue Jan 10 11:00:48 2012
@@ -150,7 +150,7 @@ namespace drawinglayer
 			aScaledPolyPolygon.transform(rObjectTransform);
 
 			// create line and stroke attribute
-			const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin());
+			const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap());
 			const attribute::StrokeAttribute aStrokeAttribute(rLine.getDotDashArray(), rLine.getFullDotDashLen());
 
 			// create primitives