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 6 : Primitive2DSequence GridPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
39 : {
40 6 : Primitive2DSequence aRetval;
41 :
42 6 : 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 : // calculate extended viewport in which grid points may lie at all
126 0 : basegfx::B2DRange aExtendedViewport;
127 :
128 0 : if(rViewInformation.getDiscreteViewport().isEmpty())
129 : {
130 : // not set, use logic size to travel over all potential grid points
131 0 : aExtendedViewport = basegfx::B2DRange(0.0, 0.0, aScale.getX(), aScale.getY());
132 : }
133 : else
134 : {
135 : // transform unit range to discrete view
136 0 : aExtendedViewport = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
137 0 : basegfx::B2DHomMatrix aTrans(rViewInformation.getObjectToViewTransformation() * getTransform());
138 0 : aExtendedViewport.transform(aTrans);
139 :
140 : // intersect with visible part
141 0 : aExtendedViewport.intersect(rViewInformation.getDiscreteViewport());
142 :
143 0 : if(!aExtendedViewport.isEmpty())
144 : {
145 : // convert back and apply scale
146 0 : aTrans.invert();
147 0 : aTrans.scale(aScale.getX(), aScale.getY());
148 0 : aExtendedViewport.transform(aTrans);
149 :
150 : // crop start/end in X/Y to multiples of logical step width
151 0 : const double fHalfCrossSize((rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(3.0, 0.0)).getLength());
152 0 : const double fMinX(floor((aExtendedViewport.getMinX() - fHalfCrossSize) / fStepX) * fStepX);
153 0 : const double fMaxX(ceil((aExtendedViewport.getMaxX() + fHalfCrossSize) / fStepX) * fStepX);
154 0 : const double fMinY(floor((aExtendedViewport.getMinY() - fHalfCrossSize) / fStepY) * fStepY);
155 0 : const double fMaxY(ceil((aExtendedViewport.getMaxY() + fHalfCrossSize) / fStepY) * fStepY);
156 :
157 : // put to aExtendedViewport and crop on object logic size
158 : aExtendedViewport = basegfx::B2DRange(
159 0 : std::max(fMinX, 0.0),
160 0 : std::max(fMinY, 0.0),
161 0 : std::min(fMaxX, aScale.getX()),
162 0 : std::min(fMaxY, aScale.getY()));
163 0 : }
164 : }
165 :
166 0 : if(!aExtendedViewport.isEmpty())
167 : {
168 : // prepare point vectors for point and cross markers
169 0 : std::vector< basegfx::B2DPoint > aPositionsPoint;
170 0 : std::vector< basegfx::B2DPoint > aPositionsCross;
171 :
172 0 : for(double fX(aExtendedViewport.getMinX()); fX < aExtendedViewport.getMaxX(); fX += fStepX)
173 : {
174 0 : const bool bXZero(basegfx::fTools::equalZero(fX));
175 :
176 0 : for(double fY(aExtendedViewport.getMinY()); fY < aExtendedViewport.getMaxY(); fY += fStepY)
177 : {
178 0 : const bool bYZero(basegfx::fTools::equalZero(fY));
179 :
180 0 : if(!bXZero && !bYZero)
181 : {
182 : // get discrete position and test against 3x3 area surrounding it
183 : // since it's a cross
184 0 : const double fHalfCrossSize(3.0 * 0.5);
185 0 : const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fY));
186 : const basegfx::B2DRange aDiscreteRangeCross(
187 0 : aViewPos.getX() - fHalfCrossSize, aViewPos.getY() - fHalfCrossSize,
188 0 : aViewPos.getX() + fHalfCrossSize, aViewPos.getY() + fHalfCrossSize);
189 :
190 0 : if(rViewInformation.getDiscreteViewport().overlaps(aDiscreteRangeCross))
191 : {
192 0 : const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
193 0 : aPositionsCross.push_back(aLogicPos);
194 0 : }
195 : }
196 :
197 0 : if(getSubdivisionsX() && !bYZero)
198 : {
199 0 : double fF(fX + fSmallStepX);
200 :
201 0 : for(sal_uInt32 a(1); a < nSmallStepsX && fF < aExtendedViewport.getMaxX(); a++, fF += fSmallStepX)
202 : {
203 0 : const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fF, fY));
204 :
205 0 : if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
206 : {
207 0 : const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
208 0 : aPositionsPoint.push_back(aLogicPos);
209 : }
210 0 : }
211 : }
212 :
213 0 : if(getSubdivisionsY() && !bXZero)
214 : {
215 0 : double fF(fY + fSmallStepY);
216 :
217 0 : for(sal_uInt32 a(1); a < nSmallStepsY && fF < aExtendedViewport.getMaxY(); a++, fF += fSmallStepY)
218 : {
219 0 : const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fF));
220 :
221 0 : if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
222 : {
223 0 : const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
224 0 : aPositionsPoint.push_back(aLogicPos);
225 : }
226 0 : }
227 : }
228 : }
229 : }
230 :
231 : // prepare return value
232 0 : const sal_uInt32 nCountPoint(aPositionsPoint.size());
233 0 : const sal_uInt32 nCountCross(aPositionsCross.size());
234 0 : const sal_uInt32 nRetvalCount((nCountPoint ? 1 : 0) + (nCountCross ? 1 : 0));
235 0 : sal_uInt32 nInsertCounter(0);
236 :
237 0 : aRetval.realloc(nRetvalCount);
238 :
239 : // add PointArrayPrimitive2D if point markers were added
240 0 : if(nCountPoint)
241 : {
242 0 : aRetval[nInsertCounter++] = Primitive2DReference(new PointArrayPrimitive2D(aPositionsPoint, getBColor()));
243 : }
244 :
245 : // add MarkerArrayPrimitive2D if cross markers were added
246 0 : if(nCountCross)
247 : {
248 0 : if(!getSubdivisionsX() && !getSubdivisionsY())
249 : {
250 : // no subdivisions, so fall back to points at grid positions, no need to
251 : // visualize a difference between divisions and sub-divisions
252 0 : aRetval[nInsertCounter++] = Primitive2DReference(new PointArrayPrimitive2D(aPositionsCross, getBColor()));
253 : }
254 : else
255 : {
256 0 : aRetval[nInsertCounter++] = Primitive2DReference(new MarkerArrayPrimitive2D(aPositionsCross, getCrossMarker()));
257 : }
258 0 : }
259 0 : }
260 : }
261 :
262 6 : return aRetval;
263 : }
264 :
265 846 : GridPrimitive2D::GridPrimitive2D(
266 : const basegfx::B2DHomMatrix& rTransform,
267 : double fWidth,
268 : double fHeight,
269 : double fSmallestViewDistance,
270 : double fSmallestSubdivisionViewDistance,
271 : sal_uInt32 nSubdivisionsX,
272 : sal_uInt32 nSubdivisionsY,
273 : const basegfx::BColor& rBColor,
274 : const BitmapEx& rCrossMarker)
275 : : BufferedDecompositionPrimitive2D(),
276 : maTransform(rTransform),
277 : mfWidth(fWidth),
278 : mfHeight(fHeight),
279 : mfSmallestViewDistance(fSmallestViewDistance),
280 : mfSmallestSubdivisionViewDistance(fSmallestSubdivisionViewDistance),
281 : mnSubdivisionsX(nSubdivisionsX),
282 : mnSubdivisionsY(nSubdivisionsY),
283 : maBColor(rBColor),
284 : maCrossMarker(rCrossMarker),
285 : maLastObjectToViewTransformation(),
286 846 : maLastViewport()
287 : {
288 846 : }
289 :
290 572 : bool GridPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
291 : {
292 572 : if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
293 : {
294 572 : const GridPrimitive2D& rCompare = static_cast<const GridPrimitive2D&>(rPrimitive);
295 :
296 572 : return (getTransform() == rCompare.getTransform()
297 232 : && getWidth() == rCompare.getWidth()
298 232 : && getHeight() == rCompare.getHeight()
299 232 : && getSmallestViewDistance() == rCompare.getSmallestViewDistance()
300 232 : && getSmallestSubdivisionViewDistance() == rCompare.getSmallestSubdivisionViewDistance()
301 232 : && getSubdivisionsX() == rCompare.getSubdivisionsX()
302 232 : && getSubdivisionsY() == rCompare.getSubdivisionsY()
303 232 : && getBColor() == rCompare.getBColor()
304 804 : && getCrossMarker() == rCompare.getCrossMarker());
305 : }
306 :
307 0 : return false;
308 : }
309 :
310 1466 : basegfx::B2DRange GridPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
311 : {
312 : // get object's range
313 1466 : basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
314 1466 : aUnitRange.transform(getTransform());
315 :
316 : // intersect with visible part
317 1466 : aUnitRange.intersect(rViewInformation.getViewport());
318 :
319 1466 : return aUnitRange;
320 : }
321 :
322 6 : Primitive2DSequence GridPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
323 : {
324 6 : ::osl::MutexGuard aGuard( m_aMutex );
325 :
326 6 : if(getBuffered2DDecomposition().hasElements())
327 : {
328 0 : if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
329 : {
330 : // conditions of last local decomposition have changed, delete
331 0 : const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DSequence());
332 : }
333 : }
334 :
335 6 : if(!getBuffered2DDecomposition().hasElements())
336 : {
337 : // remember ViewRange and ViewTransformation
338 6 : const_cast< GridPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
339 6 : const_cast< GridPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
340 : }
341 :
342 : // use parent implementation
343 6 : return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
344 : }
345 :
346 : // provide unique ID
347 1764 : ImplPrimitive2DIDBlock(GridPrimitive2D, PRIMITIVE2D_ID_GRIDPRIMITIVE2D)
348 :
349 : } // end of namespace primitive2d
350 : } // end of namespace drawinglayer
351 :
352 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|