From 160014f44bc92bba18a1137efd54887fc61b8f4a Mon Sep 17 00:00:00 2001 From: Kurt Nordback Date: Wed, 15 Jul 2026 16:45:15 -0600 Subject: [PATCH] Initial implementation of rendering for funnel charts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Framework for funnel chart rendering. For now this just renders them as horizontal bar charts: that is, not properly centred as a funnel. UI functionality is untested and probably broken. Signed-off-by: Kurt Nordback Change-Id: I3a0e923b801c840138c90dcf3609253a467d23d1 Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/7905 Tested-by: Jenkins CPCI Reviewed-by: Tomaž Vajngerl (cherry picked from commit 172e4233b70f1d73ca942876f3f47458843ffa36) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/209739 Tested-by: Jenkins Reviewed-by: Xisco Fauli --- chart2/Library_chart2.mk | 1 + chart2/qa/extras/chart2import2.cxx | 13 ++ chart2/qa/extras/data/xlsx/color_funnel.xlsx | Bin 13156 -> 10828 bytes chart2/source/model/template/ChartType.cxx | 12 +- .../model/template/ChartTypeTemplate.cxx | 1 + .../source/model/template/FunnelChartType.cxx | 47 ++++++ .../source/model/template/FunnelChartType.hxx | 5 + chart2/source/view/charttypes/BarChart.cxx | 11 +- chart2/source/view/charttypes/FunnelChart.cxx | 139 ++++++++++++++++++ chart2/source/view/charttypes/FunnelChart.hxx | 47 ++++++ .../source/view/charttypes/VSeriesPlotter.cxx | 5 +- .../drawingml/chart/typegroupconverter.hxx | 3 + oox/source/drawingml/chart/axisconverter.cxx | 10 +- .../drawingml/chart/typegroupconverter.cxx | 48 +++--- 14 files changed, 297 insertions(+), 45 deletions(-) create mode 100644 chart2/source/view/charttypes/FunnelChart.cxx create mode 100644 chart2/source/view/charttypes/FunnelChart.hxx diff --git a/chart2/Library_chart2.mk b/chart2/Library_chart2.mk index e9e40e2c8daa..69f041939f16 100644 --- a/chart2/Library_chart2.mk +++ b/chart2/Library_chart2.mk @@ -254,6 +254,7 @@ $(eval $(call gb_Library_add_exception_objects,chart2,\ chart2/source/view/charttypes/BubbleChart \ chart2/source/view/charttypes/CandleStickChart \ chart2/source/view/charttypes/CategoryPositionHelper \ + chart2/source/view/charttypes/FunnelChart \ chart2/source/view/charttypes/NetChart \ chart2/source/view/charttypes/PieChart \ chart2/source/view/charttypes/UnsupportedChart \ diff --git a/chart2/qa/extras/chart2import2.cxx b/chart2/qa/extras/chart2import2.cxx index 1e83d03d7c56..1ca923d983d0 100644 --- a/chart2/qa/extras/chart2import2.cxx +++ b/chart2/qa/extras/chart2import2.cxx @@ -497,6 +497,19 @@ CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testStockChartShiftedCategoryPosition) CPPUNIT_ASSERT(aScaleData.ShiftedCategoryPosition); } +CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testFunnelChartShiftedCategoryPosition) +{ + loadFromFile(u"xlsx/color_funnel.xlsx"); + uno::Reference xChartDoc = getChartDocFromSheet(0); + CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is()); + + Reference xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0); + CPPUNIT_ASSERT(xAxis.is()); + + chart2::ScaleData aScaleData = xAxis->getScaleData(); + CPPUNIT_ASSERT(aScaleData.ShiftedCategoryPosition); +} + CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf133376) { // FIXME: the DPI check should be removed when either (1) the test is fixed to work with diff --git a/chart2/qa/extras/data/xlsx/color_funnel.xlsx b/chart2/qa/extras/data/xlsx/color_funnel.xlsx index 80d16d5d1b84c9b7e576cc064a1871e9388acb75..80b1a746343dff36f7c64b3c8fd11aab8e038664 100644 GIT binary patch literal 10828 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 z zeQ3yCz~tvIzEBn94ztcZ)dj@m#d!K)uywwJ_;Y@z{B@(-1PFG{=4aWS1ia ztK^psOvY^12r6-vb?CXzn7inM(nME}By=ocQcX$H8yf`Y%q{>1qp!$Ogn z4@tkuqBr6`w;4eri=Yo{O+;X&dxuPrASV|Fv!VxvIWkMx5{ajKXzYga6}S3$+_o6F z$2}lGB-?nL-rC$suI!W5miYwDMGr|OrDk-APOzkd!u6*cXn`WZG*Uyo`=82aEqer| z{aIcH`0<6}gGieGfrq0beiMx)3fux!Fvv>hMS2Tb2Kg(i|8`+n5GH7LW8E(P`{4lM^%QV{Cos3P8z5@oA?v|p4&LLm?9|V&~ zWz)0&gN6Q^|0DiS$fIwfYxmzc@7Ke7m*3v6Bzn}WlMycH!~+I!_oY5aUL>6W!tZ0< z6>#__Szd)98OuyvnLQQVVkGzDkKH@}hD}w?seEJTc)0I*;M;Kk;++1r?fWYSZ~fMT zl?OdZ{1{FHc}L0+pKtN^zEoCM;}Syi`3u+~fH!RC{__pRpLvcKudr{qL4U$4eue;b z$6+nFcyp(Hh=mt7_5nu}*%Z%Dqfp(t@Vn9AHed-u`&}ZXHe4T$qoOS6{bIut;sNcO zF8(R2lK#%-1DlK9M}!x7Q0PF9v_Es}`B#sK@h^G2mPD?y<#Xj7>9Ivy_tq_BPojoN za9ZB6Xi8k!v?eMpgkf!cZhk&nx7E{A9I4vkKq)%5@nYHD8qvsZTqV>3sDza(zY`b$ z=Ai38HJHxFrU}n{bM=Dq_;a1kT%-{wDGPQ|02CKy|`D+C=6X` z2|)^eAu|Dj_P8l@P>RUwd@W%>2Ag}7hMw(cewCBiQIe< zfPMs9FM(n5m3ldgQ8{P|b1Np$SnUv6ookuRr5isg^%NwAB%ki{eqKW?y6xgZlwqfG zW#Z&XNvMzC@SeAHrAb&BTC@SOd=;kn7YuUB&%R_e%jUZ^DI?*`P`>dIGg%~K6zi8( z^PXGyGL-I3jCyWCU=gs=2JJat(;7>B3#QIu+`;3oS7sm2b6iGwXfcQbSvjXOAbn7& zW|690<4vH?nC^AL5DGIruPR#oq-O@|CBZzimO3XC z-=@-KfgMnmvqdzXDM@0ZR9U<&;D5`B8$ln`sV_|0xXj+niyd#B-=ue?#R-GoTI0^Xh)>&gNn<#M+iy~q4Bp6i z?qT7&2XzW}Tp6p<1=l+F$kH@`n~&@kN=s0xDJF2)qkFmOwB~?Z?fhpNn$97Ne&p=4hCWzI7h=zSF{B{e-=l7nA8ETQz(0P z$c9G4XJLGTvV^+XI`HG|i%yTGN5e!vSK2>A{LRME{-x8u>NUS?yE)m?dXWgJw>7jI zvLNnj$_!QHl+{A9K}oHIn+MZcIk2(r64|QfoXn}BvZKNo_iE;*r?U+L$3gXnTu8>N zBwk63&+$_-R48`k?AqP7F$BrJ8zLc7IzCG)IR$!KnmKfN5Rrn$XL{_~U6yEe(4YXS zWR{~I4jk~A6pF?kxrz5F9+Bu!(j@Ot?YLV>(`qCUWxlvzs8lY*{-oR-S&xs#4lxgb zr4}XGOwV;X3~NA77&9sIaq$;#uY?=ET`AQ4Myk*peI58NC%WM?5cSy&Xe;-^CM=7H z9EMhP$=G30jDV@tOnK3Dlt%)^C*Tw$w^Z$_R;dC?%-guaW_cL(+`EA(j~OX^7jb-i zRJ%G&I`MeH3?%yY2GN8@87c=6&F&N#F*63}W}kw}V*Nz3pz86R!TGPH6^VqL)>{m! zoX5i0#>VAS)n~B{g^h)5$5yJ%5NLE3x}yejd3*Xa2hahvC;se>%FRWd<5|_;udB}< zPxuObJwE`pAg?*B5*`<>VXtTMt=CmsVb7f|`DFaBsLBr8?hTDd+#%V=YHfPXn?K($ znL0#!Zq+e{iDs(ykCr2P7+o@Nza`H2QG_3#NS2!WGMKQ&O~lL&o9b0!$*6_qOK zk9Rz4XhTpc{;idW91++p5fFH|k7)((KWr|DLoifCoy~x~-B=|D~4t))v-w_P_MfV@w*s_qp4< zUK~x}4XlLWUwH}HF8ZcswWZ(>blwa#!^H{)?AZ-#F!@6 z5hysvzx#uRbh{{nqV$Syl0uv}=bBI>cthTiH(HTPV#n+p+*uc3z{DSR*kjK^vJB zZcPZF%kTV37!?IBqOvp6*3;`zFLy=Cif)CGLh@sXvRVj8GljlKe%Iimtt$ij@E6Y> zWDZdUYatrDC|QXgL+kGba@-uEC1Ac^NK1|b@9G~K0PzY0*=Z>|Gd~&x@A#`7hNIXT zt9+UB0&DV@wlrt65+}s_#K*FsN)kux)F&YYaL?yxLV-67gmGOl^!PTodJ>O-4aX=Z zjhsZhbKHk9Sry@6;E|8dJS)F9m?!R_E>eAP=1`jFZMMuqM{B_di3)IuPP;`j0x)psCXDU+TwiW9 zaNA71&IJeYsB2){U@3EXIScrkar~tx!&Aj+BOVm*-JS2oB2#JOG9-X#FoHyqZQ4Nl z=y1wQHAi`*OG^iFmBRa7QsW#XxbzJ=s9e?7w6EiIF=tEb87FKr6C~`D;z47CQJQQ9 z0&g~mC^BV{^|X|zd}cOkscd6 zGH)=Fw=24N31MjnNl0^~`)=`zP-??fWrCh>ATq6|P@IKN5q!e#4 z6yCS43>ZDxEzf@PIulk4U-UDc&iOzpL=3ACG?s2I^CE;t%}EswIiC&bw^|4s2aqV0 z!w^rInRYa^8#0t|>oIz;x~{n}{P3mx3JX2M^2t6SxcoP@ompT_Y88a-VClwc0qa<8 zV$m$lpkHWhU7?-=Bxpx2Zl&GqVtP$r0h2sHfSco5cAMoQWUP=q4FT=+Nr_V=hK`RV zGfj1sbNjvrt?A>MdTn z;=j$n(cZz@@|RpkDxT{PXaVcX`B3wVN{}%V)nOdmO4rdVwZQh)ebQkbZlu1)kwJuf zGLyUyLSK>QkQ7gsiO`46Pirm0WEM?X_O`DoGes=p;hwIpKRx zF)Jf@&~BSvO;AlR?Z*eyLD3$_-#LM^`jM!F#Y{?llN@aB-Nna>erQ31= z3DU5uXNNFu1QvYU419mGEi#F0N9Bk{6f*qd(o=e4(u)kyYA@|c!CYDrveU zOsmQ&DtiEwI*P<3inMF_#?NaC{~Oe@Ntl<8LkClI*&$$>!iFxrg*OP6tbk=RqK%8m zosU0c<v#NIf3N3*_(13mq zZoQsfdKujMH~sHpTym2C#(6y`@-pl6o6w*C^FNjN{}}}PbMfmY<%@Rxrt{~l_j&Q3 zlO=xwyl#fP^fG@_)pN@H4Dh?N`RCHt?Sz*K;%^FnM*3&tf9sw63HZ8L`O<~>O#ujh z0{-qv{E70q==bk!xuE<)`EQ}`KLN?>a^J7uRY-v7jS9ihIYN54rE_cg}vWa&?Y z*TLUQB>S5#p1bC+cJVu|{S)GKVDu6t{-y_2=@} zH_^)_{+p_vkLSO)&p%iC|F!y+zyG)0K|Sl-e^=}O_wM==;I(J@H^AC6!2jctkdp*| Uc0xcv$j>h?Fd(2h@|UOo1K??+qyPW_ literal 13156 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 WA%MR5Tc|?@@BxcT$PB;z_5T2#x8WE7 diff --git a/chart2/source/model/template/ChartType.cxx b/chart2/source/model/template/ChartType.cxx index d3f4bee9f4f0..e4eaf091eaef 100644 --- a/chart2/source/model/template/ChartType.cxx +++ b/chart2/source/model/template/ChartType.cxx @@ -444,14 +444,12 @@ bool ChartType::isSupportingOverlapAndGapWidthProperties(sal_Int32 nDimensionCou return false; OUString aChartTypeName = getChartType(); - if (aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN)) - return true; - - if (aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BAR)) - return true; - - if (aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_HISTOGRAM)) + if (aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN) || + aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BAR) || + aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_FUNNEL) || + aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_HISTOGRAM)) { return true; + } return false; } diff --git a/chart2/source/model/template/ChartTypeTemplate.cxx b/chart2/source/model/template/ChartTypeTemplate.cxx index 1447159641f7..c70a019eb968 100644 --- a/chart2/source/model/template/ChartTypeTemplate.cxx +++ b/chart2/source/model/template/ChartTypeTemplate.cxx @@ -585,6 +585,7 @@ void ChartTypeTemplate::adaptScales( m_aServiceName.indexOf("Column") != -1 || (m_aServiceName.indexOf("Bar") != -1 && !m_aServiceName.indexOf("BarOfPie")) || + m_aServiceName.indexOf("Funnel") != -1 || m_aServiceName.endsWith("Close"); } bool bSupportsDates = xChartType.is() ? xChartType->isSupportingDateAxis(nDimensionX) : true; diff --git a/chart2/source/model/template/FunnelChartType.cxx b/chart2/source/model/template/FunnelChartType.cxx index 0e54470c26f2..0e933a762a99 100644 --- a/chart2/source/model/template/FunnelChartType.cxx +++ b/chart2/source/model/template/FunnelChartType.cxx @@ -9,8 +9,15 @@ #include "FunnelChartType.hxx" #include +#include +#include +#include +#include #include + #include +#include + #include namespace com::sun::star::uno @@ -110,6 +117,46 @@ void FunnelChartType::GetDefaultValue(sal_Int32 nHandle, uno::Any& rAny) const rAny = (*aFound).second; } +rtl::Reference<::chart::BaseCoordinateSystem> +FunnelChartType::createCoordinateSystem2(sal_Int32 DimensionCount) +{ + rtl::Reference xResult + = new CartesianCoordinateSystem(DimensionCount); + + for (sal_Int32 i = 0; i < DimensionCount; i++) + { + rtl::Reference xAxis = xResult->getAxisByDimension2(i, MAIN_AXIS_INDEX); + if (!xAxis.is()) + continue; + + chart2::ScaleData aScaleData = xAxis->getScaleData(); + + if (i == 0) // X-axis + { + // Set up X-axis specifically for histogram bins + aScaleData.AxisType = chart2::AxisType::CATEGORY; + aScaleData.AutoDateAxis = false; + aScaleData.ShiftedCategoryPosition = true; + aScaleData.Orientation = chart2::AxisOrientation_MATHEMATICAL; + + // Clear any existing scaling/categories + AxisHelper::removeExplicitScaling(aScaleData); + aScaleData.Categories.clear(); + } + else if (i == 1) // Y-axis + { + aScaleData.AxisType = chart2::AxisType::REALNUMBER; + aScaleData.ShiftedCategoryPosition = false; + aScaleData.Orientation = chart2::AxisOrientation_MATHEMATICAL; + aScaleData.Scaling = AxisHelper::createLinearScaling(); + } + + xAxis->setScaleData(aScaleData); + } + + return xResult; +} + ::cppu::IPropertyArrayHelper& SAL_CALL FunnelChartType::getInfoHelper() { return StaticFunnelChartTypeInfoHelper(); diff --git a/chart2/source/model/template/FunnelChartType.hxx b/chart2/source/model/template/FunnelChartType.hxx index a4bde4768081..f67a9a8561a0 100644 --- a/chart2/source/model/template/FunnelChartType.hxx +++ b/chart2/source/model/template/FunnelChartType.hxx @@ -8,7 +8,9 @@ */ #pragma once +#include #include +#include namespace chart { @@ -27,6 +29,9 @@ public: private: explicit FunnelChartType(const FunnelChartType& rOther); + virtual rtl::Reference<::chart::BaseCoordinateSystem> + createCoordinateSystem2(sal_Int32 DimensionCount) override; + // ____ XChartType ____ virtual OUString SAL_CALL getChartType() override; virtual css::uno::Sequence SAL_CALL getSupportedPropertyRoles() override; diff --git a/chart2/source/view/charttypes/BarChart.cxx b/chart2/source/view/charttypes/BarChart.cxx index f3c4b9eada38..3d8f4ee8db78 100644 --- a/chart2/source/view/charttypes/BarChart.cxx +++ b/chart2/source/view/charttypes/BarChart.cxx @@ -913,16 +913,13 @@ void BarChart::doXSlot( } else //m_nDimension!=3 { - drawing::Position3D aLeftUpperPoint( fLogicX-fLogicBarWidth/2.0,fUpperYValue,fLogicZ ); - drawing::Position3D aRightUpperPoint( fLogicX+fLogicBarWidth/2.0,fUpperYValue,fLogicZ ); std::vector> aPoly { { // inner vector - drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ), - drawing::Position3D( fLogicX+fLogicBarWidth/2.0,fLowerYValue,fLogicZ), - aRightUpperPoint, - aLeftUpperPoint, - drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ) + drawing::Position3D( fLogicX-fLogicBarWidth/2.0, fLowerYValue, fLogicZ), + drawing::Position3D( fLogicX+fLogicBarWidth/2.0, fLowerYValue, fLogicZ), + drawing::Position3D( fLogicX+fLogicBarWidth/2.0, fUpperYValue, fLogicZ), + drawing::Position3D( fLogicX-fLogicBarWidth/2.0, fUpperYValue, fLogicZ ) } }; pPosHelper->transformScaledLogicToScene( aPoly ); diff --git a/chart2/source/view/charttypes/FunnelChart.cxx b/chart2/source/view/charttypes/FunnelChart.cxx new file mode 100644 index 000000000000..c75d3a2ff625 --- /dev/null +++ b/chart2/source/view/charttypes/FunnelChart.cxx @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "FunnelChart.hxx" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace chart +{ +using namespace ::com::sun::star; + +FunnelChart::FunnelChart(const rtl::Reference& xChartTypeModel, + sal_Int32 nDimensionCount) + : BarChart(xChartTypeModel, nDimensionCount) +{ + // We only support 2 dimensional funnel charts + assert(nDimensionCount == 2 && "FunnelChart only supports 2D charts"); + + // Runtime check for all builds + if (nDimensionCount != 2) + { + // Log a warning or throw an exception if appropriate + SAL_WARN("chart2", "FunnelChart created with invalid dimension count. Forcing 2D."); + } + + PlotterBase::m_pPosHelper = &m_aMainPosHelper; + VSeriesPlotter::m_pMainPosHelper = &m_aMainPosHelper; + + try + { + if (m_xChartTypeModel.is()) + m_xChartTypeModel->getPropertyValue(u"GapwidthSequence"_ustr) >>= m_aGapwidthSequence; + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("chart2", ""); + } +} + +FunnelChart::~FunnelChart() {} + +#if 0 +PlottingPositionHelper& FunnelChart::getPlottingPositionHelper(sal_Int32 nAxisIndex) const +{ + PlottingPositionHelper& rPosHelper = VSeriesPlotter::getPlottingPositionHelper(nAxisIndex); + BarPositionHelper* pFunnelPosHelper = dynamic_cast(&rPosHelper); + if (pFunnelPosHelper && nAxisIndex >= 0) + { + if (nAxisIndex < m_aGapwidthSequence.getLength()) + pFunnelPosHelper->setOuterDistance(m_aGapwidthSequence[nAxisIndex] / 100.0); + } + + return rPosHelper; +} + +css::awt::Point FunnelChart::getLabelScreenPositionAndAlignment( + LabelAlignment& rAlignment, sal_Int32 nLabelPlacement, double fScaledX, + double fScaledLowerYValue, double fScaledUpperYValue, double fBaseValue, + BarPositionHelper const* pPosHelper) const +{ + double fY = fScaledUpperYValue; + bool bReverse = !pPosHelper->isMathematicalOrientationY(); + bool bNormalOutside = (!bReverse == (fBaseValue < fScaledUpperYValue)); + + switch (nLabelPlacement) + { + case css::chart::DataLabelPlacement::TOP: + fY = bReverse ? fScaledLowerYValue : fScaledUpperYValue; + rAlignment = LABEL_ALIGN_TOP; + break; + case css::chart::DataLabelPlacement::BOTTOM: + fY = bReverse ? fScaledUpperYValue : fScaledLowerYValue; + rAlignment = LABEL_ALIGN_BOTTOM; + break; + case css::chart::DataLabelPlacement::OUTSIDE: + fY = (fBaseValue < fScaledUpperYValue) ? fScaledUpperYValue : fScaledLowerYValue; + rAlignment = bNormalOutside ? LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM; + break; + case css::chart::DataLabelPlacement::INSIDE: + fY = (fBaseValue < fScaledUpperYValue) ? fScaledUpperYValue : fScaledLowerYValue; + rAlignment = bNormalOutside ? LABEL_ALIGN_BOTTOM : LABEL_ALIGN_TOP; + break; + case css::chart::DataLabelPlacement::CENTER: + fY -= (fScaledUpperYValue - fScaledLowerYValue) / 2.0; + rAlignment = LABEL_ALIGN_CENTER; + break; + default: + OSL_FAIL("this label alignment is not implemented yet"); + break; + } + + css::drawing::Position3D aScenePosition3D( + pPosHelper->transformScaledLogicToScene(fScaledX, fY, 0.0, true)); + return LabelPositionHelper(2, m_xLogicTarget).transformSceneToScreenPosition(aScenePosition3D); +} +#endif + +void FunnelChart::createShapes() +{ + if (m_aZSlots.empty()) //No data series Available + return; + + OSL_ENSURE(m_xLogicTarget.is() && m_xFinalTarget.is(), + "FunnelChart is not properly initialized"); // Ensuring chart is properly initialized + if (!(m_xLogicTarget.is() && m_xFinalTarget.is())) + return; + + if (!m_pMainPosHelper) + m_pMainPosHelper = new BarPositionHelper(); + + BarChart::createShapes(); +} + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/chart2/source/view/charttypes/FunnelChart.hxx b/chart2/source/view/charttypes/FunnelChart.hxx new file mode 100644 index 000000000000..73dec3868732 --- /dev/null +++ b/chart2/source/view/charttypes/FunnelChart.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "BarChart.hxx" +#include "BarPositionHelper.hxx" + +#include + +namespace chart +{ +class FunnelChart : public BarChart +{ +public: + FunnelChart() = delete; + + FunnelChart(const rtl::Reference& xChartTypeModel, sal_Int32 nDimensionCount); + virtual ~FunnelChart() override; + + virtual void createShapes() override; + +private: +#if 0 + virtual PlottingPositionHelper& getPlottingPositionHelper(sal_Int32 nAxisIndex) const override; + + css::awt::Point getLabelScreenPositionAndAlignment(LabelAlignment& rAlignment, + sal_Int32 nLabelPlacement, double fScaledX, + double fScaledLowerYValue, + double fScaledUpperYValue, double fBaseValue, + BarPositionHelper const* pPosHelper) const; +#endif + +private: + BarPositionHelper m_aMainPosHelper; + css::uno::Sequence m_aGapwidthSequence; +}; + +} // namespace chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/chart2/source/view/charttypes/VSeriesPlotter.cxx b/chart2/source/view/charttypes/VSeriesPlotter.cxx index 226f83c650ec..299ce8e4a2c9 100644 --- a/chart2/source/view/charttypes/VSeriesPlotter.cxx +++ b/chart2/source/view/charttypes/VSeriesPlotter.cxx @@ -59,6 +59,7 @@ #include "CandleStickChart.hxx" #include "BubbleChart.hxx" #include "NetChart.hxx" +#include "FunnelChart.hxx" #include "UnsupportedChart.hxx" #include #include @@ -2901,8 +2902,8 @@ VSeriesPlotter* VSeriesPlotter::createSeriesPlotter( pRet = new NetChart(xChartTypeModel,nDimensionCount,false,std::make_unique()); else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) ) pRet = new CandleStickChart(xChartTypeModel,nDimensionCount); - else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_AREA) ) - pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true); + else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_FUNNEL) ) + pRet = new FunnelChart(xChartTypeModel,nDimensionCount); else pRet = new UnsupportedChart(xChartTypeModel); return pRet; diff --git a/oox/inc/drawingml/chart/typegroupconverter.hxx b/oox/inc/drawingml/chart/typegroupconverter.hxx index c013153ec0a3..9ac13baf1f1e 100644 --- a/oox/inc/drawingml/chart/typegroupconverter.hxx +++ b/oox/inc/drawingml/chart/typegroupconverter.hxx @@ -106,6 +106,9 @@ struct TypeGroupInfo bool mbSwappedAxesSet; /// True = X axis and Y axis are swapped. bool mbSupportsStacking; /// True = data points can be stacked on each other. bool mbPictureOptions; /// True = bitmaps support options from c:pictureOptions. + bool mbShiftedCatPos; /// True = default category axis position is shifted + /// This can get overridden in AxisConverter::convertFromModel() + bool mb3dShiftedCatPos; /// True = default category axis position is shifted for 3d charts }; const TypeGroupInfo& GetTypeGroupInfo( TypeId eType ); diff --git a/oox/source/drawingml/chart/axisconverter.cxx b/oox/source/drawingml/chart/axisconverter.cxx index 5d43fba3838d..d67744f3a089 100644 --- a/oox/source/drawingml/chart/axisconverter.cxx +++ b/oox/source/drawingml/chart/axisconverter.cxx @@ -333,14 +333,14 @@ void AxisConverter::convertFromModel(const Reference& rxCoord } /* set default ShiftedCategoryPosition values for some charttype, because the XML can contain wrong CrossBetween value, if came from MSO */ - if( rTypeGroups.front()->is3dChart() && (rTypeInfo.meTypeId == TYPEID_BAR || rTypeInfo.meTypeId == TYPEID_HORBAR || rTypeInfo.meTypeId == TYPEID_STOCK) ) - aScaleData.ShiftedCategoryPosition = true; - else if( rTypeInfo.meTypeId == TYPEID_RADARLINE || rTypeInfo.meTypeId == TYPEID_RADARAREA ) + if( rTypeGroups.front()->is3dChart()) { + aScaleData.ShiftedCategoryPosition = rTypeInfo.mb3dShiftedCatPos; + } else if( rTypeInfo.meTypeId == TYPEID_RADARLINE || rTypeInfo.meTypeId == TYPEID_RADARAREA ) aScaleData.ShiftedCategoryPosition = false; else if( pCrossingAxis->mnCrossBetween != -1 ) /*because of backwards compatibility*/ aScaleData.ShiftedCategoryPosition = pCrossingAxis->mnCrossBetween == XML_between; - else if( rTypeInfo.meTypeCategory == TYPECATEGORY_BAR || rTypeInfo.meTypeId == TYPEID_LINE || rTypeInfo.meTypeId == TYPEID_STOCK ) - aScaleData.ShiftedCategoryPosition = true; + else + aScaleData.ShiftedCategoryPosition = rTypeInfo.mbShiftedCatPos; } else { diff --git a/oox/source/drawingml/chart/typegroupconverter.cxx b/oox/source/drawingml/chart/typegroupconverter.cxx index b3343c20ec38..7bcd8a1955e8 100644 --- a/oox/source/drawingml/chart/typegroupconverter.cxx +++ b/oox/source/drawingml/chart/typegroupconverter.cxx @@ -91,33 +91,33 @@ namespace csscd = css::chart::DataLabelPlacement; const TypeGroupInfo spTypeInfos[] = { - // type-id type-category service varied-point-color default label pos polar area2d 1stvis xcateg swap stack picopt - { TYPEID_BAR, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, true, true }, - { TYPEID_HORBAR, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, true, true, true }, - { TYPEID_HISTO, TYPECATEGORY_HISTO, SERVICE_CHART2_HISTO, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, true, true }, - { TYPEID_LINE, TYPECATEGORY_LINE, SERVICE_CHART2_LINE, VARPOINTMODE_SINGLE, csscd::RIGHT, false, false, false, true, false, true, false }, - { TYPEID_AREA, TYPECATEGORY_LINE, SERVICE_CHART2_AREA, VARPOINTMODE_NONE, csscd::CENTER, false, true, false, true, false, true, false }, - { TYPEID_STOCK, TYPECATEGORY_LINE, SERVICE_CHART2_CANDLE, VARPOINTMODE_NONE, csscd::RIGHT, false, false, false, true, false, true, false }, - { TYPEID_RADARLINE, TYPECATEGORY_RADAR, SERVICE_CHART2_NET, VARPOINTMODE_SINGLE, csscd::OUTSIDE, true, false, false, true, false, false, false }, - { TYPEID_RADARAREA, TYPECATEGORY_RADAR, SERVICE_CHART2_FILLEDNET, VARPOINTMODE_NONE, csscd::OUTSIDE, true, true, false, true, false, false, false }, - { TYPEID_PIE, TYPECATEGORY_PIE, SERVICE_CHART2_PIE, VARPOINTMODE_MULTI, csscd::AVOID_OVERLAP, true, true, true, true, false, false, false }, - { TYPEID_DOUGHNUT, TYPECATEGORY_PIE, SERVICE_CHART2_PIE, VARPOINTMODE_MULTI, csscd::AVOID_OVERLAP, true, true, false, true, false, false, false }, - { TYPEID_OFPIE, TYPECATEGORY_PIE, SERVICE_CHART2_PIE, VARPOINTMODE_MULTI, csscd::AVOID_OVERLAP, true, true, true, true, false, false, false }, - { TYPEID_SCATTER, TYPECATEGORY_SCATTER, SERVICE_CHART2_SCATTER, VARPOINTMODE_SINGLE, csscd::RIGHT, false, false, false, false, false, false, false }, - { TYPEID_BUBBLE, TYPECATEGORY_SCATTER, SERVICE_CHART2_BUBBLE, VARPOINTMODE_SINGLE, csscd::RIGHT, false, true, false, false, false, false, false }, - { TYPEID_SURFACE, TYPECATEGORY_SURFACE, SERVICE_CHART2_SURFACE, VARPOINTMODE_NONE, csscd::RIGHT, false, true, false, true, false, false, false }, - { TYPEID_FUNNEL, TYPECATEGORY_FUNNEL, SERVICE_CHART2_FUNNEL, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, false, false }, - { TYPEID_BOXWHISKER,TYPECATEGORY_BOXWHISKER, SERVICE_CHART2_BOXWHISKER, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, false, false }, - { TYPEID_CLUSTEREDCOLUMN,TYPECATEGORY_CLUSTEREDCOLUMN, SERVICE_CHART2_CLUSTEREDCOLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, false, false }, - { TYPEID_PARETOLINE,TYPECATEGORY_PARETOLINE, SERVICE_CHART2_PARETOLINE, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, false, false }, - { TYPEID_REGIONMAP, TYPECATEGORY_REGIONMAP, SERVICE_CHART2_REGIONMAP, VARPOINTMODE_NONE, csscd::OUTSIDE, false, true, false, true, false, false, false }, - { TYPEID_SUNBURST, TYPECATEGORY_SUNBURST, SERVICE_CHART2_SUNBURST, VARPOINTMODE_MULTI, csscd::OUTSIDE, false, true, false, true, false, false, false }, - { TYPEID_TREEMAP, TYPECATEGORY_TREEMAP, SERVICE_CHART2_TREEMAP, VARPOINTMODE_MULTI, csscd::OUTSIDE, false, true, false, true, false, false, false }, - { TYPEID_WATERFALL, TYPECATEGORY_WATERFALL, SERVICE_CHART2_WATERFALL, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, false, false } + // type-id type-category service varied-point-color default label pos polar area2d 1stvis xcateg swap stack picopt shftcat 3dshftcat + { TYPEID_BAR, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, true, true, true, true }, + { TYPEID_HORBAR, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, true, true, true, true, true }, + { TYPEID_HISTO, TYPECATEGORY_HISTO, SERVICE_CHART2_HISTO, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, true, true, false, false}, + { TYPEID_LINE, TYPECATEGORY_LINE, SERVICE_CHART2_LINE, VARPOINTMODE_SINGLE, csscd::RIGHT, false, false, false, true, false, true, false, true, false}, + { TYPEID_AREA, TYPECATEGORY_LINE, SERVICE_CHART2_AREA, VARPOINTMODE_NONE, csscd::CENTER, false, true, false, true, false, true, false, false, false}, + { TYPEID_STOCK, TYPECATEGORY_LINE, SERVICE_CHART2_CANDLE, VARPOINTMODE_NONE, csscd::RIGHT, false, false, false, true, false, true, false, true, true }, + { TYPEID_RADARLINE, TYPECATEGORY_RADAR, SERVICE_CHART2_NET, VARPOINTMODE_SINGLE, csscd::OUTSIDE, true, false, false, true, false, false, false, false, false}, + { TYPEID_RADARAREA, TYPECATEGORY_RADAR, SERVICE_CHART2_FILLEDNET, VARPOINTMODE_NONE, csscd::OUTSIDE, true, true, false, true, false, false, false, false, false}, + { TYPEID_PIE, TYPECATEGORY_PIE, SERVICE_CHART2_PIE, VARPOINTMODE_MULTI, csscd::AVOID_OVERLAP, true, true, true, true, false, false, false, false, false}, + { TYPEID_DOUGHNUT, TYPECATEGORY_PIE, SERVICE_CHART2_PIE, VARPOINTMODE_MULTI, csscd::AVOID_OVERLAP, true, true, false, true, false, false, false, false, false}, + { TYPEID_OFPIE, TYPECATEGORY_PIE, SERVICE_CHART2_PIE, VARPOINTMODE_MULTI, csscd::AVOID_OVERLAP, true, true, true, true, false, false, false, false, false}, + { TYPEID_SCATTER, TYPECATEGORY_SCATTER, SERVICE_CHART2_SCATTER, VARPOINTMODE_SINGLE, csscd::RIGHT, false, false, false, false, false, false, false, false, false}, + { TYPEID_BUBBLE, TYPECATEGORY_SCATTER, SERVICE_CHART2_BUBBLE, VARPOINTMODE_SINGLE, csscd::RIGHT, false, true, false, false, false, false, false, false, false}, + { TYPEID_SURFACE, TYPECATEGORY_SURFACE, SERVICE_CHART2_SURFACE, VARPOINTMODE_NONE, csscd::RIGHT, false, true, false, true, false, false, false, false, false}, + { TYPEID_FUNNEL, TYPECATEGORY_FUNNEL, SERVICE_CHART2_FUNNEL, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, true, false, false, true, false}, + { TYPEID_BOXWHISKER,TYPECATEGORY_BOXWHISKER, SERVICE_CHART2_BOXWHISKER, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, false, false, false, false}, + { TYPEID_CLUSTEREDCOLUMN,TYPECATEGORY_CLUSTEREDCOLUMN, SERVICE_CHART2_CLUSTEREDCOLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, false, false, false, false}, + { TYPEID_PARETOLINE,TYPECATEGORY_PARETOLINE, SERVICE_CHART2_PARETOLINE, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, false, false, false, false}, + { TYPEID_REGIONMAP, TYPECATEGORY_REGIONMAP, SERVICE_CHART2_REGIONMAP, VARPOINTMODE_NONE, csscd::OUTSIDE, false, true, false, true, false, false, false, false, false}, + { TYPEID_SUNBURST, TYPECATEGORY_SUNBURST, SERVICE_CHART2_SUNBURST, VARPOINTMODE_MULTI, csscd::OUTSIDE, false, true, false, true, false, false, false, false, false}, + { TYPEID_TREEMAP, TYPECATEGORY_TREEMAP, SERVICE_CHART2_TREEMAP, VARPOINTMODE_MULTI, csscd::OUTSIDE, false, true, false, true, false, false, false, false, false}, + { TYPEID_WATERFALL, TYPECATEGORY_WATERFALL, SERVICE_CHART2_WATERFALL, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, false, false, false, false} }; const TypeGroupInfo saUnknownTypeInfo = - { TYPEID_UNKNOWN, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, true, true }; + { TYPEID_UNKNOWN, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, true, true, false, false}; const TypeGroupInfo& lclGetTypeInfoFromTypeId( TypeId eTypeId ) {