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 { 45 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 // prepare point vectors for point and cross markers 133 std::vector< basegfx::B2DPoint > aPositionsPoint; 134 std::vector< basegfx::B2DPoint > aPositionsCross; 135 136 for(double fX(0.0); fX < aScale.getX(); fX += fStepX) 137 { 138 const bool bXZero(basegfx::fTools::equalZero(fX)); 139 140 for(double fY(0.0); fY < aScale.getY(); fY += fStepY) 141 { 142 const bool bYZero(basegfx::fTools::equalZero(fY)); 143 144 if(!bXZero && !bYZero) 145 { 146 // get discrete position and test against 3x3 area surrounding it 147 // since it's a cross 148 const double fHalfCrossSize(3.0 * 0.5); 149 const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fY)); 150 const basegfx::B2DRange aDiscreteRangeCross( 151 aViewPos.getX() - fHalfCrossSize, aViewPos.getY() - fHalfCrossSize, 152 aViewPos.getX() + fHalfCrossSize, aViewPos.getY() + fHalfCrossSize); 153 154 if(rViewInformation.getDiscreteViewport().overlaps(aDiscreteRangeCross)) 155 { 156 const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos); 157 aPositionsCross.push_back(aLogicPos); 158 } 159 } 160 161 if(getSubdivisionsX() && !bYZero) 162 { 163 double fF(fX + fSmallStepX); 164 165 for(sal_uInt32 a(1L); a < nSmallStepsX && fF < aScale.getX(); a++, fF += fSmallStepX) 166 { 167 const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fF, fY)); 168 169 if(rViewInformation.getDiscreteViewport().isInside(aViewPos)) 170 { 171 const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos); 172 aPositionsPoint.push_back(aLogicPos); 173 } 174 } 175 } 176 177 if(getSubdivisionsY() && !bXZero) 178 { 179 double fF(fY + fSmallStepY); 180 181 for(sal_uInt32 a(1L); a < nSmallStepsY && fF < aScale.getY(); a++, fF += fSmallStepY) 182 { 183 const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fF)); 184 185 if(rViewInformation.getDiscreteViewport().isInside(aViewPos)) 186 { 187 const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos); 188 aPositionsPoint.push_back(aLogicPos); 189 } 190 } 191 } 192 } 193 } 194 195 // prepare return value 196 const sal_uInt32 nCountPoint(aPositionsPoint.size()); 197 const sal_uInt32 nCountCross(aPositionsCross.size()); 198 const sal_uInt32 nRetvalCount((nCountPoint ? 1 : 0) + (nCountCross ? 1 : 0)); 199 sal_uInt32 nInsertCounter(0); 200 201 aRetval.realloc(nRetvalCount); 202 203 // add PointArrayPrimitive2D if point markers were added 204 if(nCountPoint) 205 { 206 aRetval[nInsertCounter++] = Primitive2DReference(new PointArrayPrimitive2D(aPositionsPoint, getBColor())); 207 } 208 209 // add MarkerArrayPrimitive2D if cross markers were added 210 if(nCountCross) 211 { 212 if(!getSubdivisionsX() && !getSubdivisionsY()) 213 { 214 // no subdivisions, so fall back to points at grid positions, no need to 215 // visualize a difference between divisions and sub-divisions 216 aRetval[nInsertCounter++] = Primitive2DReference(new PointArrayPrimitive2D(aPositionsCross, getBColor())); 217 } 218 else 219 { 220 aRetval[nInsertCounter++] = Primitive2DReference(new MarkerArrayPrimitive2D(aPositionsCross, getCrossMarker())); 221 } 222 } 223 } 224 225 return aRetval; 226 } 227 228 GridPrimitive2D::GridPrimitive2D( 229 const basegfx::B2DHomMatrix& rTransform, 230 double fWidth, 231 double fHeight, 232 double fSmallestViewDistance, 233 double fSmallestSubdivisionViewDistance, 234 sal_uInt32 nSubdivisionsX, 235 sal_uInt32 nSubdivisionsY, 236 const basegfx::BColor& rBColor, 237 const BitmapEx& rCrossMarker) 238 : BufferedDecompositionPrimitive2D(), 239 maTransform(rTransform), 240 mfWidth(fWidth), 241 mfHeight(fHeight), 242 mfSmallestViewDistance(fSmallestViewDistance), 243 mfSmallestSubdivisionViewDistance(fSmallestSubdivisionViewDistance), 244 mnSubdivisionsX(nSubdivisionsX), 245 mnSubdivisionsY(nSubdivisionsY), 246 maBColor(rBColor), 247 maCrossMarker(rCrossMarker), 248 maLastObjectToViewTransformation(), 249 maLastViewport() 250 { 251 } 252 253 bool GridPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 254 { 255 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) 256 { 257 const GridPrimitive2D& rCompare = (GridPrimitive2D&)rPrimitive; 258 259 return (getTransform() == rCompare.getTransform() 260 && getWidth() == rCompare.getWidth() 261 && getHeight() == rCompare.getHeight() 262 && getSmallestViewDistance() == rCompare.getSmallestViewDistance() 263 && getSmallestSubdivisionViewDistance() == rCompare.getSmallestSubdivisionViewDistance() 264 && getSubdivisionsX() == rCompare.getSubdivisionsX() 265 && getSubdivisionsY() == rCompare.getSubdivisionsY() 266 && getBColor() == rCompare.getBColor() 267 && getCrossMarker() == rCompare.getCrossMarker()); 268 } 269 270 return false; 271 } 272 273 basegfx::B2DRange GridPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const 274 { 275 // get object's range 276 basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); 277 aUnitRange.transform(getTransform()); 278 279 // intersect with visible part 280 aUnitRange.intersect(rViewInformation.getViewport()); 281 282 return aUnitRange; 283 } 284 285 Primitive2DSequence GridPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const 286 { 287 ::osl::MutexGuard aGuard( m_aMutex ); 288 289 if(getBuffered2DDecomposition().hasElements()) 290 { 291 if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation()) 292 { 293 // conditions of last local decomposition have changed, delete 294 const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DSequence()); 295 } 296 } 297 298 if(!getBuffered2DDecomposition().hasElements()) 299 { 300 // remember ViewRange and ViewTransformation 301 const_cast< GridPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation(); 302 const_cast< GridPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport(); 303 } 304 305 // use parent implementation 306 return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation); 307 } 308 309 // provide unique ID 310 ImplPrimitrive2DIDBlock(GridPrimitive2D, PRIMITIVE2D_ID_GRIDPRIMITIVE2D) 311 312 } // end of namespace primitive2d 313 } // end of namespace drawinglayer 314 315 ////////////////////////////////////////////////////////////////////////////// 316 // eof 317