Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/chart2/source/view/main/SeriesPlotterContainer.cxx
Line
Count
Source
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 <sal/config.h>
21
22
#include <cstddef>
23
24
#include "SeriesPlotterContainer.hxx"
25
26
#include <Diagram.hxx>
27
#include <ChartType.hxx>
28
#include <DataSeries.hxx>
29
#include <ChartModel.hxx>
30
#include <ChartTypeHelper.hxx>
31
#include <ObjectIdentifier.hxx>
32
#include <DiagramHelper.hxx>
33
#include <Axis.hxx>
34
#include <AxisIndexDefines.hxx>
35
#include <ChartColorScheme.hxx>
36
#include <DataSeriesHelper.hxx>
37
#include <ExplicitCategoriesProvider.hxx>
38
#include <unonames.hxx>
39
40
#include <com/sun/star/chart/ChartAxisPosition.hpp>
41
#include <com/sun/star/chart2/AxisType.hpp>
42
#include <com/sun/star/chart2/PieChartSubType.hpp>
43
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
44
45
#include <servicenames_charttypes.hxx>
46
#include <comphelper/diagnose_ex.hxx>
47
48
namespace chart
49
{
50
using namespace ::css;
51
using namespace ::css::chart2;
52
53
using ::css::uno::Reference;
54
using ::css::uno::Sequence;
55
using ::css::uno::Any;
56
57
SeriesPlotterContainer::SeriesPlotterContainer(
58
    std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList)
59
0
    : m_rVCooSysList(rVCooSysList)
60
0
    , m_nMaxAxisIndex(0)
61
0
    , m_bChartTypeUsesShiftedCategoryPositionPerDefault(false)
62
0
    , m_nDefaultDateNumberFormat(0)
63
0
{
64
0
}
65
66
SeriesPlotterContainer::~SeriesPlotterContainer()
67
0
{
68
    // - remove plotter from coordinatesystems
69
0
    for (auto& nC : m_rVCooSysList)
70
0
        nC->clearMinimumAndMaximumSupplierList();
71
0
}
72
73
std::vector<LegendEntryProvider*> SeriesPlotterContainer::getLegendEntryProviderList()
74
0
{
75
0
    std::vector<LegendEntryProvider*> aRet(m_aSeriesPlotterList.size());
76
0
    sal_Int32 nN = 0;
77
0
    for (const std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList)
78
0
        aRet[nN++] = aPlotter.get();
79
0
    return aRet;
80
0
}
81
82
VCoordinateSystem* SeriesPlotterContainer::findInCooSysList(
83
    const std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList,
84
    const rtl::Reference<BaseCoordinateSystem>& xCooSys)
85
0
{
86
0
    for (auto& pVCooSys : rVCooSysList)
87
0
    {
88
0
        if (pVCooSys->getModel() == xCooSys)
89
0
            return pVCooSys.get();
90
0
    }
91
0
    return nullptr;
92
0
}
93
94
VCoordinateSystem* SeriesPlotterContainer::getCooSysForPlotter(
95
    const std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList,
96
    MinimumAndMaximumSupplier* pMinimumAndMaximumSupplier)
97
0
{
98
0
    if (!pMinimumAndMaximumSupplier)
99
0
        return nullptr;
100
0
    for (auto& pVCooSys : rVCooSysList)
101
0
    {
102
0
        if (pVCooSys->hasMinimumAndMaximumSupplier(pMinimumAndMaximumSupplier))
103
0
            return pVCooSys.get();
104
0
    }
105
0
    return nullptr;
106
0
}
107
108
VCoordinateSystem* SeriesPlotterContainer::addCooSysToList(
109
    std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList,
110
    const rtl::Reference<BaseCoordinateSystem>& xCooSys, ChartModel& rChartModel)
111
0
{
112
0
    VCoordinateSystem* pExistingVCooSys
113
0
        = SeriesPlotterContainer::findInCooSysList(rVCooSysList, xCooSys);
114
0
    if (pExistingVCooSys)
115
0
        return pExistingVCooSys;
116
117
0
    std::unique_ptr<VCoordinateSystem> pVCooSys
118
0
        = VCoordinateSystem::createCoordinateSystem(xCooSys);
119
0
    if (!pVCooSys)
120
0
        return nullptr;
121
122
0
    OUString aCooSysParticle(
123
0
        ObjectIdentifier::createParticleForCoordinateSystem(xCooSys, &rChartModel));
124
0
    pVCooSys->setParticle(aCooSysParticle);
125
126
0
    pVCooSys->setExplicitCategoriesProvider(new ExplicitCategoriesProvider(xCooSys, rChartModel));
127
0
    rVCooSysList.push_back(std::move(pVCooSys));
128
0
    return rVCooSysList.back().get();
129
0
}
130
131
void SeriesPlotterContainer::initializeCooSysAndSeriesPlotter(ChartModel& rChartModel)
132
0
{
133
0
    rtl::Reference<Diagram> xDiagram = rChartModel.getFirstChartDiagram();
134
0
    if (!xDiagram.is())
135
0
        return;
136
137
0
    uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(&rChartModel);
138
0
    if (rChartModel.hasInternalDataProvider() && xDiagram->isSupportingDateAxis())
139
0
        m_nDefaultDateNumberFormat = DiagramHelper::getDateNumberFormat(xNumberFormatsSupplier);
140
141
0
    sal_Int32 nDimensionCount = xDiagram->getDimension();
142
0
    if (!nDimensionCount)
143
0
    {
144
        //@todo handle mixed dimension
145
0
        nDimensionCount = 2;
146
0
    }
147
148
0
    bool bSortByXValues = false;
149
0
    bool bConnectBars = false;
150
0
    bool bGroupBarsPerAxis = true;
151
0
    bool bIncludeHiddenCells = true;
152
0
    bool bSecondaryYaxisVisible = true;
153
0
    sal_Int32 nStartingAngle = 90;
154
0
    sal_Int32 n3DRelativeHeight = 100;
155
0
    PieChartSubType ePieChartSubType = PieChartSubType_NONE;
156
0
    double nSplitPos = 2;
157
0
    try
158
0
    {
159
0
        xDiagram->getPropertyValue(CHART_UNONAME_SORT_BY_XVALUES) >>= bSortByXValues;
160
0
        xDiagram->getPropertyValue(u"ConnectBars"_ustr) >>= bConnectBars;
161
0
        xDiagram->getPropertyValue(u"GroupBarsPerAxis"_ustr) >>= bGroupBarsPerAxis;
162
0
        xDiagram->getPropertyValue(u"IncludeHiddenCells"_ustr) >>= bIncludeHiddenCells;
163
0
        xDiagram->getPropertyValue(u"StartingAngle"_ustr) >>= nStartingAngle;
164
165
0
        if (nDimensionCount == 3)
166
0
        {
167
0
            xDiagram->getPropertyValue(u"3DRelativeHeight"_ustr) >>= n3DRelativeHeight;
168
0
        }
169
0
        xDiagram->getPropertyValue(u"SubPieType"_ustr) >>= ePieChartSubType;
170
171
0
        xDiagram->getPropertyValue(u"SplitPos"_ustr) >>= nSplitPos;
172
0
    }
173
0
    catch (const uno::Exception&)
174
0
    {
175
0
        DBG_UNHANDLED_EXCEPTION("chart2");
176
0
    }
177
178
0
    if (xDiagram->getDataTable().is())
179
0
        m_bTableShiftPosition = true;
180
181
    //prepare for autoscaling and shape creation
182
    // - create plotter for charttypes (for each first scale group at each plotter, as they are independent)
183
    // - add series to plotter (thus each charttype can provide minimum and maximum values for autoscaling)
184
    // - add plotter to coordinate systems
185
186
    //iterate through all coordinate systems
187
0
    uno::Reference<XColorScheme> xColorScheme;
188
0
    if (!rChartModel.usesColorPalette())
189
0
        xColorScheme = xDiagram->getDefaultColorScheme();
190
0
    else
191
0
        xColorScheme = new ChartColorScheme(*rChartModel.getCurrentColorPalette());
192
0
    auto aCooSysList = xDiagram->getBaseCoordinateSystems();
193
0
    sal_Int32 nGlobalSeriesIndex = 0; //for automatic symbols
194
0
    for (std::size_t nCS = 0; nCS < aCooSysList.size(); ++nCS)
195
0
    {
196
0
        const rtl::Reference<BaseCoordinateSystem>& xCooSys(aCooSysList[nCS]);
197
0
        VCoordinateSystem* pVCooSys
198
0
            = SeriesPlotterContainer::addCooSysToList(m_rVCooSysList, xCooSys, rChartModel);
199
        // Let's check whether the secondary Y axis is visible
200
0
        try
201
0
        {
202
0
            if (xCooSys->getMaximumAxisIndexByDimension(1) > 0)
203
0
            {
204
0
                rtl::Reference<Axis> xAxisProp = xCooSys->getAxisByDimension2(1, 1);
205
0
                xAxisProp->getPropertyValue(u"Show"_ustr) >>= bSecondaryYaxisVisible;
206
0
            }
207
0
        }
208
0
        catch (const lang::IndexOutOfBoundsException&)
209
0
        {
210
0
            TOOLS_WARN_EXCEPTION("chart2", "");
211
0
        }
212
        //iterate through all chart types in the current coordinate system
213
0
        std::vector<rtl::Reference<ChartType>> aChartTypeList(xCooSys->getChartTypes2());
214
0
        for (std::size_t nT = 0; nT < aChartTypeList.size(); ++nT)
215
0
        {
216
0
            const rtl::Reference<ChartType>& xChartType(aChartTypeList[nT]);
217
0
            if (nDimensionCount == 3
218
0
                && xChartType->getChartType().equalsIgnoreAsciiCase(
219
0
                       CHART2_SERVICE_NAME_CHARTTYPE_PIE))
220
0
            {
221
0
                try
222
0
                {
223
0
                    sal_Int32 n3DRelativeHeightOldValue(100);
224
0
                    uno::Any aAny = xChartType->getFastPropertyValue(
225
0
                        PROP_PIECHARTTYPE_3DRELATIVEHEIGHT); // "3DRelativeHeight"
226
0
                    aAny >>= n3DRelativeHeightOldValue;
227
0
                    if (n3DRelativeHeightOldValue != n3DRelativeHeight)
228
0
                        xChartType->setFastPropertyValue(
229
0
                            PROP_PIECHARTTYPE_3DRELATIVEHEIGHT, // "3DRelativeHeight"
230
0
                            uno::Any(n3DRelativeHeight));
231
0
                }
232
0
                catch (const uno::Exception&)
233
0
                {
234
0
                }
235
0
            }
236
237
0
            if (ePieChartSubType != PieChartSubType_NONE)
238
0
            {
239
0
                xChartType->setFastPropertyValue(PROP_PIECHARTTYPE_SUBTYPE,
240
0
                                                 uno::Any(ePieChartSubType));
241
                // Reset the diagram-level property so it's not persistent.
242
0
                xDiagram->setPropertyValue(u"SubPieType"_ustr, uno::Any(PieChartSubType_NONE));
243
244
0
                xChartType->setFastPropertyValue(PROP_PIECHARTTYPE_SPLIT_POS, uno::Any(nSplitPos));
245
                //xDiagram->setPropertyValue(u"SplitPos"_ustr, uno::Any(nSplitPos));
246
0
            }
247
248
0
            if (nT == 0)
249
0
                m_bChartTypeUsesShiftedCategoryPositionPerDefault
250
0
                    = ChartTypeHelper::shiftCategoryPosAtXAxisPerDefault(xChartType);
251
252
0
            bool bExcludingPositioning
253
0
                = xDiagram->getDiagramPositioningMode() == DiagramPositioningMode::Excluding;
254
0
            VSeriesPlotter* pPlotter = VSeriesPlotter::createSeriesPlotter(
255
0
                xChartType, nDimensionCount, bExcludingPositioning);
256
0
            if (!pPlotter)
257
0
                continue;
258
259
0
            m_aSeriesPlotterList.push_back(std::unique_ptr<VSeriesPlotter>(pPlotter));
260
0
            pPlotter->setNumberFormatsSupplier(xNumberFormatsSupplier);
261
0
            pPlotter->setColorScheme(xColorScheme);
262
0
            if (pVCooSys)
263
0
                pPlotter->setExplicitCategoriesProvider(pVCooSys->getExplicitCategoriesProvider());
264
0
            sal_Int32 nMissingValueTreatment
265
0
                = xDiagram->getCorrectedMissingValueTreatment(xChartType);
266
267
0
            if (pVCooSys)
268
0
                pVCooSys->addMinimumAndMaximumSupplier(pPlotter);
269
270
0
            sal_Int32 zSlot = -1;
271
0
            sal_Int32 xSlot = -1;
272
0
            sal_Int32 ySlot = -1;
273
0
            const std::vector<rtl::Reference<DataSeries>>& aSeriesList
274
0
                = xChartType->getDataSeries2();
275
0
            for (std::size_t nS = 0; nS < aSeriesList.size(); ++nS)
276
0
            {
277
0
                rtl::Reference<DataSeries> const& xDataSeries = aSeriesList[nS];
278
0
                if (!bIncludeHiddenCells && !xDataSeries->hasUnhiddenData())
279
0
                    continue;
280
281
0
                std::unique_ptr<VDataSeries> pSeries(new VDataSeries(xDataSeries));
282
283
0
                pSeries->setGlobalSeriesIndex(nGlobalSeriesIndex);
284
0
                nGlobalSeriesIndex++;
285
286
0
                if (bSortByXValues)
287
0
                    pSeries->doSortByXValues();
288
289
0
                pSeries->setConnectBars(bConnectBars);
290
0
                pSeries->setGroupBarsPerAxis(bGroupBarsPerAxis);
291
0
                pSeries->setStartingAngle(nStartingAngle);
292
293
0
                pSeries->setMissingValueTreatment(nMissingValueTreatment);
294
295
0
                OUString aSeriesParticle(ObjectIdentifier::createParticleForSeries(0, nCS, nT, nS));
296
0
                pSeries->setParticle(aSeriesParticle);
297
298
0
                OUString aRole(ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection(
299
0
                    xChartType));
300
0
                pSeries->setRoleOfSequenceForDataLabelNumberFormatDetection(aRole);
301
302
                //ignore secondary axis for charttypes that do not support them
303
0
                if (pSeries->getAttachedAxisIndex() != MAIN_AXIS_INDEX
304
0
                    && (!(xChartType.is() ? xChartType->isSupportingSecondaryAxis(nDimensionCount)
305
0
                                          : true)
306
0
                        || !bSecondaryYaxisVisible))
307
0
                {
308
0
                    pSeries->setAttachedAxisIndex(MAIN_AXIS_INDEX);
309
0
                }
310
311
0
                StackingDirection eDirection = pSeries->getStackingDirection();
312
0
                switch (eDirection)
313
0
                {
314
0
                    case StackingDirection_NO_STACKING:
315
0
                        xSlot++;
316
0
                        ySlot = -1;
317
0
                        if (zSlot < 0)
318
0
                            zSlot = 0;
319
0
                        break;
320
0
                    case StackingDirection_Y_STACKING:
321
0
                        ySlot++;
322
0
                        if (xSlot < 0)
323
0
                            xSlot = 0;
324
0
                        if (zSlot < 0)
325
0
                            zSlot = 0;
326
0
                        break;
327
0
                    case StackingDirection_Z_STACKING:
328
0
                        zSlot++;
329
0
                        xSlot = -1;
330
0
                        ySlot = -1;
331
0
                        break;
332
0
                    default:
333
                        // UNO enums have one additional auto-generated case
334
0
                        break;
335
0
                }
336
0
                pPlotter->addSeries(std::move(pSeries), zSlot, xSlot, ySlot);
337
0
            }
338
0
        }
339
0
    }
340
341
0
    auto order
342
0
        = [](const std::unique_ptr<VSeriesPlotter>& a, const std::unique_ptr<VSeriesPlotter>& b) {
343
0
              return a->getRenderOrder() < b->getRenderOrder();
344
0
          };
345
346
0
    std::stable_sort(m_aSeriesPlotterList.begin(), m_aSeriesPlotterList.end(), order);
347
348
    //transport seriesnames to the coordinatesystems if needed
349
0
    if (m_aSeriesPlotterList.empty())
350
0
        return;
351
352
0
    uno::Sequence<OUString> aSeriesNames;
353
0
    bool bSeriesNamesInitialized = false;
354
0
    for (auto& pVCooSys : m_rVCooSysList)
355
0
    {
356
0
        if (pVCooSys->needSeriesNamesForAxis())
357
0
        {
358
0
            if (!bSeriesNamesInitialized)
359
0
            {
360
0
                aSeriesNames = m_aSeriesPlotterList[0]->getSeriesNames();
361
0
                bSeriesNamesInitialized = true;
362
0
            }
363
0
            pVCooSys->setSeriesNamesForAxis(aSeriesNames);
364
0
        }
365
0
    }
366
0
}
367
368
bool SeriesPlotterContainer::isCategoryPositionShifted(const chart2::ScaleData& rSourceScale,
369
                                                       bool bHasComplexCategories)
370
0
{
371
0
    if (rSourceScale.AxisType == AxisType::CATEGORY)
372
0
        return bHasComplexCategories || rSourceScale.ShiftedCategoryPosition
373
0
               || m_bTableShiftPosition || m_bChartTypeUsesShiftedCategoryPositionPerDefault;
374
375
0
    if (rSourceScale.AxisType == AxisType::DATE)
376
0
        return rSourceScale.ShiftedCategoryPosition;
377
378
0
    return rSourceScale.AxisType == AxisType::SERIES;
379
0
}
380
381
void SeriesPlotterContainer::initAxisUsageList(const Date& rNullDate)
382
0
{
383
0
    m_aAxisUsageList.clear();
384
385
    // Loop through coordinate systems in the diagram (though for now
386
    // there should only be one coordinate system per diagram).
387
0
    for (auto& pVCooSys : m_rVCooSysList)
388
0
    {
389
0
        rtl::Reference<BaseCoordinateSystem> xCooSys = pVCooSys->getModel();
390
0
        sal_Int32 nDimCount = xCooSys->getDimension();
391
0
        auto xChartType = AxisHelper::getChartTypeByIndex(xCooSys, 0);
392
0
        bool bComplexCategoryAllowed
393
0
            = xChartType.is() ? xChartType->isSupportingComplexCategory() : true;
394
395
0
        for (sal_Int32 nDimIndex = 0; nDimIndex < nDimCount; ++nDimIndex)
396
0
        {
397
0
            bool bDateAxisAllowed
398
0
                = xChartType.is() ? xChartType->isSupportingDateAxis(nDimIndex) : true;
399
400
            // Each dimension may have primary and secondary axes.
401
0
            const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimIndex);
402
0
            for (sal_Int32 nAxisIndex = 0; nAxisIndex <= nMaxAxisIndex; ++nAxisIndex)
403
0
            {
404
0
                rtl::Reference<Axis> xAxis = xCooSys->getAxisByDimension2(nDimIndex, nAxisIndex);
405
406
0
                if (!xAxis.is())
407
0
                    continue;
408
409
0
                if (m_aAxisUsageList.find(xAxis) == m_aAxisUsageList.end())
410
0
                {
411
                    // Create axis usage object for this axis.
412
413
0
                    chart2::ScaleData aSourceScale = xAxis->getScaleData();
414
0
                    ExplicitCategoriesProvider* pCatProvider
415
0
                        = pVCooSys->getExplicitCategoriesProvider();
416
0
                    if (nDimIndex == 0)
417
0
                        AxisHelper::checkDateAxis(aSourceScale, pCatProvider, bDateAxisAllowed);
418
419
0
                    bool bHasComplexCat = pCatProvider && pCatProvider->hasComplexCategories()
420
0
                                          && bComplexCategoryAllowed;
421
0
                    aSourceScale.ShiftedCategoryPosition
422
0
                        = isCategoryPositionShifted(aSourceScale, bHasComplexCat);
423
424
0
                    m_aAxisUsageList[xAxis].aAutoScaling = ScaleAutomatism(aSourceScale, rNullDate);
425
0
                }
426
427
0
                AxisUsage& rAxisUsage = m_aAxisUsageList[xAxis];
428
0
                rAxisUsage.addCoordinateSystem(pVCooSys.get(), nDimIndex, nAxisIndex);
429
0
            }
430
0
        }
431
0
    }
432
433
    // Determine the highest axis index of all dimensions.
434
0
    m_nMaxAxisIndex = 0;
435
0
    for (const auto& pVCooSys : m_rVCooSysList)
436
0
    {
437
0
        rtl::Reference<BaseCoordinateSystem> xCooSys = pVCooSys->getModel();
438
0
        sal_Int32 nDimCount = xCooSys->getDimension();
439
440
0
        for (sal_Int32 nDimIndex = 0; nDimIndex < nDimCount; ++nDimIndex)
441
0
        {
442
0
            for (auto& axisUsage : m_aAxisUsageList)
443
0
            {
444
0
                sal_Int32 nLocalMax = axisUsage.second.getMaxAxisIndexForDimension(nDimIndex);
445
0
                if (m_nMaxAxisIndex < nLocalMax)
446
0
                    m_nMaxAxisIndex = nLocalMax;
447
0
            }
448
0
        }
449
0
    }
450
0
}
451
452
void SeriesPlotterContainer::setScalesFromCooSysToPlotter()
453
0
{
454
    //set scales to plotter to enable them to provide the preferred scene AspectRatio
455
0
    for (const std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList)
456
0
    {
457
0
        VSeriesPlotter* pSeriesPlotter = aPlotter.get();
458
0
        VCoordinateSystem* pVCooSys
459
0
            = SeriesPlotterContainer::getCooSysForPlotter(m_rVCooSysList, pSeriesPlotter);
460
0
        if (pVCooSys)
461
0
        {
462
0
            pSeriesPlotter->setScales(pVCooSys->getExplicitScales(0, 0),
463
0
                                      pVCooSys->getPropertySwapXAndYAxis());
464
0
            sal_Int32 nMaxAxisIndex = pVCooSys->getMaximumAxisIndexByDimension(
465
0
                1); //only additional value axis are relevant for series plotter
466
0
            for (sal_Int32 nI = 1; nI <= nMaxAxisIndex; nI++)
467
0
                pSeriesPlotter->addSecondaryValueScale(pVCooSys->getExplicitScale(1, nI), nI);
468
0
        }
469
0
    }
470
0
}
471
472
void SeriesPlotterContainer::setNumberFormatsFromAxes()
473
0
{
474
    //set numberformats to plotter to enable them to display the data labels in the numberformat of the axis
475
0
    for (const std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList)
476
0
    {
477
0
        VSeriesPlotter* pSeriesPlotter = aPlotter.get();
478
0
        VCoordinateSystem* pVCooSys
479
0
            = SeriesPlotterContainer::getCooSysForPlotter(m_rVCooSysList, pSeriesPlotter);
480
0
        if (pVCooSys)
481
0
        {
482
0
            AxesNumberFormats aAxesNumberFormats;
483
0
            const rtl::Reference<BaseCoordinateSystem>& xCooSys = pVCooSys->getModel();
484
0
            sal_Int32 nDimensionCount = xCooSys->getDimension();
485
0
            for (sal_Int32 nDimensionIndex = 0; nDimensionIndex < nDimensionCount;
486
0
                 ++nDimensionIndex)
487
0
            {
488
0
                const sal_Int32 nMaximumAxisIndex
489
0
                    = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
490
0
                for (sal_Int32 nAxisIndex = 0; nAxisIndex <= nMaximumAxisIndex; ++nAxisIndex)
491
0
                {
492
0
                    try
493
0
                    {
494
0
                        rtl::Reference<Axis> xAxisProp
495
0
                            = xCooSys->getAxisByDimension2(nDimensionIndex, nAxisIndex);
496
0
                        if (xAxisProp.is())
497
0
                        {
498
0
                            sal_Int32 nNumberFormatKey(0);
499
0
                            if (xAxisProp->getPropertyValue(CHART_UNONAME_NUMFMT)
500
0
                                >>= nNumberFormatKey)
501
0
                            {
502
0
                                aAxesNumberFormats.setFormat(nNumberFormatKey, nDimensionIndex,
503
0
                                                             nAxisIndex);
504
0
                            }
505
0
                            else if (nDimensionIndex == 0)
506
0
                            {
507
                                //provide a default date format for date axis with own data
508
0
                                aAxesNumberFormats.setFormat(m_nDefaultDateNumberFormat,
509
0
                                                             nDimensionIndex, nAxisIndex);
510
0
                            }
511
0
                        }
512
0
                    }
513
0
                    catch (const lang::IndexOutOfBoundsException&)
514
0
                    {
515
0
                        TOOLS_WARN_EXCEPTION("chart2", "");
516
0
                    }
517
0
                }
518
0
            }
519
0
        }
520
0
    }
521
0
}
522
523
void SeriesPlotterContainer::updateScalesAndIncrementsOnAxes()
524
0
{
525
0
    for (auto& nC : m_rVCooSysList)
526
0
        nC->updateScalesAndIncrementsOnAxes();
527
0
}
528
529
void SeriesPlotterContainer::doAutoScaling(ChartModel& rChartModel)
530
0
{
531
0
    if (m_aSeriesPlotterList.empty() || m_aAxisUsageList.empty())
532
        // We need these two containers populated to do auto-scaling.  Bail out.
533
0
        return;
534
535
    //iterate over the main scales first than secondary axis
536
0
    for (sal_Int32 nAxisIndex = 0; nAxisIndex <= m_nMaxAxisIndex; ++nAxisIndex)
537
0
    {
538
        // - first do autoscale for all x and z scales (because they are treated independent)
539
0
        for (auto & [ rAxis, rAxisUsage ] : m_aAxisUsageList)
540
0
        {
541
0
            (void)rAxis;
542
0
            rAxisUsage.prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 0, nAxisIndex);
543
0
            rAxisUsage.prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 2, nAxisIndex);
544
545
0
            ExplicitScaleData aExplicitScale;
546
0
            ExplicitIncrementData aExplicitIncrement;
547
0
            rAxisUsage.aAutoScaling.calculateExplicitScaleAndIncrement(aExplicitScale,
548
0
                                                                       aExplicitIncrement);
549
550
0
            rAxisUsage.setExplicitScaleAndIncrement(0, nAxisIndex, aExplicitScale,
551
0
                                                    aExplicitIncrement);
552
0
            rAxisUsage.setExplicitScaleAndIncrement(2, nAxisIndex, aExplicitScale,
553
0
                                                    aExplicitIncrement);
554
0
        }
555
556
        // - second do autoscale for the dependent y scales (the coordinate systems are prepared with x and z scales already )
557
0
        for (auto & [ rAxis, rAxisUsage ] : m_aAxisUsageList)
558
0
        {
559
0
            (void)rAxis;
560
0
            rAxisUsage.prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 1, nAxisIndex);
561
562
0
            ExplicitScaleData aExplicitScale;
563
0
            ExplicitIncrementData aExplicitIncrement;
564
0
            rAxisUsage.aAutoScaling.calculateExplicitScaleAndIncrement(aExplicitScale,
565
0
                                                                       aExplicitIncrement);
566
567
0
            rAxisUsage.setExplicitScaleAndIncrement(0, nAxisIndex, aExplicitScale,
568
0
                                                    aExplicitIncrement);
569
0
            rAxisUsage.setExplicitScaleAndIncrement(1, nAxisIndex, aExplicitScale,
570
0
                                                    aExplicitIncrement);
571
0
            rAxisUsage.setExplicitScaleAndIncrement(2, nAxisIndex, aExplicitScale,
572
0
                                                    aExplicitIncrement);
573
0
        }
574
0
    }
575
0
    AdaptScaleOfYAxisWithoutAttachedSeries(rChartModel);
576
0
}
577
578
void SeriesPlotterContainer::AdaptScaleOfYAxisWithoutAttachedSeries(ChartModel& rModel)
579
0
{
580
    //issue #i80518#
581
0
    for (sal_Int32 nAxisIndex = 0; nAxisIndex <= m_nMaxAxisIndex; nAxisIndex++)
582
0
    {
583
0
        for (auto & [ rAxis, rAxisUsage ] : m_aAxisUsageList)
584
0
        {
585
0
            (void)rAxis;
586
0
            std::vector<VCoordinateSystem*> aVCooSysList_Y
587
0
                = rAxisUsage.getCoordinateSystems(1, nAxisIndex);
588
0
            if (aVCooSysList_Y.empty())
589
0
                continue;
590
591
0
            rtl::Reference<Diagram> xDiagram(rModel.getFirstChartDiagram());
592
0
            if (!xDiagram.is())
593
0
                continue;
594
595
0
            bool bSeriesAttachedToThisAxis = false;
596
0
            sal_Int32 nAttachedAxisIndex = -1;
597
0
            {
598
0
                std::vector<rtl::Reference<DataSeries>> aSeriesVector = xDiagram->getDataSeries();
599
0
                for (auto const& series : aSeriesVector)
600
0
                {
601
0
                    sal_Int32 nCurrentIndex = series->getAttachedAxisIndex();
602
0
                    if (nAxisIndex == nCurrentIndex)
603
0
                    {
604
0
                        bSeriesAttachedToThisAxis = true;
605
0
                        break;
606
0
                    }
607
0
                    else if (nAttachedAxisIndex < 0 || nAttachedAxisIndex > nCurrentIndex)
608
0
                        nAttachedAxisIndex = nCurrentIndex;
609
0
                }
610
0
            }
611
612
0
            if (bSeriesAttachedToThisAxis || nAttachedAxisIndex < 0)
613
0
                continue;
614
615
0
            for (VCoordinateSystem* nC : aVCooSysList_Y)
616
0
            {
617
0
                nC->prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 1, nAttachedAxisIndex);
618
619
0
                ExplicitScaleData aExplicitScaleSource
620
0
                    = nC->getExplicitScale(1, nAttachedAxisIndex);
621
0
                ExplicitIncrementData aExplicitIncrementSource
622
0
                    = nC->getExplicitIncrement(1, nAttachedAxisIndex);
623
624
0
                ExplicitScaleData aExplicitScaleDest = nC->getExplicitScale(1, nAxisIndex);
625
0
                ExplicitIncrementData aExplicitIncrementDest
626
0
                    = nC->getExplicitIncrement(1, nAxisIndex);
627
628
0
                aExplicitScaleDest.Orientation = aExplicitScaleSource.Orientation;
629
0
                aExplicitScaleDest.Scaling = aExplicitScaleSource.Scaling;
630
0
                aExplicitScaleDest.AxisType = aExplicitScaleSource.AxisType;
631
632
0
                aExplicitIncrementDest.BaseValue = aExplicitIncrementSource.BaseValue;
633
634
0
                ScaleData aScale(rAxisUsage.aAutoScaling.getScale());
635
0
                if (!aScale.Minimum.hasValue())
636
0
                {
637
0
                    bool bNewMinOK = true;
638
0
                    double fMax = 0.0;
639
0
                    if (aScale.Maximum >>= fMax)
640
0
                        bNewMinOK = (aExplicitScaleSource.Minimum <= fMax);
641
0
                    if (bNewMinOK)
642
0
                        aExplicitScaleDest.Minimum = aExplicitScaleSource.Minimum;
643
0
                }
644
0
                else
645
0
                    aExplicitIncrementDest.BaseValue = aExplicitScaleDest.Minimum;
646
647
0
                if (!aScale.Maximum.hasValue())
648
0
                {
649
0
                    bool bNewMaxOK = true;
650
0
                    double fMin = 0.0;
651
0
                    if (aScale.Minimum >>= fMin)
652
0
                        bNewMaxOK = (fMin <= aExplicitScaleSource.Maximum);
653
0
                    if (bNewMaxOK)
654
0
                        aExplicitScaleDest.Maximum = aExplicitScaleSource.Maximum;
655
0
                }
656
0
                if (!aScale.Origin.hasValue())
657
0
                    aExplicitScaleDest.Origin = aExplicitScaleSource.Origin;
658
659
0
                if (!aScale.IncrementData.Distance.hasValue())
660
0
                    aExplicitIncrementDest.Distance = aExplicitIncrementSource.Distance;
661
662
0
                bool bAutoMinorInterval = true;
663
0
                if (aScale.IncrementData.SubIncrements.hasElements())
664
0
                    bAutoMinorInterval
665
0
                        = !(aScale.IncrementData.SubIncrements[0].IntervalCount.hasValue());
666
0
                if (bAutoMinorInterval)
667
0
                {
668
0
                    if (!aExplicitIncrementDest.SubIncrements.empty()
669
0
                        && !aExplicitIncrementSource.SubIncrements.empty())
670
0
                        aExplicitIncrementDest.SubIncrements[0].IntervalCount
671
0
                            = aExplicitIncrementSource.SubIncrements[0].IntervalCount;
672
0
                }
673
674
0
                nC->setExplicitScaleAndIncrement(1, nAxisIndex, aExplicitScaleDest,
675
0
                                                 aExplicitIncrementDest);
676
0
            }
677
0
        }
678
0
    }
679
680
0
    if (!AxisHelper::isAxisPositioningEnabled())
681
0
        return;
682
683
    //correct origin for y main axis (the origin is where the other main axis crosses)
684
0
    sal_Int32 nAxisIndex = 0;
685
0
    sal_Int32 nDimensionIndex = 1;
686
0
    for (auto & [ rAxis, rAxisUsage ] : m_aAxisUsageList)
687
0
    {
688
0
        (void)rAxis;
689
0
        std::vector<VCoordinateSystem*> aVCooSysList
690
0
            = rAxisUsage.getCoordinateSystems(nDimensionIndex, nAxisIndex);
691
0
        size_t nC;
692
0
        for (nC = 0; nC < aVCooSysList.size(); nC++)
693
0
        {
694
0
            ExplicitScaleData aExplicitScale(
695
0
                aVCooSysList[nC]->getExplicitScale(nDimensionIndex, nAxisIndex));
696
0
            ExplicitIncrementData aExplicitIncrement(
697
0
                aVCooSysList[nC]->getExplicitIncrement(nDimensionIndex, nAxisIndex));
698
699
0
            rtl::Reference<BaseCoordinateSystem> xCooSys(aVCooSysList[nC]->getModel());
700
0
            rtl::Reference<Axis> xAxis = xCooSys->getAxisByDimension2(nDimensionIndex, nAxisIndex);
701
0
            rtl::Reference<Axis> xCrossingMainAxis
702
0
                = AxisHelper::getCrossingMainAxis(xAxis, xCooSys);
703
704
0
            if (xCrossingMainAxis.is())
705
0
            {
706
0
                css::chart::ChartAxisPosition eCrossingMainAxisPos(
707
0
                    css::chart::ChartAxisPosition_ZERO);
708
0
                xCrossingMainAxis->getPropertyValue(u"CrossoverPosition"_ustr)
709
0
                    >>= eCrossingMainAxisPos;
710
0
                if (eCrossingMainAxisPos == css::chart::ChartAxisPosition_VALUE)
711
0
                {
712
0
                    double fValue = 0.0;
713
0
                    xCrossingMainAxis->getPropertyValue(u"CrossoverValue"_ustr) >>= fValue;
714
0
                    aExplicitScale.Origin = fValue;
715
0
                }
716
0
                else if (eCrossingMainAxisPos == css::chart::ChartAxisPosition_ZERO)
717
0
                    aExplicitScale.Origin = 0.0;
718
0
                else if (eCrossingMainAxisPos == css::chart::ChartAxisPosition_START)
719
0
                    aExplicitScale.Origin = aExplicitScale.Minimum;
720
0
                else if (eCrossingMainAxisPos == css::chart::ChartAxisPosition_END)
721
0
                    aExplicitScale.Origin = aExplicitScale.Maximum;
722
0
            }
723
724
0
            aVCooSysList[nC]->setExplicitScaleAndIncrement(nDimensionIndex, nAxisIndex,
725
0
                                                           aExplicitScale, aExplicitIncrement);
726
0
        }
727
0
    }
