1 /*
2  * Animation plugin for compiz/beryl
3  *
4  * animation.c
5  *
6  * Copyright : (C) 2006 Erkin Bahceci
7  * E-mail    : erkinbah@gmail.com
8  *
9  * Based on Wobbly and Minimize plugins by
10  *           : David Reveman
11  * E-mail    : davidr@novell.com>
12  *
13  * Particle system added by : (C) 2006 Dennis Kasprzyk
14  * E-mail                   : onestone@beryl-project.org
15  *
16  * Beam-Up added by : Florencio Guimaraes
17  * E-mail           : florencio@nexcorp.com.br
18  *
19  * Hexagon tessellator added by : Mike Slegeir
20  * E-mail                       : mikeslegeir@mail.utexas.edu>
21  *
22  * This program is free software; you can redistribute it and/or
23  * modify it under the terms of the GNU General Public License
24  * as published by the Free Software Foundation; either version 2
25  * of the License, or (at your option) any later version.
26  *
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30  * GNU General Public License for more details.
31  *
32  * You should have received a copy of the GNU General Public License
33  * along with this program; if not, write to the Free Software
34  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
35  */
36 
37 #include "animation-internal.h"
38 
39 // =====================  Effect: Zoom and Sidekick  =========================
40 
41 Bool
fxSidekickInit(CompWindow * w)42 fxSidekickInit (CompWindow * w)
43 {
44     ANIM_WINDOW(w);
45 
46     // determine number of rotations randomly in [0.9, 1.1] range
47     aw->numZoomRotations =
48 	animGetF (w, ANIM_SCREEN_OPTION_SIDEKICK_NUM_ROTATIONS) *
49 	(1.0f + 0.2f * rand() / RAND_MAX - 0.1f);
50 
51     float winCenterX = WIN_X(w) + WIN_W(w) / 2.0;
52     float iconCenterX = aw->com.icon.x + aw->com.icon.width / 2.0;
53 
54     // if window is to the right of icon, rotate clockwise instead
55     // to make rotation look more pleasant
56     if (winCenterX > iconCenterX)
57 	aw->numZoomRotations *= -1;
58 
59     return fxZoomInit (w);
60 }
61 
62 static float
fxZoomGetSpringiness(CompWindow * w)63 fxZoomGetSpringiness (CompWindow *w)
64 {
65     ANIM_WINDOW(w);
66 
67     if (aw->com.curAnimEffect == AnimEffectZoom)
68 	return 2 * animGetF (w, ANIM_SCREEN_OPTION_ZOOM_SPRINGINESS);
69     else if (aw->com.curAnimEffect == AnimEffectSidekick)
70 	return 1.6 * animGetF (w, ANIM_SCREEN_OPTION_SIDEKICK_SPRINGINESS);
71     else
72 	return 0.0f;
73 }
74 
75 Bool
fxZoomInit(CompWindow * w)76 fxZoomInit (CompWindow * w)
77 {
78     ANIM_WINDOW(w);
79 
80     if ((aw->com.curAnimEffect == AnimEffectSidekick &&
81 	 (animGetI (w, ANIM_SCREEN_OPTION_SIDEKICK_ZOOM_FROM_CENTER) ==
82 	  ZoomFromCenterOn ||
83 	  ((aw->com.curWindowEvent == WindowEventMinimize ||
84 	    aw->com.curWindowEvent == WindowEventUnminimize) &&
85 	   animGetI (w, ANIM_SCREEN_OPTION_SIDEKICK_ZOOM_FROM_CENTER) ==
86 	   ZoomFromCenterMin) ||
87 	  ((aw->com.curWindowEvent == WindowEventOpen ||
88 	    aw->com.curWindowEvent == WindowEventClose) &&
89 	   animGetI (w, ANIM_SCREEN_OPTION_SIDEKICK_ZOOM_FROM_CENTER) ==
90 	   ZoomFromCenterCreate))) ||
91 	(aw->com.curAnimEffect == AnimEffectZoom &&
92 	 (animGetI (w, ANIM_SCREEN_OPTION_ZOOM_FROM_CENTER) ==
93 	  ZoomFromCenterOn ||
94 	  ((aw->com.curWindowEvent == WindowEventMinimize ||
95 	    aw->com.curWindowEvent == WindowEventUnminimize) &&
96 	   animGetI (w, ANIM_SCREEN_OPTION_ZOOM_FROM_CENTER) ==
97 	   ZoomFromCenterMin) ||
98 	  ((aw->com.curWindowEvent == WindowEventOpen ||
99 	    aw->com.curWindowEvent == WindowEventClose) &&
100 	   animGetI (w, ANIM_SCREEN_OPTION_ZOOM_FROM_CENTER) ==
101 	   ZoomFromCenterCreate))))
102     {
103 	aw->com.icon.x =
104 	    WIN_X(w) + WIN_W(w) / 2 - aw->com.icon.width / 2;
105 	aw->com.icon.y =
106 	    WIN_Y(w) + WIN_H(w) / 2 - aw->com.icon.height / 2;
107     }
108 
109     // allow extra time for spring damping / deceleration
110     if ((aw->com.curWindowEvent == WindowEventUnminimize ||
111 	 aw->com.curWindowEvent == WindowEventOpen) &&
112 	fxZoomGetSpringiness (w) > 1e-4)
113     {
114 	aw->com.animTotalTime /= SPRINGY_ZOOM_PERCEIVED_T;
115     }
116     else if ((aw->com.curAnimEffect == AnimEffectZoom ||
117 	      aw->com.curAnimEffect == AnimEffectSidekick) &&
118 	     (aw->com.curWindowEvent == WindowEventOpen ||
119 	      aw->com.curWindowEvent == WindowEventClose))
120     {
121 	aw->com.animTotalTime /= NONSPRINGY_ZOOM_PERCEIVED_T;
122     }
123     else
124     {
125 	aw->com.animTotalTime /= ZOOM_PERCEIVED_T;
126     }
127     aw->com.animRemainingTime = aw->com.animTotalTime;
128 
129     aw->com.usingTransform = TRUE;
130 
131     return defaultAnimInit (w);
132 }
133 
fxZoomAnimProgress(CompWindow * w,float * moveProgress,float * scaleProgress,Bool neverSpringy)134 void fxZoomAnimProgress (CompWindow *w,
135 			 float *moveProgress,
136 			 float *scaleProgress,
137 			 Bool neverSpringy)
138 {
139     ANIM_WINDOW(w);
140 
141     float forwardProgress =
142 	1 - aw->com.animRemainingTime /
143 	(aw->com.animTotalTime - aw->com.timestep);
144     forwardProgress = MIN(forwardProgress, 1);
145     forwardProgress = MAX(forwardProgress, 0);
146 
147     float x = forwardProgress;
148     Bool backwards = FALSE;
149     int animProgressDir = 1;
150 
151     if (aw->com.curWindowEvent == WindowEventUnminimize ||
152 	aw->com.curWindowEvent == WindowEventOpen)
153 	animProgressDir = 2;
154     if (aw->com.animOverrideProgressDir != 0)
155 	animProgressDir = aw->com.animOverrideProgressDir;
156     if ((animProgressDir == 1 &&
157 	 (aw->com.curWindowEvent == WindowEventUnminimize ||
158 	  aw->com.curWindowEvent == WindowEventOpen)) ||
159 	(animProgressDir == 2 &&
160 	 (aw->com.curWindowEvent == WindowEventMinimize ||
161 	  aw->com.curWindowEvent == WindowEventClose)))
162 	backwards = TRUE;
163     if (backwards)
164 	x = 1 - x;
165 
166     float dampBase = (pow(1-pow(x,1.2)*0.5,10)-pow(0.5,10))/(1-pow(0.5,10));
167     float nonSpringyProgress =
168 	1 - pow(decelerateProgressCustom(1 - x, .5f, .8f), 1.7f);
169 
170     if (moveProgress && scaleProgress)
171     {
172 	float damping =
173 	    pow(dampBase, 0.5);
174 
175 	float damping2 =
176 	    ((pow(1-(pow(x,0.7)*0.5),10)-pow(0.5,10))/(1-pow(0.5,10))) *
177 	    0.7 + 0.3;
178 	float springiness = 0;
179 
180 	// springy only when appearing
181 	if ((aw->com.curWindowEvent == WindowEventUnminimize ||
182 	     aw->com.curWindowEvent == WindowEventOpen) &&
183 	    !neverSpringy)
184 	{
185 	    springiness = fxZoomGetSpringiness (w);
186 	}
187 
188 	float springyMoveProgress =
189 	    cos(2*M_PI*pow(x,1)*1.25) * damping * damping2;
190 
191 	if (springiness > 1e-4f)
192 	{
193 	    if (x > 0.2)
194 	    {
195 		springyMoveProgress *= springiness;
196 	    }
197 	    else
198 	    {
199 		// interpolate between (springyMoveProgress * springiness)
200 		// and springyMoveProgress for smooth transition at 0.2
201 		// (where it crosses y=0)
202 		float progressUpto02 = x / 0.2f;
203 		springyMoveProgress =
204 		    (1 - progressUpto02) * springyMoveProgress +
205 		    progressUpto02 * springyMoveProgress * springiness;
206 	    }
207 	    *moveProgress = 1 - springyMoveProgress;
208 	}
209 	else
210 	{
211 	    *moveProgress = nonSpringyProgress;
212 	}
213 	if (aw->com.curWindowEvent == WindowEventUnminimize ||
214 	    aw->com.curWindowEvent == WindowEventOpen)
215 	    *moveProgress = 1 - *moveProgress;
216 	if (backwards)
217 	    *moveProgress = 1 - *moveProgress;
218 
219 	float scProgress = nonSpringyProgress;
220 	if (aw->com.curWindowEvent == WindowEventUnminimize ||
221 	    aw->com.curWindowEvent == WindowEventOpen)
222 	    scProgress = 1 - scProgress;
223 	if (backwards)
224 	    scProgress = 1 - scProgress;
225 
226 	*scaleProgress =
227 	    pow(scProgress, 1.25);
228     }
229 }
230 
231 void
fxZoomUpdateWindowAttrib(CompWindow * w,WindowPaintAttrib * wAttrib)232 fxZoomUpdateWindowAttrib (CompWindow * w,
233 			  WindowPaintAttrib * wAttrib)
234 {
235     ANIM_WINDOW(w);
236 
237     float forwardProgress;
238     float dummy;
239 
240     fxZoomAnimProgress (w, &dummy, &forwardProgress, FALSE);
241 
242     wAttrib->opacity =
243 	(GLushort) (aw->com.storedOpacity * (1 - forwardProgress));
244 }
245 
246 static void
getZoomCenterScaleFull(CompWindow * w,Point * pCurCenter,Point * pCurScale,Point * pWinCenter,Point * pIconCenter,float * pRotateProgress)247 getZoomCenterScaleFull (CompWindow *w,
248 			Point *pCurCenter, Point *pCurScale,
249 			Point *pWinCenter, Point *pIconCenter,
250 			float *pRotateProgress)
251 {
252     ANIM_WINDOW(w);
253 
254     Point winCenter =
255 	{(WIN_X(w) + WIN_W(w) / 2.0),
256 	 (WIN_Y(w) + WIN_H(w) / 2.0)};
257     Point iconCenter =
258 	{aw->com.icon.x + aw->com.icon.width / 2.0,
259 	 aw->com.icon.y + aw->com.icon.height / 2.0};
260     Point winSize =
261 	{WIN_W(w), WIN_H(w)};
262     winSize.x = (winSize.x == 0 ? 1 : winSize.x);
263     winSize.y = (winSize.y == 0 ? 1 : winSize.y);
264 
265     float scaleProgress;
266     float moveProgress;
267     float rotateProgress = 0;
268 
269     if (aw->com.curAnimEffect == AnimEffectSidekick)
270     {
271 	fxZoomAnimProgress (w, &moveProgress, &scaleProgress, FALSE);
272 	rotateProgress = moveProgress;
273     }
274     else if (aw->com.curAnimEffect == AnimEffectZoom)
275     {
276 	fxZoomAnimProgress (w, &moveProgress, &scaleProgress, FALSE);
277     }
278     else
279     {
280 	// other effects use this for minimization
281 	fxZoomAnimProgress (w, &moveProgress, &scaleProgress, TRUE);
282     }
283 
284     Point curCenter =
285 	{(1 - moveProgress) * winCenter.x + moveProgress * iconCenter.x,
286 	 (1 - moveProgress) * winCenter.y + moveProgress * iconCenter.y};
287     Point curScale =
288 	{((1 - scaleProgress) * winSize.x + scaleProgress * aw->com.icon.width) /
289 	 winSize.x,
290 	 ((1 - scaleProgress) * winSize.y + scaleProgress * aw->com.icon.height) /
291 	 winSize.y};
292 
293     // Copy calculated variables
294     if (pCurCenter)
295 	*pCurCenter = curCenter;
296     if (pCurScale)
297 	*pCurScale = curScale;
298     if (pWinCenter)
299 	*pWinCenter = winCenter;
300     if (pIconCenter)
301 	*pIconCenter = iconCenter;
302     if (pRotateProgress)
303 	*pRotateProgress = rotateProgress;
304 }
305 
306 inline void
getZoomCenterScale(CompWindow * w,Point * pCurCenter,Point * pCurScale)307 getZoomCenterScale (CompWindow *w,
308 		    Point *pCurCenter, Point *pCurScale)
309 {
310     getZoomCenterScaleFull (w, pCurCenter, pCurScale, NULL, NULL, NULL);
311 }
312 
313 void
applyZoomTransform(CompWindow * w)314 applyZoomTransform (CompWindow * w)
315 {
316     ANIM_WINDOW(w);
317 
318     CompTransform *transform = &aw->com.transform;
319 
320     Point curCenter;
321     Point curScale;
322     Point winCenter;
323     Point iconCenter;
324     float rotateProgress;
325 
326     getZoomCenterScaleFull (w, &curCenter, &curScale,
327 			    &winCenter, &iconCenter, &rotateProgress);
328 
329     if (fxZoomGetSpringiness (w) == 0.0f &&
330 	(aw->com.curAnimEffect == AnimEffectZoom ||
331 	 aw->com.curAnimEffect == AnimEffectSidekick) &&
332 	(aw->com.curWindowEvent == WindowEventOpen ||
333 	 aw->com.curWindowEvent == WindowEventClose))
334     {
335 	matrixTranslate (transform,
336 			 iconCenter.x, iconCenter.y, 0);
337 	matrixScale (transform, curScale.x, curScale.y, curScale.y);
338 	matrixTranslate (transform,
339 			 -iconCenter.x, -iconCenter.y, 0);
340 
341 	if (aw->com.curAnimEffect == AnimEffectSidekick)
342 	{
343 	    matrixTranslate (transform, winCenter.x, winCenter.y, 0);
344 	    matrixRotate (transform, rotateProgress * 360 * aw->numZoomRotations,
345 			  0.0f, 0.0f, 1.0f);
346 	    matrixTranslate (transform, -winCenter.x, -winCenter.y, 0);
347 	}
348     }
349     else
350     {
351 	matrixTranslate (transform, winCenter.x, winCenter.y, 0);
352 	float tx, ty;
353 	if (aw->com.curAnimEffect != AnimEffectZoom)
354 	{
355 	    // avoid parallelogram look
356 	    float maxScale = MAX(curScale.x, curScale.y);
357 	    matrixScale (transform, maxScale, maxScale, maxScale);
358 	    tx = (curCenter.x - winCenter.x) / maxScale;
359 	    ty = (curCenter.y - winCenter.y) / maxScale;
360 	}
361 	else
362 	{
363 	    matrixScale (transform, curScale.x, curScale.y, curScale.y);
364 	    tx = (curCenter.x - winCenter.x) / curScale.x;
365 	    ty = (curCenter.y - winCenter.y) / curScale.y;
366 	}
367 	matrixTranslate (transform, tx, ty, 0);
368 	if (aw->com.curAnimEffect == AnimEffectSidekick)
369 	{
370 	    matrixRotate (transform, rotateProgress * 360 * aw->numZoomRotations,
371 			  0.0f, 0.0f, 1.0f);
372 	}
373 	matrixTranslate (transform, -winCenter.x, -winCenter.y, 0);
374     }
375 }
376 
377