1

I'm trying to draw a line from the edges of the two circles in HTML5 canvas. So, I know the coordinates of the centers of two circles and their radius.

  1. Circles drawn randomly.
  2. Line should move from the edge of one circle to the other.

Please help!

P.S. Sorry for my english :)

UPDATE:

I'm trying this, but how to know angle?

from_line_x = circle1.x + circle1.radius * Math.cos(Math.PI * angle);
from_line_y = circle1.y + cirlce1.radius * Math.sin(Math.PI * angle);
to_line_x = circle2.x - circle2.radius * Math.cos(Math.PI * angle);
to_line_y = circle2.y - circle2.radius * Math.sin(Math.PI * angle);

UPDATE2:

I think I found how to find the angle. But as a circle drawn randomly, the line is drawn is not always true. How then should look algorithm?

Sorry for my english again.

newca9h
  • 53
  • 2
  • 6
  • What is the edge of a circle ? A small drawing would help. – GameAlchemist Apr 22 '14 at 12:39
  • Like this: http://sayakhov.ru/media/uploads/example.png and this, but with HTML5 canvas: http://jsfiddle.net/5YmJv/ – newca9h Apr 22 '14 at 15:51
  • In response to your update, you don't need to know the angle to compute the end-points of the line. Imagine a line extending from the center of one to the center of the other. This line intersects each endpoint of the line. Normalize this vector, you can use: it, the circles' radii and the circle centers to compute the line-segment end-points. See my comment to @Tamura's solution. – enhzflep Apr 22 '14 at 16:13
  • I saw your comment, but I do not understand how to write code for this :) Thank you! – newca9h Apr 22 '14 at 16:24

4 Answers4

5

Here's a solution that will achieve what you've asked for.

I've declared 3 'classes' to make things clearer to read. First, I define a generic shape class. Next, I define a basic circle class. Finally, I define a vec2 class. You could easily extend this as I have done, and add other shapes that inherit from the shape class - i.e square triangle, etc.

I create 10 circles at random positions and radii. I then draw a line between each circle and the one following it. I didn't bother with the 'wrap-around' case, so I draw 10 circles and 9 lines (I dont draw from circle 9 to circle 0)

I've used some of the code Tamura left, hence the familiar dimensions and id of the canvas.

<!doctype html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e)}
window.addEventListener('load', onDocLoaded, false);

var shapeList = [];

function onDocLoaded()
{
    var i, n=10;
    var canvas = byId('myCanvas');

    for (i=0; i<n; i++)
    {
        shapeList[i] = new circle_t(Math.random()*578, Math.random()*400, Math.random()*30 + 20);
        shapeList[i].draw(canvas);
    }

    for (i=0; i<n-1; i++)
        draw_line2(shapeList[i].origX, shapeList[i].origY, shapeList[i].radius, shapeList[i+1].origX, shapeList[i+1].origY, shapeList[i+1].radius);
}   

var shape_t = function(x,y)
{
    this.origX = (x==undefined ? 0 : x);
    this.origY = (y==undefined ? 0 : y);
}
shape_t.prototype =
{
    origX:0, origY:0, typeString:'shape',
    setPos: function(x,y){this.x=x;this.y=y;},
    setType: function(typeString){this.typeString = typeString;},
    toString: function(){return this.typeString + " - " + this.origX + "," + this.origY;},
    draw: function(canElem){},
};

function circle_t(x,y,radius)
{
    this.origX = (x==undefined ? 0 : x);
    this.origY = (y==undefined ? 0 : y);
    this.radius = (radius==undefined ? 10 : radius);
    this.setType("circle");
}
circle_t.prototype = new shape_t();
circle_t.prototype.constructor = circle_t;
circle_t.prototype.draw = function(canElem, color)
{
    var ctx = canElem.getContext('2d');
    var col = 'black';
    if (color != undefined)
        col = color;
    drawCircle(this.origX, this.origY, this.radius, ctx, col);
}

circle_t.prototype.setRadius = function(radius)
{
    if (radius != undefined)
        this.radius = radius;
}

