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 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_drawinglayer.hxx"
26 
27 #include <drawinglayer/primitive2d/gridprimitive2d.hxx>
28 #include <basegfx/tools/canvastools.hxx>
29 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
30 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
31 #include <drawinglayer/geometry/viewinformation2d.hxx>
32 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
33 #include <basegfx/matrix/b2dhommatrixtools.hxx>
34 
35 //////////////////////////////////////////////////////////////////////////////
36 
37 using namespace com::sun::star;
38 
39 //////////////////////////////////////////////////////////////////////////////
40 
41 namespace drawinglayer
42 {
43 	namespace primitive2d
44 	{
create2DDecomposition(const geometry::ViewInformation2D & rViewInformation) const45 		Primitive2DSequence GridPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
46 		{
47 			Primitive2DSequence aRetval;
48 
49 			if(!rViewInformation.getViewport().isEmpty() && getWidth() > 0.0 && getHeight() > 0.0)
50 			{
51 				// decompose grid matrix to get logic size
52 				basegfx::B2DVector aScale, aTranslate;
53 				double fRotate, fShearX;
54 				getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
55 
56 				// create grid matrix which transforms from scaled logic to view
57 				basegfx::B2DHomMatrix aRST(basegfx::tools::createShearXRotateTranslateB2DHomMatrix(
58 					fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
59 				aRST *= rViewInformation.getObjectToViewTransformation();
60 
61 				// get step widths
62 				double fStepX(getWidth());
63 				double fStepY(getHeight());
64 				const double fMinimalStep(10.0);
65 
66 				// guarantee a step width of 10.0
67 				if(basegfx::fTools::less(fStepX, fMinimalStep))
68 				{
69 					fStepX = fMinimalStep;
70 				}
71 
72 				if(basegfx::fTools::less(fStepY, fMinimalStep))
73 				{
74 					fStepY = fMinimalStep;
75 				}
76 
77 				// get relative distances in view coordinates
78 				double fViewStepX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fStepX, 0.0)).getLength());
79 				double fViewStepY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fStepY)).getLength());
80 				double fSmallStepX(1.0), fViewSmallStepX(1.0), fSmallStepY(1.0), fViewSmallStepY(1.0);
81 				sal_uInt32 nSmallStepsX(0L), nSmallStepsY(0L);
82 
83 				// setup subdivisions
84 				if(getSubdivisionsX())
85 				{
86 					fSmallStepX = fStepX / getSubdivisionsX();
87 					fViewSmallStepX = fViewStepX / getSubdivisionsX();
88 				}
89 
90 				if(getSubdivisionsY())
91 				{
92 					fSmallStepY = fStepY / getSubdivisionsY();
93 					fViewSmallStepY = fViewStepY / getSubdivisionsY();
94 				}
95 
96 				// correct step width
97 				while(fViewStepX < getSmallestViewDistance())
98 				{
99 					fViewStepX *= 2.0;
100 					fStepX *= 2.0;
101 				}
102 
103 				while(fViewStepY < getSmallestViewDistance())
104 				{
105 					fViewStepY *= 2.0;
106 					fStepY *= 2.0;
107 				}
108 
109 				// correct small step width
110 				if(getSubdivisionsX())
111 				{
112 					while(fViewSmallStepX < getSmallestSubdivisionViewDistance())
113 					{
114 						fViewSmallStepX *= 2.0;
115 						fSmallStepX *= 2.0;
116 					}
117 
118 					nSmallStepsX = (sal_uInt32)(fStepX / fSmallStepX);
119 				}
120 
121 				if(getSubdivisionsY())
122 				{
123 					while(fViewSmallStepY < getSmallestSubdivisionViewDistance())
124 					{
125 						fViewSmallStepY *= 2.0;
126 						fSmallStepY *= 2.0;
127 					}
128 
129 					nSmallStepsY = (sal_uInt32)(fStepY / fSmallStepY);
130 				}
131 
132                 // calculate extended viewport in which grid points may lie at all
133                 basegfx::B2DRange aExtendedViewport;
134 
135                 if(rViewInformation.getDiscreteViewport().isEmpty())
136                 {
137                     // not set, use logic size to travel over all potentioal grid points
138                     aExtendedViewport = basegfx::B2DRange(0.0, 0.0, aScale.getX(), aScale.getY());
139                 }
140                 else
141                 {
142                     // transform unit range to discrete view
143                     aExtendedViewport = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
144                     basegfx::B2DHomMatrix aTrans(rViewInformation.getObjectToViewTransformation() * getTransform());
145                     aExtendedViewport.transform(aTrans);
146 
147                     // intersect with visible part
148                     aExtendedViewport.intersect(rViewInformation.getDiscreteViewport());
149 
150                     if(!aExtendedViewport.isEmpty())
151                     {
152                         // convert back and apply scale
153                         aTrans.invert();
154                         aTrans.scale(aScale.getX(), aScale.getY());
155                         aExtendedViewport.transform(aTrans);
156 
157                         // crop start/end in X/Y to multiples of logical step width
158                         const double fHalfCrossSize((rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(3.0, 0.0)).getLength());
159                         const double fMinX(floor((aExtendedViewport.getMinX() - fHalfCrossSize) / fStepX) * fStepX);
160                         const double fMaxX(ceil((aExtendedViewport.getMaxX() + fHalfCrossSize) / fStepX) * fStepX);
161                         const double fMinY(floor((aExtendedViewport.getMinY() - fHalfCrossSize) / fStepY) * fStepY);
162                         const double fMaxY(ceil((aExtendedViewport.getMaxY() + fHalfCrossSize) / fStepY) * fStepY);
163 
164                         // put to aExtendedViewport and crop on object logic size
165                         aExtendedViewport = basegfx::B2DRange(
166                             std::max(fMinX, 0.0),
167                             std::max(fMinY, 0.0),
168                             std::min(fMaxX, aScale.getX()),
169                             std::min(fMaxY, aScale.getY()));
170                     }
171                 }
172 
173                 if(!aExtendedViewport.isEmpty())
174                 {
175 				    // prepare point vectors for point and cross markers
176 				    std::vector< basegfx::B2DPoint > aPositionsPoint;
177 				    std::vector< basegfx::B2DPoint > aPositionsCross;
178 
179 				    for(double fX(aExtendedViewport.getMinX()); fX < aExtendedViewport.getMaxX(); fX += fStepX)
180 				    {
181 					    const bool bXZero(basegfx::fTools::equalZero(fX));
182 
183 					    for(double fY(aExtendedViewport.getMinY()); fY < aExtendedViewport.getMaxY(); fY += fStepY)
184 					    {
185 						    const bool bYZero(basegfx::fTools::equalZero(fY));
186 
187                             if(!bXZero && !bYZero)
188                             {
189                                 // get discrete position and test against 3x3 area surrounding it
190                                 // since it's a cross
191                                 const double fHalfCrossSize(3.0 * 0.5);
192     						    const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fY));
193                                 const basegfx::B2DRange aDiscreteRangeCross(
194                                     aViewPos.getX() - fHalfCrossSize, aViewPos.getY() - fHalfCrossSize,
195                                     aViewPos.getX() + fHalfCrossSize, aViewPos.getY() + fHalfCrossSize);
196 
197                                 if(rViewInformation.getDiscreteViewport().overlaps(aDiscreteRangeCross))
198                                 {
199 							        const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
200 							        aPositionsCross.push_back(aLogicPos);
201                                 }
202                             }
203 
204 						    if(getSubdivisionsX() && !bYZero)
205 						    {
206 							    double fF(fX + fSmallStepX);
207 
208 							    for(sal_uInt32 a(1); a < nSmallStepsX && fF < aExtendedViewport.getMaxX(); a++, fF += fSmallStepX)
209 							    {
210 								    const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fF, fY));
211 
212 								    if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
213 								    {
214 									    const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
215 									    aPositionsPoint.push_back(aLogicPos);
216 								    }
217 							    }
218 						    }
219 
220 						    if(getSubdivisionsY() && !bXZero)
221 						    {
222 							    double fF(fY + fSmallStepY);
223 
224 							    for(sal_uInt32 a(1); a < nSmallStepsY && fF < aExtendedViewport.getMaxY(); a++, fF += fSmallStepY)
225 							    {
226 								    const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fF));
227 
228 								    if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
229 								    {
230 									    const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
231 									    aPositionsPoint.push_back(aLogicPos);
232 								    }
233 							    }
234 						    }
235 					    }
236 				    }
237 
238 				    // prepare return value
239 				    const sal_uInt32 nCountPoint(aPositionsPoint.size());
240 				    const sal_uInt32 nCountCross(aPositionsCross.size());
241 				    const sal_uInt32 nRetvalCount((nCountPoint ? 1 : 0) + (nCountCross ? 1 : 0));
242 				    sal_uInt32 nInsertCounter(0);
243 
244 				    aRetval.realloc(nRetvalCount);
245 
246 				    // add PointArrayPrimitive2D if point markers were added
247 				    if(nCountPoint)
248 				    {
249 					    aRetval[nInsertCounter++] = Primitive2DReference(new PointArrayPrimitive2D(aPositionsPoint, getBColor()));
250 				    }
251 
252 				    // add MarkerArrayPrimitive2D if cross markers were added
253 				    if(nCountCross)
254 				    {
255     				    if(!getSubdivisionsX() && !getSubdivisionsY())
256                         {
257                             // no subdivisions, so fall back to points at grid positions, no need to
258                             // visualize a difference between divisions and sub-divisions
259     					    aRetval[nInsertCounter++] = Primitive2DReference(new PointArrayPrimitive2D(aPositionsCross, getBColor()));
260                         }
261                         else
262                         {
263     					    aRetval[nInsertCounter++] = Primitive2DReference(new MarkerArrayPrimitive2D(aPositionsCross, getCrossMarker()));
264                         }
265 				    }
266                 }
267 			}
268 
269 			return aRetval;
270 		}
271 
GridPrimitive2D(const basegfx::B2DHomMatrix & rTransform,double fWidth,double fHeight,double fSmallestViewDistance,double fSmallestSubdivisionViewDistance,sal_uInt32 nSubdivisionsX,sal_uInt32 nSubdivisionsY,const basegfx::BColor & rBColor,const BitmapEx & rCrossMarker)272 		GridPrimitive2D::GridPrimitive2D(
273 			const basegfx::B2DHomMatrix& rTransform,
274 			double fWidth,
275 			double fHeight,
276 			double fSmallestViewDistance,
277 			double fSmallestSubdivisionViewDistance,
278 			sal_uInt32 nSubdivisionsX,
279 			sal_uInt32 nSubdivisionsY,
280 			const basegfx::BColor& rBColor,
281 			const BitmapEx& rCrossMarker)
282 		:	BufferedDecompositionPrimitive2D(),
283 			maTransform(rTransform),
284 			mfWidth(fWidth),
285 			mfHeight(fHeight),
286 			mfSmallestViewDistance(fSmallestViewDistance),
287 			mfSmallestSubdivisionViewDistance(fSmallestSubdivisionViewDistance),
288 			mnSubdivisionsX(nSubdivisionsX),
289 			mnSubdivisionsY(nSubdivisionsY),
290 			maBColor(rBColor),
291 			maCrossMarker(rCrossMarker),
292 			maLastObjectToViewTransformation(),
293 			maLastViewport()
294 		{
295 		}
296 
operator ==(const BasePrimitive2D & rPrimitive) const297 		bool GridPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
298 		{
299 			if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
300 			{
301 				const GridPrimitive2D& rCompare = (GridPrimitive2D&)rPrimitive;
302 
303 				return (getTransform() == rCompare.getTransform()
304 					&& getWidth() == rCompare.getWidth()
305 					&& getHeight() == rCompare.getHeight()
306 					&& getSmallestViewDistance() == rCompare.getSmallestViewDistance()
307 					&& getSmallestSubdivisionViewDistance() == rCompare.getSmallestSubdivisionViewDistance()
308 					&& getSubdivisionsX() == rCompare.getSubdivisionsX()
309 					&& getSubdivisionsY() == rCompare.getSubdivisionsY()
310 					&& getBColor() == rCompare.getBColor()
311 					&& getCrossMarker() == rCompare.getCrossMarker());
312 			}
313 
314 			return false;
315 		}
316 
getB2DRange(const geometry::ViewInformation2D & rViewInformation) const317 		basegfx::B2DRange GridPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
318 		{
319 			// get object's range
320 			basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
321 			aUnitRange.transform(getTransform());
322 
323 			// intersect with visible part
324 			aUnitRange.intersect(rViewInformation.getViewport());
325 
326 			return aUnitRange;
327 		}
328 
get2DDecomposition(const geometry::ViewInformation2D & rViewInformation) const329 		Primitive2DSequence GridPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
330 		{
331 			::osl::MutexGuard aGuard( m_aMutex );
332 
333 			if(getBuffered2DDecomposition().hasElements())
334 			{
335 				if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
336 				{
337 					// conditions of last local decomposition have changed, delete
338 					const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DSequence());
339 				}
340 			}
341 
342 			if(!getBuffered2DDecomposition().hasElements())
343 			{
344 				// remember ViewRange and ViewTransformation
345 				const_cast< GridPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
346 				const_cast< GridPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
347 			}
348 
349 			// use parent implementation
350 			return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
351 		}
352 
353 		// provide unique ID
354 		ImplPrimitrive2DIDBlock(GridPrimitive2D, PRIMITIVE2D_ID_GRIDPRIMITIVE2D)
355 
356 	} // end of namespace primitive2d
357 } // end of namespace drawinglayer
358 
359 //////////////////////////////////////////////////////////////////////////////
360 // eof
361