Cubic Bezier Generator

by 2 contributors:
<html>
    <canvas id="bezier" width="336" height="336">
    </canvas>
    <form>
       <label for="x1">x1 = </label><input onchange="updateCanvas();" type="text" maxlength=6 id="x1" value="0.79" class='field'>
       <label for="y1">y1 = </label><input onchange="updateCanvas();return true;"  type="text" maxlength=6 id="y1" value="0.33" class='field'>
       <label for="x2">x2 = </label><input onchange="updateCanvas();return true;"  type="text" maxlength=6 id="x2" value="0.14" class='field'>
       <label for="y2">y2 = </label><input onchange="updateCanvas();return true;"  type="text" maxlength=6 id="y2" value="0.53" class='field'>
       <br>
       <output id="output">Log</output>
    </form>
</html>
     
.field {
    width: 40px;
}
     
function updateCanvas() {

    var x1 = document.getElementById('x1').value;
    var y1 = document.getElementById('y1').value;
    var x2 = document.getElementById('x2').value;
    var y2 = document.getElementById('y2').value;

    drawBezierCurve(x1, y1, x2, y2);
}

const radius = 4;
// Place needed to draw the rulers
const rulers = 30.5;
const margin = 10.5;
const basic_scale_size = 5; // Size of 0.1 tick on the rulers
var scaling; //LIMITATION: scaling is computed once: if canvas.height/canvas.width change it won't be recalculated
var dragSM = 0; // Drag state machine: 0 = nodrag, others = object being dragged

function initCanvas() {
    // get the canvas element using the DOM   
    var canvas = document.getElementById('bezier');

    // Make sure we don't execute when canvas isn't supported   
    if (canvas.getContext) {
        // use getContext to use the canvas for drawing     
        var ctx = canvas.getContext('2d');

        scaling = Math.min(canvas.height - rulers - margin, canvas.width - rulers - margin);

        canvas.onmousedown = mouseDown;
        canvas.onmouseup = mouseUp;
    } else {
        alert('You need Safari or Firefox 1.5+ to see this demo.');
    }
}

function cX(x) {
    return x * scaling + rulers;
}

function reverseX(x) {
    return (x - rulers) / scaling;
}

function lX(x) {
    //Used when drawing vertical lines to prevent subpixel blur
    var result = cX(x);
    return Math.round(result) == result ? result + 0.5 : result;
}

function cY(y) {

    return (1 - y) * scaling + margin;
}

function reverseY(y) {
    return (margin - y) / scaling + 1;
}

function lY(y) {
    // Used when drawing horizontal lines to prevent subpixel blur
    var result = cY(y);
    return Math.round(result) == result ? result + 0.5 : result;
}

function drawBezierCurve(x1, y1, x2, y2) {

    // get the canvas element using the DOM   
    var canvas = document.getElementById('bezier');

    // Make sure we don't execute when canvas isn't supported   
    if (canvas.getContext) {

        // use getContext to use the canvas for drawing     
        var ctx = canvas.getContext('2d');



        // Clear canvas
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // Draw the rulers
        ctx.beginPath();
        ctx.strokeStyle = "black";

        // Draw the Y axis
        ctx.moveTo(cX(0), cY(0));
        ctx.lineTo(cX(1), cY(0));
        ctx.textAlign = "right";

        for (var i = 0.1; i <= 1; i = i + 0.1) {
            ctx.moveTo(-basic_scale_size + cX(0), lY(i));
            if ((i == 0.5) || (i > 0.9)) {
                ctx.moveTo(-2 * basic_scale_size + cX(0), lY(i));
                ctx.fillText(Math.round(i * 10) / 10, -3 * basic_scale_size + cX(0), cY(i) + 4);
                // Limitation the constant 4 should be font size dependant
            }
            ctx.lineTo(cX(0), lY(i));
        }
        ctx.stroke();
        ctx.closePath();
        ctx.beginPath();

        // Draw the Y axis label
        ctx.save();
        ctx.rotate(-Math.PI / 2);
        ctx.textAlign = "left";
        ctx.fillText("Output (Value Ratio)", -cY(0), -3 * basic_scale_size + cX(0));
        ctx.restore();

        // Draw the X axis
        ctx.moveTo(cX(0), cY(0));
        ctx.lineTo(cX(0), cY(1));
        ctx.textAlign = "center";
        for (i = 0.1; i <= 1; i = i + 0.1) {
            ctx.moveTo(lX(i), basic_scale_size + cY(0));
            if ((i == 0.5) || (i > 0.9)) {
                ctx.moveTo(lX(i), 2 * basic_scale_size + cY(0));
                ctx.fillText(Math.round(i * 10) / 10, cX(i), 4 * basic_scale_size + cY(0));
                // Limitation the constant 4 should be dependant of the font size
            }
            ctx.lineTo(lX(i), cY(0));
        }

        // Draw the X axis label
        ctx.textAlign = "left";
        ctx.fillText("Input (Time Duration Ratio)", cX(0), 4 * basic_scale_size + cY(0));
        // Limitation the constant 4 should be dependant of the font size
        ctx.stroke();
        ctx.closePath();

        // Draw the Bézier Curve
        ctx.beginPath();
        ctx.moveTo(cX(0), cY(0));
        ctx.strokeStyle = 'blue';
        ctx.bezierCurveTo(cX(x1), cY(y1), cX(x2), cY(y2), cX(1), cY(1));
        ctx.stroke();
        ctx.closePath();



        // Draw the P2 point (with a line to P0)
        ctx.beginPath();
        ctx.strokeStyle = 'red';
        ctx.moveTo(cX(x1), cY(y1));
        ctx.lineTo(cX(0), cY(0));
        ctx.stroke();
        ctx.closePath();
        ctx.beginPath();
        ctx.moveTo(cX(x1), cY(y1));
        ctx.arc(cX(x1), cY(y1), radius, 0, 2 * Math.PI);
        ctx.stroke();
        ctx.fillStyle = 'white';
        ctx.fill();
        ctx.closePath();



        // Draw the P3 point (with a line to P1)
        ctx.beginPath();
        ctx.strokeStyle = 'red';
        ctx.moveTo(cX(x2), cY(y2));
        ctx.lineTo(cX(1), cY(1));
        ctx.stroke();
        ctx.closePath();

        ctx.beginPath();
        ctx.moveTo(cX(x2), cY(y2));
        ctx.arc(cX(x2), cY(y2), radius, 0, 2 * Math.PI);
        ctx.stroke();
        ctx.fill();
        ctx.closePath();

        // Draw the P1(1,1) point (with dashed hints)
        ctx.beginPath();
        ctx.moveTo(cX(1), cY(1));
        ctx.strokeStyle = 'lightgrey';
        ctx.lineTo(cX(0), cY(1));
        ctx.moveTo(cX(1), cY(1));
        ctx.lineTo(cX(1), cY(0));
        ctx.stroke();
        ctx.closePath();

        ctx.beginPath();
        ctx.strokeStyle = "black";
        ctx.fillStyle = "black";
        ctx.arc(cX(1), cY(1), radius, 0, 2 * Math.PI);
        ctx.fill();
        ctx.stroke();
        ctx.closePath();


        // Draw the P0(0,0) point
        ctx.beginPath();
        ctx.arc(cX(0), cY(0), radius, 0, 2 * Math.PI);
        ctx.fill();
        ctx.stroke();
        ctx.closePath();


    } else {
        alert('You need Safari or Firefox 1.5+ to see this demo.');
    }
}

