diff options
-rw-r--r-- | svx/qa/unit/customshapes.cxx | 53 | ||||
-rw-r--r-- | svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp | bin | 0 -> 12420 bytes | |||
-rw-r--r-- | svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx | bin | 0 -> 15580 bytes | |||
-rw-r--r-- | svx/source/customshapes/EnhancedCustomShape2d.cxx | 112 |
4 files changed, 120 insertions, 45 deletions
diff --git a/svx/qa/unit/customshapes.cxx b/svx/qa/unit/customshapes.cxx index d8f7fb8a5bf9..309c5155d8da 100644 --- a/svx/qa/unit/customshapes.cxx +++ b/svx/qa/unit/customshapes.cxx @@ -704,6 +704,59 @@ CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf103474_commandT_CaseZeroHeight) CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart y-coordinate", 9999.0, aStart.getY(), 1.0); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd y-coordinate", 1999.0, aEnd.getY(), 1.0); } + +CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf103474_commandG_CaseZeroHeight) +{ + // Some as above, but with shape with command G. + OUString sURL + = m_directories.getURLFromSrc(sDataDirectory) + "tdf103474_commandG_CaseZeroHeight.odp"; + mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument"); + CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is()); + uno::Reference<drawing::XShape> xShape(getShape(0)); + // The end points of the straight line segment should have the same x-coordinate of left + // of shape, and different y-coordinates, one top and the other bottom of the shape. + SdrObjCustomShape& rSdrObjCustomShape( + static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape))); + EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape); + SdrPathObj* pPathObj = static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry()); + CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj); + const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(1), + aPolyPolygon.count()); + const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0)); + // Get the middle points of the polygon. They are the endpoints of the + // straight line segment regardless of the quarter ellipse parts, because + // the shape is symmetric. + const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(aPolygon.count() / 2 - 1)); + const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() / 2)); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart x-coordinate", 1999.0, aStart.getX(), 1.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd x-coordinate", 1999.0, aEnd.getX(), 1.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart y-coordinate", 9999.0, aStart.getY(), 1.0); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd y-coordinate", 1999.0, aEnd.getY(), 1.0); +} + +CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf122323_largeSwingAngle) +{ + // SwingAngles are clamped to [-360;360] in MS Office. Error was, that LO calculated + // the end angle and used it modulo 360, no full ellipse was drawn. + OUString sURL + = m_directories.getURLFromSrc(sDataDirectory) + "tdf122323_swingAngle_larger360deg.pptx"; + mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument"); + CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is()); + uno::Reference<drawing::XShape> xShape(getShape(0)); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + SdrObjCustomShape& rSdrObjCustomShape( + static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape))); + EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape); + SdrPathObj* pPathObj = static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry()); + CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj); + const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly()); + const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0)); + const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(0)); + // last point comes from line to center, therefore -2 instead of -1 + const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() - 2)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Start <> End", aStart, aEnd); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp b/svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp Binary files differnew file mode 100644 index 000000000000..9b36d45eed6a --- /dev/null +++ b/svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp diff --git a/svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx b/svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx Binary files differnew file mode 100644 index 000000000000..919675ef9d27 --- /dev/null +++ b/svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx diff --git a/svx/source/customshapes/EnhancedCustomShape2d.cxx b/svx/source/customshapes/EnhancedCustomShape2d.cxx index 8b570edd809e..89360b62fc70 100644 --- a/svx/source/customshapes/EnhancedCustomShape2d.cxx +++ b/svx/source/customshapes/EnhancedCustomShape2d.cxx @@ -2356,58 +2356,80 @@ void EnhancedCustomShape2d::CreateSubPath( case ARCANGLETO : { - double fWR, fHR, fStartAngle, fSwingAngle; + double fWR, fHR; // in Shape coordinate system + double fStartAngle, fSwingAngle; // in deg for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( rSrcPt + 1 < nCoordSize ); i++ ) { - GetParameter ( fWR, seqCoordinates[ static_cast<sal_uInt16>(rSrcPt) ].First, true, false ); - GetParameter ( fHR, seqCoordinates[ static_cast<sal_uInt16>(rSrcPt) ].Second, false, true ); - - GetParameter ( fStartAngle, seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + 1) ].First, false, false ); - GetParameter ( fSwingAngle, seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + 1 ) ].Second, false, false ); - - // Convert angles to radians, but don't do any scaling / translation yet. - - fStartAngle = basegfx::deg2rad(fStartAngle); - fSwingAngle = basegfx::deg2rad(fSwingAngle); + basegfx::B2DPoint aTempPair; + aTempPair = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt)], false /*bScale*/, false /*bReplaceGeoSize*/); + fWR = aTempPair.getX(); + fHR = aTempPair.getY(); + aTempPair = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt + 1)], false /*bScale*/, false /*bReplaceGeoSize*/); + fStartAngle = aTempPair.getX(); + fSwingAngle = aTempPair.getY(); + + // tdf#122323 MS Office clamps the swing angle to [-360,360]. Such restriction + // is neither in OOXML nor in ODF. Nevertheless, to be compatible we do it for + // "ooxml-foo" shapes. Those shapes have their origin in MS Office. + if (bOOXMLShape) + { + fSwingAngle = std::clamp(fSwingAngle, -360.0, 360.0); + } SAL_INFO("svx", "ARCANGLETO scale: " << fWR << "x" << fHR << " angles: " << fStartAngle << "," << fSwingAngle); - bool bClockwise = fSwingAngle >= 0.0; - - if (aNewB2DPolygon.count() > 0) + if (aNewB2DPolygon.count() > 0) // otherwise no "current point" { - basegfx::B2DPoint aStartPointB2D( aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count() - 1 ) ); - Point aStartPoint( 0, 0 ); - - double fT = atan2((fWR*sin(fStartAngle)), (fHR*cos(fStartAngle))); - double fTE = atan2((fWR*sin(fStartAngle + fSwingAngle)), fHR*cos(fStartAngle + fSwingAngle)); - - SAL_INFO("svx", "ARCANGLETO angles: " << fStartAngle << ", " << fSwingAngle - << " --> parameters: " << fT <<", " << fTE ); - - fWR *= fXScale; - fHR *= fYScale; - - tools::Rectangle aRect ( Point ( aStartPoint.getX() - fWR*cos(fT) - fWR, aStartPoint.getY() - fHR*sin(fT) - fHR ), - Point ( aStartPoint.getX() - fWR*cos(fT) + fWR, aStartPoint.getY() - fHR*sin(fT) + fHR) ); - - Point aEndPoint ( aStartPoint.getX() - fWR*(cos(fT) - cos(fTE)), aStartPoint.getY() - fHR*(sin(fT) - sin(fTE)) ); - - SAL_INFO( - "svx", - "ARCANGLETO rect: " << aRect.Left() << ", " - << aRect.Top() << " x " << aRect.Right() - << ", " << aRect.Bottom() << " start: " - << aStartPoint.X() << ", " - << aStartPoint.Y() << " end: " - << aEndPoint.X() << ", " << aEndPoint.Y() - << " clockwise: " << int(bClockwise)); - basegfx::B2DPolygon aArc = CreateArc( aRect, bClockwise ? aEndPoint : aStartPoint, bClockwise ? aStartPoint : aEndPoint, bClockwise, aStartPoint == aEndPoint && ((bClockwise && fSwingAngle > F_PI) || (!bClockwise && fSwingAngle < -F_PI))); - // Now that we have the arc, move it to aStartPointB2D. - basegfx::B2DHomMatrix aMatrix = basegfx::utils::createTranslateB2DHomMatrix(aStartPointB2D.getX(), aStartPointB2D.getY()); - aArc.transform(aMatrix); - aNewB2DPolygon.append(aArc); + // use similar methods as in command U + basegfx::B2DPolygon aTempB2DPolygon; + + if (fWR == 0.0 && fHR == 0.0) + { + // degenerated ellipse, add this one point + aTempB2DPolygon.append(basegfx::B2DPoint(0.0, 0.0)); + } + else + { + double fEndAngle = fStartAngle + fSwingAngle; + // Generate arc with ellipse left|top = 0|0. + basegfx::B2DPoint aCenter(fWR, fHR); + if (fSwingAngle < 0.0) + std::swap(fStartAngle, fEndAngle); + double fS; // fFrom in radians in [0..2Pi[ + double fE; // fTo or fEndAngle in radians in [0..2PI[ + double fFrom(fStartAngle); + // createPolygonFromEllipseSegment expects angles in [0..2PI[. + if (fSwingAngle >= 360.0 || fSwingAngle <= -360.0) + { + double fTo(fFrom + 180.0); + while (fTo < fEndAngle) + { + fS = lcl_getNormalizedCircleAngleRad(fWR, fHR, fFrom); + fE = lcl_getNormalizedCircleAngleRad(fWR, fHR, fTo); + aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fWR, fHR, fS,fE)); + fFrom = fTo; + fTo += 180.0; + } + } + fS = lcl_getNormalizedCircleAngleRad(fWR, fHR, fFrom); + fE = lcl_getNormalizedCircleAngleRad(fWR, fHR, fEndAngle); + aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fWR, fHR,fS, fE)); + if (fSwingAngle < 0) + aTempB2DPolygon.flip(); + aTempB2DPolygon.removeDoublePoints(); + } + // Scale arc to 1/100mm + basegfx::B2DHomMatrix aMatrix = basegfx::utils::createScaleB2DHomMatrix(fXScale, fYScale); + aTempB2DPolygon.transform(aMatrix); + + // Now that we have the arc, move it to the "current point". + basegfx::B2DPoint aCurrentPointB2D( aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count() - 1 ) ); + const double fDx(aCurrentPointB2D.getX() - aTempB2DPolygon.getB2DPoint(0).getX()); + const double fDy(aCurrentPointB2D.getY() - aTempB2DPolygon.getB2DPoint(0).getY()); + aMatrix = basegfx::utils::createTranslateB2DHomMatrix(fDx, fDy); + aTempB2DPolygon.transform(aMatrix); + aNewB2DPolygon.append(aTempB2DPolygon); } rSrcPt += 2; |