Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx
Line
Count
Source
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
#include <primitive3d/hatchtextureprimitive3d.hxx>
21
#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
22
#include <basegfx/polygon/b2dpolypolygon.hxx>
23
#include <basegfx/polygon/b3dpolygon.hxx>
24
#include <basegfx/polygon/b2dpolygon.hxx>
25
#include <basegfx/polygon/b2dpolypolygontools.hxx>
26
#include <basegfx/range/b2drange.hxx>
27
#include <texture/texture.hxx>
28
#include <basegfx/polygon/b2dpolygonclipper.hxx>
29
#include <basegfx/matrix/b3dhommatrix.hxx>
30
#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
31
#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
32
#include <utility>
33
34
35
using namespace com::sun::star;
36
37
38
namespace drawinglayer::primitive3d
39
{
40
        Primitive3DContainer HatchTexturePrimitive3D::impCreate3DDecomposition() const
41
0
        {
42
0
            Primitive3DContainer aRetval;
43
44
0
            if(!getChildren().empty())
45
0
            {
46
0
                const Primitive3DContainer aSource(getChildren());
47
0
                const size_t nSourceCount(aSource.size());
48
0
                std::vector< Primitive3DReference > aDestination;
49
50
0
                for(size_t a(0); a < nSourceCount; a++)
51
0
                {
52
                    // get reference
53
0
                    const Primitive3DReference& xReference(aSource[a]);
54
55
0
                    if(xReference.is())
56
0
                    {
57
0
                        const BasePrimitive3D* pBasePrimitive = static_cast< const BasePrimitive3D* >(xReference.get());
58
59
                        // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch
60
                        // not all content is needed, remove transparencies and ModifiedColorPrimitives
61
0
                        switch(pBasePrimitive->getPrimitive3DID())
62
0
                        {
63
0
                            case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D :
64
0
                            {
65
                                // polyPolygonMaterialPrimitive3D, check texturing and hatching
66
0
                                const PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const PolyPolygonMaterialPrimitive3D& >(*pBasePrimitive);
67
0
                                const basegfx::B3DPolyPolygon& aFillPolyPolygon(rPrimitive.getB3DPolyPolygon());
68
69
0
                                if(maHatch.isFillBackground())
70
0
                                {
71
                                    // add original primitive for background
72
0
                                    aDestination.push_back(xReference);
73
0
                                }
74
75
0
                                if(aFillPolyPolygon.areTextureCoordinatesUsed())
76
0
                                {
77
0
                                    const sal_uInt32 nPolyCount(aFillPolyPolygon.count());
78
0
                                    basegfx::B2DPolyPolygon aTexPolyPolygon;
79
0
                                    basegfx::B2DPoint a2N;
80
0
                                    basegfx::B2DVector a2X, a2Y;
81
0
                                    basegfx::B3DPoint a3N;
82
0
                                    basegfx::B3DVector a3X, a3Y;
83
0
                                    bool b2N(false), b2X(false), b2Y(false);
84
85
0
                                    for(sal_uInt32 b(0); b < nPolyCount; b++)
86
0
                                    {
87
0
                                        const basegfx::B3DPolygon& aPartPoly(aFillPolyPolygon.getB3DPolygon(b));
88
0
                                        const sal_uInt32 nPointCount(aPartPoly.count());
89
0
                                        basegfx::B2DPolygon aTexPolygon;
90
91
0
                                        for(sal_uInt32 c(0); c < nPointCount; c++)
92
0
                                        {
93
0
                                            const basegfx::B2DPoint a2Candidate(aPartPoly.getTextureCoordinate(c));
94
95
0
                                            if(!b2N)
96
0
                                            {
97
0
                                                a2N = a2Candidate;
98
0
                                                a3N = aPartPoly.getB3DPoint(c);
99
0
                                                b2N = true;
100
0
                                            }
101
0
                                            else if(!b2X && !a2N.equal(a2Candidate))
102
0
                                            {
103
0
                                                a2X = a2Candidate - a2N;
104
0
                                                a3X = aPartPoly.getB3DPoint(c) - a3N;
105
0
                                                b2X = true;
106
0
                                            }
107
0
                                            else if(!b2Y && !a2N.equal(a2Candidate) && !a2X.equal(a2Candidate))
108
0
                                            {
109
0
                                                a2Y = a2Candidate - a2N;
110
111
0
                                                const double fCross(a2X.cross(a2Y));
112
113
0
                                                if(!basegfx::fTools::equalZero(fCross))
114
0
                                                {
115
0
                                                    a3Y = aPartPoly.getB3DPoint(c) - a3N;
116
0
                                                    b2Y = true;
117
0
                                                }
118
0
                                            }
119
120
0
                                            aTexPolygon.append(a2Candidate);
121
0
                                        }
122
123
0
                                        aTexPolygon.setClosed(true);
124
0
                                        aTexPolyPolygon.append(aTexPolygon);
125
0
                                    }
126
127
0
                                    if(b2N && b2X && b2Y)
128
0
                                    {
129
                                        // found two linearly independent 2D vectors
130
                                        // get 2d range of texture coordinates
131
0
                                        const basegfx::B2DRange aOutlineRange(aTexPolyPolygon.getB2DRange());
132
0
                                        const basegfx::BColor aHatchColor(getHatch().getColor());
133
0
                                        const double fAngle(getHatch().getAngle());
134
0
                                        std::vector< basegfx::B2DHomMatrix > aMatrices;
135
136
                                        // get hatch transformations
137
0
                                        switch(getHatch().getStyle())
138
0
                                        {
139
0
                                            case attribute::HatchStyle::Triple:
140
0
                                            {
141
                                                // rotated 45 degrees
142
0
                                                texture::GeoTexSvxHatch aHatch(
143
0
                                                    aOutlineRange,
144
0
                                                    aOutlineRange,
145
0
                                                    getHatch().getDistance(),
146
0
                                                    fAngle - M_PI_4);
147
148
0
                                                aHatch.appendTransformations(aMatrices);
149
150
0
                                                [[fallthrough]];
151
0
                                            }
152
0
                                            case attribute::HatchStyle::Double:
153
0
                                            {
154
                                                // rotated 90 degrees
155
0
                                                texture::GeoTexSvxHatch aHatch(
156
0
                                                    aOutlineRange,
157
0
                                                    aOutlineRange,
158
0
                                                    getHatch().getDistance(),
159
0
                                                    fAngle - M_PI_2);
160
161
0
                                                aHatch.appendTransformations(aMatrices);
162
163
0
                                                [[fallthrough]];
164
0
                                            }
165
0
                                            case attribute::HatchStyle::Single:
166
0
                                            {
167
                                                // angle as given
168
0
                                                texture::GeoTexSvxHatch aHatch(
169
0
                                                    aOutlineRange,
170
0
                                                    aOutlineRange,
171
0
                                                    getHatch().getDistance(),
172
0
                                                    fAngle);
173
174
0
                                                aHatch.appendTransformations(aMatrices);
175
0
                                            }
176
0
                                        }
177
178
                                        // create geometry from unit line
179
0
                                        basegfx::B2DPolyPolygon a2DHatchLines;
180
0
                                        basegfx::B2DPolygon a2DUnitLine;
181
0
                                        a2DUnitLine.append(basegfx::B2DPoint(0.0, 0.0));
182
0
                                        a2DUnitLine.append(basegfx::B2DPoint(1.0, 0.0));
183
184
0
                                        for(const basegfx::B2DHomMatrix & rMatrix : aMatrices)
185
0
                                        {
186
0
                                            basegfx::B2DPolygon aNewLine(a2DUnitLine);
187
0
                                            aNewLine.transform(rMatrix);
188
0
                                            a2DHatchLines.append(aNewLine);
189
0
                                        }
190
191
0
                                        if(a2DHatchLines.count())
192
0
                                        {
193
                                            // clip against texture polygon
194
0
                                            a2DHatchLines = basegfx::utils::clipPolyPolygonOnPolyPolygon(a2DHatchLines, aTexPolyPolygon, true, true);
195
0
                                        }
196
197
0
                                        if(a2DHatchLines.count())
198
0
                                        {
199
                                            // create 2d matrix with 2d vectors as column vectors and 2d point as offset, this represents
200
                                            // a coordinate system transformation from unit coordinates to the new coordinate system
201
0
                                            basegfx::B2DHomMatrix a2D;
202
0
                                            a2D.set(0, 0, a2X.getX());
203
0
                                            a2D.set(1, 0, a2X.getY());
204
0
                                            a2D.set(0, 1, a2Y.getX());
205
0
                                            a2D.set(1, 1, a2Y.getY());
206
0
                                            a2D.set(0, 2, a2N.getX());
207
0
                                            a2D.set(1, 2, a2N.getY());
208
209
                                            // invert that transformation, so we have a back-transformation from texture coordinates
210
                                            // to unit coordinates
211
0
                                            a2D.invert();
212
0
                                            a2DHatchLines.transform(a2D);
213
214
                                            // expand back-transformed geometry to 3D
215
0
                                            basegfx::B3DPolyPolygon a3DHatchLines(basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(a2DHatchLines, 0.0));
216
217
                                            // create 3d matrix with 3d vectors as column vectors (0,0,1 as Z) and 3d point as offset, this represents
218
                                            // a coordinate system transformation from unit coordinates to the object's 3d coordinate system
219
0
                                            basegfx::B3DHomMatrix a3D;
220
0
                                            a3D.set(0, 0, a3X.getX());
221
0
                                            a3D.set(1, 0, a3X.getY());
222
0
                                            a3D.set(2, 0, a3X.getZ());
223
0
                                            a3D.set(0, 1, a3Y.getX());
224
0
                                            a3D.set(1, 1, a3Y.getY());
225
0
                                            a3D.set(2, 1, a3Y.getZ());
226
0
                                            a3D.set(0, 3, a3N.getX());
227
0
                                            a3D.set(1, 3, a3N.getY());
228
0
                                            a3D.set(2, 3, a3N.getZ());
229
230
                                            // transform hatch lines to 3D object coordinates
231
0
                                            a3DHatchLines.transform(a3D);
232
233
                                            // build primitives from this geometry
234
0
                                            const sal_uInt32 nHatchLines(a3DHatchLines.count());
235
236
0
                                            for(sal_uInt32 d(0); d < nHatchLines; d++)
237
0
                                            {
238
0
                                                const Primitive3DReference xRef(new PolygonHairlinePrimitive3D(a3DHatchLines.getB3DPolygon(d), aHatchColor));
239
0
                                                aDestination.push_back(xRef);
240
0
                                            }
241
0
                                        }
242
0
                                    }
243
0
                                }
244
245
0
                                break;
246
0
                            }
247
0
                            default :
248
0
                            {
249
                                // add reference to result
250
0
                                aDestination.push_back(xReference);
251
0
                                break;
252
0
                            }
253
0
                        }
254
0
                    }
255
0
                }
256
257
                // prepare return value
258
0
                const sal_uInt32 nDestSize(aDestination.size());
259
0
                aRetval.resize(nDestSize);
260
261
0
                for(sal_uInt32 b(0); b < nDestSize; b++)
262
0
                {
263
0
                    aRetval[b] = aDestination[b];
264
0
                }
265
0
            }
266
267
0
            return aRetval;
268
0
        }
269
270
        HatchTexturePrimitive3D::HatchTexturePrimitive3D(
271
            attribute::FillHatchAttribute aHatch,
272
            const Primitive3DContainer& rChildren,
273
            const basegfx::B2DVector& rTextureSize,
274
            bool bModulate,
275
            bool bFilter)
276
0
        :   TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter),
