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