LibreOffice Module drawinglayer (master) 1
polygonprimitive2d.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
31#include <com/sun/star/drawing/LineCap.hpp>
32#include <utility>
33
34using namespace com::sun::star;
35
36namespace
37{
38void implGrowHairline(basegfx::B2DRange& rRange,
39 const drawinglayer::geometry::ViewInformation2D& rViewInformation)
40{
41 if (!rRange.isEmpty())
42 {
43 // Calculate view-dependent hairline width
44 const basegfx::B2DVector aDiscreteSize(
45 rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
46 const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
47
48 if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
49 {
50 rRange.grow(fDiscreteHalfLineWidth);
51 }
52 }
53}
54}
55
57{
59 const basegfx::BColor& rBColor)
60 : maPolygon(std::move(aPolygon))
61 , maBColor(rBColor)
62{
63}
64
66{
67 if (BasePrimitive2D::operator==(rPrimitive))
68 {
69 const PolygonHairlinePrimitive2D& rCompare
70 = static_cast<const PolygonHairlinePrimitive2D&>(rPrimitive);
71
72 return (getB2DPolygon() == rCompare.getB2DPolygon() && getBColor() == rCompare.getBColor());
73 }
74
75 return false;
76}
77
80{
81 // this is a hairline, thus the line width is view-dependent. Get range of polygon
82 // as base size
84
85 // Calculate and grow by view-dependent hairline width
86 implGrowHairline(aRetval, rViewInformation);
87
88 // return range
89 return aRetval;
90}
91
92// provide unique ID
94{
96}
97
99 const basegfx::B2DPoint& rEnd,
100 const basegfx::BColor& rBColor)
102 , maStart(rStart)
103 , maEnd(rEnd)
104 , maBColor(rBColor)
105{
106}
107
109{
110 if (BasePrimitive2D::operator==(rPrimitive))
111 {
112 const SingleLinePrimitive2D& rCompare(
113 static_cast<const SingleLinePrimitive2D&>(rPrimitive));
114
115 return (getStart() == rCompare.getStart() && getEnd() == rCompare.getEnd()
116 && getBColor() == rCompare.getBColor());
117 }
118
119 return false;
120}
121
124{
125 basegfx::B2DRange aRetval(getStart(), getEnd());
126
127 // Calculate and grow by view-dependent hairline width
128 implGrowHairline(aRetval, rViewInformation);
129
130 return aRetval;
131}
132
134{
136}
137
140 const geometry::ViewInformation2D& /*rViewInformation*/) const
141{
142 if (getStart() == getEnd())
143 {
144 // single point
145 std::vector<basegfx::B2DPoint> aPoints = { getStart() };
146 Primitive2DContainer aSequence
147 = { new PointArrayPrimitive2D(std::move(aPoints), getBColor()) };
148 rVisitor.visit(aSequence);
149 }
150 else
151 {
152 // line
153 basegfx::B2DPolygon aPolygon{ getStart(), getEnd() };
154 Primitive2DContainer aSequence = { new PolygonHairlinePrimitive2D(aPolygon, getBColor()) };
155 rVisitor.visit(aSequence);
156 }
157}
158
160 const basegfx::BColor& rBColor)
162 , maB2DRange(rB2DRange)
163 , maBColor(rBColor)
164{
165}
166
168{
169 if (BasePrimitive2D::operator==(rPrimitive))
170 {
171 const LineRectanglePrimitive2D& rCompare(
172 static_cast<const LineRectanglePrimitive2D&>(rPrimitive));
173
174 return (getB2DRange() == rCompare.getB2DRange() && getBColor() == rCompare.getBColor());
175 }
176
177 return false;
178}
179
182{
184
185 // Calculate and grow by view-dependent hairline width
186 implGrowHairline(aRetval, rViewInformation);
187
188 return aRetval;
189}
190
192{
194}
195
198 const geometry::ViewInformation2D& /*rViewInformation*/) const
199{
200 if (getB2DRange().isEmpty())
201 {
202 // no geometry, done
203 return;
204 }
205
207 Primitive2DContainer aSequence = { new PolygonHairlinePrimitive2D(aPolygon, getBColor()) };
208 rVisitor.visit(aSequence);
209}
210
212 Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
213{
214 // calculate logic DashLength
215 const basegfx::B2DVector aDashVector(rViewInformation.getInverseObjectToViewTransformation()
217 const double fLogicDashLength(aDashVector.getX());
218
219 if (fLogicDashLength > 0.0 && !getRGBColorA().equal(getRGBColorB()))
220 {
221 // apply dashing; get line and gap snippets
222 std::vector<double> aDash;
223 basegfx::B2DPolyPolygon aDashedPolyPolyA;
224 basegfx::B2DPolyPolygon aDashedPolyPolyB;
225
226 aDash.push_back(fLogicDashLength);
227 aDash.push_back(fLogicDashLength);
228 basegfx::utils::applyLineDashing(getB2DPolygon(), aDash, &aDashedPolyPolyA,
229 &aDashedPolyPolyB, 2.0 * fLogicDashLength);
230
231 rContainer.push_back(
232 new PolyPolygonHairlinePrimitive2D(std::move(aDashedPolyPolyA), getRGBColorA()));
233 rContainer.push_back(
234 new PolyPolygonHairlinePrimitive2D(std::move(aDashedPolyPolyB), getRGBColorB()));
235 }
236 else
237 {
238 rContainer.push_back(new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA()));
239 }
240}
241
243 const basegfx::BColor& rRGBColorA,
244 const basegfx::BColor& rRGBColorB,
245 double fDiscreteDashLength)
246 : maPolygon(std::move(aPolygon))
247 , maRGBColorA(rRGBColorA)
248 , maRGBColorB(rRGBColorB)
249 , mfDiscreteDashLength(fDiscreteDashLength)
250{
251}
252
254{
255 if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
256 {
257 const PolygonMarkerPrimitive2D& rCompare
258 = static_cast<const PolygonMarkerPrimitive2D&>(rPrimitive);
259
260 return (getB2DPolygon() == rCompare.getB2DPolygon()
261 && getRGBColorA() == rCompare.getRGBColorA()
262 && getRGBColorB() == rCompare.getRGBColorB()
264 }
265
266 return false;
267}
268
271{
272 // this is a hairline, thus the line width is view-dependent. Get range of polygon
273 // as base size
275
276 if (!aRetval.isEmpty())
277 {
278 // Calculate view-dependent hairline width
279 const basegfx::B2DVector aDiscreteSize(
280 rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
281 const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
282
283 if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
284 {
285 aRetval.grow(fDiscreteHalfLineWidth);
286 }
287 }
288
289 // return range
290 return aRetval;
291}
292
295 const geometry::ViewInformation2D& rViewInformation) const
296{
297 bool bNeedNewDecomposition(false);
298
299 if (!getBuffered2DDecomposition().empty())
300 {
301 if (rViewInformation.getInverseObjectToViewTransformation()
303 {
304 bNeedNewDecomposition = true;
305 }
306 }
307
308 if (bNeedNewDecomposition)
309 {
310 // conditions of last local decomposition have changed, delete
313 }
314
315 if (getBuffered2DDecomposition().empty())
316 {
317 // remember last used InverseObjectToViewTransformation
318 PolygonMarkerPrimitive2D* pThat = const_cast<PolygonMarkerPrimitive2D*>(this);
320 = rViewInformation.getInverseObjectToViewTransformation();
321 }
322
323 // use parent implementation
325}
326
327// provide unique ID
329{
331}
332
333} // end of namespace
334
336{
338 Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
339{
340 if (!getB2DPolygon().count())
341 return;
342
343 // #i102241# try to simplify before usage
345 basegfx::B2DPolyPolygon aHairLinePolyPolygon;
346
347 if (getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
348 {
349 // no line dashing, just copy
350 aHairLinePolyPolygon.append(aB2DPolygon);
351 }
352 else
353 {
354 // apply LineStyle
355 basegfx::utils::applyLineDashing(aB2DPolygon, getStrokeAttribute().getDotDashArray(),
356 &aHairLinePolyPolygon, nullptr,
357 getStrokeAttribute().getFullDotDashLen());
358 }
359
360 const sal_uInt32 nCount(aHairLinePolyPolygon.count());
361
362 if (!getLineAttribute().isDefault() && getLineAttribute().getWidth())
363 {
364 // create fat line data
365 const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0);
366 const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin());
367 const css::drawing::LineCap aLineCap(getLineAttribute().getLineCap());
368 basegfx::B2DPolyPolygon aAreaPolyPolygon;
369 const double fMiterMinimumAngle(getLineAttribute().getMiterMinimumAngle());
370
371 for (sal_uInt32 a(0); a < nCount; a++)
372 {
373 // New version of createAreaGeometry; now creates bezier polygons
375 aHairLinePolyPolygon.getB2DPolygon(a), fHalfLineWidth, aLineJoin, aLineCap,
376 basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/,
377 0.4 /* default fMaxPartOfEdge*/, fMiterMinimumAngle));
378 }
379
380 // create primitive
381 for (sal_uInt32 b(0); b < aAreaPolyPolygon.count(); b++)
382 {
383 // put into single polyPolygon primitives to make clear that this is NOT meant
384 // to be painted as a single tools::PolyPolygon (XORed as fill rule). Alternatively, a
385 // melting process may be used here one day.
386 basegfx::B2DPolyPolygon aNewPolyPolygon(aAreaPolyPolygon.getB2DPolygon(b));
387 const basegfx::BColor aColor(getLineAttribute().getColor());
388 rContainer.push_back(
389 new PolyPolygonColorPrimitive2D(std::move(aNewPolyPolygon), aColor));
390 }
391 }
392 else
393 {
394 rContainer.push_back(new PolyPolygonHairlinePrimitive2D(std::move(aHairLinePolyPolygon),
395 getLineAttribute().getColor()));
396 }
397}
398
400 const attribute::LineAttribute& rLineAttribute,
401 attribute::StrokeAttribute aStrokeAttribute)
402 : maPolygon(std::move(aPolygon))
403 , maLineAttribute(rLineAttribute)
404 , maStrokeAttribute(std::move(aStrokeAttribute))
405 , maBufferedRange()
406{
407 // MM01: keep these - these are no curve-decompposers but just checks
408 // simplify curve segments: moved here to not need to use it
409 // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
411}
412
414 const attribute::LineAttribute& rLineAttribute)
415 : maPolygon(std::move(aPolygon))
416 , maLineAttribute(rLineAttribute)
417 , maBufferedRange()
418{
419 // MM01: keep these - these are no curve-decompposers but just checks
420 // simplify curve segments: moved here to not need to use it
421 // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
423}
424
426{
427 if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
428 {
429 const PolygonStrokePrimitive2D& rCompare
430 = static_cast<const PolygonStrokePrimitive2D&>(rPrimitive);
431
432 return (getB2DPolygon() == rCompare.getB2DPolygon()
433 && getLineAttribute() == rCompare.getLineAttribute()
434 && getStrokeAttribute() == rCompare.getStrokeAttribute());
435 }
436
437 return false;
438}
439
442{
444 {
445 // use the view-independent, buffered B2DRange
446 return maBufferedRange;
447 }
448
449 if (getLineAttribute().getWidth())
450 {
451 bool bUseDecomposition(false);
452
453 if (basegfx::B2DLineJoin::Miter == getLineAttribute().getLineJoin())
454 {
455 // if line is mitered, use parent call since mitered line
456 // geometry may use more space than the geometry grown by half line width
457 bUseDecomposition = true;
458 }
459
460 if (!bUseDecomposition && css::drawing::LineCap_SQUARE == getLineAttribute().getLineCap())
461 {
462 // when drawing::LineCap_SQUARE is used the below method to grow the polygon
463 // range by half line width will not work, so use decomposition. Interestingly,
464 // the grow method below works perfectly for LineCap_ROUND since the grow is in
465 // all directions and the rounded cap needs the same grow in all directions independent
466 // from its orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE
467
468 // NOTE: I thought about using [sqrt(2) * 0.5] a a factor for LineCap_SQUARE and not
469 // set bUseDecomposition. I even tried that it works. Then an auto-test failing showed
470 // not only that view-dependent stuff needs to be considered (what is done for the
471 // hairline case below), *BUT* also e.g. conversions to PNG/exports use the B2DRange
472 // of the geometry, so:
473 // - expanding by 1/2 LineWidth is OK for rounded
474 // - expanding by more (like sqrt(2) * 0.5 * LineWidth) immediately extends the size
475 // of e.g. geometry converted to PNG, plus many similar cases that cannot be thought
476 // of in advance.
477 // This means that converting those thought-experiment examples in (4) will and do lead
478 // to bigger e.g. Bitmap conversion(s), not avoiding but painting the free space. That
479 // could only be done by correctly and fully decomposing the geometry, including
480 // stroke, and accepting the cost...
481 bUseDecomposition = true;
482 }
483
484 if (bUseDecomposition)
485 {
486 // get correct range by using the decomposition fallback, reasons see above cases
487
488 // It is not a good idea to temporarily (re)set the PolygonStrokePrimitive2D
489 // to default. While it is understandable to use the speed advantage, it is
490 // bad for quite some reasons:
491 //
492 // (1) As described in include/drawinglayer/primitive2d/baseprimitive2d.hxx
493 // a Primitive is "noncopyable to make clear that a primitive is a read-only
494 // instance and copying or changing values is not intended". This is the base
495 // assumption for many decisions for Primitive handling.
496 // (2) For example, that the decomposition is *always* re-usable. It cannot change
497 // and is correct when it already exists since the values the decomposition is
498 // based on cannot change.
499 // (3) If this *is* done (like it was here) and the Primitive is derived from
500 // BufferedDecompositionPrimitive2D and thus buffers it's decomposition,
501 // the risk is that in this case the *wrong* decomposition will be used by
502 // other PrimitiveProcessors. Maybe not by the VclPixelProcessor2D/VclProcessor2D
503 // since it handles this primitive directly - not even sure for all cases.
504 // Sooner or later another PrimitiveProcessor will re-use this wrong temporary
505 // decomposition, and as an error, a non-stroked line will be painted/used.
506 // (4) The B2DRange is not strictly defined as minimal bound for the geometry,
507 // but it should be as small/tight as possible. Making it larger risks more
508 // area to be invalidated (repaint) and processed (all geometric stuff,l may
509 // include future and existing exports to other formats which are or will be
510 // implemented as PrimitiveProcessor). It is easy to imagine cases with much
511 // too large B2DRange - a line with a pattern that would solve to a single
512 // small start-rectangle and rest is empty, or a circle with a stroke that
513 // makes only a quarter of it visible.
514 //
515 // The reason to do this is speed, what is a good argument. But speed should
516 // only be used if the pair of [correctness/speed] does not sacrifice the correctness
517 // over the speed.
518 // Luckily there are alternatives to solve this and to keep [correctness/speed]
519 // valid:
520 //
521 // (a) Reset the temporary decomposition after having const-casted and
522 // changed maStrokeAttribute.
523 // Disadvantage: More const-cast hacks, plus this temporary decomposition
524 // will be potentially done repeatedly (every time
525 // PolygonStrokePrimitive2D::getB2DRange is called)
526 // (b) Use a temporary, local PolygonStrokePrimitive2D here, with neutral
527 // PolygonStrokePrimitive2D and call ::getB2DRange() at it. That way
528 // the buffered decomposition will not be harmed.
529 // Disadvantage: Same as (a), decomposition will be potentially done repeatedly
530 // (c) Use a temporary, local PolygonStrokePrimitive2D and buffer B2DRange
531 // locally for this Primitive. Due to (1)/(2) this cannot change, so
532 // when calculated once it is totally legal to use it.
533 //
534 // Thus here I would use (c): It accepts the disadvantages of (4) over speed, but
535 // avoids the errors/problems from (1-4).
536 // Additional argument for this: The hairline case below *also* uses the full
537 // B2DRange of the polygon, ignoring an evtl. stroke, so (4) applies
538 if (!getStrokeAttribute().isDefault())
539 {
540 // only do this if StrokeAttribute is used, else recursion may happen (!)
542 aTemporaryPrimitiveWithoutStroke(new primitive2d::PolygonStrokePrimitive2D(
545 = aTemporaryPrimitiveWithoutStroke
546 ->BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
547 }
548 else
549 {
550 // fallback to normal decompose, that result can be used for visualization
551 // later, too. Still buffer B2DRange in maBufferedRange, so it needs to be
552 // merged into one B2DRange only once
554 }
555 }
556 else
557 {
558 // for all other B2DLINEJOIN_* get the range from the base geometry
559 // and expand by half the line width.
561 maBufferedRange.grow(getLineAttribute().getWidth() * 0.5);
562 }
563
564 return maBufferedRange;
565 }
566
567 // It is a hairline, thus the line width is view-dependent. Get range of polygon
568 // as base size.
569 // CAUTION: Since a hairline *is* view-dependent,
570 // - either use maBufferedRange, additionally remember view-dependent
571 // factor & reset if that changes
572 // - or do not buffer for hairline -> not really needed, the range is buffered
573 // in the B2DPolygon, no decomposition is needed and a simple grow is cheap
574 basegfx::B2DRange aHairlineRange = getB2DPolygon().getB2DRange();
575
576 if (!aHairlineRange.isEmpty())
577 {
578 // Calculate view-dependent hairline width
579 const basegfx::B2DVector aDiscreteSize(
580 rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
581 const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
582
583 if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
584 {
585 aHairlineRange.grow(fDiscreteHalfLineWidth);
586 }
587 }
588
589 return aHairlineRange;
590}
591
592// provide unique ID
594{
596}
597
599 Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
600{
601 if (!getB2DPolygon().count())
602 return;
603
604 const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth()));
605 const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight()));
606
607 if (bHasWidth && bHasHeight)
608 {
609 // create waveline curve
610 basegfx::B2DPolygon aWaveline(
612 rContainer.push_back(new PolygonStrokePrimitive2D(std::move(aWaveline), getLineAttribute(),
614 }
615 else
616 {
617 // flat waveline, decompose to simple line primitive
618 rContainer.push_back(new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(),
620 }
621}
622
624 const attribute::LineAttribute& rLineAttribute,
625 const attribute::StrokeAttribute& rStrokeAttribute,
626 double fWaveWidth, double fWaveHeight)
627 : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute)
628 , mfWaveWidth(fWaveWidth)
629 , mfWaveHeight(fWaveHeight)
630{
631 if (mfWaveWidth < 0.0)
632 {
633 mfWaveWidth = 0.0;
634 }
635
636 if (mfWaveHeight < 0.0)
637 {
638 mfWaveHeight = 0.0;
639 }
640}
641
643 const attribute::LineAttribute& rLineAttribute,
644 double fWaveWidth, double fWaveHeight)
645 : PolygonStrokePrimitive2D(rPolygon, rLineAttribute)
646 , mfWaveWidth(fWaveWidth)
647 , mfWaveHeight(fWaveHeight)
648{
649 if (mfWaveWidth < 0.0)
650 {
651 mfWaveWidth = 0.0;
652 }
653
654 if (mfWaveHeight < 0.0)
655 {
656 mfWaveHeight = 0.0;
657 }
658}
659
661{
662 if (PolygonStrokePrimitive2D::operator==(rPrimitive))
663 {
664 const PolygonWavePrimitive2D& rCompare
665 = static_cast<const PolygonWavePrimitive2D&>(rPrimitive);
666
667 return (getWaveWidth() == rCompare.getWaveWidth()
668 && getWaveHeight() == rCompare.getWaveHeight());
669 }
670
671 return false;
672}
673
676{
677 // get range of parent
679
680 // if WaveHeight, grow by it
682 {
683 aRetval.grow(getWaveHeight());
684 }
685
686 // if line width, grow by it
687 if (basegfx::fTools::more(getLineAttribute().getWidth(), 0.0))
688 {
689 aRetval.grow(getLineAttribute().getWidth() * 0.5);
690 }
691
692 return aRetval;
693}
694
695// provide unique ID
697{
699}
700
702 Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
703{
704 // copy local polygon, it may be changed
705 basegfx::B2DPolygon aLocalPolygon(getB2DPolygon());
706 aLocalPolygon.removeDoublePoints();
709
710 if (!aLocalPolygon.isClosed() && aLocalPolygon.count() > 1)
711 {
712 // apply arrows
713 const double fPolyLength(basegfx::utils::getLength(aLocalPolygon));
714 double fStart(0.0);
715 double fEnd(0.0);
716 double fStartOverlap(0.0);
717 double fEndOverlap(0.0);
718
719 if (!getStart().isDefault() && getStart().isActive())
720 {
721 // create start arrow primitive and consume
723 aLocalPolygon, getStart().getB2DPolyPolygon(), true, getStart().getWidth(),
724 fPolyLength, getStart().isCentered() ? 0.5 : 0.0, &fStart);
725
726 // create some overlapping, compromise between straight and peaked markers
727 // for marker width 0.3cm and marker line width 0.02cm
728 fStartOverlap = getStart().getWidth() / 15.0;
729 }
730
731 if (!getEnd().isDefault() && getEnd().isActive())
732 {
733 // create end arrow primitive and consume
735 aLocalPolygon, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(),
736 fPolyLength, getEnd().isCentered() ? 0.5 : 0.0, &fEnd);
737
738 // create some overlapping
739 fEndOverlap = getEnd().getWidth() / 15.0;
740 }
741
742 if (0.0 != fStart || 0.0 != fEnd)
743 {
744 // build new poly, consume something from old poly
745 aLocalPolygon
746 = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart - fStartOverlap,
747 fPolyLength - fEnd + fEndOverlap, fPolyLength);
748 }
749 }
750
751 // add shaft
752 rContainer.push_back(new PolygonStrokePrimitive2D(std::move(aLocalPolygon), getLineAttribute(),
754
755 if (aArrowA.count())
756 {
757 rContainer.push_back(
758 new PolyPolygonColorPrimitive2D(std::move(aArrowA), getLineAttribute().getColor()));
759 }
760
761 if (aArrowB.count())
762 {
763 rContainer.push_back(
764 new PolyPolygonColorPrimitive2D(std::move(aArrowB), getLineAttribute().getColor()));
765 }
766}
767
769 const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
770 const attribute::StrokeAttribute& rStrokeAttribute,
772 : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute)
773 , maStart(rStart)
774 , maEnd(rEnd)
775{
776}
777
779 const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
781 : PolygonStrokePrimitive2D(rPolygon, rLineAttribute)
782 , maStart(rStart)
783 , maEnd(rEnd)
784{
785}
786
788{
789 if (PolygonStrokePrimitive2D::operator==(rPrimitive))
790 {
791 const PolygonStrokeArrowPrimitive2D& rCompare
792 = static_cast<const PolygonStrokeArrowPrimitive2D&>(rPrimitive);
793
794 return (getStart() == rCompare.getStart() && getEnd() == rCompare.getEnd());
795 }
796
797 return false;
798}
799
801 const geometry::ViewInformation2D& rViewInformation) const
802{
803 if (getStart().isActive() || getEnd().isActive())
804 {
805 // use decomposition when line start/end is used
806 return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
807 }
808 else
809 {
810 // get range from parent
811 return PolygonStrokePrimitive2D::getB2DRange(rViewInformation);
812 }
813}
814
815// provide unique ID
817{
819}
820
821} // end of namespace
822
823/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point maStart
const Point maEnd
B2DPolygon maPolygon
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
sal_uInt32 count() const
bool isClosed() const
B2DRange const & getB2DRange() const
sal_uInt32 count() const
double getLength() const
void grow(TYPE fValue)
bool isEmpty() const
TYPE getX() const
const basegfx::B2DHomMatrix & getInverseObjectToViewTransformation() const
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const
The default implementation will use getDecomposition results to create the range.
const Primitive2DContainer & getBuffered2DDecomposition() const
access methods to maBuffered2DDecomposition.
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
The getDecomposition default implementation will on demand use create2DDecomposition() if maBuffered2...
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
return as PolygonHairlinePrimitive2D
const basegfx::B2DRange & getB2DRange() const
data read access
LineRectanglePrimitive2D(const basegfx::B2DRange &rB2DRange, const basegfx::BColor &rBColor)
constructor
PolygonHairlinePrimitive2D(basegfx::B2DPolygon aPolygon, const basegfx::BColor &rBColor)
constructor
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
const basegfx::B2DPolygon & getB2DPolygon() const
data read access
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
Override standard getDecomposition to be view-dependent here.
PolygonMarkerPrimitive2D(basegfx::B2DPolygon aPolygon, const basegfx::BColor &rRGBColorA, const basegfx::BColor &rRGBColorB, double fDiscreteDashLength)
constructor
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
const basegfx::B2DPolygon & getB2DPolygon() const
data read access
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &rViewInformation) const override
local decomposition.
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
basegfx::B2DHomMatrix maLastInverseObjectToViewTransformation
decomposition is view-dependent, remember last InverseObjectToViewTransformation
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &rViewInformation) const override
local decomposition.
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
const attribute::LineStartEndAttribute & getStart() const
data read access
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
const attribute::LineStartEndAttribute & getEnd() const
PolygonStrokeArrowPrimitive2D(const basegfx::B2DPolygon &rPolygon, const attribute::LineAttribute &rLineAttribute, const attribute::StrokeAttribute &rStrokeAttribute, const attribute::LineStartEndAttribute &rStart, const attribute::LineStartEndAttribute &rEnd)
constructor
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
const attribute::StrokeAttribute & getStrokeAttribute() const
PolygonStrokePrimitive2D(basegfx::B2DPolygon aPolygon, const attribute::LineAttribute &rLineAttribute, attribute::StrokeAttribute aStrokeAttribute)
constructor
basegfx::B2DRange maBufferedRange
the buffered result of PolygonStrokePrimitive2D::getB2DRange
const basegfx::B2DPolygon & getB2DPolygon() const
data read access
const attribute::LineAttribute & getLineAttribute() const
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &rViewInformation) const override
local decomposition.
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &rViewInformation) const override
local decomposition.
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
PolygonWavePrimitive2D(const basegfx::B2DPolygon &rPolygon, const attribute::LineAttribute &rLineAttribute, const attribute::StrokeAttribute &rStrokeAttribute, double fWaveWidth, double fWaveHeight)
constructor
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
virtual void visit(const Primitive2DReference &)=0
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
const basegfx::B2DPoint & getStart() const
data read access
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
return as PolygonHairlinePrimitive2D
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
SingleLinePrimitive2D(const basegfx::B2DPoint &rStart, const basegfx::B2DPoint &rEnd, const basegfx::BColor &rBColor)
constructor
int nCount
#define PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D
#define PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
uno_Any a
bool more(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
bool equal(T const &rfValA, T const &rfValB)
double getLength(const B2DPolygon &rCandidate)
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
B2DPolyPolygon createAreaGeometry(const B2DPolygon &rCandidate, double fHalfLineWidth, B2DLineJoin eJoin, css::drawing::LineCap eCap, double fMaxAllowedAngle=basegfx::deg2rad(12.5), double fMaxPartOfEdge=0.4, double fMiterMinimumAngle=basegfx::deg2rad(15.0))
void applyLineDashing(const B2DPolygon &rCandidate, const std::vector< double > &rDotDashArray, B2DPolyPolygon *pLineTarget, B2DPolyPolygon *pGapTarget, double fDotDashLength)
B2DPolygon createWaveline(const B2DPolygon &rCandidate, double fWaveWidth, double fWaveHeight)
B2DPolygon getSnippetAbsolute(const B2DPolygon &rCandidate, double fFrom, double fTo, double fLength)
B2DPolygon simplifyCurveSegments(const B2DPolygon &rCandidate)
B2DPolyPolygon createAreaGeometryForLineStartEnd(const B2DPolygon &rCandidate, const B2DPolyPolygon &rArrow, bool bStart, double fWidth, double fCandidateLength, double fDockingPosition, double *pConsumedLength=nullptr, double fShift=0.0)
constexpr double deg2rad(double v)
bool isActive()