summaryrefslogtreecommitdiff
path: root/chart2/source/view/main/Clipping.cxx
blob: 4d6c2b1fced6692263dfa6cc36221ec1fef2f756 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#include "Clipping.hxx"
#include "CommonConverters.hxx"

#ifndef _COM_SUN_STAR_DRAWING_POSITION3D_HPP_
#include <com/sun/star/drawing/Position3D.hpp>
#endif

//.............................................................................
namespace chart
{
//.............................................................................
using namespace ::com::sun::star;

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

namespace{
/** @descr  This is a supporting function for lcl_clip2d.  It computes a new parametric
            value for an entering (dTE) or leaving (dTL) intersection point with one
            of the edges bounding the clipping area.
            For explanation of the parameters please refer to :

            Liang-Biarsky parametric line-clipping algorithm as described in:
            Computer Graphics: principles and practice, 2nd ed.,
            James D. Foley et al.,
            Section 3.12.4 on page 117.
*/
bool lcl_CLIPt(double fDenom,double fNum, double & fTE, double & fTL)
{
    double  fT;

    if (fDenom > 0)             //  Intersection enters: PE
    {
        fT = fNum / fDenom;     //  Parametric value at the intersection.
        if (fT > fTL)           //  fTE and fTL crossover
            return false;       //    therefore reject the line.
        else if (fT > fTE)      //  A new fTE has been found.
            fTE = fT;
    }
    else if (fDenom < 0)        //  Intersection leaves: PL
    {
        fT = fNum / fDenom;     //  Parametric Value at the intersection.
        if (fT < fTE)           //  fTE and fTL crossover
            return false;       //    therefore reject the line.
        else if (fT < fTL)      //  A new fTL has been found.
            fTL = fT;
    }
    else if (fNum > 0)
        return false;           //  Line lies on the outside of the edge.

    return true;
}

/** @descr  The line given by it's two endpoints rP0 and rP1 is clipped at the rectangle
            rRectangle.  If there is at least a part of it visible then TRUE is returned and
            the endpoints of that part are stored in rP0 and rP1.  The points rP0 and rP1
            may have the same coordinates.
        @param  rP0 Start point of the line to clip.  Modified to contain a start point inside
            the clipping area if possible.
        @param  rP1 End point of the line to clip.  Modified to contain an end point inside
            the clipping area if possible.
        @param  rRectangle Clipping area.
        @return If the line lies completely or partly inside the clipping area then TRUE
            is returned.  If the line lies completely outside then FALSE is returned and rP0 and
            rP1 are left unmodified.
*/
bool lcl_clip2d(DoublePoint& rPoint0, DoublePoint& rPoint1, const DoubleRectangle& rRectangle)
{
    //Direction vector of the line.
    DoublePoint aDirection = rPoint1 - rPoint0;

    if( aDirection.X==0 && aDirection.Y==0 && rRectangle.isInside(rPoint0) )
    {
        //  Degenerate case of a zero length line.
        return true;
    }
    else
    {
        //  Values of the line parameter where the line enters resp. leaves the rectangle.
        double fTE = 0,
               fTL = 1;

        //  Test wether at least a part lies in the four half-planes with respect to
        //  the rectangles four edges.
        if( lcl_CLIPt(aDirection.X, rRectangle.Left - rPoint0.X, fTE, fTL) )
            if( lcl_CLIPt(-aDirection.X, rPoint0.X - rRectangle.Right, fTE, fTL) )
                if( lcl_CLIPt(aDirection.Y, rRectangle.Top - rPoint0.Y, fTE, fTL) )
                    if( lcl_CLIPt(-aDirection.Y, rPoint0.Y - rRectangle.Bottom, fTE, fTL) )
                    {
                        //  At least a part is visible.
                        if (fTL < 1)
                        {
                            //  Compute the new end point.
                            rPoint1.X = rPoint0.X + fTL * aDirection.X;
                            rPoint1.Y = rPoint0.Y + fTL * aDirection.Y;
                        }
                        if (fTE > 0)
                        {
                            //  Compute the new starting point.
                            rPoint0.X = rPoint0.X + fTE * aDirection.X;
                            rPoint0.Y = rPoint0.Y + fTE * aDirection.Y;
                        }
                        return true;
                    }

        //  Line is not visible.
        return false;
    }
}

bool lcl_clip2d_(drawing::Position3D& rPoint0, drawing::Position3D& rPoint1, const DoubleRectangle& rRectangle)
{
    DoublePoint aP0(rPoint0.PositionX,rPoint0.PositionY);
    DoublePoint aP1(rPoint1.PositionX,rPoint1.PositionY);
    bool bRet = lcl_clip2d( aP0, aP1, rRectangle );

    rPoint0.PositionX = aP0.X;
    rPoint0.PositionY = aP0.Y;
    rPoint1.PositionX = aP1.X;
    rPoint1.PositionY = aP1.Y;

    return bRet;
}

}//end anonymous namespace

void Clipping::clipPolygonAtRectangle( const drawing::PolyPolygonShape3D& rPolygon, const DoubleRectangle& rRectangle, drawing::PolyPolygonShape3D& aResult )
{
    //rPolygon:    XPolygon --> drawing::PolyPolygonShape3D
    //aResult: XPolyPolygon --> drawing::PolyPolygonShape3D

    aResult.SequenceX.realloc(0);
    aResult.SequenceY.realloc(0);
    aResult.SequenceZ.realloc(0);

    if(!rPolygon.SequenceX.getLength())
        return;

    drawing::Position3D aFrom;
    drawing::Position3D aTo;
    sal_Int32 nOldPointCount = rPolygon.SequenceX[0].getLength();
    sal_Int32 nNewPoly = -1;

    // set last point to a position outside the rectangle, such that the first
    // time lcl_clip2d returns true, the comparison to last will always yield false
    drawing::Position3D aLast(rRectangle.Left-1.0,rRectangle.Top-1.0, 0.0 );

    for(sal_Int32 nOldPoint=1; nOldPoint<nOldPointCount; nOldPoint++)
    {
        aFrom = getPointFromPoly(rPolygon,nOldPoint-1);
        aTo = getPointFromPoly(rPolygon,nOldPoint);
        if( lcl_clip2d_(aFrom, aTo, rRectangle) )
        {
            // compose an XPolygon of as many consecutive points as possible
            if(aFrom == aLast)
            {
                if( !(aTo==aFrom) )
                    AddPointToPoly( aResult, aTo, nNewPoly );
            }
            else
            {
                nNewPoly++;
                AddPointToPoly( aResult, aFrom, nNewPoly );
                if( !(aTo==aFrom) )
                    AddPointToPoly( aResult, aTo, nNewPoly );
            }
            aLast = aTo;
        }
    }
}

//.............................................................................
} //namespace chart
//.............................................................................