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