xref: /AOO41X/main/drawinglayer/source/primitive2d/gridprimitive2d.cxx (revision 2a27d9ca92002687c22a80f87b4827f1c65aad4c)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_drawinglayer.hxx"
26 
27 #include <drawinglayer/primitive2d/gridprimitive2d.hxx>
28 #include <basegfx/tools/canvastools.hxx>
29 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
30 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
31 #include <drawinglayer/geometry/viewinformation2d.hxx>
32 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
33 #include <basegfx/matrix/b2dhommatrixtools.hxx>
34 
35 //////////////////////////////////////////////////////////////////////////////
36 
37 using namespace com::sun::star;
38 
39 //////////////////////////////////////////////////////////////////////////////
40 
41 namespace drawinglayer
42 {
43     namespace primitive2d
44     {
create2DDecomposition(const geometry::ViewInformation2D & rViewInformation) const45         Primitive2DSequence GridPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
46         {
47             Primitive2DSequence aRetval;
48 
49             if(!rViewInformation.getViewport().isEmpty() && getWidth() > 0.0 && getHeight() > 0.0)
50             {
51                 // decompose grid matrix to get logic size
52                 basegfx::B2DVector aScale, aTranslate;
53                 double fRotate, fShearX;
54                 getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
55 
56                 // create grid matrix which transforms from scaled logic to view
57                 basegfx::B2DHomMatrix aRST(basegfx::tools::createShearXRotateTranslateB2DHomMatrix(
58                     fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
59                 aRST *= rViewInformation.getObjectToViewTransformation();
60 
61                 // get step widths
62                 double fStepX(getWidth());
63                 double fStepY(getHeight());
64                 const double fMinimalStep(10.0);
65 
66                 // guarantee a step width of 10.0
67                 if(basegfx::fTools::less(fStepX, fMinimalStep))
68                 {
69                     fStepX = fMinimalStep;
70                 }
71 
72                 if(basegfx::fTools::less(fStepY, fMinimalStep))
73                 {
74                     fStepY = fMinimalStep;
75                 }
76 
77                 // get relative distances in view coordinates
78                 double fViewStepX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fStepX, 0.0)).getLength());
79                 double fViewStepY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fStepY)).getLength());
80                 double fSmallStepX(1.0), fViewSmallStepX(1.0), fSmallStepY(1.0), fViewSmallStepY(1.0);
81                 sal_uInt32 nSmallStepsX(0L), nSmallStepsY(0L);
82 
83                 // setup subdivisions
84                 if(getSubdivisionsX())
85                 {
86                     fSmallStepX = fStepX / getSubdivisionsX();
87                     fViewSmallStepX = fViewStepX / getSubdivisionsX();
88                 }
89 
90                 if(getSubdivisionsY())
91                 {
92                     fSmallStepY = fStepY / getSubdivisionsY();
93                     fViewSmallStepY = fViewStepY / getSubdivisionsY();
94                 }
95 
96                 // correct step width
97                 while(fViewStepX < getSmallestViewDistance())
98                 {
99                     fViewStepX *= 2.0;
100                     fStepX *= 2.0;
101                 }
102 
103                 while(fViewStepY < getSmallestViewDistance())
104                 {
105                     fViewStepY *= 2.0;
106                     fStepY *= 2.0;
107                 }
108 
109                 // correct small step width
110                 if(getSubdivisionsX())
111                 {
112                     while(fViewSmallStepX < getSmallestSubdivisionViewDistance())
113                     {
114                         fViewSmallStepX *= 2.0;
115                         fSmallStepX *= 2.0;
116                     }
117 
118                     nSmallStepsX = (sal_uInt32)(fStepX / fSmallStepX);
119                 }
120 
121                 if(getSubdivisionsY())
122                 {
123                     while(fViewSmallStepY < getSmallestSubdivisionViewDistance())
124                     {
125                         fViewSmallStepY *= 2.0;
126                         fSmallStepY *= 2.0;
127                     }
128 
129                     nSmallStepsY = (sal_uInt32)(fStepY / fSmallStepY);
130                 }
131 
132                 // calculate extended viewport in which grid points may lie at all
133                 basegfx::B2DRange aExtendedViewport;
134 
135                 if(rViewInformation.getDiscreteViewport().isEmpty())
136                 {
137                     // not set, use logic size to travel over all potentioal grid points
138                     aExtendedViewport = basegfx::B2DRange(0.0, 0.0, aScale.getX(), aScale.getY());
139                 }
140                 else
141                 {
142                     // transform unit range to discrete view
143                     aExtendedViewport = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
144                     basegfx::B2DHomMatrix aTrans(rViewInformation.getObjectToViewTransformation() * getTransform());
145                     aExtendedViewport.transform(aTrans);
146 
147                     // intersect with visible part
148                     aExtendedViewport.intersect(rViewInformation.getDiscreteViewport());
149 
150                     if(!aExtendedViewport.isEmpty())
151                     {
152                         // convert back and apply scale
153                         aTrans.invert();
154                         aTrans.scale(aScale.getX(), aScale.getY());
155                         aExtendedViewport.transform(aTrans);
156 
157                         // crop start/end in X/Y to multiples of logical step width
158                         const double fHalfCrossSize((rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(3.0, 0.0)).getLength());
159                         const double fMinX(floor((aExtendedViewport.getMinX() - fHalfCrossSize) / fStepX) * fStepX);
160                         const double fMaxX(ceil((aExtendedViewport.getMaxX() + fHalfCrossSize) / fStepX) * fStepX);
161                         const double fMinY(floor((aExtendedViewport.getMinY() - fHalfCrossSize) / fStepY) * fStepY);
162                         const double fMaxY(ceil((aExtendedViewport.getMaxY() + fHalfCrossSize) / fStepY) * fStepY);
163 
164                         // put to aExtendedViewport and crop on object logic size
165                         aExtendedViewport = basegfx::B2DRange(
166                             std::max(fMinX, 0.0),
167                             std::max(fMinY, 0.0),
168                             std::min(fMaxX, aScale.getX()),
169                             std::min(fMaxY, aScale.getY()));
170                     }
171                 }
172 
173                 if(!aExtendedViewport.isEmpty())
174                 {
175                     // prepare point vectors for point and cross markers
176                     std::vector< basegfx::B2DPoint > aPositionsPoint;
177                     std::vector< basegfx::B2DPoint > aPositionsCross;
178 
179                     for(double fX(aExtendedViewport.getMinX()); fX < aExtendedViewport.getMaxX(); fX += fStepX)
180                     {
181                         const bool bXZero(basegfx::fTools::equalZero(fX));
182 
183                         for(double fY(aExtendedViewport.getMinY()); fY < aExtendedViewport.getMaxY(); fY += fStepY)
184                         {
185                             const bool bYZero(basegfx::fTools::equalZero(fY));
186 
187                             if(!bXZero && !bYZero)
188                             {
189                                 // get discrete position and test against 3x3 area surrounding it
190                                 // since it's a cross
191                                 const double fHalfCrossSize(3.0 * 0.5);
192                                 const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fY));
193                                 const basegfx::B2DRange aDiscreteRangeCross(
194                                     aViewPos.getX() - fHalfCrossSize, aViewPos.getY() - fHalfCrossSize,
195                                     aViewPos.getX() + fHalfCrossSize, aViewPos.getY() + fHalfCrossSize);
196 
197                                 if(rViewInformation.getDiscreteViewport().overlaps(aDiscreteRangeCross))
198                                 {
199                                     const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
200                                     aPositionsCross.push_back(aLogicPos);
201                                 }
202                             }
203 
204                             if(getSubdivisionsX() && !bYZero)
205                             {
206                                 double fF(fX + fSmallStepX);
207 
208                                 for(sal_uInt32 a(1); a < nSmallStepsX && fF < aExtendedViewport.getMaxX(); a++, fF += fSmallStepX)
209                                 {
210                                     const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fF, fY));
211 
212                                     if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
213                                     {
214                                         const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
215                                         aPositionsPoint.push_back(aLogicPos);
216                                     }
217                                 }
218                             }
219 
220                             if(getSubdivisionsY() && !bXZero)
221                             {
222                                 double fF(fY + fSmallStepY);
223 
224                                 for(sal_uInt32 a(1); a < nSmallStepsY && fF < aExtendedViewport.getMaxY(); a++, fF += fSmallStepY)
225                                 {
226                                     const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fF));
227 
228                                     if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
229                                     {
230                                         const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
231                                         aPositionsPoint.push_back(aLogicPos);
232                                     }
233                                 }
234                             }
235                         }
236                     }
237 
238                     // prepare return value
239                     const sal_uInt32 nCountPoint(aPositionsPoint.size());
240                     const sal_uInt32 nCountCross(aPositionsCross.size());
241                     const sal_uInt32 nRetvalCount((nCountPoint ? 1 : 0) + (nCountCross ? 1 : 0));
242                     sal_uInt32 nInsertCounter(0);
243 
244                     aRetval.realloc(nRetvalCount);
245 
246                     // add PointArrayPrimitive2D if point markers were added
247                     if(nCountPoint)
248                     {
249                         aRetval[nInsertCounter++] = Primitive2DReference(new PointArrayPrimitive2D(aPositionsPoint, getBColor()));
250                     }
251 
252                     // add MarkerArrayPrimitive2D if cross markers were added
253                     if(nCountCross)
254                     {
255                         if(!getSubdivisionsX() && !getSubdivisionsY())
256                         {
257                             // no subdivisions, so fall back to points at grid positions, no need to
258                             // visualize a difference between divisions and sub-divisions
259                             aRetval[nInsertCounter++] = Primitive2DReference(new PointArrayPrimitive2D(aPositionsCross, getBColor()));
260                         }
261                         else
262                         {
263                             aRetval[nInsertCounter++] = Primitive2DReference(new MarkerArrayPrimitive2D(aPositionsCross, getCrossMarker()));
264                         }
265                     }
266                 }
267             }
268 
269             return aRetval;
270         }
271 
GridPrimitive2D(const basegfx::B2DHomMatrix & rTransform,double fWidth,double fHeight,double fSmallestViewDistance,double fSmallestSubdivisionViewDistance,sal_uInt32 nSubdivisionsX,sal_uInt32 nSubdivisionsY,const basegfx::BColor & rBColor,const BitmapEx & rCrossMarker)272         GridPrimitive2D::GridPrimitive2D(
273             const basegfx::B2DHomMatrix& rTransform,
274             double fWidth,
275             double fHeight,
276             double fSmallestViewDistance,
277             double fSmallestSubdivisionViewDistance,
278             sal_uInt32 nSubdivisionsX,
279             sal_uInt32 nSubdivisionsY,
280             const basegfx::BColor& rBColor,
281             const BitmapEx& rCrossMarker)
282         :   BufferedDecompositionPrimitive2D(),
283             maTransform(rTransform),
284             mfWidth(fWidth),
285             mfHeight(fHeight),
286             mfSmallestViewDistance(fSmallestViewDistance),
287             mfSmallestSubdivisionViewDistance(fSmallestSubdivisionViewDistance),
288             mnSubdivisionsX(nSubdivisionsX),
289             mnSubdivisionsY(nSubdivisionsY),
290             maBColor(rBColor),
291             maCrossMarker(rCrossMarker),
292             maLastObjectToViewTransformation(),
293             maLastViewport()
294         {
295         }
296 
operator ==(const BasePrimitive2D & rPrimitive) const297         bool GridPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
298         {
299             if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
300             {
301                 const GridPrimitive2D& rCompare = (GridPrimitive2D&)rPrimitive;
302 
303                 return (getTransform() == rCompare.getTransform()
304                     && getWidth() == rCompare.getWidth()
305                     && getHeight() == rCompare.getHeight()
306                     && getSmallestViewDistance() == rCompare.getSmallestViewDistance()
307                     && getSmallestSubdivisionViewDistance() == rCompare.getSmallestSubdivisionViewDistance()
308                     && getSubdivisionsX() == rCompare.getSubdivisionsX()
309                     && getSubdivisionsY() == rCompare.getSubdivisionsY()
310                     && getBColor() == rCompare.getBColor()
311                     && getCrossMarker() == rCompare.getCrossMarker());
312             }
313 
314             return false;
315         }
316 
getB2DRange(const geometry::ViewInformation2D & rViewInformation) const317         basegfx::B2DRange GridPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
318         {
319             // get object's range
320             basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
321             aUnitRange.transform(getTransform());
322 
323             // intersect with visible part
324             aUnitRange.intersect(rViewInformation.getViewport());
325 
326             return aUnitRange;
327         }
328 
get2DDecomposition(const geometry::ViewInformation2D & rViewInformation) const329         Primitive2DSequence GridPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
330         {
331             ::osl::MutexGuard aGuard( m_aMutex );
332 
333             if(getBuffered2DDecomposition().hasElements())
334             {
335                 if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
336                 {
337                     // conditions of last local decomposition have changed, delete
338                     const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DSequence());
339                 }
340             }
341 
342             if(!getBuffered2DDecomposition().hasElements())
343             {
344                 // remember ViewRange and ViewTransformation
345                 const_cast< GridPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
346                 const_cast< GridPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
347             }
348 
349             // use parent implementation
350             return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
351         }
352 
353         // provide unique ID
354         ImplPrimitrive2DIDBlock(GridPrimitive2D, PRIMITIVE2D_ID_GRIDPRIMITIVE2D)
355 
356     } // end of namespace primitive2d
357 } // end of namespace drawinglayer
358 
359 //////////////////////////////////////////////////////////////////////////////
360 // eof
361