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  return;
77 
78  svx::frame::RefMode aRefMode(rStyle.GetRefMode());
79  Color aPrim(rStyle.GetColorPrim());
80  Color aSecn(rStyle.GetColorSecn());
81  const bool bSecnUsed(0.0 != rStyle.Secn());
82 
83  // Get the single segment line widths. This is the point where the
84  // minimal discrete unit will be used if given (fMinimalDiscreteUnit). If
85  // not given it's 0.0 and thus will have no influence.
86  double fPrim(snapToDiscreteUnit(rStyle.Prim(), fMinimalDiscreteUnit));
87  const double fDist(snapToDiscreteUnit(rStyle.Dist(), fMinimalDiscreteUnit));
88  double fSecn(snapToDiscreteUnit(rStyle.Secn(), fMinimalDiscreteUnit));
89 
90  // Of course also do not use svx::frame::Style::GetWidth() for obvious
91  // reasons.
92  const double fStyleWidth(fPrim + fDist + fSecn);
93 
94  if(bMirrored)
95  {
96  switch(aRefMode)
97  {
100  default: break;
101  }
102 
103  if(bSecnUsed)
104  {
105  std::swap(aPrim, aSecn);
106  std::swap(fPrim, fSecn);
107  }
108  }
109 
110  if (svx::frame::RefMode::Centered != aRefMode)
111  {
112  const double fHalfWidth(fStyleWidth * 0.5);
113 
114  if (svx::frame::RefMode::Begin == aRefMode)
115  {
116  // move aligned below vector
117  mfRefModeOffset = fHalfWidth;
118  }
119  else if (svx::frame::RefMode::End == aRefMode)
120  {
121  // move aligned above vector
122  mfRefModeOffset = -fHalfWidth;
123  }
124  }
125 
126  if (bSecnUsed)
127  {
128  // both or all three lines used
129  const bool bPrimTransparent(rStyle.GetColorPrim().IsFullyTransparent());
130  const bool bDistTransparent(!rStyle.UseGapColor() || rStyle.GetColorGap().IsFullyTransparent());
131  const bool bSecnTransparent(aSecn.IsFullyTransparent());
132 
133  if(!bPrimTransparent || !bDistTransparent || !bSecnTransparent)
134  {
135  const double a(mfRefModeOffset - (fStyleWidth * 0.5));
136  const double b(a + fPrim);
137  const double c(b + fDist);
138  const double d(c + fSecn);
139 
140  maOffsets.push_back(
141  OffsetAndHalfWidthAndColor(
142  (a + b) * 0.5,
143  fPrim * 0.5,
144  nullptr != pForceColor ? *pForceColor : aPrim));
145 
146  maOffsets.push_back(
147  OffsetAndHalfWidthAndColor(
148  (b + c) * 0.5,
149  fDist * 0.5,
150  rStyle.UseGapColor()
151  ? (nullptr != pForceColor ? *pForceColor : rStyle.GetColorGap())
152  : COL_TRANSPARENT));
153 
154  maOffsets.push_back(
155  OffsetAndHalfWidthAndColor(
156  (c + d) * 0.5,
157  fSecn * 0.5,
158  nullptr != pForceColor ? *pForceColor : aSecn));
159  }
160  }
161  else
162  {
163  // one line used, push two values, from outer to inner
164  if(!rStyle.GetColorPrim().IsFullyTransparent())
165  {
166  maOffsets.push_back(
167  OffsetAndHalfWidthAndColor(
168  mfRefModeOffset,
169  fPrim * 0.5,
170  nullptr != pForceColor ? *pForceColor : aPrim));
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  return;
212 
213  // create angle between both. angle() needs vectors pointing away from the same point,
214  // so take the mirrored one. Add F_PI to get from -pi..+pi to [0..F_PI2] for sorting
215  const double fAngle(basegfx::B2DVector(-rMyVector.getX(), -rMyVector.getY()).angle(rOtherVector) + F_PI);
216  maEntries.emplace_back(
217  rStyle,
218  rOtherVector,
219  fAngle,
220  bMirrored,
221  nullptr,
222  fMinimalDiscreteUnit);
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  return;
320 
321  const size_t nOffsetA(rCombination.size());
322 
323  if(1 == nOffsetA)
324  {
325  Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0);
326  rCombination.getColorAndOffsetAndHalfWidth(0, aMyColor, fMyOffset, fMyHalfWidth);
327 
328  if(!aMyColor.IsFullyTransparent())
329  {
330  const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth)));
331  const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth)));
332  std::vector< CutSet > aCutSets;
333 
334  for(const auto& rStyleCandidate : rStyleVector)
335  {
336  const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector()));
337  const size_t nOffsetB(rStyleCandidate.size());
338 
339  for(size_t other(0); other < nOffsetB; other++)
340  {
341  Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0);
342  rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth);
343 
344  if(!aOtherColor.IsFullyTransparent())
345  {
346  const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth)));
347  const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth)));
348 
349  CutSet aNewCutSet;
350  getCutSet(aNewCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector());
351  aCutSets.push_back(aNewCutSet);
352  }
353  }
354  }
355 
356  if(!aCutSets.empty())
357  {
358  CutSet aCutSet(aCutSets[0]);
359  const size_t nNumCutSets(aCutSets.size());
360 
361  if(1 != nNumCutSets)
362  {
363  double fCutSet(aCutSet.getSum());
364 
365  for(size_t a(1); a < nNumCutSets; a++)
366  {
367  const CutSet& rCandidate(aCutSets[a]);
368  const double fCandidate(rCandidate.getSum());
369 
370  if(basegfx::fTools::equalZero(fCandidate - fCutSet))
371  {
372  // both have equal center point, use medium cut
373  const double fNewOLML(std::max(std::min(rCandidate.mfOLML, rCandidate.mfORML), std::min(aCutSet.mfOLML, aCutSet.mfORML)));
374  const double fNewORML(std::min(std::max(rCandidate.mfOLML, rCandidate.mfORML), std::max(aCutSet.mfOLML, aCutSet.mfORML)));
375  const double fNewOLMR(std::max(std::min(rCandidate.mfOLMR, rCandidate.mfORMR), std::min(aCutSet.mfOLMR, aCutSet.mfORMR)));
376  const double fNewORMR(std::min(std::max(rCandidate.mfOLMR, rCandidate.mfORMR), std::max(aCutSet.mfOLMR, aCutSet.mfORMR)));
377  aCutSet.mfOLML = fNewOLML;
378  aCutSet.mfORML = fNewORML;
379  aCutSet.mfOLMR = fNewOLMR;
380  aCutSet.mfORMR = fNewORMR;
381  fCutSet = aCutSet.getSum();
382  }
383  else if(fCandidate < fCutSet)
384  {
385  // get minimum
386  fCutSet = fCandidate;
387  aCutSet = rCandidate;
388  }
389  }
390  }
391 
392  ExtendSet& rExt(rExtendSet[0]);
393 
394  rExt.mfExtLeft = std::min(aCutSet.mfOLML, aCutSet.mfORML);
395  rExt.mfExtRight = std::min(aCutSet.mfOLMR, aCutSet.mfORMR);
396  }
397  }
398  }
399  else
400  {
401  size_t nVisEdgeUp(0);
402  size_t nVisEdgeDn(0);
403 
404  for(size_t my(0); my < nOffsetA; my++)
405  {
406  Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0);
407  rCombination.getColorAndOffsetAndHalfWidth(my, aMyColor, fMyOffset, fMyHalfWidth);
408 
409  if(!aMyColor.IsFullyTransparent())
410  {
411  const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth)));
412  const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth)));
413  const bool bUpper(my <= (nOffsetA >> 1));
414  const StyleVectorCombination& rStyleCandidate(bUpper ? rStyleVector.front() : rStyleVector.back());
415  const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector()));
416  const size_t nOffsetB(rStyleCandidate.size());
417  std::vector< CutSet > aCutSets;
418 
419  for(size_t other(0); other < nOffsetB; other++)
420  {
421  Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0);
422  rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth);
423 
424  if(!aOtherColor.IsFullyTransparent())
425  {
426  const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth)));
427  const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth)));
428  CutSet aCutSet;
429  getCutSet(aCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector());
430  aCutSets.push_back(aCutSet);
431  }
432  }
433 
434  if(!aCutSets.empty())
435  {
436  // sort: min to start, max to end
437  std::sort(aCutSets.begin(), aCutSets.end());
438  const bool bOtherUpper(rStyleCandidate.getAngle() > F_PI);
439 
440  // check if we need min or max
441  // bUpper bOtherUpper MinMax
442  // t t max
443  // t f min
444  // f f max
445  // f t min
446  const bool bMax(bUpper == bOtherUpper);
447  size_t nBaseIndex(0);
448  const size_t nNumCutSets(aCutSets.size());
449 
450  if(bMax)
451  {
452  // access at end
453  nBaseIndex = nNumCutSets - 1 - (bUpper ? nVisEdgeUp : nVisEdgeDn);
454  }
455  else
456  {
457  // access at start
458  nBaseIndex = bUpper ? nVisEdgeUp : nVisEdgeDn;
459  }
460 
461  const size_t nSecuredIndex(std::clamp(nBaseIndex, size_t(0), size_t(nNumCutSets - 1)));
462  const CutSet& rCutSet(aCutSets[nSecuredIndex]);
463  ExtendSet& rExt(rExtendSet[my]);
464 
465  rExt.mfExtLeft = std::min(rCutSet.mfOLML, rCutSet.mfORML);
466  rExt.mfExtRight = std::min(rCutSet.mfOLMR, rCutSet.mfORMR);
467  }
468 
469  if(bUpper)
470  {
471  nVisEdgeUp++;
472  }
473  else
474  {
475  nVisEdgeDn++;
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(aMyColor.IsFullyTransparent())
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 
646 {
648  const svx::frame::Style& rStyle,
649  const basegfx::B2DVector& rNormalizedPerpendicular,
650  bool bStyleMirrored)
651  : maStyle(rStyle),
652  maNormalizedPerpendicular(rNormalizedPerpendicular),
653  mbStyleMirrored(bStyleMirrored)
654  {
655  }
656 
658  {
659  return mbStyleMirrored == rCompare.mbStyleMirrored
660  && maStyle == rCompare.maStyle
661  && maNormalizedPerpendicular == rCompare.maNormalizedPerpendicular;
662  }
663 
665  const basegfx::B2DPoint& rOrigin,
666  const basegfx::B2DVector& rX,
667  const svx::frame::Style& rStyle,
668  const Color* pForceColor)
669  : maOrigin(rOrigin),
670  maX(rX),
671  maStyle(rStyle),
672  maColor(nullptr != pForceColor ? *pForceColor : Color()),
673  mbForceColor(nullptr != pForceColor),
674  maStart(),
675  maEnd()
676  {
677  }
678 
680  bool bStart,
681  const svx::frame::Style& rStyle,
682  const basegfx::B2DVector& rNormalizedPerpendicular,
683  bool bStyleMirrored)
684  {
685  if(rStyle.IsUsed())
686  {
687  if(bStart)
688  {
689  maStart.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored);
690  }
691  else
692  {
693  maEnd.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored);
694  }
695  }
696  }
697 
699  Primitive2DContainer& rContainer,
700  double fMinimalDiscreteUnit) const
701  {
702  StyleVectorTable aStartVector;
703  StyleVectorTable aEndVector;
704  const basegfx::B2DVector aAxis(-maX);
705 
706  for(const auto& rStart : maStart)
707  {
708  aStartVector.add(
709  rStart.getStyle(),
710  maX,
711  rStart.getNormalizedPerpendicular(),
712  rStart.getStyleMirrored(),
713  fMinimalDiscreteUnit);
714  }
715 
716  for(const auto& rEnd : maEnd)
717  {
718  aEndVector.add(
719  rEnd.getStyle(),
720  aAxis,
721  rEnd.getNormalizedPerpendicular(),
722  rEnd.getStyleMirrored(),
723  fMinimalDiscreteUnit);
724  }
725 
726  aStartVector.sort();
727  aEndVector.sort();
728 
729  CreateBorderPrimitives(
730  rContainer,
731  maOrigin,
732  maX,
733  maStyle,
734  aStartVector,
735  aEndVector,
736  mbForceColor ? &maColor : nullptr,
737  fMinimalDiscreteUnit);
738  }
739 
741  {
742  double fRetval(getMinimalNonZeroBorderWidthFromStyle(0.0, maStyle));
743 
744  for(const auto& rStart : maStart)
745  {
746  fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rStart.getStyle());
747  }
748 
749  for(const auto& rEnd : maEnd)
750  {
751  fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rEnd.getStyle());
752  }
753 
754  return fRetval;
755  }
756 
757 
759  {
760  return maOrigin == rCompare.maOrigin
761  && maX == rCompare.maX
762  && maStyle == rCompare.maStyle
763  && maColor == rCompare.maColor
764  && mbForceColor == rCompare.mbForceColor
765  && maStart == rCompare.maStart
766  && maEnd == rCompare.maEnd;
767  }
768 
769 
771  Primitive2DContainer& rContainer,
772  const geometry::ViewInformation2D& /*aViewInformation*/) const
773  {
774  if(!getFrameBorders())
775  {
776  return;
777  }
778 
779  Primitive2DContainer aRetval;
780 
781  // Check and use the minimal non-zero BorderWidth for decompose
782  // if that is set and wanted
783  const double fMinimalDiscreteUnit(doForceToSingleDiscreteUnit()
785  : 0.0);
786 
787  {
788  // decompose all buffered SdrFrameBorderData entries and try to merge them
789  // to reduce existing number of BorderLinePrimitive2D(s)
790  for(const auto& rCandidate : *getFrameBorders())
791  {
792  // get decomposition on one SdrFrameBorderData entry
793  Primitive2DContainer aPartial;
794  rCandidate.create2DDecomposition(
795  aPartial,
796  fMinimalDiscreteUnit);
797 
798  for(const auto& aCandidatePartial : aPartial)
799  {
800  if(aRetval.empty())
801  {
802  // no local data yet, just add as 1st entry, done
803  aRetval.append(aCandidatePartial);
804  }
805  else
806  {
807  bool bDidMerge(false);
808 
809  for(auto& aCandidateRetval : aRetval)
810  {
811  // try to merge by appending new data to existing data
812  const drawinglayer::primitive2d::Primitive2DReference aMergeRetvalPartial(
814  static_cast<BorderLinePrimitive2D*>(aCandidateRetval.get()),
815  static_cast<BorderLinePrimitive2D*>(aCandidatePartial.get())));
816 
817  if(aMergeRetvalPartial.is())
818  {
819  // could append, replace existing data with merged data, done
820  aCandidateRetval = aMergeRetvalPartial;
821  bDidMerge = true;
822  break;
823  }
824 
825  // try to merge by appending existing data to new data
826  const drawinglayer::primitive2d::Primitive2DReference aMergePartialRetval(
828  static_cast<BorderLinePrimitive2D*>(aCandidatePartial.get()),
829  static_cast<BorderLinePrimitive2D*>(aCandidateRetval.get())));
830 
831  if(aMergePartialRetval.is())
832  {
833  // could append, replace existing data with merged data, done
834  aCandidateRetval = aMergePartialRetval;
835  bDidMerge = true;
836  break;
837  }
838  }
839 
840  if(!bDidMerge)
841  {
842  // no merge after checking all existing data, append as new segment
843  aRetval.append(aCandidatePartial);
844  }
845  }
846  }
847  }
848  }
849 
850  rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end());
851  }
852 
854  std::shared_ptr<SdrFrameBorderDataVector>& rFrameBorders,
855  bool bForceToSingleDiscreteUnit)
857  maFrameBorders(std::move(rFrameBorders)),
858  mfMinimalNonZeroBorderWidth(0.0),
859  mfMinimalNonZeroBorderWidthUsedForDecompose(0.0),
860  mbForceToSingleDiscreteUnit(bForceToSingleDiscreteUnit)
861  {
863  {
864  // detect used minimal non-zero partial border width
865  for(const auto& rCandidate : *getFrameBorders())
866  {
867  mfMinimalNonZeroBorderWidth = getMinimalNonZeroValue(
869  rCandidate.getMinimalNonZeroBorderWidth());
870  }
871  }
872  }
873 
875  {
876  if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
877  {
878  const SdrFrameBorderPrimitive2D& rCompare = static_cast<const SdrFrameBorderPrimitive2D&>(rPrimitive);
879 
880  return (getFrameBorders() == rCompare.getFrameBorders()
881  || (getFrameBorders() && rCompare.getFrameBorders()
882  && *getFrameBorders() == *rCompare.getFrameBorders()))
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
929 
930 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
RefMode
Specifies how the reference points for frame borders are used.
Definition: framelink.hxx:36
double Dist() const
Definition: framelink.hxx:129
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
RefMode GetRefMode() const
Definition: framelink.hxx:123
double getX() const
The reference points specify the begin of the frame border width.
SvxBorderLineStyle Type() const
Definition: framelink.hxx:132
bool areParallel(const B2DVector &rVecA, const B2DVector &rVecB)
double getY() const
Color GetColorSecn() const
Definition: framelink.hxx:125
basegfx::BColor maColor
The reference points specify the end of the frame border width.
Color GetColorGap() const
Definition: framelink.hxx:126
bool IsUsed() const
Check if this style is used - this depends on it having any width definition.
Definition: framelink.hxx:136
bool UseGapColor() const
Definition: framelink.hxx:127
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)
Primitive2DReference DRAWINGLAYER_DLLPUBLIC tryMergeBorderLinePrimitive2D(const BorderLinePrimitive2D *pCandidateA, const BorderLinePrimitive2D *pCandidateB)
double d
bool operator==(const SdrFrameBorderData &rCompare) const
double Secn() const
Definition: framelink.hxx:130
#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
bool IsFullyTransparent() const
ImplPrimitive2DIDBlock(BorderLinePrimitive2D, PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D) Primitive2DReference tryMergeBorderLinePrimitive2D(const BorderLinePrimitive2D *pCandidateA
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:131
svx::frame::Style maStyle
X-Axis of borderline with length.
const basegfx::B2DHomMatrix & getInverseObjectToViewTransformation() const
css::uno::Reference< css::graphic::XPrimitive2D > Primitive2DReference
Color GetColorPrim() const
Definition: framelink.hxx:124
bool operator<(const Subset &rLHS, const Subset &rRHS)
Definition: ucsubset.hxx:50
SdrFrameBorderPrimitive2D(std::shared_ptr< SdrFrameBorderDataVector > &rFrameBorders, bool bForceToSingleDiscreteUnit)
SdrConnectStyleData(const svx::frame::Style &rStyle, const basegfx::B2DVector &rNormalizedPerpendicular, bool bStyleMirrored)
Contains the widths of primary and secondary line of a frame style.
Definition: framelink.hxx:98
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)
Frame borders are drawn centered to the reference points.
double Prim() const
Definition: framelink.hxx:128
B2DVector getNormalizedPerpendicular(const B2DVector &rVec)
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
void setBuffered2DDecomposition(const Primitive2DContainer &rNew)