计算过椭圆中心的一条线段与椭圆的交点。
效果如下:
1、通过公式计算。

从网上找来的公式。
如果实际应用中椭圆的中心不在坐标原点,可以通过坐标变换将其移至坐标原点。
因为我们的线段过椭圆的中心,所以要么与椭圆有交点,要么无交点,不存在相切的情况;并且由于是线段,最多只有一个交点。
由于线段过椭圆中心(即坐标原点,因为我们平移了坐标轴),所以直线方程中,b=0。
线段的一个端点是固定的(坐标原点),判断椭圆和线段有没有交点可以更简单。只需要判断线段的另一个端点和椭圆的关系即可。点在椭圆内,无交点;点在椭圆上,端点就是交点;点在椭圆外,有交点。
判断点和椭圆的位置关系很简单,比如点的坐标是(x0,y0),将点的坐标代入椭圆方程。
x02/m2+y02/n2<1,点在椭圆内;
x02/m2+y02/n2=1,点在椭圆上;
x02/m2+y02/n2>1,点在椭圆外;
先判断椭圆和线段是否有交点,有交点且交点不是线段端点,再通过公式计算。
2、数值法
我们发现判断点和椭圆的位置关系很简单。
那么,如果线段和椭圆有交点,通过二分法可以很快的逼近交点坐标。查找过程如下图。

对比:
公式法得到的结果精确,速度快,但是核心公式相对来说比较复杂;
数值法只能得到近似解,速度相对较慢,但是核心公式简单。
代码如下:
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
/**
* @author hanyeah
*/
public class Main extends MovieClip {
public function Main() {
// constructor code
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function enterFrameHandler(e:Event):void
{
var a:Number = 100;
var b:Number = 50;
var ox:Number=275;
var oy:Number = 200;
var p:Point = calEllipseLineIntersectPoint2(a, b, ox, oy, new Point(mouseX, mouseY));
graphics.clear();
graphics.lineStyle(1, 0x000000);
graphics.drawEllipse(ox - a, oy - b, 2 * a, 2 * b);
graphics.lineStyle(1, 0xff0000);
graphics.moveTo(ox, oy);
graphics.lineTo(mouseX, mouseY);
if (p!=null){
graphics.lineStyle(1, 0x00ff00);
graphics.beginFill(0x00ff00, 1.0);
graphics.drawCircle(p.x, p.y, 5);
graphics.endFill();
}
}
/**
* 线段和椭圆的交点。数值法。
* @param a
* @param b
* @param offX
* @param offY
* @param p
* @return
*/
public function calEllipseLineIntersectPoint(a:Number, b:Number, offX:Number, offY:Number, p:Point):Point{
var xM:Number = p.x - offX;
var yM:Number = p.y - offY;
var r:Number = calEllipsePointRelationship(a, b, xM, yM);
if (r<0){
return null;
}
else if(r==0){
return new Point(xM+offX,yM+offY);
}
else{
var xL:Number = 0;
var yL:Number = 0;
var xR:Number = xM;
var yR:Number = yM;
while (true){
xM = (xL + xR) / 2;
yM = (yL + yR) / 2;
if (distance(xL,yL,xR,yR)<1){
break;
}
var r0:Number = calEllipsePointRelationship(a, b, xM, yM);
if (r0==0){
break;
}
else if (r0<0){
xL = xM;
yL = yM;
}
else{
xR = xM;
yR = yM;
}
}
return new Point(xM+offX,yM+offY);
}
return null;
}
public function calEllipsePointRelationship(a:Number, b:Number,x0:Number,y0:Number):Number{
return x0 * x0 / (a * a) + y0 * y0 / (b * b)-1;
}
public function distance(x0:Number,y0:Number,x1:Number,y1:Number):Number{
return Math.sqrt((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1));
}
/**
* 线段和椭圆的交点。公式法。
* @param a
* @param b
* @param offX
* @param offY
* @param p
* @return
*/
public function calEllipseLineIntersectPoint2(a:Number, b:Number, offX:Number, offY:Number, p:Point):Point{
var x1:Number = p.x - offX;
var y1:Number = p.y - offY;
var r:Number = calEllipsePointRelationship(a, b, x1, y1);
if (r<0){
return null;
}
else if(r==0){
return new Point(x1+offX,y1+offY);
}
else{
if (x1 == 0){
return new Point(0+offX,(y1>0?b:-b)+offY);
}
var k:Number = y1 / x1;
var x2:Number = a * b / Math.sqrt(b * b + k * k * a * a);
if (x1*x2<0){
x2 =-x2;
}
return new Point(x2 + offX, k * x2 + offY);
}
return null;
}
}
}
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。