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