function drawCircle(x, y, radius, ctx, col)
{
    ctx.save();
    if (col == undefined)
        col = 'black';
    ctx.strokeStyle = col;
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.arc(x,y,radius,(Math.PI/180)*0, (Math.PI/180)*360, false);
    ctx.stroke();
    ctx.closePath();
    ctx.restore();
}

// define a vec2 class to make vector maths easier (simpler to read)
function vec2(x,y)
{
    this.length = function()
    {
        return Math.sqrt((this.x * this.x) + (this.y*this.y));
    }
    this.normalize = function()
    {
        var scale = this.length();
        this.x /= scale;
        this.y /= scale;
    }
    this.x = x;
    this.y = y;
}

function draw_line2(center1_x, center1_y, radius1, center2_x, center2_y, radius2)
{
    var betweenVec = new vec2(center2_x - center1_x, center2_y - center1_y);
    betweenVec.normalize();

    var p1x = center1_x + (radius1 * betweenVec.x);
    var p1y = center1_y + (radius1 * betweenVec.y);

    var p2x = center2_x - (radius2 * betweenVec.x);
    var p2y = center2_y - (radius2 * betweenVec.y);

    var canvas = document.getElementById('myCanvas');
    var context = canvas.getContext('2d');
    context.beginPath();
        context.moveTo(p1x,p1y);
        context.lineTo(p2x,p2y);
    context.stroke();
}
</script>
</head>
<body>
    <canvas id="myCanvas" width="578" height="400"></canvas>
</body>
</html>

See here for a live demo: http://jsfiddle.net/YYjYL/

enhzflep
  • 12,927
  • 2
  • 32
  • 51
  • Thank you very much, enhzflep, it has been a long time since I had algebra in college and was too lazy to figure out the logic. Your code helped me. – Andy Apr 23 '16 at 19:16
3

3 steps solutions :
- build all your random circle coordinates (x,y,r).
- draw all lines in between their center as you see fit.
- draw all circles.

(!)

code is very easy :

http://jsbin.com/qutahatu/1/edit?js,output

enter image description here

var circles = [];

function createCircles(cnt) {
  circles = [];
  for (var i=0; i<cnt; i++) {
    var x = 5+ Math.random() *300;
    var y = 5+ Math.random() *300;
    var r = 20  + Math.random() *6;    
    circles.push({x:x,y:y,r:r});
  }
}

function drawLines() {
  var cnt= circles.length;
   ctx.strokeStyle = '#000';
  ctx.lineWidth = 2;
  ctx.beginPath();
  ctx.moveTo(circles[0].x, circles[0].y);
  for (var i=1; i<cnt; i++) {
     ctx.lineTo(circles[i].x, circles[i].y);
  }
  ctx.stroke(); 
}

function drawCircles() {
  var cnt= circles.length;
   ctx.fillStyle = '#4A8';
  for (var i=0; i<cnt; i++) {
     ctx.beginPath();
     ctx.arc(circles[i].x, circles[i].y, circles[i].r, 0, 6.282);
     ctx.fill();
  }
}

createCircles(4);
drawLines();
drawCircles();
GameAlchemist
  • 18,995
  • 7
  • 36
  • 59
  • A different way? I mean, what I would like is a line from the edge of the circle, and not from the center. – newca9h Apr 22 '14 at 16:30
  • that will do just that, since the circles will hide the part of the line that is below the circles. – GameAlchemist Apr 22 '14 at 16:45
  • Nice simple code, my +1. This is a much better approach than mine if the circles are opaque. No need for messing with unit vectors if you can't see behind the circles. Nice. – enhzflep Apr 22 '14 at 17:11
  • Thanks ! Yes this solution won't work non-opaque circles, but -like you it seems- i liked the simplicity. – GameAlchemist Apr 22 '14 at 17:21
  • i am having problem with canvas 5 like when animating my movable rectangles they get on to one another.This is 1 demo js bin i have created:http://output.jsbin.com/zacacufuxa – I Love Stackoverflow May 13 '16 at 05:55
  • 1
    @learning : This has little relation with the question ! :-) To avoid collision between your rectangles you have to test for those collisions and solve them. Not so easy a problem if you want to handle the case of multiple-collision properly. I did a small simple demo to get you started, look at `updateUsers`. http://jsbin.com/tomuzawusu/1/edit?js,output Good luck in your project ! – GameAlchemist May 13 '16 at 08:08
  • Thank you so much for your help.But animation stuck alot.its not smoother at all.Sorry i am very new with this canvas stuff and i have seen your answer.Can you please help me little bit with this – I Love Stackoverflow May 13 '16 at 08:45
  • 1
    @Learning : i wrote an article about animation here : http://codepen.io/gamealchemist/post/animationcanvas1 – GameAlchemist May 13 '16 at 08:57
  • This is awesome.Actually this is my original code:http://jsbin.com/ruhiwotewu/1/edit?html,js,console,output. But what happens with this code is it is consuming lots of memory and after continuosly running for 10-15 minutes it hang the browser.can you please take a look and tell me that whther i should go with canvas or it is possible to do something better with my original code.please – I Love Stackoverflow May 13 '16 at 09:02
  • 1
    @Learning : Thanks. No time to look, sorry. narrow down your issue and post a question here on S.O. if you're stuck. Good luck. – GameAlchemist May 13 '16 at 09:07
  • Can you please help me with this question:http://stackoverflow.com/questions/37208156/update-html-canvas-tag-on-every-ajax-request-with-new-data – I Love Stackoverflow May 13 '16 at 10:56
1

Here is the JSFIDDLE link: http://jsfiddle.net/WfF3v/1/. Is that what you want?

Below is the JS code:

function draw_circle(center_x, center_y){
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = center_x;
var centerY = center_y;
var radius = 70;

context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
}

function draw_line(center1_x, center1_y, center2_x, center2_y) {
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

context.beginPath();
context.moveTo(center1_x, center1_y);
context.lineTo(center2_x, center2_y);
context.stroke();
}

draw_circle(100,100);
draw_circle(300,200);
draw_line(100,100,300,200);
Tamura
  • 1,788
  • 1
  • 14
  • 10
  • 1
    My +1. Good solution, although I note the original question states "***edge*** of circle to another circle's ***edge***". If you calculate the unit vector between each of their origins, you can use it to compute the point of intersection with the outline. In your example, if the unit vector extends in the direction from c1's origin to c2's origin: `lineEndpoint1 = circle1_origin + (unit_vector_between_origins * circle1_radius)` and `lineEndpoint2 = circle2_origin - (unit_vector_between_origins * circle2_radius)` You could then use draw the line between these two computed coords. – enhzflep Apr 22 '14 at 12:35
  • No it's not. I need from the edges of the circles, and not from the center. From the center and I got :) but thanks for your answer. – newca9h Apr 22 '14 at 15:33
0

Had the same problem so I made these functions based on sketch. connectingPts takes the two circle's coordinates {x: x, y: y, r: radius} and returns a 2 element array of the coordinates of the line segment connecting the two circles from their edges.

function connectingPts(t1, t2) {
    let p1, p2;

    if (t1.x < t2.x) {
        p1 = { ...t1 };
        p2 = { ...t2 };
    } else {
        p1 = { ...t2 };
        p2 = { ...t1 };
    }

    let dx = Math.abs(p1.x - p2.x);
    let dy = Math.abs(p1.y - p2.y);

    let alpha = radToDeg(Math.atan(dy / dx));
    let beta = 90 - alpha;

    return [
        rotatePt(
            { ...p1, x: p1.x + p1.r },
            p1,
            (p2.y < p1.y ? 360 : alpha * 2) - alpha,
        ),
        rotatePt(
            { ...p2, x: p2.x + p2.r },
            p2,
            (p2.y > p1.y ? 270 - 2 * beta : 90) + beta,
        ),
    ];
}

function rotatePt(p, o, deg) {
    let res = { ...p };

    let _x = res.x - o.x;
    let _y = res.y - o.y;

    res.x = _x * Math.cos(degToRad(deg)) - _y * Math.sin(degToRad(deg));
    res.y = _x * Math.sin(degToRad(deg)) + _y * Math.cos(degToRad(deg));

    res.x += o.x;
    res.y += o.y;

    return res;
}

function degToRad(deg) {
    return (deg * Math.PI) / 180;
}

function radToDeg(rad) {
    return (rad * 180) / Math.PI;
}