source: trunk/src/org/expeditee/gui/FrameTransition.java@ 1460

Last change on this file since 1460 was 1144, checked in by bln4, 6 years ago

Used Eclipse refactoring to encapsulate Point.X and Point.Y

File size: 11.0 KB
Line 
1package org.expeditee.gui;
2
3import java.lang.reflect.InvocationTargetException;
4import java.lang.reflect.Method;
5
6import org.expeditee.core.Colour;
7import org.expeditee.core.Dimension;
8import org.expeditee.core.Fill;
9import org.expeditee.core.GradientFill;
10import org.expeditee.core.Image;
11import org.expeditee.core.Point;
12import org.expeditee.core.bounds.AxisAlignedBoxBounds;
13import org.expeditee.gio.EcosystemManager;
14import org.expeditee.gio.GraphicsManager;
15import org.expeditee.items.Item;
16
17/**
18 * Manages slide animations between frames
19 * Reflection is used to call these methods, therefore any additions should only need to be added in this class.
20 * No further changes to other code should be required.
21 *
22 * TODO: Rework above comment. cts16
23 *
24 * @author cts16
25 */
26public class FrameTransition
27{
28 /** The parameters for transition methods. */
29 private static final Class<?>[] REFLECTION_PARAMS = { Image.class, AxisAlignedBoxBounds.class };
30
31 /** The height of the drop-shadow below transition frames. */
32 private static final int SHADOW_HEIGHT = 16;
33
34 /** The colour to use for shadows. */
35 private static final Colour SHADOW_COLOUR = Colour.FromRGBA255(0, 0, 0, 64);
36
37 /** The default duration for transitions (in milliseconds). */
38 public static final long DEFAULT_TRANSITION_DURATION = 500;
39
40 /** Stores the image of the frame being transitioned away from. */
41 private Image _fromFrameImage = null;
42
43 /** Which transition method we are currently using. */
44 private Method _method;
45
46 /** How long the current transition is (in milliseconds). */
47 private Long _duration = null;
48
49 /** How far through the transition we are (in milliseconds). */
50 private long _completed = 0;
51
52 /** When we last updated the transition (as returned by System.currentTimeMillis()). */
53 private long _lastUpdateTime = 0;
54
55 /** Standard constructor. */
56 public FrameTransition(Image fromFrameImage, String method)
57 {
58 _fromFrameImage = fromFrameImage;
59 _method = reflectMethod(method);
60 }
61
62 /** Whether the transition has been constructed properly. */
63 public boolean isValid()
64 {
65 return _fromFrameImage != null && _method != null;
66 }
67
68 /** Sets the timing data for this transition to their initial default values. */
69 private void initialiseTiming()
70 {
71 initialiseTiming(DEFAULT_TRANSITION_DURATION);
72 }
73
74 /** Sets the timing data for this transition to their initial values. */
75 private void initialiseTiming(long duration)
76 {
77 // Check if already initialised
78 if (_duration != null || duration < 0) return;
79
80 _duration = duration;
81 _completed = 0;
82 _lastUpdateTime = System.currentTimeMillis();
83 }
84
85 /**
86 * Updates the timing data for the transition and returns the elapsed
87 * time since the last update.
88 */
89 private long updateTiming()
90 {
91 // No need to update if we've finished
92 if (isCompleted()) return 0;
93
94 long currentTime = System.currentTimeMillis();
95 long deltaTime = currentTime - _lastUpdateTime;
96 _completed += deltaTime;
97 if (_completed > _duration) {
98 long overshoot = _completed - _duration;
99 _completed -= overshoot;
100 currentTime -= overshoot;
101 deltaTime -= overshoot;
102 }
103 _lastUpdateTime = currentTime;
104 return deltaTime;
105 }
106
107 /** Gets the percentage of the transition that has elapsed. */
108 private double getPercentageComplete()
109 {
110 if (_duration == 0.0) return 1.0;
111
112 return ((double) _completed) / _duration;
113 }
114
115 public boolean isCompleted()
116 {
117 return _completed == _duration;
118 }
119
120 /** Gets the transition method that corresponds to the given string. */
121 private Method reflectMethod(String method)
122 {
123 try {
124 return this.getClass().getDeclaredMethod(method, REFLECTION_PARAMS);
125 } catch (NoSuchMethodException | SecurityException e) {
126 return null;
127 }
128 }
129
130 /** Draws the selected transition. Returns whether the transition draw completed successfully. */
131 public boolean drawTransition(Image toFrameImage, AxisAlignedBoxBounds area)
132 {
133 // No need to draw if not a valid transition
134 if (!isValid()) return false;
135
136 // Invoke the drawing method
137 try {
138 _method.invoke(this, toFrameImage, area);
139 return true;
140 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
141 System.err.println("An Reflection Exception occurred trying to invoke '" + _method.getName() + "'");
142 e.printStackTrace();
143 return false;
144 }
145 }
146
147 /*
148 *
149 * Transition methods.
150 *
151 */
152
153 public void template(Image toImage, AxisAlignedBoxBounds area)
154 {
155 initialiseTiming();
156 updateTiming();
157
158 GraphicsManager g = EcosystemManager.getGraphicsManager();
159 }
160
161 /** Slides new frame over current from from right to left. */
162 public void CoverFromRight(Image toImage, AxisAlignedBoxBounds area)
163 {
164 initialiseTiming();
165 updateTiming();
166
167 int frameX = area.getMinX() + (int) (area.getWidth() * (1.0 - getPercentageComplete()));
168 Point topLeft = new Point(frameX, 0);
169 Dimension size = area.getSize();
170
171 EcosystemManager.getGraphicsManager().drawRectangle(topLeft, size, 0.0, new Fill(Item.DEFAULT_BACKGROUND), null, null, null);
172 EcosystemManager.getGraphicsManager().drawImage(toImage, topLeft, size);
173 }
174
175 /** Slides new frame over current from from left to right. */
176 public void CoverFromLeft(Image toImage, AxisAlignedBoxBounds area)
177 {
178 initialiseTiming();
179 updateTiming();
180
181 int frameX = area.getMinX() - (int) (area.getWidth() * (1.0 - getPercentageComplete()));
182 Point topLeft = new Point(frameX, 0);
183 Dimension size = area.getSize();
184
185 EcosystemManager.getGraphicsManager().drawRectangle(topLeft, size, 0.0, new Fill(Item.DEFAULT_BACKGROUND), null, null, null);
186 EcosystemManager.getGraphicsManager().drawImage(toImage, topLeft, size);
187 }
188
189 /** Another name for CoverFromRight. */
190 public void Swipe(Image toImage, AxisAlignedBoxBounds area)
191 {
192 CoverFromRight(toImage, area);
193 }
194
195 /** Expands the new frame over the current one from the left. */
196 public void WipeFromLeft(Image toImage, AxisAlignedBoxBounds area)
197 {
198 initialiseTiming();
199 updateTiming();
200
201 int cropWidth = (int) (toImage.getWidth() * getPercentageComplete());
202 Dimension cropSize = new Dimension(cropWidth, toImage.getHeight());
203 int drawWidth = (int) (area.getWidth() * getPercentageComplete());
204 Dimension drawSize = new Dimension(drawWidth, area.getHeight());
205
206 if (cropWidth == 0 || drawWidth == 0) return;
207
208 EcosystemManager.getGraphicsManager().drawRectangle(area.getTopLeft(), drawSize, 0.0, new Fill(Item.DEFAULT_BACKGROUND), null, null, null);
209 EcosystemManager.getGraphicsManager().drawImage(toImage, area.getTopLeft(), drawSize, 0.0, Point.ORIGIN, cropSize);
210 }
211
212 /** Expands the new frame over the current one from the right. */
213 public void WipeFromRight(Image toImage, AxisAlignedBoxBounds area)
214 {
215 initialiseTiming();
216 updateTiming();
217
218 int cropWidth = (int) (toImage.getWidth() * getPercentageComplete());
219 Dimension cropSize = new Dimension(cropWidth, toImage.getHeight());
220 int drawWidth = (int) (area.getWidth() * getPercentageComplete());
221 Dimension drawSize = new Dimension(drawWidth, area.getHeight());
222 int cropX = toImage.getWidth() - cropWidth;
223 Point cropTopLeft = new Point(cropX, 0);
224 int drawX = area.getWidth() - drawWidth;
225 Point drawTopLeft = area.getTopLeft().clone().add(drawX, 0);
226
227 if (cropWidth == 0 || drawWidth == 0) return;
228
229 EcosystemManager.getGraphicsManager().drawRectangle(drawTopLeft, drawSize, 0.0, new Fill(Item.DEFAULT_BACKGROUND), null, null, null);
230 EcosystemManager.getGraphicsManager().drawImage(toImage, drawTopLeft, drawSize, 0.0, cropTopLeft, cropSize);
231 }
232
233 /**
234 * Drops new frame from top of display. Draws rectangle with gradient
235 * effect to create illusion of drop shadow.
236 */
237 public void FallDown(Image toImage, AxisAlignedBoxBounds area)
238 {
239 initialiseTiming();
240 updateTiming();
241
242 int y = area.getMinY() - (int) (area.getHeight() * (1.0 - getPercentageComplete()));
243 Point drawTopLeft = new Point(area.getMinX(), y);
244 Point shadowTopLeft = drawTopLeft.clone().add(0, area.getHeight());
245 Dimension shadowSize = new Dimension(area.getWidth(), SHADOW_HEIGHT);
246 GradientFill shadowFill = new GradientFill(SHADOW_COLOUR, new Point(0, shadowTopLeft.getY()), Colour.TRANSPARENT, new Point(0, shadowTopLeft.getY() + SHADOW_HEIGHT));
247
248 EcosystemManager.getGraphicsManager().drawRectangle(drawTopLeft, area.getSize(), 0.0, new Fill(Item.DEFAULT_BACKGROUND), null, null, null);
249 EcosystemManager.getGraphicsManager().drawRectangle(shadowTopLeft, shadowSize, 0.0, shadowFill, null, null, null);
250 EcosystemManager.getGraphicsManager().drawImage(toImage, drawTopLeft, area.getSize());
251 }
252
253 /** Fades out to the given colour and then fades in the new frame. */
254 public void FadeIn(Image toImage, AxisAlignedBoxBounds area, Colour colour)
255 {
256 initialiseTiming();
257 updateTiming();
258
259 GraphicsManager g = EcosystemManager.getGraphicsManager();
260
261 double progress = getPercentageComplete();
262
263 // Start by fading the overlay colour in
264 if (progress < 0.5) {
265 float alpha = (float) (progress * 2);
266
267 g.drawRectangle(area.getTopLeft(), area.getSize(), 0.0, new Fill(Item.DEFAULT_BACKGROUND), null, null, null);
268 g.drawImage(_fromFrameImage, area.getTopLeft(), area.getSize());
269 g.setCompositeAlpha(alpha);
270 g.drawRectangle(area.getTopLeft(), area.getSize(), 0.0, new Fill(colour), null, null, null);
271
272 // Then fade the overlay out to reveal the new frame
273 } else {
274 float alpha = 1.0f - (float) ((progress - 0.5) * 2);
275
276 g.drawRectangle(area.getTopLeft(), area.getSize(), 0.0, new Fill(Item.DEFAULT_BACKGROUND), null, null, null);
277 g.drawImage(toImage, area.getTopLeft(), area.getSize());
278 g.setCompositeAlpha(alpha);
279 g.drawRectangle(area.getTopLeft(), area.getSize(), 0.0, new Fill(colour), null, null, null);
280
281 }
282 }
283
284 /** Fades out to black and then fades in the new frame. */
285 public void FadeInBlack(Image toImage, AxisAlignedBoxBounds area)
286 {
287 FadeIn(toImage, area, Colour.BLACK);
288 }
289
290 /** Fades out to white and then fades in the new frame. */
291 public void FadeInWhite(Image toImage, AxisAlignedBoxBounds area)
292 {
293 FadeIn(toImage, area, Colour.WHITE);
294 }
295
296 /** Fades the current frame out while the new frame fades in overtop. */
297 public void CrossFade(Image toImage, AxisAlignedBoxBounds area)
298 {
299 // Based on: http://stackoverflow.com/questions/20346661/java-fade-in-and-out-of-images
300 // TODO: make use of timer, as this example does, rather than fixed delay
301
302 initialiseTiming();
303 updateTiming();
304
305 GraphicsManager g = EcosystemManager.getGraphicsManager();
306
307 float fadeInAlpha = (float) getPercentageComplete();
308 float fadeOutAlpha = 1.0f - fadeInAlpha;
309
310 // Clear the screen
311 g.clear(Item.DEFAULT_BACKGROUND);
312
313 // Draw the current frame with decreasing alpha
314 g.setCompositeAlpha(fadeOutAlpha);
315 g.drawImage(_fromFrameImage, area.getTopLeft(), area.getSize());
316
317 // Draw the in-image with increasing alpha
318 g.setCompositeAlpha(fadeInAlpha);
319 g.drawImage(toImage, area.getTopLeft(), area.getSize());
320 }
321}
Note: See TracBrowser for help on using the repository browser.