function mouseDown(e) {

    var canvas = document.getElementById('bezier');

    var x1 = cX(document.getElementById('x1').value);
    var y1 = cY(document.getElementById('y1').value);

    var x = e.pageX - canvas.offsetLeft;
    var y = e.pageY - canvas.offsetTop;

    var output = document.getElementById('output');
    output.value = "(" + x + "," + y + ") should be (" + x1 + "," + y1 + ")";


    if ((x1 + radius >= x) && (x1 - radius <= x) && (y1 + radius >= y) && (y1 - radius <= y)) {
        var output = document.getElementById('output');
        output.value = "P1!";
        dragSM = 1;
    }

    var x2 = cX(document.getElementById('x2').value);
    var y2 = cY(document.getElementById('y2').value);

    if ((x2 + radius >= x) && (x2 - radius <= x) && (y2 + radius >= y) && (y2 - radius <= y)) {
        var output = document.getElementById('output');
        output.value = "P2!";
        dragSM = 2;
    }

    // If we are starting a drag
    if (dragSM != 0) {
        canvas.onmousemove = mouseMove;
    }
}

function mouseUp(e) {

    var output = document.getElementById('output');
    output.value = "Mouse up!";
    dragSM = 0;
    canvas.onmousemove = null;
}

function mouseMove(e) {
    if (dragSM != 0) {
        var canvas = document.getElementById('bezier');

        var x = e.pageX - canvas.offsetLeft;
        var y = e.pageY - canvas.offsetTop;

        var output = document.getElementById('output');
        output.value = "Drag!";

        if (dragSM == 1) {
            var x1 = document.getElementById('x1');
            var y1 = document.getElementById('y1');
            x1.value = reverseX(x);
            x1.value = Math.round(x1.value * 10000) / 10000;
            y1.value = reverseY(y);
            y1.value = Math.round(y1.value * 10000) / 10000;
            if (x1.value < 0) { x1.value = 0; }
            if (x1.value > 1) { x1.value = 1; }
        } else if (dragSM == 2) {
            var x2 = document.getElementById('x2');
            var y2 = document.getElementById('y2');
            x2.value = reverseX(x);
            x2.value = Math.round(x2.value * 10000) / 10000;
            y2.value = reverseY(y);
            y2.value = Math.round(y2.value * 10000) / 10000;
            if (x2.value < 0) { x2.value = 0; }
            if (x2.value > 1) { x2.value = 1; }
        }
        updateCanvas();
    }
}

initCanvas();
updateCanvas();
     

Draft
This page is not complete.

This is a sample tool; it lets you edit Bezier curves. This is not really yet a useful tool, but will be!

 

Document Tags and Contributors

Tags: 
Contributors to this page: teoli, Sheppy
Last updated by: teoli,