0

ポイントから楕円の境界線までの最短距離を見つけることができるように、円を楕円に変換する方程式を探しています。円と点の間の距離の方程式を見つけましたが、それを楕円で機能するように変換する方法がわかりません。

px と py は点、x と y は円の原点、ray は半径です。

closestCirclePoint: function(px, py, x, y, ray) {
    var tg = (x += ray, y += ray, 0);
    return function(x, y, x0, y0) {
        return Math.sqrt((x -= x0) * x + (y -= y0) * y);
    }(px, py, x, y) > ray
      ? {x: Math.cos(tg = Math.atan2(py - y, px - x)) * ray + x,
         y: Math.sin(tg) * ray + y}
      : {x: px, y: py};
}
4

1 に答える 1

2

[回答への追加: 楕円上の最近点を近似する方法]

実用性のために完璧さを犠牲にしても構わないと思っているなら…</p>

目標点に「近い」楕円点を計算する方法を次に示します。

ここに画像の説明を入力

メソッド:

  • 目標点が楕円のどの象限にあるかを決定します。
  • その象限の開始ラジアン角と終了ラジアン角を計算します。
  • その楕円象限に沿ってポイントを計算します (「楕円を歩く」)。
  • 計算された楕円点ごとに、ターゲット ポイントまでの距離を計算します。
  • ターゲットまでの距離が最短の楕円点を保存します。

短所:

  • 結果は概算です。
  • これは、数学的に完全な計算よりもエレガントではありません。力ずくの方法を使用します。
  • (ただし、効率的な力ずくの方法)。

長所:

  • 近似結果はかなり良好です。
  • パフォーマンスはかなり良いです。
  • 計算ははるかに簡単です。
  • 計算は、数学的に完全な計算よりも (おそらく) 高速です。
  • (約 20 の三角関数の計算と加算/減算のコストがかかります)
  • より高い精度が必要な場合は、1 つの変数を変更するだけです
  • (もちろん、精度が高いほど、より多くの計算が必要になります)

パフォーマンスに関する注意:

  • 楕円上のすべての「歩行点」を事前に計算して、パフォーマンスをさらに向上させることができます。

このメソッドのコードは次のとおりです。

    // calc a point on the ellipse that is "near-ish" the target point
    // uses "brute force"
    function getEllipsePt(targetPtX,targetPtY){

        // calculate which ellipse quadrant the targetPt is in
        var q;
        if(targetPtX>cx){
            q=(targetPtY>cy)?0:3;
        }else{
            q=(targetPtY>cy)?1:2;
        }

        // calc beginning and ending radian angles to check
        var r1=q*halfPI;
        var r2=(q+1)*halfPI;
        var dr=halfPI/steps;
        var minLengthSquared=200000000;
        var minX,minY;

        // walk the ellipse quadrant and find a near-point
        for(var r=r1;r<r2;r+=dr){

            // get a point on the ellipse at radian angle == r
            var ellipseX=cx+radiusX*Math.cos(r);
            var ellipseY=cy+radiusY*Math.sin(r);

            // calc distance from ellipsePt to targetPt
            var dx=targetPtX-ellipseX;
            var dy=targetPtY-ellipseY;
            var lengthSquared=dx*dx+dy*dy;

            // if new length is shortest, save this ellipse point
            if(lengthSquared<minLengthSquared){
                minX=ellipseX;
                minY=ellipseY;
                minLengthSquared=lengthSquared;
            }
        }

        return({x:minX,y:minY});
    }

ここにコードとフィドルがあります: http://jsfiddle.net/m1erickson/UDBkV/

<!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; }
    #wrapper{
        position:relative;
        width:300px;
        height:300px;
    }
    #canvas{
        position:absolute; top:0px; left:0px;
        border:1px solid green;
        width:100%;
        height:100%;
    }
    #canvas2{
        position:absolute; top:0px; left:0px;
        border:1px solid red;
        width:100%;
        height:100%;
    }
</style>

<script>
$(function(){

    // get canvas references
    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var canvas2=document.getElementById("canvas2");
    var ctx2=canvas2.getContext("2d");

    // calc canvas position on page
    var canvasOffset=$("#canvas").offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;


    // define the ellipse
    var cx=150;
    var cy=150;
    var radiusX=50;
    var radiusY=25;
    var halfPI=Math.PI/2;
    var steps=8; // larger == greater accuracy


    // get mouse position
    // calc a point on the ellipse that is "near-ish"
    // display a line between the mouse and that ellipse point
    function handleMouseMove(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);

      // Put your mousemove stuff here
      var pt=getEllipsePt(mouseX,mouseY);

      // testing: draw results
      drawResults(mouseX,mouseY,pt.x,pt.y);
    }


    // calc a point on the ellipse that is "near-ish" the target point
    // uses "brute force"
    function getEllipsePt(targetPtX,targetPtY){

        // calculate which ellipse quadrant the targetPt is in
        var q;
        if(targetPtX>cx){
            q=(targetPtY>cy)?0:3;
        }else{
            q=(targetPtY>cy)?1:2;
        }

        // calc beginning and ending radian angles to check
        var r1=q*halfPI;
        var r2=(q+1)*halfPI;
        var dr=halfPI/steps;
        var minLengthSquared=200000000;
        var minX,minY;

        // walk the ellipse quadrant and find a near-point
        for(var r=r1;r<r2;r+=dr){

            // get a point on the ellipse at radian angle == r
            var ellipseX=cx+radiusX*Math.cos(r);
            var ellipseY=cy+radiusY*Math.sin(r);

            // calc distance from ellipsePt to targetPt
            var dx=targetPtX-ellipseX;
            var dy=targetPtY-ellipseY;
            var lengthSquared=dx*dx+dy*dy;

            // if new length is shortest, save this ellipse point
            if(lengthSquared<minLengthSquared){
                minX=ellipseX;
                minY=ellipseY;
                minLengthSquared=lengthSquared;
            }
        }

        return({x:minX,y:minY});
    }

    // listen for mousemoves
    $("#canvas").mousemove(function(e){handleMouseMove(e);});



    // testing: draw the ellipse on the background canvas
    function drawEllipse(){
        ctx2.beginPath()
        ctx2.moveTo(cx+radiusX,cy)
        for(var r=0;r<2*Math.PI;r+=2*Math.PI/60){
            var ellipseX=cx+radiusX*Math.cos(r);
            var ellipseY=cy+radiusY*Math.sin(r);
            ctx2.lineTo(ellipseX,ellipseY)
        }
        ctx2.closePath();
        ctx2.lineWidth=5;
        ctx2.stroke();
    }

    // testing: draw line from mouse to ellipse
    function drawResults(mouseX,mouseY,ellipseX,ellipseY){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        ctx.beginPath();
        ctx.moveTo(mouseX,mouseY);
        ctx.lineTo(ellipseX,ellipseY);
        ctx.lineWidth=1;
        ctx.strokeStyle="red";
        ctx.stroke();
    }


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

</head>

<body>
    <div id="wrapper">
        <canvas id="canvas2" width=300 height=300></canvas>
        <canvas id="canvas" width=300 height=300></canvas>
    </div>
</body>
</html>

元の回答

円と楕円の関係は次のとおりです

水平に配置された楕円の場合:

ここに画像の説明を入力

(x x) / (a a) + (y y) / (b b) == 1;

どこでaは水平頂点までの長さで、どこでbは垂直頂点までの長さです。

円と楕円の関係:

a==b の場合、楕円は円です。

でも...!

任意の点から楕円上の点までの最小距離を計算するには、円よりもはるかに多くの計算が必要です。

計算へのリンクは次のとおりです (DistancePointEllipseEllipsoid.cpp をクリックします)。

http://www.geometrictools.com/SampleMathematics/DistancePointEllipseEllipsoid/DistancePointEllipseEllipsoid.html

于 2013-08-21T16:52:04.087 に答える