LibreOffice Module svx (master) 1
EnhancedCustomShape3d.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
22#include <svx/deflt3d.hxx>
23#include <svx/svdmodel.hxx>
24#include <tools/poly.hxx>
25#include <svx/svditer.hxx>
26#include <svx/svdobj.hxx>
27#include <svx/svdoashp.hxx>
28#include <svl/itemset.hxx>
29#include <svl/whiter.hxx>
30#include <svx/xfillit0.hxx>
31#include <svx/xlineit0.hxx>
32#include <svx/xsflclit.hxx>
33#include <svx/xbtmpit.hxx>
34#include <svx/xflclit.hxx>
35#include <svx/svdopath.hxx>
36#include <svx/svddef.hxx>
37#include <svx/svx3ditems.hxx>
38#include <extrud3d.hxx>
39#include <svx/xflbmtit.hxx>
40#include <svx/xlnclit.hxx>
41#include <svx/sdasitm.hxx>
42#include <svx/scene3d.hxx>
43#include <com/sun/star/drawing/Position3D.hpp>
44#include <com/sun/star/drawing/Direction3D.hpp>
45#include <com/sun/star/drawing/NormalsKind.hpp>
46#include <com/sun/star/drawing/ShadeMode.hpp>
48#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
49#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp>
50#include <com/sun/star/drawing/ProjectionMode.hpp>
58#include <svx/xlnwtit.hxx>
59#include <svx/xlntrit.hxx>
60#include <svx/xfltrit.hxx>
62
63using namespace com::sun::star;
64using namespace com::sun::star::uno;
65
66namespace {
67
68void GetOrigin( const SdrCustomShapeGeometryItem& rItem, double& rOriginX, double& rOriginY )
69{
70 css::drawing::EnhancedCustomShapeParameterPair aOriginParaPair;
71 const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", "Origin" );
72 if ( ! ( pAny && ( *pAny >>= aOriginParaPair ) && ( aOriginParaPair.First.Value >>= rOriginX ) && ( aOriginParaPair.Second.Value >>= rOriginY ) ) )
73 {
74 rOriginX = 0.50;
75 rOriginY =-0.50;
76 }
77}
78
79void GetRotateAngle( const SdrCustomShapeGeometryItem& rItem, double& rAngleX, double& rAngleY )
80{
81 css::drawing::EnhancedCustomShapeParameterPair aRotateAngleParaPair;
82 const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", "RotateAngle" );
83 if ( ! ( pAny && ( *pAny >>= aRotateAngleParaPair ) && ( aRotateAngleParaPair.First.Value >>= rAngleX ) && ( aRotateAngleParaPair.Second.Value >>= rAngleY ) ) )
84 {
85 rAngleX = 0.0;
86 rAngleY = 0.0;
87 }
88 rAngleX = basegfx::deg2rad(rAngleX);
89 rAngleY = basegfx::deg2rad(rAngleY);
90}
91
92void GetSkew( const SdrCustomShapeGeometryItem& rItem, double& rSkewAmount, double& rSkewAngle )
93{
94 css::drawing::EnhancedCustomShapeParameterPair aSkewParaPair;
95 const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", "Skew" );
96 if ( ! ( pAny && ( *pAny >>= aSkewParaPair ) && ( aSkewParaPair.First.Value >>= rSkewAmount ) && ( aSkewParaPair.Second.Value >>= rSkewAngle ) ) )
97 {
98 rSkewAmount = 50;
99 // ODF default is 45, but older ODF documents expect -135 as default. For intermediate
100 // solution see tdf#141301 and tdf#141127.
101 // MS Office default -135 is set in msdffimp.cxx to make import independent from setting here.
102 rSkewAngle = -135;
103 }
104 rSkewAngle = basegfx::deg2rad(rSkewAngle);
105}
106
107void GetExtrusionDepth( const SdrCustomShapeGeometryItem& rItem, const double* pMap, double& rBackwardDepth, double& rForwardDepth )
108{
109 css::drawing::EnhancedCustomShapeParameterPair aDepthParaPair;
110 double fDepth = 0, fFraction = 0;
111 const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", "Depth" );
112 if ( pAny && ( *pAny >>= aDepthParaPair ) && ( aDepthParaPair.First.Value >>= fDepth ) && ( aDepthParaPair.Second.Value >>= fFraction ) )
113 {
114 rForwardDepth = fDepth * fFraction;
115 rBackwardDepth = fDepth - rForwardDepth;
116 }
117 else
118 {
119 rBackwardDepth = 1270;
120 rForwardDepth = 0;
121 }
122 if ( pMap )
123 {
124 double fMap = *pMap;
125 rBackwardDepth *= fMap;
126 rForwardDepth *= fMap;
127 }
128}
129
130double GetDouble( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName, double fDefault )
131{
132 double fRetValue = fDefault;
133 const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", rPropertyName );
134 if ( pAny )
135 *pAny >>= fRetValue;
136 return fRetValue;
137}
138
139drawing::ShadeMode GetShadeMode( const SdrCustomShapeGeometryItem& rItem, const drawing::ShadeMode eDefault )
140{
141 drawing::ShadeMode eRet( eDefault );
142 const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", "ShadeMode" );
143 if ( pAny )
144 *pAny >>= eRet;
145 return eRet;
146}
147
148bool GetBool( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName, const bool bDefault )
149{
150 bool bRetValue = bDefault;
151 const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", rPropertyName );
152 if ( pAny )
153 *pAny >>= bRetValue;
154 return bRetValue;
155}
156
157drawing::Position3D GetPosition3D( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName,
158 const drawing::Position3D& rDefault, const double* pMap )
159{
160 drawing::Position3D aRetValue( rDefault );
161 const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", rPropertyName );
162 if ( pAny )
163 *pAny >>= aRetValue;
164 if ( pMap )
165 {
166 aRetValue.PositionX *= *pMap;
167 aRetValue.PositionY *= *pMap;
168 aRetValue.PositionZ *= *pMap;
169 }
170 return aRetValue;
171}
172
173drawing::Direction3D GetDirection3D( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName, const drawing::Direction3D& rDefault )
174{
175 drawing::Direction3D aRetValue( rDefault );
176 const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", rPropertyName );
177 if ( pAny )
178 *pAny >>= aRetValue;
179 return aRetValue;
180}
181
182sal_Int16 GetMetalType(const SdrCustomShapeGeometryItem& rItem, const sal_Int16 eDefault)
183{
184 sal_Int16 aRetValue(eDefault);
185 const Any* pAny = rItem.GetPropertyValueByName("Extrusion", "MetalType");
186 if (pAny)
187 *pAny >>= aRetValue;
188 return aRetValue;
189}
190
191// Calculates the light directions for the additional lights, which are used to emulate soft
192// lights of MS Office. Method needs to be documented in the Wiki
193// https://wiki.documentfoundation.org/Development/ODF_Implementer_Notes in part
194// List_of_LibreOffice_ODF_implementation-defined_items
195// The method expects vector rLight to be normalized and results normalized vectors.
196void lcl_SoftLightsDirection(const basegfx::B3DVector& rLight, basegfx::B3DVector& rSoftUp,
197 basegfx::B3DVector& rSoftDown, basegfx::B3DVector& rSoftRight,
198 basegfx::B3DVector& rSoftLeft)
199{
200 constexpr double fAngle = basegfx::deg2rad(60); // angle between regular light and soft light
201
202 // We first create directions around (0|0|1) and then rotate them to the light position.
203 rSoftUp = basegfx::B3DVector(0.0, sin(fAngle), cos(fAngle));
204 rSoftDown = basegfx::B3DVector(0.0, -sin(fAngle), cos(fAngle));
205 rSoftRight = basegfx::B3DVector(sin(fAngle), 0.0, cos(fAngle));
206 rSoftLeft = basegfx::B3DVector(-sin(fAngle), 0.0, cos(fAngle));
207
208 basegfx::B3DHomMatrix aRotateMat;
209 aRotateMat.rotate(0.0, 0.0, M_PI_4);
210 if (rLight.getX() == 0.0 && rLight.getZ() == 0.0)
211 {
212 // Special case with light from top or bottom
213 if (rLight.getY() >= 0.0)
214 aRotateMat.rotate(-M_PI_2, 0.0, 0.0);
215 else
216 aRotateMat.rotate(M_PI_2, 0.0, 0.0);
217 }
218 else
219 {
220 // Azimuth from z-axis to x-axis. (0|0|1) to (1|0|0) is 90deg.
221 double fAzimuth = atan2(rLight.getX(), rLight.getZ());
222 // Elevation from xz-plane to y-axis. (0|0|1) to (0|1|0) is 90deg.
223 double fElevation = atan2(rLight.getY(), std::hypot(rLight.getX(), rLight.getZ()));
224 aRotateMat.rotate(-fElevation, fAzimuth, 0.0);
225 }
226
227 rSoftUp = aRotateMat * rSoftUp;
228 rSoftDown = aRotateMat * rSoftDown;
229 rSoftRight = aRotateMat * rSoftRight;
230 rSoftLeft = aRotateMat * rSoftLeft;
231}
232}
233
235 const SdrObject* pShape2d,
236 const SdrObjCustomShape& rSdrObjCustomShape)
237{
239 const SdrCustomShapeGeometryItem& rGeometryItem(rSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY));
240 double fMap(1.0), *pMap = nullptr;
241 Fraction aFraction( rSdrObjCustomShape.getSdrModelFromSdrObject().GetScaleFraction() );
242
243 if ( aFraction.GetNumerator() != 1 || aFraction.GetDenominator() != 1 )
244 {
245 fMap *= double(aFraction);
246 pMap = &fMap;
247 }
248
249 if ( rSdrObjCustomShape.getSdrModelFromSdrObject().GetScaleUnit() != MapUnit::Map100thMM )
250 {
251 DBG_ASSERT( rSdrObjCustomShape.getSdrModelFromSdrObject().GetScaleUnit() == MapUnit::MapTwip, "EnhancedCustomShape3d::Current MapMode is Unsupported" );
252 // But we could use MapToO3tlUnit from <tools/UnitConversion> ... ?
254 pMap = &fMap;
255 }
256
257 if ( GetBool( rGeometryItem, "Extrusion", false ) )
258 {
259 bool bIsMirroredX(rSdrObjCustomShape.IsMirroredX());
260 bool bIsMirroredY(rSdrObjCustomShape.IsMirroredY());
261 tools::Rectangle aSnapRect(rSdrObjCustomShape.GetLogicRect());
262 Degree100 nObjectRotation(rSdrObjCustomShape.GetRotateAngle());
263 if ( nObjectRotation )
264 {
265 double a = toRadians(36000_deg100 - nObjectRotation);
266 tools::Long dx = aSnapRect.Right() - aSnapRect.Left();
267 tools::Long dy = aSnapRect.Bottom()- aSnapRect.Top();
268 Point aP( aSnapRect.TopLeft() );
269 RotatePoint( aP, rSdrObjCustomShape.GetSnapRect().Center(), sin( a ), cos( a ) );
270 aSnapRect.SetLeft( aP.X() );
271 aSnapRect.SetTop( aP.Y() );
272 aSnapRect.SetRight( aSnapRect.Left() + dx );
273 aSnapRect.SetBottom( aSnapRect.Top() + dy );
274 }
275 Point aCenter( aSnapRect.Center() );
276
277 SfxItemSet aSet( rSdrObjCustomShape.GetMergedItemSet() );
278
279 // tdf#146360 If the ItemSet of the source SdrObject has a parent
280 // (which means it has a StyleSheet), we need to do some old-style
281 // 'BurnInStyleSheetAttributes' action.
282 // That means to set all Items which are set in the StyleSheet
283 // directly in the ItemSet.
284 // This is okay here since the 3D SdrObjects created are
285 // placeholders that get rendered, but never reach the
286 // surface/the user. If attributes for the source SdrObject
287 // change, these will be recreated.
288 // The problem is that while "aSet" still has a ptr to the style's
289 // ItemSet, this gets lost at the ItemSet of the SdrObject when
290 // an ItemSet gets set at the 3D SdrObject, like in diverse
291 // SetMergedItemSet calls below. This leads to fetching the wrong
292 // (default) FillBitmap in the calls p3DObj->GetMergedItem below
293 // (which is 32x32 white, that's what you see without the fix).
294 // This could also be fixed (tried it) by either
295 // - using rSdrObjCustomShape.GetMergedItem
296 // - setting the StyleSheet at 3D SdrObjects ASAP (done at caller)
297 // but both solutions contain the risk to not find all places, so
298 // it's just more safe to merge the StyleSheet attributes to the
299 // ItemSet used for the whole creation.
300 if(nullptr != aSet.GetParent())
301 {
302 SfxWhichIter aIter(aSet);
303 sal_uInt16 nWhich(aIter.FirstWhich());
304 const SfxPoolItem *pItem(nullptr);
305
306 while(nWhich)
307 {
308 // this may look at 1st look like doing nothing, but it converts
309 // items set in parent/style to SfxItemState::SET items in the
310 // ItemSet (see AttributeProperties::ForceStyleToHardAttributes())
311 if(SfxItemState::SET == aSet.GetItemState(nWhich, true, &pItem))
312 {
313 aSet.Put(*pItem);
314 }
315
316 nWhich = aIter.NextWhich();
317 }
318
319 aSet.SetParent(nullptr);
320 }
321
322 //SJ: vertical writing is not required, by removing this item no outliner is created
324
325 // #i105323# For 3D AutoShapes, the shadow attribute has to be applied to each
326 // created visualisation helper model shape individually. The shadow itself
327 // will then be rendered from the 3D renderer correctly for the whole 3D scene
328 // (and thus behind all objects of which the visualisation may be built). So,
329 // do NOT remove it from the ItemSet here.
330 // aSet.ClearItem(SDRATTR_SHADOW);
331
332 std::vector< E3dCompoundObject* > aPlaceholderObjectList;
333
334 double fExtrusionBackward, fExtrusionForward;
335 GetExtrusionDepth( rGeometryItem, pMap, fExtrusionBackward, fExtrusionForward );
336 double fDepth = fExtrusionBackward + fExtrusionForward;
337 if ( fDepth < 1.0 )
338 fDepth = 1.0;
339
340 drawing::ProjectionMode eProjectionMode( drawing::ProjectionMode_PARALLEL );
341 const Any* pAny = rGeometryItem.GetPropertyValueByName( "Extrusion", "ProjectionMode" );
342 if ( pAny )
343 *pAny >>= eProjectionMode;
344 // pShape2d Convert in scenes which include 3D Objects
345 E3dDefaultAttributes a3DDefaultAttr;
346 a3DDefaultAttr.SetDefaultLatheCharacterMode( true );
347 a3DDefaultAttr.SetDefaultExtrudeCharacterMode( true );
348
349 rtl::Reference<E3dScene> pScene = new E3dScene(rSdrObjCustomShape.getSdrModelFromSdrObject());
350
351 bool bSceneHasObjects ( false );
352 bool bUseTwoFillStyles( false );
353
354 drawing::ShadeMode eShadeMode( GetShadeMode( rGeometryItem, drawing::ShadeMode_FLAT ) );
355 bool bUseExtrusionColor = GetBool( rGeometryItem, "Color", false );
356
357 drawing::FillStyle eFillStyle( aSet.Get(XATTR_FILLSTYLE).GetValue() );
358 pScene->GetProperties().SetObjectItem( Svx3DShadeModeItem(static_cast<sal_uInt16>(eShadeMode)));
360 aSet.Put( Svx3DTextureModeItem( 1 ) );
361 // SPECIFIC needed for ShadeMode_SMOOTH and ShadeMode_PHONG, otherwise FLAT is faster.
362 if (eShadeMode == drawing::ShadeMode_SMOOTH || eShadeMode == drawing::ShadeMode_PHONG)
363 aSet.Put( Svx3DNormalsKindItem(static_cast<sal_uInt16>(drawing::NormalsKind_SPECIFIC)));
364 else
365 aSet.Put( Svx3DNormalsKindItem(static_cast<sal_uInt16>(drawing::NormalsKind_FLAT)));
366
367 if ( eShadeMode == drawing::ShadeMode_DRAFT )
368 {
369 aSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) );
370 aSet.Put( XFillStyleItem ( drawing::FillStyle_NONE ) );
371 aSet.Put( makeSvx3DDoubleSidedItem( true ) );
372 }
373 else
374 {
375 aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
376 if ( eFillStyle == drawing::FillStyle_NONE )
377 aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) );
378 else if ( ( eFillStyle == drawing::FillStyle_BITMAP ) || ( eFillStyle == drawing::FillStyle_GRADIENT ) || bUseExtrusionColor )
379 bUseTwoFillStyles = true;
380
381 // If shapes are mirrored once (mirroring two times correct geometry again)
382 // double-sided at the object and two-sided-lighting at the scene need to be set.
383
384 // #i122777# Also use double sided for two fill styles since there several 3d objects get
385 // created with a depth of 0; one of them is the backside which needs double-sided to
386 // get visible
387 if(bUseTwoFillStyles || (bIsMirroredX && !bIsMirroredY) || (!bIsMirroredX && bIsMirroredY))
388 {
389 aSet.Put( makeSvx3DDoubleSidedItem( true ) );
390 pScene->GetProperties().SetObjectItem( makeSvx3DTwoSidedLightingItem( true ) );
391 }
392 }
393
394 tools::Rectangle aBoundRect2d;
395 basegfx::B2DPolyPolygon aTotalPolyPoly;
396 SdrObjListIter aIter( *pShape2d, SdrIterMode::DeepNoGroups );
397 const bool bMultipleSubObjects(aIter.Count() > 1);
398 const bool bFuzzing(utl::ConfigManager::IsFuzzing());
399
400 while( aIter.IsMore() )
401 {
402 const SdrObject* pNext = aIter.Next();
403 bool bIsPlaceholderObject = (pNext->GetMergedItem( XATTR_FILLSTYLE ).GetValue() == drawing::FillStyle_NONE )
404 && (pNext->GetMergedItem( XATTR_LINESTYLE ).GetValue() == drawing::LineStyle_NONE );
405 basegfx::B2DPolyPolygon aPolyPoly;
406 SfxItemSet aLocalSet(aSet);
407 drawing::FillStyle aLocalFillStyle(eFillStyle);
408
409 if ( auto pPathObj = dynamic_cast<const SdrPathObj*>(pNext) )
410 {
411 const SfxItemSet& rSet = pNext->GetMergedItemSet();
412 bool bNeedToConvertToContour(false);
413
414 // do conversion only for single line objects; for all others a fill and a
415 // line object get created. When we have fill, we want no line. That line has
416 // always been there, but since it was never converted to contour, it kept
417 // invisible (all this 'hidden' logic should be migrated to primitives).
418 if(!bMultipleSubObjects)
419 {
420 const drawing::FillStyle eStyle(rSet.Get(XATTR_FILLSTYLE).GetValue());
421
422 if(drawing::FillStyle_NONE == eStyle)
423 {
426
427 bNeedToConvertToContour = (0.0 < aLine.getWidth() || 0.0 != aLine.getFullDotDashLen());
428
429 if(!bNeedToConvertToContour && !aLine.isDefault())
430 {
433
434 if((aLineStartEnd.getStartWidth() && aLineStartEnd.isStartActive())
435 || (aLineStartEnd.getEndWidth() && aLineStartEnd.isEndActive()))
436 {
437 bNeedToConvertToContour = true;
438 }
439 }
440 }
441 }
442
443 if (bNeedToConvertToContour && !bFuzzing)
444 {
445 rtl::Reference<SdrObject> pNewObj = pNext->ConvertToContourObj(const_cast< SdrObject* >(pNext));
446 SdrPathObj* pNewPathObj = dynamic_cast< SdrPathObj* >(pNewObj.get());
447
448 if(pNewPathObj)
449 {
450 aPolyPoly = pNewPathObj->GetPathPoly();
451
452 if(aPolyPoly.isClosed())
453 {
454 // correct item properties from line to fill style
455 if(eShadeMode == drawing::ShadeMode_DRAFT)
456 {
457 // for draft, create wireframe with fixed line width
458 aLocalSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
459 aLocalSet.Put(XLineWidthItem(40));
460 aLocalFillStyle = drawing::FillStyle_NONE;
461 }
462 else
463 {
464 // switch from line to fill, copy line attr to fill attr (color, transparence)
465 aLocalSet.Put(XLineWidthItem(0));
466 aLocalSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
467 aLocalSet.Put(XFillColorItem(OUString(), aLocalSet.Get(XATTR_LINECOLOR).GetColorValue()));
468 aLocalSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
469 aLocalSet.Put(XFillTransparenceItem(aLocalSet.Get(XATTR_LINETRANSPARENCE).GetValue()));
470 aLocalFillStyle = drawing::FillStyle_SOLID;
471 }
472 }
473 else
474 {
475 // correct item properties to hairlines
476 aLocalSet.Put(XLineWidthItem(0));
477 aLocalSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
478 }
479 }
480 }
481 else
482 {
483 aPolyPoly = pPathObj->GetPathPoly();
484 }
485 }
486 else
487 {
488 rtl::Reference<SdrObject> pNewObj = pNext->ConvertToPolyObj( false, false );
489 SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pNewObj.get() );
490 if ( pPath )
491 aPolyPoly = pPath->GetPathPoly();
492 }
493
494 if( aPolyPoly.count() )
495 {
496 if(aPolyPoly.areControlPointsUsed())
497 {
498 aPolyPoly = basegfx::utils::adaptiveSubdivideByAngle(aPolyPoly);
499 }
500
501 const basegfx::B2DRange aTempRange(basegfx::utils::getRange(aPolyPoly));
502 const tools::Rectangle aBoundRect(basegfx::fround(aTempRange.getMinX()), basegfx::fround(aTempRange.getMinY()), basegfx::fround(aTempRange.getMaxX()), basegfx::fround(aTempRange.getMaxY()));
503 aTotalPolyPoly.append(aPolyPoly);
504 aBoundRect2d.Union( aBoundRect );
505
506 // #i122777# depth 0 is okay for planes when using double-sided
507 rtl::Reference<E3dCompoundObject> p3DObj = new E3dExtrudeObj(
508 rSdrObjCustomShape.getSdrModelFromSdrObject(),
509 a3DDefaultAttr,
510 aPolyPoly,
511 bUseTwoFillStyles ? 0 : fDepth );
512
513 p3DObj->NbcSetLayer( pShape2d->GetLayer() );
514 p3DObj->SetMergedItemSet( aLocalSet );
515
516 if ( bIsPlaceholderObject )
517 aPlaceholderObjectList.push_back( p3DObj.get() );
518 else if ( bUseTwoFillStyles )
519 {
520 BitmapEx aFillBmp;
521 bool bFillBmpTile = p3DObj->GetMergedItem( XATTR_FILLBMP_TILE ).GetValue();
522 if ( bFillBmpTile )
523 {
524 const XFillBitmapItem& rBmpItm = p3DObj->GetMergedItem(XATTR_FILLBITMAP);
525 aFillBmp = rBmpItm.GetGraphicObject().GetGraphic().GetBitmapEx();
526
527 // #i122777# old adaptation of FillStyle bitmap size to 5-times the original size; this is not needed
528 // anymore and was used in old times to male the fill look better when converting to 3D. Removed
529 // from regular 3D objects for some time, also needs to be removed from CustomShapes
530
531 //Size aLogicalSize = aFillBmp.GetPrefSize();
532 //if ( aFillBmp.GetPrefMapMode() == MapUnit::MapPixel )
533 // aLogicalSize = Application::GetDefaultDevice()->PixelToLogic( aLogicalSize, MapUnit::Map100thMM );
534 //else
535 // aLogicalSize = OutputDevice::LogicToLogic( aLogicalSize, aFillBmp.GetPrefMapMode(), MapUnit::Map100thMM );
536 //aLogicalSize.Width() *= 5; ;// :-( nice scaling, look at engine3d/obj3d.cxx
537 //aLogicalSize.Height() *= 5;
538 //aFillBmp.SetPrefSize( aLogicalSize );
539 //aFillBmp.SetPrefMapMode( MapUnit::Map100thMM );
540 //p3DObj->SetMergedItem(XFillBitmapItem(String(), Graphic(aFillBmp)));
541 }
542 else
543 {
544 if ( aSnapRect != aBoundRect && aSnapRect.GetWidth() > 0 && aSnapRect.GetHeight() > 0)
545 {
546 const XFillBitmapItem& rBmpItm = p3DObj->GetMergedItem(XATTR_FILLBITMAP);
547 aFillBmp = rBmpItm.GetGraphicObject().GetGraphic().GetBitmapEx();
548 Size aBmpSize( aFillBmp.GetSizePixel() );
549 double fXScale = static_cast<double>(aBoundRect.GetWidth()) / static_cast<double>(aSnapRect.GetWidth());
550 double fYScale = static_cast<double>(aBoundRect.GetHeight()) / static_cast<double>(aSnapRect.GetHeight());
551
552 Point aPt( static_cast<sal_Int32>( static_cast<double>( aBoundRect.Left() - aSnapRect.Left() )* static_cast<double>(aBmpSize.Width()) / static_cast<double>(aSnapRect.GetWidth()) ),
553 static_cast<sal_Int32>( static_cast<double>( aBoundRect.Top() - aSnapRect.Top() ) * static_cast<double>(aBmpSize.Height()) / static_cast<double>(aSnapRect.GetHeight()) ) );
554 Size aSize( static_cast<sal_Int32>( aBmpSize.Width() * fXScale ),
555 static_cast<sal_Int32>( aBmpSize.Height() * fYScale ) );
556 tools::Rectangle aCropRect( aPt, aSize );
557 aFillBmp.Crop( aCropRect );
558 p3DObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aFillBmp)));
559 }
560 }
561 pScene->InsertObject( p3DObj.get() );
562 p3DObj = new E3dExtrudeObj(
563 rSdrObjCustomShape.getSdrModelFromSdrObject(),
564 a3DDefaultAttr,
565 aPolyPoly,
566 fDepth);
567 p3DObj->NbcSetLayer( pShape2d->GetLayer() );
568 p3DObj->SetMergedItemSet( aLocalSet );
569 if ( bUseExtrusionColor )
570 p3DObj->SetMergedItem( XFillColorItem( "", rSdrObjCustomShape.GetMergedItem( XATTR_SECONDARYFILLCOLOR ).GetColorValue() ) );
571 p3DObj->SetMergedItem( XFillStyleItem( drawing::FillStyle_SOLID ) );
572 p3DObj->SetMergedItem( Svx3DCloseFrontItem( false ) );
573 p3DObj->SetMergedItem( Svx3DCloseBackItem( false ) );
574 pScene->InsertObject( p3DObj.get() );
575
576 // #i122777# depth 0 is okay for planes when using double-sided
577 p3DObj = new E3dExtrudeObj(
578 rSdrObjCustomShape.getSdrModelFromSdrObject(),
579 a3DDefaultAttr,
580 std::move(aPolyPoly),
581 0);
582
583 p3DObj->NbcSetLayer( pShape2d->GetLayer() );
584 p3DObj->SetMergedItemSet( aLocalSet );
585
586 basegfx::B3DHomMatrix aFrontTransform( p3DObj->GetTransform() );
587 aFrontTransform.translate( 0.0, 0.0, fDepth );
588 p3DObj->NbcSetTransform( aFrontTransform );
589
590 if ( ( aLocalFillStyle == drawing::FillStyle_BITMAP ) && !aFillBmp.IsEmpty() )
591 {
592 p3DObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aFillBmp)));
593 }
594 }
595 else if ( aLocalFillStyle == drawing::FillStyle_NONE )
596 {
597 const XLineColorItem& rLineColor = p3DObj->GetMergedItem( XATTR_LINECOLOR );
598 p3DObj->SetMergedItem( XFillColorItem( "", rLineColor.GetColorValue() ) );
599 p3DObj->SetMergedItem( makeSvx3DDoubleSidedItem( true ) );
600 p3DObj->SetMergedItem( Svx3DCloseFrontItem( false ) );
601 p3DObj->SetMergedItem( Svx3DCloseBackItem( false ) );
602 }
603 pScene->InsertObject( p3DObj.get() );
604 bSceneHasObjects = true;
605 }
606 }
607
608 if ( bSceneHasObjects ) // is the SdrObject properly converted
609 {
610 // then we can change the return value
611 pRet = pScene;
612
613 // Camera settings, Perspective ...
614 Camera3D rCamera = pScene->GetCamera();
615 pScene->NbcSetSnapRect( aSnapRect );
616
617 // InitScene replacement
618 double fW = aBoundRect2d.getOpenWidth();
619 double fH = aBoundRect2d.getOpenHeight();
620 rCamera.SetAutoAdjustProjection( false );
621 rCamera.SetViewWindow( -fW / 2, - fH / 2, fW, fH);
622 basegfx::B3DPoint aLookAt( 0.0, 0.0, 0.0 );
623 basegfx::B3DPoint aCamPos( 0.0, 0.0, 100.0 );
624 rCamera.SetPosAndLookAt( aCamPos, aLookAt );
625 rCamera.SetFocalLength( 1.0 );
626 ProjectionType eProjectionType( eProjectionMode == drawing::ProjectionMode_PARALLEL ? ProjectionType::Parallel : ProjectionType::Perspective );
627 rCamera.SetProjection( eProjectionType );
628 pScene->SetCamera( rCamera );
629 pScene->SetBoundAndSnapRectsDirty();
630
631 basegfx::B3DHomMatrix aNewTransform( pScene->GetTransform() );
632 basegfx::B2DHomMatrix aPolyPolyTransform;
633 // Apply flip and z-rotation to scene transformation (y up). At same time transform
634 // aTotalPolyPoly (y down) which will be used for 2D boundRect of shape having 2D
635 // transformations applied.
636
637 // API values use shape center as origin. Move scene so, that shape center is origin.
638 aNewTransform.translate( -aCenter.X(), aCenter.Y(), -fExtrusionBackward);
639 aPolyPolyTransform.translate(-aCenter.X(), -aCenter.Y());
640
641 double fZRotate(basegfx::deg2rad(rSdrObjCustomShape.GetObjectRotation()));
642 if ( fZRotate != 0.0 )
643 {
644 aNewTransform.rotate( 0.0, 0.0, fZRotate );
645 aPolyPolyTransform.rotate(-fZRotate);
646 }
647 if ( bIsMirroredX )
648 {
649 aNewTransform.scale( -1.0, 1, 1 );
650 aPolyPolyTransform.scale(-1.0, 1);
651 }
652 if ( bIsMirroredY )
653 {
654 aNewTransform.scale( 1, -1.0, 1 );
655 aPolyPolyTransform.scale(1, -1.0);
656 }
657 aPolyPolyTransform.translate(aCenter.X(), aCenter.Y());
658 aTotalPolyPoly.transform(aPolyPolyTransform);
659
660 // x- and y-rotation have an own rotation center. x- and y-value of rotation center are
661 // fractions of shape size, z-value is in Hmm in property. Shape center is (0 0 0).
662 // Values in property are in custom shape extrusion space with y-axis down.
663 double fXRotate, fYRotate;
664 GetRotateAngle( rGeometryItem, fXRotate, fYRotate );
665 drawing::Direction3D aRotationCenterDefault( 0, 0, 0 );
666 drawing::Direction3D aRotationCenter( GetDirection3D( rGeometryItem, "RotationCenter", aRotationCenterDefault ) );
667 aRotationCenter.DirectionX *= aSnapRect.getOpenWidth();
668 aRotationCenter.DirectionY *= aSnapRect.getOpenHeight();
669 if (pMap)
670 {
671 aRotationCenter.DirectionZ *= *pMap;
672 }
673 aNewTransform.translate( -aRotationCenter.DirectionX, aRotationCenter.DirectionY, -aRotationCenter.DirectionZ );
674 if( fYRotate != 0.0 )
675 aNewTransform.rotate( 0.0, -fYRotate, 0.0 );
676 if( fXRotate != 0.0 )
677 aNewTransform.rotate( -fXRotate, 0.0, 0.0 );
678 aNewTransform.translate(aRotationCenter.DirectionX, -aRotationCenter.DirectionY, aRotationCenter.DirectionZ);
679
680 // oblique parallel projection is done by shearing the object, not by moving the camera
681 if (eProjectionMode == drawing::ProjectionMode_PARALLEL)
682 {
683 double fSkew, fAlpha;
684 GetSkew( rGeometryItem, fSkew, fAlpha );
685 if ( fSkew != 0.0 )
686 {
687 double fInvTanBeta( fSkew / 100.0 );
688 if(fInvTanBeta)
689 {
690 aNewTransform.shearXY(
691 fInvTanBeta * cos(fAlpha),
692 fInvTanBeta * sin(fAlpha));
693 }
694 }
695 }
696
697 pScene->NbcSetTransform( aNewTransform );
698
699 // These values are used later again, so declare them outside the if-statement. They will
700 // contain the absolute values of ViewPoint in 3D scene coordinate system, y-axis up.
701 double fViewPointX = 0; // dummy values
702 double fViewPointY = 0;
703 double fViewPointZ = 25000;
704 if (eProjectionMode == drawing::ProjectionMode_PERSPECTIVE)
705 {
706 double fOriginX, fOriginY;
707 // Calculate BoundRect of shape, including flip and z-rotation, from aTotalPolyPoly.
708 tools::Rectangle aBoundAfter2DTransform; // aBoundAfter2DTransform has y-axis down.
709 basegfx::B2DRange aTotalPolyPolyRange(aTotalPolyPoly.getB2DRange());
710 aBoundAfter2DTransform.SetLeft(aTotalPolyPolyRange.getMinX());
711 aBoundAfter2DTransform.SetTop(aTotalPolyPolyRange.getMinY());
712 aBoundAfter2DTransform.SetRight(aTotalPolyPolyRange.getMaxX());
713 aBoundAfter2DTransform.SetBottom(aTotalPolyPolyRange.getMaxY());
714
715 // Property "Origin" in API is relative to bounding box of shape after 2D
716 // transformations. Range is [-0.5;0.5] with center of bounding box as 0.
717 // Resolve "Origin" fractions to length
718 GetOrigin( rGeometryItem, fOriginX, fOriginY );
719 fOriginX *= aBoundAfter2DTransform.GetWidth();
720 fOriginY *= aBoundAfter2DTransform.GetHeight();
721 // Resolve length to absolute value for 3D
722 fOriginX += aBoundAfter2DTransform.Center().X();
723 fOriginY += aBoundAfter2DTransform.Center().Y();
724 fOriginY = - fOriginY;
725 // Scene is translated so that shape center is origin of coordinate system.
726 // Translate point "Origin" too.
727 fOriginX -= aCenter.X();
728 fOriginY -= -aCenter.Y();
729 // API ViewPoint values are relative to point "Origin" and have y-axis down.
730 // ToDo: These default ViewPoint values are used as default by MS Office. But ODF
731 // default is (3500, -3500, 25000), details in tdf#146192.
732 drawing::Position3D aViewPointDefault( 3472, -3472, 25000 );
733 drawing::Position3D aViewPoint( GetPosition3D( rGeometryItem, "ViewPoint", aViewPointDefault, pMap ) );
734 fViewPointX = aViewPoint.PositionX + fOriginX;
735 fViewPointY = - aViewPoint.PositionY + fOriginY;
736 fViewPointZ = aViewPoint.PositionZ;
737 }
738
739 // now set correct camera position
740 if (eProjectionMode == drawing::ProjectionMode_PARALLEL)
741 {
742 basegfx::B3DPoint _aLookAt( 0.0, 0.0, 0.0 );
743 basegfx::B3DPoint _aNewCamPos( 0.0, 0.0, 25000.0 );
744 rCamera.SetPosAndLookAt( _aNewCamPos, _aLookAt );
745 pScene->SetCamera( rCamera );
746 }
747 else
748 {
749 basegfx::B3DPoint _aLookAt(fViewPointX, fViewPointY, 0.0);
750 basegfx::B3DPoint aNewCamPos(fViewPointX, fViewPointY, fViewPointZ);
751 rCamera.SetPosAndLookAt( aNewCamPos, _aLookAt );
752 pScene->SetCamera( rCamera );
753 }
754
755 // NbcSetTransform has not updated the scene 2D rectangles.
756 // Idea: Get a bound volume as polygon from bound rectangle of shape without 2D
757 // transformations. Calculate its projection to the XY-plane. Then calculate the bounding
758 // rectangle of the projection and convert this rectangle back to absolute 2D coordinates.
759 // Set that as 2D rectangle of the scene.
760 const tools::Polygon aPolygon(aBoundRect2d); // y-up
761 basegfx::B3DPolygon aPolygonBoundVolume; // y-down, scene coordinates
762 for (sal_uInt16 i = 0; i < 4; i++ )
763 {
764 aPolygonBoundVolume.append(basegfx::B3DPoint(aPolygon[i].X(), -aPolygon[i].Y(), 0));
765 }
766 for (sal_uInt16 i = 0; i < 4; i++ )
767 {
768 aPolygonBoundVolume.append(basegfx::B3DPoint(aPolygon[i].X(), -aPolygon[i].Y(), fDepth));
769 }
770 aPolygonBoundVolume.transform(aNewTransform);
771
772 // projection
773 tools::Polygon a2DProjectionResult(8); // in fact 3D points with z=0
774 for (sal_uInt16 i = 0; i < 8; i++ )
775 {
776 const basegfx::B3DPoint aPoint3D(aPolygonBoundVolume.getB3DPoint(i));
777
778 if (eProjectionMode == drawing::ProjectionMode_PARALLEL)
779 {
780 a2DProjectionResult[i].setX(aPoint3D.getX());
781 a2DProjectionResult[i].setY(aPoint3D.getY());
782 }
783 else
784 {
785 // skip point if line from viewpoint to point is parallel to xy-plane
786 if (double fDiv = aPoint3D.getZ() - fViewPointZ; fDiv != 0.0)
787 {
788 double f = (- fViewPointZ) / fDiv;
789 double fX = (aPoint3D.getX() - fViewPointX) * f + fViewPointX;
790 double fY = (aPoint3D.getY() - fViewPointY) * f + fViewPointY;;
791 a2DProjectionResult[i].setX(static_cast<sal_Int32>(fX));
792 a2DProjectionResult[i].setY(static_cast<sal_Int32>(fY));
793 }
794 }
795 }
796 // Convert to y-axis down
797 for (sal_uInt16 i = 0; i < 8; i++ )
798 {
799 a2DProjectionResult[i].setY(- a2DProjectionResult[i].Y());
800 }
801 // Shift back to shape center
802 a2DProjectionResult.Translate(aCenter);
803
804 pScene->SetLogicRect(a2DProjectionResult.GetBoundRect());
805
806
807 // light and material
808
809 // "LightFace" has nothing corresponding in 3D rendering engine.
810 /* bool bLightFace = */ GetBool(rGeometryItem, "LightFace", true); // default in ODF
811
812 // Light directions
813
814 drawing::Direction3D aFirstLightDirectionDefault(50000.0, 0.0, 10000.0);
815 drawing::Direction3D aFirstLightDirection(GetDirection3D( rGeometryItem, "FirstLightDirection", aFirstLightDirectionDefault));
816 if (aFirstLightDirection.DirectionX == 0.0 && aFirstLightDirection.DirectionY == 0.0
817 && aFirstLightDirection.DirectionZ == 0.0)
818 aFirstLightDirection.DirectionZ = 1.0;
819 basegfx::B3DVector aLight1Vector(aFirstLightDirection.DirectionX, -aFirstLightDirection.DirectionY, aFirstLightDirection.DirectionZ);
820 aLight1Vector.normalize();
821
822 drawing::Direction3D aSecondLightDirectionDefault(-50000.0, 0.0, 10000.0);
823 drawing::Direction3D aSecondLightDirection(GetDirection3D( rGeometryItem, "SecondLightDirection", aSecondLightDirectionDefault));
824 if (aSecondLightDirection.DirectionX == 0.0 && aSecondLightDirection.DirectionY == 0.0
825 && aSecondLightDirection.DirectionZ == 0.0)
826 aSecondLightDirection.DirectionZ = 1.0;
827 basegfx::B3DVector aLight2Vector(aSecondLightDirection.DirectionX, -aSecondLightDirection.DirectionY, aSecondLightDirection.DirectionZ);
828 aLight2Vector.normalize();
829
830 // Light Intensity
831
832 // For "FirstLight" the 3D-Scene light "1" is regularly used. In case of surface "Matte"
833 // the light 4 is used instead. For "SecondLight" the 3D-Scene light "2" is regularly used.
834 // In case first or second light is not harsh, the lights 5 to 8 are used in addition
835 // to get a soft light appearance.
836 // The 3D-Scene light "3" is currently not used.
837
838 // ODF default 66%. MS Office default 38000/65536=0.579 is set in import filter.
839 double fLight1Intensity = GetDouble(rGeometryItem, "FirstLightLevel", 66) / 100.0;
840 // ODF and MS Office have both default 'true'.
841 bool bFirstLightHarsh = GetBool(rGeometryItem, "FirstLightHarsh", true);
842 // ODF default 66%. MS Office default 38000/65536=0.579 is set in import filter
843 double fLight2Intensity = GetDouble(rGeometryItem, "SecondLightLevel", 66) / 100.0;
844 // ODF has default 'true'. MS Office default 'false' is set in import.
845 bool bSecondLightHarsh = GetBool(rGeometryItem, "SecondLightHarsh", true);
846
847 // ODF default 33%. MS Office default 20000/65536=0.305 is set in import filter.
848 double fAmbientIntensity = GetDouble(rGeometryItem, "Brightness", 33) / 100.0;
849
850 double fLight1IntensityForSpecular(fLight1Intensity); // remember original value
851 if (!bFirstLightHarsh || !bSecondLightHarsh) // might need softing lights
852 {
853 bool bNeedSoftLights(false); // catch case of lights with zero intensity.
854 basegfx::B3DVector aLight5Vector;
855 basegfx::B3DVector aLight6Vector;
856 basegfx::B3DVector aLight7Vector;
857 basegfx::B3DVector aLight8Vector;
858 // The needed light intensities depend on the angle between regular light and
859 // additional lights, currently for 60deg.
860 Color aHoriSoftLightColor;
861 Color aVertSoftLightColor;
862
863 if (!bSecondLightHarsh && fLight2Intensity > 0.0
864 && (bFirstLightHarsh || fLight1Intensity == 0.0)) // only second light soft
865 {
866 // That is default for shapes generated in the UI, for LO and MS Office as well.
867 bNeedSoftLights = true;
868 double fLight2SoftIntensity = fLight2Intensity * 0.40;
869 aHoriSoftLightColor = Color(basegfx::BColor(fLight2SoftIntensity).clamp());
870 aVertSoftLightColor = aHoriSoftLightColor;
871 fLight2Intensity *= 0.2;
872
873 lcl_SoftLightsDirection(aLight2Vector, aLight5Vector, aLight6Vector,
874 aLight7Vector, aLight8Vector);
875 }
876 else if (!bFirstLightHarsh && fLight1Intensity > 0.0
877 && (bSecondLightHarsh || fLight2Intensity == 0.0)) // only first light soft
878 {
879 bNeedSoftLights = true;
880 double fLight1SoftIntensity = fLight1Intensity * 0.40;
881 aHoriSoftLightColor = Color(basegfx::BColor(fLight1SoftIntensity).clamp());
882 aVertSoftLightColor = aHoriSoftLightColor;
883 fLight1Intensity *= 0.2;
884
885 lcl_SoftLightsDirection(aLight1Vector, aLight5Vector, aLight6Vector,
886 aLight7Vector, aLight8Vector);
887 }
888 else if (!bFirstLightHarsh && fLight1Intensity > 0.0 && !bSecondLightHarsh
889 && fLight2Intensity > 0.0) // both lights soft
890 {
891 bNeedSoftLights = true;
892 // We do not hat enough lights. We use two soft lights for FirstLight and two for
893 // SecondLight and double intensity.
894 double fLight1SoftIntensity = fLight1Intensity * 0.8;
895 fLight1Intensity *= 0.4;
896 aHoriSoftLightColor = Color(basegfx::BColor(fLight1SoftIntensity).clamp());
897 basegfx::B3DVector aDummy1, aDummy2;
898 lcl_SoftLightsDirection(aLight1Vector, aDummy1, aDummy2, aLight7Vector,
899 aLight8Vector);
900
901 double fLight2SoftIntensity = fLight2Intensity * 0.8;
902 aVertSoftLightColor = Color(basegfx::BColor(fLight2SoftIntensity).clamp());
903 fLight2Intensity *= 0.4;
904 lcl_SoftLightsDirection(aLight2Vector, aLight5Vector, aLight6Vector, aDummy1,
905 aDummy2);
906 }
907
908 if (bNeedSoftLights)
909 {
910 pScene->GetProperties().SetObjectItem(
911 makeSvx3DLightDirection5Item(aLight5Vector));
912 pScene->GetProperties().SetObjectItem(
913 makeSvx3DLightcolor5Item(aVertSoftLightColor));
914 pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff5Item(true));
915 pScene->GetProperties().SetObjectItem(
916 makeSvx3DLightDirection6Item(aLight6Vector));
917 pScene->GetProperties().SetObjectItem(
918 makeSvx3DLightcolor6Item(aVertSoftLightColor));
919 pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff6Item(true));
920 pScene->GetProperties().SetObjectItem(
921 makeSvx3DLightDirection7Item(aLight7Vector));
922 pScene->GetProperties().SetObjectItem(
923 makeSvx3DLightcolor7Item(aHoriSoftLightColor));
924 pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff7Item(true));
925 pScene->GetProperties().SetObjectItem(
926 makeSvx3DLightDirection8Item(aLight8Vector));
927 pScene->GetProperties().SetObjectItem(
928 makeSvx3DLightcolor8Item(aHoriSoftLightColor));
929 pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff8Item(true));
930 }
931 }
932
933 // ToDo: MSO seems to add half of the surplus to ambient color. ODF restricts value to <1.
934 if (fLight1Intensity > 1.0)
935 {
936 fAmbientIntensity += (fLight1Intensity - 1.0) / 2.0;
937 }
938
939 // ToDo: How to handle fAmbientIntensity larger 1.0 ? Perhaps lighten object color?
940
941 // Now set the regularly 3D-scene light attributes.
942 Color aAmbientColor(basegfx::BColor(fAmbientIntensity).clamp());
943 pScene->GetProperties().SetObjectItem(makeSvx3DAmbientcolorItem(aAmbientColor));
944
945 pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection1Item(aLight1Vector));
946 pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff1Item(fLight1Intensity > 0.0));
947 Color aLight1Color(basegfx::BColor(fLight1Intensity).clamp());
948 pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor1Item(aLight1Color));
949
950 pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection2Item(aLight2Vector));
951 pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff2Item(fLight2Intensity > 0.0));
952 Color aLight2Color(basegfx::BColor(fLight2Intensity).clamp());
953 pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor2Item(aLight2Color));
954
955 // Object reactions on light
956 // Diffusion, Specular-Color and -Intensity are object properties, not scene properties.
957 // Surface flag "Metal" is an object property too.
958
959 // Property "Diffusion" would correspond to style attribute "drd3:diffuse-color".
960 // But that is not implemented. We cannot ignore the attribute because MS Office sets
961 // attribute c3DDiffuseAmt to 43712 (Type Fixed 16.16, approx 66,9%) instead of MSO
962 // default 65536 (100%), if the user sets surface 'Metal' in the UI of MS Office.
963 // We will change the material color of the 3D object as ersatz.
964 // ODF data type is percent with default 0%. MSO default is set in import filter.
965 double fDiffusion = GetDouble(rGeometryItem, "Diffusion", 0.0) / 100.0;
966
967 // ODF standard specifies for value true: "the specular color for the shading of an
968 // extruded shape is gray (red, green and blue values of 200) instead of white and 15% is
969 // added to the specularity."
970 // Neither 'specularity' nor 'specular color' is clearly defined in the standard. ODF term
971 // 'specularity' seems to correspond to UI field 'Specular Intensity' for 3D scenes.
972 // MS Office uses current material color in case 'Metal' is set. To detect, whether
973 // rendering similar to MS Office has to be used the property 'MetalType' is used. It is
974 // set on import and in the extrusion bar.
975 bool bMetal = GetBool(rGeometryItem, "Metal", false);
976 sal_Int16 eMetalType(
977 GetMetalType(rGeometryItem, drawing::EnhancedCustomShapeMetalType::MetalODF));
978 bool bMetalMSCompatible
979 = eMetalType == drawing::EnhancedCustomShapeMetalType::MetalMSCompatible;
980
981 // Property "Specularity" corresponds to 3D object style attribute dr3d:specular-color.
982 double fSpecularity = GetDouble(rGeometryItem, "Specularity", 0) / 100.0;
983
984 if (bMetal && !bMetalMSCompatible)
985 {
986 fSpecularity *= 200.0 / 255.0;
987 }
988
989 // MS Office seems to render as if 'Specular Color' = Specularity * Light1Intensity.
990 double fShadingFactor = fLight1IntensityForSpecular * fSpecularity;
991 Color aSpecularCol(basegfx::BColor(fShadingFactor).clamp());
992 // In case of bMetalMSCompatible the color will be recalculated in the below loop.
993
994 // Shininess ODF default 50 (unit %). MS Office default 5, import filter makes *10.
995 // Shininess corresponds to "Specular Intensity" with the nonlinear relationship
996 // "Specular Intensity" = 2^c3DShininess = 2^("Shininess" / 10)
997 double fShininess = GetDouble(rGeometryItem, "Shininess", 50) / 10.0;
998 fShininess = std::clamp<double>(pow(2, fShininess), 0.0, 100.0);
999 sal_uInt16 nIntensity = static_cast<sal_uInt16>(basegfx::fround(fShininess));
1000 if (bMetal && !bMetalMSCompatible)
1001 {
1002 nIntensity += 15; // as specified in ODF
1003 nIntensity = std::clamp<sal_uInt16>(nIntensity, 0, 100);
1004 }
1005
1006 SdrObjListIter aSceneIter(*pScene, SdrIterMode::DeepNoGroups);
1007 while (aSceneIter.IsMore())
1008 {
1009 const SdrObject* pNext = aSceneIter.Next();
1010
1011 // Change material color as ersatz for missing style attribute "drd3:diffuse-color".
1012 // For this ersatz we exclude case fDiffusion == 0.0, because for older documents this
1013 // attribute is not written out to draw:extrusion-diffusion and ODF default 0 would
1014 // produce black objects.
1015 const Color& rMatColor
1016 = pNext->GetProperties().GetItem(XATTR_FILLCOLOR).GetColorValue();
1017 Color aOldMatColor(rMatColor);
1018 if (basegfx::fTools::more(fDiffusion, 0.0)
1019 && !basegfx::fTools::equal(fDiffusion, 1.0))
1020 {
1021 // Occurs e.g. with MS surface preset 'Metal'.
1022 sal_uInt16 nHue;
1023 sal_uInt16 nSaturation;
1024 sal_uInt16 nBrightness;
1025 rMatColor.RGBtoHSB(nHue, nSaturation, nBrightness);
1026 nBrightness
1027 = static_cast<sal_uInt16>(static_cast<double>(nBrightness) * fDiffusion);
1028 nBrightness = std::clamp<sal_uInt16>(nBrightness, 0, 100);
1029 Color aNewMatColor = Color::HSBtoRGB(nHue, nSaturation, nBrightness);
1030 pNext->GetProperties().SetObjectItem(XFillColorItem("", aNewMatColor));
1031 }
1032
1033 // Using material color instead of gray in case of MS Office compatible rendering.
1034 if (bMetal && bMetalMSCompatible)
1035 {
1036 sal_uInt16 nHue;
1037 sal_uInt16 nSaturation;
1038 sal_uInt16 nBrightness;
1039 aOldMatColor.RGBtoHSB(nHue, nSaturation, nBrightness);
1040 nBrightness = static_cast<sal_uInt16>(static_cast<double>(nBrightness)
1041 * fShadingFactor);
1042 nBrightness = std::clamp<sal_uInt16>(nBrightness, 0, 100);
1043 aSpecularCol = Color::HSBtoRGB(nHue, nSaturation, nBrightness);
1044 }
1045
1049 }
1050
1051 // fSpecularity = 0 is used to indicate surface preset "Matte".
1052 if (basegfx::fTools::equalZero(fSpecularity))
1053 {
1054 // First light in LO 3D engine is always specular, all other lights are never specular.
1055 // We copy light1 values to light4 and use it instead of light1 in the 3D scene.
1056 pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff1Item(false));
1057 pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff4Item(true));
1058 pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor4Item(aLight1Color));
1059 pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection4Item(aLight1Vector));
1060 }
1061
1062 // removing placeholder objects
1063 for (E3dCompoundObject* pTemp : aPlaceholderObjectList)
1064 {
1065 pScene->RemoveObject( pTemp->GetOrdNum() );
1066 }
1067 }
1068 }
1069 return pRet;
1070}
1071
1072/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsEmpty() const
bool Crop(const tools::Rectangle &rRectPixel)
const Size & GetSizePixel() const
void SetAutoAdjustProjection(bool bAdjust)
Definition: camera3d.hxx:62
void SetFocalLength(double fLen)
Definition: camera3d.cxx:172
void SetPosAndLookAt(const basegfx::B3DPoint &rNewPos, const basegfx::B3DPoint &rNewLookAt)
Definition: camera3d.cxx:69
void SetViewWindow(double fX, double fY, double fW, double fH)
Definition: camera3d.cxx:41
void RGBtoHSB(sal_uInt16 &nHue, sal_uInt16 &nSaturation, sal_uInt16 &nBrightness) const
static Color HSBtoRGB(sal_uInt16 nHue, sal_uInt16 nSaturation, sal_uInt16 nBrightness)
static rtl::Reference< SdrObject > Create3DObject(const SdrObject *pShape2d, const SdrObjCustomShape &rSdrObjCustomShape)
sal_Int32 GetNumerator() const
sal_Int32 GetDenominator() const
const Graphic & GetGraphic() const
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
virtual const tools::Rectangle & GetSnapRect() const override
Definition: svdoattr.cxx:49
css::uno::Any * GetPropertyValueByName(const OUString &rPropName)
MapUnit GetScaleUnit() const
Definition: svdmodel.hxx:371
const Fraction & GetScaleFraction() const
Definition: svdmodel.hxx:373
double GetObjectRotation() const
Definition: svdoashp.hxx:141
bool IsMirroredY() const
Definition: svdoashp.cxx:477
bool IsMirroredX() const
Definition: svdoashp.cxx:468
size_t Count() const
Definition: svditer.hxx:69
SdrObject * Next()
Definition: svditer.hxx:63
bool IsMore() const
Definition: svditer.hxx:62
Abstract DrawObject.
Definition: svdobj.hxx:261
const SfxPoolItem & GetMergedItem(const sal_uInt16 nWhich) const
Definition: svdobj.cxx:2012
virtual sdr::properties::BaseProperties & GetProperties() const
Definition: svdobj.cxx:220
rtl::Reference< SdrObject > ConvertToPolyObj(bool bBezier, bool bLineToArea) const
Definition: svdobj.cxx:2623
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:289
rtl::Reference< SdrObject > ConvertToContourObj(SdrObject *pRet, bool bForceLineDash=false) const
Definition: svdobj.cxx:2584
const SfxItemSet & GetMergedItemSet() const
Definition: svdobj.cxx:1977
virtual SdrLayerID GetLayer() const
Definition: svdobj.cxx:645
const basegfx::B2DPolyPolygon & GetPathPoly() const
Definition: svdopath.hxx:141
virtual Degree100 GetRotateAngle() const override
Definition: svdotxtr.cxx:84
virtual const tools::Rectangle & GetLogicRect() const override
Definition: svdotxtr.cxx:69
void SetParent(const SfxItemSet *pNew)
const SfxItemSet * GetParent() const
sal_uInt16 ClearItem(sal_uInt16 nWhich=0)
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
const SfxPoolItem * Put(const SfxPoolItem &rItem, sal_uInt16 nWhich)
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
sal_uInt16 FirstWhich()
sal_uInt16 NextWhich()
constexpr tools::Long Height() const
constexpr tools::Long Width() const
void SetProjection(ProjectionType ePrj)
Definition: viewpt3d.hxx:80
const Color & GetColorValue() const
Definition: xattr.cxx:300
const GraphicObject & GetGraphicObject() const
Definition: xbtmpit.hxx:56
void rotate(double fRadiant)
void translate(double fX, double fY)
void scale(double fX, double fY)
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void transform(const basegfx::B2DHomMatrix &rMatrix)
B2DRange getB2DRange() const
bool areControlPointsUsed() const
sal_uInt32 count() const
void shearXY(double fSx, double fSy)
void rotate(double fAngleX, double fAngleY, double fAngleZ)
void translate(double fX, double fY, double fZ)
void scale(double fX, double fY, double fZ)
void append(const B3DPoint &rPoint, sal_uInt32 nCount=1)
B3DPoint const & getB3DPoint(sal_uInt32 nIndex) const
void transform(const B3DHomMatrix &rMatrix)
B3DVector & normalize()
TYPE getMaxX() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
TYPE getX() const
TYPE getZ() const
TYPE getY() const
const SfxPoolItem & GetItem(const sal_uInt16 nWhich) const
Definition: properties.cxx:103
virtual void SetObjectItem(const SfxPoolItem &rItem)=0
tools::Rectangle GetBoundRect() const
void Translate(const Point &rTrans)
constexpr Point Center() const
constexpr tools::Long GetWidth() const
constexpr void SetLeft(tools::Long v)
constexpr void SetTop(tools::Long v)
constexpr tools::Long Top() const
constexpr Point TopLeft() const
tools::Long getOpenHeight() const
constexpr void SetRight(tools::Long v)
constexpr tools::Long Right() const
constexpr void SetBottom(tools::Long v)
constexpr tools::Long GetHeight() const
tools::Rectangle & Union(const tools::Rectangle &rRect)
tools::Long getOpenWidth() const
constexpr tools::Long Left() const
constexpr tools::Long Bottom() const
static bool IsFuzzing()
#define DBG_ASSERT(sCon, aError)
double toRadians(D x)
uno_Any a
bool more(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
bool equal(T const &rfValA, T const &rfValB)
B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon &rCandidate, double fAngleBound)
B2DRange getRange(const B2DPolygon &rCandidate)
B2IRange fround(const B2DRange &rRange)
constexpr double deg2rad(double v)
attribute::SdrLineAttribute createNewSdrLineAttribute(const SfxItemSet &rSet)
attribute::SdrLineStartEndAttribute createNewSdrLineStartEndAttribute(const SfxItemSet &rSet, double fWidth)
int i
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
long Long
#define Y
static SfxItemSet & rSet
constexpr TypedWhichId< SdrCustomShapeGeometryItem > SDRATTR_CUSTOMSHAPE_GEOMETRY(SDRATTR_CUSTOMSHAPE_FIRST+2)
constexpr TypedWhichId< SvxWritingModeItem > SDRATTR_TEXTDIRECTION(SDRATTR_NOTPERSIST_FIRST+34)
void RotatePoint(Point &rPnt, const Point &rRef, double sn, double cs)
Definition: svdtrans.hxx:101
SvxB3DVectorItem makeSvx3DLightDirection6Item(const basegfx::B3DVector &rVec)
Definition: svx3ditems.hxx:255
SfxBoolItem makeSvx3DLightOnOff2Item(bool bVal)
Definition: svx3ditems.hxx:207
SvxColorItem makeSvx3DLightcolor1Item(const Color &rCol)
Definition: svx3ditems.hxx:167
SfxBoolItem makeSvx3DTwoSidedLightingItem(bool bVal)
Definition: svx3ditems.hxx:163
SfxUInt16Item makeSvx3DMaterialSpecularIntensityItem(sal_uInt16 nVal)
Definition: svx3ditems.hxx:116
SvxColorItem makeSvx3DLightcolor8Item(const Color &rCol)
Definition: svx3ditems.hxx:195
SfxBoolItem makeSvx3DLightOnOff5Item(bool bVal)
Definition: svx3ditems.hxx:219
SfxUInt16Item makeSvx3DPercentDiagonalItem(sal_uInt16 nVal)
Definition: svx3ditems.hxx:34
SvxColorItem makeSvx3DLightcolor2Item(const Color &rCol)
Definition: svx3ditems.hxx:171
SfxBoolItem makeSvx3DDoubleSidedItem(bool bVal)
Definition: svx3ditems.hxx:58
SvxColorItem makeSvx3DLightcolor6Item(const Color &rCol)
Definition: svx3ditems.hxx:187
SvxB3DVectorItem makeSvx3DLightDirection5Item(const basegfx::B3DVector &rVec)
Definition: svx3ditems.hxx:251
SvxB3DVectorItem makeSvx3DLightDirection8Item(const basegfx::B3DVector &rVec)
Definition: svx3ditems.hxx:263
SvxColorItem makeSvx3DLightcolor5Item(const Color &rCol)
Definition: svx3ditems.hxx:183
SvxColorItem makeSvx3DMaterialSpecularItem(const Color &rCol)
Definition: svx3ditems.hxx:112
SvxColorItem makeSvx3DAmbientcolorItem(const Color &rCol)
Definition: svx3ditems.hxx:199
SfxBoolItem makeSvx3DLightOnOff7Item(bool bVal)
Definition: svx3ditems.hxx:227
SvxB3DVectorItem makeSvx3DLightDirection4Item(const basegfx::B3DVector &rVec)
Definition: svx3ditems.hxx:247
SfxBoolItem makeSvx3DLightOnOff1Item(bool bVal)
Definition: svx3ditems.hxx:203
SvxColorItem makeSvx3DLightcolor4Item(const Color &rCol)
Definition: svx3ditems.hxx:179
SvxB3DVectorItem makeSvx3DLightDirection1Item(const basegfx::B3DVector &rVec)
Definition: svx3ditems.hxx:235
SfxBoolItem makeSvx3DLightOnOff4Item(bool bVal)
Definition: svx3ditems.hxx:215
SfxBoolItem makeSvx3DLightOnOff8Item(bool bVal)
Definition: svx3ditems.hxx:231
SvxB3DVectorItem makeSvx3DLightDirection2Item(const basegfx::B3DVector &rVec)
Definition: svx3ditems.hxx:239
SfxBoolItem makeSvx3DLightOnOff6Item(bool bVal)
Definition: svx3ditems.hxx:223
SvxB3DVectorItem makeSvx3DLightDirection7Item(const basegfx::B3DVector &rVec)
Definition: svx3ditems.hxx:259
SvxColorItem makeSvx3DLightcolor7Item(const Color &rCol)
Definition: svx3ditems.hxx:191
ProjectionType
Definition: viewpt3d.hxx:38
constexpr TypedWhichId< XFillColorItem > XATTR_FILLCOLOR(XATTR_FILL_FIRST+1)
constexpr TypedWhichId< XSecondaryFillColorItem > XATTR_SECONDARYFILLCOLOR(XATTR_FILL_FIRST+12)
constexpr TypedWhichId< XLineColorItem > XATTR_LINECOLOR(XATTR_LINE_FIRST+3)
constexpr TypedWhichId< XFillBmpTileItem > XATTR_FILLBMP_TILE(XATTR_FILL_FIRST+7)
constexpr TypedWhichId< XFillBitmapItem > XATTR_FILLBITMAP(XATTR_FILL_FIRST+4)
constexpr TypedWhichId< XLineStyleItem > XATTR_LINESTYLE(XATTR_LINE_FIRST)
constexpr TypedWhichId< XFillStyleItem > XATTR_FILLSTYLE(XATTR_FILL_FIRST)
constexpr TypedWhichId< XLineTransparenceItem > XATTR_LINETRANSPARENCE(XATTR_LINE_FIRST+10)