I was inspired by this to write a graphic snow animation in Java/Processing. It works great, but it has some design issues.The full project is here on github;Mainpackage com.jatkin.snowday;import processing.core.PApplet;import processing.core.PGraphics;/** * Created by Jarrett on 02/04/16. */public class Main extends PApplet {    public static void main(String[] args) {PApplet.main(Main.class.getCanonicalName());}    final int backgroundColor = color(0, 146, 178);    final World world = new World();    /**     * Processing 3 has changed the basic format for starting an app from raw     * java. Size now must be called from `settings`.     */    @Override    public void settings() {        size(500, 300, FX2D);    }    @Override    public void draw() {        fill(255);        noStroke();        background(backgroundColor);        world.tick(sketchWidth(), sketchHeight());        world.render(getGraphics());    }}Worldpackage com.jatkin.snowday;import com.jatkin.snowday.snowflake.FlakeFactory;import com.jatkin.snowday.snowflake.Snowflake;import processing.core.PGraphics;import processing.core.PVector;import java.util.ArrayList;import java.util.List;/** * This class controls all physics for SnowDay. * * Created by Jarrett on 02/04/16. */public class World {    private float gravity;    private List<Snowflake> flurry;    private Wind wind;    private int flakeCountTarget;    public World() { this(2); } // FIXME magic number    public World(int gravity) {        this.gravity = gravity;        flurry = new ArrayList<>();        wind = new Wind();        flakeCountTarget = 200;// FIXME magic number    }    /**     * Advance the snow animation 1 frame     */    public void tick(int screenWidth, int screenHeight) {        wind.tick();        manageSnowflakeCount(screenWidth, screenHeight);        edgeWrapSnowflakes(screenWidth, screenHeight);        updateSnowflakePosition();    }    /**     * Wrap flakes around edged of the screen.     */    private void edgeWrapSnowflakes(int screenWidth, int screenHeight) {        flurry.forEach(snowflake -> {            if (snowflake.getPosition().x > screenWidth)                snowflake.getPosition().x = 0;            else if (snowflake.getPosition().x < 0)                snowflake.getPosition().x = screenWidth;        });    }    /**     * Reset off screen flakes.     * @param screenWidth     * @param screenHeight     */    private void manageSnowflakeCount(int screenWidth, int screenHeight) {        flurry.removeIf(flake -> flake.getPosition().y > screenHeight);        int newFlakeCount = (int)Math.sqrt(flakeCountTarget - flurry.size());        for (int i = 0; i < newFlakeCount; i++)            flurry.add(FlakeFactory.genRandomSnowflake(screenWidth));    }    /**     * Apply wind, gravity and rotation to all the snowflakes     */    private void updateSnowflakePosition() {        for (Snowflake flake : flurry) {            PVector flakePosition = flake.getPosition();            float weight = flake.getWeight();            float downwardsMotion = weight*gravity;            float windMotion = (wind.getSpeed() / weight);            flake.getSpeed().set(windMotion, downwardsMotion);            flakePosition.add(flake.getSpeed());            flake.setRotation(flake.getRotation() + flake.getRotationSpeed());        }    }    /**     * Render all the snowflakes this world has onto the surface     * @param surface     */    public void render(PGraphics surface) {        for (Snowflake flake : flurry) {            flake.render(surface);        }    }    public float getGravity() {        return gravity;    }}Windpackage com.jatkin.snowday;import static java.lang.Math.*;/** * Created by Jarrett on 02/05/16. */public class Wind {    private final static float maxWind = 6;    private final static int maxDurationInMs = 5000;    private final static int minDurationInMs = 2000;    private float targetSpeed = 0;    private float speed = 0;    private long windStopTime = 0;    public void tick() {        long now = System.currentTimeMillis();        if (now > windStopTime)            calculateNewWind();        else            interpolateWindSpeed();    }    private void calculateNewWind() {        float newWindSpeed = (float)sqrt(random()*maxWind*maxWind);        if (random()>0.5) newWindSpeed = -newWindSpeed;        int newDuration = (int) (random() * (maxDurationInMs - minDurationInMs)) + minDurationInMs;        targetSpeed = newWindSpeed;        windStopTime = newDuration + System.currentTimeMillis();    }    private void interpolateWindSpeed() {        float speedDiff = targetSpeed - speed;        float speedChange = (float) (sqrt(abs(speedDiff))/15);        if (speedDiff > 0)            speed += speedChange;        else            speed -= speedChange;    }    public float getSpeed() {        return speed;    }}snowflake.Snowflakepackage com.jatkin.snowday.snowflake;import processing.core.PGraphics;import processing.core.PVector;/** * Created by Jarrett on 02/04/16. */public abstract class Snowflake {    private float weight = 1;    private float rotation = 0;    private float rotationSpeed = 0;    final private PVector position = new PVector();    final private PVector speed = new PVector();    /**     * Render this flake on the surface     * @param surface     */    abstract public void render(PGraphics surface);    public float getRotationSpeed() {        return rotationSpeed;    }    public void setRotationSpeed(float rotationSpeed) {        this.rotationSpeed = rotationSpeed;    }    public PVector getPosition() {        return position;    }    public float getWeight() {        return weight;    }    public void setWeight(float weight) {        this.weight = weight;    }    public PVector getSpeed() {        return speed;    }    public float getRotation() {        return rotation;    }    public void setRotation(float rotation) {        this.rotation = rotation;    }}snowflake.SquareSnowflakepackage com.jatkin.snowday.snowflake;import processing.core.PConstants;import processing.core.PGraphics;/** * Created by Jarrett on 02/04/16. */public class SquareSnowflake extends Snowflake {    private final float size = 13;    @Override    public void render(PGraphics surface) {        surface.pushMatrix();        surface.translate(getPosition().x, getPosition().y - size/2);        surface.rotate(getRotation());        surface.rectMode(PConstants.CENTER);        surface.rect(0, 0, size, size);        surface.popMatrix();    }}snowflake.FlakeFactorypackage com.jatkin.snowday.snowflake;/** * Created by Jarrett on 02/05/16. */public class FlakeFactory {    public static Snowflake genRandomSnowflake(int maxXLoc) {        Snowflake flake = new SquareSnowflake();        flake.setWeight((float)(Math.random()/2)+1);      // FIXME: magic numbers        flake.setRotationSpeed((float)(Math.random()*Math.PI/18d));        flake.getPosition().x = (int)(Math.random()*maxXLoc);        return flake;    }}The questions I have are ordered in greatest to least importance to me.My main issue with this code is the magic numbers. Magic numbers are everywhere. I was able to sensibly move some of the numbers to private static final fields, but some of the rest don't seem to belong anywhere. For example FlakeFactory#genRandomSnowflake:public static Snowflake genRandomSnowflake(int maxXLoc) {    Snowflake flake = new SquareSnowflake();    flake.setWeight((float)(Math.random() / 2 ) + 1);    flake.setRotationSpeed((float)(Math.random() * Math.PI / 18d));    flake.getPosition().x = (int)(Math.random() * maxXLoc);    return flake;}Many magic numbers exist here, mostly to keep the animation rate sane.Other than that, how does my overall design look? I encapsulated nearly all the physics code in World. Is it a good or bad idea?I think most of the code is reasonably split up into useful, descriptive methods with some documentation where I thought it was required. How clean is the overall code inside the methods? Documentation quality?Keep in mind that I am completely self taught.Installing processingProcessing isn't on Maven central, so to install it you need to download it.Go to https://processing.org/download/ and download version 3.0.1Unpack the download and locate the \core\library folderAdd the entire folder to your classpath
Java snow animation
java;animation;physics;processing
That looks really good! I just have a few improvements, nothing big.Wind.interplorateWindSpeed()A math function called signum could be used instead of if statements.float speedDiff = targetSpeed - speed;float speedChange = (float) (sqrt(abs(speedDiff))/15);speed += speedChange * signum(speedDiff);WindYou might consider moving all the private floats values into a constructor, just for consistency.ThoughtsReally these are just small tweaks, you have some very well-written and documented code! The next thing to do would be to make more types of snowflakes (hexagons, stars, textures) for variety.