LibreOffice Module drawinglayer (master) 1
sdrextrudeprimitive3d.cxx
Go to the documentation of this file.
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 <sal/config.h>
21
22#include <cmath>
23
33#include <utility>
34
35
36using namespace com::sun::star;
37
38
40{
42 {
44
45 // get slices
46 const Slice3DVector& rSliceVector = getSlices();
47
48 if(!rSliceVector.empty())
49 {
50 sal_uInt32 a;
51
52 // decide what to create
53 const css::drawing::NormalsKind eNormalsKind(getSdr3DObjectAttribute().getNormalsKind());
54 const bool bCreateNormals(css::drawing::NormalsKind_SPECIFIC == eNormalsKind);
55 const bool bCreateTextureCoordinatesX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionX());
56 const bool bCreateTextureCoordinatesY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC == getSdr3DObjectAttribute().getTextureProjectionY());
57 basegfx::B2DHomMatrix aTexTransform;
58
59 if(!getSdrLFSAttribute().getFill().isDefault() && (bCreateTextureCoordinatesX || bCreateTextureCoordinatesY))
60 {
62 const double fFrontLength(basegfx::utils::getLength(aFirstPolygon));
63 const double fFrontArea(basegfx::utils::getArea(aFirstPolygon));
64 const double fSqrtFrontArea(sqrt(fFrontArea));
65 double fRelativeTextureWidth = basegfx::fTools::equalZero(fSqrtFrontArea) ? 1.0 : fFrontLength / fSqrtFrontArea;
66 fRelativeTextureWidth = std::trunc(fRelativeTextureWidth - 0.5);
67
68 if(fRelativeTextureWidth < 1.0)
69 {
70 fRelativeTextureWidth = 1.0;
71 }
72
73 aTexTransform.translate(-0.5, -0.5);
74 aTexTransform.scale(-1.0, -1.0);
75 aTexTransform.translate(0.5, 0.5);
76 aTexTransform.scale(fRelativeTextureWidth, 1.0);
77 }
78
79 // create geometry
80 std::vector< basegfx::B3DPolyPolygon > aFill;
81 extractPlanesFromSlice(aFill, rSliceVector,
82 bCreateNormals, getSmoothNormals(), getSmoothLids(), false,
83 0.5, 0.6, bCreateTextureCoordinatesX || bCreateTextureCoordinatesY, aTexTransform);
84
85 // get full range
86 const basegfx::B3DRange aRange(getRangeFrom3DGeometry(aFill));
87
88 // normal creation
89 if(!getSdrLFSAttribute().getFill().isDefault())
90 {
91 if(css::drawing::NormalsKind_SPHERE == eNormalsKind)
92 {
94 }
95 else if(css::drawing::NormalsKind_FLAT == eNormalsKind)
96 {
98 }
99
100 if(getSdr3DObjectAttribute().getNormalsInvert())
101 {
103 }
104 }
105
106 // texture coordinates
107 if(!getSdrLFSAttribute().getFill().isDefault())
108 {
110 getSdr3DObjectAttribute().getTextureProjectionX(),
111 getSdr3DObjectAttribute().getTextureProjectionY(),
112 aFill,
113 aRange,
115 }
116
117 if(!getSdrLFSAttribute().getFill().isDefault())
118 {
119 // add fill
121 aFill,
122 getTransform(),
125 getSdrLFSAttribute().getFill(),
126 getSdrLFSAttribute().getFillFloatTransGradient());
127 }
128 else
129 {
130 // create simplified 3d hit test geometry
132 aFill,
133 getTransform(),
136 }
137
138 // add line
139 if(!getSdrLFSAttribute().getLine().isDefault())
140 {
141 if(getSdr3DObjectAttribute().getReducedLineGeometry())
142 {
143 // create geometric outlines with reduced line geometry for chart.
144 const basegfx::B3DPolyPolygon aVerLine(extractVerticalLinesFromSlice(rSliceVector));
145 const sal_uInt32 nCount(aVerLine.count());
146 basegfx::B3DPolyPolygon aReducedLoops;
147 basegfx::B3DPolyPolygon aNewLineGeometry;
148
149 // sort out doubles (front and back planes when no edge rounding is done). Since
150 // this is a line geometry merged from PolyPolygons, loop over all Polygons
151 for(a = 0; a < nCount; a++)
152 {
153 const sal_uInt32 nReducedCount(aReducedLoops.count());
154 const basegfx::B3DPolygon& aCandidate(aVerLine.getB3DPolygon(a));
155 bool bAdd(true);
156
157 if(nReducedCount)
158 {
159 for(sal_uInt32 b(0); bAdd && b < nReducedCount; b++)
160 {
161 if(aCandidate == aReducedLoops.getB3DPolygon(b))
162 {
163 bAdd = false;
164 }
165 }
166 }
167
168 if(bAdd)
169 {
170 aReducedLoops.append(aCandidate);
171 }
172 }
173
174 // from here work with reduced loops and reduced count without changing them
175 const sal_uInt32 nReducedCount(aReducedLoops.count());
176
177 if(nReducedCount > 1)
178 {
179 for(sal_uInt32 b(1); b < nReducedCount; b++)
180 {
181 // get loop pair
182 const basegfx::B3DPolygon& aCandA(aReducedLoops.getB3DPolygon(b - 1));
183 const basegfx::B3DPolygon& aCandB(aReducedLoops.getB3DPolygon(b));
184
185 // for each loop pair create the connection edges
187 rViewInformation,
188 getTransform(),
189 aCandA,
190 aCandB,
191 aNewLineGeometry);
192 }
193 }
194
195 // add reduced loops themselves
196 aNewLineGeometry.append(aReducedLoops);
197
198 // to create vertical edges at non-C1/C2 steady loops, use maCorrectedPolyPolygon
199 // directly since the 3D Polygons do not support this.
200 //
201 // Unfortunately there is no bezier polygon provided by the chart module; one reason is
202 // that the API for extrude wants a 3D polygon geometry (for historical reasons, i guess)
203 // and those have no beziers. Another reason is that he chart module uses self-created
204 // stuff to create the 2D geometry (in ShapeFactory::createPieSegment), but this geometry
205 // does not contain bezier infos, either. The only way which is possible for now is to 'detect'
206 // candidates for vertical edges of pie segments by looking for the angles in the polygon.
207 //
208 // This is all not very well designed ATM. Ideally, the ReducedLineGeometry is responsible
209 // for creating the outer geometry edges (createReducedOutlines), but for special edges
210 // like the vertical ones for pie center and both start/end, the incarnation with the
211 // knowledge about that it needs to create those and IS a pie segment -> in this case,
212 // the chart itself.
213 const sal_uInt32 nPolyCount(maCorrectedPolyPolygon.count());
214
215 for(sal_uInt32 c(0); c < nPolyCount; c++)
216 {
218 const sal_uInt32 nPointCount(aCandidate.count());
219
220 if(nPointCount > 2)
221 {
222 sal_uInt32 nIndexA(nPointCount);
223 sal_uInt32 nIndexB(nPointCount);
224 sal_uInt32 nIndexC(nPointCount);
225
226 for(sal_uInt32 d(0); d < nPointCount; d++)
227 {
228 const sal_uInt32 nPrevInd((d + nPointCount - 1) % nPointCount);
229 const sal_uInt32 nNextInd((d + 1) % nPointCount);
230 const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(d));
231 const basegfx::B2DVector aPrev(aCandidate.getB2DPoint(nPrevInd) - aPoint);
232 const basegfx::B2DVector aNext(aCandidate.getB2DPoint(nNextInd) - aPoint);
233 const double fAngle(aPrev.angle(aNext));
234
235 // take each angle which deviates more than 10% from going straight as
236 // special edge. This will detect the two outer edges of pie segments,
237 // but not always the center one (think about a near 180 degree pie)
238 if(M_PI - fabs(fAngle) > M_PI * 0.1)
239 {
240 if(nPointCount == nIndexA)
241 {
242 nIndexA = d;
243 }
244 else if(nPointCount == nIndexB)
245 {
246 nIndexB = d;
247 }
248 else if(nPointCount == nIndexC)
249 {
250 nIndexC = d;
251 d = nPointCount;
252 }
253 }
254 }
255
256 const bool bIndexAUsed(nIndexA != nPointCount);
257 const bool bIndexBUsed(nIndexB != nPointCount);
258 bool bIndexCUsed(nIndexC != nPointCount);
259
260 if(bIndexCUsed)
261 {
262 // already three special edges found, so the center one was already detected
263 // and does not need to be searched
264 }
265 else if(bIndexAUsed && bIndexBUsed)
266 {
267 // outer edges detected (they are approx. 90 degrees), but center one not.
268 // Look with the knowledge that it's in-between the two found ones
269 if(((nIndexA + 2) % nPointCount) == nIndexB)
270 {
271 nIndexC = (nIndexA + 1) % nPointCount;
272 }
273 else if(((nIndexA + nPointCount - 2) % nPointCount) == nIndexB)
274 {
275 nIndexC = (nIndexA + nPointCount - 1) % nPointCount;
276 }
277
278 bIndexCUsed = (nIndexC != nPointCount);
279 }
280
281 if(bIndexAUsed)
282 {
283 const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexA));
284 const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0);
285 const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth());
286 basegfx::B3DPolygon aToBeAdded;
287
288 aToBeAdded.append(aStart);
289 aToBeAdded.append(aEnd);
290 aNewLineGeometry.append(aToBeAdded);
291 }
292
293 if(bIndexBUsed)
294 {
295 const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexB));
296 const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0);
297 const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth());
298 basegfx::B3DPolygon aToBeAdded;
299
300 aToBeAdded.append(aStart);
301 aToBeAdded.append(aEnd);
302 aNewLineGeometry.append(aToBeAdded);
303 }
304
305 if(bIndexCUsed)
306 {
307 const basegfx::B2DPoint aPoint(aCandidate.getB2DPoint(nIndexC));
308 const basegfx::B3DPoint aStart(aPoint.getX(), aPoint.getY(), 0.0);
309 const basegfx::B3DPoint aEnd(aPoint.getX(), aPoint.getY(), getDepth());
310 basegfx::B3DPolygon aToBeAdded;
311
312 aToBeAdded.append(aStart);
313 aToBeAdded.append(aEnd);
314 aNewLineGeometry.append(aToBeAdded);
315 }
316 }
317 }
318
319 // append loops themselves
320 aNewLineGeometry.append(aReducedLoops);
321
322 if(aNewLineGeometry.count())
323 {
325 aNewLineGeometry, getTransform(), getSdrLFSAttribute().getLine()));
326 aRetval.append(aLines);
327 }
328 }
329 else
330 {
331 // extract line geometry from slices
332 const basegfx::B3DPolyPolygon aHorLine(extractHorizontalLinesFromSlice(rSliceVector, false));
333 const basegfx::B3DPolyPolygon aVerLine(extractVerticalLinesFromSlice(rSliceVector));
334
335 // add horizontal lines
337 aHorLine, getTransform(), getSdrLFSAttribute().getLine()));
338 aRetval.append(aHorLines);
339
340 // add vertical lines
342 aVerLine, getTransform(), getSdrLFSAttribute().getLine()));
343 aRetval.append(aVerLines);
344 }
345 }
346
347 // add shadow
348 if(!getSdrLFSAttribute().getShadow().isDefault() && !aRetval.empty())
349 {
351 aRetval, getSdrLFSAttribute().getShadow(), getSdr3DObjectAttribute().getShadow3D()));
352 aRetval.append(aShadow);
353 }
354 }
355
356 return aRetval;
357 }
358
360 {
361 // prepare the polygon. No double points, correct orientations and a correct
362 // outmost polygon are needed
363 // Also important: subdivide here to ensure equal point count for all slices (!)
368
369 // prepare slices as geometry
371 }
372
374 {
375 // This can be made dependent of getSdrLFSAttribute().getFill() and getSdrLFSAttribute().getLine()
376 // again when no longer geometry is needed for non-visible 3D objects as it is now for chart
377 if(getPolyPolygon().count() && maSlices.empty())
378 {
379 std::unique_lock aGuard( m_aMutex );
380
381 const_cast< SdrExtrudePrimitive3D& >(*this).impCreateSlices();
382 }
383
384 return maSlices;
385 }
386
388 const basegfx::B3DHomMatrix& rTransform,
389 const basegfx::B2DVector& rTextureSize,
390 const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
391 const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute,
392 basegfx::B2DPolyPolygon aPolyPolygon,
393 double fDepth,
394 double fDiagonal,
395 double fBackScale,
396 bool bSmoothNormals,
397 bool bSmoothLids,
398 bool bCharacterMode,
399 bool bCloseFront,
400 bool bCloseBack)
401 : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
402 maPolyPolygon(std::move(aPolyPolygon)),
403 mfDepth(fDepth),
404 mfDiagonal(fDiagonal),
405 mfBackScale(fBackScale),
406 mbSmoothNormals(bSmoothNormals),
407 mbSmoothLids(bSmoothLids),
408 mbCharacterMode(bCharacterMode),
409 mbCloseFront(bCloseFront),
410 mbCloseBack(bCloseBack)
411 {
412 // make sure depth is positive
414 {
415 mfDepth = 0.0;
416 }
417
418 // make sure the percentage value getDiagonal() is between 0.0 and 1.0
420 {
421 mfDiagonal = 0.0;
422 }
424 {
425 mfDiagonal = 1.0;
426 }
427
428 // no close front/back when polygon is not closed
429 if(getPolyPolygon().count() && !getPolyPolygon().getB2DPolygon(0).isClosed())
430 {
431 mbCloseFront = mbCloseBack = false;
432 }
433
434 // no edge rounding when not closing
435 if(!getCloseFront() && !getCloseBack())
436 {
437 mfDiagonal = 0.0;
438 }
439 }
440
442 {
443 }
444
446 {
447 if(SdrPrimitive3D::operator==(rPrimitive))
448 {
449 const SdrExtrudePrimitive3D& rCompare = static_cast< const SdrExtrudePrimitive3D& >(rPrimitive);
450
451 return (getPolyPolygon() == rCompare.getPolyPolygon()
452 && getDepth() == rCompare.getDepth()
453 && getDiagonal() == rCompare.getDiagonal()
454 && getBackScale() == rCompare.getBackScale()
455 && getSmoothNormals() == rCompare.getSmoothNormals()
456 && getSmoothLids() == rCompare.getSmoothLids()
457 && getCharacterMode() == rCompare.getCharacterMode()
458 && getCloseFront() == rCompare.getCloseFront()
459 && getCloseBack() == rCompare.getCloseBack());
460 }
461
462 return false;
463 }
464
466 {
467 // use default from sdrPrimitive3D which uses transformation expanded by line width/2
468 // The parent implementation which uses the ranges of the decomposition would be more
469 // correct, but for historical reasons it is necessary to do the old method: To get
470 // the range of the non-transformed geometry and transform it then. This leads to different
471 // ranges where the new method is more correct, but the need to keep the old behaviour
472 // has priority here.
474 }
475
477 {
478 if(getSdr3DObjectAttribute().getReducedLineGeometry())
479 {
482 && *mpLastRLGViewInformation != rViewInformation))
483 {
484 std::unique_lock aGuard( m_aMutex );
485
486 // conditions of last local decomposition with reduced lines have changed. Remember
487 // new one and clear current decompositiopn
488 SdrExtrudePrimitive3D* pThat = const_cast< SdrExtrudePrimitive3D* >(this);
490 pThat->mpLastRLGViewInformation = rViewInformation;
491 }
492 }
493
494 // no test for buffering needed, call parent
495 return SdrPrimitive3D::get3DDecomposition(rViewInformation);
496 }
497
498 // provide unique ID
500
501} // end of namespace
502
503/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double d
void translate(double fX, double fY)
void scale(double fX, double fY)
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
sal_uInt32 count() const
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
sal_uInt32 count() const
double angle(const B2DVector &rVec) const
sal_uInt32 count() const
void append(const B3DPolygon &rPolygon, sal_uInt32 nCount=1)
B3DPolygon const & getB3DPolygon(sal_uInt32 nIndex) const
void append(const B3DPoint &rPoint, sal_uInt32 nCount=1)
TYPE getX() const
TYPE getY() const
void setBuffered3DDecomposition(const Primitive3DContainer &rNew)
const Primitive3DContainer & getBuffered3DDecomposition() const
access methods to maBuffered3DDecomposition.
virtual Primitive3DContainer get3DDecomposition(const geometry::ViewInformation3D &rViewInformation) const override
The getDecomposition default implementation will on demand use create3DDecomposition() if maBuffered3...
void append(const Primitive3DContainer &rSource)
SdrExtrudePrimitive3D(const basegfx::B3DHomMatrix &rTransform, const basegfx::B2DVector &rTextureSize, const attribute::SdrLineFillShadowAttribute3D &rSdrLFSAttribute, const attribute::Sdr3DObjectAttribute &rSdr3DObjectAttribute, basegfx::B2DPolyPolygon aPolyPolygon, double fDepth, double fDiagonal, double fBackScale, bool bSmoothNormals, bool bSmoothLids, bool bCharacterMode, bool bCloseFront, bool bCloseBack)
constructor
virtual basegfx::B3DRange getB3DRange(const geometry::ViewInformation3D &rViewInformation) const override
get range
const Slice3DVector & getSlices() const
get (evtl. create) slices
virtual bool operator==(const BasePrimitive3D &rPrimitive) const override
compare operator
const basegfx::B2DPolyPolygon & getPolyPolygon() const
data read access
virtual Primitive3DContainer get3DDecomposition(const geometry::ViewInformation3D &rViewInformation) const override
Overridden to allow for reduced line mode to decide if to buffer decomposition or not.
virtual Primitive3DContainer create3DDecomposition(const geometry::ViewInformation3D &rViewInformation) const override
local decomposition.
std::optional< geometry::ViewInformation3D > mpLastRLGViewInformation
decomposition data when ReducedLineGeometry is used, see get3DDecomposition
basegfx::B2DPolyPolygon maCorrectedPolyPolygon
geometry helper for slices
const basegfx::B3DHomMatrix & getTransform() const
data read access
basegfx::B3DRange get3DRangeFromSlices(const Slice3DVector &rSlices) const
implementation for primitive3D which will use given Slice3Ds and expand by evtl.
const basegfx::B2DVector & getTextureSize() const
const attribute::Sdr3DObjectAttribute & getSdr3DObjectAttribute() const
const attribute::SdrLineFillShadowAttribute3D & getSdrLFSAttribute() const
int nCount
#define PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D
uno_Any a
bool lessOrEqual(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
bool moreOrEqual(const T &rfValA, const T &rfValB)
double getArea(const B2DPolygon &rCandidate)
double getLength(const B2DPolygon &rCandidate)
B2DPolyPolygon correctOutmostPolygon(const B2DPolyPolygon &rCandidate)
B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon &rCandidate, double fAngleBound)
B2DPolyPolygon correctOrientations(const B2DPolyPolygon &rCandidate)
SdrPrimitive3D class.
void applyNormalsInvertTo3DGeometry(std::vector< basegfx::B3DPolyPolygon > &rFill)
ImplPrimitive3DIDBlock(PolygonHairlinePrimitive3D, PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D) Primitive3DContainer PolygonStrokePrimitive3D
Primitive3DContainer create3DPolyPolygonFillPrimitives(const std::vector< basegfx::B3DPolyPolygon > &r3DPolyPolygonVector, const basegfx::B3DHomMatrix &rObjectTransform, const basegfx::B2DVector &rTextureSize, const attribute::Sdr3DObjectAttribute &aSdr3DObjectAttribute, const attribute::SdrFillAttribute &rFill, const attribute::FillGradientAttribute &rFillGradient)
basegfx::B3DPolyPolygon extractHorizontalLinesFromSlice(const Slice3DVector &rSliceVector, bool bCloseHorLines)
helpers for geometry extraction
Primitive3DContainer createHiddenGeometryPrimitives3D(const std::vector< basegfx::B3DPolyPolygon > &r3DPolyPolygonVector, const basegfx::B3DHomMatrix &rObjectTransform, const basegfx::B2DVector &rTextureSize, const attribute::Sdr3DObjectAttribute &aSdr3DObjectAttribute)
void applyNormalsKindSphereTo3DGeometry(std::vector< basegfx::B3DPolyPolygon > &rFill, const basegfx::B3DRange &rRange)
basegfx::B3DRange getRangeFrom3DGeometry(std::vector< basegfx::B3DPolyPolygon > &rFill)
void extractPlanesFromSlice(std::vector< basegfx::B3DPolyPolygon > &rFill, const Slice3DVector &rSliceVector, bool bCreateNormals, bool bSmoothNormals, bool bSmoothLids, bool bClosed, double fSmoothNormalsMix, double fSmoothLidsMix, bool bCreateTextureCoordinates, const basegfx::B2DHomMatrix &rTexTransform)
void createExtrudeSlices(Slice3DVector &rSliceVector, const basegfx::B2DPolyPolygon &rSource, double fBackScale, double fDiagonal, double fDepth, bool bCharacterMode, bool bCloseFront, bool bCloseBack)
void applyTextureTo3DGeometry(css::drawing::TextureProjectionMode eModeX, css::drawing::TextureProjectionMode eModeY, std::vector< basegfx::B3DPolyPolygon > &rFill, const basegfx::B3DRange &rRange, const basegfx::B2DVector &rTextureSize)
void applyNormalsKindFlatTo3DGeometry(std::vector< basegfx::B3DPolyPolygon > &rFill)
::std::vector< Slice3D > Slice3DVector
typedef for a group of Slice3Ds
Primitive3DContainer createShadowPrimitive3D(const Primitive3DContainer &rSource, const attribute::SdrShadowAttribute &rShadow, bool bShadow3D)
void createReducedOutlines(const geometry::ViewInformation3D &rViewInformation, const basegfx::B3DHomMatrix &rObjectTransform, const basegfx::B3DPolygon &rLoopA, const basegfx::B3DPolygon &rLoopB, basegfx::B3DPolyPolygon &rTarget)
basegfx::B3DPolyPolygon extractVerticalLinesFromSlice(const Slice3DVector &rSliceVector)
Primitive3DContainer create3DPolyPolygonLinePrimitives(const basegfx::B3DPolyPolygon &rUnitPolyPolygon, const basegfx::B3DHomMatrix &rObjectTransform, const attribute::SdrLineAttribute &rLine)
basegfx::B2DPolyPolygon maPolyPolygon