728
0
}
729
730
drawing::Direction3D SeriesPlotterContainer::getPreferredAspectRatio()
731
0
{
732
0
    drawing::Direction3D aPreferredAspectRatio(1.0, 1.0, 1.0);
733
734
    //get a list of all preferred aspect ratios and combine them
735
    //first with special demands wins (less or equal zero <-> arbitrary)
736
0
    double fx, fy, fz;
737
0
    fx = fy = fz = -1.0;
738
0
    for (const std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList)
739
0
    {
740
0
        drawing::Direction3D aSingleRatio(aPlotter->getPreferredDiagramAspectRatio());
741
0
        if (fx < 0 && aSingleRatio.DirectionX > 0)
742
0
            fx = aSingleRatio.DirectionX;
743
744
0
        if (fy < 0 && aSingleRatio.DirectionY > 0)
745
0
        {
746
0
            if (fx > 0 && aSingleRatio.DirectionX > 0)
747
0
                fy = fx * aSingleRatio.DirectionY / aSingleRatio.DirectionX;
748
0
            else if (fz > 0 && aSingleRatio.DirectionZ > 0)
749
0
                fy = fz * aSingleRatio.DirectionY / aSingleRatio.DirectionZ;
750
0
            else
751
0
                fy = aSingleRatio.DirectionY;
752
0
        }
753
754
0
        if (fz < 0 && aSingleRatio.DirectionZ > 0)
755
0
        {
756
0
            if (fx > 0 && aSingleRatio.DirectionX > 0)
757
0
                fz = fx * aSingleRatio.DirectionZ / aSingleRatio.DirectionX;
758
0
            else if (fy > 0 && aSingleRatio.DirectionY > 0)
759
0
                fz = fy * aSingleRatio.DirectionZ / aSingleRatio.DirectionY;
760
0
            else
761
0
                fz = aSingleRatio.DirectionZ;
762
0
        }
763
764
0
        if (fx > 0 && fy > 0 && fz > 0)
765
0
            break;
766
0
    }
767
0
    aPreferredAspectRatio = drawing::Direction3D(fx, fy, fz);
768
0
    return aPreferredAspectRatio;
769
0
}
770
771
} //end chart2 namespace
772
773
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */