在圆上从一个点到相反的切线画一条线? AS3中的锥/楔形状

时间:2011-04-15 23:58:56

标签: flash actionscript-3 math geometry

这应该是一个简单的几何:我如何计算在下面的代码中绘制线条的点,以便它形成2D锥形或楔形?

import flash.geom.Point;

//draw circle
var mc=new Sprite()
mc.graphics.lineStyle(0,0)
mc.graphics.drawCircle(0,0,30)
mc.x=mc.y=Math.random()*300+100
addChild(mc)

//draw lines:
graphics.lineStyle(0,0)
var p=new Point(Math.random()*500,Math.random()*400)
graphics.moveTo(p.x, p.y)
graphics.lineTo(mc.x,mc.y) // << should be point on edge of circle
graphics.moveTo(p.x, p.y)
graphics.lineTo(mc.x,mc.y) // << should be point on opposite edge of circle

更新:
谢谢大家,我应该提到我的目标不是绘制楔形,而是从随机点到现有圆的边缘画一条线。

如果你对代数比动作更舒服,也许你可以看看这个图形并为我发布一个公式? tangents

5 个答案:

答案 0 :(得分:2)

你的问题是关于Thales定理(见http://en.wikipedia.org/wiki/Thales%27_theorem)。

以下是与AS3一起使用的略有修改的定理。

import flash.geom.Point;

// The radius of the circle
var r1:Number = 30;
// The center point of the circle
var cp:Number = Math.random() * 300+100;
var c:Point = new Point(cp, cp);

// draw circle
var mc=new Sprite();
mc.graphics.lineStyle(0,0);
mc.graphics.drawCircle(0,0,r1);
mc.x = mc.y = cp;
addChild(mc);

// The point
var p = new Point(Math.random() * 500, Math.random() * 400);

// Calculate points for intesecting circle
var c2:Point = Point.interpolate(c, p, 0.5);
var r2:Number = Point.distance(c2, c);
var d:Number = Point.distance(c, c2);

// Remove comment below to see intersecting circle
//graphics.beginFill(0xFF0000, 0.25);
//graphics.drawCircle(c2.x, c2.y, r2);

var a:Number = (r1*r1 - r2*r2 + d*d) / (2*d);
var p2x:Number = c.x + a * ( c2.x - c.x ) / d;
var p2y:Number = c.y + a * ( c2.y - c.y ) / d;
var h:Number = Math.sqrt(r1*r1 - a*a);

var d1x:Number = p2x + h * ( c2.y - c.y ) / d;
var d1y:Number = p2y - h * ( c2.x - c.x ) / d;
var d2x:Number = p2x - h * ( c2.y - c.y ) / d;
var d2y:Number = p2y + h * ( c2.x - c.x ) / d;

// Draw lines
graphics.lineStyle(1, 0xFF00FF, 0.5);
graphics.moveTo(p.x, p.y);
graphics.lineTo(d1x, d1y);
graphics.moveTo(p.x, p.y);
graphics.lineTo(d2x, d2y);

最终产品:

enter image description here

绘制第二个圆圈(你实际上不需要在第二个圆圈中绘制,只需要它的中心点和半径)

enter image description here

检查以下SWF以查看操作(刷新以查看不同的随机圆圈):

http://megaswf.com/serve/1097652

答案 1 :(得分:1)

将您的点表示为P,将圆心表示为M,将圆上的切点表示为X.三角形PMT是一个直角三角形。您可能希望在阅读时将其绘制在纸上,以便更容易理解。

X的位置正好是M的位置加上X的径向矢量,即边缘MX。然后计算关于计算向量MX。

矢量MX可以分解为两个垂直分量。一个与MP平行且一个与MP垂直的。首先要做的是在这两个方向上获得单位向量。第一个很简单,因为它只是MP的标准化版本。通过交换组件和否定一个组件,可以在2D中容易地获得对此的垂直向量。你否定哪个组件在这里是无关紧要的,因为你最终想要两条切线。

现在我们有了两个单位向量,我们需要计算出创建径向向量MX所需的每个向量。将角度PMX表示为θ,我们从简单的直角三角形中得出cos(θ)= r / | MP |其中r是圆的半径和| MP |是MP的长度(您已计算得到上面的单位向量)。

垂直从X到MP的垂直给出另一个直角三角形,包括theta,其相对和相邻的边代表我们想要的两个分量。这些边的长度只是MP方向的r * cos(θ)和垂直方向的r * sin(θ)。

所以你的最终结果基本上是

X = M + r * cos(theta)* unit_MP + r * sin(theta)* unit_MP_perp_1

用于其中一个切点和

X = M + r * cos(theta)* unit_MP + r * sin(theta)* unit_MP_perp_2

为另一个。 Unit_MP和unit_MP_perp_1 / 2是我们之前制定的单位向量。 perp向量的1/2版本对应于交换后否定第一个或第二个组件。

修改

根据您添加的图表,等式变为

x1 = cx + R * cos(theta)* Ux + R * sin(theta)* U1x
y1 = cy + R * cos(theta)* Uy + R * sin(theta)* U1y

具有相似的方程(x2,y2)。在这些方程中

cos(theta)= r / D
sin(theta)= A / D

其中D = sqrt((px - cx)^ 2 +(py - cy)^ 2)

Ux =(px -cx)/ D
Uy =(py -cy)/ D

等等

U1x = -Uy
U1y = Ux

另一个切点的垂直单位矢量为

U2x = Uy
U2y = -Ux

答案 2 :(得分:1)

(xP, yP)成为切线的交点,(xC,yY)为圆的中心,您可以在此处查找切点的坐标(xT,yT)。更进一步,让T成为切线的矢量,R是半径的矢量。由于它们是垂直的,因此您有R . T = 0

这给了我们

(xT-xC,yT-yC) . (xT-xP, yT-yP) = 0

r成为圆的半径,让x:=xT-xC, y:=yT-yC, xp:=xP-xC, yp:=yP-yC(基本上,我们将圆圈移动到(0,0))。 切点位于圆上,因此您有x²+y²=r²,因此也y=sqrt(r²-x²)

应用于上述等式的变量替换给出了:

(x,y) . (x-xp, y-yp) = 0
x²-xp*x + y²-yp*y = 0

使用我们拥有的圈子信息:

r² -xp*x - yp*sqrt(r²-x²) = 0
r² -xp*x = yp*sqrt(r²-x²)
r^4 - 2*r²*xp*x + xp²*x² = yp²*(r²-x²)
(yp²+xp²)*x² - 2*r²*xp*x + r^4-yp²*r² = 0

now let a:=yp²+xp², b:=2*r²*xp, c:= (r²-yp²)*r²
=> ax² + bx + c = 0

这是quadratic equation,包含0,1或2个解决方案。 0,如果P 圆圈中,1,如果P on 圆圈,则为2,如果P 圆圈。

我不会在这里提出明确的解决方案,因为如果你将这里介绍的变量映射到你的代码中的变量,它是一个公式的地狱而且编写起来容易得多:

var sq:Function = function (f:Number) { return f*f; }, sqrt:Function = Math.sqrt;
var xp:Number = xP-xC, yp:Number = yP-yC,
    a:Number = sq(xp)+sq(yp), b:Number = 2*sq(r)*xp, c:Number = sq(r)*(sq(r)-sq(yp));
var x1:Number = (-b+sqrt(sq(b)-4*a*c)) / (2 * a),
    x2:Number = (-b+sqrt(sq(b)-4*a*c)) / (2 * a);
if (isNan(x1)) return [];
var p1:Point = new Point(x1+cX, sqrt(sq(r)-sq(x1))+cY),//calculate y and undo shift
    p2:Point = new Point(x2+cX, sqrt(sq(r)-sq(x2))+cY);
return p1.equals(p2) ? [p1] : [p1, p2];

祝你好运,因为我对微积分非常不好,加上它是04:00,所以你可以打赌,某处有一个错误,但它应该让你朝着正确的方向前进;)

答案 3 :(得分:0)

graphics.curveTo(controlX,ControlY,endX,endY);

在当前点和结束(x,y)之间绘制一个Bézier,其中控制(x,y)是曲线弯曲的点(就像Photoshop中的钢笔工具一样)。设置为半角X并使用Y调整其强度。

答案 4 :(得分:0)

这是一个用于绘制2D Wedge形状的片段。您可以调整startAngleangle变量来控制楔形的角度。半径将确定形状的宽度。这应该在Shape,Sprite,MovieClip或某个具有图形对象的子类中使用。

var i:int;
var p:Point = new Point();
var g:Graphics = graphics;
var radius:Number = 100;
var startAngle:Number = 130;
var angle:Number = 280;
var segments:Number = 40;
var degrees:Number = ( startAngle + ( angle - startAngle ) ) / segments;

g.beginFill( 0xFF0000, 1 );
for( i = 0; i <= segments; i++ ) 
{
    p.x = Math.cos( ( ( degrees * i ) + startAngle ) * Math.PI / 180 ) * radius;
    p.y = Math.sin( ( ( degrees * i ) + startAngle ) * Math.PI / 180 ) * radius;
    g.lineTo( p.x, p.y );
}
g.endFill();