4

Here I have drawn some arcs using Konvajs library, but I cannot get their width and height after the objects have been drawn, How can I do that? for quick read of code:

function drawSurface(idnumber, radius, x, y, startAngleParam, endAngleParam) {
var borderbold = 5;
var surface;
if (typeof startAngleParam !== 'undefined') {
    surface = new Konva.Shape({
        x: x,
        y: y,
        fill: '#ccc',
        stroke: "#ccc",
        strokeWidth: 8,
        id: idnumber,
        opacity: 1,
        drawFunc: function (context) {
            var startAngle = startAngleParam * Math.PI;
            var endAngle = (startAngleParam + 0.5 + endAngleParam) * Math.PI;
            var counterClockwise = false;
            context.beginPath();
            context.arc(0, 0, radius, startAngle, endAngle, counterClockwise);
            context.setAttr("lineWidth", borderbold);
            context.stroke();
            context.fillStrokeShape(this);
        }
    });
}
else {
    surface = new Konva.Circle({
        x: x,
        y: y,
        radius: radius,
        fill: '#ccc',
        strokeWidth: 3,
        id: idnumber,
        opacity: 1
    });
}
return surface;
}

Please support your answer with a code example.

Mahdi Alkhatib
  • 1,954
  • 1
  • 29
  • 43

2 Answers2

2

Find the bounding box of your arc and then calculate the width & height from the bounding box.

enter image description here

Geometrically, the only 5 possible bounding box corners are:

  • the arc centerpoint,
  • the point on the arc (if any) at 0 degrees (0 radians),
  • the point on the arc (if any) at 90 degrees (PI/2 radians),
  • the point on the arc (if any) at 180 degrees (PI radians),
  • the point on the arc (if any) at 270 degrees (PI*3/2 radians),

From these possible bounding box points, find the minimum X, minimum Y, maximum X & maximum Y. The [minX,minY] will be the top left corner of the bounding box. The [maxX,maxY] will be the bottom right corner of the bounding box.

Your arc width will be maxX-minX and height will be maxY-minY.

Here's example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;


var PI=Math.PI;
var cx=150;
var cy=150;
var radius=75;
var startAngle=-PI/4;
var endAngle=PI/3;

ctx.beginPath();
ctx.moveTo(cx,cy);
ctx.arc(cx,cy,radius,startAngle,endAngle);
ctx.closePath();
ctx.fillStyle='skyblue';
ctx.fill();
ctx.strokeStyle='lightgray';
ctx.lineWidth=3;
ctx.stroke();

var bb=arcBounds(cx,cy,radius,startAngle,endAngle);
ctx.strokeStyle='red';
ctx.lineWidth=1;
ctx.strokeRect(bb.x,bb.y,bb.width,bb.height);


function arcBounds(cx,cy,radius,startAngle,endAngle){
  var minX=1000000;
  var minY=1000000;
  var maxX=-1000000;
  var maxY=-1000000;

  var possibleBoundingPoints=[]
  // centerpoint
  possibleBoundingPoints.push({x:cx,y:cy});
  // starting angle
  possibleBoundingPoints.push(arcpoint(cx,cy,radius,startAngle));
  // ending angle
  possibleBoundingPoints.push(arcpoint(cx,cy,radius,endAngle));
  // 0 radians
  if(0>=startAngle && 0<=endAngle){
    possibleBoundingPoints.push(arcpoint(cx,cy,radius,0));
  }
  // PI/2 radians
  var angle=PI/2;
  if(angle>=startAngle && angle<=endAngle){
    possibleBoundingPoints.push(arcpoint(cx,cy,radius,angle));
  }
  // PI radians
  var angle=PI;
  if(angle>=startAngle && angle<=endAngle){
    possibleBoundingPoints.push(arcpoint(cx,cy,radius,angle));
  }
  // PI*3/2 radians
  var angle=PI*3/2;
  if(angle>=startAngle && angle<=endAngle){
    possibleBoundingPoints.push(arcpoint(cx,cy,radius,angle));
  }

  for(var i=0;i<possibleBoundingPoints.length;i++){
    var pt=possibleBoundingPoints[i];
    if(pt.x<minX){minX=pt.x;}
    if(pt.y<minY){minY=pt.y;}
    if(pt.x>maxX){maxX=pt.x;}
    if(pt.y>maxY){maxY=pt.y;}
  }

  return({ x:minX, y:minY, width:maxX-minX, height:maxY-minY });

}


function arcpoint(cx,cy,radius,angle){
  var x=cx+radius*Math.cos(angle);
  var y=cy+radius*Math.sin(angle);
  return({x:x,y:y});
}
body{ background-color: ivory; }
#canvas{border:1px solid blue;}
<canvas id="canvas" width=300 height=300></canvas>
markE
  • 102,905
  • 11
  • 164
  • 176
  • Your explanation was very detailed and awesome, I really thank you for your great effort, I have tried to apply your code on my arc(s) and tuning all parameters but the bounding box shown on a different position with different width and height though!! – Mahdi Alkhatib May 26 '15 at 08:22
  • @MahdiAlkhatib It doesn't work properly with some angles: https://jsfiddle.net/ybpqmve0/ – fabriciofreitag Feb 28 '16 at 03:03
0

Here is a different approach taken from this answer and ported to Javascript:

const PI = Math.PI;
const HALF_PI = Math.PI / 2;
const TWO_PI = Math.PI * 2;
const DEG_TO_RAD = Math.PI / 180;
const RAD_TO_DEG = 180 / Math.PI;

const getQuadrant = (_angle) => {
    const angle = _angle % (TWO_PI);

    if (angle > 0.0 && angle < HALF_PI) return 0;
    if (angle >= HALF_PI && angle < PI) return 1;
    if (angle >= PI && angle < PI + HALF_PI) return 2;
    return 3;
};

// https://stackoverflow.com/a/35977476/461048
const getArcBoundingBox = (ini, end, radius, margin = 0) => {
    const iniQuad = getQuadrant(ini);
    const endQuad = getQuadrant(end);

    const ix = Math.cos(ini) * radius;
    const iy = Math.sin(ini) * radius;
    const ex = Math.cos(end) * radius;
    const ey = Math.sin(end) * radius;

    const minX = Math.min(ix, ex);
    const minY = Math.min(iy, ey);
    const maxX = Math.max(ix, ex);
    const maxY = Math.max(iy, ey);

    const r = radius;
    const xMax = [[maxX, r, r, r], [maxX, maxX, r, r], [maxX, maxX, maxX, r], [maxX, maxX, maxX, maxX]];
    const yMax = [[maxY, maxY, maxY, maxY], [r, maxY, r, r], [r, maxY, maxY, r], [r, maxY, maxY, maxY]];
    const xMin = [[minX, -r, minX, minX], [minX, minX, minX, minX], [-r, -r, minX, -r], [-r, -r, minX, minX]];
    const yMin = [[minY, -r, -r, minY], [minY, minY, -r, minY], [minY, minY, minY, minY], [-r, -r, -r, minY]];

    const x1 = xMin[endQuad][iniQuad];
    const y1 = yMin[endQuad][iniQuad];
    const x2 = xMax[endQuad][iniQuad];
    const y2 = yMax[endQuad][iniQuad];

    const x = x1 - margin;
    const y = y1 - margin;
    const w = x2 - x1 + margin * 2;
    const h = y2 - y1 + margin * 2;

    return { x, y, w, h };
};

jsfiddle: https://jsfiddle.net/brunoimbrizi/y3to5s6n/45/

imbrizi
  • 3,788
  • 1
  • 25
  • 26