277
0
            maHatch(std::move(aHatch))
278
0
        {
279
0
        }
Unexecuted instantiation: drawinglayer::primitive3d::HatchTexturePrimitive3D::HatchTexturePrimitive3D(drawinglayer::attribute::FillHatchAttribute, drawinglayer::primitive3d::Primitive3DContainer const&, basegfx::B2DVector const&, bool, bool)
Unexecuted instantiation: drawinglayer::primitive3d::HatchTexturePrimitive3D::HatchTexturePrimitive3D(drawinglayer::attribute::FillHatchAttribute, drawinglayer::primitive3d::Primitive3DContainer const&, basegfx::B2DVector const&, bool, bool)
280
281
        bool HatchTexturePrimitive3D::operator==(const BasePrimitive3D& rPrimitive) const
282
0
        {
283
0
            if(TexturePrimitive3D::operator==(rPrimitive))
284
0
            {
285
0
                const HatchTexturePrimitive3D& rCompare = static_cast<const HatchTexturePrimitive3D&>(rPrimitive);
286
287
0
                return (getHatch() == rCompare.getHatch());
288
0
            }
289
290
0
            return false;
291
0
        }
292
293
        Primitive3DContainer HatchTexturePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
294
0
        {
295
0
            std::unique_lock aGuard( m_aMutex );
296
297
0
            if(getBuffered3DDecomposition().empty())
298
0
            {
299
0
                const_cast<HatchTexturePrimitive3D*>(this)->maBuffered3DDecomposition = impCreate3DDecomposition();
300
0
            }
301
302
0
            return getBuffered3DDecomposition();
303
0
        }
304
305
        // provide unique ID
306
        ImplPrimitive3DIDBlock(HatchTexturePrimitive3D, PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D)
307
308
} // end of namespace
309
310
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */