Rescaling Image in Windows Form - c#

I have a set of points I want to show on a 2000x2000 canvas. Here is an example: "61.86, 83 - 61.79, 82.91 - 61.77, 82.77 - 61.92, 82.76 - 61.75, 82.7 - 61.79, 82.58 - 61.85, 82.46 - 61.79, 82.17 - 61.72, 81.88 - 61.61, 81.61 - 61.51, 81.33 - 61.49, 81.02 - 61.33, 80.99 - 61.37, 80.83"
These points are from a 100x100 grid so the first one ought to be in the bottom right quarter of my 2000*2000 canvas.
To do this I have code that finds the biggest X and Y and then rescales.
List<double> MinAndMax(List<Node> spots)
{
List<double> retValues = new List<double>();
double xLowest = spots.Select(s => s.X).Min();
double xHighest = spots.Select(s => s.X).Max();
double xDifference = xHighest - xLowest;
double yLowest = spots.Select(s => s.Y).Min();
double yHighest = spots.Select(s => s.Y).Max();
double yDifference = yHighest - yLowest;
if (xLowest < yLowest)
retValues.Add(xLowest);
else
retValues.Add(yLowest);
if (xHighest < yHighest)
retValues.Add(yHighest);
else
retValues.Add(xHighest);
return retValues;
}
int Rescale(double oldValue, double oldMin, double oldMax, int newMin, int newMax)
{
return Convert.ToInt32(((oldValue - oldMin) * (newMax - newMin) / (oldMax - oldMin)) + newMin);
}
I call it like so:
double zoneMin, zoneMax;
int lowestCanvas = 150, highestCanvas = 1850;
List<Node> path = await PathMaker();
List<double> zoneMinMax = MinAndMax(path);
zoneMin = zoneMinMax[0];
zoneMax = zoneMinMax[1];
foreach (Node spot in path)
{
Point point = new Point();
point.X = Rescale(spot.X, zoneMin, zoneMax, lowestCanvas, highestCanvas);
point.Y = Rescale(spot.Y, zoneMin, zoneMax, lowestCanvas, highestCanvas);
NodeSpot dot = new NodeSpot()
{
Name = spot.Name,
Location = point,
IsInPath = true
};
drawingSurface1.Nodes.Add(dot);
}
drawingSurface1.Invalidate();
Instead of getting my little path nicely spread out, I get this odd clump in the bottom LEFT had quadrant.
I can't see where I am going wrong here. What do I need to do in order to have my 14 points spread out over the canvass?

Your issue is that you are returning a single min value and a single max value. You need separate min and max values for X and Y, as the ranges in each coordinate are different. In your sample data from the question the range of X is [-61.92, 61.86] and the range of Y is [80.83, 83]. Your approach will draw a frame covering [-61.92, -61.92] to [83, 83], with most of the points in one corner.
Your test fails to catch the problem as the X and Y values are the same in the test case. Create a test case where the X values are all negative and the Y values positive, this will show the issue.

let's take [[20, 20], [50, 50], [80, 80]] as an easy exemple.
min and max will be 20 and 80 and wanted scale is 0 to 2000.
for the point [50, 50]
(((oldValue - oldMin) * (newMax - newMin) / (oldMax - oldMin)) + newMin)
gives
((50 - 20) * (2000 - 0) / (80 - 20)) + 0
= 30*2000 / 60
= 1000 (which is half the size of the canvas)
seems coherent so the problem is not coming from the transform function
I suggest trying to debug it by printing the [X, Y] values of the "point" to be sure tho
Also print the min max of the oldScale to be sure this is not the problem

Use the Graphics.PageScale Property
See also Coordinate Systems and Transformations

Related

Finding the true anomaly from state vectors

