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 void
fxMagicLampInitGrid(CompWindow * w,int * gridWidth,int * gridHeight)40 fxMagicLampInitGrid (CompWindow *w,
41 int *gridWidth, int *gridHeight)
42 {
43 *gridWidth = 2;
44 *gridHeight = animGetI (w, ANIM_SCREEN_OPTION_MAGIC_LAMP_GRID_RES);
45 }
46
47 void
fxVacuumInitGrid(CompWindow * w,int * gridWidth,int * gridHeight)48 fxVacuumInitGrid (CompWindow *w,
49 int *gridWidth, int *gridHeight)
50 {
51 *gridWidth = 2;
52 *gridHeight = animGetI (w, ANIM_SCREEN_OPTION_VACUUM_GRID_RES);
53 }
54
55 Bool
fxMagicLampInit(CompWindow * w)56 fxMagicLampInit (CompWindow * w)
57 {
58 ANIM_WINDOW(w);
59
60 XRectangle *icon = &aw->com.icon;
61 int screenHeight = w->screen->height;
62 aw->minimizeToTop = (WIN_Y(w) + WIN_H(w) / 2) >
63 (icon->y + icon->height / 2);
64 int maxWaves;
65 float waveAmpMin, waveAmpMax;
66 float distance;
67
68 if (aw->com.curAnimEffect == AnimEffectMagicLamp)
69 {
70 maxWaves = animGetI (w, ANIM_SCREEN_OPTION_MAGIC_LAMP_MAX_WAVES);
71 waveAmpMin =
72 animGetF (w, ANIM_SCREEN_OPTION_MAGIC_LAMP_WAVE_AMP_MIN);
73 waveAmpMax =
74 animGetF (w, ANIM_SCREEN_OPTION_MAGIC_LAMP_WAVE_AMP_MAX);
75 }
76 else
77 {
78 maxWaves = 0;
79 waveAmpMin = 0;
80 waveAmpMax = 0;
81 }
82 if (waveAmpMax < waveAmpMin)
83 waveAmpMax = waveAmpMin;
84
85 if (maxWaves == 0)
86 {
87 aw->magicLampWaveCount = 0;
88 return TRUE;
89 }
90
91 // Initialize waves
92
93 if (aw->minimizeToTop)
94 distance = WIN_Y(w) + WIN_H(w) - icon->y;
95 else
96 distance = icon->y - WIN_Y(w);
97
98 aw->magicLampWaveCount =
99 1 + (float)maxWaves *distance / screenHeight;
100
101 if (!(aw->magicLampWaves))
102 {
103 aw->magicLampWaves =
104 calloc(aw->magicLampWaveCount, sizeof(WaveParam));
105 if (!aw->magicLampWaves)
106 {
107 compLogMessage ("animation", CompLogLevelError,
108 "Not enough memory");
109 return FALSE;
110 }
111 }
112 // Compute wave parameters
113
114 int ampDirection = (RAND_FLOAT() < 0.5 ? 1 : -1);
115 int i;
116 float minHalfWidth = 0.22f;
117 float maxHalfWidth = 0.38f;
118
119 for (i = 0; i < aw->magicLampWaveCount; i++)
120 {
121 aw->magicLampWaves[i].amp =
122 ampDirection * (waveAmpMax - waveAmpMin) *
123 rand() / RAND_MAX + ampDirection * waveAmpMin;
124 aw->magicLampWaves[i].halfWidth =
125 RAND_FLOAT() * (maxHalfWidth -
126 minHalfWidth) + minHalfWidth;
127
128 // avoid offset at top and bottom part by added waves
129 float availPos = 1 - 2 * aw->magicLampWaves[i].halfWidth;
130 float posInAvailSegment = 0;
131
132 if (i > 0)
133 posInAvailSegment =
134 (availPos / aw->magicLampWaveCount) * rand() / RAND_MAX;
135
136 aw->magicLampWaves[i].pos =
137 (posInAvailSegment +
138 i * availPos / aw->magicLampWaveCount +
139 aw->magicLampWaves[i].halfWidth);
140
141 // switch wave direction
142 ampDirection *= -1;
143 }
144
145 return TRUE;
146 }
147
148 void
fxMagicLampModelStep(CompWindow * w,float time)149 fxMagicLampModelStep (CompWindow *w, float time)
150 {
151 defaultAnimStep (w, time);
152
153 ANIM_WINDOW(w);
154
155 Model *model = aw->com.model;
156 XRectangle *icon = &aw->com.icon;
157
158 if ((aw->com.curWindowEvent == WindowEventOpen ||
159 aw->com.curWindowEvent == WindowEventClose) &&
160 ((aw->com.curAnimEffect == AnimEffectMagicLamp &&
161 animGetB (w, ANIM_SCREEN_OPTION_MAGIC_LAMP_MOVING_END)) ||
162 (aw->com.curAnimEffect == AnimEffectVacuum &&
163 animGetB (w, ANIM_SCREEN_OPTION_VACUUM_MOVING_END))))
164 {
165 // Update icon position
166 getMousePointerXY (w->screen, &icon->x, &icon->y);
167 }
168 float forwardProgress = defaultAnimProgress (w);
169
170 if (aw->magicLampWaveCount > 0 && !aw->magicLampWaves)
171 return;
172
173 float iconCloseEndY;
174 float iconFarEndY;
175 float winFarEndY;
176 float winVisibleCloseEndY;
177
178 float iconShadowLeft =
179 ((float)(w->output.left - w->input.left)) *
180 icon->width / w->width;
181 float iconShadowRight =
182 ((float)(w->output.right - w->input.right)) *
183 icon->width / w->width;
184
185 float sigmoid0 = sigmoid(0);
186 float sigmoid1 = sigmoid(1);
187 float winw = WIN_W(w);
188 float winh = WIN_H(w);
189
190 if (aw->minimizeToTop)
191 {
192 iconFarEndY = icon->y;
193 iconCloseEndY = icon->y + icon->height;
194 winFarEndY = WIN_Y(w) + winh;
195 winVisibleCloseEndY = WIN_Y(w);
196 if (winVisibleCloseEndY < iconCloseEndY)
197 winVisibleCloseEndY = iconCloseEndY;
198 }
199 else
200 {
201 iconFarEndY = icon->y + icon->height;
202 iconCloseEndY = icon->y;
203 winFarEndY = WIN_Y(w);
204 winVisibleCloseEndY = WIN_Y(w) + winh;
205 if (winVisibleCloseEndY > iconCloseEndY)
206 winVisibleCloseEndY = iconCloseEndY;
207 }
208
209 float preShapePhaseEnd = 0.22f;
210 float preShapeProgress = 0;
211 float postStretchProgress = 0;
212 float stretchProgress = 0;
213 float stretchPhaseEnd =
214 preShapePhaseEnd + (1 - preShapePhaseEnd) *
215 (iconCloseEndY -
216 winVisibleCloseEndY) / ((iconCloseEndY - winFarEndY) +
217 (iconCloseEndY - winVisibleCloseEndY));
218 if (stretchPhaseEnd < preShapePhaseEnd + 0.1)
219 stretchPhaseEnd = preShapePhaseEnd + 0.1;
220
221 if (forwardProgress < preShapePhaseEnd)
222 {
223 preShapeProgress = forwardProgress / preShapePhaseEnd;
224
225 // Slow down "shaping" toward the end
226 preShapeProgress = 1 - decelerateProgress(1 - preShapeProgress);
227 }
228
229 if (forwardProgress < preShapePhaseEnd)
230 {
231 stretchProgress = forwardProgress / stretchPhaseEnd;
232 }
233 else
234 {
235 if (forwardProgress < stretchPhaseEnd)
236 {
237 stretchProgress = forwardProgress / stretchPhaseEnd;
238 }
239 else
240 {
241 postStretchProgress =
242 (forwardProgress - stretchPhaseEnd) / (1 - stretchPhaseEnd);
243 }
244 }
245
246 Object *object = model->objects;
247 int i;
248 for (i = 0; i < model->numObjects; i++, object++)
249 {
250 float origx = w->attrib.x + (winw * object->gridPosition.x -
251 w->output.left) * model->scale.x;
252 float origy = w->attrib.y + (winh * object->gridPosition.y -
253 w->output.top) * model->scale.y;
254
255 float iconx =
256 (icon->x - iconShadowLeft) +
257 (icon->width + iconShadowLeft + iconShadowRight) *
258 object->gridPosition.x;
259 float icony = icon->y + icon->height * object->gridPosition.y;
260
261 float stretchedPos;
262 if (aw->minimizeToTop)
263 stretchedPos =
264 object->gridPosition.y * origy +
265 (1 - object->gridPosition.y) * icony;
266 else
267 stretchedPos =
268 (1 - object->gridPosition.y) * origy +
269 object->gridPosition.y * icony;
270
271 // Compute current y position
272 if (forwardProgress < preShapePhaseEnd)
273 {
274 object->position.y =
275 (1 - stretchProgress) * origy +
276 stretchProgress * stretchedPos;
277 }
278 else
279 {
280 if (forwardProgress < stretchPhaseEnd)
281 {
282 object->position.y =
283 (1 - stretchProgress) * origy +
284 stretchProgress * stretchedPos;
285 }
286 else
287 {
288 object->position.y =
289 (1 - postStretchProgress) *
290 stretchedPos +
291 postStretchProgress *
292 (stretchedPos + (iconCloseEndY - winFarEndY));
293 }
294 }
295
296 // Compute "target shape" x position
297 float fx = ((iconCloseEndY - object->position.y) /
298 (iconCloseEndY - winFarEndY));
299 float fy = ((sigmoid(fx) - sigmoid0) /
300 (sigmoid1 - sigmoid0));
301 float targetx = fy * (origx - iconx) + iconx;
302
303 // Apply waves
304 int i;
305 for (i = 0; i < aw->magicLampWaveCount; i++)
306 {
307 float cosfx = ((fx - aw->magicLampWaves[i].pos) /
308 aw->magicLampWaves[i].halfWidth);
309 if (cosfx < -1 || cosfx > 1)
310 continue;
311 targetx +=
312 aw->magicLampWaves[i].amp * model->scale.x *
313 (cos(cosfx * M_PI) + 1) / 2;
314 }
315
316 // Compute current x position
317 if (forwardProgress < preShapePhaseEnd)
318 object->position.x =
319 (1 - preShapeProgress) * origx + preShapeProgress * targetx;
320 else
321 object->position.x = targetx;
322
323 if (aw->minimizeToTop)
324 {
325 if (object->position.y < iconFarEndY)
326 object->position.y = iconFarEndY;
327 }
328 else
329 {
330 if (object->position.y > iconFarEndY)
331 object->position.y = iconFarEndY;
332 }
333
334 // No need to set object->position.z to 0, since they won't be used
335 // due to modelAnimIs3D being FALSE for magic lamp.
336 }
337 }
338
339