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 <drawinglayer/primitive2d/polygonprimitive2d.hxx>
21 #include <basegfx/tools/canvastools.hxx>
22 #include <basegfx/polygon/b2dpolygontools.hxx>
23 #include <basegfx/polygon/b2dpolypolygontools.hxx>
24 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
25 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
26 #include <drawinglayer/geometry/viewinformation2d.hxx>
27 #include <basegfx/polygon/b2dlinegeometry.hxx>
28 #include <com/sun/star/drawing/LineCap.hpp>
30 //////////////////////////////////////////////////////////////////////////////
32 using namespace com::sun::star
;
34 //////////////////////////////////////////////////////////////////////////////
36 namespace drawinglayer
40 PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(
41 const basegfx::B2DPolygon
& rPolygon
,
42 const basegfx::BColor
& rBColor
)
49 bool PolygonHairlinePrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
51 if(BasePrimitive2D::operator==(rPrimitive
))
53 const PolygonHairlinePrimitive2D
& rCompare
= (PolygonHairlinePrimitive2D
&)rPrimitive
;
55 return (getB2DPolygon() == rCompare
.getB2DPolygon()
56 && getBColor() == rCompare
.getBColor());
62 basegfx::B2DRange
PolygonHairlinePrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
64 // this is a hairline, thus the line width is view-dependent. Get range of polygon
66 basegfx::B2DRange
aRetval(getB2DPolygon().getB2DRange());
68 if(!aRetval
.isEmpty())
70 // Calculate view-dependent hairline width
71 const basegfx::B2DVector
aDiscreteSize(rViewInformation
.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
72 const double fDiscreteHalfLineWidth(aDiscreteSize
.getLength() * 0.5);
74 if(basegfx::fTools::more(fDiscreteHalfLineWidth
, 0.0))
76 aRetval
.grow(fDiscreteHalfLineWidth
);
85 ImplPrimitive2DIDBlock(PolygonHairlinePrimitive2D
, PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
)
87 } // end of namespace primitive2d
88 } // end of namespace drawinglayer
90 //////////////////////////////////////////////////////////////////////////////
92 namespace drawinglayer
96 Primitive2DSequence
PolygonMarkerPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& rViewInformation
) const
98 // calculate logic DashLength
99 const basegfx::B2DVector
aDashVector(rViewInformation
.getInverseObjectToViewTransformation() * basegfx::B2DVector(getDiscreteDashLength(), 0.0));
100 const double fLogicDashLength(aDashVector
.getX());
102 if(fLogicDashLength
> 0.0 && !getRGBColorA().equal(getRGBColorB()))
104 // apply dashing; get line and gap snippets
105 ::std::vector
< double > aDash
;
106 basegfx::B2DPolyPolygon aDashedPolyPolyA
;
107 basegfx::B2DPolyPolygon aDashedPolyPolyB
;
109 aDash
.push_back(fLogicDashLength
);
110 aDash
.push_back(fLogicDashLength
);
111 basegfx::tools::applyLineDashing(getB2DPolygon(), aDash
, &aDashedPolyPolyA
, &aDashedPolyPolyB
, 2.0 * fLogicDashLength
);
113 // prepare return value
114 Primitive2DSequence
aRetval(2);
116 aRetval
[0] = Primitive2DReference(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyA
, getRGBColorA()));
117 aRetval
[1] = Primitive2DReference(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyB
, getRGBColorB()));
123 const Primitive2DReference
xRef(new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA()));
124 return Primitive2DSequence(&xRef
, 1L);
128 PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D(
129 const basegfx::B2DPolygon
& rPolygon
,
130 const basegfx::BColor
& rRGBColorA
,
131 const basegfx::BColor
& rRGBColorB
,
132 double fDiscreteDashLength
)
133 : BufferedDecompositionPrimitive2D(),
135 maRGBColorA(rRGBColorA
),
136 maRGBColorB(rRGBColorB
),
137 mfDiscreteDashLength(fDiscreteDashLength
),
138 maLastInverseObjectToViewTransformation()
142 bool PolygonMarkerPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
144 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
146 const PolygonMarkerPrimitive2D
& rCompare
= (PolygonMarkerPrimitive2D
&)rPrimitive
;
148 return (getB2DPolygon() == rCompare
.getB2DPolygon()
149 && getRGBColorA() == rCompare
.getRGBColorA()
150 && getRGBColorB() == rCompare
.getRGBColorB()
151 && getDiscreteDashLength() == rCompare
.getDiscreteDashLength());
157 basegfx::B2DRange
PolygonMarkerPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
159 // this is a hairline, thus the line width is view-dependent. Get range of polygon
161 basegfx::B2DRange
aRetval(getB2DPolygon().getB2DRange());
163 if(!aRetval
.isEmpty())
165 // Calculate view-dependent hairline width
166 const basegfx::B2DVector
aDiscreteSize(rViewInformation
.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
167 const double fDiscreteHalfLineWidth(aDiscreteSize
.getLength() * 0.5);
169 if(basegfx::fTools::more(fDiscreteHalfLineWidth
, 0.0))
171 aRetval
.grow(fDiscreteHalfLineWidth
);
179 Primitive2DSequence
PolygonMarkerPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D
& rViewInformation
) const
181 ::osl::MutexGuard
aGuard( m_aMutex
);
182 bool bNeedNewDecomposition(false);
184 if(getBuffered2DDecomposition().hasElements())
186 if(rViewInformation
.getInverseObjectToViewTransformation() != maLastInverseObjectToViewTransformation
)
188 bNeedNewDecomposition
= true;
192 if(bNeedNewDecomposition
)
194 // conditions of last local decomposition have changed, delete
195 const_cast< PolygonMarkerPrimitive2D
* >(this)->setBuffered2DDecomposition(Primitive2DSequence());
198 if(!getBuffered2DDecomposition().hasElements())
200 // remember last used InverseObjectToViewTransformation
201 PolygonMarkerPrimitive2D
* pThat
= const_cast< PolygonMarkerPrimitive2D
* >(this);
202 pThat
->maLastInverseObjectToViewTransformation
= rViewInformation
.getInverseObjectToViewTransformation();
205 // use parent implementation
206 return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation
);
210 ImplPrimitive2DIDBlock(PolygonMarkerPrimitive2D
, PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D
)
212 } // end of namespace primitive2d
213 } // end of namespace drawinglayer
215 //////////////////////////////////////////////////////////////////////////////
217 namespace drawinglayer
219 namespace primitive2d
221 Primitive2DSequence
PolygonStrokePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
223 if(getB2DPolygon().count())
225 // #i102241# try to simplify before usage
226 const basegfx::B2DPolygon
aB2DPolygon(basegfx::tools::simplifyCurveSegments(getB2DPolygon()));
227 basegfx::B2DPolyPolygon aHairLinePolyPolygon
;
229 if(getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
231 // no line dashing, just copy
232 aHairLinePolyPolygon
.append(aB2DPolygon
);
237 basegfx::tools::applyLineDashing(
238 aB2DPolygon
, getStrokeAttribute().getDotDashArray(),
239 &aHairLinePolyPolygon
, 0, getStrokeAttribute().getFullDotDashLen());
242 const sal_uInt32
nCount(aHairLinePolyPolygon
.count());
244 if(!getLineAttribute().isDefault() && getLineAttribute().getWidth())
246 // create fat line data
247 const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0);
248 const basegfx::B2DLineJoin
aLineJoin(getLineAttribute().getLineJoin());
249 const com::sun::star::drawing::LineCap
aLineCap(getLineAttribute().getLineCap());
250 basegfx::B2DPolyPolygon aAreaPolyPolygon
;
252 for(sal_uInt32
a(0L); a
< nCount
; a
++)
254 // New version of createAreaGeometry; now creates bezier polygons
255 aAreaPolyPolygon
.append(basegfx::tools::createAreaGeometry(
256 aHairLinePolyPolygon
.getB2DPolygon(a
),
262 // prepare return value
263 Primitive2DSequence
aRetval(aAreaPolyPolygon
.count());
266 for(sal_uInt32
b(0L); b
< aAreaPolyPolygon
.count(); b
++)
268 // put into single polyPolygon primitives to make clear that this is NOT meant
269 // to be painted as a single PolyPolygon (XORed as fill rule). Alternatively, a
270 // melting process may be used here one day.
271 const basegfx::B2DPolyPolygon
aNewPolyPolygon(aAreaPolyPolygon
.getB2DPolygon(b
));
272 static bool bTestByUsingRandomColor(false);
273 const basegfx::BColor
aColor(bTestByUsingRandomColor
274 ? basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)
275 : getLineAttribute().getColor());
276 const Primitive2DReference
xRef(new PolyPolygonColorPrimitive2D(aNewPolyPolygon
, aColor
));
284 // prepare return value
285 const Primitive2DReference
xRef(
286 new PolyPolygonHairlinePrimitive2D(
287 aHairLinePolyPolygon
,
288 getLineAttribute().getColor()));
290 return Primitive2DSequence(&xRef
, 1);
295 return Primitive2DSequence();
299 PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
300 const basegfx::B2DPolygon
& rPolygon
,
301 const attribute::LineAttribute
& rLineAttribute
,
302 const attribute::StrokeAttribute
& rStrokeAttribute
)
303 : BufferedDecompositionPrimitive2D(),
305 maLineAttribute(rLineAttribute
),
306 maStrokeAttribute(rStrokeAttribute
)
310 PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
311 const basegfx::B2DPolygon
& rPolygon
,
312 const attribute::LineAttribute
& rLineAttribute
)
313 : BufferedDecompositionPrimitive2D(),
315 maLineAttribute(rLineAttribute
),
320 bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
322 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
324 const PolygonStrokePrimitive2D
& rCompare
= (PolygonStrokePrimitive2D
&)rPrimitive
;
326 return (getB2DPolygon() == rCompare
.getB2DPolygon()
327 && getLineAttribute() == rCompare
.getLineAttribute()
328 && getStrokeAttribute() == rCompare
.getStrokeAttribute());
334 basegfx::B2DRange
PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
336 basegfx::B2DRange aRetval
;
338 if(getLineAttribute().getWidth())
340 bool bUseDecomposition(false);
342 if(basegfx::B2DLINEJOIN_MITER
== getLineAttribute().getLineJoin())
344 // if line is mitered, use parent call since mitered line
345 // geometry may use more space than the geometry grown by half line width
346 bUseDecomposition
= true;
349 if(!bUseDecomposition
&& com::sun::star::drawing::LineCap_SQUARE
== getLineAttribute().getLineCap())
351 // when drawing::LineCap_SQUARE is used the below method to grow the polygon
352 // range by half line width will not work, so use decomposition. Interestingly,
353 // the grow method below works perfectly for LineCap_ROUND since the grow is in
354 // all directions and the rounded cap needs the same grow in all directions independent
355 // from it's orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE
356 bUseDecomposition
= true;
359 if(bUseDecomposition
)
361 // get correct range by using the decomposition fallback, reasons see above cases
362 aRetval
= BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation
);
366 // for all other B2DLINEJOIN_* get the range from the base geometry
367 // and expand by half the line width
368 aRetval
= getB2DPolygon().getB2DRange();
369 aRetval
.grow(getLineAttribute().getWidth() * 0.5);
374 // this is a hairline, thus the line width is view-dependent. Get range of polygon
376 aRetval
= getB2DPolygon().getB2DRange();
378 if(!aRetval
.isEmpty())
380 // Calculate view-dependent hairline width
381 const basegfx::B2DVector
aDiscreteSize(rViewInformation
.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
382 const double fDiscreteHalfLineWidth(aDiscreteSize
.getLength() * 0.5);
384 if(basegfx::fTools::more(fDiscreteHalfLineWidth
, 0.0))
386 aRetval
.grow(fDiscreteHalfLineWidth
);
395 ImplPrimitive2DIDBlock(PolygonStrokePrimitive2D
, PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
)
397 } // end of namespace primitive2d
398 } // end of namespace drawinglayer
400 //////////////////////////////////////////////////////////////////////////////
402 namespace drawinglayer
404 namespace primitive2d
406 Primitive2DSequence
PolygonWavePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
408 Primitive2DSequence aRetval
;
410 if(getB2DPolygon().count())
412 const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth()));
413 const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight()));
415 if(bHasWidth
&& bHasHeight
)
417 // create waveline curve
418 const basegfx::B2DPolygon
aWaveline(basegfx::tools::createWaveline(getB2DPolygon(), getWaveWidth(), getWaveHeight()));
419 const Primitive2DReference
xRef(new PolygonStrokePrimitive2D(aWaveline
, getLineAttribute(), getStrokeAttribute()));
420 aRetval
= Primitive2DSequence(&xRef
, 1);
424 // flat waveline, decompose to simple line primitive
425 const Primitive2DReference
xRef(new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(), getStrokeAttribute()));
426 aRetval
= Primitive2DSequence(&xRef
, 1);
433 PolygonWavePrimitive2D::PolygonWavePrimitive2D(
434 const basegfx::B2DPolygon
& rPolygon
,
435 const attribute::LineAttribute
& rLineAttribute
,
436 const attribute::StrokeAttribute
& rStrokeAttribute
,
439 : PolygonStrokePrimitive2D(rPolygon
, rLineAttribute
, rStrokeAttribute
),
440 mfWaveWidth(fWaveWidth
),
441 mfWaveHeight(fWaveHeight
)
443 if(mfWaveWidth
< 0.0)
448 if(mfWaveHeight
< 0.0)
454 PolygonWavePrimitive2D::PolygonWavePrimitive2D(
455 const basegfx::B2DPolygon
& rPolygon
,
456 const attribute::LineAttribute
& rLineAttribute
,
459 : PolygonStrokePrimitive2D(rPolygon
, rLineAttribute
),
460 mfWaveWidth(fWaveWidth
),
461 mfWaveHeight(fWaveHeight
)
463 if(mfWaveWidth
< 0.0)
468 if(mfWaveHeight
< 0.0)
474 bool PolygonWavePrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
476 if(PolygonStrokePrimitive2D::operator==(rPrimitive
))
478 const PolygonWavePrimitive2D
& rCompare
= (PolygonWavePrimitive2D
&)rPrimitive
;
480 return (getWaveWidth() == rCompare
.getWaveWidth()
481 && getWaveHeight() == rCompare
.getWaveHeight());
487 basegfx::B2DRange
PolygonWavePrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
489 // get range of parent
490 basegfx::B2DRange
aRetval(PolygonStrokePrimitive2D::getB2DRange(rViewInformation
));
492 // if WaveHeight, grow by it
493 if(basegfx::fTools::more(getWaveHeight(), 0.0))
495 aRetval
.grow(getWaveHeight());
498 // if line width, grow by it
499 if(basegfx::fTools::more(getLineAttribute().getWidth(), 0.0))
501 aRetval
.grow(getLineAttribute().getWidth() * 0.5);
508 ImplPrimitive2DIDBlock(PolygonWavePrimitive2D
, PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D
)
510 } // end of namespace primitive2d
511 } // end of namespace drawinglayer
513 //////////////////////////////////////////////////////////////////////////////
515 namespace drawinglayer
517 namespace primitive2d
519 Primitive2DSequence
PolygonStrokeArrowPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
521 // copy local polygon, it may be changed
522 basegfx::B2DPolygon
aLocalPolygon(getB2DPolygon());
523 aLocalPolygon
.removeDoublePoints();
524 basegfx::B2DPolyPolygon aArrowA
;
525 basegfx::B2DPolyPolygon aArrowB
;
527 if(!aLocalPolygon
.isClosed() && aLocalPolygon
.count() > 1)
530 const double fPolyLength(basegfx::tools::getLength(aLocalPolygon
));
533 double fStartOverlap(0.0);
534 double fEndOverlap(0.0);
536 if(!getStart().isDefault() && getStart().isActive())
538 // create start arrow primitive and consume
539 aArrowA
= basegfx::tools::createAreaGeometryForLineStartEnd(
540 aLocalPolygon
, getStart().getB2DPolyPolygon(), true, getStart().getWidth(),
541 fPolyLength
, getStart().isCentered() ? 0.5 : 0.0, &fStart
);
543 // create some overlapping, compromise between straight and peaked markers
544 // for marker width 0.3cm and marker line width 0.02cm
545 fStartOverlap
= getStart().getWidth() / 15.0;
548 if(!getEnd().isDefault() && getEnd().isActive())
550 // create end arrow primitive and consume
551 aArrowB
= basegfx::tools::createAreaGeometryForLineStartEnd(
552 aLocalPolygon
, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(),
553 fPolyLength
, getEnd().isCentered() ? 0.5 : 0.0, &fEnd
);
555 // create some overlapping
556 fEndOverlap
= getEnd().getWidth() / 15.0;
559 if(0.0 != fStart
|| 0.0 != fEnd
)
561 // build new poly, consume something from old poly
562 aLocalPolygon
= basegfx::tools::getSnippetAbsolute(aLocalPolygon
, fStart
-fStartOverlap
, fPolyLength
- fEnd
+ fEndOverlap
, fPolyLength
);
566 // prepare return value
567 Primitive2DSequence
aRetval(1L + (aArrowA
.count() ? 1L : 0L) + (aArrowB
.count() ? 1L : 0L));
571 const Primitive2DReference
xRefShaft(new
572 PolygonStrokePrimitive2D(
573 aLocalPolygon
, getLineAttribute(), getStrokeAttribute()));
574 aRetval
[nInd
++] = xRefShaft
;
578 const Primitive2DReference
xRefA(
579 new PolyPolygonColorPrimitive2D(
580 aArrowA
, getLineAttribute().getColor()));
581 aRetval
[nInd
++] = xRefA
;
586 const Primitive2DReference
xRefB(
587 new PolyPolygonColorPrimitive2D(
588 aArrowB
, getLineAttribute().getColor()));
589 aRetval
[nInd
++] = xRefB
;
595 PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
596 const basegfx::B2DPolygon
& rPolygon
,
597 const attribute::LineAttribute
& rLineAttribute
,
598 const attribute::StrokeAttribute
& rStrokeAttribute
,
599 const attribute::LineStartEndAttribute
& rStart
,
600 const attribute::LineStartEndAttribute
& rEnd
)
601 : PolygonStrokePrimitive2D(rPolygon
, rLineAttribute
, rStrokeAttribute
),
607 bool PolygonStrokeArrowPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
609 if(PolygonStrokePrimitive2D::operator==(rPrimitive
))
611 const PolygonStrokeArrowPrimitive2D
& rCompare
= (PolygonStrokeArrowPrimitive2D
&)rPrimitive
;
613 return (getStart() == rCompare
.getStart()
614 && getEnd() == rCompare
.getEnd());
620 basegfx::B2DRange
PolygonStrokeArrowPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
622 basegfx::B2DRange aRetval
;
624 if(getStart().isActive() || getEnd().isActive())
626 // use decomposition when line start/end is used
627 return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation
);
631 // get range from parent
632 return PolygonStrokePrimitive2D::getB2DRange(rViewInformation
);
637 ImplPrimitive2DIDBlock(PolygonStrokeArrowPrimitive2D
, PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
)
639 } // end of namespace primitive2d
640 } // end of namespace drawinglayer
642 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */