How do you calculate the radius of the smallest circle in which a sprite fits?

advertisements

I am making a game in HTML5, JS.

In the game I perform the collisions by circle-shapes since my sprites are rotating.

The player has different shapes since it has different sprites when it holds different weapons.

The radius of the player is half the sprites width / height.

The sprites are resized from 16x16 to 50x50 pixels (50 / 16 = 3.125, so 3.125 times bigger)

My question is: How do you calculate the radius of the smallest circle-shape that the sprite would fit in? Like this:

Normal Sprite: http://imgur.com/wWS1nDy

Red = To calculate radius of (how?), Blue = Radius I use (sprite width or height / 2): http://imgur.com/kytpRPz

(For a better look of the images open them in a image program or something to zoom in please.)


Several steps to getting your answer...

Demo: http://jsfiddle.net/m1erickson/hvT63/

Determine the bounding box of the player-pixels (excluding transparent pixels).

  • Use .getImageData to get the individual pixel data for the image.

  • Examine each vertical column from the left and determine where the first player-pixel occurs.

  • Examine each vertical column from the right and determine where the first player-pixel occurs.

  • Examine each horizontal row from the top and determine where the first player-pixel occurs.

  • Examine each horizontal row from the bottom and determine where the first player-pixel occurs.

Now you have top, left, bottom & right bounding values.

Determine the radius of the bounding box (== half of its diagonal):

var dx=right-left;
var dy=bottom-top;
var radius=Math.sqrt(dx*dx+dy*dy)/2;

So your containing circle has that calculated radius.

Here's example code:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; padding:20px; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    ctx.translate(.5,.5);

    var img=new Image();
    img.crossOrigin="anonymous";
    img.onload=start;
    img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/sprite.png";

    function start(){

        var cw=canvas.width;
        var ch=canvas.height;

        ctx.drawImage(img,cw/2-img.width/2,ch/2-img.height/2);

        var data=ctx.getImageData(0,0,cw,ch).data;
        var leftX=getLeft(data,cw,ch);
        var rightX=getRight(data,cw,ch);
        var topY=getTop(data,cw,ch);
        var bottomY=getBottom(data,cw,ch);
        var w=rightX-leftX;
        var h=bottomY-topY;
        var cx=leftX+w/2;
        var cy=topY+h/2;
        var radius=Math.sqrt(w*w+h*h)/2;

        ctx.beginPath();
        ctx.arc(leftX+w/2,topY+h/2,radius,0,Math.PI*2);
        ctx.closePath();
        ctx.stroke();

        ctx.strokeRect(leftX,topY,w,h);

    }

    function getLeft(data,width,height){
        for(var x=0;x<width;x++)
        for(var y=0;y<height;y++)
        {
            if(data[(width*y+x)*4+3]>0){ return(x); }
        }
    }

    function getRight(data,width,height){
        for(var x=width-1;x>=0;x--)
        for(var y=height-1;y>=0;y--)
        {
            if(data[(width*y+x)*4+3]>0){ return(x); }
        }
    }

    function getTop(data,width,height){
        for(var y=0;y<height;y++)
        for(var x=0;x<width;x++)
        {
            if(data[(width*y+x)*4+3]>0){ return(y); }
        }
    }

    function getBottom(data,width,height){
        for(var y=height-1;y>=0;y--)
        for(var x=width-1;x>=0;x--)
        {
            if(data[(width*y+x)*4+3]>0){ return(y); }
        }
    }

}); // end $(function(){});
</script>

</head>

<body>
    <h4>1. Calc boundingbox of non-transparent pixels<br>2. Calc radius that contains that boundingbox</h4>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>