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 
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  bool bDidMerge(false);
801 
802  // This algorithm is O(N^2) and repeated dynamic_cast inside would be quite costly.
803  // So check first and skip if the primitives aren't BorderLinePrimitive2D.
804  const drawinglayer::primitive2d::BorderLinePrimitive2D* candidatePartialAsBorder
805  = dynamic_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D*>(aCandidatePartial.get());
806  if(candidatePartialAsBorder)
807  {
808  for(auto& aCandidateRetval : aRetval)
809  {
810  const drawinglayer::primitive2d::BorderLinePrimitive2D* candidateRetvalAsBorder
811  = dynamic_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D*>(aCandidateRetval.get());
812  if(candidateRetvalAsBorder)
813  {
814  // try to merge by appending new data to existing data
815  const drawinglayer::primitive2d::Primitive2DReference aMergeRetvalPartial(
817  candidateRetvalAsBorder,
818  candidatePartialAsBorder));
819 
820  if(aMergeRetvalPartial.is())
821  {
822  // could append, replace existing data with merged data, done
823  aCandidateRetval = aMergeRetvalPartial;
824  bDidMerge = true;
825  break;
826  }
827 
828  // try to merge by appending existing data to new data
829  const drawinglayer::primitive2d::Primitive2DReference aMergePartialRetval(
831  candidatePartialAsBorder,
832  candidateRetvalAsBorder));
833 
834  if(aMergePartialRetval.is())
835  {
836  // could append, replace existing data with merged data, done
837  aCandidateRetval = aMergePartialRetval;
838  bDidMerge = true;
839  break;
840  }
841  }
842  }
843  }
844 
845  if(!bDidMerge)
846  {
847  // no merge after checking all existing data, append as new segment
848  aRetval.append(aCandidatePartial);
849  }
850  }
851  }
852  }
853 
854  rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end());
855  }
856 
858  std::shared_ptr<SdrFrameBorderDataVector>& rFrameBorders,
859  bool bForceToSingleDiscreteUnit)
861  maFrameBorders(std::move(rFrameBorders)),
862  mfMinimalNonZeroBorderWidth(0.0),
863  mfMinimalNonZeroBorderWidthUsedForDecompose(0.0),
864  mbForceToSingleDiscreteUnit(bForceToSingleDiscreteUnit)
865  {
867  {
868  // detect used minimal non-zero partial border width
869  for(const auto& rCandidate : *getFrameBorders())
870  {
871  mfMinimalNonZeroBorderWidth = getMinimalNonZeroValue(
873  rCandidate.getMinimalNonZeroBorderWidth());
874  }
875  }
876  }
877 
879  {
880  if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
881  {
882  const SdrFrameBorderPrimitive2D& rCompare = static_cast<const SdrFrameBorderPrimitive2D&>(rPrimitive);
883 
884  return (getFrameBorders() == rCompare.getFrameBorders()
885  || (getFrameBorders() && rCompare.getFrameBorders()
886  && *getFrameBorders() == *rCompare.getFrameBorders()))
888  }
889 
890  return false;
891  }
892 
895  const geometry::ViewInformation2D& rViewInformation) const
896  {
898  {
899  // Get the current DiscreteUnit, look at X and Y and use the maximum
900  const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
901  double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY())));
902 
903  if(fDiscreteUnit <= mfMinimalNonZeroBorderWidth)
904  {
905  // no need to use it, reset
906  fDiscreteUnit = 0.0;
907  }
908 
910  {
911  // conditions of last local decomposition have changed, delete
912  // possible content
913  if(!getBuffered2DDecomposition().empty())
914  {
916  }
917 
918  // remember new conditions
919  const_cast< SdrFrameBorderPrimitive2D* >(this)->mfMinimalNonZeroBorderWidthUsedForDecompose = fDiscreteUnit;
920  }
921  }
922 
923  // call parent. This will call back ::create2DDecomposition above
924  // where mfMinimalNonZeroBorderWidthUsedForDecompose will be used
925  // when doForceToSingleDiscreteUnit() is true
926  BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
927  }
928 
929  // provide unique ID
931 
932 } // end of namespace
933 
934 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double Dist() const
Definition: framelink.hxx:160
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:154
double getX() const
SvxBorderLineStyle Type() const
Definition: framelink.hxx:163
bool areParallel(const B2DVector &rVecA, const B2DVector &rVecB)
double getY() const
Color GetColorSecn() const
Definition: framelink.hxx:156
The reference points specify the end of the frame border width.
basegfx::BColor maColor
Color GetColorGap() const
Definition: framelink.hxx:157
bool IsUsed() const
Check if this style is used - this depends on it having any width definition.
Definition: framelink.hxx:167
bool UseGapColor() const
Definition: framelink.hxx:158
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:161
#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
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:162
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:155
bool operator<(const Subset &rLHS, const Subset &rRHS)
Definition: ucsubset.hxx:49
SdrFrameBorderPrimitive2D(std::shared_ptr< SdrFrameBorderDataVector > &rFrameBorders, bool bForceToSingleDiscreteUnit)
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
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:159
B2DVector getNormalizedPerpendicular(const B2DVector &rVec)
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)