1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: plotareaconverter.cxx,v $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 #include "oox/drawingml/chart/plotareaconverter.hxx"
33 #include <com/sun/star/drawing/Direction3D.hpp>
34 #include <com/sun/star/drawing/ProjectionMode.hpp>
35 #include <com/sun/star/drawing/ShadeMode.hpp>
36 #include <com/sun/star/chart2/XChartDocument.hpp>
37 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
38 #include <com/sun/star/chart2/XDiagram.hpp>
39 #include "oox/drawingml/chart/axisconverter.hxx"
40 #include "oox/drawingml/chart/plotareamodel.hxx"
41 #include "oox/drawingml/chart/typegroupconverter.hxx"
42 #include "properties.hxx"
44 using ::rtl::OUString
;
45 using ::com::sun::star::uno::Reference
;
46 using ::com::sun::star::uno::Sequence
;
47 using ::com::sun::star::uno::Exception
;
48 using ::com::sun::star::uno::UNO_QUERY_THROW
;
49 using ::com::sun::star::chart2::XCoordinateSystem
;
50 using ::com::sun::star::chart2::XCoordinateSystemContainer
;
51 using ::com::sun::star::chart2::XDiagram
;
57 // ============================================================================
61 /** Axes set model. This is a helper class for the plot area converter. */
64 typedef ModelVector
< TypeGroupModel
> TypeGroupVector
;
65 typedef ModelMap
< sal_Int32
, AxisModel
> AxisMap
;
67 TypeGroupVector maTypeGroups
;
70 inline explicit AxesSetModel() {}
71 inline ~AxesSetModel() {}
74 // ============================================================================
76 /** Axes set converter. This is a helper class for the plot area converter. */
77 class AxesSetConverter
: public ConverterBase
< AxesSetModel
>
80 explicit AxesSetConverter( const ConverterRoot
& rParent
, AxesSetModel
& rModel
);
81 virtual ~AxesSetConverter();
83 /** Converts the axes set model to a chart2 diagram. Returns an automatic
84 chart title from a single series title, if possible. */
85 void convertFromModel(
86 const Reference
< XDiagram
>& rxDiagram
,
87 View3DModel
& rView3DModel
,
88 sal_Int32 nAxesSetIdx
,
89 bool bSupportsVaryColorsByPoint
);
91 /** Returns the automatic chart title if the axes set contains only one series. */
92 inline const ::rtl::OUString
& getAutomaticTitle() const { return maAutoTitle
; }
93 /** Returns true, if the chart is three-dimensional. */
94 inline bool is3dChart() const { return mb3dChart
; }
95 /** Returns true, if chart type supports wall and floor format in 3D mode. */
96 inline bool isWall3dChart() const { return mbWall3dChart
; }
99 ::rtl::OUString maAutoTitle
;
104 // ----------------------------------------------------------------------------
106 AxesSetConverter::AxesSetConverter( const ConverterRoot
& rParent
, AxesSetModel
& rModel
) :
107 ConverterBase
< AxesSetModel
>( rParent
, rModel
),
109 mbWall3dChart( false )
113 AxesSetConverter::~AxesSetConverter()
117 ModelRef
< AxisModel
> lclGetOrCreateAxis( const AxesSetModel::AxisMap
& rFromAxes
, sal_Int32 nAxisIdx
, sal_Int32 nDefTypeId
)
119 ModelRef
< AxisModel
> xAxis
= rFromAxes
.get( nAxisIdx
);
121 xAxis
.create( nDefTypeId
).mbDeleted
= true; // missing axis is invisible
125 void AxesSetConverter::convertFromModel( const Reference
< XDiagram
>& rxDiagram
,
126 View3DModel
& rView3DModel
, sal_Int32 nAxesSetIdx
, bool bSupportsVaryColorsByPoint
)
128 // create type group converter objects for all type groups
129 typedef RefVector
< TypeGroupConverter
> TypeGroupConvVector
;
130 TypeGroupConvVector aTypeGroups
;
131 for( AxesSetModel::TypeGroupVector::iterator aIt
= mrModel
.maTypeGroups
.begin(), aEnd
= mrModel
.maTypeGroups
.end(); aIt
!= aEnd
; ++aIt
)
132 aTypeGroups
.push_back( TypeGroupConvVector::value_type( new TypeGroupConverter( *this, **aIt
) ) );
134 OSL_ENSURE( !aTypeGroups
.empty(), "AxesSetConverter::convertFromModel - no type groups in axes set" );
135 if( !aTypeGroups
.empty() ) try
137 // first type group needed for coordinate system and axis conversion
138 TypeGroupConverter
& rFirstTypeGroup
= *aTypeGroups
.front();
140 // get automatic chart title, if there is only one type group
141 if( aTypeGroups
.size() == 1 )
142 maAutoTitle
= rFirstTypeGroup
.getSingleSeriesTitle();
144 /* Create a coordinate system. For now, all type groups from all axes sets
145 have to be inserted into one coordinate system. Later, chart2 should
146 support using one coordinate system for each axes set. */
147 Reference
< XCoordinateSystem
> xCoordSystem
;
148 Reference
< XCoordinateSystemContainer
> xCoordSystemCont( rxDiagram
, UNO_QUERY_THROW
);
149 Sequence
< Reference
< XCoordinateSystem
> > aCoordSystems
= xCoordSystemCont
->getCoordinateSystems();
150 if( aCoordSystems
.hasElements() )
152 OSL_ENSURE( aCoordSystems
.getLength() == 1, "AxesSetConverter::convertFromModel - too many coordinate systems" );
153 xCoordSystem
= aCoordSystems
[ 0 ];
154 OSL_ENSURE( xCoordSystem
.is(), "AxesSetConverter::convertFromModel - invalid coordinate system" );
158 xCoordSystem
= rFirstTypeGroup
.createCoordinateSystem();
159 if( xCoordSystem
.is() )
160 xCoordSystemCont
->addCoordinateSystem( xCoordSystem
);
164 mb3dChart
= rFirstTypeGroup
.is3dChart();
165 mbWall3dChart
= rFirstTypeGroup
.isWall3dChart();
168 View3DConverter
aView3DConv( *this, rView3DModel
);
169 aView3DConv
.convertFromModel( rxDiagram
, rFirstTypeGroup
);
172 /* Convert all chart type groups. Each type group will add its series
173 to the data provider attached to the chart document. */
174 if( xCoordSystem
.is() )
176 // convert all axes (create missing axis models)
177 ModelRef
< AxisModel
> xXAxis
= lclGetOrCreateAxis( mrModel
.maAxes
, API_X_AXIS
, rFirstTypeGroup
.getTypeInfo().mbCategoryAxis
? C_TOKEN( catAx
) : C_TOKEN( valAx
) );
178 ModelRef
< AxisModel
> xYAxis
= lclGetOrCreateAxis( mrModel
.maAxes
, API_Y_AXIS
, C_TOKEN( valAx
) );
180 AxisConverter
aXAxisConv( *this, *xXAxis
);
181 aXAxisConv
.convertFromModel( xCoordSystem
, rFirstTypeGroup
, xYAxis
.get(), nAxesSetIdx
, API_X_AXIS
);
182 AxisConverter
aYAxisConv( *this, *xYAxis
);
183 aYAxisConv
.convertFromModel( xCoordSystem
, rFirstTypeGroup
, xXAxis
.get(), nAxesSetIdx
, API_Y_AXIS
);
185 if( rFirstTypeGroup
.isDeep3dChart() )
187 ModelRef
< AxisModel
> xZAxis
= lclGetOrCreateAxis( mrModel
.maAxes
, API_Z_AXIS
, C_TOKEN( serAx
) );
188 AxisConverter
aZAxisConv( *this, *xZAxis
);
189 aZAxisConv
.convertFromModel( xCoordSystem
, rFirstTypeGroup
, 0, nAxesSetIdx
, API_Z_AXIS
);
192 // convert all chart type groups, this converts all series data and formatting
193 for( TypeGroupConvVector::iterator aTIt
= aTypeGroups
.begin(), aTEnd
= aTypeGroups
.end(); aTIt
!= aTEnd
; ++aTIt
)
194 (*aTIt
)->convertFromModel( rxDiagram
, xCoordSystem
, nAxesSetIdx
, bSupportsVaryColorsByPoint
);
204 // ============================================================================
206 View3DConverter::View3DConverter( const ConverterRoot
& rParent
, View3DModel
& rModel
) :
207 ConverterBase
< View3DModel
>( rParent
, rModel
)
211 View3DConverter::~View3DConverter()
215 void View3DConverter::convertFromModel( const Reference
< XDiagram
>& rxDiagram
, TypeGroupConverter
& rTypeGroup
)
217 namespace cssd
= ::com::sun::star::drawing
;
218 PropertySet
aPropSet( rxDiagram
);
220 sal_Int32 nRotationY
= 0;
221 sal_Int32 nRotationX
= 0;
222 bool bRightAngled
= false;
223 sal_Int32 nAmbientColor
= 0;
224 sal_Int32 nLightColor
= 0;
226 if( rTypeGroup
.getTypeInfo().meTypeCategory
== TYPECATEGORY_PIE
)
228 // Y rotation used as 'first pie slice angle' in 3D pie charts
229 rTypeGroup
.convertPieRotation( aPropSet
, mrModel
.monRotationY
.get( 0 ) );
230 // X rotation a.k.a. elevation (map OOXML [0..90] to Chart2 [-90,0])
231 nRotationX
= getLimitedValue
< sal_Int32
, sal_Int32
>( mrModel
.monRotationX
.get( 15 ), 0, 90 ) - 90;
232 // no right-angled axes in pie charts
233 bRightAngled
= false;
234 // ambient color (Gray 30%)
235 nAmbientColor
= 0xB3B3B3;
236 // light color (Gray 70%)
237 nLightColor
= 0x4C4C4C;
239 else // 3D bar/area/line charts
241 // Y rotation (OOXML [0..359], Chart2 [-179,180])
242 nRotationY
= mrModel
.monRotationY
.get( 20 );
243 // X rotation a.k.a. elevation (OOXML [-90..90], Chart2 [-179,180])
244 nRotationX
= getLimitedValue
< sal_Int32
, sal_Int32
>( mrModel
.monRotationX
.get( 15 ), -90, 90 );
246 bRightAngled
= mrModel
.mbRightAngled
;
247 // ambient color (Gray 20%)
248 nAmbientColor
= 0xCCCCCC;
249 // light color (Gray 60%)
250 nLightColor
= 0x666666;
253 // Y rotation (map OOXML [0..359] to Chart2 [-179,180])
255 if( nRotationY
> 180 ) nRotationY
-= 360;
256 /* Perspective (map OOXML [0..200] to Chart2 [0,100]). Seems that MSO 2007 is
257 buggy here, the XML plugin of MSO 2003 writes the correct perspective in
258 the range from 0 to 100. We will emulate the wrong behaviour of MSO 2007. */
259 sal_Int32 nPerspective
= getLimitedValue
< sal_Int32
, sal_Int32
>( mrModel
.mnPerspective
/ 2, 0, 100 );
260 // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
261 bool bParallel
= bRightAngled
|| (nPerspective
== 0);
262 cssd::ProjectionMode eProjMode
= bParallel
? cssd::ProjectionMode_PARALLEL
: cssd::ProjectionMode_PERSPECTIVE
;
264 // set rotation properties
265 aPropSet
.setProperty( PROP_RotationVertical
, nRotationY
);
266 aPropSet
.setProperty( PROP_RotationHorizontal
, nRotationX
);
267 aPropSet
.setProperty( PROP_Perspective
, nPerspective
);
268 aPropSet
.setProperty( PROP_RightAngledAxes
, bRightAngled
);
269 aPropSet
.setProperty( PROP_D3DScenePerspective
, eProjMode
);
271 // set light settings
272 aPropSet
.setProperty( PROP_D3DSceneShadeMode
, cssd::ShadeMode_FLAT
);
273 aPropSet
.setProperty( PROP_D3DSceneAmbientColor
, nAmbientColor
);
274 aPropSet
.setProperty( PROP_D3DSceneLightOn1
, false );
275 aPropSet
.setProperty( PROP_D3DSceneLightOn2
, true );
276 aPropSet
.setProperty( PROP_D3DSceneLightColor2
, nLightColor
);
277 aPropSet
.setProperty( PROP_D3DSceneLightDirection2
, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
280 // ============================================================================
282 WallFloorConverter::WallFloorConverter( const ConverterRoot
& rParent
, WallFloorModel
& rModel
) :
283 ConverterBase
< WallFloorModel
>( rParent
, rModel
)
287 WallFloorConverter::~WallFloorConverter()
291 void WallFloorConverter::convertFromModel( const Reference
< XDiagram
>& rxDiagram
, ObjectType eObjType
)
295 PropertySet aPropSet
;
298 case OBJECTTYPE_FLOOR
: aPropSet
.set( rxDiagram
->getFloor() ); break;
299 case OBJECTTYPE_WALL
: aPropSet
.set( rxDiagram
->getWall() ); break;
300 default: OSL_ENSURE( false, "WallFloorConverter::convertFromModel - invalid object type" );
303 getFormatter().convertFrameFormatting( aPropSet
, mrModel
.mxShapeProp
, mrModel
.mxPicOptions
.getOrCreate(), eObjType
);
307 // ============================================================================
309 PlotAreaConverter::PlotAreaConverter( const ConverterRoot
& rParent
, PlotAreaModel
& rModel
) :
310 ConverterBase
< PlotAreaModel
>( rParent
, rModel
),
312 mbWall3dChart( false )
316 PlotAreaConverter::~PlotAreaConverter()
320 void PlotAreaConverter::convertFromModel( View3DModel
& rView3DModel
)
322 /* Create the diagram object and attach it to the chart document. One
323 diagram is used to carry all coordinate systems and data series. */
324 Reference
< XDiagram
> xDiagram
;
327 xDiagram
.set( createInstance( CREATE_OUSTRING( "com.sun.star.chart2.Diagram" ) ), UNO_QUERY_THROW
);
328 getChartDocument()->setFirstDiagram( xDiagram
);
334 // store all axis models in a map, keyed by axis identifier
335 typedef ModelMap
< sal_Int32
, AxisModel
> AxisMap
;
337 for( PlotAreaModel::AxisVector::iterator aAIt
= mrModel
.maAxes
.begin(), aAEnd
= mrModel
.maAxes
.end(); aAIt
!= aAEnd
; ++aAIt
)
339 PlotAreaModel::AxisVector::value_type xAxis
= *aAIt
;
340 OSL_ENSURE( xAxis
->mnAxisId
>= 0, "PlotAreaConverter::convertFromModel - invalid axis identifier" );
341 OSL_ENSURE( !aAxisMap
.has( xAxis
->mnAxisId
), "PlotAreaConverter::convertFromModel - axis identifiers not unique" );
342 if( xAxis
->mnAxisId
>= 0 )
343 aAxisMap
[ xAxis
->mnAxisId
] = xAxis
;
346 // group the type group models into different axes sets
347 typedef ModelVector
< AxesSetModel
> AxesSetVector
;
348 AxesSetVector aAxesSets
;
349 sal_Int32 nMaxSeriesIdx
= -1;
350 for( PlotAreaModel::TypeGroupVector::iterator aTIt
= mrModel
.maTypeGroups
.begin(), aTEnd
= mrModel
.maTypeGroups
.end(); aTIt
!= aTEnd
; ++aTIt
)
352 PlotAreaModel::TypeGroupVector::value_type xTypeGroup
= *aTIt
;
353 if( !xTypeGroup
->maSeries
.empty() )
355 // try to find a compatible axes set for the type group
356 AxesSetModel
* pAxesSet
= 0;
357 for( AxesSetVector::iterator aASIt
= aAxesSets
.begin(), aASEnd
= aAxesSets
.end(); !pAxesSet
&& (aASIt
!= aASEnd
); ++aASIt
)
358 if( (*aASIt
)->maTypeGroups
.front()->maAxisIds
== xTypeGroup
->maAxisIds
)
359 pAxesSet
= aASIt
->get();
361 // not possible to insert into an existing axes set -> start a new axes set
364 pAxesSet
= &aAxesSets
.create();
365 // find axis models used by the type group
366 const TypeGroupModel::AxisIdVector
& rAxisIds
= xTypeGroup
->maAxisIds
;
367 if( rAxisIds
.size() >= 1 )
368 pAxesSet
->maAxes
[ API_X_AXIS
] = aAxisMap
.get( rAxisIds
[ 0 ] );
369 if( rAxisIds
.size() >= 2 )
370 pAxesSet
->maAxes
[ API_Y_AXIS
] = aAxisMap
.get( rAxisIds
[ 1 ] );
371 if( rAxisIds
.size() >= 3 )
372 pAxesSet
->maAxes
[ API_Z_AXIS
] = aAxisMap
.get( rAxisIds
[ 2 ] );
375 // insert the type group model
376 pAxesSet
->maTypeGroups
.push_back( xTypeGroup
);
378 // collect the maximum series index for automatic series formatting
379 for( TypeGroupModel::SeriesVector::iterator aSIt
= xTypeGroup
->maSeries
.begin(), aSEnd
= xTypeGroup
->maSeries
.end(); aSIt
!= aSEnd
; ++aSIt
)
380 nMaxSeriesIdx
= ::std::max( nMaxSeriesIdx
, (*aSIt
)->mnIndex
);
383 getFormatter().setMaxSeriesIndex( nMaxSeriesIdx
);
385 // varying point colors only for single series in single chart type
386 bool bSupportsVaryColorsByPoint
= mrModel
.maTypeGroups
.size() == 1;
388 // convert all axes sets
389 for( AxesSetVector::iterator aASBeg
= aAxesSets
.begin(), aASIt
= aASBeg
, aASEnd
= aAxesSets
.end(); aASIt
!= aASEnd
; ++aASIt
)
391 AxesSetConverter
aAxesSetConv( *this, **aASIt
);
392 sal_Int32 nAxesSetIdx
= static_cast< sal_Int32
>( aASIt
- aASBeg
);
393 aAxesSetConv
.convertFromModel( xDiagram
, rView3DModel
, nAxesSetIdx
, bSupportsVaryColorsByPoint
);
394 if( nAxesSetIdx
== 0 )
396 maAutoTitle
= aAxesSetConv
.getAutomaticTitle();
397 mb3dChart
= aAxesSetConv
.is3dChart();
398 mbWall3dChart
= aAxesSetConv
.isWall3dChart();
402 maAutoTitle
= OUString();
406 // plot area formatting
407 if( xDiagram
.is() && !mb3dChart
)
409 PropertySet
aPropSet( xDiagram
->getWall() );
410 getFormatter().convertFrameFormatting( aPropSet
, mrModel
.mxShapeProp
, OBJECTTYPE_PLOTAREA2D
);
414 // ============================================================================
417 } // namespace drawingml