c# transform line equation to change reference origin and angle - c#

Suppose I have the following situation.
I have a straight line equation in the form y=mx+q referred to the xOy plain.
I have the need to translate the line equation into the x'O'y' plain.
How would you procede in an efficient manner?
I should add that I know the coordinates and rotation of the two origins referred to a perfectly straight reference plain that can be seen as the paper on which the origins are drawn.

Refer to wikipedia : https://en.wikipedia.org/wiki/Rotation_of_axes#:~:text=If%20the%20curve%20
For just a rotation of coordinate systems: you can get the corpondances:
That's equivalent to :
in your case you can add the translation between the 2 coordinate systems, so you would have:
x' = x.cos(Th) + y.sin(Th) + tx
y' = -x.sin(Th) + y.cos(Th) + ty
with Th = Theta angle and T = (tx, ty) translation vector between both coordinate systems.
Once you have that you can simply replace x and y and simplify in the equation y=mx+q as you know:
y = mx + q
y' = m2 x' + q2
x' = x.cos(Th) + y.sin(Th) + tx
y' = -x.sin(Th) + y.cos(Th) + ty
(your goal is to find m2 and q2 knowing Theta and T)
Note: I think in 2D it is better to use the general equation of straight lines which is ax + by +c = 0 instead of the one you use y = mx +q. This is because vertical lines, whose equation are of the form "x = q" can't be written with the form y = mx +q.

Considering that the laser is my reference point for the line I did as follows:
private Model RotoTranslate(Model line, LaserContainer laser)
{
Model transformModel = new Model();
float cosA = (float)Math.Cos((laser.Rotation * Math.PI) / 180.0f);
float sinA = (float)Math.Sin((laser.Rotation * Math.PI) / 180.0f);
if (line.XModel)
{
transformModel.XModel = true;
transformModel.M = ((line.M * cosA) - sinA) / ((line.M * sinA) + cosA);
transformModel.Q = (line.Q + (laser.PositionGraph.Y * ((line.M * sinA) + cosA)) -
(laser.PositionGraph.X * ((line.M * cosA) - sinA))) / ((line.M * sinA) + cosA);
}
else
{
transformModel.XModel = false;
transformModel.M = ((line.M * cosA) + sinA) / (-(line.M * sinA) + cosA);
transformModel.Q = (line.Q + (laser.PositionGraph.X * (-(line.M * sinA) + cosA)) -
(laser.PositionGraph.Y * ((line.M * cosA) + sinA))) / (-(line.M * sinA) + cosA);
}
return transformModel;
}
I have to test now and see if there are exceptions I didn't account for...
The definition of XModel essentially refers to the way the line equation is expressed.
If the model has:
XModel = true;
then the equation is in the form y=mx+q, otherwise it's in the form x=my+q;

Related

How to draw arrow head with coordinates?

Is there a nice way to draw an arrowhead, when you have only coordinates of the source and destination point of the line?
So I'm using ITextSharp, programming in c#.
I know how to easily calculate slope and stuff but is there a way of calculating the edge coordinates for the "arrowhead" triangle (marked red in image)?
Thanks in advance
Your central line is vector from (x1,y1) to (x2,y2)
Backward direction vector D = (dx, dy) = (x1-x2, y1-y2)
Length of it: Norm = Sqrt(dx * dx + dy * dy)
Normalize it: uD = (udx, udy) = (dx/Norm, dy/Norm)
To form "wings" of arrow, rotate uD by needed angle. For example, I use angle Pi/6 with Cos(Pi/6) = Sqrt(3)/2 and Sin(Pi/6) = 1/2
ax = udx * Sqrt(3)/2 - udy * 1/2
ay = udx * 1/2 + udy * Sqrt(3)/2
bx = udx * Sqrt(3)/2 + udy * 1/2
by = - udx * 1/2 + udy * Sqrt(3)/2
Points for head with wing length L=20:
(x1 + 20 * ax, y1 + 20 * ay)
and
(x1 + 20 * bx, y1 + 20 * by)
If we define two (2-dimensional) vectors p2 and p3 for the end point of your line and the end point of one side of the arrow respectively you will get p3 by evaluating
p3 = p2 + A(alpha)s
s is a vector (l, 0) where l is the length of one side of the arrow.
alpha is the angle of this line against the x-axis i.e. the sum of the angle of your given line and the positive or negative angle between this line and the arrow line.
The components of the matrix are:
a11 = a22 = cos(alpha)
a12 = -sin(alpha)
a21 = sin(alpha)
Edit: As the y-component of s is zero a12 and a22 are not needed.

draw an equilateral triangle C#

I have tried to adapt some code I came across to draw an equilateral triangle in c#
public void drawTriangle(PaintEventArgs e, int x, int y, int distance)
{
float angle = 0;
SolidBrush brs = new SolidBrush(Color.Green);
PointF[] p = new PointF[3];
p[0].X = x;
p[0].Y = y;
p[1].Y = (float)( x + distance * Math.Cos(angle + Math.PI / 3));
p[1].X = (float)( y + distance * Math.Sin(angle + Math.PI / 3));
p[2].Y = (float)( x + distance * Math.Cos(angle - Math.PI / 3));
p[2].X = (float)( y + distance * Math.Sin(angle - Math.PI / 3));
e.Graphics.FillPolygon(brs, p);
}
Unfortunately, this doesn't even come close. I have drawn equilateral triangles, but the points were always based on the centers of congruent circles. I am trying to find a simpler way. I am sure there must be an obvious problem with this code, but I am trying to learn the math needed as I go, so I don't know what it is. Thanks for your time.
Try this approach. I assume that for zero angle p[0] is left bottom vertex, p[1] is right bottom (the same horizontal).
(BTW, you have got strange mangling of Y/X)
p[0].X = x;
p[0].Y = y;
p[1].X = (float)( x + distance * Math.Cos(angle));
p[1].Y = (float)( y + distance * Math.Sin(angle));
p[2].X = (float)( x + distance * Math.Cos(angle + Math.PI / 3));
p[2].Y = (float)( y + distance * Math.Sin(angle + Math.PI / 3));

Vector.AngleBetween in WinRT

Currently i am converting a Desktop application to Windows 8 application. To get a angle between 2 points in desktop application they use Vector.AngleBetween(vector1, vector2). Using "Point" i got the vector values in WinRT. Like this,
var vectorX = point1.X - point2.X;
var vectorY = point1.Y - point2.Y;
Point vector = new Point(vectorX , vectorY);
But i don't find any way to get a angle between 2 points in WinRT. I got this function from online,
public double GetAngleOfLineBetweenTwoPoints(Point p1, Point p2)
{
var xDiff = p2.X - p1.X;
var yDiff = p2.Y - p1.Y;
return Math.Atan2(yDiff , xDiff) * (180 / Math.PI);
}
but it wont give the exact result like "Vector.AngleBetween". Is there any better way available to get a result like "Vector.AngleBetween" in WinRT...?
I don't think your math is right. You can calculate angle between vectors using dot product and arcus cosinus, pseudo-code below:
double vectorALength = sqrt(vectorA.x * vectorA.x + vectorA.y * vectorA.y);
double vectorBLength = sqrt(vectorB.x * vectorB.x + vectorB.y * vectorB.y);
double dotProduct = vectorA.x * vectorB.x + vectorA.y + vectorB.y
double cosAngle = dotProduct / (vectorALength * vectorBLength);
double angle = Math.Acos(cosAngle) * (180 / Math.PI);
If I'm correct this should give you roughly right answer. Details and better explenations can be found on internet, e.g. Dot product

Create a SqlGeography polygon-circle from a center and radius

