image-processingcanvasbevelled

canvas (about) 5px line -- add bevel effect to make it 3D -- any ideas? (Sample of desired final effect included)


I want to draw (for instance) a line on HTML5 canvas. Then I want to add an image bevel effect to that line. So it looks 3D, like a piece of string sitting on the canvas. Has anyone seen an image processing effect that can do something along that line?

enter image description here


Solution

  • I think the only way to do that kind of drawing is to draw step by step the curve, and apply at each step a texture perpendicular to the local tangent.

    But you seem to want another feature : be able to 'twist' the texture depending on the part of the curve. Here there are so mant options to parameterize this i can't guess how you'll store/interpolate the twist of the texture. So below i just had the texture 'turn' with t, the parameter of the bezier curve.

    http://jsfiddle.net/gamealchemist/YPKV9/

    Example : Image showing a texture applied to the curve, and below the same curve with the same texture that turns along with the curve.

    enter image description here

    //  ----------------------------------------------
    //  Draw bezier curve filled with 
    //    with txCv used as perpendicular texture 
    //    signature : drawTexturedBezier ( txCv, a, b, c, d, twisted )
    //    a,b,c,d are control points, twisted means
    //      do we have to twist the curve.
    //          Width of the bezier is == to txCv.width/2 
    //          when the draw resolution is tx.height (lower = nicer+slower, 3 seems fine )
    //  ----------------------------------------------
    function drawTexturedBezier() {
        // build onnly once local vars
        var pt = {
            x: 0,
            y: 0
        };
        var tg = {
            x: 0,
            y: 0
        };
        var lastPt = {
            x: 0,
            y: 0
        };
        drawTexturedBezier = function (txCv, a, b, c, d, twisted) {
            var lineHeight = txCv.height;
            var t = 0;
            var incr = 1 / 1000;
            var pointCount = 0;
            pt.x = a.x;
            pt.y = a.y;
            var thisCoordsForT = coordsForT.bind(null, a, b, c, d);
            var thisTgtForT = tangentForT.bind(null, a, b, c, d);
            // scan the bezier curve by increment of lineHeight distance
            //  on the curve.
            do {
                // update last point
                lastPt.x = pt.x;
                lastPt.y = pt.y;
                // seek next point far enough from previous point
                // just compute one point ahead
                //   using average estimate for t of '1 point ahead'
                thisCoordsForT(t + incr, pt);
                var dx = pt.x - lastPt.x;
                dx *= dx;
                var dy = pt.y - lastPt.y;
                dy *= dy;
                // compute distance to previous point
                var dist = Math.sqrt(dx + dy);
                // compute required t increment
                // considering curve is locally linear
                //  0.92 compensates for the error.
                var tIncrement = 0.92 * incr * (lineHeight / dist);
                t += tIncrement;
                // compute point
                thisCoordsForT(t, pt);
                pointCount++;
                // update regular increment with local one 
                incr = 0.2 * incr + 0.8 * tIncrement;
                // compute tangent for current point
                thisTgtForT(t, tg);
                // draw with perpendicular texture
                drawTexturedLine(txCv, pt, tg, t, twisted);
            } while (t < 1);
        };
        return drawTexturedBezier.apply(this, arguments);
    }
    

    With

    // draws a rect centered on pt, for a curve having tg as local tangent
    //     having size of : txCv.width/2 X txCv.height
    //      using the txCv horizontal pattern as pattern.
    function drawTexturedLine(txCv, pt, tg, t, twisted) {
        var lineWidth = txCv.width / 2;
        var lineHeight = txCv.height;
        context.save();
        context.beginPath();
        context.lineWidth = 2;
        context.translate(pt.x, pt.y);
        context.rotate(Math.PI + Math.atan2(-tg.x, tg.y));
        var twistFactor = 0;
        if (twisted) {
            twistFactor = (2 * t + animating * Date.now() / 2000) % 1;
        }
        context.drawImage(txCv,
        twistFactor * lineWidth, 0, lineWidth, lineHeight, -0.5 * lineWidth, -0.5 * lineHeight, lineWidth, lineHeight);
        context.restore();
    }