Draw angle arc inside the shape[steep angle]

2019-08-30 19:38发布

问题:

i have a four points. its draggable. at any point i click 'draw' button i want to show the angles. i tried but it will draw arc outside for steep angle. but i want to draw the arc inside the shape at any point.

$(document).ready(function () {
var c = document.getElementById('canvas');
var ctx = c.getContext("2d");
var a1 = parseInt($("#p1").css("left")) - 5;
var a2 = parseInt($("#p1").css("top")) - 5;
var b1 = parseInt($("#p2").css("left")) - 5;
var b2 = parseInt($("#p2").css("top")) - 5;
var c1 = parseInt($("#p3").css("left")) - 5;
var c2 = parseInt($("#p3").css("top")) - 5;
var d1 = parseInt($("#p4").css("left")) - 5;
var d2 = parseInt($("#p4").css("top")) - 5;

ctx.beginPath();
ctx.moveTo(a1, a2)
ctx.lineTo(b1, b2)
ctx.lineTo(c1, c2)
ctx.lineTo(d1, d2)
ctx.lineTo(a1, a2)
ctx.fillStyle = '#E6E0EC';
ctx.fill();
ctx.strokeStyle = "#604A7B";
ctx.lineWidth = "3"
ctx.stroke();
ctx.closePath();
$("#p1,#p2,#p3,#p4").draggable({
    drag: function () {

        a1 = parseInt($("#p1").css("left")) - 5;
        a2 = parseInt($("#p1").css("top")) - 5;
        b1 = parseInt($("#p2").css("left")) - 5;
        b2 = parseInt($("#p2").css("top")) - 5;
        c1 = parseInt($("#p3").css("left")) - 5;
        c2 = parseInt($("#p3").css("top")) - 5;
        d1 = parseInt($("#p4").css("left")) - 5;
        d2 = parseInt($("#p4").css("top")) - 5;

        ctx.clearRect(0, 0, 500, 500);
        ctx.beginPath();
        ctx.moveTo(a1, a2)
        ctx.lineTo(b1, b2)
        ctx.lineTo(c1, c2)
        ctx.lineTo(d1, d2)
        ctx.lineTo(a1, a2)
        ctx.fillStyle = '#E6E0EC';
        ctx.fill();
        ctx.strokeStyle = "#604A7B";
        ctx.lineWidth = "3"
        ctx.stroke();
        ctx.closePath();
    }

});

$("#draw").click(function () {

    var dx1 = a1 - b1;
    var dy1 = a2 - b2;
    var dx2 = c1 - b1;
    var dy2 = c2 - b2;
    var ax1 = Math.atan2(dy1, dx1);
    var ax2 = Math.atan2(dy2, dx2);
    var a = parseInt((ax2 - ax1) * 180 / Math.PI + 360) % 360;

    /// detect if arc is inside or outside polygon:
    var aa = ax1 + 0.25;
    var x = b1 + 20 * Math.cos(aa);
    var y = b2 + 20 * Math.sin(aa);
    var isInside = ctx.isPointInPath(x, y);

    ctx.save();
    ctx.beginPath();
    ctx.moveTo(b1, b2);        
    ctx.arc(b1, b2, 20, ax1, ax2, !isInside);

    ctx.closePath();
    ctx.fillStyle = "red";
    ctx.globalAlpha = 0.25;
    ctx.fill();
    ctx.restore();
    ctx.fillStyle = "black";
    ctx.fillText((isInside ? a : 360-a) + '°', b1 + 15, b2);

});
});

please lookout the fiddle:http://jsfiddle.net/AbdiasSoftware/BKK5z/6/

Thank in advance.

in chrome i get result like this

in firefox i get result like this

回答1:

Ok, turns out the problem is not so much related to the point-in-path but rather with path itself.

The problem

When there where only a single arc drawn the previous path was used to detect if the point was inside the polygon or not.

When the other three arcs are added the previous path now becomes the last drawn arc instead. This way the inside polygon detection will not be able to return the proper state as it will test with the arc instead.

Solution

Updated fiddle here

Re-factor the code so that the original path of the polygon can be recreated before each check. This is a relative fast process as we don't have to draw anything to canvas to get a valid path (the costly part is the drawing itself).

Do the following to fix -

First create a generic function to create the path. This method can be used for rendering as well as checking path only:

function createPath(a1,a2, b1, b2, c1, c2, d1, d2) {
    ctx.beginPath();
    ctx.moveTo(a1, a2);
    ctx.lineTo(b1, b2);
    ctx.lineTo(c1, c2);
    ctx.lineTo(d1, d2);
    //ctx.lineTo(a1, a2); /// not needed
    ctx.closePath();
}

Now modify the draw method:

drag: function () {

    a1 = parseInt($("#p1").css("left")) - 5;
    ... snipped ...

    ctx.clearRect(0, 0, 500, 500);
    createPath(a1, a2, b1, b2, c1, c2, d1, d2);  /// use here

    ctx.fillStyle = '#E6E0EC';
    ctx.fill();
    ... snipped ...
}

This will render the polygon as before.

Now the key thing, recreate the path for each arc that is drawn but without rendering it:

$("#draw").click(function () {
    createPath(a1, a2, b1, b2, c1, c2, d1, d2);
    draw(a1,b1,c1,a2,b2,c2);

    createPath(a1, a2, b1, b2, c1, c2, d1, d2);
    draw(b1,c1,d1,b2,c2,d2);

    createPath(a1, a2, b1, b2, c1, c2, d1, d2);
    draw(c1,d1,a1,c2,d2,a2);

    createPath(a1, a2, b1, b2, c1, c2, d1, d2);
    draw(d1, a1, b1, d2, a2, b2);

});

A second minor issue is the "sensitivity" of that infamous delta value. Up-adjust it a bit again to ~0.20-ish and you should be good to go.

As I mentioned in the previous answer, the ideal solution for this value is to calculate an "average" between the two angles so the angle is in the middle of the arc.



回答2:

[ Updated answer with demo that allows draggable corner points ]

Here is a demo showing inside angles where you can drag the corner points:

http://jsfiddle.net/m1erickson/6ujbL/

Here is a demo showing outside angles where you can drag the corner points:

http://jsfiddle.net/m1erickson/9kGmw/

If you want to draw an angle-symbol on an inside angle you can call this function by supplying corner (vertex) points in counter-clockwise order: point3, point2, point1:

function drawAngleSymbol(pt3,pt2,pt1){ ... }

If you want to draw an angle-symbol on an outside angle you can call this same function and supplying corner points in clockwise order: point1, point2, point3:

function drawAngleSymbol(pt1,pt2,pt3){ ... }