1

It's hard to explain the problem. but I'm gonna try my best. First I make 2 lines, a line contain a start point, and a end point, like this

line = {
   startPoint{x: , y:}
   endPoint{x: , y:}
}

And then I draw the two lines on the canvas forming something like a corner of a triangle like this.

First drawn triangle corner

I now move the lines away from each other with the length Radius*2 like shown below Line move with radius * 2

Then how can i now draw a arc using both endpoint as tangents point, like shown below Finale proudct

Do I need to use arc to this or can I do it with arcto? And if it's arc; how do I then give it of begin drawing and ending point so it draw it like shown on the image in the last figure. Thank for your time, any input helps. Sorry again for the bad description of the problem

  • UPDATE - its seems i did not explain my problem fully. So here is a little update. Using the examples giving here. I end up with a Oval circle. an what i 'm trying to get is a round circle between the lines.
DaCh
  • 921
  • 1
  • 14
  • 48
  • First moveTo then lineTo (line1) then perhaps quadraticCurveTo using the intersection of the lines as the middle point and start of Line 2 as end and finally lineTo (Line 2 ) might be good enough solution? – Tero Tolonen May 13 '15 at 22:51
  • @TeroTolonen i tried, but I think that the part that always went wrong, was how to calc the middle point X, Y – DaCh May 13 '15 at 22:56
  • There should be a lot of solutions for that in Internets http://jsfiddle.net/justin_c_rounds/Gd2S2/ – Tero Tolonen May 13 '15 at 23:00
  • @TeroTolonen is mostly of what i wanted, my only problem with this is that the control point, is longer away from the circle's center and it shouldn't be like radius Does it make sens? – DaCh May 14 '15 at 00:00
  • Well, you can move the control point, but check the other solutions too, they all have different shapes and depends what you are looking which is the best, bezier curve would guarantee the continuity of the lines at the endpoints – Tero Tolonen May 14 '15 at 00:06

2 Answers2

0

Given 2 line segments that meet at a common point, you can apply a rounded intersection to them using a cubic Bezier curve:

enter image description here

Here's how...

  • Given a point p1 and line segments that extend to from p1 to p0 (P10) and from p1 to p2 (P12):

    var p0={x:50,y:50};
    var p1={x:100,y:150};
    var p2={x:250,y:100};
    
  • Calculate points on P10 & P12 that are a specified percent of the way from the common point (p1) back towards their respective starting points (p0 & p2):

    var lerp=function(a,b,x){ return(a+x*(b-a)); };
    var dx,dy,length;
    var offsetPct=0.15;
    
    // calc a point on P10 that is 15% of the way from p1 to p0
    dx=p1.x-p0.x;
    dy=p1.y-p0.y;
    p00={ x:lerp(p1.x,p0.x,offsetPct), y:lerp(p1.y,p0.y,offsetPct) }
    
    // calc a point on P12 that is 15% of the way from p1 to p2
    dx=p1.x-p2.x;
    dy=p1.y-p2.y;
    p22={ x:lerp(p1.x,p2.x,offsetPct), y:lerp(p1.y,p2.y,offsetPct) }
    
  • Then you can draw your rounded intersection using the shortened line segments and a cubic Bezier curve:

Example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

var p0={x:50,y:50};
var p1={x:100,y:150};
var p2={x:150,y:50};


roundedIntersection(p0,p1,p2,0.15);


function roundedIntersection(p0,p1,p2,offsetPct){

  var lerp=function(a,b,x){ return(a+x*(b-a)); };
  var dx,dy,length;
  dx=p1.x-p0.x;
  dy=p1.y-p0.y;
  p00={ x:lerp(p1.x,p0.x,offsetPct), y:lerp(p1.y,p0.y,offsetPct) }
  dx=p1.x-p2.x;
  dy=p1.y-p2.y;
  p22={ x:lerp(p1.x,p2.x,offsetPct), y:lerp(p1.y,p2.y,offsetPct) }

  ctx.beginPath();
  ctx.moveTo(p0.x,p0.y);
  ctx.lineTo(p00.x,p00.y);
  ctx.bezierCurveTo( p1.x,p1.y,  p1.x,p1.y  ,p22.x,p22.y);
  ctx.lineTo(p2.x,p2.y);
  ctx.stroke();

  dot(p0.x,p0.y);
  dot(p1.x,p1.y);
  dot(p2.x,p2.y);
  dot(p00.x,p00.y);
  dot(p22.x,p22.y);

  function dot(x,y){
    ctx.beginPath();
    ctx.arc(x,y,2,0,Math.PI*2);
    ctx.closePath();
    ctx.fill();
  }

}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
markE
  • 102,905
  • 11
  • 164
  • 176
  • i think that bezierCurveTo is the thing i need. thanks for your answer. i just need to find to "control point" that will make a round circle now. – DaCh May 15 '15 at 09:42
  • You can make your vertex joins more "rounded" by making the Bezier curve start closer to the starting points of the line segments. For example, change the `offsetPct` to 0.50 and the join will be quite rounded: `roundedIntersection(p0,p1,p2,0.50);` You can make the join perfectly round by using arc / arcTo, but that's at the cost of pulling the arc further away from the original point joining the 2 segments. – markE May 15 '15 at 16:03
