4

I'm trying to draw a smooth curved arc between two points in canvas, I have set up the points as sutch note these are dynamic and can change.

var p1 = {
    x=100, y=100 
}

var p2 = {
    x=255, y=255
}

The curve would look something like this

curves

Here my started code, I can't get my head around the math/logic of this function:

function curveA2B(a,b){

    var mindpoint = {
        x: (a.x+b.x)/2,
        y: (a.y+b.y)/2,
        d: Math.sqrt(Math.pow(b.x-a.x,2) + Math.pow(b.y-a.y,2))
    };

    context.beginPath();
    context.arc(
        a.x,
        a.y,
        mindpoint.d/2,
        1.5*Math.PI,
        0,
        false
    );

    context.arc(
        b.x,
        b.y,
        mindpoint.d/2,
        1*Math.PI,
        0.5*Math.PI,
        true
    );

    context.context.stroke();

}

The dynamic examples is here: http://jsfiddle.net/CezarisLT/JDdjp/6/

Kivylius
  • 6,357
  • 11
  • 44
  • 71
  • 2
    possible duplicate of [how to draw smooth curve through N points using javascript HTML5 canvas?](http://stackoverflow.com/questions/7054272/how-to-draw-smooth-curve-through-n-points-using-javascript-html5-canvas) – DevlshOne Dec 03 '13 at 14:23
  • 1
    Hint: think triangles. – DevlshOne Dec 03 '13 at 14:24
  • Hint: think splines! @DevlshOne: the previous SO answer you reference has a nice example of connecting points with a spline. A spline is just a set of individual quadratic curves that are designed to look like a single curve that passes through a given set of points. – markE Dec 03 '13 at 16:16

2 Answers2

4

I created a function that would be easily modifiable to many needs called plot_curve that gives you an idea of the breakdown of your problem.

A quick DEMO: http://jsfiddle.net/LVFat/

function plot_curve(x,y,xx,yy, target,color)
{
    var startX=x;  
    var startY=y;  

    var endX=xx;  
    var endY=yy;  

    var diff_x = xx - x;
    var diff_y = yy - y;


    var bezierX=x;  // x1
    var bezierY=yy; // y2

    console.log("bx:"+bezierX);
    console.log("by:"+bezierY); 

    var cx,cy, t;   

    for(t=0.0; t<=1; t+=0.01)  
    {  
        cx =  Math.round(  (1-t)*(1-t)*startX + 2*(1-t) * t * bezierX + t*t*endX);  
        cy =  Math.round(  (1-t)*(1-t)*startY + 2*(1-t) * t * bezierY + t*t*endY);  

        // change this part to whatever you are trying to manipulate to the curve
        plot_pixel( Math.round(cx), Math.round(cy), target, color);
    }  
}

example... (works with a divCanvas function I made.. check out jsfiddle link...)

plot_curve(25,25,5,5, ".divCanvas","blue");

if you just want the coords for the curve between the two points, try this:

function plot_curve(x,y,xx,yy)
{
    // returns an array of x,y coordinates to graph a perfect curve between 2 points.
    var startX=x;  
    var startY=y;  

    var endX=xx;  
    var endY=yy;  

    var diff_x = xx - x;
    var diff_y = yy - y;

    var xy = [];
    var xy_count = -1;

    var bezierX=x;  // x1
    var bezierY=yy; // y2

    var t;   

    for(t=0.0; t<=1; t+=0.01)  
    {
      xy_count++;
      xy[xy_count] = {};
      xy[xy_count].x = Math.round(  (1-t)*(1-t)*startX + 2*(1-t) * t * bezierX + t*t*endX);
      xy[xy_count].y = Math.round(  (1-t)*(1-t)*startY + 2*(1-t) * t * bezierY + t*t*endY);
    }

    return xy; // returns array of coordinates
}
PlugTrade
  • 837
  • 11
  • 19
  • Can you help me reduce the size of the curve? I want just a slight curve instead of the one specified in the demo. https://prnt.sc/pxh6w6 – ChiragMS Nov 15 '19 at 16:30
2

You can use the mid of the two points as two radius settings for the x and y axis.

The following example is simplified but it shows one approach to create smooth curves inside the boxes as in your example.

The boxes will always scale so that the curves goes through the mid point between the two points (alter the end point for example).

DEMO

Snapshot from demo

/// set up some values
var ctx = demo.getContext('2d'),
    p1 = {x:100, y:100},           /// point 1
    p2 = {x:355, y:255},           /// point 2
    mx = (p2.x - p1.x) * 0.5,      /// mid-point between point 1 and 2
    my = (p2.y - p1.y) * 0.5,
    c1 = {x: p1.x, y: p1.y + my},  /// create center point objects
    c2 = {x: p2.x, y: p2.y - my},
    steps = 0.05;                  /// curve resolution

/// mark the points and the boxes which represent the center of those
ctx.fillStyle = '#ff6e6e';
ctx.fillRect(p1.x, p1.y, mx, my);

ctx.fillStyle = '#6e93ff';
ctx.fillRect(p1.x + mx, p1.y + my, mx, my);

Then we render the quarter ellipse for each "box":

/// render the smooth curves using 1/4 ellipses    
ctx.beginPath();

for(var isFirst = true,            /// first point is moveTo, rest lineTo
        angle = 1.5 * Math.PI,     /// start angle in radians
        goal = 2 * Math.PI,        /// goal angle
        x, y; angle < goal; angle += steps) {

    /// calculate x and y using cos/sin
    x = c1.x + mx * Math.cos(angle);
    y = c1.y + my * Math.sin(angle);

    /// move or draw line
    (isFirst) ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
    isFirst = false;
}

/// second box    
for(var isFirst = true,
        angle = Math.PI,
        goal = 0.5 * Math.PI,
        x, y;angle > goal; angle -= steps) {

    x = c2.x + mx * Math.cos(angle);
    y = c2.y + my * Math.sin(angle);

    (isFirst) ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
    isFirst = false;
}
ctx.stroke();

I'll leave it to you to put this into re-usable functions. Hope this helps!

If this doesn't cut it I would recommend you to take a look at my cardinal spline implementation.

Community
  • 1
  • 1