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 	{
PolygonHairlinePrimitive2D(const basegfx::B2DPolygon & rPolygon,const basegfx::BColor & rBColor)47 		PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(
48 			const basegfx::B2DPolygon& rPolygon,
49 			const basegfx::BColor& rBColor)
50 		:	BasePrimitive2D(),
51 			maPolygon(rPolygon),
52 			maBColor(rBColor)
53 		{
54 		}
55 
operator ==(const BasePrimitive2D & rPrimitive) const56 		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 
getB2DRange(const geometry::ViewInformation2D & rViewInformation) const69 		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 	{
create2DDecomposition(const geometry::ViewInformation2D & rViewInformation) const103 		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 
PolygonMarkerPrimitive2D(const basegfx::B2DPolygon & rPolygon,const basegfx::BColor & rRGBColorA,const basegfx::BColor & rRGBColorB,double fDiscreteDashLength)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 
operator ==(const BasePrimitive2D & rPrimitive) const149 		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 
getB2DRange(const geometry::ViewInformation2D & rViewInformation) const164 		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 
get2DDecomposition(const geometry::ViewInformation2D & rViewInformation) const186 		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 	{
create2DDecomposition(const geometry::ViewInformation2D &) const228 		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 
PolygonStrokePrimitive2D(const basegfx::B2DPolygon & rPolygon,const attribute::LineAttribute & rLineAttribute,const attribute::StrokeAttribute & rStrokeAttribute)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 
PolygonStrokePrimitive2D(const basegfx::B2DPolygon & rPolygon,const attribute::LineAttribute & rLineAttribute)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 
operator ==(const BasePrimitive2D & rPrimitive) const327 		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 
getB2DRange(const geometry::ViewInformation2D & rViewInformation) const341 		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 	{
create2DDecomposition(const geometry::ViewInformation2D &) const413 		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 
PolygonWavePrimitive2D(const basegfx::B2DPolygon & rPolygon,const attribute::LineAttribute & rLineAttribute,const attribute::StrokeAttribute & rStrokeAttribute,double fWaveWidth,double fWaveHeight)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 
PolygonWavePrimitive2D(const basegfx::B2DPolygon & rPolygon,const attribute::LineAttribute & rLineAttribute,double fWaveWidth,double fWaveHeight)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 
operator ==(const BasePrimitive2D & rPrimitive) const481 		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 
getB2DRange(const geometry::ViewInformation2D & rViewInformation) const494 		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 	{
create2DDecomposition(const geometry::ViewInformation2D &) const526 		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                 double fStartOverlap(0.0);
540                 double fEndOverlap(0.0);
541 
542 				if(!getStart().isDefault() && getStart().isActive())
543 				{
544 					// create start arrow primitive and consume
545 					aArrowA = basegfx::tools::createAreaGeometryForLineStartEnd(
546 						aLocalPolygon, getStart().getB2DPolyPolygon(), true, getStart().getWidth(),
547 						fPolyLength, getStart().isCentered() ? 0.5 : 0.0, &fStart);
548 
549                     // create some overlapping, compromise between straight and peaked markers
550                     // best for marker width 0.3cm and marker line width 0.02cm
551                     fStartOverlap = getStart().getWidth() / 15.0;
552 				}
553 
554 				if(!getEnd().isDefault() && getEnd().isActive())
555 				{
556 					// create end arrow primitive and consume
557 					aArrowB = basegfx::tools::createAreaGeometryForLineStartEnd(
558 						aLocalPolygon, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(),
559 						fPolyLength, getEnd().isCentered() ? 0.5 : 0.0, &fEnd);
560 
561 					// create some overlapping
562                     fEndOverlap = getEnd().getWidth() / 15.0;
563 				}
564 
565 				if(0.0 != fStart || 0.0 != fEnd)
566 				{
567 					// build new poly, consume something from old poly
568                     aLocalPolygon = basegfx::tools::getSnippetAbsolute(aLocalPolygon, fStart-fStartOverlap, fPolyLength - fEnd + fEndOverlap, fPolyLength);
569 				}
570 			}
571 
572 			// prepare return value
573 			Primitive2DSequence aRetval(1L + (aArrowA.count() ? 1L : 0L) + (aArrowB.count() ? 1L : 0L));
574 			sal_uInt32 nInd(0L);
575 
576 			// add shaft
577 			const Primitive2DReference xRefShaft(new
578                 PolygonStrokePrimitive2D(
579                     aLocalPolygon, getLineAttribute(), getStrokeAttribute()));
580 			aRetval[nInd++] = xRefShaft;
581 
582 			if(aArrowA.count())
583 			{
584 				const Primitive2DReference xRefA(
585                     new PolyPolygonColorPrimitive2D(
586                         aArrowA, getLineAttribute().getColor()));
587 				aRetval[nInd++] = xRefA;
588 			}
589 
590 			if(aArrowB.count())
591 			{
592 				const Primitive2DReference xRefB(
593                     new PolyPolygonColorPrimitive2D(
594                         aArrowB, getLineAttribute().getColor()));
595 				aRetval[nInd++] = xRefB;
596 			}
597 
598 			return aRetval;
599 		}
600 
PolygonStrokeArrowPrimitive2D(const basegfx::B2DPolygon & rPolygon,const attribute::LineAttribute & rLineAttribute,const attribute::StrokeAttribute & rStrokeAttribute,const attribute::LineStartEndAttribute & rStart,const attribute::LineStartEndAttribute & rEnd)601 		PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
602 			const basegfx::B2DPolygon& rPolygon,
603             const attribute::LineAttribute& rLineAttribute,
604 			const attribute::StrokeAttribute& rStrokeAttribute,
605 			const attribute::LineStartEndAttribute& rStart,
606 			const attribute::LineStartEndAttribute& rEnd)
607 		:	PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute),
608 			maStart(rStart),
609 			maEnd(rEnd)
610 		{
611 		}
612 
PolygonStrokeArrowPrimitive2D(const basegfx::B2DPolygon & rPolygon,const attribute::LineAttribute & rLineAttribute,const attribute::LineStartEndAttribute & rStart,const attribute::LineStartEndAttribute & rEnd)613 		PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
614 			const basegfx::B2DPolygon& rPolygon,
615             const attribute::LineAttribute& rLineAttribute,
616 			const attribute::LineStartEndAttribute& rStart,
617 			const attribute::LineStartEndAttribute& rEnd)
618 		:	PolygonStrokePrimitive2D(rPolygon, rLineAttribute),
619 			maStart(rStart),
620 			maEnd(rEnd)
621 		{
622 		}
623 
operator ==(const BasePrimitive2D & rPrimitive) const624 		bool PolygonStrokeArrowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
625 		{
626 			if(PolygonStrokePrimitive2D::operator==(rPrimitive))
627 			{
628 				const PolygonStrokeArrowPrimitive2D& rCompare = (PolygonStrokeArrowPrimitive2D&)rPrimitive;
629 
630 				return (getStart() == rCompare.getStart()
631 					&& getEnd() == rCompare.getEnd());
632 			}
633 
634 			return false;
635 		}
636 
getB2DRange(const geometry::ViewInformation2D & rViewInformation) const637 		basegfx::B2DRange PolygonStrokeArrowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
638 		{
639 			basegfx::B2DRange aRetval;
640 
641 			if(getStart().isActive() || getEnd().isActive())
642 			{
643 				// use decomposition when line start/end is used
644 				return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
645 			}
646 			else
647 			{
648 				// get range from parent
649 				return PolygonStrokePrimitive2D::getB2DRange(rViewInformation);
650 			}
651 		}
652 
653 		// provide unique ID
654 		ImplPrimitrive2DIDBlock(PolygonStrokeArrowPrimitive2D, PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D)
655 
656 	} // end of namespace primitive2d
657 } // end of namespace drawinglayer
658 
659 //////////////////////////////////////////////////////////////////////////////
660 // eof
661