Kivylius
Kivylius

Reputation: 6547

Drawing a smooth curved arc() between two points

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/

Upvotes: 4

Views: 8842

Answers (2)

user1693593
user1693593

Reputation:

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.

Upvotes: 2

g023
g023

Reputation: 857

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
}

Upvotes: 4

Related Questions