summaryrefslogtreecommitdiff
path: root/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'drawinglayer/source/primitive2d/polygonprimitive2d.cxx')
-rw-r--r--drawinglayer/source/primitive2d/polygonprimitive2d.cxx1175
1 files changed, 700 insertions, 475 deletions
diff --git a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
index 0251910e8252..ab6833a44ffa 100644
--- a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
@@ -17,587 +17,812 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <basegfx/polygon/b2dlinegeometry.hxx>
#include <com/sun/star/drawing/LineCap.hpp>
-
-#include <converters.hxx>
+#include <utility>
using namespace com::sun::star;
-using namespace std;
-
-namespace drawinglayer::primitive2d
+namespace
+{
+void implGrowHairline(basegfx::B2DRange& rRange,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
{
- PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(
- const basegfx::B2DPolygon& rPolygon,
- const basegfx::BColor& rBColor)
- : BasePrimitive2D(),
- maPolygon(rPolygon),
- maBColor(rBColor)
+ if (!rRange.isEmpty())
+ {
+ // Calculate view-dependent hairline width
+ const basegfx::B2DVector aDiscreteSize(
+ rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
+ const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
+
+ if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
{
+ rRange.grow(fDiscreteHalfLineWidth);
}
+ }
+}
+}
- bool PolygonHairlinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
- {
- if(BasePrimitive2D::operator==(rPrimitive))
- {
- const PolygonHairlinePrimitive2D& rCompare = static_cast<const PolygonHairlinePrimitive2D&>(rPrimitive);
+namespace drawinglayer::primitive2d
+{
+PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(basegfx::B2DPolygon aPolygon,
+ const basegfx::BColor& rBColor)
+ : maPolygon(std::move(aPolygon))
+ , maBColor(rBColor)
+{
+}
- return (getB2DPolygon() == rCompare.getB2DPolygon()
- && getBColor() == rCompare.getBColor());
- }
+bool PolygonHairlinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonHairlinePrimitive2D& rCompare
+ = static_cast<const PolygonHairlinePrimitive2D&>(rPrimitive);
- return false;
- }
+ return (getB2DPolygon() == rCompare.getB2DPolygon() && getBColor() == rCompare.getBColor());
+ }
- basegfx::B2DRange PolygonHairlinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
- {
- // this is a hairline, thus the line width is view-dependent. Get range of polygon
- // as base size
- basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange());
+ return false;
+}
- if(!aRetval.isEmpty())
- {
- // Calculate view-dependent hairline width
- const basegfx::B2DVector aDiscreteSize(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
- const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
-
- if(basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
- {
- aRetval.grow(fDiscreteHalfLineWidth);
- }
- }
+basegfx::B2DRange
+PolygonHairlinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // this is a hairline, thus the line width is view-dependent. Get range of polygon
+ // as base size
+ basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange());
- // return range
- return aRetval;
- }
+ // Calculate and grow by view-dependent hairline width
+ implGrowHairline(aRetval, rViewInformation);
- // provide unique ID
- ImplPrimitive2DIDBlock(PolygonHairlinePrimitive2D, PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D)
+ // return range
+ return aRetval;
+}
+// provide unique ID
+sal_uInt32 PolygonHairlinePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D;
+}
+
+SingleLinePrimitive2D::SingleLinePrimitive2D(const basegfx::B2DPoint& rStart,
+ const basegfx::B2DPoint& rEnd,
+ const basegfx::BColor& rBColor)
+ : BasePrimitive2D()
+ , maStart(rStart)
+ , maEnd(rEnd)
+ , maBColor(rBColor)
+{
+}
- void PolygonMarkerPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
- {
- // calculate logic DashLength
- const basegfx::B2DVector aDashVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(getDiscreteDashLength(), 0.0));
- const double fLogicDashLength(aDashVector.getX());
+bool SingleLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const SingleLinePrimitive2D& rCompare(
+ static_cast<const SingleLinePrimitive2D&>(rPrimitive));
- if(fLogicDashLength > 0.0 && !getRGBColorA().equal(getRGBColorB()))
- {
- // apply dashing; get line and gap snippets
- std::vector< double > aDash;
- basegfx::B2DPolyPolygon aDashedPolyPolyA;
- basegfx::B2DPolyPolygon aDashedPolyPolyB;
+ return (getStart() == rCompare.getStart() && getEnd() == rCompare.getEnd()
+ && getBColor() == rCompare.getBColor());
+ }
- aDash.push_back(fLogicDashLength);
- aDash.push_back(fLogicDashLength);
- basegfx::utils::applyLineDashing(getB2DPolygon(), aDash, &aDashedPolyPolyA, &aDashedPolyPolyB, 2.0 * fLogicDashLength);
+ return false;
+}
- rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyA, getRGBColorA()));
- rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyB, getRGBColorB()));
- }
- else
- {
- rContainer.push_back(new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA()));
- }
- }
+basegfx::B2DRange
+SingleLinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aRetval(getStart(), getEnd());
- PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D(
- const basegfx::B2DPolygon& rPolygon,
- const basegfx::BColor& rRGBColorA,
- const basegfx::BColor& rRGBColorB,
- double fDiscreteDashLength)
- : BufferedDecompositionPrimitive2D(),
- maPolygon(rPolygon),
- maRGBColorA(rRGBColorA),
- maRGBColorB(rRGBColorB),
- mfDiscreteDashLength(fDiscreteDashLength),
- maLastInverseObjectToViewTransformation()
- {
- }
+ // Calculate and grow by view-dependent hairline width
+ implGrowHairline(aRetval, rViewInformation);
- bool PolygonMarkerPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
- {
- if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
- {
- const PolygonMarkerPrimitive2D& rCompare = static_cast<const PolygonMarkerPrimitive2D&>(rPrimitive);
+ return aRetval;
+}
- return (getB2DPolygon() == rCompare.getB2DPolygon()
- && getRGBColorA() == rCompare.getRGBColorA()
- && getRGBColorB() == rCompare.getRGBColorB()
- && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
- }
+sal_uInt32 SingleLinePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D;
+}
- return false;
- }
+void SingleLinePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (getStart() == getEnd())
+ {
+ // single point
+ Primitive2DContainer aSequence = { new PointArrayPrimitive2D(
+ std::vector<basegfx::B2DPoint>{ getStart() }, getBColor()) };
+ rVisitor.visit(aSequence);
+ }
+ else
+ {
+ // line
+ Primitive2DContainer aSequence = { new PolygonHairlinePrimitive2D(
+ basegfx::B2DPolygon{ getStart(), getEnd() }, getBColor()) };
+ rVisitor.visit(aSequence);
+ }
+}
- basegfx::B2DRange PolygonMarkerPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
- {
- // this is a hairline, thus the line width is view-dependent. Get range of polygon
- // as base size
- basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange());
+LineRectanglePrimitive2D::LineRectanglePrimitive2D(const basegfx::B2DRange& rB2DRange,
+ const basegfx::BColor& rBColor)
+ : BasePrimitive2D()
+ , maB2DRange(rB2DRange)
+ , maBColor(rBColor)
+{
+}
- if(!aRetval.isEmpty())
- {
- // Calculate view-dependent hairline width
- const basegfx::B2DVector aDiscreteSize(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
- const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
-
- if(basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
- {
- aRetval.grow(fDiscreteHalfLineWidth);
- }
- }
+bool LineRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const LineRectanglePrimitive2D& rCompare(
+ static_cast<const LineRectanglePrimitive2D&>(rPrimitive));
- // return range
- return aRetval;
- }
+ return (getB2DRange() == rCompare.getB2DRange() && getBColor() == rCompare.getBColor());
+ }
- void PolygonMarkerPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
- {
- ::osl::MutexGuard aGuard( m_aMutex );
- bool bNeedNewDecomposition(false);
+ return false;
+}
- if(!getBuffered2DDecomposition().empty())
- {
- if(rViewInformation.getInverseObjectToViewTransformation() != maLastInverseObjectToViewTransformation)
- {
- bNeedNewDecomposition = true;
- }
- }
+basegfx::B2DRange
+LineRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aRetval(getB2DRange());
- if(bNeedNewDecomposition)
- {
- // conditions of last local decomposition have changed, delete
- const_cast< PolygonMarkerPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
- }
+ // Calculate and grow by view-dependent hairline width
+ implGrowHairline(aRetval, rViewInformation);
- if(getBuffered2DDecomposition().empty())
- {
- // remember last used InverseObjectToViewTransformation
- PolygonMarkerPrimitive2D* pThat = const_cast< PolygonMarkerPrimitive2D* >(this);
- pThat->maLastInverseObjectToViewTransformation = rViewInformation.getInverseObjectToViewTransformation();
- }
+ return aRetval;
+}
- // use parent implementation
- BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
- }
+sal_uInt32 LineRectanglePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D;
+}
- // provide unique ID
- ImplPrimitive2DIDBlock(PolygonMarkerPrimitive2D, PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D)
+void LineRectanglePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (getB2DRange().isEmpty())
+ {
+ // no geometry, done
+ return;
+ }
-} // end of namespace
+ const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(getB2DRange()));
+ Primitive2DContainer aSequence = { new PolygonHairlinePrimitive2D(aPolygon, getBColor()) };
+ rVisitor.visit(aSequence);
+}
-namespace drawinglayer
+Primitive2DReference PolygonMarkerPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& rViewInformation) const
{
- namespace primitive2d
+ // calculate logic DashLength
+ const basegfx::B2DVector aDashVector(rViewInformation.getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(getDiscreteDashLength(), 0.0));
+ const double fLogicDashLength(aDashVector.getX());
+
+ if (fLogicDashLength > 0.0 && !getRGBColorA().equal(getRGBColorB()))
{
- void PolygonStrokePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
- {
- if(!getB2DPolygon().count())
- return;
+ // apply dashing; get line and gap snippets
+ std::vector<double> aDash;
+ basegfx::B2DPolyPolygon aDashedPolyPolyA;
+ basegfx::B2DPolyPolygon aDashedPolyPolyB;
+
+ aDash.push_back(fLogicDashLength);
+ aDash.push_back(fLogicDashLength);
+ basegfx::utils::applyLineDashing(getB2DPolygon(), aDash, &aDashedPolyPolyA,
+ &aDashedPolyPolyB, 2.0 * fLogicDashLength);
+
+ Primitive2DContainer aContainer;
+ aContainer.push_back(
+ new PolyPolygonHairlinePrimitive2D(std::move(aDashedPolyPolyA), getRGBColorA()));
+ aContainer.push_back(
+ new PolyPolygonHairlinePrimitive2D(std::move(aDashedPolyPolyB), getRGBColorB()));
+ return new GroupPrimitive2D(std::move(aContainer));
+ }
+ else
+ {
+ return new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA());
+ }
+}
+
+PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D(basegfx::B2DPolygon aPolygon,
+ const basegfx::BColor& rRGBColorA,
+ const basegfx::BColor& rRGBColorB,
+ double fDiscreteDashLength)
+ : maPolygon(std::move(aPolygon))
+ , maRGBColorA(rRGBColorA)
+ , maRGBColorB(rRGBColorB)
+ , mfDiscreteDashLength(fDiscreteDashLength)
+{
+}
+
+bool PolygonMarkerPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonMarkerPrimitive2D& rCompare
+ = static_cast<const PolygonMarkerPrimitive2D&>(rPrimitive);
- // #i102241# try to simplify before usage
- const basegfx::B2DPolygon aB2DPolygon(basegfx::utils::simplifyCurveSegments(getB2DPolygon()));
- basegfx::B2DPolyPolygon aHairLinePolyPolygon;
+ return (getB2DPolygon() == rCompare.getB2DPolygon()
+ && getRGBColorA() == rCompare.getRGBColorA()
+ && getRGBColorB() == rCompare.getRGBColorB()
+ && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
+ }
- if(getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
- {
- // no line dashing, just copy
- aHairLinePolyPolygon.append(aB2DPolygon);
- }
- else
- {
- // apply LineStyle
- basegfx::utils::applyLineDashing(
- aB2DPolygon, getStrokeAttribute().getDotDashArray(),
- &aHairLinePolyPolygon, nullptr, getStrokeAttribute().getFullDotDashLen());
- }
+ return false;
+}
- const sal_uInt32 nCount(aHairLinePolyPolygon.count());
+basegfx::B2DRange
+PolygonMarkerPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // this is a hairline, thus the line width is view-dependent. Get range of polygon
+ // as base size
+ basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange());
- if(!getLineAttribute().isDefault() && getLineAttribute().getWidth())
- {
- // create fat line data
- const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0);
- const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin());
- const css::drawing::LineCap aLineCap(getLineAttribute().getLineCap());
- basegfx::B2DPolyPolygon aAreaPolyPolygon;
- const double fMiterMinimumAngle(getLineAttribute().getMiterMinimumAngle());
-
- for(sal_uInt32 a(0); a < nCount; a++)
- {
- // New version of createAreaGeometry; now creates bezier polygons
- aAreaPolyPolygon.append(basegfx::utils::createAreaGeometry(
- aHairLinePolyPolygon.getB2DPolygon(a),
- fHalfLineWidth,
- aLineJoin,
- aLineCap,
- basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/ ,
- 0.4 /* default fMaxPartOfEdge*/ ,
- fMiterMinimumAngle));
- }
-
- // create primitive
- for(sal_uInt32 b(0); b < aAreaPolyPolygon.count(); b++)
- {
- // put into single polyPolygon primitives to make clear that this is NOT meant
- // to be painted as a single tools::PolyPolygon (XORed as fill rule). Alternatively, a
- // melting process may be used here one day.
- const basegfx::B2DPolyPolygon aNewPolyPolygon(aAreaPolyPolygon.getB2DPolygon(b));
- const basegfx::BColor aColor(getLineAttribute().getColor());
- rContainer.push_back(new PolyPolygonColorPrimitive2D(aNewPolyPolygon, aColor));
- }
- }
- else
- {
- rContainer.push_back(
- new PolyPolygonHairlinePrimitive2D(
- aHairLinePolyPolygon,
- getLineAttribute().getColor()));
- }
- }
+ if (!aRetval.isEmpty())
+ {
+ // Calculate view-dependent hairline width
+ const basegfx::B2DVector aDiscreteSize(
+ rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
+ const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
- PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
- const basegfx::B2DPolygon& rPolygon,
- const attribute::LineAttribute& rLineAttribute,
- const attribute::StrokeAttribute& rStrokeAttribute)
- : BufferedDecompositionPrimitive2D(),
- maPolygon(rPolygon),
- maLineAttribute(rLineAttribute),
- maStrokeAttribute(rStrokeAttribute)
+ if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
{
- // MM01: keep these - these are no curve-decompposers but just checks
- // simplify curve segments: moved here to not need to use it
- // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
- maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
+ aRetval.grow(fDiscreteHalfLineWidth);
}
+ }
+
+ // return range
+ return aRetval;
+}
+
+void PolygonMarkerPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ bool bNeedNewDecomposition(false);
- PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
- const basegfx::B2DPolygon& rPolygon,
- const attribute::LineAttribute& rLineAttribute)
- : BufferedDecompositionPrimitive2D(),
- maPolygon(rPolygon),
- maLineAttribute(rLineAttribute),
- maStrokeAttribute()
+ if (getBuffered2DDecomposition())
+ {
+ if (rViewInformation.getInverseObjectToViewTransformation()
+ != maLastInverseObjectToViewTransformation)
{
- // MM01: keep these - these are no curve-decompposers but just checks
- // simplify curve segments: moved here to not need to use it
- // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
- maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
+ bNeedNewDecomposition = true;
}
+ }
- bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
- {
- if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
- {
- const PolygonStrokePrimitive2D& rCompare = static_cast<const PolygonStrokePrimitive2D&>(rPrimitive);
+ if (bNeedNewDecomposition)
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast<PolygonMarkerPrimitive2D*>(this)->setBuffered2DDecomposition(nullptr);
+ }
- return (getB2DPolygon() == rCompare.getB2DPolygon()
- && getLineAttribute() == rCompare.getLineAttribute()
- && getStrokeAttribute() == rCompare.getStrokeAttribute());
- }
+ if (!getBuffered2DDecomposition())
+ {
+ // remember last used InverseObjectToViewTransformation
+ PolygonMarkerPrimitive2D* pThat = const_cast<PolygonMarkerPrimitive2D*>(this);
+ pThat->maLastInverseObjectToViewTransformation
+ = rViewInformation.getInverseObjectToViewTransformation();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+}
+
+// provide unique ID
+sal_uInt32 PolygonMarkerPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D;
+}
+
+} // end of namespace
+
+namespace drawinglayer::primitive2d
+{
+Primitive2DReference PolygonStrokePrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getB2DPolygon().count())
+ return nullptr;
+
+ // #i102241# try to simplify before usage
+ const basegfx::B2DPolygon aB2DPolygon(basegfx::utils::simplifyCurveSegments(getB2DPolygon()));
+ basegfx::B2DPolyPolygon aHairLinePolyPolygon;
+
+ if (getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
+ {
+ // no line dashing, just copy
+ aHairLinePolyPolygon.append(aB2DPolygon);
+ }
+ else
+ {
+ // apply LineStyle
+ basegfx::utils::applyLineDashing(aB2DPolygon, getStrokeAttribute().getDotDashArray(),
+ &aHairLinePolyPolygon, nullptr,
+ getStrokeAttribute().getFullDotDashLen());
+ }
+
+ const sal_uInt32 nCount(aHairLinePolyPolygon.count());
- return false;
+ if (!getLineAttribute().isDefault() && getLineAttribute().getWidth())
+ {
+ // create fat line data
+ const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0);
+ const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin());
+ const css::drawing::LineCap aLineCap(getLineAttribute().getLineCap());
+ basegfx::B2DPolyPolygon aAreaPolyPolygon;
+ const double fMiterMinimumAngle(getLineAttribute().getMiterMinimumAngle());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ // New version of createAreaGeometry; now creates bezier polygons
+ aAreaPolyPolygon.append(basegfx::utils::createAreaGeometry(
+ aHairLinePolyPolygon.getB2DPolygon(a), fHalfLineWidth, aLineJoin, aLineCap,
+ basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/,
+ 0.4 /* default fMaxPartOfEdge*/, fMiterMinimumAngle));
}
- basegfx::B2DRange PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ // create primitive
+ Primitive2DContainer aContainer;
+ for (sal_uInt32 b(0); b < aAreaPolyPolygon.count(); b++)
{
- basegfx::B2DRange aRetval;
+ // put into single polyPolygon primitives to make clear that this is NOT meant
+ // to be painted as a single tools::PolyPolygon (XORed as fill rule). Alternatively, a
+ // melting process may be used here one day.
+ basegfx::B2DPolyPolygon aNewPolyPolygon(aAreaPolyPolygon.getB2DPolygon(b));
+ const basegfx::BColor aColor(getLineAttribute().getColor());
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(std::move(aNewPolyPolygon), aColor));
+ }
+ return new GroupPrimitive2D(std::move(aContainer));
+ }
+ else
+ {
+ return new PolyPolygonHairlinePrimitive2D(std::move(aHairLinePolyPolygon),
+ getLineAttribute().getColor());
+ }
+}
+
+PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(basegfx::B2DPolygon aPolygon,
+ const attribute::LineAttribute& rLineAttribute,
+ attribute::StrokeAttribute aStrokeAttribute)
+ : maPolygon(std::move(aPolygon))
+ , maLineAttribute(rLineAttribute)
+ , maStrokeAttribute(std::move(aStrokeAttribute))
+ , maBufferedRange()
+{
+ // MM01: keep these - these are no curve-decompposers but just checks
+ // simplify curve segments: moved here to not need to use it
+ // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+ maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
+}
+
+PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(basegfx::B2DPolygon aPolygon,
+ const attribute::LineAttribute& rLineAttribute)
+ : maPolygon(std::move(aPolygon))
+ , maLineAttribute(rLineAttribute)
+ , maBufferedRange()
+{
+ // MM01: keep these - these are no curve-decompposers but just checks
+ // simplify curve segments: moved here to not need to use it
+ // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+ maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
+}
- if(getLineAttribute().getWidth())
- {
- bool bUseDecomposition(false);
-
- if(basegfx::B2DLineJoin::Miter == getLineAttribute().getLineJoin())
- {
- // if line is mitered, use parent call since mitered line
- // geometry may use more space than the geometry grown by half line width
- bUseDecomposition = true;
- }
-
- if(!bUseDecomposition && css::drawing::LineCap_SQUARE == getLineAttribute().getLineCap())
- {
- // when drawing::LineCap_SQUARE is used the below method to grow the polygon
- // range by half line width will not work, so use decomposition. Interestingly,
- // the grow method below works perfectly for LineCap_ROUND since the grow is in
- // all directions and the rounded cap needs the same grow in all directions independent
- // from its orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE
- bUseDecomposition = true;
- }
-
- if (bUseDecomposition)
- {
- // get correct range by using the decomposition fallback, reasons see above cases
-
- // ofz#947 to optimize calculating the range, turn any slow dashes into a solid line
- // when just calculating bounds
- attribute::StrokeAttribute aOrigStrokeAttribute = maStrokeAttribute;
- const_cast<PolygonStrokePrimitive2D*>(this)->maStrokeAttribute = attribute::StrokeAttribute();
- aRetval = BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
- const_cast<PolygonStrokePrimitive2D*>(this)->maStrokeAttribute = aOrigStrokeAttribute;
- }
- else
- {
- // for all other B2DLINEJOIN_* get the range from the base geometry
- // and expand by half the line width
- aRetval = getB2DPolygon().getB2DRange();
- aRetval.grow(getLineAttribute().getWidth() * 0.5);
- }
- }
- else
- {
- // this is a hairline, thus the line width is view-dependent. Get range of polygon
- // as base size
- aRetval = getB2DPolygon().getB2DRange();
-
- if(!aRetval.isEmpty())
- {
- // Calculate view-dependent hairline width
- const basegfx::B2DVector aDiscreteSize(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
- const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
-
- if(basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
- {
- aRetval.grow(fDiscreteHalfLineWidth);
- }
- }
- }
+bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonStrokePrimitive2D& rCompare
+ = static_cast<const PolygonStrokePrimitive2D&>(rPrimitive);
- return aRetval;
- }
+ return (getB2DPolygon() == rCompare.getB2DPolygon()
+ && getLineAttribute() == rCompare.getLineAttribute()
+ && getStrokeAttribute() == rCompare.getStrokeAttribute());
+ }
- // provide unique ID
- ImplPrimitive2DIDBlock(PolygonStrokePrimitive2D, PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D)
+ return false;
+}
+basegfx::B2DRange
+PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (!maBufferedRange.isEmpty())
+ {
+ // use the view-independent, buffered B2DRange
+ return maBufferedRange;
+ }
+ if (getLineAttribute().getWidth())
+ {
+ bool bUseDecomposition(false);
- void PolygonWavePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ if (basegfx::B2DLineJoin::Miter == getLineAttribute().getLineJoin())
{
- if(!getB2DPolygon().count())
- return;
+ // if line is mitered, use parent call since mitered line
+ // geometry may use more space than the geometry grown by half line width
+ bUseDecomposition = true;
+ }
- const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth()));
- const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight()));
+ if (!bUseDecomposition && css::drawing::LineCap_SQUARE == getLineAttribute().getLineCap())
+ {
+ // when drawing::LineCap_SQUARE is used the below method to grow the polygon
+ // range by half line width will not work, so use decomposition. Interestingly,
+ // the grow method below works perfectly for LineCap_ROUND since the grow is in
+ // all directions and the rounded cap needs the same grow in all directions independent
+ // from its orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE
+
+ // NOTE: I thought about using [sqrt(2) * 0.5] a a factor for LineCap_SQUARE and not
+ // set bUseDecomposition. I even tried that it works. Then an auto-test failing showed
+ // not only that view-dependent stuff needs to be considered (what is done for the
+ // hairline case below), *BUT* also e.g. conversions to PNG/exports use the B2DRange
+ // of the geometry, so:
+ // - expanding by 1/2 LineWidth is OK for rounded
+ // - expanding by more (like sqrt(2) * 0.5 * LineWidth) immediately extends the size
+ // of e.g. geometry converted to PNG, plus many similar cases that cannot be thought
+ // of in advance.
+ // This means that converting those thought-experiment examples in (4) will and do lead
+ // to bigger e.g. Bitmap conversion(s), not avoiding but painting the free space. That
+ // could only be done by correctly and fully decomposing the geometry, including
+ // stroke, and accepting the cost...
+ bUseDecomposition = true;
+ }
- if(bHasWidth && bHasHeight)
+ if (bUseDecomposition)
+ {
+ // get correct range by using the decomposition fallback, reasons see above cases
+
+ // It is not a good idea to temporarily (re)set the PolygonStrokePrimitive2D
+ // to default. While it is understandable to use the speed advantage, it is
+ // bad for quite some reasons:
+ //
+ // (1) As described in include/drawinglayer/primitive2d/baseprimitive2d.hxx
+ // a Primitive is "noncopyable to make clear that a primitive is a read-only
+ // instance and copying or changing values is not intended". This is the base
+ // assumption for many decisions for Primitive handling.
+ // (2) For example, that the decomposition is *always* re-usable. It cannot change
+ // and is correct when it already exists since the values the decomposition is
+ // based on cannot change.
+ // (3) If this *is* done (like it was here) and the Primitive is derived from
+ // BufferedDecompositionPrimitive2D and thus buffers it's decomposition,
+ // the risk is that in this case the *wrong* decomposition will be used by
+ // other PrimitiveProcessors. Maybe not by the VclPixelProcessor2D/VclProcessor2D
+ // since it handles this primitive directly - not even sure for all cases.
+ // Sooner or later another PrimitiveProcessor will re-use this wrong temporary
+ // decomposition, and as an error, a non-stroked line will be painted/used.
+ // (4) The B2DRange is not strictly defined as minimal bound for the geometry,
+ // but it should be as small/tight as possible. Making it larger risks more
+ // area to be invalidated (repaint) and processed (all geometric stuff,l may
+ // include future and existing exports to other formats which are or will be
+ // implemented as PrimitiveProcessor). It is easy to imagine cases with much
+ // too large B2DRange - a line with a pattern that would solve to a single
+ // small start-rectangle and rest is empty, or a circle with a stroke that
+ // makes only a quarter of it visible.
+ //
+ // The reason to do this is speed, what is a good argument. But speed should
+ // only be used if the pair of [correctness/speed] does not sacrifice the correctness
+ // over the speed.
+ // Luckily there are alternatives to solve this and to keep [correctness/speed]
+ // valid:
+ //
+ // (a) Reset the temporary decomposition after having const-casted and
+ // changed maStrokeAttribute.
+ // Disadvantage: More const-cast hacks, plus this temporary decomposition
+ // will be potentially done repeatedly (every time
+ // PolygonStrokePrimitive2D::getB2DRange is called)
+ // (b) Use a temporary, local PolygonStrokePrimitive2D here, with neutral
+ // PolygonStrokePrimitive2D and call ::getB2DRange() at it. That way
+ // the buffered decomposition will not be harmed.
+ // Disadvantage: Same as (a), decomposition will be potentially done repeatedly
+ // (c) Use a temporary, local PolygonStrokePrimitive2D and buffer B2DRange
+ // locally for this Primitive. Due to (1)/(2) this cannot change, so
+ // when calculated once it is totally legal to use it.
+ //
+ // Thus here I would use (c): It accepts the disadvantages of (4) over speed, but
+ // avoids the errors/problems from (1-4).
+ // Additional argument for this: The hairline case below *also* uses the full
+ // B2DRange of the polygon, ignoring an evtl. stroke, so (4) applies
+ if (!getStrokeAttribute().isDefault())
{
- // create waveline curve
- const basegfx::B2DPolygon aWaveline(basegfx::utils::createWaveline(getB2DPolygon(), getWaveWidth(), getWaveHeight()));
- rContainer.push_back(new PolygonStrokePrimitive2D(aWaveline, getLineAttribute(), getStrokeAttribute()));
+ // only do this if StrokeAttribute is used, else recursion may happen (!)
+ const rtl::Reference<primitive2d::PolygonStrokePrimitive2D>
+ aTemporaryPrimitiveWithoutStroke(new primitive2d::PolygonStrokePrimitive2D(
+ getB2DPolygon(), getLineAttribute()));
+ maBufferedRange
+ = aTemporaryPrimitiveWithoutStroke
+ ->BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
}
else
{
- // flat waveline, decompose to simple line primitive
- rContainer.push_back(new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(), getStrokeAttribute()));
+ // fallback to normal decompose, that result can be used for visualization
+ // later, too. Still buffer B2DRange in maBufferedRange, so it needs to be
+ // merged into one B2DRange only once
+ maBufferedRange = BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
}
}
-
- PolygonWavePrimitive2D::PolygonWavePrimitive2D(
- const basegfx::B2DPolygon& rPolygon,
- const attribute::LineAttribute& rLineAttribute,
- const attribute::StrokeAttribute& rStrokeAttribute,
- double fWaveWidth,
- double fWaveHeight)
- : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute),
- mfWaveWidth(fWaveWidth),
- mfWaveHeight(fWaveHeight)
+ else
{
- if(mfWaveWidth < 0.0)
- {
- mfWaveWidth = 0.0;
- }
-
- if(mfWaveHeight < 0.0)
- {
- mfWaveHeight = 0.0;
- }
+ // for all other B2DLINEJOIN_* get the range from the base geometry
+ // and expand by half the line width.
+ maBufferedRange = getB2DPolygon().getB2DRange();
+ maBufferedRange.grow(getLineAttribute().getWidth() * 0.5);
}
- PolygonWavePrimitive2D::PolygonWavePrimitive2D(
- const basegfx::B2DPolygon& rPolygon,
- const attribute::LineAttribute& rLineAttribute,
- double fWaveWidth,
- double fWaveHeight)
- : PolygonStrokePrimitive2D(rPolygon, rLineAttribute),
- mfWaveWidth(fWaveWidth),
- mfWaveHeight(fWaveHeight)
- {
- if(mfWaveWidth < 0.0)
- {
- mfWaveWidth = 0.0;
- }
+ return maBufferedRange;
+ }
- if(mfWaveHeight < 0.0)
- {
- mfWaveHeight = 0.0;
- }
- }
+ // It is a hairline, thus the line width is view-dependent. Get range of polygon
+ // as base size.
+ // CAUTION: Since a hairline *is* view-dependent,
+ // - either use maBufferedRange, additionally remember view-dependent
+ // factor & reset if that changes
+ // - or do not buffer for hairline -> not really needed, the range is buffered
+ // in the B2DPolygon, no decomposition is needed and a simple grow is cheap
+ basegfx::B2DRange aHairlineRange = getB2DPolygon().getB2DRange();
- bool PolygonWavePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ if (!aHairlineRange.isEmpty())
+ {
+ // Calculate view-dependent hairline width
+ const basegfx::B2DVector aDiscreteSize(
+ rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
+ const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
+
+ if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
{
- if(PolygonStrokePrimitive2D::operator==(rPrimitive))
- {
- const PolygonWavePrimitive2D& rCompare = static_cast<const PolygonWavePrimitive2D&>(rPrimitive);
+ aHairlineRange.grow(fDiscreteHalfLineWidth);
+ }
+ }
- return (getWaveWidth() == rCompare.getWaveWidth()
- && getWaveHeight() == rCompare.getWaveHeight());
- }
+ return aHairlineRange;
+}
- return false;
- }
+// provide unique ID
+sal_uInt32 PolygonStrokePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D;
+}
- basegfx::B2DRange PolygonWavePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
- {
- // get range of parent
- basegfx::B2DRange aRetval(PolygonStrokePrimitive2D::getB2DRange(rViewInformation));
+Primitive2DReference PolygonWavePrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getB2DPolygon().count())
+ return nullptr;
- // if WaveHeight, grow by it
- if(basegfx::fTools::more(getWaveHeight(), 0.0))
- {
- aRetval.grow(getWaveHeight());
- }
+ const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth()));
+ const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight()));
- // if line width, grow by it
- if(basegfx::fTools::more(getLineAttribute().getWidth(), 0.0))
- {
- aRetval.grow(getLineAttribute().getWidth() * 0.5);
- }
+ if (bHasWidth && bHasHeight)
+ {
+ // create waveline curve
+ basegfx::B2DPolygon aWaveline(
+ basegfx::utils::createWaveline(getB2DPolygon(), getWaveWidth(), getWaveHeight()));
+ return new PolygonStrokePrimitive2D(std::move(aWaveline), getLineAttribute(),
+ getStrokeAttribute());
+ }
+ else
+ {
+ // flat waveline, decompose to simple line primitive
+ return new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(),
+ getStrokeAttribute());
+ }
+}
+
+PolygonWavePrimitive2D::PolygonWavePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute,
+ double fWaveWidth, double fWaveHeight)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute)
+ , mfWaveWidth(fWaveWidth)
+ , mfWaveHeight(fWaveHeight)
+{
+ if (mfWaveWidth < 0.0)
+ {
+ mfWaveWidth = 0.0;
+ }
- return aRetval;
- }
+ if (mfWaveHeight < 0.0)
+ {
+ mfWaveHeight = 0.0;
+ }
+}
+
+PolygonWavePrimitive2D::PolygonWavePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const attribute::LineAttribute& rLineAttribute,
+ double fWaveWidth, double fWaveHeight)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute)
+ , mfWaveWidth(fWaveWidth)
+ , mfWaveHeight(fWaveHeight)
+{
+ if (mfWaveWidth < 0.0)
+ {
+ mfWaveWidth = 0.0;
+ }
- // provide unique ID
- ImplPrimitive2DIDBlock(PolygonWavePrimitive2D, PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D)
+ if (mfWaveHeight < 0.0)
+ {
+ mfWaveHeight = 0.0;
+ }
+}
+bool PolygonWavePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (PolygonStrokePrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonWavePrimitive2D& rCompare
+ = static_cast<const PolygonWavePrimitive2D&>(rPrimitive);
- void PolygonStrokeArrowPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
- {
- // copy local polygon, it may be changed
- basegfx::B2DPolygon aLocalPolygon(getB2DPolygon());
- aLocalPolygon.removeDoublePoints();
- basegfx::B2DPolyPolygon aArrowA;
- basegfx::B2DPolyPolygon aArrowB;
+ return (getWaveWidth() == rCompare.getWaveWidth()
+ && getWaveHeight() == rCompare.getWaveHeight());
+ }
- if(!aLocalPolygon.isClosed() && aLocalPolygon.count() > 1)
- {
- // apply arrows
- const double fPolyLength(basegfx::utils::getLength(aLocalPolygon));
- double fStart(0.0);
- double fEnd(0.0);
- double fStartOverlap(0.0);
- double fEndOverlap(0.0);
-
- if(!getStart().isDefault() && getStart().isActive())
- {
- // create start arrow primitive and consume
- aArrowA = basegfx::utils::createAreaGeometryForLineStartEnd(
- aLocalPolygon, getStart().getB2DPolyPolygon(), true, getStart().getWidth(),
- fPolyLength, getStart().isCentered() ? 0.5 : 0.0, &fStart);
-
- // create some overlapping, compromise between straight and peaked markers
- // for marker width 0.3cm and marker line width 0.02cm
- fStartOverlap = getStart().getWidth() / 15.0;
- }
-
- if(!getEnd().isDefault() && getEnd().isActive())
- {
- // create end arrow primitive and consume
- aArrowB = basegfx::utils::createAreaGeometryForLineStartEnd(
- aLocalPolygon, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(),
- fPolyLength, getEnd().isCentered() ? 0.5 : 0.0, &fEnd);
-
- // create some overlapping
- fEndOverlap = getEnd().getWidth() / 15.0;
- }
-
- if(0.0 != fStart || 0.0 != fEnd)
- {
- // build new poly, consume something from old poly
- aLocalPolygon = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart-fStartOverlap, fPolyLength - fEnd + fEndOverlap, fPolyLength);
- }
- }
+ return false;
+}
- // add shaft
- rContainer.push_back(new
- PolygonStrokePrimitive2D(
- aLocalPolygon, getLineAttribute(), getStrokeAttribute()));
+basegfx::B2DRange
+PolygonWavePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // get range of parent
+ basegfx::B2DRange aRetval(PolygonStrokePrimitive2D::getB2DRange(rViewInformation));
- if(aArrowA.count())
- {
- rContainer.push_back(
- new PolyPolygonColorPrimitive2D(
- aArrowA, getLineAttribute().getColor()));
- }
+ // if WaveHeight, grow by it
+ if (basegfx::fTools::more(getWaveHeight(), 0.0))
+ {
+ aRetval.grow(getWaveHeight());
+ }
- if(aArrowB.count())
- {
- rContainer.push_back(
- new PolyPolygonColorPrimitive2D(
- aArrowB, getLineAttribute().getColor()));
- }
- }
+ // if line width, grow by it
+ if (basegfx::fTools::more(getLineAttribute().getWidth(), 0.0))
+ {
+ aRetval.grow(getLineAttribute().getWidth() * 0.5);
+ }
- PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
- const basegfx::B2DPolygon& rPolygon,
- const attribute::LineAttribute& rLineAttribute,
- const attribute::StrokeAttribute& rStrokeAttribute,
- const attribute::LineStartEndAttribute& rStart,
- const attribute::LineStartEndAttribute& rEnd)
- : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute),
- maStart(rStart),
- maEnd(rEnd)
- {
- }
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolygonWavePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D;
+}
- PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
- const basegfx::B2DPolygon& rPolygon,
- const attribute::LineAttribute& rLineAttribute,
- const attribute::LineStartEndAttribute& rStart,
- const attribute::LineStartEndAttribute& rEnd)
- : PolygonStrokePrimitive2D(rPolygon, rLineAttribute),
- maStart(rStart),
- maEnd(rEnd)
+Primitive2DReference PolygonStrokeArrowPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // copy local polygon, it may be changed
+ basegfx::B2DPolygon aLocalPolygon(getB2DPolygon());
+ aLocalPolygon.removeDoublePoints();
+ basegfx::B2DPolyPolygon aArrowA;
+ basegfx::B2DPolyPolygon aArrowB;
+
+ if (!aLocalPolygon.isClosed() && aLocalPolygon.count() > 1)
+ {
+ // apply arrows
+ const double fPolyLength(basegfx::utils::getLength(aLocalPolygon));
+ double fStart(0.0);
+ double fEnd(0.0);
+ double fStartOverlap(0.0);
+ double fEndOverlap(0.0);
+
+ if (!getStart().isDefault() && getStart().isActive())
{
+ // create start arrow primitive and consume
+ aArrowA = basegfx::utils::createAreaGeometryForLineStartEnd(
+ aLocalPolygon, getStart().getB2DPolyPolygon(), true, getStart().getWidth(),
+ fPolyLength, getStart().isCentered() ? 0.5 : 0.0, &fStart);
+
+ // create some overlapping, compromise between straight and peaked markers
+ // for marker width 0.3cm and marker line width 0.02cm
+ fStartOverlap = getStart().getWidth() / 15.0;
}
- bool PolygonStrokeArrowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ if (!getEnd().isDefault() && getEnd().isActive())
{
- if(PolygonStrokePrimitive2D::operator==(rPrimitive))
- {
- const PolygonStrokeArrowPrimitive2D& rCompare = static_cast<const PolygonStrokeArrowPrimitive2D&>(rPrimitive);
-
- return (getStart() == rCompare.getStart()
- && getEnd() == rCompare.getEnd());
- }
+ // create end arrow primitive and consume
+ aArrowB = basegfx::utils::createAreaGeometryForLineStartEnd(
+ aLocalPolygon, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(),
+ fPolyLength, getEnd().isCentered() ? 0.5 : 0.0, &fEnd);
- return false;
+ // create some overlapping
+ fEndOverlap = getEnd().getWidth() / 15.0;
}
- basegfx::B2DRange PolygonStrokeArrowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ if (0.0 != fStart || 0.0 != fEnd)
{
- if(getStart().isActive() || getEnd().isActive())
- {
- // use decomposition when line start/end is used
- return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
- }
- else
- {
- // get range from parent
- return PolygonStrokePrimitive2D::getB2DRange(rViewInformation);
- }
+ // build new poly, consume something from old poly
+ aLocalPolygon
+ = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart - fStartOverlap,
+ fPolyLength - fEnd + fEndOverlap, fPolyLength);
}
+ }
+
+ // add shaft
+ Primitive2DContainer aContainer;
+ aContainer.push_back(new PolygonStrokePrimitive2D(std::move(aLocalPolygon), getLineAttribute(),
+ getStrokeAttribute()));
+
+ if (aArrowA.count())
+ {
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(std::move(aArrowA), getLineAttribute().getColor()));
+ }
+
+ if (aArrowB.count())
+ {
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(std::move(aArrowB), getLineAttribute().getColor()));
+ }
+ return new GroupPrimitive2D(std::move(aContainer));
+}
+
+PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
+ const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute,
+ const attribute::LineStartEndAttribute& rStart, const attribute::LineStartEndAttribute& rEnd)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute)
+ , maStart(rStart)
+ , maEnd(rEnd)
+{
+}
+
+PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
+ const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
+ const attribute::LineStartEndAttribute& rStart, const attribute::LineStartEndAttribute& rEnd)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute)
+ , maStart(rStart)
+ , maEnd(rEnd)
+{
+}
- // provide unique ID
- ImplPrimitive2DIDBlock(PolygonStrokeArrowPrimitive2D, PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D)
+bool PolygonStrokeArrowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (PolygonStrokePrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonStrokeArrowPrimitive2D& rCompare
+ = static_cast<const PolygonStrokeArrowPrimitive2D&>(rPrimitive);
+ return (getStart() == rCompare.getStart() && getEnd() == rCompare.getEnd());
}
+
+ return false;
+}
+
+basegfx::B2DRange PolygonStrokeArrowPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getStart().isActive() || getEnd().isActive())
+ {
+ // use decomposition when line start/end is used
+ return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
+ }
+ else
+ {
+ // get range from parent
+ return PolygonStrokePrimitive2D::getB2DRange(rViewInformation);
+ }
+}
+
+// provide unique ID
+sal_uInt32 PolygonStrokeArrowPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D;
+}
+
} // end of namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */