1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <basegfx/polygon/b3dpolypolygontools.hxx>
21 #include <basegfx/range/b3drange.hxx>
22 #include <basegfx/polygon/b3dpolypolygon.hxx>
23 #include <basegfx/polygon/b3dpolygon.hxx>
24 #include <basegfx/polygon/b3dpolygontools.hxx>
25 #include <basegfx/matrix/b3dhommatrix.hxx>
26 #include <basegfx/numeric/ftools.hxx>
27 #include <com/sun/star/drawing/DoubleSequence.hpp>
28 #include <com/sun/star/drawing/PolyPolygonShape3D.hpp>
31 #define nMinSegments sal_uInt32(1)
32 #define nMaxSegments sal_uInt32(512)
34 namespace basegfx::utils
36 // B3DPolyPolygon tools
37 B3DRange
getRange(const B3DPolyPolygon
& rCandidate
)
41 for(const auto& rPolygon
: rCandidate
)
43 aRetval
.expand(getRange(rPolygon
));
49 B3DPolyPolygon
const & createUnitCubePolyPolygon()
51 static auto const singleton
= [] {
52 B3DPolyPolygon aRetval
;
54 aTemp
.append(B3DPoint(0.0, 0.0, 1.0));
55 aTemp
.append(B3DPoint(0.0, 1.0, 1.0));
56 aTemp
.append(B3DPoint(1.0, 1.0, 1.0));
57 aTemp
.append(B3DPoint(1.0, 0.0, 1.0));
58 aTemp
.setClosed(true);
59 aRetval
.append(aTemp
);
62 aTemp
.append(B3DPoint(0.0, 0.0, 0.0));
63 aTemp
.append(B3DPoint(0.0, 1.0, 0.0));
64 aTemp
.append(B3DPoint(1.0, 1.0, 0.0));
65 aTemp
.append(B3DPoint(1.0, 0.0, 0.0));
66 aTemp
.setClosed(true);
67 aRetval
.append(aTemp
);
70 aTemp
.append(B3DPoint(0.0, 0.0, 0.0));
71 aTemp
.append(B3DPoint(0.0, 0.0, 1.0));
72 aRetval
.append(aTemp
);
75 aTemp
.append(B3DPoint(0.0, 1.0, 0.0));
76 aTemp
.append(B3DPoint(0.0, 1.0, 1.0));
77 aRetval
.append(aTemp
);
80 aTemp
.append(B3DPoint(1.0, 1.0, 0.0));
81 aTemp
.append(B3DPoint(1.0, 1.0, 1.0));
82 aRetval
.append(aTemp
);
85 aTemp
.append(B3DPoint(1.0, 0.0, 0.0));
86 aTemp
.append(B3DPoint(1.0, 0.0, 1.0));
87 aRetval
.append(aTemp
);
93 B3DPolyPolygon
const & createUnitCubeFillPolyPolygon()
95 static auto const singleton
= [] {
96 B3DPolyPolygon aRetval
;
100 const B3DPoint
A(0.0, 0.0, 0.0);
101 const B3DPoint
B(0.0, 1.0, 0.0);
102 const B3DPoint
C(1.0, 1.0, 0.0);
103 const B3DPoint
D(1.0, 0.0, 0.0);
104 const B3DPoint
E(0.0, 0.0, 1.0);
105 const B3DPoint
F(0.0, 1.0, 1.0);
106 const B3DPoint
G(1.0, 1.0, 1.0);
107 const B3DPoint
H(1.0, 0.0, 1.0);
114 aTemp
.setClosed(true);
115 aRetval
.append(aTemp
);
123 aTemp
.setClosed(true);
124 aRetval
.append(aTemp
);
132 aTemp
.setClosed(true);
133 aRetval
.append(aTemp
);
141 aTemp
.setClosed(true);
142 aRetval
.append(aTemp
);
150 aTemp
.setClosed(true);
151 aRetval
.append(aTemp
);
159 aTemp
.setClosed(true);
160 aRetval
.append(aTemp
);
166 B3DPolyPolygon
createCubePolyPolygonFromB3DRange( const B3DRange
& rRange
)
168 B3DPolyPolygon aRetval
;
170 if(!rRange
.isEmpty())
172 aRetval
= createUnitCubePolyPolygon();
174 aTrans
.scale(rRange
.getWidth(), rRange
.getHeight(), rRange
.getDepth());
175 aTrans
.translate(rRange
.getMinX(), rRange
.getMinY(), rRange
.getMinZ());
176 aRetval
.transform(aTrans
);
177 aRetval
.removeDoublePoints();
183 B3DPolyPolygon
createCubeFillPolyPolygonFromB3DRange( const B3DRange
& rRange
)
185 B3DPolyPolygon aRetval
;
187 if(!rRange
.isEmpty())
189 aRetval
= createUnitCubeFillPolyPolygon();
191 aTrans
.scale(rRange
.getWidth(), rRange
.getHeight(), rRange
.getDepth());
192 aTrans
.translate(rRange
.getMinX(), rRange
.getMinY(), rRange
.getMinZ());
193 aRetval
.transform(aTrans
);
194 aRetval
.removeDoublePoints();
200 // helper for getting the 3D Point from given cartesian coordinates. fHor is defined from
201 // [M_PI_2 .. -M_PI_2], fVer from [0.0 .. 2PI]
202 static B3DPoint
getPointFromCartesian(double fHor
, double fVer
)
204 const double fCosVer(cos(fVer
));
205 return B3DPoint(fCosVer
* cos(fHor
), sin(fVer
), fCosVer
* -sin(fHor
));
208 B3DPolyPolygon
createUnitSpherePolyPolygon(
209 sal_uInt32 nHorSeg
, sal_uInt32 nVerSeg
,
210 double fVerStart
, double fVerStop
,
211 double fHorStart
, double fHorStop
)
213 B3DPolyPolygon aRetval
;
218 nHorSeg
= fround(fabs(fHorStop
- fHorStart
) / (M_PI
/ 12.0));
221 // min/max limitations
222 nHorSeg
= std::clamp(nHorSeg
, nMinSegments
, nMaxSegments
);
226 nVerSeg
= fround(fabs(fVerStop
- fVerStart
) / (M_PI
/ 12.0));
229 // min/max limitations
230 nVerSeg
= std::clamp(nVerSeg
, nMinSegments
, nMaxSegments
);
233 const double fVerDiffPerStep((fVerStop
- fVerStart
) / static_cast<double>(nVerSeg
));
234 const double fHorDiffPerStep((fHorStop
- fHorStart
) / static_cast<double>(nHorSeg
));
235 bool bHorClosed(fTools::equal(fHorStop
- fHorStart
, 2 * M_PI
));
236 bool bVerFromTop(fTools::equal(fVerStart
, M_PI_2
));
237 bool bVerToBottom(fTools::equal(fVerStop
, -M_PI_2
));
239 // create horizontal rings
240 const sal_uInt32
nLoopVerInit(bVerFromTop
? 1 : 0);
241 const sal_uInt32
nLoopVerLimit(bVerToBottom
? nVerSeg
: nVerSeg
+ 1);
242 const sal_uInt32
nLoopHorLimit(bHorClosed
? nHorSeg
: nHorSeg
+ 1);
244 for(a
= nLoopVerInit
; a
< nLoopVerLimit
; a
++)
246 const double fVer(fVerStart
+ (static_cast<double>(a
) * fVerDiffPerStep
));
249 for(b
= 0; b
< nLoopHorLimit
; b
++)
251 const double fHor(fHorStart
+ (static_cast<double>(b
) * fHorDiffPerStep
));
252 aNew
.append(getPointFromCartesian(fHor
, fVer
));
255 aNew
.setClosed(bHorClosed
);
256 aRetval
.append(aNew
);
259 // create vertical half-rings
260 for(a
= 0; a
< nLoopHorLimit
; a
++)
262 const double fHor(fHorStart
+ (static_cast<double>(a
) * fHorDiffPerStep
));
267 aNew
.append(B3DPoint(0.0, 1.0, 0.0));
270 for(b
= nLoopVerInit
; b
< nLoopVerLimit
; b
++)
272 const double fVer(fVerStart
+ (static_cast<double>(b
) * fVerDiffPerStep
));
273 aNew
.append(getPointFromCartesian(fHor
, fVer
));
278 aNew
.append(B3DPoint(0.0, -1.0, 0.0));
281 aRetval
.append(aNew
);
287 B3DPolyPolygon
createSpherePolyPolygonFromB3DRange( const B3DRange
& rRange
,
288 sal_uInt32 nHorSeg
, sal_uInt32 nVerSeg
,
289 double fVerStart
, double fVerStop
,
290 double fHorStart
, double fHorStop
)
292 B3DPolyPolygon
aRetval(createUnitSpherePolyPolygon(nHorSeg
, nVerSeg
, fVerStart
, fVerStop
, fHorStart
, fHorStop
));
296 // move and scale whole construct which is now in [-1.0 .. 1.0] in all directions
298 aTrans
.translate(1.0, 1.0, 1.0);
299 aTrans
.scale(rRange
.getWidth() / 2.0, rRange
.getHeight() / 2.0, rRange
.getDepth() / 2.0);
300 aTrans
.translate(rRange
.getMinX(), rRange
.getMinY(), rRange
.getMinZ());
301 aRetval
.transform(aTrans
);
307 B3DPolyPolygon
createUnitSphereFillPolyPolygon(
308 sal_uInt32 nHorSeg
, sal_uInt32 nVerSeg
,
310 double fVerStart
, double fVerStop
,
311 double fHorStart
, double fHorStop
)
313 B3DPolyPolygon aRetval
;
317 nHorSeg
= fround(fabs(fHorStop
- fHorStart
) / (M_PI
/ 12.0));
320 // min/max limitations
321 nHorSeg
= std::clamp(nHorSeg
, nMinSegments
, nMaxSegments
);
325 nVerSeg
= fround(fabs(fVerStop
- fVerStart
) / (M_PI
/ 12.0));
328 // min/max limitations
329 nVerSeg
= std::clamp(nVerSeg
, nMinSegments
, nMaxSegments
);
332 for(sal_uInt32
a(0); a
< nVerSeg
; a
++)
334 const double fVer1(fVerStart
+ (((fVerStop
- fVerStart
) * a
) / nVerSeg
));
335 const double fVer2(fVerStart
+ (((fVerStop
- fVerStart
) * (a
+ 1)) / nVerSeg
));
338 for(sal_uInt32
b(0); b
< nHorSeg
; b
++)
340 const double fHor1(fHorStart
+ (((fHorStop
- fHorStart
) * b
) / nHorSeg
));
341 const double fHor2(fHorStart
+ (((fHorStop
- fHorStart
) * (b
+ 1)) / nHorSeg
));
344 aNew
.append(getPointFromCartesian(fHor1
, fVer1
));
345 aNew
.append(getPointFromCartesian(fHor2
, fVer1
));
346 aNew
.append(getPointFromCartesian(fHor2
, fVer2
));
347 aNew
.append(getPointFromCartesian(fHor1
, fVer2
));
351 for(sal_uInt32
c(0); c
< aNew
.count(); c
++)
353 aNew
.setNormal(c
, ::basegfx::B3DVector(aNew
.getB3DPoint(c
)));
357 aNew
.setClosed(true);
358 aRetval
.append(aNew
);
365 B3DPolyPolygon
createSphereFillPolyPolygonFromB3DRange( const B3DRange
& rRange
,
366 sal_uInt32 nHorSeg
, sal_uInt32 nVerSeg
,
368 double fVerStart
, double fVerStop
,
369 double fHorStart
, double fHorStop
)
371 B3DPolyPolygon
aRetval(createUnitSphereFillPolyPolygon(nHorSeg
, nVerSeg
, bNormals
, fVerStart
, fVerStop
, fHorStart
, fHorStop
));
375 // move and scale whole construct which is now in [-1.0 .. 1.0] in all directions
377 aTrans
.translate(1.0, 1.0, 1.0);
378 aTrans
.scale(rRange
.getWidth() / 2.0, rRange
.getHeight() / 2.0, rRange
.getDepth() / 2.0);
379 aTrans
.translate(rRange
.getMinX(), rRange
.getMinY(), rRange
.getMinZ());
380 aRetval
.transform(aTrans
);
386 B3DPolyPolygon
applyDefaultNormalsSphere( const B3DPolyPolygon
& rCandidate
, const B3DPoint
& rCenter
)
388 B3DPolyPolygon aRetval
;
390 for( const auto& rB3DPolygon
: rCandidate
)
392 aRetval
.append(applyDefaultNormalsSphere(rB3DPolygon
, rCenter
));
398 B3DPolyPolygon
invertNormals( const B3DPolyPolygon
& rCandidate
)
400 B3DPolyPolygon aRetval
;
402 for( const auto& rB3DPolygon
: rCandidate
)
404 aRetval
.append(invertNormals(rB3DPolygon
));
410 B3DPolyPolygon
applyDefaultTextureCoordinatesParallel( const B3DPolyPolygon
& rCandidate
, const B3DRange
& rRange
, bool bChangeX
, bool bChangeY
)
412 B3DPolyPolygon aRetval
;
414 for( const auto& rB3DPolygon
: rCandidate
)
416 aRetval
.append(applyDefaultTextureCoordinatesParallel(rB3DPolygon
, rRange
, bChangeX
, bChangeY
));
422 B3DPolyPolygon
applyDefaultTextureCoordinatesSphere( const B3DPolyPolygon
& rCandidate
, const B3DPoint
& rCenter
, bool bChangeX
, bool bChangeY
)
424 B3DPolyPolygon aRetval
;
426 for( const auto& rB3DPolygon
: rCandidate
)
428 aRetval
.append(applyDefaultTextureCoordinatesSphere(rB3DPolygon
, rCenter
, bChangeX
, bChangeY
));
434 bool isInside(const B3DPolyPolygon
& rCandidate
, const B3DPoint
& rPoint
)
436 const sal_uInt32
nPolygonCount(rCandidate
.count());
438 if(nPolygonCount
== 1)
440 return isInside(rCandidate
.getB3DPolygon(0), rPoint
, false/*bWithBorder*/);
444 sal_Int32
nInsideCount(0);
446 for(const auto& rPolygon
: rCandidate
)
448 const bool bInside(isInside(rPolygon
, rPoint
, false/*bWithBorder*/));
456 return (nInsideCount
% 2);
460 /// converters for css::drawing::PolyPolygonShape3D
461 B3DPolyPolygon
UnoPolyPolygonShape3DToB3DPolyPolygon(
462 const css::drawing::PolyPolygonShape3D
& rPolyPolygonShape3DSource
)
464 B3DPolyPolygon aRetval
;
465 const sal_Int32
nOuterSequenceCount(rPolyPolygonShape3DSource
.SequenceX
.getLength());
467 if(nOuterSequenceCount
)
469 assert(nOuterSequenceCount
== rPolyPolygonShape3DSource
.SequenceY
.getLength()
470 && nOuterSequenceCount
471 == rPolyPolygonShape3DSource
.SequenceZ
.getLength()&&
472 "UnoPolyPolygonShape3DToB3DPolygon: Not all double sequences have the same "
475 for(sal_Int32
a(0); a
< nOuterSequenceCount
; a
++)
477 basegfx::B3DPolygon aNewPolygon
;
479 auto& rInnerSequenceX
= rPolyPolygonShape3DSource
.SequenceX
[a
];
480 auto& rInnerSequenceY
= rPolyPolygonShape3DSource
.SequenceY
[a
];
481 auto& rInnerSequenceZ
= rPolyPolygonShape3DSource
.SequenceZ
[a
];
483 const sal_Int32
nInnerSequenceCount(rInnerSequenceX
.getLength());
484 assert(nInnerSequenceCount
== rInnerSequenceY
.getLength()
485 && nInnerSequenceCount
== rInnerSequenceZ
.getLength()
486 && "UnoPolyPolygonShape3DToB3DPolygon: Not all double sequences have "
487 "the same length (!)");
489 for(sal_Int32
b(0); b
< nInnerSequenceCount
; b
++)
491 aNewPolygon
.append(basegfx::B3DPoint(rInnerSequenceX
[b
], rInnerSequenceY
[b
], rInnerSequenceZ
[b
]));
494 // #i101520# correction is needed for imported polygons of old format,
496 basegfx::utils::checkClosed(aNewPolygon
);
498 aRetval
.append(aNewPolygon
);
505 void B3DPolyPolygonToUnoPolyPolygonShape3D(
506 const B3DPolyPolygon
& rPolyPolygonSource
,
507 css::drawing::PolyPolygonShape3D
& rPolyPolygonShape3DRetval
)
509 const sal_uInt32
nPolygonCount(rPolyPolygonSource
.count());
513 rPolyPolygonShape3DRetval
.SequenceX
.realloc(nPolygonCount
);
514 rPolyPolygonShape3DRetval
.SequenceY
.realloc(nPolygonCount
);
515 rPolyPolygonShape3DRetval
.SequenceZ
.realloc(nPolygonCount
);
517 css::drawing::DoubleSequence
* pOuterSequenceX
= rPolyPolygonShape3DRetval
.SequenceX
.getArray();
518 css::drawing::DoubleSequence
* pOuterSequenceY
= rPolyPolygonShape3DRetval
.SequenceY
.getArray();
519 css::drawing::DoubleSequence
* pOuterSequenceZ
= rPolyPolygonShape3DRetval
.SequenceZ
.getArray();
521 for(sal_uInt32
a(0); a
< nPolygonCount
; a
++)
523 const basegfx::B3DPolygon
& aPoly(rPolyPolygonSource
.getB3DPolygon(a
));
524 const sal_uInt32
nPointCount(aPoly
.count());
528 const bool bIsClosed(aPoly
.isClosed());
529 const sal_uInt32
nTargetCount(bIsClosed
? nPointCount
+ 1 : nPointCount
);
530 pOuterSequenceX
->realloc(nTargetCount
);
531 pOuterSequenceY
->realloc(nTargetCount
);
532 pOuterSequenceZ
->realloc(nTargetCount
);
534 double* pInnerSequenceX
= pOuterSequenceX
->getArray();
535 double* pInnerSequenceY
= pOuterSequenceY
->getArray();
536 double* pInnerSequenceZ
= pOuterSequenceZ
->getArray();
538 for(sal_uInt32
b(0); b
< nPointCount
; b
++)
540 const basegfx::B3DPoint
aPoint(aPoly
.getB3DPoint(b
));
542 *pInnerSequenceX
++ = aPoint
.getX();
543 *pInnerSequenceY
++ = aPoint
.getY();
544 *pInnerSequenceZ
++ = aPoint
.getZ();
549 const basegfx::B3DPoint
aPoint(aPoly
.getB3DPoint(0));
551 *pInnerSequenceX
++ = aPoint
.getX();
552 *pInnerSequenceY
++ = aPoint
.getY();
553 *pInnerSequenceZ
++ = aPoint
.getZ();
558 pOuterSequenceX
->realloc(0);
559 pOuterSequenceY
->realloc(0);
560 pOuterSequenceZ
->realloc(0);
570 rPolyPolygonShape3DRetval
.SequenceX
.realloc(0);
571 rPolyPolygonShape3DRetval
.SequenceY
.realloc(0);
572 rPolyPolygonShape3DRetval
.SequenceZ
.realloc(0);
576 } // end of namespace
578 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */