1

What I am going to do is to change the curve of a circle. If I click one point in a circle and drag it to other point, that arc of the circle should be extended or contracted accordingly. I was going to use beizer curve but there's no guarantee that the new beizer curve will pass the dragged point. Attached is the image showing a new curve when mouse dragged which I can not solve. can anyone help me on this matter? I will be looking forward to your replycanvas image

LongMa Lin
  • 41
  • 6
  • 1
    why there's no guarantee? you mean when they're aligned? – Mario Vernari Jun 24 '20 at 08:10
  • 1
    I studied this material: 'https://javascript.info/bezier-curve'. when I enter three points, the new curve doesn't pass the middle point. It is just placed inside the triangle of three points. Do you have any idea how to make this pass the middle point as well? All I know is just three points coordinates. – LongMa Lin Jun 24 '20 at 08:24
  • 1
    Oh! you mean the control points! but yet given three points on the 2D space *there is* a curve fits them. Here is something might help you then: https://stackoverflow.com/questions/6711707/draw-a-quadratic-b%C3%A9zier-curve-through-three-given-points – Mario Vernari Jun 24 '20 at 08:52
  • Thank you for you answer. I had a look at the material you gave me and followed it assuming t=0.5 it gave me the new control point ( 800, 100 ) but I still get the wrong result. Please have a look at 'https://ibb.co/K0zqLPr' and tell me where I am wrong. Thanks in advance – LongMa Lin Jun 24 '20 at 09:24
  • 1
    I found a complete example that should make what you want: http://jsbin.com/ApitIxo/2/ Please, consider rewarding the original submitter here: https://stackoverflow.com/a/20309900/632445 – Mario Vernari Jun 24 '20 at 10:06
  • thank you. very perfect answer. (thumb up) – LongMa Lin Jun 24 '20 at 11:32

2 Answers2

1

You could draw two curves but make sure the control points are in line so you get a smooth transition. Using this tool I've made an example. His is however not a circle and not an ellipse. enter image description here

JerMah
  • 587
  • 4
  • 15
1

Fit circle to points

Maybe this will help.

The function at the top of the example fitCircleToPoints(x1, y1, x2, y2, x3, y3) will fit a circle to 3 points.

It returns an object

{
   x, y,   // center of circle
   radius, // radius of circle
   CCW,    // true if circle segment is counter clockwise
}

If the 3 points are all on the same line then there is no circle that can fit (radius Infinity is not valid) so the function returns undefined.

function fitCircleToPoints(x1, y1, x2, y2, x3, y3) {
    var x, y, u;
    const slopeA = (x2 - x1) / (y1 - y2); // slope of vector from point 1 to 2
    const slopeB = (x3 - x2) / (y2 - y3); // slope of vector from point 2 to 3
    if (slopeA === slopeB)  { return } // Slopes are same thus 3 points form striaght line. No circle can fit.
    if(y1 === y2){   // special case with points 1 and 2 have same y 
        x = ((x1 + x2) / 2);
        y = slopeB * x + (((y2 + y3) / 2) - slopeB * ((x2 + x3) / 2));  
    }else
    if(y2 === y3){ // special case with points 2 and 3 have same y 
        x = ((x2 + x3) / 2);
        y = slopeA * x + (((y1 + y2) / 2) - slopeA * ((x1 + x2) / 2));  
    } else{
        x = ((((y2 + y3) / 2) - slopeB * ((x2 + x3) / 2)) - (u = ((y1 + y2) / 2) - slopeA * ((x1 + x2) / 2))) / (slopeA - slopeB);
        y = slopeA * x + u;
    }
    
    return {
        x, y, 
        radius: ((x1 - x) ** 2 + (y1 - y) ** 2) ** 0.5,
        CCW: ((x3 - x1) * (y2 - y1) - (y3 - y1) * (x2 - x1)) >= 0,
    };
}





requestAnimationFrame(update);

Math.TAU = Math.PI * 2;
const ctx = canvas.getContext("2d");
const mouse  = {x : 0, y : 0, button : false}   
function mouseEvents(e){
    const bounds = canvas.getBoundingClientRect();
    mouse.x = e.pageX - bounds.left - scrollX;
    mouse.y = e.pageY - bounds.top - scrollY;
    mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
var w = canvas.width, h = canvas.height, cw = w / 2, ch = h / 2;
var nearest, ox, oy, dragging, dragIdx;
const points = [10,110,200,100,400,110];
function drawPoint(x, y, rad, col = "black") {
     ctx.strokeStyle = col;
     ctx.beginPath();
     ctx.arc(x, y, rad, 0, Math.TAU);
     ctx.stroke();
 }
function drawLines(idx, col = "black") {
     ctx.strokeStyle = col;
     ctx.beginPath();
     ctx.lineTo(points[idx++], points[idx++]);
     ctx.lineTo(points[idx++], points[idx++]);
     ctx.lineTo(points[idx++], points[idx++]);
     ctx.stroke();
} 
function drawPoints() {
  var i = 0, x, y;
  nearest = - 1;
  var minDist = 20;
  while (i < points.length) {
     drawPoint(x = points[i++], y = points[i++], 4);
     const dist = (x - mouse.x) ** 2 + (y - mouse.y) ** 2;
     if (dist < minDist) {
        minDist = dist;
        nearest = i - 2;
     }
  }
}

function update(){
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    if(w !== innerWidth || h !== innerHeight){
        cw = (w = canvas.width = innerWidth) / 2;
        ch = (h = canvas.height = innerHeight) / 2;
    }else{
        ctx.clearRect(0,0,w,h);
    }
    canvas.style.cursor = "default";
    drawPoints();
    if (nearest > -1) {
      if (mouse.button) {
        if (!dragging) {
            dragging = true;
            ox = points[nearest] - mouse.x;
            oy = points[nearest+1] - mouse.y;
            dragIdx = nearest;
        }
      } else {
          canvas.style.cursor = "move";
      }
      
      
      drawPoint(points[nearest], points[nearest + 1], 6, "red")
    }
    if (dragging) {
        if (!mouse.button) {
            dragging = false;
        } else {
            points[dragIdx] = mouse.x + ox;
            points[dragIdx + 1] = mouse.y + oy
            canvas.style.cursor = "none";
        }
    }
    
    drawLines(0, "#0002");
  
      const circle = fitCircleToPoints(points[0], points[1], points[2], points[3], points[4], points[5]);

    if (circle) {
        ctx.strokeStyle = "#000";
        const ang1 = Math.atan2(points[1] - circle.y, points[0]- circle.x);
        const ang2 = Math.atan2(points[5] - circle.y, points[4]- circle.x);
        ctx.beginPath();
        ctx.arc(circle.x, circle.y, circle.radius, ang1, ang2, circle.CCW);
        ctx.stroke();
    }
    
    requestAnimationFrame(update);
}
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>
Use mouse to move points.
Blindman67
  • 41,565
  • 7
  • 47
  • 102