CS 145 Lecture 24 – Animation
Dear students,
Let’s start with a Program This!
Write a methodtimes
with the following behavior:
times('!', 3)
yields"!!!"
times('#', 6)
yields"######"
times('-', 13)
yields"-------------"
We’ll use this method to generate a random spelunking workout.
After that, we’ll generate a few more images. This time we won’t use loops to march through every pixel. Instead we’ll loop through points on a line or timesteps in an animation! Let’s solve these problems:
- draw a line between two points
- bounce a ball
- rotate and scale a square
See you next class!
Sincerely,
P.S. It’s Haiku Friday!
They’re little windows
Shining past light, like Grandma’s
I wish pixels smelled
P.P.S. Here’s the code we wrote together…
RandomSpelunking.java
package lecture1104; import java.util.Random; public class RandomSpelunking { public static void main(String[] args) { Random generator = new Random(); int nLevels = getNumberOfLevels(); for (int i = 0; i < nLevels; ++i) { System.out.println(times('-', generator.nextInt(7) + 4)); } } public static int getNumberOfLevels() { System.out.println("Hey, we're callin' this method!"); return new Random().nextInt(20); } public static String times(char c, int n) { String s = ""; for (int i = 0; i < n; ++i) { s += c; } return s; } }
GifSequenceWriter.java
package lecture1104; // GifSequenceWriter.java // // Created by Elliot Kroo on 2009-04-25. // // This work is licensed under the Creative Commons Attribution 3.0 Unported // License. To view a copy of this license, visit // http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative // Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. import javax.imageio.*; import javax.imageio.metadata.*; import javax.imageio.stream.*; import java.awt.image.*; import java.io.*; import java.util.Iterator; public class GifSequenceWriter { protected ImageWriter gifWriter; protected ImageWriteParam imageWriteParam; protected IIOMetadata imageMetaData; protected ImageOutputStream outputStream; /** * Creates a new GifSequenceWriter * * @param outputStream * the ImageOutputStream to be written to * @param imageType * one of the imageTypes specified in BufferedImage * @param timeBetweenFramesMS * the time between frames in miliseconds * @param loopContinuously * wether the gif should loop repeatedly * @throws IIOException * if no gif ImageWriters are found * * @author Elliot Kroo (elliot[at]kroo[dot]net) */ public GifSequenceWriter(File outFile, int timeBetweenFramesMS, boolean loopContinuously) { try { // my method to create a writer gifWriter = getWriter(); imageWriteParam = gifWriter.getDefaultWriteParam(); ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam); String metaFormatName = imageMetaData.getNativeMetadataFormatName(); IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName); IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension"); graphicsControlExtensionNode.setAttribute("disposalMethod", "none"); graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE"); graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE"); graphicsControlExtensionNode.setAttribute("delayTime", Integer.toString(timeBetweenFramesMS / 10)); graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0"); IIOMetadataNode commentsNode = getNode(root, "CommentExtensions"); commentsNode.setAttribute("CommentExtension", "Created by MAH"); IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions"); IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension"); child.setAttribute("applicationID", "NETSCAPE"); child.setAttribute("authenticationCode", "2.0"); int loop = loopContinuously ? 0 : 1; child.setUserObject(new byte[] { 0x1, (byte) (loop & 0xFF), (byte) ((loop >> 8) & 0xFF) }); appEntensionsNode.appendChild(child); imageMetaData.setFromTree(metaFormatName, root); outputStream = new FileImageOutputStream(outFile); gifWriter.setOutput(outputStream); gifWriter.prepareWriteSequence(null); } catch (IOException e) { throw new RuntimeException(e); } } public void appendFrame(BufferedImage img) { try { gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam); } catch (IOException e) { throw new RuntimeException(e); } } /** * Close this GifSequenceWriter object. This does not close the underlying * stream, just finishes off the GIF. */ public void close() { try { gifWriter.endWriteSequence(); outputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } } /** * Returns the first available GIF ImageWriter using * ImageIO.getImageWritersBySuffix("gif"). * * @return a GIF ImageWriter object * @throws IIOException * if no GIF image writers are returned */ private static ImageWriter getWriter() throws IIOException { try { Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif"); if (!iter.hasNext()) { throw new IIOException("No GIF Image Writers Exist"); } else { return iter.next(); } } catch (IOException e) { throw new RuntimeException(e); } } /** * Returns an existing child node, or creates and returns a new child node (if * the requested node does not exist). * * @param rootNode * the <tt>IIOMetadataNode</tt> to search for the child node. * @param nodeName * the name of the child node. * * @return the child node, if found or a new node created with the given name. */ private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName) { int nNodes = rootNode.getLength(); for (int i = 0; i < nNodes; i++) { if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0) { return ((IIOMetadataNode) rootNode.item(i)); } } IIOMetadataNode node = new IIOMetadataNode(nodeName); rootNode.appendChild(node); return node; } }
Animation.java
package lecture1104; import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.File; public class Animation { public static void main(String[] args) { File outputFile = new File("/Users/johnch/Desktop/ball.gif"); GifSequenceWriter gif = new GifSequenceWriter(outputFile, 100, false); BufferedImage image = new BufferedImage(512, 512, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); double x = 0; double y = 0; double delta = 0.1; double velocityY = 31; double velocityX = 12; double acceleration = 2 * 9.81; for (double t = 0.0; t < 30; t += delta) { g.setColor(Color.BLUE); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setColor(Color.YELLOW); g.fillOval((int) x, (int) y, 100, 100); if (y > 410) { velocityY *= -0.9; } x += velocityX * delta; y += velocityY * delta; velocityY += acceleration; gif.appendFrame(image); } gif.close(); } }