Irregular Polygons that can be Inscribed on a Circ

2019-08-13 19:33发布

问题:

I recently asked a question about drawing polygons in an HTML5 Canvas and got an excellent answer I just need to be able to add one feature. The ability to make those two functions (draw_sharp_polygon() and draw_reuleaux_polygon()) draw irregular polygons with random angles that are still inscribed on the same circle. Preferably I'd like to do this by adding one simple Boolean argument that decides whether the polygon to be drawn will be regular or irregular.

Similar to the picture on this Wikipedia page.

回答1:

var canvas = document.getElementById('target');
var context = canvas.getContext('2d');

document.getElementById('trigger').addEventListener('click', generate, false);
generate();

function generate()
{
    var angles = randomAngles(5);
    
    var centerX = 150;
    var centerY = 150;
    var radius = 100;
    
    context.clearRect(0, 0, canvas.width, canvas.height);
    
    context.strokeStyle = 'silver';
    polygon(centerX, centerY, radius, angles);
    context.stroke();
    
    context.strokeStyle = 'silver';
    circle(centerX, centerY, radius);
    context.stroke();
    
    context.strokeStyle = 'black';
    reuleaux(centerX, centerY, radius, angles);
    context.stroke();
}

function randomAngles(numPoints)
{
    var angles = [];
    for (var i = 0; i < numPoints; i++)
    {
        angles.push(Math.random()*2*Math.PI);
    }
    angles.sort();
    return angles;
}

function polygon(centerX, centerY, radius, angles)
{
    var angle3 = angles[angles.length - 1];
    var p3 = { x: centerX + radius * Math.cos(angle3),
               y: centerY + radius * Math.sin(angle3) };
    context.beginPath();
    context.moveTo(p3.x, p3.y);
    for (var i = 0; i < angles.length; i++)
    {
        var angle1 = angle3;
        var p1 = p3;
        angle3 = angles[i];
        p3 = { x: centerX + radius * Math.cos(angle3),
               y: centerY + radius * Math.sin(angle3) };
        
        context.lineTo(p3.x, p3.y);
    }
}

function circle(centerX, centerY, radius)
{
    context.beginPath();
    context.arc(centerX, centerY, radius, 0, 2*Math.PI, false);
}

function reuleaux(centerX, centerY, radius, angles)
{
    // The "previous point" for the first segment
    var angle3 = angles[angles.length - 1];
    var p3 = { x: centerX + radius * Math.cos(angle3),
               y: centerY + radius * Math.sin(angle3) };
    
    context.beginPath();
    context.moveTo(p3.x, p3.y);
    for (var i = 0; i < angles.length; i++)
    {
        // Copy previous point as startpoint.
        var angle1 = angle3;
        var p1 = p3;
        
        // Calculate the new endpoint.
        angle3 = angles[i];
        p3 = { x: centerX + radius * Math.cos(angle3),
               y: centerY + radius * Math.sin(angle3) };
        
        // Angular size of the segment.
        var angleSize = angle3 - angle1;
        if (angleSize < 0) angleSize += 2 * Math.PI;
        
        // Middle-point
        var angle2 = angle1 + angleSize/2;
        var p2 = { x: centerX + radius * Math.cos(angle2),
                   y: centerY + radius * Math.sin(angle2) };
        
        var reuleaux_radius = radius * Math.sqrt(2 + 2*Math.cos(angleSize/2));
        
        context.arcTo(p2.x, p2.y, p3.x, p3.y, reuleaux_radius);
    }
}
<div><button id="trigger">Regenerate</button></div>
<canvas id="target" width="300" height="300"></canvas>