1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_drawinglayer.hxx"
30 
31 #include <drawinglayer/primitive2d/gridprimitive2d.hxx>
32 #include <basegfx/tools/canvastools.hxx>
33 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
34 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
35 #include <drawinglayer/geometry/viewinformation2d.hxx>
36 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
37 #include <basegfx/matrix/b2dhommatrixtools.hxx>
38 
39 //////////////////////////////////////////////////////////////////////////////
40 
41 using namespace com::sun::star;
42 
43 //////////////////////////////////////////////////////////////////////////////
44 
45 namespace drawinglayer
46 {
47 	namespace primitive2d
48 	{
49 		Primitive2DSequence GridPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
50 		{
51 			Primitive2DSequence aRetval;
52 
53 			if(!rViewInformation.getViewport().isEmpty() && getWidth() > 0.0 && getHeight() > 0.0)
54 			{
55 				// decompose grid matrix to get logic size
56 				basegfx::B2DVector aScale, aTranslate;
57 				double fRotate, fShearX;
58 				getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
59 
60 				// create grid matrix which transforms from scaled logic to view
61 				basegfx::B2DHomMatrix aRST(basegfx::tools::createShearXRotateTranslateB2DHomMatrix(
62 					fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
63 				aRST *= rViewInformation.getObjectToViewTransformation();
64 
65 				// get step widths
66 				double fStepX(getWidth());
67 				double fStepY(getHeight());
68 				const double fMinimalStep(10.0);
69 
70 				// guarantee a step width of 10.0
71 				if(basegfx::fTools::less(fStepX, fMinimalStep))
72 				{
73 					fStepX = fMinimalStep;
74 				}
75 
76 				if(basegfx::fTools::less(fStepY, fMinimalStep))
77 				{
78 					fStepY = fMinimalStep;
79 				}
80 
81 				// get relative distances in view coordinates
82 				double fViewStepX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fStepX, 0.0)).getLength());
83 				double fViewStepY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fStepY)).getLength());
84 				double fSmallStepX(1.0), fViewSmallStepX(1.0), fSmallStepY(1.0), fViewSmallStepY(1.0);
85 				sal_uInt32 nSmallStepsX(0L), nSmallStepsY(0L);
86 
87 				// setup subdivisions
88 				if(getSubdivisionsX())
89 				{
90 					fSmallStepX = fStepX / getSubdivisionsX();
91 					fViewSmallStepX = fViewStepX / getSubdivisionsX();
92 				}
93 
94 				if(getSubdivisionsY())
95 				{
96 					fSmallStepY = fStepY / getSubdivisionsY();
97 					fViewSmallStepY = fViewStepY / getSubdivisionsY();
98 				}
99 
100 				// correct step width
101 				while(fViewStepX < getSmallestViewDistance())
102 				{
103 					fViewStepX *= 2.0;
104 					fStepX *= 2.0;
105 				}
106 
107 				while(fViewStepY < getSmallestViewDistance())
108 				{
109 					fViewStepY *= 2.0;
110 					fStepY *= 2.0;
111 				}
112 
113 				// correct small step width
114 				if(getSubdivisionsX())
115 				{
116 					while(fViewSmallStepX < getSmallestSubdivisionViewDistance())
117 					{
118 						fViewSmallStepX *= 2.0;
119 						fSmallStepX *= 2.0;
120 					}
121 
122 					nSmallStepsX = (sal_uInt32)(fStepX / fSmallStepX);
123 				}
124 
125 				if(getSubdivisionsY())
126 				{
127 					while(fViewSmallStepY < getSmallestSubdivisionViewDistance())
128 					{
129 						fViewSmallStepY *= 2.0;
130 						fSmallStepY *= 2.0;
131 					}
132 
133 					nSmallStepsY = (sal_uInt32)(fStepY / fSmallStepY);
134 				}
135 
136 				// prepare point vectors for point and cross markers
137 				std::vector< basegfx::B2DPoint > aPositionsPoint;
138 				std::vector< basegfx::B2DPoint > aPositionsCross;
139 
140 				for(double fX(0.0); fX < aScale.getX(); fX += fStepX)
141 				{
142 					const bool bXZero(basegfx::fTools::equalZero(fX));
143 
144 					for(double fY(0.0); fY < aScale.getY(); fY += fStepY)
145 					{
146 						const bool bYZero(basegfx::fTools::equalZero(fY));
147 
148                         if(!bXZero && !bYZero)
149                         {
150                             // get discrete position and test against 3x3 area surrounding it
151                             // since it's a cross
152                             const double fHalfCrossSize(3.0 * 0.5);
153     						const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fY));
154                             const basegfx::B2DRange aDiscreteRangeCross(
155                                 aViewPos.getX() - fHalfCrossSize, aViewPos.getY() - fHalfCrossSize,
156                                 aViewPos.getX() + fHalfCrossSize, aViewPos.getY() + fHalfCrossSize);
157 
158                             if(rViewInformation.getDiscreteViewport().overlaps(aDiscreteRangeCross))
159                             {
160 							    const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
161 							    aPositionsCross.push_back(aLogicPos);
162                             }
163                         }
164 
165 						if(getSubdivisionsX() && !bYZero)
166 						{
167 							double fF(fX + fSmallStepX);
168 
169 							for(sal_uInt32 a(1L); a < nSmallStepsX && fF < aScale.getX(); a++, fF += fSmallStepX)
170 							{
171 								const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fF, fY));
172 
173 								if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
174 								{
175 									const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
176 									aPositionsPoint.push_back(aLogicPos);
177 								}
178 							}
179 						}
180 
181 						if(getSubdivisionsY() && !bXZero)
182 						{
183 							double fF(fY + fSmallStepY);
184 
185 							for(sal_uInt32 a(1L); a < nSmallStepsY && fF < aScale.getY(); a++, fF += fSmallStepY)
186 							{
187 								const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fF));
188 
189 								if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
190 								{
191 									const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
192 									aPositionsPoint.push_back(aLogicPos);
193 								}
194 							}
195 						}
196 					}
197 				}
198 
199 				// prepare return value
200 				const sal_uInt32 nCountPoint(aPositionsPoint.size());
201 				const sal_uInt32 nCountCross(aPositionsCross.size());
202 				const sal_uInt32 nRetvalCount((nCountPoint ? 1 : 0) + (nCountCross ? 1 : 0));
203 				sal_uInt32 nInsertCounter(0);
204 
205 				aRetval.realloc(nRetvalCount);
206 
207 				// add PointArrayPrimitive2D if point markers were added
208 				if(nCountPoint)
209 				{
210 					aRetval[nInsertCounter++] = Primitive2DReference(new PointArrayPrimitive2D(aPositionsPoint, getBColor()));
211 				}
212 
213 				// add MarkerArrayPrimitive2D if cross markers were added
214 				if(nCountCross)
215 				{
216     				if(!getSubdivisionsX() && !getSubdivisionsY())
217                     {
218                         // no subdivisions, so fall back to points at grid positions, no need to
219                         // visualize a difference between divisions and sub-divisions
220     					aRetval[nInsertCounter++] = Primitive2DReference(new PointArrayPrimitive2D(aPositionsCross, getBColor()));
221                     }
222                     else
223                     {
224     					aRetval[nInsertCounter++] = Primitive2DReference(new MarkerArrayPrimitive2D(aPositionsCross, getCrossMarker()));
225                     }
226 				}
227 			}
228 
229 			return aRetval;
230 		}
231 
232 		GridPrimitive2D::GridPrimitive2D(
233 			const basegfx::B2DHomMatrix& rTransform,
234 			double fWidth,
235 			double fHeight,
236 			double fSmallestViewDistance,
237 			double fSmallestSubdivisionViewDistance,
238 			sal_uInt32 nSubdivisionsX,
239 			sal_uInt32 nSubdivisionsY,
240 			const basegfx::BColor& rBColor,
241 			const BitmapEx& rCrossMarker)
242 		:	BufferedDecompositionPrimitive2D(),
243 			maTransform(rTransform),
244 			mfWidth(fWidth),
245 			mfHeight(fHeight),
246 			mfSmallestViewDistance(fSmallestViewDistance),
247 			mfSmallestSubdivisionViewDistance(fSmallestSubdivisionViewDistance),
248 			mnSubdivisionsX(nSubdivisionsX),
249 			mnSubdivisionsY(nSubdivisionsY),
250 			maBColor(rBColor),
251 			maCrossMarker(rCrossMarker),
252 			maLastObjectToViewTransformation(),
253 			maLastViewport()
254 		{
255 		}
256 
257 		bool GridPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
258 		{
259 			if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
260 			{
261 				const GridPrimitive2D& rCompare = (GridPrimitive2D&)rPrimitive;
262 
263 				return (getTransform() == rCompare.getTransform()
264 					&& getWidth() == rCompare.getWidth()
265 					&& getHeight() == rCompare.getHeight()
266 					&& getSmallestViewDistance() == rCompare.getSmallestViewDistance()
267 					&& getSmallestSubdivisionViewDistance() == rCompare.getSmallestSubdivisionViewDistance()
268 					&& getSubdivisionsX() == rCompare.getSubdivisionsX()
269 					&& getSubdivisionsY() == rCompare.getSubdivisionsY()
270 					&& getBColor() == rCompare.getBColor()
271 					&& getCrossMarker() == rCompare.getCrossMarker());
272 			}
273 
274 			return false;
275 		}
276 
277 		basegfx::B2DRange GridPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
278 		{
279 			// get object's range
280 			basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
281 			aUnitRange.transform(getTransform());
282 
283 			// intersect with visible part
284 			aUnitRange.intersect(rViewInformation.getViewport());
285 
286 			return aUnitRange;
287 		}
288 
289 		Primitive2DSequence GridPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
290 		{
291 			::osl::MutexGuard aGuard( m_aMutex );
292 
293 			if(getBuffered2DDecomposition().hasElements())
294 			{
295 				if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
296 				{
297 					// conditions of last local decomposition have changed, delete
298 					const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DSequence());
299 				}
300 			}
301 
302 			if(!getBuffered2DDecomposition().hasElements())
303 			{
304 				// remember ViewRange and ViewTransformation
305 				const_cast< GridPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
306 				const_cast< GridPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
307 			}
308 
309 			// use parent implementation
310 			return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
311 		}
312 
313 		// provide unique ID
314 		ImplPrimitrive2DIDBlock(GridPrimitive2D, PRIMITIVE2D_ID_GRIDPRIMITIVE2D)
315 
316 	} // end of namespace primitive2d
317 } // end of namespace drawinglayer
318 
319 //////////////////////////////////////////////////////////////////////////////
320 // eof
321