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  {
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 F_PI to get from -pi..+pi to [0..F_PI2] for sorting
213  const double fAngle(basegfx::B2DVector(-rMyVector.getX(), -rMyVector.getY()).angle(rOtherVector) + F_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() > F_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  const 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  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())
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.insert(rContainer.end(), aRetval.begin(), aRetval.end());
847  }
848 
850  std::shared_ptr<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()
876  || (getFrameBorders() && rCompare.getFrameBorders()
877  && *getFrameBorders() == *rCompare.getFrameBorders()))
879  }
880 
881  return false;
882  }
883 
886  const geometry::ViewInformation2D& rViewInformation) const
887  {
889  {
890  // Get the current DiscreteUnit, look at X and Y and use the maximum
891  const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
892  double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY())));
893 
894  if(fDiscreteUnit <= mfMinimalNonZeroBorderWidth)
895  {
896  // no need to use it, reset
897  fDiscreteUnit = 0.0;
898  }
899 
901  {
902  // conditions of last local decomposition have changed, delete
903  // possible content
904  if(!getBuffered2DDecomposition().empty())
905  {
907  }
908 
909  // remember new conditions
910  const_cast< SdrFrameBorderPrimitive2D* >(this)->mfMinimalNonZeroBorderWidthUsedForDecompose = fDiscreteUnit;
911  }
912  }
913 
914  // call parent. This will call back ::create2DDecomposition above
915  // where mfMinimalNonZeroBorderWidthUsedForDecompose will be used
916  // when doForceToSingleDiscreteUnit() is true
917  BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
918  }
919 
920  // provide unique ID
922  {
924  }
925 
926 } // end of namespace
927 
928 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
RefMode
Specifies how the reference points for frame borders are used.
Definition: framelink.hxx:36
double getY() const
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
bool equalZero(const T &rfVal)
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
RefMode GetRefMode() const
Definition: framelink.hxx:123
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)
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 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
virtual void append(const Primitive2DReference &) override
size
bool IsFullyTransparent() const
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
double getX() const
std::vector< double > GetLineDashing(SvxBorderLineStyle nDashing, double fScale)
Frame borders are drawn centered to the reference points.
double Prim() const
Definition: framelink.hxx:128
void setBuffered2DDecomposition(Primitive2DContainer &&rNew)
B2DVector getNormalizedPerpendicular(const B2DVector &rVec)
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override