开发者

Draw a line from a point to opposite tangents on a circle? Cone/wedge shape in AS3

开发者 https://www.devze.com 2023-02-26 19:54 出处:网络
This should be a bit of simple geometry: How do I calculate the points to draw the lines in the code below so that it makes a 2D cone or wedge shape?

This should be a bit of simple geometry: How do I calculate the points to draw the lines in the code below so that it makes a 2D cone or wedge shape?

import flash.geom.Point;

//draw cir开发者_Go百科cle
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

UPDATE:

Thanks guys, I should have mentioned my aim is not to draw a wedge shape, but to draw a line from a random point to the edge of an existing circle.

If you're more comfortable with algebra than actionscript, maybe you could have a look at this graphic and post a formula for me?

Draw a line from a point to opposite tangents on a circle? Cone/wedge shape in AS3


Your question is about Thales theorem (see http://en.wikipedia.org/wiki/Thales%27_theorem).

The following is the theorem modified slightly to work with 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);

The final product:

Draw a line from a point to opposite tangents on a circle? Cone/wedge shape in AS3

With the second circle drawn (you don't actually need to draw in the second circle, you just need its center point and radius)

Draw a line from a point to opposite tangents on a circle? Cone/wedge shape in AS3

Checkout the following SWF to see in action (refresh to see different random circles):

http://megaswf.com/serve/1097652


Let (xP, yP) be the intersection of the tangents, (xC,yY) be the center of the circle, where you are looking for the coordinates (xT,yT) of the tangent points. Further more let T be the vector of the tangent and R be the vector of the radius. Since they are perpendicular, you have R . T = 0.

This gives us

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

Let r be the radius of the circle and let x:=xT-xC, y:=yT-yC, xp:=xP-xC, yp:=yP-yC (basically, we move the circle into (0,0)). The tangent point is on the circle, so you have x²+y²=r² and thus also y=sqrt(r²-x²).

The variable substitution applied to the above equation gives us:

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

Using the circle information we have:

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

This is a quadratic equation with 0, 1 or 2 solutions. 0, if P is in the circle, 1, if P is on the circle and 2, if P is outside the circle.

I won't put the explicit solution here, since it's a hell of a formula and it's a lot easier to write, if you map the variables introduced here to variables in your code as:

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];

Best of luck with this, because I am very bad with calculus, plus it's 04:00 here, so you can bet, there's a mistake somewhere, but it should get you in the right direction ;)


Denote your point as P, the circle centre as M, and the tangent point on the circle as X. Triangle PMT is a right-angled triangle. You might want to sketch this out on paper as you go through it to make it easier to follow.

The position of X is just the position of M plus the radial vector to X i.e. the edge MX. The calculation is then about computing the vector MX.

The vector MX can be decomposed into two perpendicular components. One that is parallel to MP and one that is perpendicular to MP. The first thing to do is obtain unit vectors in those two directions. The first is easy as it's just the normalised version of MP. A perpendicular vector to this is easily obtained in 2D by swapping the components and negating one component. Which component you negate is irrelevant here since you ultimately want both tangent lines.

Now that we have our two unit vectors we need to work out how much of each is required to create the radial vector MX. Denoting the angle PMX by theta we have from simple right-angled triangles that cos(theta)=r/|MP| where r is the radius of your circle and |MP| is the length of MP (which you already calculated to get your unit vector above).

Dropping a perpendicular from X to MP gives another right-angled triangle involving theta whose opposite and adjacent sides represent the two components we want. The lengths of these sides are just r * cos(theta) in the MP direction and r * sin(theta) in the perpendicular direction.

So your final result is essentially

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

for one of the tangent points and

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

for the other. Unit_MP and unit_MP_perp_1/2 are the unit vectors we worked out earlier. The 1/2 version of the perp vector correspond to negating either the first or second components after the swap.

Edit

In terms of the diagram you added, the equation becomes

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

with similar equations (x2,y2). In these equations

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

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

and

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

and so

U1x = -Uy
U1y = Ux

The perpendicular unit vector for the other tangent point would be

U2x = Uy
U2y =-Ux


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

Draws a Bézier between current point and end (x,y) with control (x,y) being the point the curve is bent towards (like the pen tool in Photoshop). Set to half delta X and use Y to adjust its strength.


Here is a snippet for drawing a 2D Wedge shape. You can adjust the startAngle and angle vars to control the angle of the wedge. Radius will determine the width of the shape. This should be used within either a Shape, Sprite, MovieClip or some sub-class that has a graphics object.

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();
0

精彩评论

暂无评论...
验证码 换一张
取 消