HTML5: Get click position from canvas arc

1.4k views Asked by At

See this jsFiddle post for a working arc drawing; thanks to Simon Sarris for the fix in my previous questions.

I'm using the KineticJS plugin to create shapes and make use of event handlers. Assuming you clicked somewhere on the arc and the arc knew where you clicked (x, y), how could those 2 coordinates be used to determine a percentage?

When you click anywhere, the total percentage is always 100%.

Addin

To make this simpler, what could I do to (x, y) to virtually bend the object so that x goes from 0 to maximum x?

3

There are 3 answers

0
Joe On BEST ANSWER

Simple trigonometry. sin(angle) = opposite / adjacent. opposite is the y value and adjacent is the x value. So Math.asin((xx - x) / (yy - y)) where xx and yy are the coords of the centre of the arc. That gives you the angle, which you can then divide 2 * Math.PI by.

Off the top of my head I can't remember what happens with negative numbers. You may have to take the Math.abs value of the arguments, then work out which quadrant the click is in (easy to do by using < and >) and add Math.PI / 2 for each one.

0
sdrm On

Did not really test it, but technically, it should work:

// Where x1 and y1 should be the coordinates of the arc's center
function angle(x1, y1, x2, y2) {
    // Calculate a ยท b
    var nominator = x1 * x2 + y1 * y2;

    // Calculate ||a|| ||b||
    var denominator = Math.sqrt(x1*x1 + y1*y1) * Math.sqrt(x2*x2 + y2*y2);
    if (denominator == 0) return 0; // Indifinite angle

    // Return the angle
    return Math.acos(nominator / denominator);
}

// Returns a percent, might be negative
var percent = angle(0, 0, mouseX, mouseY) / (2*Math.PI);

Edit

For negative numbers you can try adding 1 since it's in [-1, 1] range

if (percent < 0) percent += 1;
0
Kent Fuller On

This includes a check if the mouse is inside the arc:

// Return range is 0 to Math.PI * 2
function get_mouse_circle_angle(origin_x, origin_y, mouse_x, mouse_y) {
    var mouse_angle = Math.atan2(mouse_y - origin_y, mouse_x - origin_x);
    if (mouse_angle < 0) {
        mouse_angle = (Math.PI * 2) + mouse_angle;
    }
    return mouse_angle;
}

// Return range is [0, 1)
// 0/1 is 3 oclock
function get_mouse_circle_percent(origin_x, origin_y, mouse_x, mouse_y) {
    var mouse_angle = get_mouse_circle_angle(origin_x, origin_y, mouse_x, mouse_y);
    return mouse_angle / (2 * Math.PI);
}

function get_mouse_arc_pos(origin_x, origin_y, mouse_x, mouse_y, radius, thickness) {
    var mouse_angle = Math.atan2(mouse_y - origin_y, mouse_x - origin_x);
    if (mouse_angle < 0) {
        mouse_angle = (Math.PI * 2) + mouse_angle;
    }
    var mouse_percent = mouse_angle / (2 * Math.PI);

    var circle_edge_x = origin_x + (radius + thickness / 2) * Math.cos(mouse_angle);
    var circle_edge_y = origin_y + (radius + thickness / 2) * Math.sin(mouse_angle);

    var arc_inside_x = origin_x + (radius - thickness / 2) * Math.cos(mouse_angle);
    var arc_inside_y = origin_y + (radius - thickness / 2) * Math.sin(mouse_angle);

    var is_in_circle = true;

    if (mouse_angle <= (2 * Math.PI) * 0.25) {
        if (mouse_x > circle_edge_x || mouse_y > circle_edge_y)
            is_in_circle = false;
    }
    else if (mouse_angle <= (2 * Math.PI) * 0.5) {
        if (mouse_x < circle_edge_x || mouse_y > circle_edge_y)
            is_in_circle = false;
    }
    else if (mouse_angle <= (2 * Math.PI) * 0.75) {
        if (mouse_x < circle_edge_x || mouse_y < circle_edge_y)
            is_in_circle = false;
    }
    else {
        if (mouse_x > circle_edge_x || mouse_y < circle_edge_y)
            is_in_circle = false;
    }

    var is_in_arc = is_in_circle;
    if (is_in_circle) {
        if (mouse_angle <= (2 * Math.PI) * 0.25) {
            if (mouse_x < arc_inside_x || mouse_y < arc_inside_y)
                is_in_arc = false;
        }
        else if (mouse_angle <= (2 * Math.PI) * 0.5) {
            if (mouse_x > arc_inside_x || mouse_y < arc_inside_y)
                is_in_arc = false;
        }
        else if (mouse_angle <= (2 * Math.PI) * 0.75) {
            if (mouse_x > arc_inside_x || mouse_y > arc_inside_y)
                is_in_arc = false;
        }
        else {
            if (mouse_x < arc_inside_x || mouse_y > arc_inside_y)
                is_in_arc = false;
        }
    }

    return {
        angle: mouse_angle,
        percent: mouse_percent,
        is_in_circle: is_in_circle,
        is_in_arc: is_in_arc
    };
}