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