LibreOffice Module svx (master) 1
sdrframeborderprimitive2d.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
26
27namespace
28{
29 double snapToDiscreteUnit(
30 double fValue,
31 double fMinimalDiscreteUnit)
32 {
33 if(0.0 != fValue)
34 {
35 fValue = std::max(fValue, fMinimalDiscreteUnit);
36 }
37
38 return fValue;
39 }
40
41 class StyleVectorCombination
42 {
43 private:
44 struct OffsetAndHalfWidthAndColor
45 {
46 double mfOffset;
47 double mfHalfWidth;
49
50 OffsetAndHalfWidthAndColor(double offset, double halfWidth, Color color) :
51 mfOffset(offset),
52 mfHalfWidth(halfWidth),
54 {}
55 };
56
57 double mfRefModeOffset;
58 basegfx::B2DVector maB2DVector;
59 double mfAngle;
60 std::vector< OffsetAndHalfWidthAndColor > maOffsets;
61
62 public:
63 StyleVectorCombination(
64 const svx::frame::Style& rStyle,
65 const basegfx::B2DVector& rB2DVector,
66 double fAngle,
67 bool bMirrored,
68 const Color* pForceColor,
69 double fMinimalDiscreteUnit)
70 : mfRefModeOffset(0.0),
71 maB2DVector(rB2DVector),
72 mfAngle(fAngle)
73 {
74 if (!rStyle.IsUsed())
75 return;
76
77 svx::frame::RefMode aRefMode(rStyle.GetRefMode());
78 Color aPrim(rStyle.GetColorPrim());
79 Color aSecn(rStyle.GetColorSecn());
80 const bool bSecnUsed(0.0 != rStyle.Secn());
81
82 // Get the single segment line widths. This is the point where the
83 // minimal discrete unit will be used if given (fMinimalDiscreteUnit). If
84 // not given it's 0.0 and thus will have no influence.
85 double fPrim(snapToDiscreteUnit(rStyle.Prim(), fMinimalDiscreteUnit));
86 const double fDist(snapToDiscreteUnit(rStyle.Dist(), fMinimalDiscreteUnit));
87 double fSecn(snapToDiscreteUnit(rStyle.Secn(), fMinimalDiscreteUnit));
88
89 // Of course also do not use svx::frame::Style::GetWidth() for obvious
90 // reasons.
91 const double fStyleWidth(fPrim + fDist + fSecn);
92
93 if(bMirrored)
94 {
95 switch(aRefMode)
96 {
99 default: break;
100 }
101
102 if(bSecnUsed)
103 {
104 std::swap(aPrim, aSecn);
105 std::swap(fPrim, fSecn);
106 }
107 }
108
109 if (svx::frame::RefMode::Centered != aRefMode)
110 {
111 const double fHalfWidth(fStyleWidth * 0.5);
112
113 if (svx::frame::RefMode::Begin == aRefMode)
114 {
115 // move aligned below vector
116 mfRefModeOffset = fHalfWidth;
117 }
118 else if (svx::frame::RefMode::End == aRefMode)
119 {
120 // move aligned above vector
121 mfRefModeOffset = -fHalfWidth;
122 }
123 }
124
125 if (bSecnUsed)
126 {
127 // both or all three lines used
128 const bool bPrimTransparent(rStyle.GetColorPrim().IsFullyTransparent());
129 const bool bDistTransparent(!rStyle.UseGapColor() || rStyle.GetColorGap().IsFullyTransparent());
130 const bool bSecnTransparent(aSecn.IsFullyTransparent());
131
132 if(!bPrimTransparent || !bDistTransparent || !bSecnTransparent)
133 {
134 const double a(mfRefModeOffset - (fStyleWidth * 0.5));
135 const double b(a + fPrim);
136 const double c(b + fDist);
137 const double d(c + fSecn);
138
139 maOffsets.push_back(
140 OffsetAndHalfWidthAndColor(
141 (a + b) * 0.5,
142 fPrim * 0.5,
143 nullptr != pForceColor ? *pForceColor : aPrim));
144
145 maOffsets.push_back(
146 OffsetAndHalfWidthAndColor(
147 (b + c) * 0.5,
148 fDist * 0.5,
149 rStyle.UseGapColor()
150 ? (nullptr != pForceColor ? *pForceColor : rStyle.GetColorGap())
151 : COL_TRANSPARENT));
152
153 maOffsets.push_back(
154 OffsetAndHalfWidthAndColor(
155 (c + d) * 0.5,
156 fSecn * 0.5,
157 nullptr != pForceColor ? *pForceColor : aSecn));
158 }
159 }
160 else
161 {
162 // one line used, push two values, from outer to inner
163 if(!rStyle.GetColorPrim().IsFullyTransparent())
164 {
165 maOffsets.push_back(
166 OffsetAndHalfWidthAndColor(
167 mfRefModeOffset,
168 fPrim * 0.5,
169 nullptr != pForceColor ? *pForceColor : aPrim));
170 }
171 }
172 }
173
174 double getRefModeOffset() const { return mfRefModeOffset; }
175 const basegfx::B2DVector& getB2DVector() const { return maB2DVector; }
176 double getAngle() const { return mfAngle; }
177 bool empty() const { return maOffsets.empty(); }
178 size_t size() const { return maOffsets.size(); }
179
180 void getColorAndOffsetAndHalfWidth(size_t nIndex, Color& rColor, double& rfOffset, double& rfHalfWidth) const
181 {
182 if(nIndex >= maOffsets.size())
183 return;
184 const OffsetAndHalfWidthAndColor& rCandidate(maOffsets[nIndex]);
185 rfOffset = rCandidate.mfOffset;
186 rfHalfWidth = rCandidate.mfHalfWidth;
187 rColor = rCandidate.maColor;
188 }
189 };
190
191 class StyleVectorTable
192 {
193 private:
194 std::vector< StyleVectorCombination > maEntries;
195
196 public:
197 StyleVectorTable()
198 {
199 }
200
201 void add(
202 const svx::frame::Style& rStyle,
203 const basegfx::B2DVector& rMyVector,
204 const basegfx::B2DVector& rOtherVector,
205 bool bMirrored,
206 double fMinimalDiscreteUnit)
207 {
208 if(!rStyle.IsUsed() || basegfx::areParallel(rMyVector, rOtherVector))
209 return;
210
211 // create angle between both. angle() needs vectors pointing away from the same point,
212 // so take the mirrored one. Add M_PI to get from -pi..+pi to [0..M_PI_2] for sorting
213 const double fAngle(basegfx::B2DVector(-rMyVector.getX(), -rMyVector.getY()).angle(rOtherVector) + M_PI);
214 maEntries.emplace_back(
215 rStyle,
216 rOtherVector,
217 fAngle,
218 bMirrored,
219 nullptr,
220 fMinimalDiscreteUnit);
221 }
222
223 void sort()
224 {
225 // sort inverse from highest to lowest
226 std::sort(
227 maEntries.begin(),
228 maEntries.end(),
229 [](const StyleVectorCombination& a, const StyleVectorCombination& b)
230 { return a.getAngle() > b.getAngle(); });
231 }
232
233 bool empty() const { return maEntries.empty(); }
234 const std::vector< StyleVectorCombination >& getEntries() const{ return maEntries; }
235 };
236
237 struct CutSet
238 {
239 double mfOLML;
240 double mfORML;
241 double mfOLMR;
242 double mfORMR;
243
244 CutSet() : mfOLML(0.0), mfORML(0.0), mfOLMR(0.0), mfORMR(0.0)
245 {
246 }
247
248 bool operator<( const CutSet& rOther) const
249 {
250 const double fA(mfOLML + mfORML + mfOLMR + mfORMR);
251 const double fB(rOther.mfOLML + rOther.mfORML + rOther.mfOLMR + rOther.mfORMR);
252
253 return fA < fB;
254 }
255
256 double getSum() const { return mfOLML + mfORML + mfOLMR + mfORMR; }
257 };
258
259 void getCutSet(
260 CutSet& rCutSet,
261 const basegfx::B2DPoint& rLeft,
262 const basegfx::B2DPoint& rRight,
263 const basegfx::B2DVector& rX,
264 const basegfx::B2DPoint& rOtherLeft,
265 const basegfx::B2DPoint& rOtherRight,
266 const basegfx::B2DVector& rOtherX)
267 {
269 rLeft,
270 rX,
271 rOtherLeft,
272 rOtherX,
273 CutFlagValue::LINE,
274 &rCutSet.mfOLML);
275
277 rRight,
278 rX,
279 rOtherLeft,
280 rOtherX,
281 CutFlagValue::LINE,
282 &rCutSet.mfOLMR);
283
285 rLeft,
286 rX,
287 rOtherRight,
288 rOtherX,
289 CutFlagValue::LINE,
290 &rCutSet.mfORML);
291
293 rRight,
294 rX,
295 rOtherRight,
296 rOtherX,
297 CutFlagValue::LINE,
298 &rCutSet.mfORMR);
299 }
300
301 struct ExtendSet
302 {
303 double mfExtLeft;
304 double mfExtRight;
305
306 ExtendSet() : mfExtLeft(0.0), mfExtRight(0.0) {}
307 };
308
309 void getExtends(
310 std::vector<ExtendSet>& rExtendSet, // target Left/Right values to fill
311 const basegfx::B2DPoint& rOrigin, // own vector start
312 const StyleVectorCombination& rCombination, // own vector and offsets for lines
313 const basegfx::B2DVector& rPerpendX, // normalized perpendicular to own vector
314 const std::vector< StyleVectorCombination >& rStyleVector) // other vectors emerging in this point
315 {
316 if(!(!rCombination.empty() && !rStyleVector.empty() && rCombination.size() == rExtendSet.size()))
317 return;
318
319 const size_t nOffsetA(rCombination.size());
320
321 if(1 == nOffsetA)
322 {
323 Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0);
324 rCombination.getColorAndOffsetAndHalfWidth(0, aMyColor, fMyOffset, fMyHalfWidth);
325
326 if(!aMyColor.IsFullyTransparent())
327 {
328 const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth)));
329 const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth)));
330 std::vector< CutSet > aCutSets;
331
332 for(const auto& rStyleCandidate : rStyleVector)
333 {
334 const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector()));
335 const size_t nOffsetB(rStyleCandidate.size());
336
337 for(size_t other(0); other < nOffsetB; other++)
338 {
339 Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0);
340 rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth);
341
342 if(!aOtherColor.IsFullyTransparent())
343 {
344 const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth)));
345 const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth)));
346
347 CutSet aNewCutSet;
348 getCutSet(aNewCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector());
349 aCutSets.push_back(aNewCutSet);
350 }
351 }
352 }
353
354 if(!aCutSets.empty())
355 {
356 CutSet aCutSet(aCutSets[0]);
357 const size_t nNumCutSets(aCutSets.size());
358
359 if(1 != nNumCutSets)
360 {
361 double fCutSet(aCutSet.getSum());
362
363 for(size_t a(1); a < nNumCutSets; a++)
364 {
365 const CutSet& rCandidate(aCutSets[a]);
366 const double fCandidate(rCandidate.getSum());
367
368 if(basegfx::fTools::equalZero(fCandidate - fCutSet))
369 {
370 // both have equal center point, use medium cut
371 const double fNewOLML(std::max(std::min(rCandidate.mfOLML, rCandidate.mfORML), std::min(aCutSet.mfOLML, aCutSet.mfORML)));
372 const double fNewORML(std::min(std::max(rCandidate.mfOLML, rCandidate.mfORML), std::max(aCutSet.mfOLML, aCutSet.mfORML)));
373 const double fNewOLMR(std::max(std::min(rCandidate.mfOLMR, rCandidate.mfORMR), std::min(aCutSet.mfOLMR, aCutSet.mfORMR)));
374 const double fNewORMR(std::min(std::max(rCandidate.mfOLMR, rCandidate.mfORMR), std::max(aCutSet.mfOLMR, aCutSet.mfORMR)));
375 aCutSet.mfOLML = fNewOLML;
376 aCutSet.mfORML = fNewORML;
377 aCutSet.mfOLMR = fNewOLMR;
378 aCutSet.mfORMR = fNewORMR;
379 fCutSet = aCutSet.getSum();
380 }
381 else if(fCandidate < fCutSet)
382 {
383 // get minimum
384 fCutSet = fCandidate;
385 aCutSet = rCandidate;
386 }
387 }
388 }
389
390 ExtendSet& rExt(rExtendSet[0]);
391
392 rExt.mfExtLeft = std::min(aCutSet.mfOLML, aCutSet.mfORML);
393 rExt.mfExtRight = std::min(aCutSet.mfOLMR, aCutSet.mfORMR);
394 }
395 }
396 }
397 else
398 {
399 size_t nVisEdgeUp(0);
400 size_t nVisEdgeDn(0);
401
402 for(size_t my(0); my < nOffsetA; my++)
403 {
404 Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0);
405 rCombination.getColorAndOffsetAndHalfWidth(my, aMyColor, fMyOffset, fMyHalfWidth);
406
407 if(!aMyColor.IsFullyTransparent())
408 {
409 const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth)));
410 const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth)));
411 const bool bUpper(my <= (nOffsetA >> 1));
412 const StyleVectorCombination& rStyleCandidate(bUpper ? rStyleVector.front() : rStyleVector.back());
413 const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector()));
414 const size_t nOffsetB(rStyleCandidate.size());
415 std::vector< CutSet > aCutSets;
416
417 for(size_t other(0); other < nOffsetB; other++)
418 {
419 Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0);
420 rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth);
421
422 if(!aOtherColor.IsFullyTransparent())
423 {
424 const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth)));
425 const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth)));
426 CutSet aCutSet;
427 getCutSet(aCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector());
428 aCutSets.push_back(aCutSet);
429 }
430 }
431
432 if(!aCutSets.empty())
433 {
434 // sort: min to start, max to end
435 std::sort(aCutSets.begin(), aCutSets.end());
436 const bool bOtherUpper(rStyleCandidate.getAngle() > M_PI);
437
438 // check if we need min or max
439 // bUpper bOtherUpper MinMax
440 // t t max
441 // t f min
442 // f f max
443 // f t min
444 const bool bMax(bUpper == bOtherUpper);
445 size_t nBaseIndex(0);
446 const size_t nNumCutSets(aCutSets.size());
447
448 if(bMax)
449 {
450 // access at end
451 nBaseIndex = nNumCutSets - 1 - (bUpper ? nVisEdgeUp : nVisEdgeDn);
452 }
453 else
454 {
455 // access at start
456 nBaseIndex = bUpper ? nVisEdgeUp : nVisEdgeDn;
457 }
458
459 const size_t nSecuredIndex(std::clamp(nBaseIndex, size_t(0), size_t(nNumCutSets - 1)));
460 const CutSet& rCutSet(aCutSets[nSecuredIndex]);
461 ExtendSet& rExt(rExtendSet[my]);
462
463 rExt.mfExtLeft = std::min(rCutSet.mfOLML, rCutSet.mfORML);
464 rExt.mfExtRight = std::min(rCutSet.mfOLMR, rCutSet.mfORMR);
465 }
466
467 if(bUpper)
468 {
469 nVisEdgeUp++;
470 }
471 else
472 {
473 nVisEdgeDn++;
474 }
475 }
476 }
477 }
478 }
479
515 void CreateBorderPrimitives(
517 const basegfx::B2DPoint& rOrigin,
518 const basegfx::B2DVector& rX,
519 const svx::frame::Style& rBorder,
520 const StyleVectorTable& rStartStyleVectorTable,
521 const StyleVectorTable& rEndStyleVectorTable,
522 const Color* pForceColor,
523 double fMinimalDiscreteUnit)
524 {
525 // get offset color pairs for style, one per visible line
526 const StyleVectorCombination aCombination(
527 rBorder,
528 rX,
529 0.0,
530 false,
531 pForceColor,
532 fMinimalDiscreteUnit);
533
534 if(aCombination.empty())
535 return;
536
538 const bool bHasStartStyles(!rStartStyleVectorTable.empty());
539 const bool bHasEndStyles(!rEndStyleVectorTable.empty());
540 const size_t nOffsets(aCombination.size());
541 std::vector<ExtendSet> aExtendSetStart(nOffsets);
542 std::vector<ExtendSet> aExtendSetEnd(nOffsets);
543
544 if(bHasStartStyles)
545 {
546 // create extends for line starts, use given point/vector and offsets
547 getExtends(aExtendSetStart, rOrigin, aCombination, aPerpendX, rStartStyleVectorTable.getEntries());
548 }
549
550 if(bHasEndStyles)
551 {
552 // Create extends for line ends, create inverse point/vector and inverse offsets.
553 const StyleVectorCombination aMirroredCombination(
554 rBorder,
555 -rX,
556 0.0,
557 true,
558 pForceColor,
559 fMinimalDiscreteUnit);
560
561 getExtends(aExtendSetEnd, rOrigin + rX, aMirroredCombination, -aPerpendX, rEndStyleVectorTable.getEntries());
562
563 // also need to inverse the result to apply to the correct lines
564 std::reverse(aExtendSetEnd.begin(), aExtendSetEnd.end());
565 }
566
567 std::vector< drawinglayer::primitive2d::BorderLine > aBorderlines;
568 const double fNegLength(-rX.getLength());
569
570 for(size_t a(0); a < nOffsets; a++)
571 {
572 Color aMyColor;
573 double fMyOffset(0.0);
574 double fMyHalfWidth(0.0);
575 aCombination.getColorAndOffsetAndHalfWidth(a, aMyColor, fMyOffset, fMyHalfWidth);
576 const ExtendSet& rExtStart(aExtendSetStart[a]);
577 const ExtendSet& rExtEnd(aExtendSetEnd[a]);
578
579 if(aMyColor.IsFullyTransparent())
580 {
581 aBorderlines.push_back(
583 fMyHalfWidth * 2.0));
584 }
585 else
586 {
587 aBorderlines.push_back(
590 aMyColor.getBColor(),
591 fMyHalfWidth * 2.0),
592 fNegLength * rExtStart.mfExtLeft,
593 fNegLength * rExtStart.mfExtRight,
594 fNegLength * rExtEnd.mfExtRight,
595 fNegLength * rExtEnd.mfExtLeft));
596 }
597 }
598
599 static const double fPatScFact(10.0); // 10.0 multiply, see old code
600 std::vector<double> aDashing(svtools::GetLineDashing(rBorder.Type(), rBorder.PatternScale() * fPatScFact));
601 drawinglayer::attribute::StrokeAttribute aStrokeAttribute(std::move(aDashing));
602 const basegfx::B2DPoint aStart(rOrigin + (aPerpendX * aCombination.getRefModeOffset()));
603
604 rTarget.append(
607 aStart,
608 aStart + rX,
609 std::move(aBorderlines),
610 std::move(aStrokeAttribute))));
611 }
612
613 double getMinimalNonZeroValue(double fCurrent, double fNew)
614 {
615 if(0.0 != fNew)
616 {
617 if(0.0 != fCurrent)
618 {
619 fCurrent = std::min(fNew, fCurrent);
620 }
621 else
622 {
623 fCurrent = fNew;
624 }
625 }
626
627 return fCurrent;
628 }
629
630 double getMinimalNonZeroBorderWidthFromStyle(double fCurrent, const svx::frame::Style& rStyle)
631 {
632 if(rStyle.IsUsed())
633 {
634 fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Prim());
635 fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Dist());
636 fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Secn());
637 }
638
639 return fCurrent;
640 }
641}
642
644{
646 const svx::frame::Style& rStyle,
647 const basegfx::B2DVector& rNormalizedPerpendicular,
648 bool bStyleMirrored)
649 : maStyle(rStyle),
650 maNormalizedPerpendicular(rNormalizedPerpendicular),
651 mbStyleMirrored(bStyleMirrored)
652 {
653 }
654
656 {
657 return mbStyleMirrored == rCompare.mbStyleMirrored
658 && maStyle == rCompare.maStyle
659 && maNormalizedPerpendicular == rCompare.maNormalizedPerpendicular;
660 }
661
663 const basegfx::B2DPoint& rOrigin,
664 const basegfx::B2DVector& rX,
665 const svx::frame::Style& rStyle,
666 const Color* pForceColor)
667 : maOrigin(rOrigin),
668 maX(rX),
669 maStyle(rStyle),
670 maColor(nullptr != pForceColor ? *pForceColor : Color()),
671 mbForceColor(nullptr != pForceColor)
672 {
673 }
674
676 bool bStart,
677 const svx::frame::Style& rStyle,
678 const basegfx::B2DVector& rNormalizedPerpendicular,
679 bool bStyleMirrored)
680 {
681 if(rStyle.IsUsed())
682 {
683 if(bStart)
684 {
685 maStart.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored);
686 }
687 else
688 {
689 maEnd.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored);
690 }
691 }
692 }
693
695 Primitive2DContainer& rContainer,
696 double fMinimalDiscreteUnit) const
697 {
698 StyleVectorTable aStartVector;
699 StyleVectorTable aEndVector;
700 const basegfx::B2DVector aAxis(-maX);
701
702 for(const auto& rStart : maStart)
703 {
704 aStartVector.add(
705 rStart.getStyle(),
706 maX,
707 rStart.getNormalizedPerpendicular(),
708 rStart.getStyleMirrored(),
709 fMinimalDiscreteUnit);
710 }
711
712 for(const auto& rEnd : maEnd)
713 {
714 aEndVector.add(
715 rEnd.getStyle(),
716 aAxis,
717 rEnd.getNormalizedPerpendicular(),
718 rEnd.getStyleMirrored(),
719 fMinimalDiscreteUnit);
720 }
721
722 aStartVector.sort();
723 aEndVector.sort();
724
725 CreateBorderPrimitives(
726 rContainer,
727 maOrigin,
728 maX,
729 maStyle,
730 aStartVector,
731 aEndVector,
732 mbForceColor ? &maColor : nullptr,
733 fMinimalDiscreteUnit);
734 }
735
737 {
738 double fRetval(getMinimalNonZeroBorderWidthFromStyle(0.0, maStyle));
739
740 for(const auto& rStart : maStart)
741 {
742 fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rStart.getStyle());
743 }
744
745 for(const auto& rEnd : maEnd)
746 {
747 fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rEnd.getStyle());
748 }
749
750 return fRetval;
751 }
752
753
755 {
756 return maOrigin == rCompare.maOrigin
757 && maX == rCompare.maX
758 && maStyle == rCompare.maStyle
759 && maColor == rCompare.maColor
760 && mbForceColor == rCompare.mbForceColor
761 && maStart == rCompare.maStart
762 && maEnd == rCompare.maEnd;
763 }
764
765
767 Primitive2DContainer& rContainer,
768 const geometry::ViewInformation2D& /*aViewInformation*/) const
769 {
770 if(getFrameBorders().empty())
771 {
772 return;
773 }
774
775 Primitive2DContainer aRetval;
776
777 // Check and use the minimal non-zero BorderWidth for decompose
778 // if that is set and wanted
779 const double fMinimalDiscreteUnit(doForceToSingleDiscreteUnit()
781 : 0.0);
782
783 {
784 // decompose all buffered SdrFrameBorderData entries and try to merge them
785 // to reduce existing number of BorderLinePrimitive2D(s)
786 for(const auto& rCandidate : getFrameBorders())
787 {
788 // get decomposition on one SdrFrameBorderData entry
789 Primitive2DContainer aPartial;
790 rCandidate.create2DDecomposition(
791 aPartial,
792 fMinimalDiscreteUnit);
793
794 for(const auto& aCandidatePartial : aPartial)
795 {
796 if(aRetval.empty())
797 {
798 // no local data yet, just add as 1st entry, done
799 aRetval.append(aCandidatePartial);
800 }
801 else
802 {
803 bool bDidMerge(false);
804
805 for(auto& aCandidateRetval : aRetval)
806 {
807 // try to merge by appending new data to existing data
808 const drawinglayer::primitive2d::Primitive2DReference aMergeRetvalPartial(
810 static_cast<BorderLinePrimitive2D*>(aCandidateRetval.get()),
811 static_cast<BorderLinePrimitive2D*>(aCandidatePartial.get())));
812
813 if(aMergeRetvalPartial.is())
814 {
815 // could append, replace existing data with merged data, done
816 aCandidateRetval = aMergeRetvalPartial;
817 bDidMerge = true;
818 break;
819 }
820
821 // try to merge by appending existing data to new data
822 const drawinglayer::primitive2d::Primitive2DReference aMergePartialRetval(
824 static_cast<BorderLinePrimitive2D*>(aCandidatePartial.get()),
825 static_cast<BorderLinePrimitive2D*>(aCandidateRetval.get())));
826
827 if(aMergePartialRetval.is())
828 {
829 // could append, replace existing data with merged data, done
830 aCandidateRetval = aMergePartialRetval;
831 bDidMerge = true;
832 break;
833 }
834 }
835
836 if(!bDidMerge)
837 {
838 // no merge after checking all existing data, append as new segment
839 aRetval.append(aCandidatePartial);
840 }
841 }
842 }
843 }
844 }
845
846 rContainer.append(std::move(aRetval));
847 }
848
850 SdrFrameBorderDataVector&& rFrameBorders,
851 bool bForceToSingleDiscreteUnit)
852 : maFrameBorders(std::move(rFrameBorders)),
853 mfMinimalNonZeroBorderWidth(0.0),
854 mfMinimalNonZeroBorderWidthUsedForDecompose(0.0),
855 mbForceToSingleDiscreteUnit(bForceToSingleDiscreteUnit)
856 {
858 {
859 // detect used minimal non-zero partial border width
860 for(const auto& rCandidate : getFrameBorders())
861 {
862 mfMinimalNonZeroBorderWidth = getMinimalNonZeroValue(
864 rCandidate.getMinimalNonZeroBorderWidth());
865 }
866 }
867 }
868
870 {
871 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
872 {
873 const SdrFrameBorderPrimitive2D& rCompare = static_cast<const SdrFrameBorderPrimitive2D&>(rPrimitive);
874
875 return getFrameBorders() == rCompare.getFrameBorders()
877 }
878
879 return false;
880 }
881
884 const geometry::ViewInformation2D& rViewInformation) const
885 {
887 {
888 // Get the current DiscreteUnit, look at X and Y and use the maximum
889 const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
890 double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY())));
891
892 if(fDiscreteUnit <= mfMinimalNonZeroBorderWidth)
893 {
894 // no need to use it, reset
895 fDiscreteUnit = 0.0;
896 }
897
899 {
900 // conditions of last local decomposition have changed, delete
901 // possible content
902 if(!getBuffered2DDecomposition().empty())
903 {
905 }
906
907 // remember new conditions
909 }
910 }
911
912 // call parent. This will call back ::create2DDecomposition above
913 // where mfMinimalNonZeroBorderWidthUsedForDecompose will be used
914 // when doForceToSingleDiscreteUnit() is true
916 }
917
918 // provide unique ID
920 {
922 }
923
924} // end of namespace
925
926/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
basegfx::BColor maColor
double d
bool IsFullyTransparent() const
basegfx::BColor getBColor() const
double angle(const B2DVector &rVec) const
double getLength() const
TYPE getX() const
TYPE getY() const
const basegfx::B2DHomMatrix & getInverseObjectToViewTransformation() const
const Primitive2DContainer & getBuffered2DDecomposition() const
void setBuffered2DDecomposition(Primitive2DContainer &&rNew)
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
void append(const Primitive2DReference &)
SdrConnectStyleData(const svx::frame::Style &rStyle, const basegfx::B2DVector &rNormalizedPerpendicular, bool bStyleMirrored)
void create2DDecomposition(Primitive2DContainer &rContainer, double fMinDiscreteUnit) const
basegfx::B2DVector maX
start point of borderline
svx::frame::Style maStyle
X-Axis of borderline with length.
SdrFrameBorderData(const basegfx::B2DPoint &rOrigin, const basegfx::B2DVector &rX, const svx::frame::Style &rStyle, const Color *pForceColor)
void addSdrConnectStyleData(bool bStart, const svx::frame::Style &rStyle, const basegfx::B2DVector &rNormalizedPerpendicular, bool bStyleMirrored)
bool operator==(const SdrFrameBorderData &rCompare) const
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
const SdrFrameBorderDataVector & getFrameBorders() const
SdrFrameBorderPrimitive2D(SdrFrameBorderDataVector &&rFrameBorders, bool bForceToSingleDiscreteUnit)
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &aViewInformation) const override
Contains the widths of primary and secondary line of a frame style.
Definition: framelink.hxx:99
double Secn() const
Definition: framelink.hxx:130
Color GetColorGap() const
Definition: framelink.hxx:126
Color GetColorSecn() const
Definition: framelink.hxx:125
Color GetColorPrim() const
Definition: framelink.hxx:124
bool UseGapColor() const
Definition: framelink.hxx:127
double Prim() const
Definition: framelink.hxx:128
RefMode GetRefMode() const
Definition: framelink.hxx:123
SvxBorderLineStyle Type() const
Definition: framelink.hxx:132
bool IsUsed() const
Check if this style is used - this depends on it having any width definition.
Definition: framelink.hxx:136
double PatternScale() const
Definition: framelink.hxx:131
double Dist() const
Definition: framelink.hxx:129
FilterGroup & rTarget
uno_Any a
bool equalZero(const T &rfVal)
CutFlagValue findCut(const B2DPoint &rEdge1Start, const B2DVector &rEdge1Delta, const B2DPoint &rEdge2Start, const B2DVector &rEdge2Delta, CutFlagValue aCutFlags, double *pCut1, double *pCut2)
bool areParallel(const B2DVector &rVecA, const B2DVector &rVecB)
B2DVector getNormalizedPerpendicular(const B2DVector &rVec)
size
Primitive2DReference tryMergeBorderLinePrimitive2D(const BorderLinePrimitive2D *pCandidateA, const BorderLinePrimitive2D *pCandidateB)
std::vector< SdrFrameBorderData > SdrFrameBorderDataVector
std::vector< double > GetLineDashing(SvxBorderLineStyle nDashing, double fScale)
RefMode
Specifies how the reference points for frame borders are used.
Definition: framelink.hxx:37
@ Begin
The reference points specify the begin of the frame border width.
@ End
The reference points specify the end of the frame border width.
@ Centered
Frame borders are drawn centered to the reference points.
#define PRIMITIVE2D_ID_SDRFRAMEBORDERTPRIMITIVE2D
const uno::Sequence< double > maOffsets
bool operator<(const Subset &rLHS, const Subset &rRHS)
Definition: ucsubset.hxx:50