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 
25 #include <svtools/borderhelper.hxx>
26 
27 namespace
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;
48  Color maColor;
49 
50  OffsetAndHalfWidthAndColor(double offset, double halfWidth, Color color) :
51  mfOffset(offset),
52  mfHalfWidth(halfWidth),
53  maColor(color)
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  maOffsets()
74  {
75  if (rStyle.IsUsed())
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(0xff == rStyle.GetColorPrim().GetTransparency());
129  const bool bDistTransparent(!rStyle.UseGapColor() || 0xff == rStyle.GetColorGap().GetTransparency());
130  const bool bSecnTransparent(0xff == aSecn.GetTransparency());
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(0xff != rStyle.GetColorPrim().GetTransparency())
164  {
165  maOffsets.push_back(
166  OffsetAndHalfWidthAndColor(
167  mfRefModeOffset,
168  fPrim * 0.5,
169  nullptr != pForceColor ? *pForceColor : aPrim));
170  }
171  }
172  }
173  }
174 
175  double getRefModeOffset() const { return mfRefModeOffset; }
176  const basegfx::B2DVector& getB2DVector() const { return maB2DVector; }
177  double getAngle() const { return mfAngle; }
178  bool empty() const { return maOffsets.empty(); }
179  size_t size() const { return maOffsets.size(); }
180 
181  void getColorAndOffsetAndHalfWidth(size_t nIndex, Color& rColor, double& rfOffset, double& rfHalfWidth) const
182  {
183  if(nIndex >= maOffsets.size())
184  return;
185  const OffsetAndHalfWidthAndColor& rCandidate(maOffsets[nIndex]);
186  rfOffset = rCandidate.mfOffset;
187  rfHalfWidth = rCandidate.mfHalfWidth;
188  rColor = rCandidate.maColor;
189  }
190  };
191 
192  class StyleVectorTable
193  {
194  private:
195  std::vector< StyleVectorCombination > maEntries;
196 
197  public:
198  StyleVectorTable()
199  : maEntries()
200  {
201  }
202 
203  void add(
204  const svx::frame::Style& rStyle,
205  const basegfx::B2DVector& rMyVector,
206  const basegfx::B2DVector& rOtherVector,
207  bool bMirrored,
208  double fMinimalDiscreteUnit)
209  {
210  if(rStyle.IsUsed() && !basegfx::areParallel(rMyVector, rOtherVector))
211  {
212  // create angle between both. angle() needs vectors pointing away from the same point,
213  // so take the mirrored one. Add F_PI to get from -pi..+pi to [0..F_PI2] for sorting
214  const double fAngle(basegfx::B2DVector(-rMyVector.getX(), -rMyVector.getY()).angle(rOtherVector) + F_PI);
215  maEntries.emplace_back(
216  rStyle,
217  rOtherVector,
218  fAngle,
219  bMirrored,
220  nullptr,
221  fMinimalDiscreteUnit);
222  }
223  }
224 
225  void sort()
226  {
227  // sort inverse from highest to lowest
228  std::sort(
229  maEntries.begin(),
230  maEntries.end(),
231  [](const StyleVectorCombination& a, const StyleVectorCombination& b)
232  { return a.getAngle() > b.getAngle(); });
233  }
234 
235  bool empty() const { return maEntries.empty(); }
236  const std::vector< StyleVectorCombination >& getEntries() const{ return maEntries; }
237  };
238 
239  struct CutSet
240  {
241  double mfOLML;
242  double mfORML;
243  double mfOLMR;
244  double mfORMR;
245 
246  CutSet() : mfOLML(0.0), mfORML(0.0), mfOLMR(0.0), mfORMR(0.0)
247  {
248  }
249 
250  bool operator<( const CutSet& rOther) const
251  {
252  const double fA(mfOLML + mfORML + mfOLMR + mfORMR);
253  const double fB(rOther.mfOLML + rOther.mfORML + rOther.mfOLMR + rOther.mfORMR);
254 
255  return fA < fB;
256  }
257 
258  double getSum() const { return mfOLML + mfORML + mfOLMR + mfORMR; }
259  };
260 
261  void getCutSet(
262  CutSet& rCutSet,
263  const basegfx::B2DPoint& rLeft,
264  const basegfx::B2DPoint& rRight,
265  const basegfx::B2DVector& rX,
266  const basegfx::B2DPoint& rOtherLeft,
267  const basegfx::B2DPoint& rOtherRight,
268  const basegfx::B2DVector& rOtherX)
269  {
271  rLeft,
272  rX,
273  rOtherLeft,
274  rOtherX,
275  CutFlagValue::LINE,
276  &rCutSet.mfOLML);
277 
279  rRight,
280  rX,
281  rOtherLeft,
282  rOtherX,
283  CutFlagValue::LINE,
284  &rCutSet.mfOLMR);
285 
287  rLeft,
288  rX,
289  rOtherRight,
290  rOtherX,
291  CutFlagValue::LINE,
292  &rCutSet.mfORML);
293 
295  rRight,
296  rX,
297  rOtherRight,
298  rOtherX,
299  CutFlagValue::LINE,
300  &rCutSet.mfORMR);
301  }
302 
303  struct ExtendSet
304  {
305  double mfExtLeft;
306  double mfExtRight;
307 
308  ExtendSet() : mfExtLeft(0.0), mfExtRight(0.0) {}
309  };
310 
311  void getExtends(
312  std::vector<ExtendSet>& rExtendSet, // target Left/Right values to fill
313  const basegfx::B2DPoint& rOrigin, // own vector start
314  const StyleVectorCombination& rCombination, // own vector and offsets for lines
315  const basegfx::B2DVector& rPerpendX, // normalized perpendicular to own vector
316  const std::vector< StyleVectorCombination >& rStyleVector) // other vectors emerging in this point
317  {
318  if(!rCombination.empty() && !rStyleVector.empty() && rCombination.size() == rExtendSet.size())
319  {
320  const size_t nOffsetA(rCombination.size());
321 
322  if(1 == nOffsetA)
323  {
324  Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0);
325  rCombination.getColorAndOffsetAndHalfWidth(0, aMyColor, fMyOffset, fMyHalfWidth);
326 
327  if(0xff != aMyColor.GetTransparency())
328  {
329  const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth)));
330  const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth)));
331  std::vector< CutSet > aCutSets;
332 
333  for(const auto& rStyleCandidate : rStyleVector)
334  {
335  const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector()));
336  const size_t nOffsetB(rStyleCandidate.size());
337 
338  for(size_t other(0); other < nOffsetB; other++)
339  {
340  Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0);
341  rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth);
342 
343  if(0xff != aOtherColor.GetTransparency())
344  {
345  const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth)));
346  const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth)));
347 
348  CutSet aNewCutSet;
349  getCutSet(aNewCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector());
350  aCutSets.push_back(aNewCutSet);
351  }
352  }
353  }
354 
355  if(!aCutSets.empty())
356  {
357  CutSet aCutSet(aCutSets[0]);
358  const size_t nNumCutSets(aCutSets.size());
359 
360  if(1 != nNumCutSets)
361  {
362  double fCutSet(aCutSet.getSum());
363 
364  for(size_t a(1); a < nNumCutSets; a++)
365  {
366  const CutSet& rCandidate(aCutSets[a]);
367  const double fCandidate(rCandidate.getSum());
368 
369  if(basegfx::fTools::equalZero(fCandidate - fCutSet))
370  {
371  // both have equal center point, use medium cut
372  const double fNewOLML(std::max(std::min(rCandidate.mfOLML, rCandidate.mfORML), std::min(aCutSet.mfOLML, aCutSet.mfORML)));
373  const double fNewORML(std::min(std::max(rCandidate.mfOLML, rCandidate.mfORML), std::max(aCutSet.mfOLML, aCutSet.mfORML)));
374  const double fNewOLMR(std::max(std::min(rCandidate.mfOLMR, rCandidate.mfORMR), std::min(aCutSet.mfOLMR, aCutSet.mfORMR)));
375  const double fNewORMR(std::min(std::max(rCandidate.mfOLMR, rCandidate.mfORMR), std::max(aCutSet.mfOLMR, aCutSet.mfORMR)));
376  aCutSet.mfOLML = fNewOLML;
377  aCutSet.mfORML = fNewORML;
378  aCutSet.mfOLMR = fNewOLMR;
379  aCutSet.mfORMR = fNewORMR;
380  fCutSet = aCutSet.getSum();
381  }
382  else if(fCandidate < fCutSet)
383  {
384  // get minimum
385  fCutSet = fCandidate;
386  aCutSet = rCandidate;
387  }
388  }
389  }
390 
391  ExtendSet& rExt(rExtendSet[0]);
392 
393  rExt.mfExtLeft = std::min(aCutSet.mfOLML, aCutSet.mfORML);
394  rExt.mfExtRight = std::min(aCutSet.mfOLMR, aCutSet.mfORMR);
395  }
396  }
397  }
398  else
399  {
400  size_t nVisEdgeUp(0);
401  size_t nVisEdgeDn(0);
402 
403  for(size_t my(0); my < nOffsetA; my++)
404  {
405  Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0);
406  rCombination.getColorAndOffsetAndHalfWidth(my, aMyColor, fMyOffset, fMyHalfWidth);
407 
408  if(0xff != aMyColor.GetTransparency())
409  {
410  const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth)));
411  const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth)));
412  const bool bUpper(my <= (nOffsetA >> 1));
413  const StyleVectorCombination& rStyleCandidate(bUpper ? rStyleVector.front() : rStyleVector.back());
414  const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector()));
415  const size_t nOffsetB(rStyleCandidate.size());
416  std::vector< CutSet > aCutSets;
417 
418  for(size_t other(0); other < nOffsetB; other++)
419  {
420  Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0);
421  rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth);
422 
423  if(0xff != aOtherColor.GetTransparency())
424  {
425  const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth)));
426  const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth)));
427  CutSet aCutSet;
428  getCutSet(aCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector());
429  aCutSets.push_back(aCutSet);
430  }
431  }
432 
433  if(!aCutSets.empty())
434  {
435  // sort: min to start, max to end
436  std::sort(aCutSets.begin(), aCutSets.end());
437  const bool bOtherUpper(rStyleCandidate.getAngle() > F_PI);
438 
439  // check if we need min or max
440  // bUpper bOtherUpper MinMax
441  // t t max
442  // t f min
443  // f f max
444  // f t min
445  const bool bMax(bUpper == bOtherUpper);
446  size_t nBaseIndex(0);
447  const size_t nNumCutSets(aCutSets.size());
448 
449  if(bMax)
450  {
451  // access at end
452  nBaseIndex = nNumCutSets - 1 - (bUpper ? nVisEdgeUp : nVisEdgeDn);
453  }
454  else
455  {
456  // access at start
457  nBaseIndex = bUpper ? nVisEdgeUp : nVisEdgeDn;
458  }
459 
460  const size_t nSecuredIndex(std::min(nNumCutSets - 1, std::max(nBaseIndex, static_cast< size_t >(0))));
461  const CutSet& rCutSet(aCutSets[nSecuredIndex]);
462  ExtendSet& rExt(rExtendSet[my]);
463 
464  rExt.mfExtLeft = std::min(rCutSet.mfOLML, rCutSet.mfORML);
465  rExt.mfExtRight = std::min(rCutSet.mfOLMR, rCutSet.mfORMR);
466  }
467 
468  if(bUpper)
469  {
470  nVisEdgeUp++;
471  }
472  else
473  {
474  nVisEdgeDn++;
475  }
476  }
477  }
478  }
479  }
480  }
481 
517  void CreateBorderPrimitives(
519  const basegfx::B2DPoint& rOrigin,
520  const basegfx::B2DVector& rX,
521  const svx::frame::Style& rBorder,
522  const StyleVectorTable& rStartStyleVectorTable,
523  const StyleVectorTable& rEndStyleVectorTable,
524  const Color* pForceColor,
525  double fMinimalDiscreteUnit)
526  {
527  // get offset color pairs for style, one per visible line
528  const StyleVectorCombination aCombination(
529  rBorder,
530  rX,
531  0.0,
532  false,
533  pForceColor,
534  fMinimalDiscreteUnit);
535 
536  if(aCombination.empty())
537  return;
538 
540  const bool bHasStartStyles(!rStartStyleVectorTable.empty());
541  const bool bHasEndStyles(!rEndStyleVectorTable.empty());
542  const size_t nOffsets(aCombination.size());
543  std::vector<ExtendSet> aExtendSetStart(nOffsets);
544  std::vector<ExtendSet> aExtendSetEnd(nOffsets);
545 
546  if(bHasStartStyles)
547  {
548  // create extends for line starts, use given point/vector and offsets
549  getExtends(aExtendSetStart, rOrigin, aCombination, aPerpendX, rStartStyleVectorTable.getEntries());
550  }
551 
552  if(bHasEndStyles)
553  {
554  // Create extends for line ends, create inverse point/vector and inverse offsets.
555  const StyleVectorCombination aMirroredCombination(
556  rBorder,
557  -rX,
558  0.0,
559  true,
560  pForceColor,
561  fMinimalDiscreteUnit);
562 
563  getExtends(aExtendSetEnd, rOrigin + rX, aMirroredCombination, -aPerpendX, rEndStyleVectorTable.getEntries());
564 
565  // also need to inverse the result to apply to the correct lines
566  std::reverse(aExtendSetEnd.begin(), aExtendSetEnd.end());
567  }
568 
569  std::vector< drawinglayer::primitive2d::BorderLine > aBorderlines;
570  const double fNegLength(-rX.getLength());
571 
572  for(size_t a(0); a < nOffsets; a++)
573  {
574  Color aMyColor;
575  double fMyOffset(0.0);
576  double fMyHalfWidth(0.0);
577  aCombination.getColorAndOffsetAndHalfWidth(a, aMyColor, fMyOffset, fMyHalfWidth);
578  const ExtendSet& rExtStart(aExtendSetStart[a]);
579  const ExtendSet& rExtEnd(aExtendSetEnd[a]);
580 
581  if(0xff == aMyColor.GetTransparency())
582  {
583  aBorderlines.push_back(
585  fMyHalfWidth * 2.0));
586  }
587  else
588  {
589  aBorderlines.push_back(
592  aMyColor.getBColor(),
593  fMyHalfWidth * 2.0),
594  fNegLength * rExtStart.mfExtLeft,
595  fNegLength * rExtStart.mfExtRight,
596  fNegLength * rExtEnd.mfExtRight,
597  fNegLength * rExtEnd.mfExtLeft));
598  }
599  }
600 
601  static const double fPatScFact(10.0); // 10.0 multiply, see old code
602  const std::vector<double> aDashing(svtools::GetLineDashing(rBorder.Type(), rBorder.PatternScale() * fPatScFact));
603  const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(aDashing);
604  const basegfx::B2DPoint aStart(rOrigin + (aPerpendX * aCombination.getRefModeOffset()));
605 
606  rTarget.append(
609  aStart,
610  aStart + rX,
611  aBorderlines,
612  aStrokeAttribute)));
613  }
614 
615  double getMinimalNonZeroValue(double fCurrent, double fNew)
616  {
617  if(0.0 != fNew)
618  {
619  if(0.0 != fCurrent)
620  {
621  fCurrent = std::min(fNew, fCurrent);
622  }
623  else
624  {
625  fCurrent = fNew;
626  }
627  }
628 
629  return fCurrent;
630  }
631 
632  double getMinimalNonZeroBorderWidthFromStyle(double fCurrent, const svx::frame::Style& rStyle)
633  {
634  if(rStyle.IsUsed())
635  {
636  fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Prim());
637  fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Dist());
638  fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Secn());
639  }
640 
641  return fCurrent;
642  }
643 }
644 
645 namespace drawinglayer
646 {
647  namespace primitive2d
648  {
650  const svx::frame::Style& rStyle,
651  const basegfx::B2DVector& rNormalizedPerpendicular,
652  bool bStyleMirrored)
653  : maStyle(rStyle),
654  maNormalizedPerpendicular(rNormalizedPerpendicular),
655  mbStyleMirrored(bStyleMirrored)
656  {
657  }
658 
660  const basegfx::B2DPoint& rOrigin,
661  const basegfx::B2DVector& rX,
662  const svx::frame::Style& rStyle,
663  const Color* pForceColor)
664  : maOrigin(rOrigin),
665  maX(rX),
666  maStyle(rStyle),
667  maColor(nullptr != pForceColor ? *pForceColor : Color()),
668  mbForceColor(nullptr != pForceColor),
669  maStart(),
670  maEnd()
671  {
672  }
673 
675  bool bStart,
676  const svx::frame::Style& rStyle,
677  const basegfx::B2DVector& rNormalizedPerpendicular,
678  bool bStyleMirrored)
679  {
680  if(rStyle.IsUsed())
681  {
682  if(bStart)
683  {
684  maStart.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored);
685  }
686  else
687  {
688  maEnd.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored);
689  }
690  }
691  }
692 
694  Primitive2DContainer& rContainer,
695  double fMinimalDiscreteUnit) const
696  {
697  StyleVectorTable aStartVector;
698  StyleVectorTable aEndVector;
699  const basegfx::B2DVector aAxis(-maX);
700 
701  for(const auto& rStart : maStart)
702  {
703  aStartVector.add(
704  rStart.getStyle(),
705  maX,
706  rStart.getNormalizedPerpendicular(),
707  rStart.getStyleMirrored(),
708  fMinimalDiscreteUnit);
709  }
710 
711  for(const auto& rEnd : maEnd)
712  {
713  aEndVector.add(
714  rEnd.getStyle(),
715  aAxis,
716  rEnd.getNormalizedPerpendicular(),
717  rEnd.getStyleMirrored(),
718  fMinimalDiscreteUnit);
719  }
720 
721  aStartVector.sort();
722  aEndVector.sort();
723 
724  CreateBorderPrimitives(
725  rContainer,
726  maOrigin,
727  maX,
728  maStyle,
729  aStartVector,
730  aEndVector,
731  mbForceColor ? &maColor : nullptr,
732  fMinimalDiscreteUnit);
733  }
734 
736  {
737  double fRetval(getMinimalNonZeroBorderWidthFromStyle(0.0, maStyle));
738 
739  for(const auto& rStart : maStart)
740  {
741  fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rStart.getStyle());
742  }
743 
744  for(const auto& rEnd : maEnd)
745  {
746  fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rEnd.getStyle());
747  }
748 
749  return fRetval;
750  }
751  } // end of namespace primitive2d
752 } // end of namespace drawinglayer
753 
754 namespace drawinglayer
755 {
756  namespace primitive2d
757  {
759  Primitive2DContainer& rContainer,
760  const geometry::ViewInformation2D& /*aViewInformation*/) const
761  {
762  if(!getFrameBorders())
763  {
764  return;
765  }
766 
767  Primitive2DContainer aRetval;
768 
769  // Check and use the minimal non-zero BorderWidth for decompose
770  // if that is set and wanted
771  const double fMinimalDiscreteUnit(doForceToSingleDiscreteUnit()
773  : 0.0);
774 
775  if(doMergeResult())
776  {
777  // decompose all buffered SdrFrameBorderData entries and try to merge them
778  // to reduce existing number of BorderLinePrimitive2D(s)
779  for(const auto& rCandidate : *getFrameBorders())
780  {
781  // get decomposition on one SdrFrameBorderData entry
782  Primitive2DContainer aPartial;
783  rCandidate.create2DDecomposition(
784  aPartial,
785  fMinimalDiscreteUnit);
786 
787  for(const auto& aCandidatePartial : aPartial)
788  {
789  if(aRetval.empty())
790  {
791  // no local data yet, just add as 1st entry, done
792  aRetval.append(aCandidatePartial);
793  }
794  else
795  {
796  bool bDidMerge(false);
797 
798  for(auto& aCandidateRetval : aRetval)
799  {
800  // try to merge by appending new data to existing data
801  const drawinglayer::primitive2d::Primitive2DReference aMergeRetvalPartial(
803  aCandidateRetval,
804  aCandidatePartial));
805 
806  if(aMergeRetvalPartial.is())
807  {
808  // could append, replace existing data with merged data, done
809  aCandidateRetval = aMergeRetvalPartial;
810  bDidMerge = true;
811  break;
812  }
813 
814  // try to merge by appending existing data to new data
815  const drawinglayer::primitive2d::Primitive2DReference aMergePartialRetval(
817  aCandidatePartial,
818  aCandidateRetval));
819 
820  if(aMergePartialRetval.is())
821  {
822  // could append, replace existing data with merged data, done
823  aCandidateRetval = aMergePartialRetval;
824  bDidMerge = true;
825  break;
826  }
827  }
828 
829  if(!bDidMerge)
830  {
831  // no merge after checking all existing data, append as new segment
832  aRetval.append(aCandidatePartial);
833  }
834  }
835  }
836  }
837  }
838  else
839  {
840  // just decompose all buffered SdrFrameBorderData entries, do not try to merge
841  for(const auto& rCandidate : *getFrameBorders())
842  {
843  rCandidate.create2DDecomposition(
844  aRetval,
845  fMinimalDiscreteUnit);
846  }
847  }
848 
849  rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end());
850  }
851 
853  std::shared_ptr<SdrFrameBorderDataVector>& rFrameBorders,
854  bool bMergeResult,
855  bool bForceToSingleDiscreteUnit)
857  maFrameBorders(std::move(rFrameBorders)),
858  mfMinimalNonZeroBorderWidth(0.0),
859  mfMinimalNonZeroBorderWidthUsedForDecompose(0.0),
860  mbMergeResult(bMergeResult),
861  mbForceToSingleDiscreteUnit(bForceToSingleDiscreteUnit)
862  {
864  {
865  // detect used minimal non-zero partial border width
866  for(const auto& rCandidate : *getFrameBorders())
867  {
868  mfMinimalNonZeroBorderWidth = getMinimalNonZeroValue(
870  rCandidate.getMinimalNonZeroBorderWidth());
871  }
872  }
873  }
874 
876  {
877  if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
878  {
879  const SdrFrameBorderPrimitive2D& rCompare = static_cast<const SdrFrameBorderPrimitive2D&>(rPrimitive);
880 
881  return getFrameBorders() == rCompare.getFrameBorders()
882  && doMergeResult() == rCompare.doMergeResult()
884  }
885 
886  return false;
887  }
888 
891  const geometry::ViewInformation2D& rViewInformation) const
892  {
894  {
895  // Get the current DiscreteUnit, look at X and Y and use the maximum
896  const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
897  double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY())));
898 
899  if(fDiscreteUnit <= mfMinimalNonZeroBorderWidth)
900  {
901  // no need to use it, reset
902  fDiscreteUnit = 0.0;
903  }
904 
906  {
907  // conditions of last local decomposition have changed, delete
908  // possible content
909  if(!getBuffered2DDecomposition().empty())
910  {
912  }
913 
914  // remember new conditions
915  const_cast< SdrFrameBorderPrimitive2D* >(this)->mfMinimalNonZeroBorderWidthUsedForDecompose = fDiscreteUnit;
916  }
917  }
918 
919  // call parent. This will call back ::create2DDecomposition above
920  // where mfMinimalNonZeroBorderWidthUsedForDecompose will be used
921  // when doForceToSingleDiscreteUnit() is true
922  BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
923  }
924 
925  // provide unique ID
927 
928  } // end of namespace primitive2d
929 } // end of namespace drawinglayer
930 
931 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double Dist() const
Definition: framelink.hxx:158
ImplPrimitive2DIDBlock(BorderLinePrimitive2D, PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D) Primitive2DReference tryMergeBorderLinePrimitive2D(const Primitive2DReference &rCandidateA
const std::shared_ptr< SdrFrameBorderDataVector > & getFrameBorders() const
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &aViewInformation) const override
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
sal_uInt8 GetTransparency() const
RefMode GetRefMode() const
Definition: framelink.hxx:152
double getX() const
SvxBorderLineStyle Type() const
Definition: framelink.hxx:161
bool areParallel(const B2DVector &rVecA, const B2DVector &rVecB)
double getY() const
Color GetColorSecn() const
Definition: framelink.hxx:154
The reference points specify the end of the frame border width.
Color GetColorGap() const
Definition: framelink.hxx:155
bool IsUsed() const
Check if this style is used - this depends on it having any width definition.
Definition: framelink.hxx:165
bool UseGapColor() const
Definition: framelink.hxx:156
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
void create2DDecomposition(Primitive2DContainer &rContainer, double fMinDiscreteUnit) const
void addSdrConnectStyleData(bool bStart, const svx::frame::Style &rStyle, const basegfx::B2DVector &rNormalizedPerpendicular, bool bStyleMirrored)
double d
double Secn() const
Definition: framelink.hxx:159
#define F_PI
const Primitive2DContainer & getBuffered2DDecomposition() const
uno_Any a
const uno::Sequence< double > maOffsets
static bool equalZero(const double &rfVal)
virtual void append(const Primitive2DReference &) override
size
SdrFrameBorderData(const basegfx::B2DPoint &rOrigin, const basegfx::B2DVector &rX, const svx::frame::Style &rStyle, const Color *pForceColor)
basegfx::B2DVector maX
start point of borderline
double PatternScale() const
Definition: framelink.hxx:160
svx::frame::Style maStyle
X-Axis of borderline with length.
const basegfx::B2DHomMatrix & getInverseObjectToViewTransformation() const
Color GetColorPrim() const
Definition: framelink.hxx:153
Primitive2DReference DRAWINGLAYER_DLLPUBLIC tryMergeBorderLinePrimitive2D(const Primitive2DReference &rCandidateA, const Primitive2DReference &rCandidateB)
bool operator<(const Subset &rLHS, const Subset &rRHS)
Definition: ucsubset.hxx:49
basegfx::BColor const maColor
SdrConnectStyleData(const svx::frame::Style &rStyle, const basegfx::B2DVector &rNormalizedPerpendicular, bool bStyleMirrored)
Frame borders are drawn centered to the reference points.
Contains the widths of primary and secondary line of a frame style.
Definition: framelink.hxx:101
css::uno::Reference< css::graphic::XPrimitive2D > Primitive2DReference
CutFlagValue findCut(const B2DPoint &rEdge1Start, const B2DVector &rEdge1Delta, const B2DPoint &rEdge2Start, const B2DVector &rEdge2Delta, CutFlagValue aCutFlags, double *pCut1, double *pCut2)
basegfx::BColor getBColor() const
double getLength() const
#define PRIMITIVE2D_ID_SDRFRAMEBORDERTPRIMITIVE2D
std::vector< double > GetLineDashing(SvxBorderLineStyle nDashing, double fScale)
The reference points specify the begin of the frame border width.
double Prim() const
Definition: framelink.hxx:157
B2DVector getNormalizedPerpendicular(const B2DVector &rVec)
SdrFrameBorderPrimitive2D(std::shared_ptr< SdrFrameBorderDataVector > &rFrameBorders, bool bMergeResult, bool bForceToSingleDiscreteUnit)
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
RefMode
Specifies how the reference points for frame borders are used.
Definition: framelink.hxx:39
void setBuffered2DDecomposition(const Primitive2DContainer &rNew)