xref: /AOO41X/main/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx (revision f6e50924346d0b8c0b07c91832a97665dd718b0c)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "precompiled_svx.hxx"
25 #include <svx/sdr/primitive2d/sdrmeasureprimitive2d.hxx>
26 #include <svx/sdr/primitive2d/sdrdecompositiontools.hxx>
27 #include <basegfx/matrix/b2dhommatrix.hxx>
28 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
29 #include <svx/sdr/attribute/sdrtextattribute.hxx>
30 #include <basegfx/polygon/b2dpolypolygontools.hxx>
31 #include <basegfx/tools/canvastools.hxx>
32 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
33 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
34 #include <basegfx/matrix/b2dhommatrixtools.hxx>
35 #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
36 
37 //////////////////////////////////////////////////////////////////////////////
38 
39 using namespace com::sun::star;
40 
41 //////////////////////////////////////////////////////////////////////////////
42 
43 namespace drawinglayer
44 {
45     namespace primitive2d
46     {
impCreatePart(const attribute::SdrLineAttribute & rLineAttribute,const basegfx::B2DHomMatrix & rObjectMatrix,const basegfx::B2DPoint & rStart,const basegfx::B2DPoint & rEnd,bool bLeftActive,bool bRightActive) const47         Primitive2DReference SdrMeasurePrimitive2D::impCreatePart(
48             const attribute::SdrLineAttribute& rLineAttribute,
49             const basegfx::B2DHomMatrix& rObjectMatrix,
50             const basegfx::B2DPoint& rStart,
51             const basegfx::B2DPoint& rEnd,
52             bool bLeftActive,
53             bool bRightActive) const
54         {
55             const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
56             basegfx::B2DPolygon aPolygon;
57 
58             aPolygon.append(rStart);
59             aPolygon.append(rEnd);
60 
61             if(rLineStartEnd.isDefault() || (!bLeftActive && !bRightActive))
62             {
63                 return createPolygonLinePrimitive(
64                     aPolygon,
65                     rObjectMatrix,
66                     rLineAttribute,
67                     attribute::SdrLineStartEndAttribute());
68             }
69 
70             if(bLeftActive && bRightActive)
71             {
72                 return createPolygonLinePrimitive(
73                     aPolygon,
74                     rObjectMatrix,
75                     rLineAttribute,
76                     rLineStartEnd);
77             }
78 
79             const basegfx::B2DPolyPolygon aEmpty;
80             const attribute::SdrLineStartEndAttribute aLineStartEnd(
81                 bLeftActive ? rLineStartEnd.getStartPolyPolygon() : aEmpty, bRightActive ? rLineStartEnd.getEndPolyPolygon() : aEmpty,
82                 bLeftActive ? rLineStartEnd.getStartWidth() : 0.0, bRightActive ? rLineStartEnd.getEndWidth() : 0.0,
83                 bLeftActive ? rLineStartEnd.isStartActive() : false, bRightActive ? rLineStartEnd.isEndActive() : false,
84                 bLeftActive ? rLineStartEnd.isStartCentered() : false, bRightActive? rLineStartEnd.isEndCentered() : false);
85 
86             return createPolygonLinePrimitive(aPolygon, rObjectMatrix, rLineAttribute, aLineStartEnd);
87         }
88 
create2DDecomposition(const geometry::ViewInformation2D & aViewInformation) const89         Primitive2DSequence SdrMeasurePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& aViewInformation) const
90         {
91             Primitive2DSequence aRetval;
92             SdrBlockTextPrimitive2D* pBlockText = 0;
93             basegfx::B2DRange aTextRange;
94             double fTextX((getStart().getX() + getEnd().getX()) * 0.5);
95             double fTextY((getStart().getX() + getEnd().getX()) * 0.5);
96             const basegfx::B2DVector aLine(getEnd() - getStart());
97             const double fDistance(aLine.getLength());
98             const double fAngle(atan2(aLine.getY(), aLine.getX()));
99             bool bAutoUpsideDown(false);
100             const attribute::SdrTextAttribute rTextAttribute = getSdrLSTAttribute().getText();
101             const basegfx::B2DHomMatrix aObjectMatrix(
102                 basegfx::tools::createShearXRotateTranslateB2DHomMatrix(0.0, fAngle, getStart()));
103 
104             // preapare text, but do not add yet; it needs to be aligned to
105             // the line geometry
106             if(!rTextAttribute.isDefault())
107             {
108                 basegfx::B2DHomMatrix aTextMatrix;
109                 double fTestAngle(fAngle);
110 
111                 if(getTextRotation())
112                 {
113                     aTextMatrix.rotate(-90.0 * F_PI180);
114                     fTestAngle -= (90.0 * F_PI180);
115 
116                     if(getTextAutoAngle() && fTestAngle < -F_PI)
117                     {
118                         fTestAngle += F_2PI;
119                     }
120                 }
121 
122                 if(getTextAutoAngle())
123                 {
124                     if(fTestAngle > (F_PI / 4.0) || fTestAngle < (-F_PI * (3.0 / 4.0)))
125                     {
126                         bAutoUpsideDown = true;
127                     }
128                 }
129 
130                 // create primitive and get text range
131                 pBlockText = new SdrBlockTextPrimitive2D(
132                     &rTextAttribute.getSdrText(),
133                     rTextAttribute.getOutlinerParaObject(),
134                     aTextMatrix,
135                     SDRTEXTHORZADJUST_CENTER,
136                     SDRTEXTVERTADJUST_CENTER,
137                     rTextAttribute.isScroll(),
138                     false,
139                     false,
140                     false,
141                     false);
142 
143                 aTextRange = pBlockText->getB2DRange(aViewInformation);
144             }
145 
146             // prepare line attribute and result
147             {
148                 const attribute::SdrLineAttribute rLineAttribute(getSdrLSTAttribute().getLine());
149                 bool bArrowsOutside(false);
150                 bool bMainLineSplitted(false);
151                 const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
152                 double fStartArrowW(0.0);
153                 double fStartArrowH(0.0);
154                 double fEndArrowW(0.0);
155                 double fEndArrowH(0.0);
156 
157                 if(!rLineStartEnd.isDefault())
158                 {
159                     if(rLineStartEnd.isStartActive())
160                     {
161                         const basegfx::B2DRange aArrowRange(basegfx::tools::getRange(rLineStartEnd.getStartPolyPolygon()));
162                         fStartArrowW = rLineStartEnd.getStartWidth();
163                         fStartArrowH = aArrowRange.getHeight() * fStartArrowW / aArrowRange.getWidth();
164 
165                         if(rLineStartEnd.isStartCentered())
166                         {
167                             fStartArrowH *= 0.5;
168                         }
169                     }
170 
171                     if(rLineStartEnd.isEndActive())
172                     {
173                         const basegfx::B2DRange aArrowRange(basegfx::tools::getRange(rLineStartEnd.getEndPolyPolygon()));
174                         fEndArrowW = rLineStartEnd.getEndWidth();
175                         fEndArrowH = aArrowRange.getHeight() * fEndArrowW / aArrowRange.getWidth();
176 
177                         if(rLineStartEnd.isEndCentered())
178                         {
179                             fEndArrowH *= 0.5;
180                         }
181                     }
182                 }
183 
184                 const double fSpaceNeededByArrows(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.5));
185                 const double fArrowsOutsideLen((fStartArrowH + fEndArrowH + fStartArrowW + fEndArrowW) * 0.5);
186                 const double fHalfLineWidth(rLineAttribute.getWidth() * 0.5);
187 
188                 if(fSpaceNeededByArrows > fDistance)
189                 {
190                     bArrowsOutside = true;
191                 }
192 
193                 MeasureTextPosition eHorizontal(getHorizontal());
194                 MeasureTextPosition eVertical(getVertical());
195 
196                 if(MEASURETEXTPOSITION_AUTOMATIC == eVertical)
197                 {
198                     eVertical = MEASURETEXTPOSITION_NEGATIVE;
199                 }
200 
201                 if(MEASURETEXTPOSITION_CENTERED == eVertical)
202                 {
203                     bMainLineSplitted = true;
204                 }
205 
206                 if(MEASURETEXTPOSITION_AUTOMATIC == eHorizontal)
207                 {
208                     if(aTextRange.getWidth() > fDistance)
209                     {
210                         eHorizontal = MEASURETEXTPOSITION_NEGATIVE;
211                     }
212                     else
213                     {
214                         eHorizontal = MEASURETEXTPOSITION_CENTERED;
215                     }
216 
217                     if(bMainLineSplitted)
218                     {
219                         if(aTextRange.getWidth() + fSpaceNeededByArrows > fDistance)
220                         {
221                             bArrowsOutside = true;
222                         }
223                     }
224                     else
225                     {
226                         const double fSmallArrowNeed(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.125));
227 
228                         if(aTextRange.getWidth() + fSmallArrowNeed > fDistance)
229                         {
230                             bArrowsOutside = true;
231                         }
232                     }
233                 }
234 
235                 if(MEASURETEXTPOSITION_CENTERED != eHorizontal)
236                 {
237                     bArrowsOutside = true;
238                 }
239 
240                 // switch text above/below?
241                 if(getBelow() || (bAutoUpsideDown && !getTextRotation()))
242                 {
243                     if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
244                     {
245                         eVertical = MEASURETEXTPOSITION_POSITIVE;
246                     }
247                     else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
248                     {
249                         eVertical = MEASURETEXTPOSITION_NEGATIVE;
250                     }
251                 }
252 
253                 const double fMainLineOffset(getBelow() ? getDistance() : -getDistance());
254                 const basegfx::B2DPoint aMainLeft(0.0, fMainLineOffset);
255                 const basegfx::B2DPoint aMainRight(fDistance, fMainLineOffset);
256 
257                 // main line
258                 if(bArrowsOutside)
259                 {
260                     double fLenLeft(fArrowsOutsideLen);
261                     double fLenRight(fArrowsOutsideLen);
262 
263                     if(!bMainLineSplitted)
264                     {
265                         if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
266                         {
267                             fLenLeft = fStartArrowH + aTextRange.getWidth();
268                         }
269                         else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
270                         {
271                             fLenRight = fEndArrowH + aTextRange.getWidth();
272                         }
273                     }
274 
275                     const basegfx::B2DPoint aMainLeftLeft(aMainLeft.getX() - fLenLeft, aMainLeft.getY());
276                     const basegfx::B2DPoint aMainRightRight(aMainRight.getX() + fLenRight, aMainRight.getY());
277 
278                     appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeftLeft, aMainLeft, false, true));
279                     appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainRight, aMainRightRight, true, false));
280 
281                     if(!bMainLineSplitted || MEASURETEXTPOSITION_CENTERED != eHorizontal)
282                     {
283                         appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, false, false));
284                     }
285                 }
286                 else
287                 {
288                     if(bMainLineSplitted)
289                     {
290                         const double fHalfLength((fDistance - (aTextRange.getWidth() + (fStartArrowH + fEndArrowH) * 0.25)) * 0.5);
291                         const basegfx::B2DPoint aMainInnerLeft(aMainLeft.getX() + fHalfLength, aMainLeft.getY());
292                         const basegfx::B2DPoint aMainInnerRight(aMainRight.getX() - fHalfLength, aMainRight.getY());
293 
294                         appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainInnerLeft, true, false));
295                         appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainInnerRight, aMainRight, false, true));
296                     }
297                     else
298                     {
299                         appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, true, true));
300                     }
301                 }
302 
303                 // left/right help line value preparation
304                 const double fTopEdge(getBelow() ? getUpper() + getDistance() : -getUpper() - getDistance());
305                 const double fBottomLeft(getBelow() ? getLower() - getLeftDelta() : getLeftDelta() - getLower());
306                 const double fBottomRight(getBelow() ? getLower() - getRightDelta() : getRightDelta() - getLower());
307 
308                 // left help line
309                 const basegfx::B2DPoint aLeftUp(0.0, fTopEdge);
310                 const basegfx::B2DPoint aLeftDown(0.0, fBottomLeft);
311 
312                 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aLeftDown, aLeftUp, false, false));
313 
314                 // right help line
315                 const basegfx::B2DPoint aRightUp(fDistance, fTopEdge);
316                 const basegfx::B2DPoint aRightDown(fDistance, fBottomRight);
317 
318                 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aRightDown, aRightUp, false, false));
319 
320                 // text horizontal position
321                 if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
322                 {
323                     // left
324                     const double fSmall(fArrowsOutsideLen * 0.18);
325                     fTextX = aMainLeft.getX() - (fStartArrowH + aTextRange.getWidth() + fSmall + fHalfLineWidth);
326 
327                     if(bMainLineSplitted)
328                     {
329                         fTextX -= (fArrowsOutsideLen - fStartArrowH);
330                     }
331 
332                     if(!rTextAttribute.isDefault())
333                     {
334                         fTextX -= rTextAttribute.getTextRightDistance();
335                     }
336                 }
337                 else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
338                 {
339                     // right
340                     const double fSmall(fArrowsOutsideLen * 0.18);
341                     fTextX = aMainRight.getX() + (fEndArrowH + fSmall + fHalfLineWidth);
342 
343                     if(bMainLineSplitted)
344                     {
345                         fTextX += (fArrowsOutsideLen - fEndArrowH);
346                     }
347 
348                     if(!rTextAttribute.isDefault())
349                     {
350                         fTextX += rTextAttribute.getTextLeftDistance();
351                     }
352                 }
353                 else // MEASURETEXTPOSITION_CENTERED
354                 {
355                     // centered
356                     fTextX = aMainLeft.getX() + ((fDistance - aTextRange.getWidth()) * 0.5);
357 
358                     if(!rTextAttribute.isDefault())
359                     {
360                         fTextX += (rTextAttribute.getTextLeftDistance() - rTextAttribute.getTextRightDistance()) / 2L;
361                     }
362                 }
363 
364                 // text vertical position
365                 if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
366                 {
367                     // top
368                     const double fSmall(fArrowsOutsideLen * 0.10);
369                     fTextY = aMainLeft.getY() - (aTextRange.getHeight() + fSmall + fHalfLineWidth);
370 
371                     if(!rTextAttribute.isDefault())
372                     {
373                         fTextY -= rTextAttribute.getTextLowerDistance();
374                     }
375                 }
376                 else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
377                 {
378                     // bottom
379                     const double fSmall(fArrowsOutsideLen * 0.10);
380                     fTextY = aMainLeft.getY() + (fSmall + fHalfLineWidth);
381 
382                     if(!rTextAttribute.isDefault())
383                     {
384                         fTextY += rTextAttribute.getTextUpperDistance();
385                     }
386                 }
387                 else // MEASURETEXTPOSITION_CENTERED
388                 {
389                     // centered
390                     fTextY = aMainLeft.getY() - (aTextRange.getHeight() * 0.5);
391 
392                     if(!rTextAttribute.isDefault())
393                     {
394                         fTextY += (rTextAttribute.getTextUpperDistance() - rTextAttribute.getTextLowerDistance()) / 2L;
395                     }
396                 }
397             }
398 
399             if(getSdrLSTAttribute().getLine().isDefault())
400             {
401                 // embed line geometry to invisible (100% transparent) line group for HitTest
402                 const Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(aRetval));
403 
404                 aRetval = Primitive2DSequence(&xHiddenLines, 1);
405             }
406 
407             if(pBlockText)
408             {
409                 // create transformation to text primitive end position
410                 basegfx::B2DHomMatrix aChange;
411 
412                 // handle auto text rotation
413                 if(bAutoUpsideDown)
414                 {
415                     aChange.rotate(F_PI);
416                 }
417 
418                 // move from aTextRange.TopLeft to fTextX, fTextY
419                 aChange.translate(fTextX - aTextRange.getMinX(), fTextY - aTextRange.getMinY());
420 
421                 // apply object matrix
422                 aChange *= aObjectMatrix;
423 
424                 // apply to existing text primitive
425                 SdrTextPrimitive2D* pNewBlockText = pBlockText->createTransformedClone(aChange);
426                 OSL_ENSURE(pNewBlockText, "SdrMeasurePrimitive2D::create2DDecomposition: Could not create transformed clone of text primitive (!)");
427                 delete pBlockText;
428 
429                 // add to local primitives
430                 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, Primitive2DReference(pNewBlockText));
431             }
432 
433             // add shadow
434             if(!getSdrLSTAttribute().getShadow().isDefault())
435             {
436                 aRetval = createEmbeddedShadowPrimitive(
437                     aRetval,
438                     getSdrLSTAttribute().getShadow());
439             }
440 
441             return aRetval;
442         }
443 
SdrMeasurePrimitive2D(const attribute::SdrLineShadowTextAttribute & rSdrLSTAttribute,const basegfx::B2DPoint & rStart,const basegfx::B2DPoint & rEnd,MeasureTextPosition eHorizontal,MeasureTextPosition eVertical,double fDistance,double fUpper,double fLower,double fLeftDelta,double fRightDelta,bool bBelow,bool bTextRotation,bool bTextAutoAngle)444         SdrMeasurePrimitive2D::SdrMeasurePrimitive2D(
445             const attribute::SdrLineShadowTextAttribute& rSdrLSTAttribute,
446             const basegfx::B2DPoint& rStart,
447             const basegfx::B2DPoint& rEnd,
448             MeasureTextPosition eHorizontal,
449             MeasureTextPosition eVertical,
450             double fDistance,
451             double fUpper,
452             double fLower,
453             double fLeftDelta,
454             double fRightDelta,
455             bool bBelow,
456             bool bTextRotation,
457             bool bTextAutoAngle)
458         :   BufferedDecompositionPrimitive2D(),
459             maSdrLSTAttribute(rSdrLSTAttribute),
460             maStart(rStart),
461             maEnd(rEnd),
462             meHorizontal(eHorizontal),
463             meVertical(eVertical),
464             mfDistance(fDistance),
465             mfUpper(fUpper),
466             mfLower(fLower),
467             mfLeftDelta(fLeftDelta),
468             mfRightDelta(fRightDelta),
469             mbBelow(bBelow),
470             mbTextRotation(bTextRotation),
471             mbTextAutoAngle(bTextAutoAngle)
472         {
473         }
474 
operator ==(const BasePrimitive2D & rPrimitive) const475         bool SdrMeasurePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
476         {
477             if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
478             {
479                 const SdrMeasurePrimitive2D& rCompare = (SdrMeasurePrimitive2D&)rPrimitive;
480 
481                 return (getStart() == rCompare.getStart()
482                     && getEnd() == rCompare.getEnd()
483                     && getHorizontal() == rCompare.getHorizontal()
484                     && getVertical() == rCompare.getVertical()
485                     && getDistance() == rCompare.getDistance()
486                     && getUpper() == rCompare.getUpper()
487                     && getLower() == rCompare.getLower()
488                     && getLeftDelta() == rCompare.getLeftDelta()
489                     && getRightDelta() == rCompare.getRightDelta()
490                     && getBelow() == rCompare.getBelow()
491                     && getTextRotation() == rCompare.getTextRotation()
492                     && getTextAutoAngle() == rCompare.getTextAutoAngle()
493                     && getSdrLSTAttribute() == rCompare.getSdrLSTAttribute());
494             }
495 
496             return false;
497         }
498 
499         // provide unique ID
500         ImplPrimitrive2DIDBlock(SdrMeasurePrimitive2D, PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D)
501 
502     } // end of namespace primitive2d
503 } // end of namespace drawinglayer
504 
505 //////////////////////////////////////////////////////////////////////////////
506 // eof
507