1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_drawinglayer.hxx"
26 
27 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
28 #include <basegfx/tools/canvastools.hxx>
29 #include <basegfx/polygon/b2dpolygontools.hxx>
30 #include <basegfx/polygon/b2dpolypolygontools.hxx>
31 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
32 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
33 #include <drawinglayer/geometry/viewinformation2d.hxx>
34 #include <basegfx/polygon/b2dlinegeometry.hxx>
35 #include <com/sun/star/drawing/LineCap.hpp>
36 
37 //////////////////////////////////////////////////////////////////////////////
38 
39 using namespace com::sun::star;
40 
41 //////////////////////////////////////////////////////////////////////////////
42 
43 namespace drawinglayer
44 {
45 	namespace primitive2d
46 	{
47 		PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(
48 			const basegfx::B2DPolygon& rPolygon,
49 			const basegfx::BColor& rBColor)
50 		:	BasePrimitive2D(),
51 			maPolygon(rPolygon),
52 			maBColor(rBColor)
53 		{
54 		}
55 
56 		bool PolygonHairlinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
57 		{
58 			if(BasePrimitive2D::operator==(rPrimitive))
59 			{
60 				const PolygonHairlinePrimitive2D& rCompare = (PolygonHairlinePrimitive2D&)rPrimitive;
61 
62 				return (getB2DPolygon() == rCompare.getB2DPolygon()
63 					&& getBColor() == rCompare.getBColor());
64 			}
65 
66 			return false;
67 		}
68 
69 		basegfx::B2DRange PolygonHairlinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
70 		{
71             // this is a hairline, thus the line width is view-dependent. Get range of polygon
72             // as base size
73 	        basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange());
74 
75             if(!aRetval.isEmpty())
76             {
77                 // Calculate view-dependent hairline width
78                 const basegfx::B2DVector aDiscreteSize(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
79                 const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
80 
81                 if(basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
82                 {
83 		            aRetval.grow(fDiscreteHalfLineWidth);
84                 }
85             }
86 
87             // return range
88 			return aRetval;
89 		}
90 
91 		// provide unique ID
92 		ImplPrimitrive2DIDBlock(PolygonHairlinePrimitive2D, PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D)
93 
94 	} // end of namespace primitive2d
95 } // end of namespace drawinglayer
96 
97 //////////////////////////////////////////////////////////////////////////////
98 
99 namespace drawinglayer
100 {
101 	namespace primitive2d
102 	{
103 		Primitive2DSequence PolygonMarkerPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
104 		{
105 			// calculate logic DashLength
106 			const basegfx::B2DVector aDashVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(getDiscreteDashLength(), 0.0));
107 			const double fLogicDashLength(aDashVector.getX());
108 
109 			if(fLogicDashLength > 0.0 && !getRGBColorA().equal(getRGBColorB()))
110 			{
111 				// apply dashing; get line and gap snippets
112 				::std::vector< double > aDash;
113 				basegfx::B2DPolyPolygon aDashedPolyPolyA;
114 				basegfx::B2DPolyPolygon aDashedPolyPolyB;
115 
116 				aDash.push_back(fLogicDashLength);
117 				aDash.push_back(fLogicDashLength);
118 				basegfx::tools::applyLineDashing(getB2DPolygon(), aDash, &aDashedPolyPolyA, &aDashedPolyPolyB, 2.0 * fLogicDashLength);
119 
120 				// prepare return value
121 				Primitive2DSequence aRetval(2);
122 
123 				aRetval[0] = Primitive2DReference(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyA, getRGBColorA()));
124 				aRetval[1] = Primitive2DReference(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyB, getRGBColorB()));
125 
126 				return aRetval;
127 			}
128 			else
129 			{
130 				const Primitive2DReference xRef(new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA()));
131 				return Primitive2DSequence(&xRef, 1L);
132 			}
133 		}
134 
135 		PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D(
136 			const basegfx::B2DPolygon& rPolygon,
137 			const basegfx::BColor& rRGBColorA,
138 			const basegfx::BColor& rRGBColorB,
139 			double fDiscreteDashLength)
140 		:	BufferedDecompositionPrimitive2D(),
141 			maPolygon(rPolygon),
142 			maRGBColorA(rRGBColorA),
143 			maRGBColorB(rRGBColorB),
144 			mfDiscreteDashLength(fDiscreteDashLength),
145 			maLastInverseObjectToViewTransformation()
146 		{
147 		}
148 
149 		bool PolygonMarkerPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
150 		{
151 			if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
152 			{
153 				const PolygonMarkerPrimitive2D& rCompare = (PolygonMarkerPrimitive2D&)rPrimitive;
154 
155 				return (getB2DPolygon() == rCompare.getB2DPolygon()
156 					&& getRGBColorA() == rCompare.getRGBColorA()
157 					&& getRGBColorB() == rCompare.getRGBColorB()
158 					&& getDiscreteDashLength() == rCompare.getDiscreteDashLength());
159 			}
160 
161 			return false;
162 		}
163 
164 		basegfx::B2DRange PolygonMarkerPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
165 		{
166             // this is a hairline, thus the line width is view-dependent. Get range of polygon
167             // as base size
168 	        basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange());
169 
170             if(!aRetval.isEmpty())
171             {
172                 // Calculate view-dependent hairline width
173                 const basegfx::B2DVector aDiscreteSize(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
174                 const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
175 
176                 if(basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
177                 {
178 		            aRetval.grow(fDiscreteHalfLineWidth);
179                 }
180             }
181 
182             // return range
183 			return aRetval;
184 		}
185 
186 		Primitive2DSequence PolygonMarkerPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
187 		{
188 			::osl::MutexGuard aGuard( m_aMutex );
189 			bool bNeedNewDecomposition(false);
190 
191 			if(getBuffered2DDecomposition().hasElements())
192 			{
193 				if(rViewInformation.getInverseObjectToViewTransformation() != maLastInverseObjectToViewTransformation)
194 				{
195 					bNeedNewDecomposition = true;
196 				}
197 			}
198 
199 			if(bNeedNewDecomposition)
200 			{
201 				// conditions of last local decomposition have changed, delete
202 				const_cast< PolygonMarkerPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DSequence());
203 			}
204 
205 			if(!getBuffered2DDecomposition().hasElements())
206 			{
207 				// remember last used InverseObjectToViewTransformation
208 				PolygonMarkerPrimitive2D* pThat = const_cast< PolygonMarkerPrimitive2D* >(this);
209 				pThat->maLastInverseObjectToViewTransformation = rViewInformation.getInverseObjectToViewTransformation();
210 			}
211 
212 			// use parent implementation
213 			return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
214 		}
215 
216 		// provide unique ID
217 		ImplPrimitrive2DIDBlock(PolygonMarkerPrimitive2D, PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D)
218 
219 	} // end of namespace primitive2d
220 } // end of namespace drawinglayer
221 
222 //////////////////////////////////////////////////////////////////////////////
223 
224 namespace drawinglayer
225 {
226 	namespace primitive2d
227 	{
228 		Primitive2DSequence PolygonStrokePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
229 		{
230 			if(getB2DPolygon().count())
231 			{
232                 // #i102241# try to simplify before usage
233                 const basegfx::B2DPolygon aB2DPolygon(basegfx::tools::simplifyCurveSegments(getB2DPolygon()));
234 				basegfx::B2DPolyPolygon aHairLinePolyPolygon;
235 
236 				if(getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
237 				{
238                     // no line dashing, just copy
239 					aHairLinePolyPolygon.append(aB2DPolygon);
240 				}
241 				else
242 				{
243 				    // apply LineStyle
244 				    basegfx::tools::applyLineDashing(
245                         aB2DPolygon, getStrokeAttribute().getDotDashArray(),
246                         &aHairLinePolyPolygon, 0, getStrokeAttribute().getFullDotDashLen());
247 				}
248 
249                 const sal_uInt32 nCount(aHairLinePolyPolygon.count());
250 
251                 if(!getLineAttribute().isDefault() && getLineAttribute().getWidth())
252 				{
253                     // create fat line data
254 					const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0);
255 					const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin());
256                     const com::sun::star::drawing::LineCap aLineCap(getLineAttribute().getLineCap());
257 					basegfx::B2DPolyPolygon aAreaPolyPolygon;
258 
259 					for(sal_uInt32 a(0L); a < nCount; a++)
260 					{
261                         // New version of createAreaGeometry; now creates bezier polygons
262                         aAreaPolyPolygon.append(basegfx::tools::createAreaGeometry(
263 							aHairLinePolyPolygon.getB2DPolygon(a),
264                             fHalfLineWidth,
265                             aLineJoin,
266                             aLineCap));
267 					}
268 
269 					// prepare return value
270 					Primitive2DSequence aRetval(aAreaPolyPolygon.count());
271 
272 					// create primitive
273 					for(sal_uInt32 b(0L); b < aAreaPolyPolygon.count(); b++)
274 					{
275 						// put into single polyPolygon primitives to make clear that this is NOT meant
276 						// to be painted as a single PolyPolygon (XORed as fill rule). Alternatively, a
277 						// melting process may be used here one day.
278 						const basegfx::B2DPolyPolygon aNewPolyPolygon(aAreaPolyPolygon.getB2DPolygon(b));
279     					static bool bTestByUsingRandomColor(false);
280                         const basegfx::BColor aColor(bTestByUsingRandomColor
281                             ? basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)
282                             : getLineAttribute().getColor());
283 						const Primitive2DReference xRef(new PolyPolygonColorPrimitive2D(aNewPolyPolygon, aColor));
284 						aRetval[b] = xRef;
285 					}
286 
287 					return aRetval;
288 				}
289 				else
290 				{
291 					// prepare return value
292 					const Primitive2DReference xRef(
293                         new PolyPolygonHairlinePrimitive2D(
294                             aHairLinePolyPolygon,
295                             getLineAttribute().getColor()));
296 
297                     return Primitive2DSequence(&xRef, 1);
298 				}
299 			}
300 			else
301 			{
302 				return Primitive2DSequence();
303 			}
304 		}
305 
306 		PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
307 			const basegfx::B2DPolygon& rPolygon,
308             const attribute::LineAttribute& rLineAttribute,
309 			const attribute::StrokeAttribute& rStrokeAttribute)
310 		:	BufferedDecompositionPrimitive2D(),
311 			maPolygon(rPolygon),
312             maLineAttribute(rLineAttribute),
313 			maStrokeAttribute(rStrokeAttribute)
314 		{
315 		}
316 
317 		PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
318 			const basegfx::B2DPolygon& rPolygon,
319             const attribute::LineAttribute& rLineAttribute)
320 		:	BufferedDecompositionPrimitive2D(),
321 			maPolygon(rPolygon),
322             maLineAttribute(rLineAttribute),
323 			maStrokeAttribute()
324 		{
325 		}
326 
327 		bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
328 		{
329 			if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
330 			{
331 				const PolygonStrokePrimitive2D& rCompare = (PolygonStrokePrimitive2D&)rPrimitive;
332 
333 				return (getB2DPolygon() == rCompare.getB2DPolygon()
334 					&& getLineAttribute() == rCompare.getLineAttribute()
335 					&& getStrokeAttribute() == rCompare.getStrokeAttribute());
336 			}
337 
338 			return false;
339 		}
340 
341 		basegfx::B2DRange PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
342 		{
343             basegfx::B2DRange aRetval;
344 
345             if(getLineAttribute().getWidth())
346             {
347                 bool bUseDecomposition(false);
348 
349                 if(basegfx::B2DLINEJOIN_MITER == getLineAttribute().getLineJoin())
350                 {
351                     // if line is mitered, use parent call since mitered line
352                     // geometry may use more space than the geometry grown by half line width
353                     bUseDecomposition = true;
354                 }
355 
356                 if(!bUseDecomposition && com::sun::star::drawing::LineCap_SQUARE == getLineAttribute().getLineCap())
357                 {
358                     // when drawing::LineCap_SQUARE is used the below method to grow the polygon
359                     // range by half line width will not work, so use decomposition. Interestingly,
360                     // the grow method below works perfectly for LineCap_ROUND since the grow is in
361                     // all directions and the rounded cap needs the same grow in all directions independent
362                     // from it's orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE
363                     bUseDecomposition = true;
364                 }
365 
366                 if(bUseDecomposition)
367                 {
368                     // get correct range by using the decomposition fallback, reasons see above cases
369                     aRetval = BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
370                 }
371                 else
372                 {
373                     // for all other B2DLINEJOIN_* get the range from the base geometry
374                     // and expand by half the line width
375 			        aRetval = getB2DPolygon().getB2DRange();
376 			        aRetval.grow(getLineAttribute().getWidth() * 0.5);
377                 }
378             }
379             else
380             {
381                 // this is a hairline, thus the line width is view-dependent. Get range of polygon
382                 // as base size
383     	        aRetval = getB2DPolygon().getB2DRange();
384 
385                 if(!aRetval.isEmpty())
386                 {
387                     // Calculate view-dependent hairline width
388                     const basegfx::B2DVector aDiscreteSize(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
389                     const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
390 
391                     if(basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
392                     {
393 			            aRetval.grow(fDiscreteHalfLineWidth);
394                     }
395                 }
396             }
397 
398             return aRetval;
399 		}
400 
401 		// provide unique ID
402 		ImplPrimitrive2DIDBlock(PolygonStrokePrimitive2D, PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D)
403 
404 	} // end of namespace primitive2d
405 } // end of namespace drawinglayer
406 
407 //////////////////////////////////////////////////////////////////////////////
408 
409 namespace drawinglayer
410 {
411 	namespace primitive2d
412 	{
413 		Primitive2DSequence PolygonWavePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
414 		{
415 			Primitive2DSequence aRetval;
416 
417 			if(getB2DPolygon().count())
418 			{
419 				const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth()));
420 				const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight()));
421 
422 				if(bHasWidth && bHasHeight)
423 				{
424 					// create waveline curve
425 					const basegfx::B2DPolygon aWaveline(basegfx::tools::createWaveline(getB2DPolygon(), getWaveWidth(), getWaveHeight()));
426 					const Primitive2DReference xRef(new PolygonStrokePrimitive2D(aWaveline, getLineAttribute(), getStrokeAttribute()));
427 					aRetval = Primitive2DSequence(&xRef, 1);
428 				}
429 				else
430 				{
431 					// flat waveline, decompose to simple line primitive
432 					const Primitive2DReference xRef(new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(), getStrokeAttribute()));
433 					aRetval = Primitive2DSequence(&xRef, 1);
434 				}
435 			}
436 
437 			return aRetval;
438 		}
439 
440 		PolygonWavePrimitive2D::PolygonWavePrimitive2D(
441 			const basegfx::B2DPolygon& rPolygon,
442             const attribute::LineAttribute& rLineAttribute,
443 			const attribute::StrokeAttribute& rStrokeAttribute,
444 			double fWaveWidth,
445 			double fWaveHeight)
446 		:	PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute),
447 			mfWaveWidth(fWaveWidth),
448 			mfWaveHeight(fWaveHeight)
449 		{
450 			if(mfWaveWidth < 0.0)
451 			{
452 				mfWaveWidth = 0.0;
453 			}
454 
455 			if(mfWaveHeight < 0.0)
456 			{
457 				mfWaveHeight = 0.0;
458 			}
459 		}
460 
461 		PolygonWavePrimitive2D::PolygonWavePrimitive2D(
462 			const basegfx::B2DPolygon& rPolygon,
463             const attribute::LineAttribute& rLineAttribute,
464 			double fWaveWidth,
465 			double fWaveHeight)
466 		:	PolygonStrokePrimitive2D(rPolygon, rLineAttribute),
467 			mfWaveWidth(fWaveWidth),
468 			mfWaveHeight(fWaveHeight)
469 		{
470 			if(mfWaveWidth < 0.0)
471 			{
472 				mfWaveWidth = 0.0;
473 			}
474 
475 			if(mfWaveHeight < 0.0)
476 			{
477 				mfWaveHeight = 0.0;
478 			}
479 		}
480 
481 		bool PolygonWavePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
482 		{
483 			if(PolygonStrokePrimitive2D::operator==(rPrimitive))
484 			{
485 				const PolygonWavePrimitive2D& rCompare = (PolygonWavePrimitive2D&)rPrimitive;
486 
487 				return (getWaveWidth() == rCompare.getWaveWidth()
488 					&& getWaveHeight() == rCompare.getWaveHeight());
489 			}
490 
491 			return false;
492 		}
493 
494 		basegfx::B2DRange PolygonWavePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
495 		{
496 			// get range of parent
497     		basegfx::B2DRange aRetval(PolygonStrokePrimitive2D::getB2DRange(rViewInformation));
498 
499 			// if WaveHeight, grow by it
500 			if(basegfx::fTools::more(getWaveHeight(), 0.0))
501 			{
502 				aRetval.grow(getWaveHeight());
503 			}
504 
505 			// if line width, grow by it
506 			if(basegfx::fTools::more(getLineAttribute().getWidth(), 0.0))
507 			{
508 				aRetval.grow(getLineAttribute().getWidth() * 0.5);
509 			}
510 
511 			return aRetval;
512 		}
513 
514 		// provide unique ID
515 		ImplPrimitrive2DIDBlock(PolygonWavePrimitive2D, PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D)
516 
517 	} // end of namespace primitive2d
518 } // end of namespace drawinglayer
519 
520 //////////////////////////////////////////////////////////////////////////////
521 
522 namespace drawinglayer
523 {
524 	namespace primitive2d
525 	{
526 		Primitive2DSequence PolygonStrokeArrowPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
527 		{
528 			// copy local polygon, it may be changed
529 			basegfx::B2DPolygon aLocalPolygon(getB2DPolygon());
530 			basegfx::B2DPolyPolygon aArrowA;
531 			basegfx::B2DPolyPolygon aArrowB;
532 
533 			if(!aLocalPolygon.isClosed())
534 			{
535 				// apply arrows
536 				const double fPolyLength(basegfx::tools::getLength(aLocalPolygon));
537 				double fStart(0.0);
538 				double fEnd(0.0);
539 
540 				if(!getStart().isDefault() && getStart().isActive())
541 				{
542 					// create start arrow primitive and consume
543 					aArrowA = basegfx::tools::createAreaGeometryForLineStartEnd(
544 						aLocalPolygon, getStart().getB2DPolyPolygon(), true, getStart().getWidth(),
545 						fPolyLength, getStart().isCentered() ? 0.5 : 0.0, &fStart);
546 
547 					// create some overlapping
548 					fStart *= 0.8;
549 				}
550 
551 				if(!getEnd().isDefault() && getEnd().isActive())
552 				{
553 					// create end arrow primitive and consume
554 					aArrowB = basegfx::tools::createAreaGeometryForLineStartEnd(
555 						aLocalPolygon, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(),
556 						fPolyLength, getEnd().isCentered() ? 0.5 : 0.0, &fEnd);
557 
558 					// create some overlapping
559 					fEnd *= 0.8;
560 				}
561 
562 				if(0.0 != fStart || 0.0 != fEnd)
563 				{
564 					// build new poly, consume something from old poly
565 					aLocalPolygon = basegfx::tools::getSnippetAbsolute(aLocalPolygon, fStart, fPolyLength - fEnd, fPolyLength);
566 				}
567 			}
568 
569 			// prepare return value
570 			Primitive2DSequence aRetval(1L + (aArrowA.count() ? 1L : 0L) + (aArrowB.count() ? 1L : 0L));
571 			sal_uInt32 nInd(0L);
572 
573 			// add shaft
574 			const Primitive2DReference xRefShaft(new
575                 PolygonStrokePrimitive2D(
576                     aLocalPolygon, getLineAttribute(), getStrokeAttribute()));
577 			aRetval[nInd++] = xRefShaft;
578 
579 			if(aArrowA.count())
580 			{
581 				const Primitive2DReference xRefA(
582                     new PolyPolygonColorPrimitive2D(
583                         aArrowA, getLineAttribute().getColor()));
584 				aRetval[nInd++] = xRefA;
585 			}
586 
587 			if(aArrowB.count())
588 			{
589 				const Primitive2DReference xRefB(
590                     new PolyPolygonColorPrimitive2D(
591                         aArrowB, getLineAttribute().getColor()));
592 				aRetval[nInd++] = xRefB;
593 			}
594 
595 			return aRetval;
596 		}
597 
598 		PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
599 			const basegfx::B2DPolygon& rPolygon,
600             const attribute::LineAttribute& rLineAttribute,
601 			const attribute::StrokeAttribute& rStrokeAttribute,
602 			const attribute::LineStartEndAttribute& rStart,
603 			const attribute::LineStartEndAttribute& rEnd)
604 		:	PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute),
605 			maStart(rStart),
606 			maEnd(rEnd)
607 		{
608 		}
609 
610 		PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
611 			const basegfx::B2DPolygon& rPolygon,
612             const attribute::LineAttribute& rLineAttribute,
613 			const attribute::LineStartEndAttribute& rStart,
614 			const attribute::LineStartEndAttribute& rEnd)
615 		:	PolygonStrokePrimitive2D(rPolygon, rLineAttribute),
616 			maStart(rStart),
617 			maEnd(rEnd)
618 		{
619 		}
620 
621 		bool PolygonStrokeArrowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
622 		{
623 			if(PolygonStrokePrimitive2D::operator==(rPrimitive))
624 			{
625 				const PolygonStrokeArrowPrimitive2D& rCompare = (PolygonStrokeArrowPrimitive2D&)rPrimitive;
626 
627 				return (getStart() == rCompare.getStart()
628 					&& getEnd() == rCompare.getEnd());
629 			}
630 
631 			return false;
632 		}
633 
634 		basegfx::B2DRange PolygonStrokeArrowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
635 		{
636 			basegfx::B2DRange aRetval;
637 
638 			if(getStart().isActive() || getEnd().isActive())
639 			{
640 				// use decomposition when line start/end is used
641 				return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
642 			}
643 			else
644 			{
645 				// get range from parent
646 				return PolygonStrokePrimitive2D::getB2DRange(rViewInformation);
647 			}
648 		}
649 
650 		// provide unique ID
651 		ImplPrimitrive2DIDBlock(PolygonStrokeArrowPrimitive2D, PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D)
652 
653 	} // end of namespace primitive2d
654 } // end of namespace drawinglayer
655 
656 //////////////////////////////////////////////////////////////////////////////
657 // eof
658