At school I am just finishing up fall quarter. This was my first quarter of calculus and we are having our last quiz tomorrow on Parametric functions and l'Hopital's Rule. Instead of studying (or does this count?) I built a parametric equation graphing html/js thingy. I say thingy because it is definitely not a "web app", and I refuse to use the acronym "DHTML" in any context. This is just manipulating little DOM nodes around, obviously, there are much better mediums for this sort of thing. Let me have my fun.

As I write this, I just realized I should have tried out HTML5's <canvas>! Wow, I have been meaning to mess with that for a long time, and I feel I just let myself down. I answered "HTML5 and the canvas tag" to the question "What is some technology you are currently excited about?" at my interview for the job I hold now. One moment ago I was giddy over my graphs and now I feel like an idiot. Oh well, I guess I will have to port the code over sometime in the future.

A couple words of warning before I let you have it:

  • The CSS may very well break in your browser. I have only tested this in Firefox 3. (Still haven't upgraded the desktop at home to Karmic Koala which has 3.5)
  • It is really slow. 4000 points was about the max I hit before I wanted to walk away from the browser and come back. Don't actually walk away though because you will probably get an unresponsive script warning (despite my setTimeouts) and you won't even get a pretty graph when you come back. All that time wasted for nothing! (Don't blame me, I just wrote the slow code.)
  • There is no try/catch block so invalid "formulas" (ahem, pretty much eval()ed JS) just plain won't work.

Click Here to Make Some Pretty Graphs!

Here are a couple ideas to get you started:

x = (t/7)*cos(t)
y = (t/7)*sin(t)
from 0 to 15*pi
2000 points
x = 2*cos(5*t)
y = 2*sin(3*t)
from 0 to 2*pi
1000 points

And here is the relevant JS code:

$(document).ready(function () {
    // For performance lets keep a reference to this element so we
    // don't have to search for it all the time.
    window.graph = $("#graph");

    $("#plot").click(function (event) {
        // Prevent form from POSTing and clear the graph.
        event.preventDefault();
        window.graph.html("");

        // Create the x and y functions.
        var x = textToFn($("#x").val());
        var y = textToFn($("#y").val());

        // Determine the range and step of t.
        var start = formFieldToNum("#range-start");
        var end = formFieldToNum("#range-end");
        var numPoints = formFieldToNum("#num-points");
        var step = (end - start) / numPoints;

        for (var t = start; t < end; t = t + step) {
            // setTimeout so that the browser won't freeze up. Must
            // pass t to a second function or else it will capture
            // the final value of t rather than what it is now
            // because t is passed by reference.
            (function (t) {
                setTimeout(function () {
                    plot(x(t), y(t));
                }, 1);
            }(t));
        }
    });
});

// Takes an x and a y pair that represents a point and plots the point
// on the graph.
function plot(x, y) {
    var point = $("<div class='point'></div>").css({
        "left": x*50 + "px",
        "bottom": y*50 + "px"
    });
    window.graph.append(point);
}

// Returns a function from a sample of text that has mathy things.
function textToFn(text) {
    return new Function(
        "t", ("return " + mathReplace(text) + ";"));
}

// Takes a form field's id selector and returns the number inside.
function formFieldToNum(id) {
    return parseInt(eval(mathReplace($(id).val())), 10);
}

// Convert Cos, Sin, and Pi to the JS equivalents.
function mathReplace(text) {
    return text.replace(/cos/gi, "Math.cos")
                .replace(/pi/gi, "Math.PI")
                .replace(/sin/gi, "Math.sin");
}