1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <drawinglayer/primitive2d/gridprimitive2d.hxx>
21 #include <basegfx/utils/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>
29 using namespace com::sun::star
;
32 namespace drawinglayer
36 void GridPrimitive2D::create2DDecomposition(Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& rViewInformation
) const
38 if(!rViewInformation
.getViewport().isEmpty() && getWidth() > 0.0 && getHeight() > 0.0)
40 // decompose grid matrix to get logic size
41 basegfx::B2DVector aScale
, aTranslate
;
42 double fRotate
, fShearX
;
43 getTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
);
45 // create grid matrix which transforms from scaled logic to view
46 basegfx::B2DHomMatrix
aRST(basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
47 fShearX
, fRotate
, aTranslate
.getX(), aTranslate
.getY()));
48 aRST
*= rViewInformation
.getObjectToViewTransformation();
51 double fStepX(getWidth());
52 double fStepY(getHeight());
53 const double fMinimalStep(10.0);
55 // guarantee a step width of 10.0
56 if(basegfx::fTools::less(fStepX
, fMinimalStep
))
58 fStepX
= fMinimalStep
;
61 if(basegfx::fTools::less(fStepY
, fMinimalStep
))
63 fStepY
= fMinimalStep
;
66 // get relative distances in view coordinates
67 double fViewStepX((rViewInformation
.getObjectToViewTransformation() * basegfx::B2DVector(fStepX
, 0.0)).getLength());
68 double fViewStepY((rViewInformation
.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fStepY
)).getLength());
69 double fSmallStepX(1.0), fViewSmallStepX(1.0), fSmallStepY(1.0), fViewSmallStepY(1.0);
70 sal_uInt32
nSmallStepsX(0), nSmallStepsY(0);
73 if(getSubdivisionsX())
75 fSmallStepX
= fStepX
/ getSubdivisionsX();
76 fViewSmallStepX
= fViewStepX
/ getSubdivisionsX();
79 if(getSubdivisionsY())
81 fSmallStepY
= fStepY
/ getSubdivisionsY();
82 fViewSmallStepY
= fViewStepY
/ getSubdivisionsY();
86 while(fViewStepX
< getSmallestViewDistance())
92 while(fViewStepY
< getSmallestViewDistance())
98 // correct small step width
99 if(getSubdivisionsX())
101 while(fViewSmallStepX
< getSmallestSubdivisionViewDistance())
103 fViewSmallStepX
*= 2.0;
107 nSmallStepsX
= static_cast<sal_uInt32
>(fStepX
/ fSmallStepX
);
110 if(getSubdivisionsY())
112 while(fViewSmallStepY
< getSmallestSubdivisionViewDistance())
114 fViewSmallStepY
*= 2.0;
118 nSmallStepsY
= static_cast<sal_uInt32
>(fStepY
/ fSmallStepY
);
121 // calculate extended viewport in which grid points may lie at all
122 basegfx::B2DRange aExtendedViewport
;
124 if(rViewInformation
.getDiscreteViewport().isEmpty())
126 // not set, use logic size to travel over all potential grid points
127 aExtendedViewport
= basegfx::B2DRange(0.0, 0.0, aScale
.getX(), aScale
.getY());
131 // transform unit range to discrete view
132 aExtendedViewport
= basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
133 basegfx::B2DHomMatrix
aTrans(rViewInformation
.getObjectToViewTransformation() * getTransform());
134 aExtendedViewport
.transform(aTrans
);
136 // intersect with visible part
137 aExtendedViewport
.intersect(rViewInformation
.getDiscreteViewport());
139 if(!aExtendedViewport
.isEmpty())
141 // convert back and apply scale
143 aTrans
.scale(aScale
.getX(), aScale
.getY());
144 aExtendedViewport
.transform(aTrans
);
146 // crop start/end in X/Y to multiples of logical step width
147 const double fHalfCrossSize((rViewInformation
.getInverseObjectToViewTransformation() * basegfx::B2DVector(3.0, 0.0)).getLength());
148 const double fMinX(floor((aExtendedViewport
.getMinX() - fHalfCrossSize
) / fStepX
) * fStepX
);
149 const double fMaxX(ceil((aExtendedViewport
.getMaxX() + fHalfCrossSize
) / fStepX
) * fStepX
);
150 const double fMinY(floor((aExtendedViewport
.getMinY() - fHalfCrossSize
) / fStepY
) * fStepY
);
151 const double fMaxY(ceil((aExtendedViewport
.getMaxY() + fHalfCrossSize
) / fStepY
) * fStepY
);
153 // put to aExtendedViewport and crop on object logic size
154 aExtendedViewport
= basegfx::B2DRange(
155 std::max(fMinX
, 0.0),
156 std::max(fMinY
, 0.0),
157 std::min(fMaxX
, aScale
.getX()),
158 std::min(fMaxY
, aScale
.getY()));
162 if(!aExtendedViewport
.isEmpty())
164 // prepare point vectors for point and cross markers
165 std::vector
< basegfx::B2DPoint
> aPositionsPoint
;
166 std::vector
< basegfx::B2DPoint
> aPositionsCross
;
168 for(double fX(aExtendedViewport
.getMinX()); fX
< aExtendedViewport
.getMaxX(); fX
+= fStepX
)
170 const bool bXZero(basegfx::fTools::equalZero(fX
));
172 for(double fY(aExtendedViewport
.getMinY()); fY
< aExtendedViewport
.getMaxY(); fY
+= fStepY
)
174 const bool bYZero(basegfx::fTools::equalZero(fY
));
176 if(!bXZero
&& !bYZero
)
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
);
186 if(rViewInformation
.getDiscreteViewport().overlaps(aDiscreteRangeCross
))
188 const basegfx::B2DPoint
aLogicPos(rViewInformation
.getInverseObjectToViewTransformation() * aViewPos
);
189 aPositionsCross
.push_back(aLogicPos
);
193 if(getSubdivisionsX() && !bYZero
)
195 double fF(fX
+ fSmallStepX
);
197 for(sal_uInt32
a(1); a
< nSmallStepsX
&& fF
< aExtendedViewport
.getMaxX(); a
++, fF
+= fSmallStepX
)
199 const basegfx::B2DPoint
aViewPos(aRST
* basegfx::B2DPoint(fF
, fY
));
201 if(rViewInformation
.getDiscreteViewport().isInside(aViewPos
))
203 const basegfx::B2DPoint
aLogicPos(rViewInformation
.getInverseObjectToViewTransformation() * aViewPos
);
204 aPositionsPoint
.push_back(aLogicPos
);
209 if(getSubdivisionsY() && !bXZero
)
211 double fF(fY
+ fSmallStepY
);
213 for(sal_uInt32
a(1); a
< nSmallStepsY
&& fF
< aExtendedViewport
.getMaxY(); a
++, fF
+= fSmallStepY
)
215 const basegfx::B2DPoint
aViewPos(aRST
* basegfx::B2DPoint(fX
, fF
));
217 if(rViewInformation
.getDiscreteViewport().isInside(aViewPos
))
219 const basegfx::B2DPoint
aLogicPos(rViewInformation
.getInverseObjectToViewTransformation() * aViewPos
);
220 aPositionsPoint
.push_back(aLogicPos
);
227 // prepare return value
228 const sal_uInt32
nCountPoint(aPositionsPoint
.size());
229 const sal_uInt32
nCountCross(aPositionsCross
.size());
231 // add PointArrayPrimitive2D if point markers were added
234 rContainer
.push_back(new PointArrayPrimitive2D(aPositionsPoint
, getBColor()));
237 // add MarkerArrayPrimitive2D if cross markers were added
240 if(!getSubdivisionsX() && !getSubdivisionsY())
242 // no subdivisions, so fall back to points at grid positions, no need to
243 // visualize a difference between divisions and sub-divisions
244 rContainer
.push_back(new PointArrayPrimitive2D(aPositionsCross
, getBColor()));
248 rContainer
.push_back(new MarkerArrayPrimitive2D(aPositionsCross
, getCrossMarker()));
255 GridPrimitive2D::GridPrimitive2D(
256 const basegfx::B2DHomMatrix
& rTransform
,
259 double fSmallestViewDistance
,
260 double fSmallestSubdivisionViewDistance
,
261 sal_uInt32 nSubdivisionsX
,
262 sal_uInt32 nSubdivisionsY
,
263 const basegfx::BColor
& rBColor
,
264 const BitmapEx
& rCrossMarker
)
265 : BufferedDecompositionPrimitive2D(),
266 maTransform(rTransform
),
269 mfSmallestViewDistance(fSmallestViewDistance
),
270 mfSmallestSubdivisionViewDistance(fSmallestSubdivisionViewDistance
),
271 mnSubdivisionsX(nSubdivisionsX
),
272 mnSubdivisionsY(nSubdivisionsY
),
274 maCrossMarker(rCrossMarker
),
275 maLastObjectToViewTransformation(),
280 bool GridPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
282 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
284 const GridPrimitive2D
& rCompare
= static_cast<const GridPrimitive2D
&>(rPrimitive
);
286 return (getTransform() == rCompare
.getTransform()
287 && getWidth() == rCompare
.getWidth()
288 && getHeight() == rCompare
.getHeight()
289 && getSmallestViewDistance() == rCompare
.getSmallestViewDistance()
290 && getSmallestSubdivisionViewDistance() == rCompare
.getSmallestSubdivisionViewDistance()
291 && getSubdivisionsX() == rCompare
.getSubdivisionsX()
292 && getSubdivisionsY() == rCompare
.getSubdivisionsY()
293 && getBColor() == rCompare
.getBColor()
294 && getCrossMarker() == rCompare
.getCrossMarker());
300 basegfx::B2DRange
GridPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
302 // get object's range
303 basegfx::B2DRange
aUnitRange(0.0, 0.0, 1.0, 1.0);
304 aUnitRange
.transform(getTransform());
306 // intersect with visible part
307 aUnitRange
.intersect(rViewInformation
.getViewport());
312 void GridPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor
& rVisitor
, const geometry::ViewInformation2D
& rViewInformation
) const
314 ::osl::MutexGuard
aGuard( m_aMutex
);
316 if(!getBuffered2DDecomposition().empty())
318 if(maLastViewport
!= rViewInformation
.getViewport() || maLastObjectToViewTransformation
!= rViewInformation
.getObjectToViewTransformation())
320 // conditions of last local decomposition have changed, delete
321 const_cast< GridPrimitive2D
* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
325 if(getBuffered2DDecomposition().empty())
327 // remember ViewRange and ViewTransformation
328 const_cast< GridPrimitive2D
* >(this)->maLastObjectToViewTransformation
= rViewInformation
.getObjectToViewTransformation();
329 const_cast< GridPrimitive2D
* >(this)->maLastViewport
= rViewInformation
.getViewport();
332 // use parent implementation
333 BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor
, rViewInformation
);
337 ImplPrimitive2DIDBlock(GridPrimitive2D
, PRIMITIVE2D_ID_GRIDPRIMITIVE2D
)
339 } // end of namespace primitive2d
340 } // end of namespace drawinglayer
342 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */