Friday, November 14, 2014

Orbital Simulation

Alright, let's program up some gravity!

First we'll make two objects.  The first object ({}) is velocity which will have its own x and y variables that will be applied to the particle object's x and y variables.

Since we are creating an array of this object that will have random numbers, we loop through each item and assign it a base value.  Then loop again to assign it the random values to place particles all over the screen.

Now that we're set up, we start the timer function and apply force to each particle by calculating the force, multiplying it by the velocity, and adding it to the particle coordinate.

Our center of gravity is stationary so we didn't have to really set anything up for that but setting its coordinates as a variable means that we're doing six less calculations every time the screen refreshes.  This is a very memory intensive calculation and easily gets out of control when you increase the particleIndex so anything you can do to increase efficiency is helpful in making sure the animation is smooth and ensuring that it can be rendered with older hardware.





function drawShapes() {

var particleIndex = 1, particle = {}, velocity = {}, gravityx = w / 2, gravityy = h / 2; for (z = 0; z < particleIndex; z++) { particle[z] = { x: Math.floor(Math.random() * w), y: Math.floor(Math.random() * h), color: randomColor() }; velocity[z] = { x: Math.floor((Math.random() * 5) - 5), y: Math.floor((Math.random() * 5) - 5) }; }

setInterval(function() {


clearScreen();

for (z = 0; z < particleIndex; z++) {

var distance = Math.pow((particle[z].x - gravityx), 2) + Math.pow((particle[z].y - gravityy), 2),
f = (1 / Math.sqrt(distance));

velocity[z].x += (particle[z].x - gravityx) * f;
velocity[z].y += (particle[z].y - gravityy) * f;

particle[z].x -= velocity[z].x;
particle[z].y -= velocity[z].y;

drawCircle(gravityx, gravityy, 60, "black");
drawCircle(particle[z].x, particle[z].y, 10, particle[z].color);
}

}, 50);

}

Thursday, August 7, 2014

This Is Kind of Psychadelic

I get a lot of traffic on this blog but no comments or emails.  Feedback of any kind is welcome!  And just a reminder if you haven't seen the first post, this can all be done in the browser-based environment found here.  It's a template I have set up for the code snippets highlighted near the bottom of posts.

In the function SwirlColor we use a "shortcut", the ternary operation, for something we type out a lot, the if/else statement.  Each of the following produce the same result:

1)
if      (Math.Random() > 5)  {colR     += -1;}                        
test?       -         operation       -        var    -    change depending on result
else  {colR    += 1;}                                                              
test? -  var    -   change depending on result                                               

2)                                                                       result is -  true  :   false        
colR    +=          Math.Random() > 5     ?                            -1    :    1;          
var    -   change       -           operation       -       test?    -    change depending on operation 

This isn't applicable to a lot of situations but is a fun concept to learn.  A great place to use it is on a rotating, rectangular device where we want to have a variable to be a fraction of either the height or width, depending on orientation so that the output is the same regardless of orientation.
(ex    C_C = widthInt < heightInt ? widthInt / 10 : heightInt / 10; )

New variables in this design:
  • colR/colG/colB are holders for the rgb values (ex. rgb(100,0,255) = colR,colG,colB)
  • dcMin/Max is the change(d) in color(c) Min and Max between each layer
  • SwirlRadius is each individual circle we see, starting with the largest and layering smaller circles on top in the do loop
  • SwitchColor is used to determine if we're changing R, G, or B.  It changes at the beginning of SwirlColor()






function drawShapes() {

    c.translate(w / 2, h / 2);

    speed = 500;

    swirlRadius = h / 2;
    switchColor = 1;
    i = 1;

dcMin = 1;
dcMax = 3;

    colR = 155;
    colG = 155;
    colB = 5;

    setInterval(function() {

        do {

            swirlColor();

            c.beginPath();
            c.fillStyle = "rgb(" + colR + "," + colG + "," + colB + ")";
            c.arc(0, 0, swirlRadius, 0, Math.PI * 2, true);
            c.fill();

            swirlRadius -= Math.floor((Math.random() * 10) + 5);

        } while (swirlRadius > 10);

        i++;
        swirlRadius = h / 2;

    }, speed);

}

function swirlColor() {

    if (i > 10) {
        i = 1;
        switchColor = Math.floor((Math.random() * 2) + 1);
    }

    switch (switchColor) {
    case 1:
        colR += Math.random() > 0.5 ? dcMin : dcMax;
        if (colR > 255) colR = 1;
        if (colR < 0) colR = 255;
        break;
    case 2:
        colG += Math.random() > 0.5 ? dcMin : dcMax;
        if (colG > 255) colG = 1;
        if (colG < 0) colG = 255;
        break;
    case 3:
        colB += Math.random() > 0.5 ? dcMin : dcMax;
        if (colB > 255) colB = 1;
        if (colB < 0) colB = 255;
        break;
    }

}

Wednesday, May 28, 2014

Fractal Tree

We have two new concepts to cover in creating a fractal.

First - recursion.  We'll make a function which we enter, it calls itself, then it has a way to exit itself.  branch(length) does just that.  We start out with a length of 150 (the base of the tree) and decrease 'length' by 1/3 until it reaches 10, then we exit the recursive loop.

Next to cover is save() and restore().  We save() the state of the canvas (translation, rotation, values of stroke/fill, etc.), rotate the canvas, branch out, restore() to the original canvas setup, then do the same for the other side.





function drawShapes() {

    c.translate(w / 2, h);

theta=56, i=0;

branch(150);

}

function branch(length) {

c.moveTo(0,0);
c.lineTo(0,-length);
c.stroke();
    c.translate(0, -length);

    length *= 0.66;

    if (length > 10) {

        c.save();
        c.rotate(theta);
        branch(length);
        c.restore();

        c.save();
        c.rotate(-theta);
        branch(length);
        c.restore();

    }
}


If you need further clarification on recursion, check out what branch() does when we only have one block of code between save() and restore() below.  We see a single line that is formed by calling itself, using a shorted "length" which has been rotated then drawn.  When we have both blocks of code, we're just doing this same thing but for both sides and "branching out" from each end point means we double the number of end points per recursion.






Wednesday, April 16, 2014

Vortex and Timer

Something we never touched on with the prior walkthroughs was time-dependent animation.  Let us approach this new concept with the vortex.  In JavaScript, we use the setInterval() thread seen below.

What goes inside of the drawShapes function found in the template is the set up, the timed thread, and some equations quite similar to the spiral formulas.

We translate the screen like we have done with the spiral and also rotate the screen by 90 degrees.

vh and vw are the restrictions we put on the vortex - the widest width/height are dependent on these variable numbers.

ij is a number that we can change to mix the results of the vortex spiral.

Stepping into the setInterval() method, the first thing we'll need to do is clear the screen every time and I've provided a short method which fills a white rectangle over the canvas.

We then increment the time-dependent variable, ii.  This variable works with the "waiting period" parameter near the end of the setInterval() method - in this case 50.  Mixing and matching these two will speed up the animation and can make it smoother or more jolted.





function drawShapes() {

    c.translate(w / 2, h / 2);
    c.rotate(Math.PI);

    var ii = 1,
        x, y;

    var vh = 2.5,
        vw = 2;

    ij = 2;

    r = 5;

    setInterval(function() {

        clearScreen();
        c.fillStyle = 'black';

        ii += 0.01;

        for (i = 0; i * r < h; i++) {

            x = i * vw * Math.sin((ij * i) + ii);
            y = i * vh;

            c.beginPath();
            drawCircle(x, y, 5);

            c.beginPath();
            drawCircle(-x, -y, 5);
            c.fill();
        }
    }, 50);
}

Friday, February 21, 2014

Update

Since the last post, I've created a live wallpaper for Android devices that display these designs.  You can find it by searching "ClarkMU" on the Play Store or follow this link to my app.  Help by leaving feedback and (5-star) reviews!



Tuesday, June 18, 2013

Sunflower Spiral

In celebration of summer and my sunflowers reaching 6 inches already, let's render a Fermat spiral!

The first concept is to translate the canvas.  This simply moves the center (0,0).  c.translate(w/2,h/2) puts (0,0) right in the middle of the screen rather than the top left and can be useful in a number of ways.

Next, we'll declare the constant (R)adius of each circle, our constant scaling factor (CSF) will be twice as large as our radius - this is the distance between each circle.  Constant iterations (C_I) will be our stopping point, or how far out our spiral reaches.

To calculate a Vogel spiral, we use the formula 

The next set of variables relate to the formula, where our r is the r from the calculation (polar coordinate radius, different from the radius of our circles), CSF=c, i=n, θ=θ.  Inside of our loop, we place this calculation.  We then convert from polar to cartesian coordinates and draw each point in the spiral.





function drawShapes() {

    c.translate(w / 2, h / 2);

    var R = 5,
        CSF = R * 2,
        C_I = 300,
        i = 1,
        r, x, y, theta;

    do {

        r = CSF * Math.sqrt(i);
        theta = i * 137.5;

        x = r * (Math.cos(theta));
        y = r * (Math.sin(theta));

        drawCircle(x, y, R);

        i++;
    }
    while (i < C_I);
}

Saturday, June 8, 2013

Drawing Sprites

The new variable here, C_I, is the number of times you'd like to go through the loop.  To make more variations of what you're drawing, increase C_I and C_C.





function drawShapes() { var coordX = 1, coordY = 1, cvX = 1, cvY = 1, C_I = 5, C_C = 50, i = 1, R; do { R = 5 * i; coordX = C_C * cvX; coordY = C_C * cvY; drawCircle(coordX, coordY, R); cvX++; i++; if (coordX >= w) { cvX = 1; cvY++; } } while (i <= C_I); }