Drawing smooth lines with canvas
Asked Answered
G

3

9

So far none of the threads here on smooth lines are correct.

how to draw smooth curve through N points using javascript HTML5 canvas?

Smooth user drawn lines in canvas

Both result in jagged lines. By smooth I mean using the x,y points as control points to make the line smooth. The line does NOT need to go through the points. It simply has to draw a smooth line given n points.

Basically I'm recording each line segment, then when the user mouses up, it wil smooth the line.

I've tried my own method using bezierCurveTo but that only smooths every other point, and then the connecting points are harsh still. The internet seems to think what I'm looking for is called B-Spline curves. I tried applying a linear algebra matrix to the problem, but I failed at that lol.

Here is the best curve I can get, (image). The line in red is the "smoothed" line, as you can see it smooths every other point, but not continuous. This is using the code from

how to draw smooth curve through N points using javascript HTML5 canvas?

My code does the same thing

http://www.square-bracket.com/images/smoothlines.png

Thanks for your help!

Gunzburg answered 25/10, 2011 at 15:31 Comment(0)
A
4

You need to keep the same tangent in the points bellowing in the line. Check http://jsfiddle.net/FHKuf/4/.

Edit:

Sorry, just noticed your comment today. Just happened to be testing something related and remembered your question. It happens that in the past I did wrote some code to interpolate some lines. It is called Catmull-Rom(just a ref. that I googleed) it passes by the middle control points. I did changed the code to my test and thought that you may have some use to it. See at http://jsfiddle.net/FHKuf/6/.

Antione answered 25/10, 2011 at 16:31 Comment(3)
This is much closer. But the curves at the points are curving opposite the line. So its a wavy line now. Ill see if can manip this to be right, unless you know why its doing that. thanks!Gunzburg
That is because the Catmull-Rom spline don't interpolate the first and last point in the segment and I was adding points in the begin and end of list of control points to get them drawn. Try to play with that like jsfiddle.net/FHKuf/54Antione
This a old question. I just stumbled into a library called Smooth.js. I did not test it but there is a sample, it may be worth using.Antione
H
2

I was exploring all techniques but did not get any proper solution for smooth free drawing on canvas. Then I simply used quadraticCurveTo with different logic without using original mouse points.

I first calculated control point(mid-point) and replace old mouse move point with the control point. I did this 2 times and finally applied quadraticCurveTo to the final array and I got super smooth drawing.

It was amazing. I did it without using this heavy paper.js and other smoothing libraries.

Here is my code:

currentCanvas.beginPath();
        currentCanvas.lineCap = 'round';
        currentCanvas.strokeStyle = "black";
        currentCanvas.lineWidth = "2";
        currentCanvas.moveTo(queue[0].x, queue[0].y);

        //queue is an array of original points which were stored while onmousemove event callback

        var tempQueue1 = [queue[0]];
        for (var i = 1; i < queue.length - 1;  i = i+1) {
            //if((Math.abs(queue[i].x - queue[i-1].x) >3 || Math.abs(queue[i].x - queue[i-1].x)<1) &&  (Math.abs(queue[i].y - queue[i-1].y) >3 || Math.abs(queue[i].y - queue[i-1].y)<1)){
                var c = (queue[i].x + queue[i + 1].x) / 2;
                var d = (queue[i].y + queue[i + 1].y) / 2;
                //tempQueue.push(queue[i]);
                tempQueue1.push({x:c, y:d});
                //currentCanvas.quadraticCurveTo(queue[i].x, queue[i].y, c, d);
            //}
        }

        var tempQueue2 = [tempQueue1[0]];
        for (var i = 1; i < tempQueue1.length - 1;  i = i+1) {
            //if((Math.abs(queue[i].x - queue[i-1].x) >3 || Math.abs(queue[i].x - queue[i-1].x)<1) &&  (Math.abs(queue[i].y - queue[i-1].y) >3 || Math.abs(queue[i].y - queue[i-1].y)<1)){
                var c = (tempQueue1[i].x + tempQueue1[i + 1].x) / 2;
                var d = (tempQueue1[i].y + tempQueue1[i + 1].y) / 2;
                //tempQueue.push(queue[i]);
                tempQueue2.push({x:c, y:d});
                //currentCanvas.quadraticCurveTo(queue[i].x, queue[i].y, c, d);
            //}
        }

        var tempQueue = [tempQueue2[0]];
        for (var i = 1; i < tempQueue2.length - 1;  i = i+1) {
            //if((Math.abs(queue[i].x - queue[i-1].x) >3 || Math.abs(queue[i].x - queue[i-1].x)<1) &&  (Math.abs(queue[i].y - queue[i-1].y) >3 || Math.abs(queue[i].y - queue[i-1].y)<1)){
                var c = (tempQueue2[i].x + tempQueue2[i + 1].x) / 2;
                var d = (tempQueue2[i].y + tempQueue2[i + 1].y) / 2;
                //tempQueue.push(queue[i]);
                tempQueue.push({x:c, y:d});
                //currentCanvas.quadraticCurveTo(queue[i].x, queue[i].y, c, d);
            //}
        }

        for (var i = 1; i < tempQueue.length - 2;  i = i+1) {
            //if((Math.abs(queue[i].x - queue[i-1].x) >3 || Math.abs(queue[i].x - queue[i-1].x)<1) &&  (Math.abs(queue[i].y - queue[i-1].y) >3 || Math.abs(queue[i].y - queue[i-1].y)<1)){
                var c = (tempQueue[i].x + tempQueue[i + 1].x) / 2;
                var d = (tempQueue[i].y + tempQueue[i + 1].y) / 2;
                currentCanvas.quadraticCurveTo(tempQueue[i].x, tempQueue[i].y, c, d);
            //}
        }

        // For the last 2 points
        currentCanvas.quadraticCurveTo(
        tempQueue[i].x,
        tempQueue[i].y,
        tempQueue[i+1].x,
        tempQueue[i+1].y
        );
        currentCanvas.stroke();
        queue = [];
Hoi answered 28/6, 2017 at 18:55 Comment(0)
H
0

@Manoj Verma nice idea to use line points as control points! I made some improvements to your code. Still nice smooth line without unnecessary operations. Also i fixed bug with drawing till last point

        currentCanvas.beginPath();
        currentCanvas.lineCap = 'round';
        currentCanvas.strokeStyle = "black";
        currentCanvas.lineWidth = 2;
        currentCanvas.moveTo(queue[0].x, queue[0].y);

        let x, y

        for (let i = 0; i < queue.length - 1; i++) {
            x = (queue[i].x + queue[i + 1].x) / 2;
            y = (queue[i].y + queue[i + 1].y) / 2;
            currentCanvas.quadraticCurveTo(queue[i].x, queue[i].y, x, y);
        }
        const lastPoint = queue[queue.length - 1]

        currentCanvas.quadraticCurveTo(lastPoint.x, lastPoint.y, lastPoint.x, lastPoint.y)

        currentCanvas.stroke();
Handbook answered 28/7, 2023 at 5:54 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Edgardo

© 2022 - 2025 — McMap. All rights reserved.