3

キャンバス上のランダムな位置に 200 個の円を作成する以下のコードで、ヒット テスト/オーバーラップ テストを機能させようとしています。円の位置を配列に格納しようとしていて、for ループで別の円が作成されるたびにその配列をチェックしています。ランダムに作成された x と y が既に作成された円に近すぎる場合は、新しい円を取得し続ける必要があります。すでに作成された円に近づきすぎなくなるまで、x と y をランダムにします。

私はそれをwhileループでうまく機能させることができません。

助けてください...

ありがとう

    <script type="text/javascript">

    document.addEventListener("DOMContentLoaded", canvasDraw);


    function canvasDraw () {
    var c = document.getElementById("canvas");
    var w = window.innerWidth;
    var h = window.innerHeight;
    c.width = w;
    c.height = h;

    var ctx = c.getContext("2d");
    ctx.clearRect(0,0,c.width, c.height);
    var abc = 0;

    var colours = new Array ("rgb(0,100,0)", "rgb(51,102,255)");
    var positions = new Array();


    function hitTest(x, y) {
    for(var p in positions) {
            pp = p.split(",");
        pp[0] = parseInt(pp[0]);
        pp[1] = parseInt(pp[1]);

        if(((x > (pp[0] - 24)) && (x < (pp[0] + 24))) && ((y > (pp[1] - 24)) && (y < (pp[1] + 24)))) {

            return true;
        }
    }
    return false;
}


            //Loop 200 times for 200 circles
    for (i=0; i<200; i++) {

        var x = Math.floor(Math.random()*c.width);
        var y = Math.floor(Math.random()*c.height);

        while(hitTest(x, y) == true){
            var x = Math.floor(Math.random()*c.width);
            var y = Math.floor(Math.random()*c.height);
        }

        var pos = x.toString() + "," + y.toString();
        positions.push(pos);

        var radius = 10;
        var r = radius.toString();

        var b = colours[Math.floor(Math.random()*colours.length)];

        circle(ctx,x,y, radius, b);

    }   
   }



    function circle (ctx, x, y, radius, b) {
  ctx.fillStyle = b;
  ctx.beginPath();
  ctx.arc(x, y, radius, 0, Math.PI*2, true);
  ctx.closePath();
  ctx.fill();
   }
  </script>

4

1 に答える 1

3

始める前にいくつかのこと:

  1. new Array()初期の長さを指定しない限り、 で配列を作成しないでください。使用し[]ます。
  2. を使用して配列を反復処理しないでください。カウンター付きfor...inの標準を使用してください。forこれは良い習慣です。
  3. 数値を文字列に変換して数値に戻すことは、無駄でコストがかかります。小さな配列を使用して両方の値を格納します。
  4. 「マジック ナンバー」、つまり正確な値を持つがすぐに認識しにくい数字は使用しないでください。将来のメンテナンスのために、名前付きの「定数」を使用するか、それぞれの近くに意味を示すコメントを付けてください。

では、コードを見てみましょう。

if(((x > (pp[0] - 24)) && (x < (pp[0] + 24))) && ((y > (pp[1] - 24)) && (y < (pp[1] + 24))))

正直、なにこれ?私はそれを不機嫌であいまいなスニペットと呼んでいます。学校で学んだことを思い出してください。

var dx = pp[0] - x, dy = pp[1] - y;
if (dx * dx + dy * dy < 400) return true;

それはもっと明確ではありませんか?

関数全体を見てみましょう。

function canvasDraw () {
    var c = document.getElementById("canvas");
    var w = window.innerWidth;
    var h = window.innerHeight;
    c.width = w;
    c.height = h;

    var ctx = c.getContext("2d");
    ctx.clearRect(0,0,c.width, c.height);
    // Lolwut?
    // var abc = 0;

    var colours = ["rgb(0,100,0)", "rgb(51,102,255)"];
    var positions = [];


    function hitTest(x, y) {
        for (var j = 0; j < positions.length; j++) {
            var pp = positions[j];
            var dx = pp[0] - x, dy = pp[1] - y;
            if (dx * dx + dy * dy < 400) return true;
        }
        return false;
    }


    // You declare the radius once and for all
    var radius = 10;
    // Declare the local scoped variables. You forgot i
    var x, y, i;
    for (i=0; i<200; i++) {

        // How about a do...while instead of a while?
        do {
            var x = Math.floor(Math.random()*c.width);
            var y = Math.floor(Math.random()*c.height);
        // Testing with === is faster, always do it if you know the type
        // I'll let it here, but if the type is boolean, you can avoid testing
        // at all, as in while (hitTest(x, y));
        } while (hitTest(x, y) === true);

        positions.push([x, y]);

        // This instruction is useless
        // var r = radius.toString();

        var b = colours[Math.floor(Math.random()*colours.length)];

        circle(ctx,x,y, radius, b);

    }   
}

ただし、キャンバスのサイズによっては、別の円を作成する余地がなくなり、無限ループに陥る可能性があることに注意してください。半径 10 の円を 200 個、40x40 のボックスの中に入れてみてください。別のテストを行う必要があり、それは複雑になる可能性があります。

于 2012-06-04T21:51:24.413 に答える