I'm attempting to convert from state vectors (position and speed) into Kepler elements, however I'm running into problems where a negative velocity or position will give me wrong results when trying to calculate true anomaly.
Here are the different ways I'm trying to calculate the True Anomaly:
/// <summary>
/// https://en.wikipedia.org/wiki/True_anomaly#From_state_vectors
/// </summary>
public static double TrueAnomaly(Vector4 eccentVector, Vector4 position, Vector4 velocity)
{
var dotEccPos = Vector4.Dot(eccentVector, position);
var talen = eccentVector.Length() * position.Length();
talen = dotEccPos / talen;
talen = GMath.Clamp(talen, -1, 1);
var trueAnomoly = Math.Acos(talen);
if (Vector4.Dot(position, velocity) < 0)
trueAnomoly = Math.PI * 2 - trueAnomoly;
return trueAnomoly;
}
//sgp = standard gravitational parameter
public static double TrueAnomaly(double sgp, Vector4 position, Vector4 velocity)
{
var H = Vector4.Cross(position, velocity).Length();
var R = position.Length();
var q = Vector4.Dot(position, velocity); // dot product of r*v
var TAx = H * H / (R * sgp) - 1;
var TAy = H * q / (R * sgp);
var TA = Math.Atan2(TAy, TAx);
return TA;
}
public static double TrueAnomalyFromEccentricAnomaly(double eccentricity, double eccentricAnomaly)
{
var x = Math.Sqrt(1 - Math.Pow(eccentricity, 2)) * Math.Sin(eccentricAnomaly);
var y = Math.Cos(eccentricAnomaly) - eccentricity;
return Math.Atan2(x, y);
}
public static double TrueAnomalyFromEccentricAnomaly2(double eccentricity, double eccentricAnomaly)
{
var x = Math.Cos(eccentricAnomaly) - eccentricity;
var y = 1 - eccentricity * Math.Cos(eccentricAnomaly);
return Math.Acos(x / y);
}
Edit: another way of doing it which Spectre pointed out:
public static double TrueAnomaly(Vector4 position, double loP)
{
return Math.Atan2(position.Y, position.X) - loP;
}
Positions are all relative to the parent body.
These functions all agree if position.x, position.y and velocity.y are all positive.
How do I fix these so that I get a consistent results when position and velocity are negitive?
Just to clarify: My angles appear to be sort of correct, just pointing in the wrong quadrant depending on the position and or velocity vectors.
Yeah so I was wrong, the above all do return the correct values after all.
So I found an edge case where most of the above calculations fail.
Given position and velocity:
pos = new Vector4() { X = -0.208994076275941, Y = 0.955838328099748 };
vel = new Vector4() { X = -2.1678187689294E-07, Y = -7.93096769486992E-08 };
I get some odd results, ie ~ -31.1 degrees, when I think it should return ` 31.1 (non negative). one of them returns ~ 328.8.
However testing with this position and velocity the results apear to be ok:
pos = new Vector4() { X = -0.25, Y = 0.25 };
vel = new Vector4() { X = Distance.KmToAU(-25), Y = Distance.KmToAU(-25) };
See my answer for extra code on how I'm testing and the math I'm using for some of the other variables.
I'm going around in circles on this one. this is a result of a bug in my existing code that shows up under some conditions but not others.
I guess the real question now is WHY am I getting different results with position/velocity above that don't match to my expectations or each other?
Assuming 2D case... I am doing this differently:
compute radius of semi axises and rotation
so you need to remember whole orbit and find 2 most distant points on it that is major axis a. The minor axis b usually is 90 deg from major axis but to be sure just fins 2 perpendicularly most distant points on your orbit to major axis. So now you got both semi axises. The initial rotation is computed from the major axis by atan2.
compute true anomaly E
so if center is x0,y0 (intersection of a,b or center point of both) initial rotation is ang0 (angle of a) and your point on orbit is x,y then:
E = atan2(y-y0,x-x0) - ang0
However in order to match Newton/D'Alembert physics to Kepler orbital parameters you need to boost the integration precision like I did here:
Is it possible to make realistic n-body solar system simulation in matter of size and mass?
see the [Edit3] Improving Newton D'ALembert integration precision even more in there.
For more info and equations see:
Solving Kepler's equation
[Edit1] so you want to compute V I see it like this:
As you got your coordinates relative to parent you can assume they are already in focal point centered so no need for x0,y0 anymore. Of coarse if you want high precision and have more than 2 bodies (focal mass + object + proximity object(s) like moons) then the parent mass will no longer be in focal point of orbit but close to it ... and to remedy you need to use real focal point position so x0,y0 again... So how to do it:
compute center point (cx,cy) and a,b semi axises
so its the same as in previous text.
compute focal point (x0,y0) in orbit axis aligned coordinates
simple:
x0 = cx + sqrt( a^2 + b^2 );
y0 = cy;
initial angle ang0 of a
let xa,ya be the intersection of orbit and major axis a on the side with bigger speeds (near parent object focus). Then:
ang0 = atan2( ya-cy , xa-cx );
and finally the V fore any of yours x,y
V = atan2( y-y0 , x-x0 ) - ang0;
Ok so on further testing it appears my original calcs do all return the correct values, however when I was looking at the outputs I was not taking the LoP into account and basically not recognizing that 180 is essentially the same angle as -180.
(I was also looking at the output in radians and just didn't see what should have been obvious)
Long story short, I have a bug I thought was in this area of the code and got lost in the weeds.
Seems I was wrong above. see OP for edge case.
Here's some code I used to test these,
I used variations of the following inputs:
pos = new Vector4() { X = 0.25, Y = 0.25 };
vel = new Vector4() { X = Distance.KmToAU(-25), Y = Distance.KmToAU(25) };
And tested them with the following
double parentMass = 1.989e30;
double objMass = 2.2e+15;
double sgp = GameConstants.Science.GravitationalConstant * (parentMass + objMass) / 3.347928976e33;
Vector4 ev = OrbitMath.EccentricityVector(sgp, pos, vel);
double e = ev.Length();
double specificOrbitalEnergy = Math.Pow(vel.Length(), 2) * 0.5 - sgp / pos.Length();
double a = -sgp / (2 * specificOrbitalEnergy);
double ae = e * a;
double aop = Math.Atan2(ev.Y, ev.X);
double eccentricAnomaly = OrbitMath.GetEccentricAnomalyFromStateVectors(pos, a, ae, aop);
double aopD = Angle.ToDegrees(aop);
double directAngle = Math.Atan2(pos.Y, pos.X);
var θ1 = OrbitMath.TrueAnomaly(sgp, pos, vel);
var θ2 = OrbitMath.TrueAnomaly(ev, pos, vel);
var θ3 = OrbitMath.TrueAnomalyFromEccentricAnomaly(e, eccentricAnomaly);
var θ4 = OrbitMath.TrueAnomalyFromEccentricAnomaly2(e, eccentricAnomaly);
var θ5 = OrbitMath.TrueAnomaly(pos, aop);
double angleΔ = 0.0000001; //this is the "acceptable" amount of error, really only the TrueAnomalyFromEccentricAnomaly() calcs needed this.
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ1), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ2), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ3), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ4), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ5), angleΔ);
and the following to compare the angles:
public static double DifferenceBetweenRadians(double a1, double a2)
{
return Math.PI - Math.Abs(Math.Abs(a1 - a2) - Math.PI);
}
And eccentricity Vector found thus:
public static Vector4 EccentricityVector(double sgp, Vector4 position, Vector4 velocity)
{
Vector4 angularMomentum = Vector4.Cross(position, velocity);
Vector4 foo1 = Vector4.Cross(velocity, angularMomentum) / sgp;
var foo2 = position / position.Length();
return foo1 - foo2;
}
And EccentricAnomaly:
public static double GetEccentricAnomalyFromStateVectors(Vector4 position, double a, double linierEccentricity, double aop)
{
var x = (position.X * Math.Cos(-aop)) - (position.Y * Math.Sin(-aop));
x = linierEccentricity + x;
double foo = GMath.Clamp(x / a, -1, 1); //because sometimes we were getting a floating point error that resulted in numbers infinatly smaller than -1
return Math.Acos(foo);
}
Thanks to Futurogogist and Spektre for their help.
I am assuming you are working in two dimensions?
Two dimensional vectors of position p and velocity v. The constant K is the the product of the gravitational constant and the mass of the gravity generating body. Calculate the eccentricity vector
eccVector = (dot(v, v)*p - dot(v, p)*v) / K - p / sqrt(dot(p, p));
eccentricity = sqrt(dot(eccVector, eccVector));
eccVector = eccVector / eccentricity;
b = { - eccVector.y, eccVector.x}; //unit vector perpendicular to eccVector
r = sqrt(dot(p, p));
cos_TA = dot(p, eccVector) / r; \\ cosine of true anomaly
sin_TA = dot(p, b) / r; \\ sine of true anomaly
if (sin_TA >= 0) {
trueAnomaly = arccos(cos_TA);
}
else if (sin_TA < 0){
trueAnomaly = 2*pi - arccos(cos_TA);
}

Getting a Node from a work point in space

i am trying to get a Node form a position in a world space. the nodes are arranged in a grid fashion
Below is an image of how the grids of nodes are each large grid numbered 0 - 3
are made up from a 50 x 50 grid of nodes.
the red dots within each of the grid represents which nodes was checked when a point in the world was clicked.
for the large grid labeled 0 each node is access correctly
Grid 1 has the correct X values (left to right position) but the Z is clamped at the top of the grid
Grid 2 has the opposite where the X values are clamped to the top range and the Z values are correct
Grid 3 both X and Z are clamped to the max value.
Below is the code for grid from the world position
public Node NodeFromWorldPoint(Vector3 worldPosition)
{
//Gets the Large grid number 0-3
float Largex = worldPosition.x / (4 * gridWorldSize.x);
float LargeZ = worldPosition.z / (4 * gridWorldSize.z);
Largex = Mathf.Clamp01(Largex);
LargeZ = Mathf.Clamp01(LargeZ);
int lx = Mathf.RoundToInt((2 - 1) * Largex);
int lz = Mathf.RoundToInt((2 - 1) * LargeZ);
int tilenumber = lx * 2 + lz;
//get the position of the smaller grid
float percentX = worldPosition.x / (gridWorldSize.x * 2 );
float percentZ = worldPosition.z / (gridWorldSize.z * 2);
percentX = Mathf.Clamp01(percentX);
percentZ = Mathf.Clamp01(percentZ);
int x = Mathf.RoundToInt((gridSizeX - 1 ) * percentX);
int z = Mathf.RoundToInt((gridSizeZ - 1) * percentZ);
var gc = grids.ElementAt(tilenumber);
return gc[x, z];
}
If I understood right then this should work for u. It includes the lower boundary of the node and excludes upper boundary.. i.e if u give world point 2,0 then it will return 2nd node.
public Node NodeFromWorldPoint(Vector3 worldPosition)
{
//Gets the Large grid number 0-3
int LargeX = (int)worldPosition.x / (2 * gridWorldSize.x);
int LargeZ = (int)worldPosition.z / (2 * gridWorldSize.z);
int tilenumber = LargeX*2 + LargeZ;
//get the position of the smaller grid
float percentX = worldPosition.x/gridWorldSize.x - LargeX;
float percentZ = worldPosition.z/gridWorldSize.z - LargeZ;
int x = (int)percentX/gridSizeX;
int x = (int)percentZ/gridSizeZ;
var gc = grids.ElementAt(tilenumber);
return gc[x, z];
}

Formating the Line Chart in Windows Forms

I am new to c# programming and creating my first Windows forms application. I have a two 1-D Arrays, one represents the X Axis Values and the other represents Y axis.
I am trying to create a graph from them using the following code.
public void drawgraph()
{
chart1.Series["Series1"].Name = MemsName;
for (int i = 0; i < VmicArray.Length; i++)
{
chart1.Series[MemsName].Points.AddXY(VmicArray[i], SensitivityArray[i]);
}
chart1.ChartAreas[0].AxisX.Title = "Vmic Value";
chart1.ChartAreas[0].AxisY.Title = "Sensitivity";
}
I am getting the XAxis values which I have in Array(like -2.333333754 or 6.46870) with the interval of 5 which I have set. The range of X axis is have is from -4 to +8.
Is there anyone who can help me in getting a ouput like we get in Excel Graphs? I am trying for Long time always I mess up with the values of X axis.
I Need the graph with XAxis value from -10 to +10 with the interval of 1 and mapping the Y values to corresponding X values on graph.
Unfortunately I am not able to post any Images :(
If you want to project a certain range of values to another range you need to use linear interpolation between the values. First determine the old min and max values (MinX and MaxX) and define the new limits (-10, 10).
Then use a simple formula to determine the new x value of an arbitrary old value.
double MinX = VmicArray.min;
double MaxX = VmicArray.Max;
double NewMin = -10;
double NewMax = 10;
for (i = 0; i <= VmicArray.Count - 1; i++) {
// Linear interpolation
double NewX = VmicArray(i) / (MaxX - MinX) * (NewMax - NewMin) + NewMin;
}
Recalculate each X value before you use AddXY.
In order to just change the visible bounds of each axis you can use the XAxis.Minimum and XAxis.Maximum as well as the XAxis.Interval properties:
chart1.ChartAreas[0].AxisX.Minimum = -10;
chart1.ChartAreas[0].AxisX.Maximum = 10;
chart1.ChartAreas[0].AxisX.Interval = 1;

Calculating distance for multiple 3d points

I did do some searching around before asking this, but how can I determine given a collection of coordinates, which ones are within range of one another?
if I had an set of coordinates
I know I can do something like:
float[] point1 = new float[3] {756.0,26.0,-1410.0};
float[] point2 = new float[3] {752.0,28.0,-1391.0};
float[] point3 = new float[3] {552.0,28.0,12.0};
float[] point4 = new float[3] {668.0,29.0,12.0};
float[] point5 = new float[3] {869.0,35.0,13.0};
float[] point6 = new float[3] {768.0,29.0,-1407.0};
float[] point7 = new float[3] {769.0,35.0,-1393.0};
and then compare one set of coordinates to another, but what I'm hoping to do is have a List of coordinate, and I want to display the ones that are ONLY within a certain range of one another. I'm just not sure how to do more than 1 at a time.
List<float[]> Positions = new List<float[]>();
float[] location = new float[3] { entity.X, entity.Y, entity.Z }
Postions.Add(location)
... loop thru and add all values ...
int rSquare = 25; //minimum distance I want to be less than
int x0 = 10, y0 = 10, z0 = 10; //placeholder because I dont know what I'm comparing against
var res = locations
.Select(tmp =>
{
return new
{
x = tmp[0],
y = tmp[1],
z = tmp[2]
};
})
.Where(p => (p.x - x0) * (p.x - x0) + (p.y - y0) * (p.y - y0) + (p.z - z0) * (p.z - z0) < rSquare)
.ToList();
and that would give me one coordinate compared to another and return all that had a distance < 25. How can I compare any given set of coordinates to any other given set within the Positions list? The 'placeholder' is just there to give me something to go against but I'm hoping there is an easier way than comparing them all against themselves one at a time.
thanks
edit in response: I expect that I would get back any of the coordinates that fall within the desired distance from one another. In this case, from the given 7 points, I would expect to see the values (or some representation there of) of 1,2,6 and 7 since they are all within 25 of each other. The values for 3,4,5 are well distanced from anything else so I wouldn't want to see them. I gave 7 values but the list that I ultimately have may have hundreds of x/y/z locations I want to compare.
A brute force approach could just be:
// Given a distance function:
double DistSq(float[] point1, float[] point2)
{
return (point1[0] - point2[0]) * (point1[0] - point2[0])
+ (point1[1] - point2[1]) * (point1[1] - point2[1])
+ (point1[2] - point2[2]) * (point1[2] - point2[2]);
}
You can do:
distance = 5; // How far apart can 2 points be...
double distSq = distance * distance;
var pointsWithoutOutliers = Positions.Where(p => Positions.Any(o => !Object.ReferenceEquals(o,p) && DistSq(p,0)<distSq));

How to hit test shapes in Silverlight?

I need the ability to determine which Shape a given point falls within. There will be overlapped shapes and I need to find the Shape with the smallest area. For example, given the Shapes and points illustrated in the image below the following would be true:
Point 3 - collides with star
Point 2 - collides with diamond
Point 1 - collides with circle
Given this, I would like to know if there is a built in way to do what is needed.
If you are drawing these shapes manually, you could do a second drawing pass into a separate buffer, and instead of drawing the shape, you write an ID into the buffer if the pixel is within the shape. Then your hit test just has to index into that buffer and retrieve the ID. You would get to re-use your drawing code completely, and it scales much better when you have more shapes, vertices, and hits to test.
I've arrived at a solution that meets the requirements, still interested in hearing if there is a better way of doing this. My approach is as follows: do a hit-test by bounding box, then a geometric hit test based on the type of geometry.
For Polygons, I've adapted the C code mentioned http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes /pnpoly.html to work in C#.
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
For Ellipses, I've adaptated this code: http://msdn.microsoft.com/en-us/library/aa231172%28v=vs.60%29.aspx
BOOL CCircCtrl::InCircle(CPoint& point)
{
CRect rc;
GetClientRect(rc);
GetDrawRect(&rc);
// Determine radii
double a = (rc.right - rc.left) / 2;
double b = (rc.bottom - rc.top) / 2;
// Determine x, y
double x = point.x - (rc.left + rc.right) / 2;
double y = point.y - (rc.top + rc.bottom) / 2;
// Apply ellipse formula
return ((x * x) / (a * a) + (y * y) / (b * b) <= 1);
}

Categories

Resources