0

Yes, you can use arcTo():

  • Set your first line start point with moveTo()
  • then the intersection point between the two lines as first pair
  • then the end point of the second line (what you call "startpoint line 2") the last pair.
  • Provide a radius
  • To actually draw the last line (it's only used for calculation with arcTo()) add a lineTo() for the last point pair in the arc, stroke/fill.

If you want to move the lines apart but do not know the intersection point you have to manually calculate (see getIntersection() in that answer) it by first interpolating the lines beyond their original length.

var ctx = document.querySelector("canvas").getContext("2d");

ctx.moveTo(0, 0);                 // start point
ctx.arcTo(50, 150, 100, 0, 20);   // intersection, outpoint, radius
ctx.lineTo(100, 0);               // line from arc-end to outpoint

ctx.translate(130, 0);
ctx.moveTo(0, 0);                 // start point
ctx.arcTo(50, 150, 80, 50, 8);   // intersection, outpoint, radius
ctx.lineTo(80, 50);               // line from arc-end to outpoint

ctx.stroke();
<canvas></canvas>

Here is how you can extend the lines to find the intersection point if you move them apart without knowing the intersection point:

function extendLine(line, scale) {
  var sx = line.startPoint.x,
      sy = line.startPoint.y,
      ex = line.endPoint.x,
      ey = line.endPoint.y;

   return {
     startPoint: {x: sx, y: sy},
     endPoint: {
       x: sx + (ex - sx) * scale,
       y: sy + (ey - sy) * scale
     }
   }
}

Scale can be a ridiculous value as we need to make sure that at very steep angles the lines will intersect somewhere. It does not affect calculation speed.

So then with the two lines (make sure the second line continues from first line's end-point, meaning you'll have to probably reverse the coordinates - if you want to do this dynamically you can measure the distance for each point in the second line to the end point of the first line, the shortest distance comes first as startPoint):

The execution steps would then be:

var line1 = ...,
    line2 = ...,
    line1tmp = extendLine(line1, 10000),
    line2tmp = extendLine(line2, 10000),
    ipoint = getIntersection(line1, line2); // see link above

// define the line + arcTo
ctx.moveTo(line1.startPoint.x, line1.startPoint.y);
ctx.arcTo(ipoint.x, ipoint.y, 
          line2.endPoint.x, line2.endPoint.y,
          Math.abs(line2.startPoint.x - line1.endPoint.x) / 2);
ctx.lineTo(line2.endPoint.x, line2.endPoint.y);
ctx.stroke();
  • If i use a Intersection point thats closer to line one then line to, will this circle change to a oval circle? – DaCh May 15 '15 at 10:39
  • @DaCh no, it will always stay circular (answer demo updated with another example) –  May 15 '15 at 12:22
  • after some days testing, i found out that this solution was working like a charm. but i got one question. Why it is that if i got a angle about 5 degrees and i use a big radius, the arc to draw the opposite way. here is one with 5 radius, and it's [Working](http://i58.tinypic.com/351zsxd.jpg). an here is one with 45 radius where it's [Not working](http://i58.tinypic.com/23s91ty.jpg). both have a 9 degreed angle – DaCh May 18 '15 at 13:49
  • @DaCh it's a part of how it works. If the angle and position of the lines makes it impossible to make a circular arc between the lines, the arc is "forced" up to a point where it can be produced. You will have to do some extension of one of the lines (using f.ex. interpolation) to force the arc back down. You can calculate the angle between the end points and a use a length so that the lines are reflected angles. Then find the intersecting point of the shortest line. [For example](http://i.imgur.com/JR8fhuI.png) –  May 18 '15 at 14:02