tdf#162786, tdf#161947: Add support for setuptools and pip
[LibreOffice.git] / drawinglayer / source / primitive2d / borderlineprimitive2d.cxx
blob79e34ec972ce585ee0ae9cd8101901374006f759
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <drawinglayer/geometry/viewinformation2d.hxx>
21 #include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
22 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
23 #include <basegfx/polygon/b2dpolygon.hxx>
24 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
25 #include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
26 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
27 #include <rtl/math.hxx>
29 #include <algorithm>
30 #include <utility>
33 namespace drawinglayer::primitive2d
35 BorderLine::BorderLine(
36 const drawinglayer::attribute::LineAttribute& rLineAttribute,
37 double fStartLeft,
38 double fStartRight,
39 double fEndLeft,
40 double fEndRight)
41 : maLineAttribute(rLineAttribute),
42 mfStartLeft(fStartLeft),
43 mfStartRight(fStartRight),
44 mfEndLeft(fEndLeft),
45 mfEndRight(fEndRight),
46 mbIsGap(false)
50 BorderLine::BorderLine(
51 double fWidth)
52 : maLineAttribute(basegfx::BColor(), fWidth),
53 mfStartLeft(0.0),
54 mfStartRight(0.0),
55 mfEndLeft(0.0),
56 mfEndRight(0.0),
57 mbIsGap(true)
61 BorderLine::~BorderLine()
65 bool BorderLine::operator==(const BorderLine& rBorderLine) const
67 return getLineAttribute() == rBorderLine.getLineAttribute()
68 && getStartLeft() == rBorderLine.getStartLeft()
69 && getStartRight() == rBorderLine.getStartRight()
70 && getEndLeft() == rBorderLine.getEndLeft()
71 && getEndRight() == rBorderLine.getEndRight()
72 && isGap() == rBorderLine.isGap();
75 // helper to add a centered, maybe stroked line primitive to rContainer
76 static void addPolygonStrokePrimitive2D(
77 Primitive2DContainer& rContainer,
78 const basegfx::B2DPoint& rStart,
79 const basegfx::B2DPoint& rEnd,
80 const attribute::LineAttribute& rLineAttribute,
81 const attribute::StrokeAttribute& rStrokeAttribute)
83 basegfx::B2DPolygon aPolygon;
85 aPolygon.append(rStart);
86 aPolygon.append(rEnd);
88 if (rStrokeAttribute.isDefault())
90 rContainer.push_back(
91 new PolygonStrokePrimitive2D(
92 std::move(aPolygon),
93 rLineAttribute));
95 else
97 rContainer.push_back(
98 new PolygonStrokePrimitive2D(
99 std::move(aPolygon),
100 rLineAttribute,
101 rStrokeAttribute));
105 double BorderLinePrimitive2D::getFullWidth() const
107 double fRetval(0.0);
109 for(const auto& candidate : maBorderLines)
111 fRetval += candidate.getLineAttribute().getWidth();
114 return fRetval;
117 Primitive2DReference BorderLinePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
119 if (getStart().equal(getEnd()) || getBorderLines().empty())
120 return nullptr;
122 // get data and vectors
123 basegfx::B2DVector aVector(getEnd() - getStart());
124 aVector.normalize();
125 const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector));
126 const double fFullWidth(getFullWidth());
127 double fOffset(fFullWidth * -0.5);
129 Primitive2DContainer aContainer;
130 for(const auto& candidate : maBorderLines)
132 const double fWidth(candidate.getLineAttribute().getWidth());
134 if(!candidate.isGap())
136 const basegfx::B2DVector aDeltaY(aPerpendicular * (fOffset + (fWidth * 0.5)));
137 const basegfx::B2DPoint aStart(getStart() + aDeltaY);
138 const basegfx::B2DPoint aEnd(getEnd() + aDeltaY);
139 const bool bStartPerpendicular(rtl::math::approxEqual(candidate.getStartLeft(), candidate.getStartRight()));
140 const bool bEndPerpendicular(rtl::math::approxEqual(candidate.getEndLeft(), candidate.getEndRight()));
142 if(bStartPerpendicular && bEndPerpendicular)
144 // start and end extends lead to an edge perpendicular to the line, so we can just use
145 // a PolygonStrokePrimitive2D for representation
146 addPolygonStrokePrimitive2D(
147 aContainer,
148 aStart - (aVector * candidate.getStartLeft()),
149 aEnd + (aVector * candidate.getEndLeft()),
150 candidate.getLineAttribute(),
151 getStrokeAttribute());
153 else
155 // start and/or end extensions lead to a lineStart/End that is *not*
156 // perpendicular to the line itself
157 if(getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
159 // without stroke, we can simply represent that using a filled polygon
160 const basegfx::B2DVector aHalfLineOffset(aPerpendicular * (candidate.getLineAttribute().getWidth() * 0.5));
161 basegfx::B2DPolygon aPolygon;
163 aPolygon.append(aStart - aHalfLineOffset - (aVector * candidate.getStartLeft()));
164 aPolygon.append(aEnd - aHalfLineOffset + (aVector * candidate.getEndLeft()));
165 aPolygon.append(aEnd + aHalfLineOffset + (aVector * candidate.getEndRight()));
166 aPolygon.append(aStart + aHalfLineOffset - (aVector * candidate.getStartRight()));
168 aContainer.push_back(
169 new PolyPolygonColorPrimitive2D(
170 basegfx::B2DPolyPolygon(aPolygon),
171 candidate.getLineAttribute().getColor()));
173 else
175 // with stroke, we have a problem - a filled polygon would lose the
176 // stroke. Let's represent the start and/or end as triangles, the main
177 // line still as PolygonStrokePrimitive2D.
178 // Fill default line Start/End for stroke, so we need no adaptations in else paths
179 basegfx::B2DPoint aStrokeStart(aStart - (aVector * candidate.getStartLeft()));
180 basegfx::B2DPoint aStrokeEnd(aEnd + (aVector * candidate.getEndLeft()));
181 const basegfx::B2DVector aHalfLineOffset(aPerpendicular * (candidate.getLineAttribute().getWidth() * 0.5));
183 if(!bStartPerpendicular)
185 const double fMin(std::min(candidate.getStartLeft(), candidate.getStartRight()));
186 const double fMax(std::max(candidate.getStartLeft(), candidate.getStartRight()));
187 basegfx::B2DPolygon aPolygon;
189 // create a triangle with min/max values for LineStart and add
190 if(rtl::math::approxEqual(candidate.getStartLeft(), fMax))
192 aPolygon.append(aStart - aHalfLineOffset - (aVector * candidate.getStartLeft()));
195 aPolygon.append(aStart - aHalfLineOffset - (aVector * fMin));
196 aPolygon.append(aStart + aHalfLineOffset - (aVector * fMin));
198 if(rtl::math::approxEqual(candidate.getStartRight(), fMax))
200 aPolygon.append(aStart + aHalfLineOffset - (aVector * candidate.getStartRight()));
203 aContainer.push_back(
204 new PolyPolygonColorPrimitive2D(
205 basegfx::B2DPolyPolygon(aPolygon),
206 candidate.getLineAttribute().getColor()));
208 // Adapt StrokeStart accordingly
209 aStrokeStart = aStart - (aVector * fMin);
212 if(!bEndPerpendicular)
214 const double fMin(std::min(candidate.getEndLeft(), candidate.getEndRight()));
215 const double fMax(std::max(candidate.getEndLeft(), candidate.getEndRight()));
216 basegfx::B2DPolygon aPolygon;
218 // create a triangle with min/max values for LineEnd and add
219 if(rtl::math::approxEqual(candidate.getEndLeft(), fMax))
221 aPolygon.append(aEnd - aHalfLineOffset + (aVector * candidate.getEndLeft()));
224 if(rtl::math::approxEqual(candidate.getEndRight(), fMax))
226 aPolygon.append(aEnd + aHalfLineOffset + (aVector * candidate.getEndRight()));
229 aPolygon.append(aEnd + aHalfLineOffset + (aVector * fMin));
230 aPolygon.append(aEnd - aHalfLineOffset + (aVector * fMin));
232 aContainer.push_back(
233 new PolyPolygonColorPrimitive2D(
234 basegfx::B2DPolyPolygon(aPolygon),
235 candidate.getLineAttribute().getColor()));
237 // Adapt StrokeEnd accordingly
238 aStrokeEnd = aEnd + (aVector * fMin);
241 addPolygonStrokePrimitive2D(
242 aContainer,
243 aStrokeStart,
244 aStrokeEnd,
245 candidate.getLineAttribute(),
246 getStrokeAttribute());
251 fOffset += fWidth;
253 return new GroupPrimitive2D(std::move(aContainer));
256 bool BorderLinePrimitive2D::isHorizontalOrVertical(const geometry::ViewInformation2D& rViewInformation) const
258 if (!getStart().equal(getEnd()))
260 const basegfx::B2DHomMatrix& rOTVT = rViewInformation.getObjectToViewTransformation();
261 const basegfx::B2DVector aVector(rOTVT * getEnd() - rOTVT * getStart());
263 return basegfx::fTools::equalZero(aVector.getX()) || basegfx::fTools::equalZero(aVector.getY());
266 return false;
269 BorderLinePrimitive2D::BorderLinePrimitive2D(
270 const basegfx::B2DPoint& rStart,
271 const basegfx::B2DPoint& rEnd,
272 std::vector< BorderLine >&& rBorderLines,
273 drawinglayer::attribute::StrokeAttribute aStrokeAttribute)
274 : maStart(rStart),
275 maEnd(rEnd),
276 maBorderLines(std::move(rBorderLines)),
277 maStrokeAttribute(std::move(aStrokeAttribute))
281 bool BorderLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
283 if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive))
284 return false;
286 const BorderLinePrimitive2D& rCompare = static_cast<const BorderLinePrimitive2D&>(rPrimitive);
288 return (getStart() == rCompare.getStart()
289 && getEnd() == rCompare.getEnd()
290 && getStrokeAttribute() == rCompare.getStrokeAttribute()
291 && getBorderLines() == rCompare.getBorderLines());
294 // provide unique ID
295 sal_uInt32 BorderLinePrimitive2D::getPrimitive2DID() const
297 return PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D;
300 Primitive2DReference tryMergeBorderLinePrimitive2D(
301 const BorderLinePrimitive2D* pCandidateA,
302 const BorderLinePrimitive2D* pCandidateB)
304 assert(pCandidateA);
305 assert(pCandidateB);
307 // start of candidate has to match end of this
308 if(!pCandidateA->getEnd().equal(pCandidateB->getStart()))
310 return Primitive2DReference();
313 // candidate A needs a length
314 if(pCandidateA->getStart().equal(pCandidateA->getEnd()))
316 return Primitive2DReference();
319 // candidate B needs a length
320 if(pCandidateB->getStart().equal(pCandidateB->getEnd()))
322 return Primitive2DReference();
325 // StrokeAttribute has to be equal
326 if(!(pCandidateA->getStrokeAttribute() == pCandidateB->getStrokeAttribute()))
328 return Primitive2DReference();
331 // direction has to be equal -> cross product == 0.0
332 const basegfx::B2DVector aVT(pCandidateA->getEnd() - pCandidateA->getStart());
333 const basegfx::B2DVector aVC(pCandidateB->getEnd() - pCandidateB->getStart());
334 if(aVC.cross(aVT) != 0)
336 return Primitive2DReference();
339 // number BorderLines has to be equal
340 const size_t count(pCandidateA->getBorderLines().size());
341 if(count != pCandidateB->getBorderLines().size())
343 return Primitive2DReference();
346 for(size_t a(0); a < count; a++)
348 const BorderLine& rBT(pCandidateA->getBorderLines()[a]);
349 const BorderLine& rBC(pCandidateB->getBorderLines()[a]);
351 // LineAttribute has to be the same
352 if(!(rBC.getLineAttribute() == rBT.getLineAttribute()))
354 return Primitive2DReference();
357 // isGap has to be the same
358 if(rBC.isGap() != rBT.isGap())
360 return Primitive2DReference();
363 if(rBT.isGap())
365 // when gap, width has to be equal
366 if(!rtl::math::approxEqual(rBT.getLineAttribute().getWidth(), rBC.getLineAttribute().getWidth()))
368 return Primitive2DReference();
371 else
373 // when not gap, the line extends have at least reach to the center ( > 0.0),
374 // else there is an extend usage. When > 0.0 they just overlap, no problem
375 if(rBT.getEndLeft() >= 0.0
376 && rBT.getEndRight() >= 0.0
377 && rBC.getStartLeft() >= 0.0
378 && rBC.getStartRight() >= 0.0)
380 // okay
382 else
384 return Primitive2DReference();
389 // all conditions met, create merged primitive
390 std::vector< BorderLine > aMergedBorderLines;
392 for(size_t a(0); a < count; a++)
394 const BorderLine& rBT(pCandidateA->getBorderLines()[a]);
395 const BorderLine& rBC(pCandidateB->getBorderLines()[a]);
397 if(rBT.isGap())
399 aMergedBorderLines.push_back(rBT);
401 else
403 aMergedBorderLines.push_back(
404 BorderLine(
405 rBT.getLineAttribute(),
406 rBT.getStartLeft(), rBT.getStartRight(),
407 rBC.getEndLeft(), rBC.getEndRight()));
411 return
412 new BorderLinePrimitive2D(
413 pCandidateA->getStart(),
414 pCandidateB->getEnd(),
415 std::move(aMergedBorderLines),
416 pCandidateA->getStrokeAttribute());
419 } // end of namespace
421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */