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