/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
::chart::XTransformation2* PlottingPositionHelper::getTransformationScaledLogicToScene() const
{ //this is a standard transformation for a cartesian coordinate system
//transformation from 2) to 4) //@todo 2) and 4) need an ink to a document
//we need to apply this transformation to each geometric object because of a bug/problem //of the old drawing layer (the UNO_NAME_3D_EXTRUDE_DEPTH is an integer value instead of a double ) if(!m_xTransformationLogicToScene)
{
::basegfx::B3DHomMatrix aMatrix; double MinX = getLogicMinX(); double MinY = getLogicMinY(); double MinZ = getLogicMinZ(); double MaxX = getLogicMaxX(); double MaxY = getLogicMaxY(); double MaxZ = getLogicMaxZ();
awt::Point PlottingPositionHelper::transformSceneToScreenPosition( const drawing::Position3D& rScenePosition3D
, const rtl::Reference<SvxShapeGroupAnyD>& xSceneTarget
, sal_Int32 nDimensionCount )
{ //@todo would like to have a cheaper method to do this transformation
awt::Point aScreenPoint( static_cast<sal_Int32>(rScenePosition3D.PositionX), static_cast<sal_Int32>(rScenePosition3D.PositionY) );
//transformation from scene to screen (only necessary for 3D): if(nDimensionCount==3)
{ //create 3D anchor shape
tPropertyNameMap aDummyPropertyNameMap;
rtl::Reference<Svx3DExtrudeObject> xShape3DAnchor = ShapeFactory::createCube( xSceneTarget
, rScenePosition3D,drawing::Direction3D(1,1,1)
, 0, nullptr, aDummyPropertyNameMap); //get 2D position from xShape3DAnchor
aScreenPoint = xShape3DAnchor->getPosition();
xSceneTarget->remove(xShape3DAnchor);
} return aScreenPoint;
}
void PlottingPositionHelper::transformScaledLogicToScene( drawing::PolyPolygonShape3D& rPolygon ) const
{
drawing::Position3D aScenePosition; auto SequenceXRange = asNonConstRange(rPolygon.SequenceX); auto SequenceYRange = asNonConstRange(rPolygon.SequenceY); auto SequenceZRange = asNonConstRange(rPolygon.SequenceZ); for( sal_Int32 nS = rPolygon.SequenceX.getLength(); nS--;)
{ auto xValuesRange = asNonConstRange(SequenceXRange[nS]); auto yValuesRange = asNonConstRange(SequenceYRange[nS]); auto zValuesRange = asNonConstRange(SequenceZRange[nS]); for( sal_Int32 nP = SequenceXRange[nS].getLength(); nP--; )
{ double& fX = xValuesRange[nP]; double& fY = yValuesRange[nP]; double& fZ = zValuesRange[nP];
aScenePosition = transformScaledLogicToScene( fX,fY,fZ,true );
fX = aScenePosition.PositionX;
fY = aScenePosition.PositionY;
fZ = aScenePosition.PositionZ;
}
}
}
// tdf#123504: both 0 and 360 are valid and different values here! while (fWidthAngleDegree < 0.0)
fWidthAngleDegree += 360.0; while (fWidthAngleDegree > 360.0)
fWidthAngleDegree -= 360.0;
return fWidthAngleDegree;
}
//This method does a lot of computation for understanding which scale to //utilize and if reverse orientation should be used. Indeed, for a pie or donut, //the final result is as simple as multiplying by 360 and adding //`m_fAngleDegreeOffset`. double PolarPlottingPositionHelper::transformToAngleDegree( double fLogicValueOnAngleAxis, bool bDoScaling ) const
{ double fRet=0.0;
fRet = m_fAngleDegreeOffset
+ fAxisAngleScaleDirection*(fScaledLogicAngleValue-MinAngleValue)*360.0
/fabs(MaxAngleValue-MinAngleValue); // tdf#123504: both 0 and 360 are valid and different values here! while (fRet > 360.0)
fRet -= 360.0; while (fRet < 0)
fRet += 360.0; return fRet;
}
/** * Given a value in the radius axis scale range, it returns, in the simplest * case (that is when `m_fRadiusOffset` is zero), the normalized value; when * `m_fRadiusOffset` is not zero (e.g. as in the case of a donut), the interval * used for normalization is extended by `m_fRadiusOffset`: if the axis * orientation is not reversed the new interval becomes * [scale.Minimum - m_fRadiusOffset, scale.Maximum] else it becomes * [scale.Minimum, scale.Maximum + m_fRadiusOffset]. * Pay attention here! For the latter case, since the axis orientation is * reversed, the normalization is reversed too. Indeed, we have * `transformToRadius(scale.Maximum + m_fRadiusOffset) = 0` and * `transformToRadius(scale.Minimum) = 1`. * * For a pie chart the radius axis scale range is initialized by the * `getMinimum` and `getMaximum` methods of the `PieChart` object (see notes * for `VCoordinateSystem::prepareAutomaticAxisScaling`). * So we have scale.Minimum = 0.5 (always constant!) and * scale.Maximum = 0.5 + number_of_rings + max_offset * (see notes for `PieChart::getMaxOffset`). * Hence we get the following general formulas for computing normalized inner * and outer radius: * * 1- transformToRadius(inner_radius) = * (number_of_rings - (ring_index + 1) + m_fRadiusOffset) * / (number_of_rings + max_offset + m_fRadiusOffset) * * 2- transformToRadius(outer_radius) = * (1 + number_of_rings - (ring_index + 1) + m_fRadiusOffset) * / (number_of_rings + max_offset + m_fRadiusOffset). * * Here you have to take into account that values for inner and outer radius * are swapped since the radius axis is reversed (See notes for * `PiePositionHelper::getInnerAndOuterRadius`). So indeed inner_radius is * the outer and outer_radius is the inner. Anyway still because of the reverse * orientation, the normalization performed by `transformToRadius` is reversed * too, as we have seen above. Hence `transformToRadius(inner_radius)` is * really the normalized inner radius and `transformToRadius(outer_radius)` is * really the normalized outer radius. * * Some basic examples where we apply the above formulas: * 1- For a non-exploded pie chart we have: * `transformToRadius(inner_radius) = 0`, * `transformToRadius(outer_radius) = 1`. * 2- For a non-exploded donut with a single ring we have: * `transformToRadius(inner_radius) = * m_fRadiusOffset/(1 + m_fRadiusOffset)`, * `transformToRadius(outer_radius) = * (1 + m_fRadiusOffset)/(1 + m_fRadiusOffset) = 1`. * 3- For an exploded pie chart we have: * `transformToRadius(inner_radius) = 0/(1 + max_offset) = 0`, * `transformToRadius(outer_radius) = 1/(1 + max_offset)`. * * The third example needs some remark. Both the logical inner and outer * radius passed to `transformToRadius` are offset by `max_offset`. * However the returned normalized values do not contain any (normalized) * offset term at all, otherwise the returned values would be * `max_offset/(1 + max_offset)` and `1`. Hence, for exploded pie/donut, * `transformToRadius` returns the normalized value of radii without any * offset term. These values are smaller than in the non-exploded case by an * amount equals to the value of the normalized maximum offset * (`max_offset/(1 + max_offset)` in the example above). That is due to the * fact that the normalization keeps into account the space needed for the * offset. This is the correct behavior, in fact the offset for the current * slice could be different from the maximum offset. * These remarks should clarify why the `PieChart::createDataPoint` and * `PieChart::createTextLabelShape` methods add the normalized offset (for the * current slice) to the normalized radii in order to achieve the correct * placement of slice and text shapes.
*/ double PolarPlottingPositionHelper::transformToRadius( double fLogicValueOnRadiusAxis, bool bDoScaling ) const
{ double fNormalRadius = 0.0;
{ double fScaledLogicRadiusValue = 0.0; double fX = m_bSwapXAndY ? fLogicValueOnRadiusAxis: getLogicMaxX(); double fY = m_bSwapXAndY ? getLogicMaxY() : fLogicValueOnRadiusAxis; if(bDoScaling)
doLogicScaling( &fX, &fY, nullptr );
//!! applying matrix to vector does ignore translation, so it is important to use a B3DPoint here instead of B3DVector
::basegfx::B3DPoint aPoint(fX,fY,fZ);
aPoint += aOffset;
::basegfx::B3DPoint aRet = m_aUnitCartesianToScene * aPoint; return B3DPointToPosition3D(aRet);
}
//adapt category width double fCategoryWidth = 1.0; if( !m_aScales.empty() )
{ if( m_aScales[0].AxisType == css::chart2::AxisType::DATE )
{
m_bDateAxis = true; if( nTimeResolution == css::chart::TimeUnit::YEAR )
{ constdouble fMonthCount = 12.0;//todo: this depends on the DateScaling and must be adjusted in case we use more generic calendars in future
fCategoryWidth = fMonthCount;
}
}
}
setScaledCategoryWidth(fCategoryWidth);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.