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