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