LibreOffice Module chart2 (master) 1
BubbleChart.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include "BubbleChart.hxx"
22#include <ShapeFactory.hxx>
23#include <ObjectIdentifier.hxx>
25#include <ChartType.hxx>
26
27#include <com/sun/star/chart/DataLabelPlacement.hpp>
28#include <sal/log.hxx>
29#include <osl/diagnose.h>
30
31#include <limits>
32
33namespace chart
34{
35using namespace ::com::sun::star;
36using namespace ::com::sun::star::chart2;
37
39 , sal_Int32 nDimensionCount )
40 : VSeriesPlotter( xChartTypeModel, nDimensionCount, false )
41 , m_fMaxLogicBubbleSize( 0.0 )
42 , m_fBubbleSizeFactorToScreen( 1.0 )
43{
44 // We only support 2 dimensional bubble charts
45 assert(nDimensionCount == 2);
46
47 if( !m_pMainPosHelper )
48 m_pMainPosHelper = new PlottingPositionHelper();
49 PlotterBase::m_pPosHelper = m_pMainPosHelper;
50}
51
52BubbleChart::~BubbleChart()
53{
54 delete m_pMainPosHelper;
55}
56
57void BubbleChart::calculateMaximumLogicBubbleSize()
58{
59 double fMaxSize = 0.0;
60
61 sal_Int32 nEndIndex = VSeriesPlotter::getPointCount();
62 for( sal_Int32 nIndex = 0; nIndex < nEndIndex; nIndex++ )
63 {
64 for( auto const& rZSlot : m_aZSlots )
65 {
66 for( auto const& rXSlot : rZSlot )
67 {
68 for( std::unique_ptr<VDataSeries> const & pSeries : rXSlot.m_aSeriesVector )
69 {
70 if(!pSeries)
71 continue;
72
73 double fSize = pSeries->getBubble_Size( nIndex );
74 if( fSize > fMaxSize )
75 fMaxSize = fSize;
76 }
77 }
78 }
79 }
80
81 m_fMaxLogicBubbleSize = fMaxSize;
82}
83
84void BubbleChart::calculateBubbleSizeScalingFactor()
85{
86 double fLogicZ=1.0;
87 drawing::Position3D aSceneMinPos( m_pMainPosHelper->transformLogicToScene( m_pMainPosHelper->getLogicMinX(),m_pMainPosHelper->getLogicMinY(),fLogicZ, false ) );
88 drawing::Position3D aSceneMaxPos( m_pMainPosHelper->transformLogicToScene( m_pMainPosHelper->getLogicMaxX(),m_pMainPosHelper->getLogicMaxY(),fLogicZ, false ) );
89
90 awt::Point aScreenMinPos( LabelPositionHelper(m_nDimension,m_xLogicTarget).transformSceneToScreenPosition( aSceneMinPos ) );
91 awt::Point aScreenMaxPos( LabelPositionHelper(m_nDimension,m_xLogicTarget).transformSceneToScreenPosition( aSceneMaxPos ) );
92
93 sal_Int32 nWidth = abs( aScreenMaxPos.X - aScreenMinPos.X );
94 sal_Int32 nHeight = abs( aScreenMaxPos.Y - aScreenMinPos.Y );
95
96 sal_Int32 nMinExtend = std::min( nWidth, nHeight );
97 m_fBubbleSizeFactorToScreen = nMinExtend * 0.25;//max bubble size is 25 percent of diagram size
98}
99
100drawing::Direction3D BubbleChart::transformToScreenBubbleSize( double fLogicSize )
101{
102 drawing::Direction3D aRet(0,0,0);
103
104 if( std::isnan(fLogicSize) || std::isinf(fLogicSize) )
105 return aRet;
106
107 double fMaxSize = m_fMaxLogicBubbleSize;
108
109 double fMaxRadius = sqrt( fMaxSize / M_PI );
110 double fRadius = sqrt( fLogicSize / M_PI );
111
112 aRet.DirectionX = m_fBubbleSizeFactorToScreen * fRadius / fMaxRadius;
113 aRet.DirectionY = aRet.DirectionX;
114
115 return aRet;
116}
117
118bool BubbleChart::isExpandIfValuesCloseToBorder( sal_Int32 /*nDimensionIndex*/ )
119{
120 return true;
121}
122
123bool BubbleChart::isSeparateStackingForDifferentSigns( sal_Int32 /*nDimensionIndex*/ )
124{
125 return false;
126}
127
128LegendSymbolStyle BubbleChart::getLegendSymbolStyle()
129{
130 return LegendSymbolStyle::Circle;
131}
132
133drawing::Direction3D BubbleChart::getPreferredDiagramAspectRatio() const
134{
135 return drawing::Direction3D(-1,-1,-1);
136}
137
138namespace {
139
140//better performance for big data
141struct FormerPoint
142{
143 FormerPoint( double fX, double fY, double fZ )
144 : m_fX(fX), m_fY(fY), m_fZ(fZ)
145 {}
146 FormerPoint()
147 : m_fX(std::numeric_limits<double>::quiet_NaN())
148 , m_fY(std::numeric_limits<double>::quiet_NaN())
149 , m_fZ(std::numeric_limits<double>::quiet_NaN())
150 {
151 }
152
153 double m_fX;
154 double m_fY;
155 double m_fZ;
156};
157
158}
159
160void BubbleChart::createShapes()
161{
162 if( m_aZSlots.empty() ) //no series
163 return;
164
165 OSL_ENSURE(m_xLogicTarget.is()&&m_xFinalTarget.is(),"BubbleChart is not proper initialized");
166 if(!(m_xLogicTarget.is()&&m_xFinalTarget.is()))
167 return;
168
169 //therefore create an own group for the texts and the error bars to move them to front
170 //(because the text group is created after the series group the texts are displayed on top)
171 rtl::Reference<SvxShapeGroupAnyD> xSeriesTarget = createGroupShape( m_xLogicTarget );
172 rtl::Reference< SvxShapeGroup > xTextTarget = ShapeFactory::createGroup2D( m_xFinalTarget );
173
174 //update/create information for current group
175 double fLogicZ = 1.0;//as defined
176
177 sal_Int32 const nStartIndex = 0; // inclusive ;..todo get somehow from x scale
178 sal_Int32 nEndIndex = VSeriesPlotter::getPointCount();
179 if(nEndIndex<=0)
180 nEndIndex=1;
181
182 //better performance for big data
183 std::map< VDataSeries*, FormerPoint > aSeriesFormerPointMap;
184 m_bPointsWereSkipped = false;
185 sal_Int32 nSkippedPoints = 0;
186 sal_Int32 nCreatedPoints = 0;
187
188 calculateMaximumLogicBubbleSize();
189 calculateBubbleSizeScalingFactor();
190 if( m_fMaxLogicBubbleSize <= 0 || m_fBubbleSizeFactorToScreen <= 0 )
191 return;
192
193 //iterate through all x values per indices
194 for( sal_Int32 nIndex = nStartIndex; nIndex < nEndIndex; nIndex++ )
195 {
196 for( auto const& rZSlot : m_aZSlots )
197 {
198 for( auto const& rXSlot : rZSlot )
199 {
200 //iterate through all series
201 for( std::unique_ptr<VDataSeries> const & pSeries : rXSlot.m_aSeriesVector )
202 {
203 if(!pSeries)
204 continue;
205
206 bool bHasFillColorMapping = pSeries->hasPropertyMapping("FillColor");
207 bool bHasBorderColorMapping = pSeries->hasPropertyMapping("LineColor");
208
209 rtl::Reference<SvxShapeGroupAnyD> xSeriesGroupShape_Shapes = getSeriesGroupShape(pSeries.get(), xSeriesTarget);
210
211 sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex();
212 PlottingPositionHelper& rPosHelper
213 = getPlottingPositionHelper(nAttachedAxisIndex);
214 m_pPosHelper = &rPosHelper;
215
216 //collect data point information (logic coordinates, style ):
217 double fLogicX = pSeries->getXValue(nIndex);
218 double fLogicY = pSeries->getYValue(nIndex);
219 double fBubbleSize = pSeries->getBubble_Size( nIndex );
220
221 if( fBubbleSize<0.0 )
222 continue;
223
224 if( fBubbleSize == 0.0 || std::isnan(fBubbleSize) )
225 continue;
226
227 if( std::isnan(fLogicX) || std::isinf(fLogicX)
228 || std::isnan(fLogicY) || std::isinf(fLogicY) )
229 continue;
230
231 bool bIsVisible = rPosHelper.isLogicVisible(fLogicX, fLogicY, fLogicZ);
232
233 drawing::Position3D aUnscaledLogicPosition( fLogicX, fLogicY, fLogicZ );
234 drawing::Position3D aScaledLogicPosition(aUnscaledLogicPosition);
235 rPosHelper.doLogicScaling(aScaledLogicPosition);
236
237 //transformation 3) -> 4)
238 drawing::Position3D aScenePosition(
239 rPosHelper.transformLogicToScene(fLogicX, fLogicY, fLogicZ, false));
240
241 //better performance for big data
242 FormerPoint aFormerPoint( aSeriesFormerPointMap[pSeries.get()] );
243 rPosHelper.setCoordinateSystemResolution(m_aCoordinateSystemResolution);
244 if (!pSeries->isAttributedDataPoint(nIndex)
245 && rPosHelper.isSameForGivenResolution(
246 aFormerPoint.m_fX, aFormerPoint.m_fY, aFormerPoint.m_fZ,
247 aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY,
248 aScaledLogicPosition.PositionZ))
249 {
250 nSkippedPoints++;
251 m_bPointsWereSkipped = true;
252 continue;
253 }
254 aSeriesFormerPointMap[pSeries.get()] = FormerPoint(aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY, aScaledLogicPosition.PositionZ);
255
256 //create a single datapoint if point is visible
257 if( !bIsVisible )
258 continue;
259
260 //create a group shape for this point and add to the series shape:
261 OUString aPointCID = ObjectIdentifier::createPointCID(
262 pSeries->getPointCID_Stub(), nIndex );
263 rtl::Reference<SvxShapeGroupAnyD> xPointGroupShape_Shapes(
264 createGroupShape(xSeriesGroupShape_Shapes,aPointCID) );
265
266 {
267 nCreatedPoints++;
268
269 //create data point
270 drawing::Direction3D aSymbolSize = transformToScreenBubbleSize( fBubbleSize );
271 rtl::Reference<SvxShapeCircle> xShape = ShapeFactory::createCircle2D( xPointGroupShape_Shapes
272 , aScenePosition, aSymbolSize );
273
274 PropertyMapper::setMappedProperties( *xShape
275 , pSeries->getPropertiesOfPoint( nIndex )
276 , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() );
277
278 if(bHasFillColorMapping)
279 {
280 double nPropVal = pSeries->getValueByProperty(nIndex, "FillColor");
281 if(!std::isnan(nPropVal))
282 {
283 xShape->SvxShape::setPropertyValue("FillColor", uno::Any(static_cast<sal_Int32>(nPropVal)));
284 }
285 }
286 if(bHasBorderColorMapping)
287 {
288 double nPropVal = pSeries->getValueByProperty(nIndex, "LineColor");
289 if(!std::isnan(nPropVal))
290 {
291 xShape->SvxShape::setPropertyValue("LineColor", uno::Any(static_cast<sal_Int32>(nPropVal)));
292 }
293 }
294
295 ::chart::ShapeFactory::setShapeName( xShape, "MarkHandles" );
296
297 //create data point label
298 if( pSeries->getDataPointLabelIfLabel(nIndex) )
299 {
300 LabelAlignment eAlignment = LABEL_ALIGN_TOP;
301 drawing::Position3D aScenePosition3D( aScenePosition.PositionX
302 , aScenePosition.PositionY
303 , aScenePosition.PositionZ+getTransformedDepth() );
304
305 sal_Int32 nLabelPlacement = pSeries->getLabelPlacement(
306 nIndex, m_xChartTypeModel, rPosHelper.isSwapXAndY());
307
308 switch(nLabelPlacement)
309 {
310 case css::chart::DataLabelPlacement::TOP:
311 aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1);
312 eAlignment = LABEL_ALIGN_TOP;
313 break;
314 case css::chart::DataLabelPlacement::BOTTOM:
315 aScenePosition3D.PositionY += (aSymbolSize.DirectionY/2+1);
316 eAlignment = LABEL_ALIGN_BOTTOM;
317 break;
318 case css::chart::DataLabelPlacement::LEFT:
319 aScenePosition3D.PositionX -= (aSymbolSize.DirectionX/2+1);
320 eAlignment = LABEL_ALIGN_LEFT;
321 break;
322 case css::chart::DataLabelPlacement::RIGHT:
323 aScenePosition3D.PositionX += (aSymbolSize.DirectionX/2+1);
324 eAlignment = LABEL_ALIGN_RIGHT;
325 break;
326 case css::chart::DataLabelPlacement::CENTER:
327 eAlignment = LABEL_ALIGN_CENTER;
328 break;
329 default:
330 OSL_FAIL("this label alignment is not implemented yet");
331 aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1);
332 eAlignment = LABEL_ALIGN_TOP;
333 break;
334 }
335
336 awt::Point aScreenPosition2D( LabelPositionHelper(m_nDimension,m_xLogicTarget)
337 .transformSceneToScreenPosition( aScenePosition3D ) );
338 sal_Int32 nOffset = 0;
339 if(eAlignment!=LABEL_ALIGN_CENTER)
340 nOffset = 100;//add some spacing //@todo maybe get more intelligent values
341 createDataLabel( xTextTarget, *pSeries, nIndex
342 , fBubbleSize, fBubbleSize, aScreenPosition2D, eAlignment, nOffset );
343 }
344 }
345
346 //remove PointGroupShape if empty
347 if(!xPointGroupShape_Shapes->getCount())
348 xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shapes);
349
350 }//next series in x slot (next y slot)
351 }//next x slot
352 }//next z slot
353 }//next category
354 SAL_INFO(
355 "chart2",
356 "skipped points: " << nSkippedPoints << " created points: "
357 << nCreatedPoints);
358}
359
360} //namespace chart
361
362/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double m_fY
double m_fX
double m_fZ
bool isSameForGivenResolution(double fX, double fY, double fZ, double fX2, double fY2, double fZ2)
void setCoordinateSystemResolution(const css::uno::Sequence< sal_Int32 > &rCoordinateSystemResolution)
bool isLogicVisible(double fX, double fY, double fZ) const
virtual css::drawing::Position3D transformLogicToScene(double fX, double fY, double fZ, bool bClip) const
void doLogicScaling(double *pX, double *pY, double *pZ) const
static void setShapeName(const rtl::Reference< SvxShape > &xShape, const OUString &rName)
sal_Int32 nIndex
#define SAL_INFO(area, stream)
@ LABEL_ALIGN_CENTER
@ LABEL_ALIGN_TOP
@ LABEL_ALIGN_RIGHT
@ LABEL_ALIGN_LEFT
@ LABEL_ALIGN_BOTTOM
SwNodeOffset abs(const SwNodeOffset &a)