Related
I am using System.Drawing to plot a bitmap and a graphic, gp. This method is working for me.
I have previously drawn a circle with radius = 250. As below there are 17 precalulated angle(nn) with text printed horizontally to the left(far) and to the right(near) depending on the 90 to 270 line.
I would like to graph the text at the point radially outward from the center starting at radius = 250 using gp.RotateTransform(angle(nn)) or any other method. Obviously RotateTransform did not produce good results.
Dim txtBrush As New SolidBrush(Color.Blue)
Dim hFont As New System.Drawing.Font("Verdana", 10, FontStyle.Regular)
Dim format1 As New StringFormat(StringFormatFlags.NoClip)
Dim format2 As New StringFormat(StringFormatFlags.NoClip)
format1.LineAlignment = StringAlignment.Far
format1.Alignment = StringAlignment.Far
format2.LineAlignment = StringAlignment.Near
format2.Alignment = StringAlignment.Near
For nn = 0 To 17
If angle(nn) >= 90 And angle(nn) <= 270 Then 'gp.RotateTransform(angle(nn))
gp.DrawString(ephemeris(angle(nn)), hFont, txtBrush, 250 * Math.Cos(angle(nn) * Pi / 180) + xCenter, -250 * Math.Sin(angle(nn) * Pi / 180) + yCenter, format1)
Else
gp.DrawString(ephemeris(angle(nn)), hFont, txtBrush, 250 * Math.Cos(angle(nn) * Pi / 180) + xCenter, -250 * Math.Sin(angle(nn) * Pi / 180) + yCenter, format2)
End If
Next nn
Drawn a line from a point A to point B. Let d be offset. Let C be point to be tested.
I am going to do a kind of hit testing around the line with offset.
How can i do the hit testing around the line with the given offset.
Ex: A = (10,10), B (30,30), offset = 2. choose C as any point. Please Refer the image in the link please.
http://s10.postimg.org/6by2dzvax/reference.png
Please help me.
Thanks in advance.
Find offset for C.
e.g. dx1 and dy1. If dy1/dx1=dy/dx then your C hits the line.
For segment you should also check if whether dx1 < dx or dy1 < dy.
In other words, you want to check if that point C is inside a certain rectangle, with dimensions 2*d and |A-B|+2*d. You need to represent the line as u*x+v*y+w=0, this can be accomplished by
u = A.y-B.y
v = B.x-A.x
w = A.x*B.y - A.y * B.x
Then the (signed) distance of C from that line would be
d = (u*C.x + v*C.y +w) / sqrt( u*u+v*v)
You compare abs(d) to your offset.
The next step would be to check the position of C in the direction of the line. To that end you consider the orthogonal line u2*x+v2*y+w2=0 with
u2 = v
v2 = -u
w2 = -u2*(A.x+B.x)/2 - v2*(A.y+B.y)/2
and the distance
d2 = (u2 * C.x + v2 * C.y + w2 ) / sqrt( u2*u2+v2*v2 )
This distance must be compared to something like the length of the line+offset:
abs(d2) < |A-B| / 2 + offset
A convenient trick is to rotate and translate the plane in such a way that the segment AB maps to the segment (0, 0)-(0, L) (just like on the image), L being the segment length.
If you apply the same transform to C, then it a very simple matter to test inclusion in the rectangle.
That useful transform is given by:
x = ((X - XA).(XB - XA) + (Y - YA).(YB - YA)) / L
y = ((X - XA).(YB - YA) - (Y - YA).(XB - XA)) / L
maybe you can use this function to count the shortest distance of the point to the line. If the distance is <= offset, then that point is hitting the line.
private double pointDistanceToLine(PointF line1, PointF line2, PointF pt)
{
var isValid = false;
PointF r = new PointF();
if (line1.Y == line2.Y && line1.X == line2.X)
line1.Y -= 0.00001f;
double U = ((pt.Y - line1.Y ) * (line2.Y - line1.Y )) + ((pt.X - line1.X) * (line2.X - line1.X));
double Udenom = Math.Pow(line2.Y - line1.Y , 2) + Math.Pow(line2.X - line1.X, 2);
U /= Udenom;
r.Y = (float)(line1.Y + (U * (line2.Y - line1.Y ))); r.X = (float)(line1.X + (U * (line2.X - line1.X)));
double minX, maxX, minY , maxY ;
minX = Math.Min(line1.Y , line2.Y );
maxX = Math.Max(line1.Y , line2.Y );
minY = Math.Min(line1.X, line2.X);
maxY = Math.Max(line1.X, line2.X);
isValid = (r.Y >= minX && r.Y <= maxX) && (r.X >= minY && r.X <= maxY );
//return isValid ? r : null;
if (isValid)
{
double result = Math.Pow((pt.X - r.X), 2) + Math.Pow((pt.Y - r.Y), 2);
result = Math.Sqrt(result);
return result;
}
else {
double result1 = Math.Pow((pt.X - line1.X), 2) + Math.Pow((pt.Y - line1.Y), 2);
result1 = Math.Sqrt(result1);
double result2 = Math.Pow((pt.X - line2.X), 2) + Math.Pow((pt.Y - line2.Y), 2);
result2 = Math.Sqrt(result2);
return Math.Min(result1, result2);
}
}
If I want to generate a bunch of points distributed uniformly around a circle, I can do this (python):
r = 5 #radius
n = 20 #points to generate
circlePoints = [
(r * math.cos(theta), r * math.sin(theta))
for theta in (math.pi*2 * i/n for i in range(n))
]
However, the same logic doesn't generate uniform points on an ellipse: points on the "ends" are more closely spaced than points on the "sides".
r1 = 5
r2 = 10
n = 20 #points to generate
ellipsePoints = [
(r1 * math.cos(theta), r2 * math.sin(theta))
for theta in (math.pi*2 * i/n for i in range(n))
]
Is there an easy way to generate equally spaced points around an ellipse?
This is an old thread, but since I am seeking the same task of creating evenly spaced points along and ellipse and was not able to find an implementation, I offer this Java code that implements the pseudo code of Howard:
package com.math;
public class CalculatePoints {
public static void main(String[] args) {
// TODO Auto-generated method stub
/*
*
dp(t) = sqrt( (r1*sin(t))^2 + (r2*cos(t))^2)
circ = sum(dp(t), t=0..2*Pi step 0.0001)
n = 20
nextPoint = 0
run = 0.0
for t=0..2*Pi step 0.0001
if n*run/circ >= nextPoint then
set point (r1*cos(t), r2*sin(t))
nextPoint = nextPoint + 1
next
run = run + dp(t)
next
*/
double r1 = 20.0;
double r2 = 10.0;
double theta = 0.0;
double twoPi = Math.PI*2.0;
double deltaTheta = 0.0001;
double numIntegrals = Math.round(twoPi/deltaTheta);
double circ=0.0;
double dpt=0.0;
/* integrate over the elipse to get the circumference */
for( int i=0; i < numIntegrals; i++ ) {
theta += i*deltaTheta;
dpt = computeDpt( r1, r2, theta);
circ += dpt;
}
System.out.println( "circumference = " + circ );
int n=20;
int nextPoint = 0;
double run = 0.0;
theta = 0.0;
for( int i=0; i < numIntegrals; i++ ) {
theta += deltaTheta;
double subIntegral = n*run/circ;
if( (int) subIntegral >= nextPoint ) {
double x = r1 * Math.cos(theta);
double y = r2 * Math.sin(theta);
System.out.println( "x=" + Math.round(x) + ", y=" + Math.round(y));
nextPoint++;
}
run += computeDpt(r1, r2, theta);
}
}
static double computeDpt( double r1, double r2, double theta ) {
double dp=0.0;
double dpt_sin = Math.pow(r1*Math.sin(theta), 2.0);
double dpt_cos = Math.pow( r2*Math.cos(theta), 2.0);
dp = Math.sqrt(dpt_sin + dpt_cos);
return dp;
}
}
(UPDATED: to reflect new packaging).
An efficient solution of this problem for Python can be found in the numeric branch FlyingCircus-Numeric, derivated from the FlyingCircus Python package.
Disclaimer: I am the main author of them.
Briefly, the (simplified) code looks (where a is the minor axis, and b is the major axis):
import numpy as np
import scipy as sp
import scipy.optimize
def angles_in_ellipse(
num,
a,
b):
assert(num > 0)
assert(a < b)
angles = 2 * np.pi * np.arange(num) / num
if a != b:
e2 = (1.0 - a ** 2.0 / b ** 2.0)
tot_size = sp.special.ellipeinc(2.0 * np.pi, e2)
arc_size = tot_size / num
arcs = np.arange(num) * arc_size
res = sp.optimize.root(
lambda x: (sp.special.ellipeinc(x, e2) - arcs), angles)
angles = res.x
return angles
It makes use of scipy.special.ellipeinc() which provides the numerical integral along the perimeter of the ellipse, and scipy.optimize.root()
for solving the equal-arcs length equation for the angles.
To test that it is actually working:
a = 10
b = 20
n = 16
phi = angles_in_ellipse(n, a, b)
print(np.round(np.rad2deg(phi), 2))
# [ 0. 17.55 36.47 59.13 90. 120.87 143.53 162.45 180. 197.55
# 216.47 239.13 270. 300.87 323.53 342.45]
e = (1.0 - a ** 2.0 / b ** 2.0) ** 0.5
arcs = sp.special.ellipeinc(phi, e)
print(np.round(np.diff(arcs), 4))
# [0.3022 0.2982 0.2855 0.2455 0.2455 0.2855 0.2982 0.3022 0.3022 0.2982
# 0.2855 0.2455 0.2455 0.2855 0.2982]
# plotting
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca()
ax.axes.set_aspect('equal')
ax.scatter(b * np.sin(phi), a * np.cos(phi))
plt.show()
You have to calculate the perimeter, then divide it into equal length arcs. The length of an arc of an ellipse is an elliptic integral and cannot be written in closed form so you need numerical computation.
The article on ellipses on wolfram gives you the formula needed to do this, but this is going to be ugly.
A possible (numerical) calculation can look as follows:
dp(t) = sqrt( (r1*sin(t))^2 + (r2*cos(t))^2)
circ = sum(dp(t), t=0..2*Pi step 0.0001)
n = 20
nextPoint = 0
run = 0.0
for t=0..2*Pi step 0.0001
if n*run/circ >= nextPoint then
set point (r1*cos(t), r2*sin(t))
nextPoint = nextPoint + 1
next
run = run + dp(t)
next
This is a simple numerical integration scheme. If you need better accuracy you might also use any other integration method.
I'm sure this thread is long dead by now, but I just came across this issue and this was the closest that came to a solution.
I started with Dave's answer here, but I noticed that it wasn't really answering the poster's question. It wasn't dividing the ellipse equally by arc lengths, but by angle.
Anyway, I made some adjustments to his (awesome) work to get the ellipse to divide equally by arc length instead (written in C# this time). If you look at the code, you'll see some of the same stuff -
void main()
{
List<Point> pointsInEllipse = new List<Point>();
// Distance in radians between angles measured on the ellipse
double deltaAngle = 0.001;
double circumference = GetLengthOfEllipse(deltaAngle);
double arcLength = 0.1;
double angle = 0;
// Loop until we get all the points out of the ellipse
for (int numPoints = 0; numPoints < circumference / arcLength; numPoints++)
{
angle = GetAngleForArcLengthRecursively(0, arcLength, angle, deltaAngle);
double x = r1 * Math.Cos(angle);
double y = r2 * Math.Sin(angle);
pointsInEllipse.Add(new Point(x, y));
}
}
private double GetLengthOfEllipse()
{
// Distance in radians between angles
double deltaAngle = 0.001;
double numIntegrals = Math.Round(Math.PI * 2.0 / deltaAngle);
double radiusX = (rectangleRight - rectangleLeft) / 2;
double radiusY = (rectangleBottom - rectangleTop) / 2;
// integrate over the elipse to get the circumference
for (int i = 0; i < numIntegrals; i++)
{
length += ComputeArcOverAngle(radiusX, radiusY, i * deltaAngle, deltaAngle);
}
return length;
}
private double GetAngleForArcLengthRecursively(double currentArcPos, double goalArcPos, double angle, double angleSeg)
{
// Calculate arc length at new angle
double nextSegLength = ComputeArcOverAngle(majorRadius, minorRadius, angle + angleSeg, angleSeg);
// If we've overshot, reduce the delta angle and try again
if (currentArcPos + nextSegLength > goalArcPos) {
return GetAngleForArcLengthRecursively(currentArcPos, goalArcPos, angle, angleSeg / 2);
// We're below the our goal value but not in range (
} else if (currentArcPos + nextSegLength < goalArcPos - ((goalArcPos - currentArcPos) * ARC_ACCURACY)) {
return GetAngleForArcLengthRecursively(currentArcPos + nextSegLength, goalArcPos, angle + angleSeg, angleSeg);
// current arc length is in range (within error), so return the angle
} else
return angle;
}
private double ComputeArcOverAngle(double r1, double r2, double angle, double angleSeg)
{
double distance = 0.0;
double dpt_sin = Math.Pow(r1 * Math.Sin(angle), 2.0);
double dpt_cos = Math.Pow(r2 * Math.Cos(angle), 2.0);
distance = Math.Sqrt(dpt_sin + dpt_cos);
// Scale the value of distance
return distance * angleSeg;
}
From my answer in BSE here .
I add it in stackoverflow as it is a different approach which does not rely on a fixed iteration steps but rely on a convergence of the distances between the points, to the mean distance.
So the calculation is shorter as it depends only on the wanted vertices amount and on the precision to reach (about 6 iterations for less than 0.01%).
The principle is :
0/ First step : calculate the points normally using a * cos(t) and b * sin(t)
1/ Calculate the lengths between vertices
2/ Adjust the angles variations depending on the gap between each distance to the mean distance
3/ Reposition the points
4/ Exit when the wanted precision is reached or return to 1/
import bpy, bmesh
from math import radians, sqrt, cos, sin
rad90 = radians( 90.0 )
rad180 = radians( 180.0 )
def createVertex( bm, x, y ): #uses bmesh to create a vertex
return bm.verts.new( [x, y, 0] )
def listSum( list, index ): #helper to sum on a list
sum = 0
for i in list:
sum = sum + i[index]
return sum
def calcLength( points ): #calculate the lenghts for consecutives points
prevPoint = points[0]
for point in points :
dx = point[0] - prevPoint[0]
dy = point[1] - prevPoint[1]
dist = sqrt( dx * dx + dy *dy )
point[3] = dist
prevPoint = point
def calcPos( points, a, b ): #calculate the positions following the angles
angle = 0
for i in range( 1, len(points) - 1 ):
point = points[i]
angle += point[2]
point[0] = a * cos( angle )
point[1] = b * sin( angle )
def adjust( points ): #adjust the angle by comparing each length to the mean length
totalLength = listSum( points, 3 )
averageLength = totalLength / (len(points) - 1)
maxRatio = 0
for i in range( 1, len(points) ):
point = points[i]
ratio = (averageLength - point[3]) / averageLength
point[2] = (1.0 + ratio) * point[2]
absRatio = abs( ratio )
if absRatio > maxRatio:
maxRatio = absRatio
return maxRatio
def ellipse( bm, a, b, steps, limit ):
delta = rad90 / steps
angle = 0.0
points = [] #will be a list of [ [x, y, angle, length], ...]
for step in range( steps + 1 ) :
x = a * cos( angle )
y = b * sin( angle )
points.append( [x, y, delta, 0.0] )
angle += delta
print( 'start' )
doContinue = True
while doContinue:
calcLength( points )
maxRatio = adjust( points )
calcPos( points, a, b )
doContinue = maxRatio > limit
print( maxRatio )
verts = []
for point in points:
verts.append( createVertex( bm, point[0], point[1] ) )
for i in range( 1, len(verts) ):
bm.edges.new( [verts[i - 1], verts[i]] )
A = 4
B = 6
bm = bmesh.new()
ellipse( bm, A, B, 32, 0.00001 )
mesh = bpy.context.object.data
bm.to_mesh(mesh)
mesh.update()
Do take into consideration the formula for ellipse perimeter as under if the ellipse is squashed. (If the minor axis is three times as small as the major axis)
tot_size = np.pi*(3*(a+b) -np.sqrt((3*a+b)*a+3*b))
Ellipse Perimeter
There is working MATLAB code available here. I replicate that below in case that link ever goes dead. Credits are due to the original author.
This code assumes that the major axis is a line segment from (x1, y1) to (x2, y2) and e is the eccentricity of the ellipse.
a = 1/2*sqrt((x2-x1)^2+(y2-y1)^2);
b = a*sqrt(1-e^2);
t = linspace(0,2*pi, 20);
X = a*cos(t);
Y = b*sin(t);
w = atan2(y2-y1,x2-x1);
x = (x1+x2)/2 + X*cos(w) - Y*sin(w);
y = (y1+y2)/2 + X*sin(w) + Y*cos(w);
plot(x,y,'o')
axis equal
I'm trying to find the point of intersection between a sphere and a line but honestly, I don't have any idea of how to do so.
Could anyone help me on this one ?
Express the line as an function of t:
{ x(t) = x0*(1-t) + t*x1
{ y(t) = y0*(1-t) + t*y1
{ z(t) = z0*(1-t) + t*z1
When t = 0, it will be at one end-point (x0,y0,z0). When t = 1, it will be at the other end-point (x1,y1,z1).
Write a formula for the distance to the center of the sphere (squared) in t (where (xc,yc,zc) is the center of the sphere):
f(t) = (x(t) - xc)^2 + (y(t) - yc)^2 + (z(t) - zc)^2
Solve for t when f(t) equals R^2 (R being the radius of the sphere):
(x(t) - xc)^2 + (y(t) - yc)^2 + (z(t) - zc)^2 = R^2
A = (x0-xc)^2 + (y0-yc)^2 + (z0-zc)^2 - R^2
B = (x1-xc)^2 + (y1-yc)^2 + (z1-zc)^2 - A - C - R^2
C = (x0-x1)^2 + (y0-y1)^2 + (z0-z1)^2
Solve A + B*t + C*t^2 = 0 for t. This is a normal quadratic equation.
You can get up to two solutions. Any solution where t lies between 0 and 1 are valid.
If you got a valid solution for t, plug it in the first equations to get the point of intersection.
I assumed you meant a line segment (two end-points). If you instead want a full line (infinite length), then you could pick two points along the line (not too close), and use them. Also let t be any real value, not just between 0 and 1.
Edit: I fixed the formula for B. I was mixing up the signs. Thanks M Katz, for mentioning that it didn't work.
I believe there is an inaccuracy in the solution by Markus Jarderot. Not sure what the problem is, but I'm pretty sure I translated it faithfully to code, and when I tried to find the intersection of a line segment known to cross into a sphere, I got a negative discriminant (no solutions).
I found this: http://www.codeproject.com/Articles/19799/Simple-Ray-Tracing-in-C-Part-II-Triangles-Intersec, which gives a similar but slightly different derivation.
I turned that into the following C# code and it works for me:
public static Point3D[] FindLineSphereIntersections( Point3D linePoint0, Point3D linePoint1, Point3D circleCenter, double circleRadius )
{
// http://www.codeproject.com/Articles/19799/Simple-Ray-Tracing-in-C-Part-II-Triangles-Intersec
double cx = circleCenter.X;
double cy = circleCenter.Y;
double cz = circleCenter.Z;
double px = linePoint0.X;
double py = linePoint0.Y;
double pz = linePoint0.Z;
double vx = linePoint1.X - px;
double vy = linePoint1.Y - py;
double vz = linePoint1.Z - pz;
double A = vx * vx + vy * vy + vz * vz;
double B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz);
double C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy +
pz * pz - 2 * pz * cz + cz * cz - circleRadius * circleRadius;
// discriminant
double D = B * B - 4 * A * C;
if ( D < 0 )
{
return new Point3D[ 0 ];
}
double t1 = ( -B - Math.Sqrt ( D ) ) / ( 2.0 * A );
Point3D solution1 = new Point3D( linePoint0.X * ( 1 - t1 ) + t1 * linePoint1.X,
linePoint0.Y * ( 1 - t1 ) + t1 * linePoint1.Y,
linePoint0.Z * ( 1 - t1 ) + t1 * linePoint1.Z );
if ( D == 0 )
{
return new Point3D[] { solution1 };
}
double t2 = ( -B + Math.Sqrt( D ) ) / ( 2.0 * A );
Point3D solution2 = new Point3D( linePoint0.X * ( 1 - t2 ) + t2 * linePoint1.X,
linePoint0.Y * ( 1 - t2 ) + t2 * linePoint1.Y,
linePoint0.Z * ( 1 - t2 ) + t2 * linePoint1.Z );
// prefer a solution that's on the line segment itself
if ( Math.Abs( t1 - 0.5 ) < Math.Abs( t2 - 0.5 ) )
{
return new Point3D[] { solution1, solution2 };
}
return new Point3D[] { solution2, solution1 };
}
Don't have enough reputation to comment on M Katz answer, but his answer assumes that the line can go on infinitely in each direction. If you need only the line SEGMENT's intersection points, you need t1 and t2 to be less than one (based on the definition of a parameterized equation). Please see my answer in C# below:
public static Point3D[] FindLineSphereIntersections(Point3D linePoint0, Point3D linePoint1, Point3D circleCenter, double circleRadius)
{
double cx = circleCenter.X;
double cy = circleCenter.Y;
double cz = circleCenter.Z;
double px = linePoint0.X;
double py = linePoint0.Y;
double pz = linePoint0.Z;
double vx = linePoint1.X - px;
double vy = linePoint1.Y - py;
double vz = linePoint1.Z - pz;
double A = vx * vx + vy * vy + vz * vz;
double B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz);
double C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy +
pz * pz - 2 * pz * cz + cz * cz - circleRadius * circleRadius;
// discriminant
double D = B * B - 4 * A * C;
double t1 = (-B - Math.Sqrt(D)) / (2.0 * A);
Point3D solution1 = new Point3D(linePoint0.X * (1 - t1) + t1 * linePoint1.X,
linePoint0.Y * (1 - t1) + t1 * linePoint1.Y,
linePoint0.Z * (1 - t1) + t1 * linePoint1.Z);
double t2 = (-B + Math.Sqrt(D)) / (2.0 * A);
Point3D solution2 = new Point3D(linePoint0.X * (1 - t2) + t2 * linePoint1.X,
linePoint0.Y * (1 - t2) + t2 * linePoint1.Y,
linePoint0.Z * (1 - t2) + t2 * linePoint1.Z);
if (D < 0 || t1 > 1 || t2 >1)
{
return new Point3D[0];
}
else if (D == 0)
{
return new [] { solution1 };
}
else
{
return new [] { solution1, solution2 };
}
}
You may use Wolfram Alpha to solve it in the coordinate system where the sphere is centered.
In this system, the equations are:
Sphere:
x^2 + y^2 + z^2 = r^2
Straight line:
x = x0 + Cos[x1] t
y = y0 + Cos[y1] t
z = z0 + Cos[z1] t
Then we ask Wolfram Alpha to solve for t: (Try it!)
and after that you may change again to your original coordinate system (a simple translation)
Find the solution of the two equations in (x,y,z) describing the line and the sphere.
There may be 0, 1 or 2 solutions.
0 implies they don't intersect
1 implies the line is a tangent to the sphere
2 implies the line passes through the sphere.
Here's a more concise formulation using inner products, less than 100 LOCs, and no external links. Also, the question was asked for a line, not a line segment.
Assume that the sphere is centered at C with radius r. The line is described by P+l*D where D*D=1. P and C are points, D is a vector, l is a number.
We set PC = P-C, pd = PC*D and s = pd*pd - PC*PC + r*r. If s < 0 there are no solutions, if s == 0 there is just one, otherwise there are two. For the solutions we set l = -pd +- sqrt(s), then plug into P+l*D.
Or you can just find the formula of both:
line: (x-x0)/a=(y-y0)/b=(z-z0)/c, which are symmetric equations of the line segment between the points you can find.
sphere: (x-xc)^2+(y-yc)^2+(z-zc)^2 = R^2.
Use the symmetric equation to find relationship between x and y, and x and z.
Then plug in y and z in terms of x into the equation of the sphere.
Then find x, and then you can find y and z.
If x gives you an imaginary result, that means the line and the sphere doesn't intersect.
I don't have the reputation to comment on Ashavsky's solution, but the check at the end needed a bit more tweaking.
if (D < 0)
return new Point3D[0];
else if ((t1 > 1 || t1 < 0) && (t2 > 1 || t2 < 0))
return new Point3D[0];
else if (!(t1 > 1 || t1 < 0) && (t2 > 1 || t2 < 0))
return new [] { solution1 };
else if ((t1 > 1 || t1 < 0) && !(t2 > 1 || t2 < 0))
return new [] { solution2 };
else if (D == 0)
return new [] { solution1 };
else
return new [] { solution1, solution2 };
how can i extract rotation, scale and translation values from 2d transformation matrix? i mean a have a 2d transformation
matrix = [1, 0, 0, 1, 0, 0]
matrix.rotate(45 / 180 * PI)
matrix.scale(3, 4)
matrix.translate(50, 100)
matrix.rotate(30 / 180 * PI)
matrix.scale(-2, 4)
now my matrix have values [a, b, c, d, tx, ty]
lets forget about the processes above and imagine that we have only the values a, b, c, d, tx, ty
how can i find total rotation and scale values via a, b, c, d, tx, ty
sorry for my english
Thanks your advance
EDIT
I think it should be an answer somewhere...
i just tried in Flash Builder (AS3) like this
var m:Matrix = new Matrix;
m.rotate(.25 * Math.PI);
m.scale(4, 5);
m.translate(100, 50);
m.rotate(.33 * Math.PI);
m.scale(-3, 2.5);
var shape:Shape = new Shape;
shape.transform.matrix = m;
trace(shape.x, shape.y, shape.scaleX, shape.scaleY, shape.rotation);
and the output is:
x = -23.6
y = 278.8
scaleX = 11.627334873920528
scaleY = -13.54222263865791
rotation = 65.56274134518259 (in degrees)
Not all values of a,b,c,d,tx,ty will yield a valid rotation sequence. I assume the above values are part of a 3x3 homogeneous rotation matrix in 2D
| a b tx |
A = | c d ty |
| 0 0 1 |
which transforms the coordinates [x, y, 1] into:
[x', y', 1] = A * |x|
|y|
|z|
Thus set the traslation into [dx, dy]=[tx, ty]
The scale is sx = sqrt(a² + c²) and sy = sqrt(b² + d²)
The rotation angle is t = atan(c/d) or t = atan(-b/a) as also they should be the same.
Otherwise you don't have a valid rotation matrix.
The above transformation is expanded to:
x' = tx + sx (x Cos θ - y Sin θ)
y' = ty + sy (x Sin θ + y Cos θ)
when the order is rotation, followed by scale and then translation.
I ran into this problem today and found the easiest solution to transform a point using the matrix. This way, you can extract the translation first, then rotation and scaling.
This only works if x and y are always scaled the same (uniform scaling).
Given your matrix m which has undergone a series of transforms,
var translate:Point;
var rotate:Number;
var scale:Number;
// extract translation
var p:Point = new Point();
translate = m.transformPoint(p);
m.translate( -translate.x, -translate.y);
// extract (uniform) scale
p.x = 1.0;
p.y = 0.0;
p = m.transformPoint(p);
scale = p.length;
// and rotation
rotate = Math.atan2(p.y, p.x);
There you go!
The term for this is matrix decomposition. Here is a solution that includes skew as described by Frédéric Wang.
function decompose_2d_matrix(mat) {
var a = mat[0];
var b = mat[1];
var c = mat[2];
var d = mat[3];
var e = mat[4];
var f = mat[5];
var delta = a * d - b * c;
let result = {
translation: [e, f],
rotation: 0,
scale: [0, 0],
skew: [0, 0],
};
// Apply the QR-like decomposition.
if (a != 0 || b != 0) {
var r = Math.sqrt(a * a + b * b);
result.rotation = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
result.scale = [r, delta / r];
result.skew = [Math.atan((a * c + b * d) / (r * r)), 0];
} else if (c != 0 || d != 0) {
var s = Math.sqrt(c * c + d * d);
result.rotation =
Math.PI / 2 - (d > 0 ? Math.acos(-c / s) : -Math.acos(c / s));
result.scale = [delta / s, s];
result.skew = [0, Math.atan((a * c + b * d) / (s * s))];
} else {
// a = b = c = d = 0
}
return result;
}
If in scaling you'd scaled by the same amount in x and in y, then the determinant of the matrix, i.e. ad-bc, which tells you the area multiplier would tell you the linear change of scale too - it would be the square root of the determinant. atan( b/a ) or better atan2( b,a ) would tell you the total angle you have rotated through.
However, as your scaling isn't uniform, there is usually not going to be a way to condense your series of rotations and scaling to a single rotation followed by a single non-uniform scaling in x and y.