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 20 21 #include <sdr/contact/viewcontactofsdrpathobj.hxx> 22 #include <svx/svdopath.hxx> 23 #include <sdr/primitive2d/sdrattributecreator.hxx> 24 #include <basegfx/polygon/b2dpolypolygontools.hxx> 25 #include <sdr/primitive2d/sdrpathprimitive2d.hxx> 26 #include <basegfx/matrix/b2dhommatrixtools.hxx> 27 #include <basegfx/polygon/b2dpolygontools.hxx> 28 #include <osl/diagnose.h> 29 #include <vcl/canvastools.hxx> 30 31 namespace sdr::contact 32 { ViewContactOfSdrPathObj(SdrPathObj & rPathObj)33 ViewContactOfSdrPathObj::ViewContactOfSdrPathObj(SdrPathObj& rPathObj) 34 : ViewContactOfTextObj(rPathObj) 35 { 36 } 37 ~ViewContactOfSdrPathObj()38 ViewContactOfSdrPathObj::~ViewContactOfSdrPathObj() 39 { 40 } 41 42 /// return true if polycount == 1 ensureGeometry(basegfx::B2DPolyPolygon & rUnitPolyPolygon)43 static bool ensureGeometry(basegfx::B2DPolyPolygon& rUnitPolyPolygon) 44 { 45 sal_uInt32 nPolyCount(rUnitPolyPolygon.count()); 46 sal_uInt32 nPointCount(0); 47 48 for(auto const& rPolygon : std::as_const(rUnitPolyPolygon)) 49 { 50 nPointCount += rPolygon.count(); 51 // return early if we definitely have geometry 52 if (nPointCount > 1) 53 return nPolyCount == 1; 54 } 55 56 if(!nPointCount) 57 { 58 OSL_FAIL("PolyPolygon object without geometry detected, this should not be created (!)"); 59 basegfx::B2DPolygon aFallbackLine; 60 aFallbackLine.append(basegfx::B2DPoint(0.0, 0.0)); 61 aFallbackLine.append(basegfx::B2DPoint(1000.0, 1000.0)); 62 rUnitPolyPolygon = basegfx::B2DPolyPolygon(aFallbackLine); 63 64 nPolyCount = 1; 65 } 66 67 return nPolyCount == 1; 68 } 69 createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor & rVisitor) const70 void ViewContactOfSdrPathObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const 71 { 72 const SfxItemSet& rItemSet = GetPathObj().GetMergedItemSet(); 73 const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute( 74 drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute( 75 rItemSet, 76 GetPathObj().getText(0), 77 false)); 78 basegfx::B2DPolyPolygon aUnitPolyPolygon(GetPathObj().GetPathPoly()); 79 bool bPolyCountIsOne(ensureGeometry(aUnitPolyPolygon)); 80 81 // prepare object transformation and unit polygon (direct model data) 82 basegfx::B2DHomMatrix aObjectMatrix; 83 basegfx::B2DPolyPolygon aUnitDefinitionPolyPolygon; 84 const bool bIsLine( 85 !aUnitPolyPolygon.areControlPointsUsed() 86 && bPolyCountIsOne 87 && 2 == aUnitPolyPolygon.getB2DPolygon(0).count()); 88 89 if(bIsLine) 90 { 91 // special handling for single line mode (2 points) 92 const basegfx::B2DPolygon & rSubPolygon(aUnitPolyPolygon.getB2DPolygon(0)); 93 const basegfx::B2DPoint aStart(rSubPolygon.getB2DPoint(0)); 94 const basegfx::B2DPoint aEnd(rSubPolygon.getB2DPoint(1)); 95 const basegfx::B2DVector aLine(aEnd - aStart); 96 97 // #i102548# create new unit polygon for line (horizontal) 98 static const basegfx::B2DPolygon aNewPolygon{basegfx::B2DPoint(0.0, 0.0), basegfx::B2DPoint(1.0, 0.0)}; 99 aUnitPolyPolygon.setB2DPolygon(0, aNewPolygon); 100 101 // #i102548# fill objectMatrix with rotation and offset (no shear for lines) 102 aObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( 103 aLine.getLength(), 1.0, 104 0.0, 105 atan2(aLine.getY(), aLine.getX()), 106 aStart.getX(), aStart.getY()); 107 } 108 else 109 { 110 // #i102548# create unscaled, unsheared, unrotated and untranslated polygon 111 // (unit polygon) by creating the object matrix and back-transforming the polygon 112 const basegfx::B2DRange aObjectRange(basegfx::utils::getRange(aUnitPolyPolygon)); 113 const GeoStat& rGeoStat(GetPathObj().GetGeoStat()); 114 const double fWidth(aObjectRange.getWidth()); 115 const double fHeight(aObjectRange.getHeight()); 116 const double fScaleX(basegfx::fTools::equalZero(fWidth) ? 1.0 : fWidth); 117 const double fScaleY(basegfx::fTools::equalZero(fHeight) ? 1.0 : fHeight); 118 119 aObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( 120 fScaleX, fScaleY, 121 -rGeoStat.mfTanShearAngle, 122 rGeoStat.m_nRotationAngle ? toRadians(36000_deg100 - rGeoStat.m_nRotationAngle) : 0.0, 123 aObjectRange.getMinX(), aObjectRange.getMinY()); 124 125 // create unit polygon from object's absolute path 126 basegfx::B2DHomMatrix aInverse(aObjectMatrix); 127 aInverse.invert(); 128 aUnitPolyPolygon.transform(aInverse); 129 130 // OperationSmiley: Check if a FillGeometryDefiningShape is set 131 const SdrObject* pFillGeometryDefiningShape(GetPathObj().getFillGeometryDefiningShape()); 132 133 if(nullptr != pFillGeometryDefiningShape) 134 { 135 // If yes, get it's BoundRange and use as defining Geometry for the FillStyle. 136 // If no, aUnitDefinitionPolyPolygon will just be empty and thus be interpreted 137 // as unused. 138 // Using SnapRect will make the FillDefinition to always be extended e.g. 139 // for rotated/sheared objects. 140 const tools::Rectangle& rSnapRect(pFillGeometryDefiningShape->GetSnapRect()); 141 142 aUnitDefinitionPolyPolygon.append( 143 basegfx::utils::createPolygonFromRect( 144 vcl::unotools::b2DRectangleFromRectangle(rSnapRect))); 145 146 // use same coordinate system as the shape geometry -> this 147 // makes it relative to shape's unit geometry and thus freely 148 // transformable with the shape 149 aUnitDefinitionPolyPolygon.transform(aInverse); 150 } 151 } 152 153 // create primitive. Always create primitives to allow the decomposition of 154 // SdrPathPrimitive2D to create needed invisible elements for HitTest and/or BoundRect 155 const drawinglayer::primitive2d::Primitive2DReference xReference( 156 new drawinglayer::primitive2d::SdrPathPrimitive2D( 157 aObjectMatrix, 158 aAttribute, 159 std::move(aUnitPolyPolygon), 160 std::move(aUnitDefinitionPolyPolygon))); 161 162 rVisitor.visit(xReference); 163 } 164 165 } // end of namespace 166 167 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 168
