LibreOffice Module drawinglayer (master) 1
gridprimitive2d.cxx
Go to the documentation of this file.
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
26#include <utility>
27
28
29using namespace com::sun::star;
30
31
33{
35 {
36 if(!(!rViewInformation.getViewport().isEmpty() && getWidth() > 0.0 && getHeight() > 0.0))
37 return;
38
39 // decompose grid matrix to get logic size
40 basegfx::B2DVector aScale, aTranslate;
41 double fRotate, fShearX;
42 getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
43
44 // create grid matrix which transforms from scaled logic to view
46 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
47 aRST *= rViewInformation.getObjectToViewTransformation();
48
49 // get step widths
50 double fStepX(getWidth());
51 double fStepY(getHeight());
52 const double fMinimalStep(10.0);
53
54 // guarantee a step width of 10.0
55 if(basegfx::fTools::less(fStepX, fMinimalStep))
56 {
57 fStepX = fMinimalStep;
58 }
59
60 if(basegfx::fTools::less(fStepY, fMinimalStep))
61 {
62 fStepY = fMinimalStep;
63 }
64
65 // get relative distances in view coordinates
66 double fViewStepX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fStepX, 0.0)).getLength());
67 double fViewStepY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fStepY)).getLength());
68 double fSmallStepX(1.0), fViewSmallStepX(1.0), fSmallStepY(1.0), fViewSmallStepY(1.0);
69 sal_uInt32 nSmallStepsX(0), nSmallStepsY(0);
70
71 // setup subdivisions
73 {
74 fSmallStepX = fStepX / getSubdivisionsX();
75 fViewSmallStepX = fViewStepX / getSubdivisionsX();
76 }
77
79 {
80 fSmallStepY = fStepY / getSubdivisionsY();
81 fViewSmallStepY = fViewStepY / getSubdivisionsY();
82 }
83
84 // correct step width
85 while(fViewStepX < getSmallestViewDistance())
86 {
87 fViewStepX *= 2.0;
88 fStepX *= 2.0;
89 }
90
91 while(fViewStepY < getSmallestViewDistance())
92 {
93 fViewStepY *= 2.0;
94 fStepY *= 2.0;
95 }
96
97 // correct small step width
99 {
100 while(fViewSmallStepX < getSmallestSubdivisionViewDistance())
101 {
102 fViewSmallStepX *= 2.0;
103 fSmallStepX *= 2.0;
104 }
105
106 nSmallStepsX = static_cast<sal_uInt32>(fStepX / fSmallStepX);
107 }
108
109 if(getSubdivisionsY())
110 {
111 while(fViewSmallStepY < getSmallestSubdivisionViewDistance())
112 {
113 fViewSmallStepY *= 2.0;
114 fSmallStepY *= 2.0;
115 }
116
117 nSmallStepsY = static_cast<sal_uInt32>(fStepY / fSmallStepY);
118 }
119
120 // calculate extended viewport in which grid points may lie at all
121 basegfx::B2DRange aExtendedViewport;
122
123 if(rViewInformation.getDiscreteViewport().isEmpty())
124 {
125 // not set, use logic size to travel over all potential grid points
126 aExtendedViewport = basegfx::B2DRange(0.0, 0.0, aScale.getX(), aScale.getY());
127 }
128 else
129 {
130 // transform unit range to discrete view
131 aExtendedViewport = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
133 aExtendedViewport.transform(aTrans);
134
135 // intersect with visible part
136 aExtendedViewport.intersect(rViewInformation.getDiscreteViewport());
137
138 if(!aExtendedViewport.isEmpty())
139 {
140 // convert back and apply scale
141 aTrans.invert();
142 aTrans.scale(aScale.getX(), aScale.getY());
143 aExtendedViewport.transform(aTrans);
144
145 // crop start/end in X/Y to multiples of logical step width
146 const double fHalfCrossSize((rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(3.0, 0.0)).getLength());
147 const double fMinX(floor((aExtendedViewport.getMinX() - fHalfCrossSize) / fStepX) * fStepX);
148 const double fMaxX(ceil((aExtendedViewport.getMaxX() + fHalfCrossSize) / fStepX) * fStepX);
149 const double fMinY(floor((aExtendedViewport.getMinY() - fHalfCrossSize) / fStepY) * fStepY);
150 const double fMaxY(ceil((aExtendedViewport.getMaxY() + fHalfCrossSize) / fStepY) * fStepY);
151
152 // put to aExtendedViewport and crop on object logic size
153 aExtendedViewport = basegfx::B2DRange(
154 std::max(fMinX, 0.0),
155 std::max(fMinY, 0.0),
156 std::min(fMaxX, aScale.getX()),
157 std::min(fMaxY, aScale.getY()));
158 }
159 }
160
161 if(aExtendedViewport.isEmpty())
162 return;
163
164 // prepare point vectors for point and cross markers
165 std::vector< basegfx::B2DPoint > aPositionsPoint;
166 std::vector< basegfx::B2DPoint > aPositionsCross;
167
168 for(double fX(aExtendedViewport.getMinX()); fX < aExtendedViewport.getMaxX(); fX += fStepX)
169 {
170 const bool bXZero(basegfx::fTools::equalZero(fX));
171
172 for(double fY(aExtendedViewport.getMinY()); fY < aExtendedViewport.getMaxY(); fY += fStepY)
173 {
174 const bool bYZero(basegfx::fTools::equalZero(fY));
175
176 if(!bXZero && !bYZero)
177 {
178 // get discrete position and test against 3x3 area surrounding it
179 // since it's a cross
180 const double fHalfCrossSize(3.0 * 0.5);
181 const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fY));
182 const basegfx::B2DRange aDiscreteRangeCross(
183 aViewPos.getX() - fHalfCrossSize, aViewPos.getY() - fHalfCrossSize,
184 aViewPos.getX() + fHalfCrossSize, aViewPos.getY() + fHalfCrossSize);
185
186 if(rViewInformation.getDiscreteViewport().overlaps(aDiscreteRangeCross))
187 {
188 const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
189 aPositionsCross.push_back(aLogicPos);
190 }
191 }
192
193 if(getSubdivisionsX() && !bYZero)
194 {
195 double fF(fX + fSmallStepX);
196
197 for(sal_uInt32 a(1); a < nSmallStepsX && fF < aExtendedViewport.getMaxX(); a++, fF += fSmallStepX)
198 {
199 const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fF, fY));
200
201 if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
202 {
203 const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
204 aPositionsPoint.push_back(aLogicPos);
205 }
206 }
207 }
208
209 if(getSubdivisionsY() && !bXZero)
210 {
211 double fF(fY + fSmallStepY);
212
213 for(sal_uInt32 a(1); a < nSmallStepsY && fF < aExtendedViewport.getMaxY(); a++, fF += fSmallStepY)
214 {
215 const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fF));
216
217 if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
218 {
219 const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
220 aPositionsPoint.push_back(aLogicPos);
221 }
222 }
223 }
224 }
225 }
226
227 // prepare return value
228 const sal_uInt32 nCountPoint(aPositionsPoint.size());
229 const sal_uInt32 nCountCross(aPositionsCross.size());
230
231 // add PointArrayPrimitive2D if point markers were added
232 if(nCountPoint)
233 {
234 rContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsPoint), getBColor()));
235 }
236
237 // add MarkerArrayPrimitive2D if cross markers were added
238 if(!nCountCross)
239 return;
240
242 {
243 // no subdivisions, so fall back to points at grid positions, no need to
244 // visualize a difference between divisions and sub-divisions
245 rContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsCross), getBColor()));
246 }
247 else
248 {
249 rContainer.push_back(new MarkerArrayPrimitive2D(std::move(aPositionsCross), getCrossMarker()));
250 }
251 }
252
254 basegfx::B2DHomMatrix aTransform,
255 double fWidth,
256 double fHeight,
257 double fSmallestViewDistance,
258 double fSmallestSubdivisionViewDistance,
259 sal_uInt32 nSubdivisionsX,
260 sal_uInt32 nSubdivisionsY,
261 const basegfx::BColor& rBColor,
262 const BitmapEx& rCrossMarker)
263 : maTransform(std::move(aTransform)),
264 mfWidth(fWidth),
265 mfHeight(fHeight),
266 mfSmallestViewDistance(fSmallestViewDistance),
267 mfSmallestSubdivisionViewDistance(fSmallestSubdivisionViewDistance),
268 mnSubdivisionsX(nSubdivisionsX),
269 mnSubdivisionsY(nSubdivisionsY),
270 maBColor(rBColor),
271 maCrossMarker(rCrossMarker)
272 {
273 }
274
275 bool GridPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
276 {
277 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
278 {
279 const GridPrimitive2D& rCompare = static_cast<const GridPrimitive2D&>(rPrimitive);
280
281 return (getTransform() == rCompare.getTransform()
282 && getWidth() == rCompare.getWidth()
283 && getHeight() == rCompare.getHeight()
286 && getSubdivisionsX() == rCompare.getSubdivisionsX()
287 && getSubdivisionsY() == rCompare.getSubdivisionsY()
288 && getBColor() == rCompare.getBColor()
289 && getCrossMarker() == rCompare.getCrossMarker());
290 }
291
292 return false;
293 }
294
296 {
297 // get object's range
298 basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
299 aUnitRange.transform(getTransform());
300
301 // intersect with visible part
302 aUnitRange.intersect(rViewInformation.getViewport());
303
304 return aUnitRange;
305 }
306
308 {
309 if(!getBuffered2DDecomposition().empty())
310 {
311 if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
312 {
313 // conditions of last local decomposition have changed, delete
315 }
316 }
317
318 if(getBuffered2DDecomposition().empty())
319 {
320 // remember ViewRange and ViewTransformation
321 const_cast< GridPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
322 const_cast< GridPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
323 }
324
325 // use parent implementation
327 }
328
329 // provide unique ID
331 {
333 }
334
335} // end of namespace
336
337/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void scale(double fX, double fY)
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
TYPE getMaxX() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
void intersect(const Range2D &rRange)
bool isInside(const Tuple2D< TYPE > &rTuple) const
bool isEmpty() const
bool overlaps(const Range2D &rRange) const
TYPE getX() const
TYPE getY() const
const basegfx::B2DRange & getViewport() const
Empty viewport means everything is visible.
const basegfx::B2DHomMatrix & getInverseObjectToViewTransformation() const
const basegfx::B2DRange & getDiscreteViewport() const
On-demand prepared Viewport in discrete units for convenience Empty viewport means everything is visi...
const basegfx::B2DHomMatrix & getObjectToViewTransformation() const
On-demand prepared Object to View transformation and its inverse for convenience.
const Primitive2DContainer & getBuffered2DDecomposition() const
access methods to maBuffered2DDecomposition.
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
The getDecomposition default implementation will on demand use create2DDecomposition() if maBuffered2...
GridPrimitive2D(basegfx::B2DHomMatrix aTransform, double fWidth, double fHeight, double fSmallestViewDistance, double fSmallestSubdivisionViewDistance, sal_uInt32 nSubdivisionsX, sal_uInt32 nSubdivisionsY, const basegfx::BColor &rBColor, const BitmapEx &rCrossMarker)
constructor
const basegfx::BColor & getBColor() const
basegfx::B2DHomMatrix maLastObjectToViewTransformation
the last used object to view transformtion and the last Viewport, used from getDecomposition for deci...
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get 2d range
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
const basegfx::B2DHomMatrix & getTransform() const
data read access
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
Override standard getDecomposition to be view-dependent here.
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &rViewInformation) const override
create local decomposition
#define PRIMITIVE2D_ID_GRIDPRIMITIVE2D
basegfx::B2DHomMatrix maTransform
the geometric definition
uno_Any a
bool equalZero(const T &rfVal)
bool less(const T &rfValA, const T &rfValB)
B2DHomMatrix createShearXRotateTranslateB2DHomMatrix(double fShearX, double fRadiant, double fTranslateX, double fTranslateY)