I have been trying to draw equilateral triangles on the sides of a larger triangle. The first triangle is drawn by a separate method setting points A, B and C. So far I have just started with two sides, I am able to find the first two points of the smaller triangles, but I am unable to determine the correct formula for the third. I have tried refreshing my memory of trigonometry but I am at an impasse.
float a =0;
Point p = new Point(pnlDisplay.Width / 2 - (pnlDisplay.Width / 2) /3, 200);
Triangle t = new Triangle(p, pnlDisplay.Width / 3, 0);
drawEqTriangle(e, t);
Point p1 = new Point();
Point p2 = new Point();
Point p3 = new Point();
p1.X = Convert.ToInt32(A.X + t.size / 3);
p1.Y = Convert.ToInt32(A.Y);
p2.X = Convert.ToInt32(A.X + (t.size - t.size / 3));
p2.Y = Convert.ToInt32(A.Y);
//////////////////////////////
p3.X = Convert.ToInt32((A.X - t.size / 3) * Math.Sin(a));
p3.Y = Convert.ToInt32((A.Y - t.size / 3) * Math.Cos(a));
drawTriangle(e, p1, p2, p3);
p1.X = Convert.ToInt32((B.X - t.size / 3 * Math.Cos(t.angle + Math.PI / 3)));
p1.Y = Convert.ToInt32((B.Y + t.size / 3 * Math.Sin(t.angle+ Math.PI / 3)));
p2.X = Convert.ToInt32((B.X - (t.size - t.size / 3) * Math.Cos(t.angle + Math.PI / 3)));
p2.Y = Convert.ToInt32((B.Y + (t.size - t.size / 3) * Math.Sin(t.angle + Math.PI / 3)));
//////////////////////////////
p3.X = Convert.ToInt32((B.X - t.size / 3) * Math.Cos(a));
p3.Y = Convert.ToInt32((B.Y - t.size / 3) * Math.Tan(a));
drawTriangle(e, p1, p2, p3);
This may be a question for the math subsection, but I thought I would try here first. What I need is the formula for p3.X and p3.Y
Any help would be greatly appreciated.
EDIT: changing "a" to float a = Convert.ToSingle( 60 * Math.PI / 180);
results in this:
FINAL EDIT:
Using MBo's answer:
Let's build universal formulas for any triangle orientation (note that it is worth to use A[] array for big triangle instead of explicit A,B,C vertices)
p1.X = A.X * 2 / 3 + B.X / 3;
p1.Y = A.Y * 2 / 3 + B.Y / 3;
p2.X = A.X / 3 + B.X * 2 / 3;
p2.Y = A.Y / 3 + B.Y * 2 / 3;
D.X = (A.X - p1.X);
D.Y = (A.Y - p1.Y);
//note - angle sign depends on ABC orientation CW/CCW
p3.X = p1.X + D.X * Cos(2*Pi/3) - D.Y * Sin(2*Pi/3)
p3.Y = p1.Y + D.X * Sin(2*Pi/3) + D.Y * Cos(2*Pi/3)
Related
i need to draw an arrow representing the rotation of a circle in WPF. The best i got so far is this one. But it seems that the arrowhead is not correctly aligned with the angle of the arrowarc.
private void InternalDrawArrowGeometry(StreamGeometryContext context)
{
var angleWidth = 45;
var startAngle = Rotation + 90;
var endAngle = Rotation + angleWidth + 90;
var xEnd = Radius * Math.Cos(startAngle * Math.PI / 180.0);
var yEnd = Radius * Math.Sin(startAngle * Math.PI / 180.0);
var xStart = Radius * Math.Cos(endAngle * Math.PI / 180.0);
var yStart = Radius * Math.Sin(endAngle * Math.PI / 180.0);
var b = angleWidth * Math.PI/180;
var pt1 = new Point(CentreX + xStart, CentreY - yStart);
var pt2 = new Point(CentreX + xEnd, CentreY - yEnd);
var len2 = 1;
const int angle = 45;
var pt3 = new Point(
pt2.X + (len2 / b) * ((pt1.X - pt2.X) * Math.Cos(angle) + (pt1.Y - pt2.Y) * Math.Sin(angle)),
pt2.Y + (len2 / b) * ((pt1.Y - pt2.Y) * Math.Cos(angle) - (pt1.X - pt2.X) * Math.Sin(angle)));
var pt4 = new Point(
pt2.X + (len2 / b) * ((pt1.X - pt2.X) * Math.Cos(angle) - (pt1.Y - pt2.Y) * Math.Sin(angle)),
pt2.Y + (len2 / b) * ((pt1.Y - pt2.Y) * Math.Cos(angle) + (pt1.X - pt2.X) * Math.Sin(angle)));
context.BeginFigure(pt1,
false, // Filled
false); // Closed
context.ArcTo(pt2,
new Size(Radius, Radius),
0.0, // rotationAngle
startAngle - endAngle > 180, // greater than 180 deg?
SweepDirection.Clockwise,
true, // isStroked
false);
context.LineTo(pt3, true, false);
context.LineTo(pt2, true, false);
context.LineTo(pt4, true, false);
}
Has anyone coded something like this correctly and give me the code or can tell me whats wrong with my code?
This question already has answers here:
Is there any way to draw an image to use 4 points rather than 3 (perspective warp)
(4 answers)
Perspective Image Transformation with tiling
(2 answers)
Closed 4 years ago.
I want to rotate a bitmap image constantly but the array of four elements of points that I created doesn't fit in this method. drawimage(image,points,srcrectangle,Graphicsunit).
I was reading microsft document about drawimage method and It says that it will work for 3 points(paralellogram). So I tried with 3 points, and It works as microsoft document says. But I need these four elements, perhaps I'm wrong , can someone just tell me how this method works?
public override void dibujar(Graphics area, Bitmap imagen)
{
radio = Math.Sqrt(Math.Pow(ancho, 2) + Math.Pow(largo, 2)) / 2;
Rectangle porcion = new Rectangle(indicex * ancho, indicey * largo, ancho, largo);
Point p1 = new Point(x + (int)(radio * Math.Sin(Math.PI * angulo / 180)), y+(int)(radio * Math.Cos(Math.PI * angulo / 180)));
Point p2 = new Point(x + ancho + (int)(radio * Math.Sin(Math.PI * (angulo-90) / 180)), y + (int)(radio * Math.Cos(Math.PI * (angulo - 90) / 180)));
Point p3 = new Point(x + (int)(radio * Math.Sin(Math.PI * (angulo - 180) / 180)), y - largo + (int)(radio * Math.Cos(Math.PI * (angulo - 180) / 180)));
Point p4 = new Point(x + ancho + (int)(radio * Math.Sin(Math.PI * (angulo - 270) / 180)), y - largo + (int)(radio * Math.Cos(Math.PI * (angulo - 270) / 180)));
Point[] points = { p2, p3, p4 }; // Get all points in one array
area.DrawImage(imagen, points, porcion, GraphicsUnit.Pixel);
if (angulo == 0)
{
angulo = 360;
}
else
{
angulo--;
}
x += dx;
}
This method throw NotimplementedException when my array is of four elements
I have a midpoint p1 that is used to create a line along an angle A:
double startX = p1.X - lineHalfLength * Math.Cos(-A * Math.PI/180);
double endX = p1.X + lineHalfLength * Math.Cos(-A * Math.PI/180);
double startY = p1.Y - lineHalfLength * Math.Sin(-A * Math.PI/180);
double endY = p1.Y + lineHalfLength* Math.Sin(-A * Math.PI/180);
So far so good.
Now I need to create a new line that is parallel to the first, at a distance D perpendicularly.
Once I figure out midpoint p2 for the new line, I can create the line (same logic as above), but getting that point is evading me, high school trig was just too long ago. Here's my current attempt:
p2.X = p1.X + D * Math.Cos((A + 90) * Math.PI/180);
p2.Y = p1.Y + D * Math.Sin((A + 90) * Math.PI/180);
This pretty much works for 45 degrees, but nothing else.
What am I missing here?
You got the right idea to add 90 degrees to the angle you're using, but the angle you are using is measured clockwise from the left hand side of the horizontal whereas the convention is anticlockwise from the right hand side of the horizontal.
With the usual convention for angles, I would have expected to see code more like this to find the line ends
double startX = p1.X - lineHalfLength * Math.Cos(A * Math.PI/180);
double endX = p1.X + lineHalfLength * Math.Cos(A * Math.PI/180);
double startY = p1.Y - lineHalfLength * Math.Sin(A * Math.PI/180);
double endY = p1.Y + lineHalfLength* Math.Sin(A * Math.PI/180);
in which case adding 90 to A ought to work just fine.
(note cos(-x)=cos(x) and sin(-x)=-sin(x))
Maintaining your convention for angle, wrap your code into a line function, and you can then add 90 degrees to the angle you pass into your own function.
void LineFromMidpoint(double A, Point p1, double dist, out Point p2, out Point p3)
{
double startX = p1.X - dist * Math.Cos(-A * Math.PI/180);
double endX = p1.X + dist * Math.Cos(-A * Math.PI/180);
double startY = p1.Y - dist * Math.Sin(-A * Math.PI/180);
double endY = p1.Y + dist* Math.Sin(-A * Math.PI/180);
p2 = new Point {X=startX, Y=startY};
p3 = new Point {X=endX, Y=endY};
}
Call it once to get the original line ends, p2, another time to get the perpendicular line ends p3, and two more times to get the parallel lines, p4, p5.
void Main()
{
Point p1 = new Point {X=10, Y=5};
Point p2start, p2end;
double A=15;
double lineHalfLength = 10;
LineFromMidpoint(A, p1, lineHalfLength, out p2start, out p2end);
Console.WriteLine("p1={0}, p2={1}, p3={2}", p1, p2start, p2end);
double B=A+90;
double perpDist=2;
Point p3firstMidpoint, p3secondMidpoint;
LineFromMidpoint(B, p1, perpDist, out p3firstMidpoint, out p3secondMidpoint);
Console.WriteLine("p3firstMidpoint={0}, p3secondMidpoint={1}",
p3firstMidpoint, p3secondMidpoint);
Point p4start, p4end;
LineFromMidpoint(A, p3firstMidpoint, lineHalfLength, out p4start, out p4end);
Console.WriteLine("p4start={0}, p4end={1}", p4start, p4end);
Point p5start, p5end;
LineFromMidpoint(A, p3secondMidpoint, lineHalfLength, out p5start, out p5end);
Console.WriteLine("p5start={0}, p5end={1}", p5start, p5end);
}
Plotting the points results in
Oiginal line ends (black), perpendicular line ends (red), parallel lines (blue and green).
This pretty much works for 45 degrees
That's because SIN(45) and COS(45) are equal, and you're using the wrong one in your X calculation:
p2.X = p1.X + D * Math.Cos((A + 90) * Math.PI/180);
p2.Y = p1.Y + D * Math.Sin((A + 90) * Math.PI/180);
I have been trying to figure this out for sometime now..
The problem to solve..
Say I have 3 Points..
P1 ---------- P2, and P3 can be anywhere around P1 and P2
What is the formula to calculate so that P3 is interpolated onto the line between P1 and P2?
I need a formula that calculates new X,Y coordinates for P3 that falls on the line between P1 and P2..
My code as of so far..
public Point lerp(Point P0, Point P1, Point P)
{
double y1 = P0.Y + (P1.Y - P0.Y) * ((P.X - P0.X) / (P1.X - P0.X));
double x1 = P.X;
double y2 = P.Y;
double x2 = P0.X + (P1.X - P0.X) * ((P.Y - P0.Y) / (P1.Y - P0.Y));
return new Point((x1 + x2) / 2, (y1 + y2) / 2);
}
And my reference..
http://en.wikipedia.org/wiki/Linear_interpolation
The above code gets it close, but its slightly off...
Here is the converted javascript code from Corey Ogburn
public Point _pointOnLine(Point pt1, Point pt2, Point pt)
{
bool isValid = false;
var r = new Point(0, 0);
if (pt1.Y == pt2.Y && pt1.X == pt2.X) { pt1.Y -= 0.00001; }
var U = ((pt.Y - pt1.Y) * (pt2.Y - pt1.Y)) + ((pt.X - pt1.X) * (pt2.X - pt1.X));
var Udenom = Math.Pow(pt2.Y - pt1.Y, 2) + Math.Pow(pt2.X - pt1.X, 2);
U /= Udenom;
r.Y = pt1.Y + (U * (pt2.Y - pt1.Y));
r.X = pt1.X + (U * (pt2.X - pt1.X));
double minx, maxx, miny, maxy;
minx = Math.Min(pt1.X, pt2.X);
maxx = Math.Max(pt1.X, pt2.X);
miny = Math.Min(pt1.Y, pt2.Y);
maxy = Math.Max(pt1.Y, pt2.Y);
isValid = (r.X >= minx && r.X <= maxx) && (r.Y >= miny && r.Y <= maxy);
return isValid ? r : new Point();
}
Here's some javascript code we've used here at work (a GIS company) to figure out the closest point on a line the mouse is next to in a situation where a user wants to split the line by adding a vertex to it. Should be easy to move over to C#:
function _pointOnLine(line1, line2, pt) {
var isValid = false;
var r = new Microsoft.Maps.Location(0, 0);
if (line1.latitude == line2.latitude && line1.longitude == line2.longitude) line1.latitude -= 0.00001;
var U = ((pt.latitude - line1.latitude) * (line2.latitude - line1.latitude)) + ((pt.longitude - line1.longitude) * (line2.longitude - line1.longitude));
var Udenom = Math.pow(line2.latitude - line1.latitude, 2) + Math.pow(line2.longitude - line1.longitude, 2);
U /= Udenom;
r.latitude = line1.latitude + (U * (line2.latitude - line1.latitude));
r.longitude = line1.longitude + (U * (line2.longitude - line1.longitude));
var minx, maxx, miny, maxy;
minx = Math.min(line1.latitude, line2.latitude);
maxx = Math.max(line1.latitude, line2.latitude);
miny = Math.min(line1.longitude, line2.longitude);
maxy = Math.max(line1.longitude, line2.longitude);
isValid = (r.latitude >= minx && r.latitude <= maxx) && (r.longitude >= miny && r.longitude <= maxy);
return isValid ? r : null;
}
line1 is a point with a latitude and longitude to represent one of the endpoints of the line, equivalent to your P1. line2 is the other endpoint: P2. pt is your P3. This will return the point on the line that P3 is perpendicular through. If P3 is past either end of the line, this will return null which means that one of the two end points is the closest point to P3.
For clarity:
The problem is that you Point has integer values for X and Y and therefore you are doing integer division. Try to cast your values into float or double, do the calculations and then return them back to the integers.
Note that when you are doing this:
(P1.Y - P0.Y) * ((P.X - P0.X) / (P1.X - P0.X))
you are actualy loosing the precision since the result of 5/2 is 2, not 2.5 but when your values are real numbers then 5.0/2.0 is indeed 2.5.
You should try this:
double y1 = P0.Y + (double)(P1.Y - P0.Y) * ((double)(P.X - P0.X) / (double)(P1.X - P0.X));
double x1 = P.X; //these two are implicit casts
double y2 = P.Y;
double x2 = P0.X + (double)(P1.X - P0.X) * ((double)(P.Y - P0.Y) / (double)(P1.Y - P0.Y));
return new Point((x1 + x2) / 2.0, (y1 + y2) / 2.0); //put 2.0 for any case even though x1+x2 is `double`
Also, then you are converting from double to int, decimal part of the number is automatically cut off so for instance 3.87 will become 3. Than your last line should be more precise if you could use this:
return new Point((x1 + x2) / 2.0 + 0.5, (y1 + y2) / 2.0 + 0.5);
which will effectively round double values to the closer integer value.
EDIT:
But if you just want to find the point p3 on the line between the two points, than it is easier to use this approach:
public Point lerp(Point P0, Point P1)
{
double x = ((double)P0.X + P1.X)/2.0;
double y = (double)P0.Y + (double)(P1.Y - P0.Y) * ((double)(x - P0.X) / (double)(P1.X - P0.X));
return new Point(x + 0.5, y + 0.5);
}
This is an old question and I found the Corey Ogburn solution quite useful. But I thought it might be helpful for others to post a less "map" version of the javascript code - which I used in canvas drawing.
export const pointOnLine = (p0, p1, q) => {
// p0 and p1 define the line segment
// q is the reference point (aka mouse)
// returns point on the line closest to px
if (p0.x == p1.x && p0.y == p1.y) p0.x -= 0.00001;
const Unumer = ((q.x - p0.x) * (p1.x - p0.x)) + ((q.y - p0.y) * (p1.y - p0.y));
const Udenom = Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2);
const U = Unumer / Udenom;
const r = {
x: p0.x + (U * (p1.x - p0.x)),
y: p0.y + (U * (p1.y - p0.y))
}
const minx = Math.min(p0.x, p1.x);
const maxx = Math.max(p0.x, p1.x);
const miny = Math.min(p0.y, p1.y);
const maxy = Math.max(p0.y, p1.y);
const isValid = (r.x >= minx && r.x <= maxx) && (r.y >= miny && r.y <= maxy);
return isValid ? r : null;
}
I need to find a point where a line (its origin is ellipse' center) intersects an ellipse in 2D... I can easily find a point on a circle, because I know an angle F and the circle' radius (R):
x = x0 + R * cosF
y = y0 + R * sinF
However I just can't figure how am I supposed to deal with an ellipse... I know it's dimensions (A & B), but what is the way of finding parameter T?!
x = x0 + A * cosT
y = y0 + B * sinT
From what I understand the parameter T (T angle) is not far from the F angle (approximately +-15 degrees in some cases), but I just can't figure how to calculate it!!!
If there is a kind hearted soul, please help me with this problem...
The standard equation of an ellipse, stationed at 0,0, is:
1 = (x)^2 / (a) + (y)^2 / (b)
Where a is 1/2 the diameter on the horizontal axis, and b is 1/2 the diameter on the vertical axis.
you have a line, assuming an equation:
y = (m)(x - x0) + y0
So, let us plug-and-play!
1 = (x)^2 / (a) + (m(x - x0) + y0)^2 / (b)
1 = x^2 / a + (mx + (y0 - mx0))^2 / b
1 = x^2 / a + (m^2 * x^2 + 2mx*(y0 - mx0) + (y0 - mx0)^2) / b
1 = x^2 / a + (m^2 x^2) / b + (2mx*(y0 - mx0) + (y0^2 - 2y0mx0 + m^2*x0^2)) / b
1 = ((x^2 * b) / (a * b)) + ((m^2 * x^2 * a) / (a * b)) + (2mxy0 - 2m^2xx0)/b + (y0^2 - 2y0mx0 + m^2*x0^2)/b
1 = ((bx^2 + am^2x^2)/(ab)) + (x*(2my0 - 2m^2x0))/b + (y0^2 - 2y0mx0 + m^2*x0^2)/b
0 = x^2*((b + a*m^2)/(ab)) + x*((2my0 - 2m^2x0)/b) + (((y0^2 - 2y0mx0 + m^2*x0^2)/b) - 1)
That last equation follows the form of a standard quadratic equation.
So just use the quadratic formula, with:
((b + a*m^2)/(ab))
((2my0 - 2m^2x0)/b)
and
(((y0^2 - 2y0mx0 + m^2*x0^2)/b) - 1)
to get the X values at the intersections; Then, plug in those values into your original line equation to get the Y values.
Good luck!
Don't do it this way. Instead check the equation that forms an ellipse and that forming a line and solve the set:
The ellipse: (x/a)^2 + (y/b)^2 = 1
Your line: y = cx
You know a, b and c, so finding a solution is going to be easy. You'll find two solutions, because the line crosses the ellipse twice.
EDIT: Note I moved your ellipse's center to (0,0). It makes everything easier. Just add (x0,y0) to the solution.
public Hits<float2> EllipseLineIntersection ( float rx , float ry , float2 p1 , float2 p2 )
{
Hits<float2> hits = default(Hits<float2>);
float2 p3, p4;
Rect rect = default(Rect);
{
rect.xMin = math.min(p1.x,p2.x);
rect.xMax = math.max(p1.x,p2.x);
rect.yMin = math.min(p1.y,p2.y);
rect.yMax = math.max(p1.y,p2.y);
}
float s = ( p2.y - p1.y )/( p2.x - p1.x );
float si = p2.y - ( s * p2.x );
float a = ( ry*ry )+( rx*rx * s*s );
float b = 2f * rx*rx * si * s;
float c = rx*rx * si*si - rx*rx * ry*ry;
float radicand_sqrt = math.sqrt( ( b*b )-( 4f * a * c) );
p3.x = ( -b - radicand_sqrt )/( 2f*a );
p4.x = ( -b + radicand_sqrt )/( 2f*a );
p3.y = s*p3.x + si;
p4.y = s*p4.x + si;
if( rect.Contains(p3) ) hits.Push( p3 );
if( rect.Contains(p4) ) hits.Push( p4 );
return hits;
}
public struct Hits<T>
{
public byte count;
public T point0, point1;
public void Push ( T val )
{
if( count==0 ) { point0 = val; count ++; }
else if( count==1 ) { point1 = val; count ++; }
else print("This structure can only fit 2 values");
}
}
I wrote a C# code for your problem and I hope you can find it helpful. the distance function inside this code calculates euclidean distance between two points in space.
wX denotes horizontal radios of ellipse and wY denotes vertical radios.
private PointF LineIntersectEllipse(PointF A, PointF B, float wX, float wY)
{
double dx = B.X - A.X;
double dy = B.Y - A.Y;
double theta = Math.Atan2(dy, dx);
double r = distance(A, B) - ((wX * wY) / Math.Sqrt(Math.Pow(wY * Math.Cos(theta), 2) + Math.Pow(wX * Math.Sin(theta), 2)));
return PointF((float)(A.X + r * Math.Cos(theta)), (float)(A.Y + r * Math.Sin(theta)));
}
Andrew Ĺukasik posted a good and useful answer, however it is not using regular C# types. As I wrote in the comments, I converted the code using System.Drawing objects PointF and RectangleF. I found out that if the points given as parameters are aligned as a vertical or horizontal line, then "rect" will have a width or a height equal to 0. Then, rect.Contains(point) will return false even if the point is on this line.
I also modified the "Hits" structure to check if the point pushed is not already existing, which is the case if the line is perfectly tangent, then p3 and p4 will have same coordinates, as the exact tangent point is the only crossing point.
Here is the new code taking care of all the cases :
public static Hits<PointF> EllipseLineIntersection0(float rx, float ry, PointF p1, PointF p2)
{
Hits<PointF> hits = default(Hits<PointF>);
PointF p3 = new PointF();
PointF p4 = new PointF();
var rect = default(RectangleF);
rect.X = Math.Min(p1.X, p2.X);
rect.Width = Math.Max(p1.X, p2.X) - rect.X;
rect.Y = Math.Min(p1.Y, p2.Y);
rect.Height = Math.Max(p1.Y, p2.Y) - rect.Y;
float s = (p2.Y - p1.Y) / (p2.X - p1.X);
float si = p2.Y - (s * p2.X);
float a = (ry * ry) + (rx * rx * s * s);
float b = 2f * rx * rx * si * s;
float c = rx * rx * si * si - rx * rx * ry * ry;
float radicand_sqrt = (float)Math.Sqrt((b * b) - (4f * a * c));
p3.X = (-b - radicand_sqrt) / (2f * a);
p4.X = (-b + radicand_sqrt) / (2f * a);
p3.Y = s * p3.X + si;
p4.Y = s * p4.X + si;
if (rect.Width == 0)
{
if (p3.Y >= rect.Y && p3.Y <= rect.Y + rect.Height) hits.Push(p3);
if (p4.Y >= rect.Y && p4.Y <= rect.Y + rect.Height) hits.Push(p4);
}
else if (rect.Height == 0)
{
if (p3.X >= rect.X && p3.X <= rect.X + rect.Width) hits.Push(p3);
if (p4.X >= rect.X && p4.X <= rect.X + rect.Width) hits.Push(p4);
}
else
{
if (rect.Contains(p3)) hits.Push(p3);
if (rect.Contains(p4)) hits.Push(p4);
}
return hits;
}
public struct Hits<T>
{
public byte Count;
public T P0, P1;
public void Push(T val)
{
if (Count == 0) { P0 = val; Count++; }
else if (Count == 1) { if (!P0.Equals(val)) { P1 = val; Count++; } }
else throw new OverflowException("Structure Hits can only fit 2 values.");
}
}