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 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_chart2.hxx"
26 
27 #include "RelativePositionHelper.hxx"
28 #include <rtl/math.hxx>
29 
30 using namespace ::com::sun::star;
31 
32 namespace chart
33 {
34 
getReanchoredPosition(const chart2::RelativePosition & rPosition,const chart2::RelativeSize & rObjectSize,drawing::Alignment aNewAnchor)35 chart2::RelativePosition RelativePositionHelper::getReanchoredPosition(
36     const chart2::RelativePosition & rPosition,
37     const chart2::RelativeSize & rObjectSize,
38     drawing::Alignment aNewAnchor )
39 {
40     chart2::RelativePosition aResult( rPosition );
41     if( rPosition.Anchor != aNewAnchor )
42     {
43         sal_Int32 nShiftHalfWidths  = 0;
44         sal_Int32 nShiftHalfHeights = 0;
45 
46         // normalize to top-left
47         switch( rPosition.Anchor )
48         {
49             case drawing::Alignment_TOP_LEFT:
50                 break;
51             case drawing::Alignment_LEFT:
52                 nShiftHalfHeights -= 1;
53                 break;
54             case drawing::Alignment_BOTTOM_LEFT:
55                 nShiftHalfHeights -= 2;
56                 break;
57             case drawing::Alignment_TOP:
58                 nShiftHalfWidths  -= 1;
59                 break;
60             case drawing::Alignment_CENTER:
61                 nShiftHalfWidths  -= 1;
62                 nShiftHalfHeights -= 1;
63                 break;
64             case drawing::Alignment_BOTTOM:
65                 nShiftHalfWidths  -= 1;
66                 nShiftHalfHeights -= 2;
67                 break;
68             case drawing::Alignment_TOP_RIGHT:
69                 nShiftHalfWidths  -= 2;
70                 break;
71             case drawing::Alignment_RIGHT:
72                 nShiftHalfWidths  -= 2;
73                 nShiftHalfHeights -= 1;
74                 break;
75             case drawing::Alignment_BOTTOM_RIGHT:
76                 nShiftHalfWidths  -= 2;
77                 nShiftHalfHeights -= 2;
78                 break;
79             case drawing::Alignment_MAKE_FIXED_SIZE:
80                 break;
81         }
82 
83         // transform
84         switch( aNewAnchor )
85         {
86             case drawing::Alignment_TOP_LEFT:
87                 break;
88             case drawing::Alignment_LEFT:
89                 nShiftHalfHeights += 1;
90                 break;
91             case drawing::Alignment_BOTTOM_LEFT:
92                 nShiftHalfHeights += 2;
93                 break;
94             case drawing::Alignment_TOP:
95                 nShiftHalfWidths  += 1;
96                 break;
97             case drawing::Alignment_CENTER:
98                 nShiftHalfWidths  += 1;
99                 nShiftHalfHeights += 1;
100                 break;
101             case drawing::Alignment_BOTTOM:
102                 nShiftHalfWidths  += 1;
103                 nShiftHalfHeights += 2;
104                 break;
105             case drawing::Alignment_TOP_RIGHT:
106                 nShiftHalfWidths  += 2;
107                 break;
108             case drawing::Alignment_RIGHT:
109                 nShiftHalfWidths  += 2;
110                 nShiftHalfHeights += 1;
111                 break;
112             case drawing::Alignment_BOTTOM_RIGHT:
113                 nShiftHalfWidths  += 2;
114                 nShiftHalfHeights += 2;
115                 break;
116             case drawing::Alignment_MAKE_FIXED_SIZE:
117                 break;
118         }
119 
120         if( nShiftHalfWidths != 0 )
121             aResult.Primary += (rObjectSize.Primary / 2.0) * nShiftHalfWidths;
122         if( nShiftHalfHeights != 0 )
123             aResult.Secondary += (rObjectSize.Secondary / 2.0) * nShiftHalfHeights;
124     }
125 
126     return aResult;
127 }
128 
129 
getUpperLeftCornerOfAnchoredObject(awt::Point aPoint,awt::Size aObjectSize,drawing::Alignment aAnchor)130 awt::Point RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
131       awt::Point aPoint
132     , awt::Size aObjectSize
133     , drawing::Alignment aAnchor )
134 {
135     awt::Point aResult( aPoint );
136 
137     double fXDelta = 0.0;
138     double fYDelta = 0.0;
139 
140     // adapt x-value
141     switch( aAnchor )
142     {
143         case drawing::Alignment_TOP:
144         case drawing::Alignment_CENTER:
145         case drawing::Alignment_BOTTOM:
146             fXDelta -= static_cast< double >( aObjectSize.Width ) / 2.0;
147             break;
148         case drawing::Alignment_TOP_RIGHT:
149         case drawing::Alignment_RIGHT:
150         case drawing::Alignment_BOTTOM_RIGHT:
151             fXDelta -= aObjectSize.Width;
152             break;
153         case drawing::Alignment_TOP_LEFT:
154         case drawing::Alignment_LEFT:
155         case drawing::Alignment_BOTTOM_LEFT:
156         default:
157             // nothing to do
158             break;
159     }
160 
161     // adapt y-value
162     switch( aAnchor )
163     {
164         case drawing::Alignment_LEFT:
165         case drawing::Alignment_CENTER:
166         case drawing::Alignment_RIGHT:
167             fYDelta -= static_cast< double >( aObjectSize.Height ) / 2.0;
168             break;
169         case drawing::Alignment_BOTTOM_LEFT:
170         case drawing::Alignment_BOTTOM:
171         case drawing::Alignment_BOTTOM_RIGHT:
172             fYDelta -= aObjectSize.Height;
173             break;
174         case drawing::Alignment_TOP_LEFT:
175         case drawing::Alignment_TOP:
176         case drawing::Alignment_TOP_RIGHT:
177         default:
178             // nothing to do
179             break;
180     }
181 
182     aResult.X += static_cast< sal_Int32 >( ::rtl::math::round( fXDelta ));
183     aResult.Y += static_cast< sal_Int32 >( ::rtl::math::round( fYDelta ));
184 
185     return aResult;
186 }
187 
getCenterOfAnchoredObject(awt::Point aPoint,awt::Size aUnrotatedObjectSize,drawing::Alignment aAnchor,double fAnglePi)188 awt::Point RelativePositionHelper::getCenterOfAnchoredObject(
189       awt::Point aPoint
190     , awt::Size aUnrotatedObjectSize
191     , drawing::Alignment aAnchor
192     , double fAnglePi )
193 {
194     awt::Point aResult( aPoint );
195 
196     double fXDelta = 0.0;
197     double fYDelta = 0.0;
198 
199     // adapt x-value
200     switch( aAnchor )
201     {
202         case drawing::Alignment_TOP:
203         case drawing::Alignment_CENTER:
204         case drawing::Alignment_BOTTOM:
205             // nothing to do
206             break;
207         case drawing::Alignment_TOP_RIGHT:
208         case drawing::Alignment_RIGHT:
209         case drawing::Alignment_BOTTOM_RIGHT:
210             fXDelta -= aUnrotatedObjectSize.Width/2;
211             break;
212         case drawing::Alignment_TOP_LEFT:
213         case drawing::Alignment_LEFT:
214         case drawing::Alignment_BOTTOM_LEFT:
215         default:
216             fXDelta += aUnrotatedObjectSize.Width/2;
217             break;
218     }
219 
220     // adapt y-value
221     switch( aAnchor )
222     {
223         case drawing::Alignment_LEFT:
224         case drawing::Alignment_CENTER:
225         case drawing::Alignment_RIGHT:
226             // nothing to do
227             break;
228         case drawing::Alignment_BOTTOM_LEFT:
229         case drawing::Alignment_BOTTOM:
230         case drawing::Alignment_BOTTOM_RIGHT:
231             fYDelta -= aUnrotatedObjectSize.Height/2;
232             break;
233         case drawing::Alignment_TOP_LEFT:
234         case drawing::Alignment_TOP:
235         case drawing::Alignment_TOP_RIGHT:
236             fYDelta += aUnrotatedObjectSize.Height/2;
237         default:
238             // nothing to do
239             break;
240     }
241 
242     //take rotation into account:
243     aResult.X += static_cast< sal_Int32 >(
244         ::rtl::math::round(    fXDelta * rtl::math::cos( fAnglePi ) + fYDelta * rtl::math::sin( fAnglePi ) ) );
245     aResult.Y += static_cast< sal_Int32 >(
246         ::rtl::math::round(  - fXDelta * rtl::math::sin( fAnglePi ) + fYDelta * rtl::math::cos( fAnglePi ) ) );
247 
248     return aResult;
249 }
250 
centerGrow(chart2::RelativePosition & rInOutPosition,chart2::RelativeSize & rInOutSize,double fAmountX,double fAmountY,bool bCheck)251 bool RelativePositionHelper::centerGrow(
252     chart2::RelativePosition & rInOutPosition,
253     chart2::RelativeSize & rInOutSize,
254     double fAmountX, double fAmountY,
255     bool bCheck /* = true */ )
256 {
257     chart2::RelativePosition aPos( rInOutPosition );
258     chart2::RelativeSize     aSize( rInOutSize );
259     const double fPosCheckThreshold = 0.02;
260     const double fSizeCheckThreshold = 0.1;
261 
262     // grow/shrink, back to relaative
263     aSize.Primary += fAmountX;
264     aSize.Secondary += fAmountY;
265 
266     double fShiftAmountX = fAmountX / 2.0;
267     double fShiftAmountY = fAmountY / 2.0;
268 
269     // shift X
270     switch( rInOutPosition.Anchor )
271     {
272         case drawing::Alignment_TOP_LEFT:
273         case drawing::Alignment_LEFT:
274         case drawing::Alignment_BOTTOM_LEFT:
275             aPos.Primary -= fShiftAmountX;
276             break;
277         case drawing::Alignment_TOP:
278         case drawing::Alignment_CENTER:
279         case drawing::Alignment_BOTTOM:
280             // nothing
281             break;
282         case drawing::Alignment_TOP_RIGHT:
283         case drawing::Alignment_RIGHT:
284         case drawing::Alignment_BOTTOM_RIGHT:
285             aPos.Primary += fShiftAmountX;
286             break;
287         case drawing::Alignment_MAKE_FIXED_SIZE:
288             break;
289     }
290 
291     // shift Y
292     switch( rInOutPosition.Anchor )
293     {
294         case drawing::Alignment_TOP:
295         case drawing::Alignment_TOP_LEFT:
296         case drawing::Alignment_TOP_RIGHT:
297             aPos.Secondary -= fShiftAmountY;
298             break;
299         case drawing::Alignment_CENTER:
300         case drawing::Alignment_LEFT:
301         case drawing::Alignment_RIGHT:
302             // nothing
303             break;
304         case drawing::Alignment_BOTTOM:
305         case drawing::Alignment_BOTTOM_LEFT:
306         case drawing::Alignment_BOTTOM_RIGHT:
307             aPos.Secondary += fShiftAmountY;
308             break;
309         case drawing::Alignment_MAKE_FIXED_SIZE:
310             break;
311     }
312 
313     // anchor must not be changed
314     OSL_ASSERT( rInOutPosition.Anchor == aPos.Anchor );
315 
316     if( rInOutPosition.Primary == aPos.Primary &&
317         rInOutPosition.Secondary == aPos.Secondary &&
318         rInOutSize.Primary == aSize.Primary &&
319         rInOutSize.Secondary == aSize.Secondary )
320         return false;
321 
322     // check
323     if( bCheck )
324     {
325         // Note: this somewhat complicated check allows the output being
326         // out-of-bounds if the input was also out-of-bounds, and the change is
327         // for "advantage".  E.g., you have a chart that laps out on the left
328         // side. If you shrink it, this should be possible, also if it still
329         // laps out on the left side afterwards. But you shouldn't be able to
330         // grow it then.
331 
332         chart2::RelativePosition aUpperLeft(
333             RelativePositionHelper::getReanchoredPosition( aPos, aSize, drawing::Alignment_TOP_LEFT ));
334         chart2::RelativePosition aLowerRight(
335             RelativePositionHelper::getReanchoredPosition( aPos, aSize, drawing::Alignment_BOTTOM_RIGHT ));
336 
337         // Do not grow, if this leads to corners being off-screen
338         if( fAmountX > 0.0 &&
339             ( (aUpperLeft.Primary < fPosCheckThreshold) ||
340               (aLowerRight.Primary > (1.0 - fPosCheckThreshold)) ))
341             return false;
342         if( fAmountY > 0.0 &&
343             ( (aUpperLeft.Secondary < fPosCheckThreshold) ||
344               (aLowerRight.Secondary > (1.0 - fPosCheckThreshold)) ))
345             return false;
346 
347         // Do not shrink, if this leads to a size too small
348         if( fAmountX < 0.0 &&
349             ( aSize.Primary < fSizeCheckThreshold ))
350             return false;
351         if( fAmountY < 0.0 &&
352             ( aSize.Secondary < fSizeCheckThreshold ))
353             return false;
354     }
355 
356     rInOutPosition = aPos;
357     rInOutSize = aSize;
358     return true;
359 }
360 
moveObject(chart2::RelativePosition & rInOutPosition,const chart2::RelativeSize & rObjectSize,double fAmountX,double fAmountY,bool bCheck)361 bool RelativePositionHelper::moveObject(
362     chart2::RelativePosition & rInOutPosition,
363     const chart2::RelativeSize & rObjectSize,
364     double fAmountX, double fAmountY,
365     bool bCheck /* = true */ )
366 {
367     chart2::RelativePosition aPos( rInOutPosition );
368     aPos.Primary += fAmountX;
369     aPos.Secondary += fAmountY;
370     const double fPosCheckThreshold = 0.02;
371 
372     if( bCheck )
373     {
374         chart2::RelativePosition aUpperLeft(
375             RelativePositionHelper::getReanchoredPosition( aPos, rObjectSize, drawing::Alignment_TOP_LEFT ));
376         chart2::RelativePosition aLowerRight( aUpperLeft );
377         aLowerRight.Primary += rObjectSize.Primary;
378         aLowerRight.Secondary += rObjectSize.Secondary;
379 
380         const double fFarEdgeThreshold = 1.0 - fPosCheckThreshold;
381         if( ( fAmountX > 0.0 && (aLowerRight.Primary > fFarEdgeThreshold)) ||
382             ( fAmountX < 0.0 && (aUpperLeft.Primary < fPosCheckThreshold)) ||
383             ( fAmountY > 0.0 && (aLowerRight.Secondary > fFarEdgeThreshold)) ||
384             ( fAmountY < 0.0 && (aUpperLeft.Secondary < fPosCheckThreshold)) )
385             return false;
386     }
387 
388     rInOutPosition = aPos;
389     return true;
390 }
391 
392 } //  namespace chart
393