I would like to save a circle in a sql-server 2008 geography field, using c#.
In c# I have a latitude, a longitude and a radius but I just can't find a way to calculate the polygon that would represent the circle and create a SqlGeography from it.
I have tried to following function to create the polygon:
private List<Coordinate> getCirclePoints(Coordinate center, int radius, int speed) //speed 1: draws 360 sides, 2 draws 180 etc...
{
var centerLat = (center.Latitude * Math.PI) / 180.0; //rad
var centerLng = (center.Longitude * Math.PI) / 180.0; //rad
var dist = (float)radius / 6371.0; //d = angular distance covered on earth's surface
var circlePoints = new List<Coordinate>();
for (int x = 0; x <= 360; x += speed)
{
var brng = x * Math.PI / 180.0; //rad
var latitude = Math.Asin(Math.Sin(centerLat) * Math.Cos(dist) + Math.Cos(centerLat) * Math.Sin(dist) * Math.Cos(brng));
var longitude = ((centerLng + Math.Atan2(Math.Sin(brng) * Math.Sin(dist) * Math.Cos(centerLat), Math.Cos(dist) - Math.Sin(centerLat) * Math.Sin(latitude))) * 180.0) / Math.PI;
circlePoints.Add(new Coordinate((latitude * 180.0) / Math.PI, longitude));
}
return circlePoints;
}
And then try to convert this List<Coordinate> to a parsable string:
var s = "POLYGON((" + string.Join(",", points.ConvertAll(p => p.Longitude + " " + p.Latitude).ToArray()) + "))";
var poly = SqlGeography.STPolyFromText(new System.Data.SqlTypes.SqlChars((SqlString)s), 4326);
But it always complains the polygon has to be on a single hemisphere, where I'm sure it is the case.
Am I on the right track at all? Is there any other (simpler) way to do this?
OK, found the answer on my own. The trick is to create a point
var point = SqlGeography.Point(latitude, longitude, 4326);
Then create a buffer around the point
var poly = point.BufferWithTolerance(radiusInMeter, 0.01, true); //0.01 is to simplify the polygon to keep only a few sides
Then you could simply create a SqlCommand and add the polygon as parameter:
var param = new SqlParameter(#"Polygon", poly);
param.UdtTypeName = "Geography";
command.Parameters.Add(param);
Hope that will help someone else in the future!

How do I draw an image based on a simple polygon?

I'd like to copy a roughly rectangular area to a rectangular area. Example:
Both areas are defined by their corner points. The general direction is kept (no flipping etc).
Simply rotating the source image does not work since opposing sides may be of different length.
So far I found no way to do this in pure C# (except manual pixel copying), so I guess I have to resort to the Windows API or some 3rd party library?
Since I could not find an answer, I wrote a naive implementation myself. It works reasonably well.
Examples
I drew all examples manually in Paint, so they are not very exact - it was just enough to test some basics.
a) Slight rotation.
Source:
Result:
b) Various sides
Source:
Result:
c) Perspective
Source:
Result:
Code
(it's specialized to my use case, but it should be easy to adapt):
// _Corners are, well, the 4 corners in the source image
// _Px is an array of pixels extracted from the source image
public void Rescale ()
{
RescaleImage (
_Corners[0],
_Corners[1],
_Corners[3],
_Corners[2],
100,
100);
}
private void RescaleImage (PointF TL, PointF TR, PointF LL, PointF LR, int sx, int sy)
{
var bmpOut = new Bitmap (sx, sy);
for (int x = 0; x < sx; x++) {
for (int y = 0; y < sy; y++) {
/*
* relative position
*/
double rx = (double) x / sx;
double ry = (double) y / sy;
/*
* get top and bottom position
*/
double topX = TL.X + rx * (TR.X - TL.X);
double topY = TL.Y + rx * (TR.Y - TL.Y);
double bottomX = LL.X + rx * (LR.X - LL.X);
double bottomY = LL.Y + rx * (LR.Y - LL.Y);
/*
* select center between top and bottom point
*/
double centerX = topX + ry * (bottomX - topX);
double centerY = topY + ry * (bottomY - topY);
/*
* store result
*/
var c = PolyColor (centerX, centerY);
bmpOut.SetPixel (x, y, c);
}
}
bmpOut.Save (_Path + "out5 rescale out.bmp");
}
private Color PolyColor (double x, double y)
{
// get fractions
double xf = x - (int) x;
double yf = y - (int) y;
// 4 colors - we're flipping sides so we can use the distance instead of inverting it later
Color cTL = _Px[(int) y + 1, (int) x + 1];
Color cTR = _Px[(int) y + 1, (int) x + 0];
Color cLL = _Px[(int) y + 0, (int) x + 1];
Color cLR = _Px[(int) y + 0, (int) x + 0];
// 4 distances
double dTL = Math.Sqrt (xf * xf + yf * yf);
double dTR = Math.Sqrt ((1 - xf) * (1 - xf) + yf * yf);
double dLL = Math.Sqrt (xf * xf + (1 - yf) * (1 - yf));
double dLR = Math.Sqrt ((1 - xf) * (1 - xf) + (1 - yf) * (1 - yf));
// 4 parts
double factor = 1.0 / (dTL + dTR + dLL + dLR);
dTL *= factor;
dTR *= factor;
dLL *= factor;
dLR *= factor;
// accumulate parts
double r = dTL * cTL.R + dTR * cTR.R + dLL * cLL.R + dLR * cLR.R;
double g = dTL * cTL.G + dTR * cTR.G + dLL * cLL.G + dLR * cLR.G;
double b = dTL * cTL.B + dTR * cTR.B + dLL * cLL.B + dLR * cLR.B;
Color c = Color.FromArgb ((int) (r + 0.5), (int) (g + 0.5), (int) (b + 0.5));
return c;
}
Generally speaking, what you want to do is map the destination coordinates to the source coordinates through a transform function:
for (int y = 0; y < destHeight; y++) {
for (x=0; x < destWidth; x++) {
Color c = Transform(x, y, sourceImage, sourceTransform);
SetPixel(destImage, x, y, c);
}
}
Let's assume that sourceTransform is an object that encapsulates a transformation from source to dest coordinates (and vice versa).
Working in dest coordinates will make it easier to avoid that curve in your retransformed source image and will allow you to better antialias, as you can map the corners of the dest pixel to the source image and sample within it and interpolate/extrapolate.
In your case you're going to have a set of linear equations that do the mapping - in this case this is known as quadrilateral warping - see this previous question.

Categories