1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <basegfx/polygon/b2dpolygontools.hxx>
21 #include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
22 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
23 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
24 #include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
25 #include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
26 #include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
27 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
28 #include <drawinglayer/geometry/viewinformation2d.hxx>
29 #include <basegfx/polygon/b2dlinegeometry.hxx>
30 #include <com/sun/star/drawing/LineCap.hpp>
33 using namespace com::sun::star
;
35 namespace drawinglayer::primitive2d
37 PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(basegfx::B2DPolygon aPolygon
,
38 const basegfx::BColor
& rBColor
)
39 : maPolygon(std::move(aPolygon
))
44 bool PolygonHairlinePrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
46 if (BasePrimitive2D::operator==(rPrimitive
))
48 const PolygonHairlinePrimitive2D
& rCompare
49 = static_cast<const PolygonHairlinePrimitive2D
&>(rPrimitive
);
51 return (getB2DPolygon() == rCompare
.getB2DPolygon() && getBColor() == rCompare
.getBColor());
58 PolygonHairlinePrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
60 // this is a hairline, thus the line width is view-dependent. Get range of polygon
62 basegfx::B2DRange
aRetval(getB2DPolygon().getB2DRange());
64 if (!aRetval
.isEmpty())
66 // Calculate view-dependent hairline width
67 const basegfx::B2DVector
aDiscreteSize(
68 rViewInformation
.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
69 const double fDiscreteHalfLineWidth(aDiscreteSize
.getLength() * 0.5);
71 if (basegfx::fTools::more(fDiscreteHalfLineWidth
, 0.0))
73 aRetval
.grow(fDiscreteHalfLineWidth
);
82 sal_uInt32
PolygonHairlinePrimitive2D::getPrimitive2DID() const
84 return PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
;
87 void PolygonMarkerPrimitive2D::create2DDecomposition(
88 Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& rViewInformation
) const
90 // calculate logic DashLength
91 const basegfx::B2DVector
aDashVector(rViewInformation
.getInverseObjectToViewTransformation()
92 * basegfx::B2DVector(getDiscreteDashLength(), 0.0));
93 const double fLogicDashLength(aDashVector
.getX());
95 if (fLogicDashLength
> 0.0 && !getRGBColorA().equal(getRGBColorB()))
97 // apply dashing; get line and gap snippets
98 std::vector
<double> aDash
;
99 basegfx::B2DPolyPolygon aDashedPolyPolyA
;
100 basegfx::B2DPolyPolygon aDashedPolyPolyB
;
102 aDash
.push_back(fLogicDashLength
);
103 aDash
.push_back(fLogicDashLength
);
104 basegfx::utils::applyLineDashing(getB2DPolygon(), aDash
, &aDashedPolyPolyA
,
105 &aDashedPolyPolyB
, 2.0 * fLogicDashLength
);
107 rContainer
.push_back(
108 new PolyPolygonHairlinePrimitive2D(std::move(aDashedPolyPolyA
), getRGBColorA()));
109 rContainer
.push_back(
110 new PolyPolygonHairlinePrimitive2D(std::move(aDashedPolyPolyB
), getRGBColorB()));
114 rContainer
.push_back(new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA()));
118 PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D(basegfx::B2DPolygon aPolygon
,
119 const basegfx::BColor
& rRGBColorA
,
120 const basegfx::BColor
& rRGBColorB
,
121 double fDiscreteDashLength
)
122 : maPolygon(std::move(aPolygon
))
123 , maRGBColorA(rRGBColorA
)
124 , maRGBColorB(rRGBColorB
)
125 , mfDiscreteDashLength(fDiscreteDashLength
)
129 bool PolygonMarkerPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
131 if (BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
133 const PolygonMarkerPrimitive2D
& rCompare
134 = static_cast<const PolygonMarkerPrimitive2D
&>(rPrimitive
);
136 return (getB2DPolygon() == rCompare
.getB2DPolygon()
137 && getRGBColorA() == rCompare
.getRGBColorA()
138 && getRGBColorB() == rCompare
.getRGBColorB()
139 && getDiscreteDashLength() == rCompare
.getDiscreteDashLength());
146 PolygonMarkerPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
148 // this is a hairline, thus the line width is view-dependent. Get range of polygon
150 basegfx::B2DRange
aRetval(getB2DPolygon().getB2DRange());
152 if (!aRetval
.isEmpty())
154 // Calculate view-dependent hairline width
155 const basegfx::B2DVector
aDiscreteSize(
156 rViewInformation
.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
157 const double fDiscreteHalfLineWidth(aDiscreteSize
.getLength() * 0.5);
159 if (basegfx::fTools::more(fDiscreteHalfLineWidth
, 0.0))
161 aRetval
.grow(fDiscreteHalfLineWidth
);
169 void PolygonMarkerPrimitive2D::get2DDecomposition(
170 Primitive2DDecompositionVisitor
& rVisitor
,
171 const geometry::ViewInformation2D
& rViewInformation
) const
173 bool bNeedNewDecomposition(false);
175 if (!getBuffered2DDecomposition().empty())
177 if (rViewInformation
.getInverseObjectToViewTransformation()
178 != maLastInverseObjectToViewTransformation
)
180 bNeedNewDecomposition
= true;
184 if (bNeedNewDecomposition
)
186 // conditions of last local decomposition have changed, delete
187 const_cast<PolygonMarkerPrimitive2D
*>(this)->setBuffered2DDecomposition(
188 Primitive2DContainer());
191 if (getBuffered2DDecomposition().empty())
193 // remember last used InverseObjectToViewTransformation
194 PolygonMarkerPrimitive2D
* pThat
= const_cast<PolygonMarkerPrimitive2D
*>(this);
195 pThat
->maLastInverseObjectToViewTransformation
196 = rViewInformation
.getInverseObjectToViewTransformation();
199 // use parent implementation
200 BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor
, rViewInformation
);
204 sal_uInt32
PolygonMarkerPrimitive2D::getPrimitive2DID() const
206 return PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D
;
209 } // end of namespace
211 namespace drawinglayer::primitive2d
213 void PolygonStrokePrimitive2D::create2DDecomposition(
214 Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
216 if (!getB2DPolygon().count())
219 // #i102241# try to simplify before usage
220 const basegfx::B2DPolygon
aB2DPolygon(basegfx::utils::simplifyCurveSegments(getB2DPolygon()));
221 basegfx::B2DPolyPolygon aHairLinePolyPolygon
;
223 if (getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
225 // no line dashing, just copy
226 aHairLinePolyPolygon
.append(aB2DPolygon
);
231 basegfx::utils::applyLineDashing(aB2DPolygon
, getStrokeAttribute().getDotDashArray(),
232 &aHairLinePolyPolygon
, nullptr,
233 getStrokeAttribute().getFullDotDashLen());
236 const sal_uInt32
nCount(aHairLinePolyPolygon
.count());
238 if (!getLineAttribute().isDefault() && getLineAttribute().getWidth())
240 // create fat line data
241 const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0);
242 const basegfx::B2DLineJoin
aLineJoin(getLineAttribute().getLineJoin());
243 const css::drawing::LineCap
aLineCap(getLineAttribute().getLineCap());
244 basegfx::B2DPolyPolygon aAreaPolyPolygon
;
245 const double fMiterMinimumAngle(getLineAttribute().getMiterMinimumAngle());
247 for (sal_uInt32
a(0); a
< nCount
; a
++)
249 // New version of createAreaGeometry; now creates bezier polygons
250 aAreaPolyPolygon
.append(basegfx::utils::createAreaGeometry(
251 aHairLinePolyPolygon
.getB2DPolygon(a
), fHalfLineWidth
, aLineJoin
, aLineCap
,
252 basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/,
253 0.4 /* default fMaxPartOfEdge*/, fMiterMinimumAngle
));
257 for (sal_uInt32
b(0); b
< aAreaPolyPolygon
.count(); b
++)
259 // put into single polyPolygon primitives to make clear that this is NOT meant
260 // to be painted as a single tools::PolyPolygon (XORed as fill rule). Alternatively, a
261 // melting process may be used here one day.
262 basegfx::B2DPolyPolygon
aNewPolyPolygon(aAreaPolyPolygon
.getB2DPolygon(b
));
263 const basegfx::BColor
aColor(getLineAttribute().getColor());
264 rContainer
.push_back(
265 new PolyPolygonColorPrimitive2D(std::move(aNewPolyPolygon
), aColor
));
270 rContainer
.push_back(new PolyPolygonHairlinePrimitive2D(std::move(aHairLinePolyPolygon
),
271 getLineAttribute().getColor()));
275 PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(basegfx::B2DPolygon aPolygon
,
276 const attribute::LineAttribute
& rLineAttribute
,
277 attribute::StrokeAttribute aStrokeAttribute
)
278 : maPolygon(std::move(aPolygon
))
279 , maLineAttribute(rLineAttribute
)
280 , maStrokeAttribute(std::move(aStrokeAttribute
))
283 // MM01: keep these - these are no curve-decompposers but just checks
284 // simplify curve segments: moved here to not need to use it
285 // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
286 maPolygon
= basegfx::utils::simplifyCurveSegments(maPolygon
);
289 PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(basegfx::B2DPolygon aPolygon
,
290 const attribute::LineAttribute
& rLineAttribute
)
291 : maPolygon(std::move(aPolygon
))
292 , maLineAttribute(rLineAttribute
)
295 // MM01: keep these - these are no curve-decompposers but just checks
296 // simplify curve segments: moved here to not need to use it
297 // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
298 maPolygon
= basegfx::utils::simplifyCurveSegments(maPolygon
);
301 bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
303 if (BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
305 const PolygonStrokePrimitive2D
& rCompare
306 = static_cast<const PolygonStrokePrimitive2D
&>(rPrimitive
);
308 return (getB2DPolygon() == rCompare
.getB2DPolygon()
309 && getLineAttribute() == rCompare
.getLineAttribute()
310 && getStrokeAttribute() == rCompare
.getStrokeAttribute());
317 PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
319 if (!maBufferedRange
.isEmpty())
321 // use the view-independent, buffered B2DRange
322 return maBufferedRange
;
325 if (getLineAttribute().getWidth())
327 bool bUseDecomposition(false);
329 if (basegfx::B2DLineJoin::Miter
== getLineAttribute().getLineJoin())
331 // if line is mitered, use parent call since mitered line
332 // geometry may use more space than the geometry grown by half line width
333 bUseDecomposition
= true;
336 if (!bUseDecomposition
&& css::drawing::LineCap_SQUARE
== getLineAttribute().getLineCap())
338 // when drawing::LineCap_SQUARE is used the below method to grow the polygon
339 // range by half line width will not work, so use decomposition. Interestingly,
340 // the grow method below works perfectly for LineCap_ROUND since the grow is in
341 // all directions and the rounded cap needs the same grow in all directions independent
342 // from its orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE
344 // NOTE: I thought about using [sqrt(2) * 0.5] a a factor for LineCap_SQUARE and not
345 // set bUseDecomposition. I even tried that it works. Then an auto-test failing showed
346 // not only that view-dependent stuff needs to be considered (what is done for the
347 // hairline case below), *BUT* also e.g. conversions to PNG/exports use the B2DRange
348 // of the geometry, so:
349 // - expanding by 1/2 LineWidth is OK for rounded
350 // - expanding by more (like sqrt(2) * 0.5 * LineWidth) immediately extends the size
351 // of e.g. geometry converted to PNG, plus many similar cases that cannot be thought
353 // This means that converting those thought-experiment examples in (4) will and do lead
354 // to bigger e.g. Bitmap conversion(s), not avoiding but painting the free space. That
355 // could only be done by correctly and fully decomposing the geometry, including
356 // stroke, and accepting the cost...
357 bUseDecomposition
= true;
360 if (bUseDecomposition
)
362 // get correct range by using the decomposition fallback, reasons see above cases
364 // It is not a good idea to temporarily (re)set the PolygonStrokePrimitive2D
365 // to default. While it is understandable to use the speed advantage, it is
366 // bad for quite some reasons:
368 // (1) As described in include/drawinglayer/primitive2d/baseprimitive2d.hxx
369 // a Primitive is "noncopyable to make clear that a primitive is a read-only
370 // instance and copying or changing values is not intended". This is the base
371 // assumption for many decisions for Primitive handling.
372 // (2) For example, that the decomposition is *always* re-usable. It cannot change
373 // and is correct when it already exists since the values the decomposition is
374 // based on cannot change.
375 // (3) If this *is* done (like it was here) and the Primitive is derived from
376 // BufferedDecompositionPrimitive2D and thus buffers it's decomposition,
377 // the risk is that in this case the *wrong* decomposition will be used by
378 // other PrimitiveProcessors. Maybe not by the VclPixelProcessor2D/VclProcessor2D
379 // since it handles this primitive directly - not even sure for all cases.
380 // Sooner or later another PrimitiveProcessor will re-use this wrong temporary
381 // decomposition, and as an error, a non-stroked line will be painted/used.
382 // (4) The B2DRange is not strictly defined as minimal bound for the geometry,
383 // but it should be as small/tight as possible. Making it larger risks more
384 // area to be invalidated (repaint) and processed (all geometric stuff,l may
385 // include future and existing exports to other formats which are or will be
386 // implemented as PrimitiveProcessor). It is easy to imagine cases with much
387 // too large B2DRange - a line with a pattern that would solve to a single
388 // small start-rectangle and rest is empty, or a circle with a stroke that
389 // makes only a quarter of it visible.
391 // The reason to do this is speed, what is a good argument. But speed should
392 // only be used if the pair of [correctness/speed] does not sacrifice the correctness
394 // Luckily there are alternatives to solve this and to keep [correctness/speed]
397 // (a) Reset the temporary decomposition after having const-casted and
398 // changed maStrokeAttribute.
399 // Disadvantage: More const-cast hacks, plus this temporary decomposition
400 // will be potentially done repeatedly (every time
401 // PolygonStrokePrimitive2D::getB2DRange is called)
402 // (b) Use a temporary, local PolygonStrokePrimitive2D here, with neutral
403 // PolygonStrokePrimitive2D and call ::getB2DRange() at it. That way
404 // the buffered decomposition will not be harmed.
405 // Disadvantage: Same as (a), decomposition will be potentially done repeatedly
406 // (c) Use a temporary, local PolygonStrokePrimitive2D and buffer B2DRange
407 // locally for this Primitive. Due to (1)/(2) this cannot change, so
408 // when calculated once it is totally legal to use it.
410 // Thus here I would use (c): It accepts the disadvantages of (4) over speed, but
411 // avoids the errors/problems from (1-4).
412 // Additional argument for this: The hairline case below *also* uses the full
413 // B2DRange of the polygon, ignoring an evtl. stroke, so (4) applies
414 if (!getStrokeAttribute().isDefault())
416 // only do this if StrokeAttribute is used, else recursion may happen (!)
417 const rtl::Reference
<primitive2d::PolygonStrokePrimitive2D
>
418 aTemporaryPrimitiveWithoutStroke(new primitive2d::PolygonStrokePrimitive2D(
419 getB2DPolygon(), getLineAttribute()));
421 = aTemporaryPrimitiveWithoutStroke
422 ->BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation
);
426 // fallback to normal decompose, that result can be used for visualization
427 // later, too. Still buffer B2DRange in maBufferedRange, so it needs to be
428 // merged into one B2DRange only once
429 maBufferedRange
= BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation
);
434 // for all other B2DLINEJOIN_* get the range from the base geometry
435 // and expand by half the line width.
436 maBufferedRange
= getB2DPolygon().getB2DRange();
437 maBufferedRange
.grow(getLineAttribute().getWidth() * 0.5);
440 return maBufferedRange
;
443 // It is a hairline, thus the line width is view-dependent. Get range of polygon
445 // CAUTION: Since a hairline *is* view-dependent,
446 // - either use maBufferedRange, additionally remember view-dependent
447 // factor & reset if that changes
448 // - or do not buffer for hairline -> not really needed, the range is buffered
449 // in the B2DPolygon, no decomposition is needed and a simple grow is cheap
450 basegfx::B2DRange aHairlineRange
= getB2DPolygon().getB2DRange();
452 if (!aHairlineRange
.isEmpty())
454 // Calculate view-dependent hairline width
455 const basegfx::B2DVector
aDiscreteSize(
456 rViewInformation
.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
457 const double fDiscreteHalfLineWidth(aDiscreteSize
.getLength() * 0.5);
459 if (basegfx::fTools::more(fDiscreteHalfLineWidth
, 0.0))
461 aHairlineRange
.grow(fDiscreteHalfLineWidth
);
465 return aHairlineRange
;
469 sal_uInt32
PolygonStrokePrimitive2D::getPrimitive2DID() const
471 return PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
;
474 void PolygonWavePrimitive2D::create2DDecomposition(
475 Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
477 if (!getB2DPolygon().count())
480 const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth()));
481 const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight()));
483 if (bHasWidth
&& bHasHeight
)
485 // create waveline curve
486 basegfx::B2DPolygon
aWaveline(
487 basegfx::utils::createWaveline(getB2DPolygon(), getWaveWidth(), getWaveHeight()));
488 rContainer
.push_back(new PolygonStrokePrimitive2D(std::move(aWaveline
), getLineAttribute(),
489 getStrokeAttribute()));
493 // flat waveline, decompose to simple line primitive
494 rContainer
.push_back(new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(),
495 getStrokeAttribute()));
499 PolygonWavePrimitive2D::PolygonWavePrimitive2D(const basegfx::B2DPolygon
& rPolygon
,
500 const attribute::LineAttribute
& rLineAttribute
,
501 const attribute::StrokeAttribute
& rStrokeAttribute
,
502 double fWaveWidth
, double fWaveHeight
)
503 : PolygonStrokePrimitive2D(rPolygon
, rLineAttribute
, rStrokeAttribute
)
504 , mfWaveWidth(fWaveWidth
)
505 , mfWaveHeight(fWaveHeight
)
507 if (mfWaveWidth
< 0.0)
512 if (mfWaveHeight
< 0.0)
518 PolygonWavePrimitive2D::PolygonWavePrimitive2D(const basegfx::B2DPolygon
& rPolygon
,
519 const attribute::LineAttribute
& rLineAttribute
,
520 double fWaveWidth
, double fWaveHeight
)
521 : PolygonStrokePrimitive2D(rPolygon
, rLineAttribute
)
522 , mfWaveWidth(fWaveWidth
)
523 , mfWaveHeight(fWaveHeight
)
525 if (mfWaveWidth
< 0.0)
530 if (mfWaveHeight
< 0.0)
536 bool PolygonWavePrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
538 if (PolygonStrokePrimitive2D::operator==(rPrimitive
))
540 const PolygonWavePrimitive2D
& rCompare
541 = static_cast<const PolygonWavePrimitive2D
&>(rPrimitive
);
543 return (getWaveWidth() == rCompare
.getWaveWidth()
544 && getWaveHeight() == rCompare
.getWaveHeight());
551 PolygonWavePrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
553 // get range of parent
554 basegfx::B2DRange
aRetval(PolygonStrokePrimitive2D::getB2DRange(rViewInformation
));
556 // if WaveHeight, grow by it
557 if (basegfx::fTools::more(getWaveHeight(), 0.0))
559 aRetval
.grow(getWaveHeight());
562 // if line width, grow by it
563 if (basegfx::fTools::more(getLineAttribute().getWidth(), 0.0))
565 aRetval
.grow(getLineAttribute().getWidth() * 0.5);
572 sal_uInt32
PolygonWavePrimitive2D::getPrimitive2DID() const
574 return PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D
;
577 void PolygonStrokeArrowPrimitive2D::create2DDecomposition(
578 Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
580 // copy local polygon, it may be changed
581 basegfx::B2DPolygon
aLocalPolygon(getB2DPolygon());
582 aLocalPolygon
.removeDoublePoints();
583 basegfx::B2DPolyPolygon aArrowA
;
584 basegfx::B2DPolyPolygon aArrowB
;
586 if (!aLocalPolygon
.isClosed() && aLocalPolygon
.count() > 1)
589 const double fPolyLength(basegfx::utils::getLength(aLocalPolygon
));
592 double fStartOverlap(0.0);
593 double fEndOverlap(0.0);
595 if (!getStart().isDefault() && getStart().isActive())
597 // create start arrow primitive and consume
598 aArrowA
= basegfx::utils::createAreaGeometryForLineStartEnd(
599 aLocalPolygon
, getStart().getB2DPolyPolygon(), true, getStart().getWidth(),
600 fPolyLength
, getStart().isCentered() ? 0.5 : 0.0, &fStart
);
602 // create some overlapping, compromise between straight and peaked markers
603 // for marker width 0.3cm and marker line width 0.02cm
604 fStartOverlap
= getStart().getWidth() / 15.0;
607 if (!getEnd().isDefault() && getEnd().isActive())
609 // create end arrow primitive and consume
610 aArrowB
= basegfx::utils::createAreaGeometryForLineStartEnd(
611 aLocalPolygon
, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(),
612 fPolyLength
, getEnd().isCentered() ? 0.5 : 0.0, &fEnd
);
614 // create some overlapping
615 fEndOverlap
= getEnd().getWidth() / 15.0;
618 if (0.0 != fStart
|| 0.0 != fEnd
)
620 // build new poly, consume something from old poly
622 = basegfx::utils::getSnippetAbsolute(aLocalPolygon
, fStart
- fStartOverlap
,
623 fPolyLength
- fEnd
+ fEndOverlap
, fPolyLength
);
628 rContainer
.push_back(new PolygonStrokePrimitive2D(std::move(aLocalPolygon
), getLineAttribute(),
629 getStrokeAttribute()));
633 rContainer
.push_back(
634 new PolyPolygonColorPrimitive2D(std::move(aArrowA
), getLineAttribute().getColor()));
639 rContainer
.push_back(
640 new PolyPolygonColorPrimitive2D(std::move(aArrowB
), getLineAttribute().getColor()));
644 PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
645 const basegfx::B2DPolygon
& rPolygon
, const attribute::LineAttribute
& rLineAttribute
,
646 const attribute::StrokeAttribute
& rStrokeAttribute
,
647 const attribute::LineStartEndAttribute
& rStart
, const attribute::LineStartEndAttribute
& rEnd
)
648 : PolygonStrokePrimitive2D(rPolygon
, rLineAttribute
, rStrokeAttribute
)
654 PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
655 const basegfx::B2DPolygon
& rPolygon
, const attribute::LineAttribute
& rLineAttribute
,
656 const attribute::LineStartEndAttribute
& rStart
, const attribute::LineStartEndAttribute
& rEnd
)
657 : PolygonStrokePrimitive2D(rPolygon
, rLineAttribute
)
663 bool PolygonStrokeArrowPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
665 if (PolygonStrokePrimitive2D::operator==(rPrimitive
))
667 const PolygonStrokeArrowPrimitive2D
& rCompare
668 = static_cast<const PolygonStrokeArrowPrimitive2D
&>(rPrimitive
);
670 return (getStart() == rCompare
.getStart() && getEnd() == rCompare
.getEnd());
676 basegfx::B2DRange
PolygonStrokeArrowPrimitive2D::getB2DRange(
677 const geometry::ViewInformation2D
& rViewInformation
) const
679 if (getStart().isActive() || getEnd().isActive())
681 // use decomposition when line start/end is used
682 return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation
);
686 // get range from parent
687 return PolygonStrokePrimitive2D::getB2DRange(rViewInformation
);
692 sal_uInt32
PolygonStrokeArrowPrimitive2D::getPrimitive2DID() const
694 return PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
;
697 } // end of namespace
699 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */