xref: /trunk/main/chart2/source/view/main/Clipping.cxx (revision 74cbd1f1)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 
25 // MARKER(update_precomp.py): autogen include statement, do not remove
26 #include "precompiled_chart2.hxx"
27 
28 #include "Clipping.hxx"
29 #include "CommonConverters.hxx"
30 #include "BaseGFXHelper.hxx"
31 
32 #include <com/sun/star/drawing/Position3D.hpp>
33 #include <com/sun/star/drawing/DoubleSequence.hpp>
34 
35 //.............................................................................
36 namespace chart
37 {
38 //.............................................................................
39 using namespace ::com::sun::star;
40 using ::basegfx::B2DRectangle;
41 using ::basegfx::B2DTuple;
42 
43 //-----------------------------------------------------------------------------
44 //-----------------------------------------------------------------------------
45 //-----------------------------------------------------------------------------
46 
47 namespace{
48 /**	@descr	This is a supporting function for lcl_clip2d.  It computes a new parametric
49 			value for an entering (dTE) or leaving (dTL) intersection point with one
50 			of the edges bounding the clipping area.
51 			For explanation of the parameters please refer to :
52 
53             Liang-Biarsky parametric line-clipping algorithm as described in:
54 			Computer Graphics: principles and practice, 2nd ed.,
55 			James D. Foley et al.,
56 			Section 3.12.4 on page 117.
57 */
lcl_CLIPt(double fDenom,double fNum,double & fTE,double & fTL)58 bool lcl_CLIPt(double fDenom,double fNum, double & fTE, double & fTL)
59 {
60 	double	fT;
61 
62 	if (fDenom > 0)				//	Intersection enters: PE
63 	{
64 		fT = fNum / fDenom;		//	Parametric value at the intersection.
65 		if (fT > fTL)			//	fTE and fTL crossover
66 			return false;		//	  therefore reject the line.
67 		else if (fT > fTE)		//	A new fTE has been found.
68 			fTE = fT;
69 	}
70 	else if (fDenom < 0)		//	Intersection leaves: PL
71 	{
72 		fT = fNum / fDenom;		//	Parametric Value at the intersection.
73 		if (fT < fTE)			//	fTE and fTL crossover
74 			return false;		//	  therefore reject the line.
75 		else if (fT < fTL)		//	A new fTL has been found.
76 			fTL = fT;
77 	}
78 	else if (fNum > 0)
79 		return false;			//	Line lies on the outside of the edge.
80 
81 	return true;
82 }
83 
84 /**	@descr	The line given by it's two endpoints rP0 and rP1 is clipped at the rectangle
85 			rRectangle.  If there is at least a part of it visible then sal_True is returned and
86 			the endpoints of that part are stored in rP0 and rP1.  The points rP0 and rP1
87 			may have the same coordinates.
88 		@param	rP0 Start point of the line to clip.  Modified to contain a start point inside
89 			the clipping area if possible.
90 		@param	rP1 End point of the line to clip.  Modified to contain an end point inside
91 			the clipping area if possible.
92 		@param	rRectangle Clipping area.
93 		@return	If the line lies completely or partly inside the clipping area then TRUE
94 			is returned.  If the line lies completely outside then sal_False is returned and rP0 and
95 			rP1 are left unmodified.
96 */
lcl_clip2d(B2DTuple & rPoint0,B2DTuple & rPoint1,const B2DRectangle & rRectangle)97 bool lcl_clip2d(B2DTuple& rPoint0, B2DTuple& rPoint1, const B2DRectangle& rRectangle)
98 {
99 	//Direction vector of the line.
100     B2DTuple aDirection = rPoint1 - rPoint0;
101 
102 	if( aDirection.getX()==0 && aDirection.getY()==0 && rRectangle.isInside(rPoint0) )
103 	{
104 		//	Degenerate case of a zero length line.
105 		return true;
106 	}
107 	else
108 	{
109 		//	Values of the line parameter where the line enters resp. leaves the rectangle.
110 		double fTE = 0,
111 			   fTL = 1;
112 
113 		//	Test whether at least a part lies in the four half-planes with respect to
114 		//	the rectangles four edges.
115 		if( lcl_CLIPt(aDirection.getX(), rRectangle.getMinX() - rPoint0.getX(), fTE, fTL) )
116 			if( lcl_CLIPt(-aDirection.getX(), rPoint0.getX() - rRectangle.getMaxX(), fTE, fTL) )
117 				if( lcl_CLIPt(aDirection.getY(), rRectangle.getMinY() - rPoint0.getY(), fTE, fTL) )
118 					if( lcl_CLIPt(-aDirection.getY(), rPoint0.getY() - rRectangle.getMaxY(), fTE, fTL) )
119 					{
120 						//	At least a part is visible.
121 						if (fTL < 1)
122 						{
123 							//	Compute the new end point.
124 							rPoint1.setX( rPoint0.getX() + fTL * aDirection.getX() );
125 							rPoint1.setY( rPoint0.getY() + fTL * aDirection.getY() );
126 						}
127 						if (fTE > 0)
128 						{
129 							//	Compute the new starting point.
130 							rPoint0.setX( rPoint0.getX() + fTE * aDirection.getX() );
131 							rPoint0.setY( rPoint0.getY() + fTE * aDirection.getY() );
132 						}
133 						return true;
134 					}
135 
136 		//	Line is not visible.
137 		return false;
138 	}
139 }
140 
lcl_clip2d_(drawing::Position3D & rPoint0,drawing::Position3D & rPoint1,const B2DRectangle & rRectangle)141 bool lcl_clip2d_(drawing::Position3D& rPoint0, drawing::Position3D& rPoint1, const B2DRectangle& rRectangle)
142 {
143     B2DTuple aP0(rPoint0.PositionX,rPoint0.PositionY);
144     B2DTuple aP1(rPoint1.PositionX,rPoint1.PositionY);
145     bool bRet = lcl_clip2d( aP0, aP1, rRectangle );
146 
147     rPoint0.PositionX = aP0.getX();
148     rPoint0.PositionY = aP0.getY();
149     rPoint1.PositionX = aP1.getX();
150     rPoint1.PositionY = aP1.getY();
151 
152     return bRet;
153 }
154 
lcl_addPointToPoly(drawing::PolyPolygonShape3D & rPoly,const drawing::Position3D & rPos,sal_Int32 nPolygonIndex,std::vector<sal_Int32> & rResultPointCount,sal_Int32 nReservePointCount)155 void lcl_addPointToPoly( drawing::PolyPolygonShape3D& rPoly
156         , const drawing::Position3D& rPos
157         , sal_Int32 nPolygonIndex
158         , std::vector< sal_Int32 >& rResultPointCount
159         , sal_Int32 nReservePointCount )
160 {
161     if(nPolygonIndex<0)
162     {
163         OSL_ENSURE( false, "The polygon index needs to be > 0");
164         nPolygonIndex=0;
165     }
166 
167     //make sure that we have enough polygons
168     if(nPolygonIndex >= rPoly.SequenceX.getLength() )
169     {
170         rPoly.SequenceX.realloc(nPolygonIndex+1);
171         rPoly.SequenceY.realloc(nPolygonIndex+1);
172         rPoly.SequenceZ.realloc(nPolygonIndex+1);
173         rResultPointCount.resize(nPolygonIndex+1,0);
174     }
175 
176     drawing::DoubleSequence* pOuterSequenceX = &rPoly.SequenceX.getArray()[nPolygonIndex];
177     drawing::DoubleSequence* pOuterSequenceY = &rPoly.SequenceY.getArray()[nPolygonIndex];
178     drawing::DoubleSequence* pOuterSequenceZ = &rPoly.SequenceZ.getArray()[nPolygonIndex];
179 
180     sal_Int32 nNewResultPointCount = rResultPointCount[nPolygonIndex]+1;
181     sal_Int32 nSeqLength = pOuterSequenceX->getLength();
182 
183     if( nSeqLength <= nNewResultPointCount )
184     {
185         sal_Int32 nReallocLength = nReservePointCount;
186         if( nNewResultPointCount > nReallocLength )
187         {
188             nReallocLength = nNewResultPointCount;
189             DBG_ERROR("this should not be the case to avoid performance problems");
190         }
191         pOuterSequenceX->realloc(nReallocLength);
192         pOuterSequenceY->realloc(nReallocLength);
193         pOuterSequenceZ->realloc(nReallocLength);
194     }
195 
196     double* pInnerSequenceX = pOuterSequenceX->getArray();
197     double* pInnerSequenceY = pOuterSequenceY->getArray();
198     double* pInnerSequenceZ = pOuterSequenceZ->getArray();
199 
200     pInnerSequenceX[nNewResultPointCount-1] = rPos.PositionX;
201     pInnerSequenceY[nNewResultPointCount-1] = rPos.PositionY;
202     pInnerSequenceZ[nNewResultPointCount-1] = rPos.PositionZ;
203     rResultPointCount[nPolygonIndex]=nNewResultPointCount;
204 }
205 
206 }//end anonymous namespace
207 
clipPolygonAtRectangle(const drawing::PolyPolygonShape3D & rPolygon,const B2DRectangle & rRectangle,drawing::PolyPolygonShape3D & aResult,bool bSplitPiecesToDifferentPolygons)208 void Clipping::clipPolygonAtRectangle( const drawing::PolyPolygonShape3D& rPolygon
209                                       , const B2DRectangle& rRectangle
210                                       , drawing::PolyPolygonShape3D& aResult
211                                       , bool bSplitPiecesToDifferentPolygons )
212 {
213     aResult.SequenceX.realloc(0);
214     aResult.SequenceY.realloc(0);
215     aResult.SequenceZ.realloc(0);
216 
217     if(!rPolygon.SequenceX.getLength())
218         return;
219 
220     //need clipping?:
221     {
222         ::basegfx::B3DRange a3DRange( BaseGFXHelper::getBoundVolume( rPolygon ) );
223         ::basegfx::B2DRange a2DRange( a3DRange.getMinX(), a3DRange.getMinY(), a3DRange.getMaxX(), a3DRange.getMaxY() );
224         if( rRectangle.isInside( a2DRange ) )
225         {
226 		    aResult = rPolygon;
227             return;
228 	    }
229         else
230         {
231             a2DRange.intersect( rRectangle );
232             if( a2DRange.isEmpty() )
233                 return;
234         }
235     }
236 
237     //
238     std::vector< sal_Int32 > aResultPointCount;//per polygon index
239 
240     //apply clipping:
241     drawing::Position3D aFrom;
242     drawing::Position3D aTo;
243 
244     sal_Int32 nNewPolyIndex = 0;
245     sal_Int32 nOldPolyCount = rPolygon.SequenceX.getLength();
246     for(sal_Int32 nOldPolyIndex=0; nOldPolyIndex<nOldPolyCount; nOldPolyIndex++, nNewPolyIndex++ )
247     {
248         sal_Int32 nOldPointCount = rPolygon.SequenceX[nOldPolyIndex].getLength();
249 
250         // set last point to a position outside the rectangle, such that the first
251         // time lcl_clip2d returns true, the comparison to last will always yield false
252         drawing::Position3D aLast(rRectangle.getMinX()-1.0,rRectangle.getMinY()-1.0, 0.0 );
253 
254 	    for(sal_Int32 nOldPoint=1; nOldPoint<nOldPointCount; nOldPoint++)
255 	    {
256 		    aFrom = getPointFromPoly(rPolygon,nOldPoint-1,nOldPolyIndex);
257 		    aTo = getPointFromPoly(rPolygon,nOldPoint,nOldPolyIndex);
258 		    if( lcl_clip2d_(aFrom, aTo, rRectangle) )
259 		    {
260                 // compose an Polygon of as many consecutive points as possible
261                 if(aFrom == aLast)
262                 {
263                     if( !(aTo==aFrom) )
264                     {
265                         lcl_addPointToPoly( aResult, aTo, nNewPolyIndex, aResultPointCount, nOldPointCount );
266                     }
267                 }
268                 else
269                 {
270                     if( bSplitPiecesToDifferentPolygons && nOldPoint!=1 )
271                     {
272                         if( nNewPolyIndex < aResult.SequenceX.getLength()
273                                 && aResultPointCount[nNewPolyIndex]>0 )
274                             nNewPolyIndex++;
275                     }
276                     lcl_addPointToPoly( aResult, aFrom, nNewPolyIndex, aResultPointCount, nOldPointCount );
277                     if( !(aTo==aFrom) )
278                         lcl_addPointToPoly( aResult, aTo, nNewPolyIndex, aResultPointCount, nOldPointCount );
279                 }
280                 aLast = aTo;
281             }
282         }
283     }
284     //free unused space
285     for( sal_Int32 nPolygonIndex = aResultPointCount.size(); nPolygonIndex--; )
286     {
287         drawing::DoubleSequence* pOuterSequenceX = &aResult.SequenceX.getArray()[nPolygonIndex];
288         drawing::DoubleSequence* pOuterSequenceY = &aResult.SequenceY.getArray()[nPolygonIndex];
289         drawing::DoubleSequence* pOuterSequenceZ = &aResult.SequenceZ.getArray()[nPolygonIndex];
290 
291         sal_Int32 nUsedPointCount = aResultPointCount[nPolygonIndex];
292         pOuterSequenceX->realloc(nUsedPointCount);
293         pOuterSequenceY->realloc(nUsedPointCount);
294         pOuterSequenceZ->realloc(nUsedPointCount);
295     }
296 }
297 
298 //.............................................................................
299 } //namespace chart
300 //.............................................................................
301