I am trying to rotate the clock hands using PanGestureRecognizer for BoxView.
Currently I can correctly rotate the hand, but only on one side of the clock, on the other side the hand does not move correctly. Also, the coordinates for some reason depend on how many times you rotate the arrow.
My OnPanUpdated function
switch (e.StatusType)
{
case GestureStatus.Running:
Point UpdatedPan = new Point(e.TotalX, e.TotalY);
UpdateHandRotation(hourHand, UpdatedPan);
break;
}
And the main function
private void UpdateHandRotation(BoxView hand, Point UpdatedPan)
{
// Get current hand rotation in radians
double r = hand.Rotation * Math.PI / 180;
Point center = new Point(absoluteLayout.Width / 2, absoluteLayout.Height / 2);
double radius = 0.45 * Math.Min(absoluteLayout.Width, absoluteLayout.Height);
double handEndX = Math.Cos(r) * radius + center.X;
double handEndY = Math.Sin(r) * radius + center.Y;
double movedEndX = handEndX + UpdatedPan.X;
double movedEndY = handEndY + UpdatedPan.Y;
double radians = Math.Atan2(movedEndY - center.Y, movedEndX - center.X);
// Convert to degrees
var angle = radians * (180.0 / Math.PI);
hand.Rotation = angle;
}
Here is an example of how this works.
Related
I've been trying to get this code to work properly for the past hour and I almost got it complete. Everything works, but the float verticalDegrees.
In Detail Question: How do I get this code working so it returns XYZ from horizontal degrees, vertical degrees, radius and origin?
This link helped me, but it's missing Z coordinate
This is what I have so far:
private float[] DegreesToXYZ(float horizonalDegrees, float verticalDegrees, float radius, float[] origin)
{
float[] xyz = new float[3];
double radiansH = horizonalDegrees * Math.PI / 180.0;
double radiansV = verticalDegrees * Math.PI / 180.0;
xyz[1] = (float)Math.Cos(radiansH) * radius + origin[1];
xyz[0] = (float)Math.Sin(-radiansH) * radius + origin[0];
double deltaXY = Math.Sqrt(origin[0] * origin[0] + origin[1] * origin[1]);
xyz[2] = (float)Math.Atan2(origin[2], deltaXY);
return xyz;
}
This method converts spherical coordinates into cartesian coordinates:
private static double[] DegreesToXYZ(double radius, double theta, double phi, double[] originXYZ)
{
theta *= Math.PI / 180;//converting degress into radians
phi *= Math.PI / 180;//converting degress into radians
double[] xyz = new double[3];
xyz[0] = originXYZ[0] + radius * Math.Cos(theta) * Math.Sin(phi);//x
xyz[1] = originXYZ[1] + radius * Math.Sin(theta) * Math.Sin(phi);//y
xyz[2] = originXYZ[2] + radius * Math.Cos(phi);//z
return xyz;
}
Where theta is the 'horizontal' or 'azimuth' angle (angle from the x-axis in the x-y plane), and phi is the 'inclination' (angle from the positive
z axis) or 'vertical' angle.The radius is the distance to a given point (x,y,z) in cartesian coordinates.
Seems you have spherical coordinates and want to get Cartesian coordinates. In this case
x = x0 + r * Cos(fi) * Sin(theta)
y = y0 + r * Sin(fi) * Sin(theta)
z = z0 + r * Cos(theta)
Here fi is your "horizontal angle", theta is "vertical angle", x0..z0 are origin coordinates
I have a line (DrawLine-Event) with the points (a,b) (c,d) and now I want to calculate the angle of it but I don't know how.
I tried it like this:
double atan = ((d - b) / c - a)) * Math.PI / 180;
double solution = Math.Atan(atan);
int angle = Convert.ToInt32(Math.Round(solution * 180 / Math.PI));
You can use Vector.AngleBetween Method to get the angle
private Double angleBetweenExample()
{
Vector vector1 = new Vector(20, 30);
Vector vector2 = new Vector(45, 70);
Double angleBetween;
// angleBetween is approximately equal to 0.9548
angleBetween = Vector.AngleBetween(vector1, vector2);
return angleBetween;
}
I was trying to rotate 2 windows form button as in the following image:
During their rotation, the distance between them should be 0 and when you click a label, they should "rotate" 90 degree, as like:
if (red is up and black is down)
{
red will be down and black will be up;
}
else
{
red will be up and black will be down;
}
I used this method to return the desired point "location", but i couldn't obtain the desired rotation "effect":
public static Point Rotate(Point point, Point pivot, double angleDegree)
{
double angle = angleDegree * Math.PI / 180;
double cos = Math.Cos(angle);
double sin = Math.Sin(angle);
int dx = point.X - pivot.X;
int dy = point.Y - pivot.Y;
double x = cos * dx - sin * dy + pivot.X;
double y = sin * dx + cos * dy + pivot.X;
Point rotated = new Point((int)Math.Round(x), (int)Math.Round(y));
return rotated;
}
like in my comment it has to look like this:
private Point calculateCircumferencePoint(double radoffset, Point center, double radius)
{
Point res = new Point();
double x = center.X + radius * Math.Cos(radoffset);
double y = center.Y + radius * Math.Sin(radoffset);
res.X = (int)x;
res.Y = (int)y;
return res;
}
here is also a test application: https://github.com/hrkrx/TestAppCircularRotation
EDIT: for a second button you just need to set the initial offset to Math.PI;
EDIT2: To rotate the buttons like they cross (like the path of an 8) you need to set the radius to Sin(radoffset) or Cos(radoffset)
I know that the theory of rotating a point by an angle is on the internet a million times, but I don't get my code to work properly.
I have a line with 2 points, when you click on 1 of the 2 points, you will rotate the point relative to the other point. In my testcase I have a upper left point and a bottom right point, so a diagonal line.
I want to make sure the line snaps to a 90 degrees rotation so It will always be a straight line (either vertically or horizontally). So I first get the current angle, then get the angle that It should be, and calculate the difference.
Point startPoint = obj2.Location;
Point currentEndPoint = new Point(obj2.Location.X + obj2.Size.Width, obj2.Location.Y + obj2.Size.Height);
Point newEndPoint = e.Location;
double angle = MathHelper.GetAngleOfVerticalLine(startPoint, newEndPoint);
double angleToBe = MathHelper.GetClosestNumber(angle, new double[] { 0, 90, 180, 270, 360 });
double angleToDo = 0.0; // -5
if (angle < angleToBe)
{
angleToDo = Math.Abs(angle - angleToBe);
}
else
{
angleToDo = angleToBe - angle;
}
angleToDo %= 360;
Point newSize = MathHelper.RotatePoint(newEndPoint, startPoint, angleToDo);
obj.Size = (Size)newSize;
public static Point RotatePoint(Point pointToRotate, Point centerPoint, double angleInDegrees)
{
double angleInRadians = angleInDegrees * (Math.PI / 180);
double cosTheta = Math.Cos(angleInRadians);
double sinTheta = Math.Sin(angleInRadians);
return new Point
{
X =
(int)
(cosTheta * (pointToRotate.X - centerPoint.X) -
sinTheta * (pointToRotate.Y - centerPoint.Y) + centerPoint.X),
Y =
(int)
(sinTheta * (pointToRotate.X - centerPoint.X) +
cosTheta * (pointToRotate.Y - centerPoint.Y) + centerPoint.Y)
};
}
But the results that I get, are not straight lines but they are kind of random. The angle, angleToBe and angleToDo work properly. The RotatePoint method should be the problem then, but I'm not 100% sure about that.
Can't you use the Matrix.Rotate class to do the heavy lifting? Source: http://msdn.microsoft.com/en-us/library/s0s56wcf.aspx (Of course the math is half the fun).
I'm looking for a bit of math help. I have a game were a 2D heightmap is generated and then stretched over a sphere using a length/direction formula. Now I need to know how to calculate the height between 2 points on my heightmap.
What I know:
The array that holds the heightmap
The angle in radians to my object
how many points there are on the heightmap
My problem look somewhat like so:
image
more images
The red and blue lines are the 2 heightmap points, and the light blue is where I'd like to calculate the height at.
Here's my current code to do it, but it doesn't work to well.
public double getheight(double angle)
{
//find out angle between 2 heightmap point
double offset = MathHelper.TwoPi / (heightmap.Length - 1);
//total brainfart attempt
double lowerAngle = offset * angle;
double upperAngle = offset * angle + offset;
//find heights
double height1 = heightmap[(int)lowerAngle];
double height2 = heightmap[(int)upperAngle];
//find offset angle
double u = angle - lowerAngle / (upperAngle - lowerAngle);
//return the height
return height1 + (height1 - height2) * u;
}
from my vegetation code, this seems to work okay, but is to rough to use for units and such, as they jump up/down as they move, due to it using only 1 heightmap point.
double[] hMap = planet.getHeightMap();
double i = hMap.Length / (Math.PI * 2);
this.height = hMap[(int)(angle * i)];
EDIT: example at end based on additional question info
Sounds to me like a linear interpolation - if you look at it from a 2d point of view, you've got two points:
(x1, y1) = point one on heightmap
(x2, y2) = point two on heightmap
and one point somewhere between (x1,x2) at an unknown height:
pu = (xu, yu)
A generic formula for LERP is:
pu = p0 + (p1 - p0) * u
where:
p0 = first value
p1 = second value
u = % where your unknown point lies between (p0,p1)
Here, we'll say p0 == y2 and p1 == y1. Now we need to determine "how far" the unknown point is between x1 and x2 - if you know the angles to the two heightmap points, this is easy:
u = ang(xu) - ang(x1) / (ang(x2) - ang(x1))
Alternatively, you could project your angle out to Max(y1,y2) and get the "unknown x pos" that way, then calculate the above.
So, let's try a contrived example:
p1 = point one in map = (1,2) therefore ang(p1) ~ 57 degrees
p2 = point two in map = (2,4) therefore ang(p2) ~ 114 degrees
note that here, the "x axis" is along the surface of the sphere, and the "y-axis" is the distance away from the center.
pu = object location = py #angle 100 degrees ~ 1.74 radians
px = (1.74 rad - 1 rad ) / (2 rad - 1 rad) = 0.74 / 1.0 = 0.74 => 74%
py = y0 + (y1 - y0) * u
= 2 + (4 - 2) * 0.74
= 2.96
Hopefully I didn't drop or misplace a sign there somewhere... :)
Ok, your example code - I've tweaked it a bit, here's what I've come up with:
First, let's define some helpers of my own:
public static class MathHelper
{
public const double TwoPi = Math.PI * 2.0;
public static double DegToRad(double deg)
{
return (TwoPi / 360.0) * deg;
}
public static double RadToDeg(double rad)
{
return (360.0 / TwoPi) * rad;
}
// given an upper/lower bounds, "clamp" the value into that
// range, wrapping over to lower if higher than upper, and
// vice versa
public static int WrapClamp(int value, int lower, int upper)
{
return value > upper ? value - upper - 1
: value < lower ? upper - value - 1
: value;
}
}
Our Test setup:
void Main()
{
var random = new Random();
// "sea level"
var baseDiameter = 10;
// very chaotic heightmap
heightmap = Enumerable
.Range(0, 360)
.Select(_ => random.NextDouble() * baseDiameter)
.ToArray();
// let's walk by half degrees, since that's roughly how many points we have
for(double i=0;i<360;i+=0.5)
{
var angleInDegrees = i;
var angleInRads = MathHelper.DegToRad(i);
Console.WriteLine("Height at angle {0}°({1} rad):{2} (using getheight:{3})",
angleInDegrees,
angleInRads,
heightmap[(int)angleInDegrees],
getheight(angleInRads));
}
}
double[] heightmap;
And our "getheight" method:
// assume: input angle is in radians
public double getheight(double angle)
{
//find out angle between 2 heightmap point
double dTheta = MathHelper.TwoPi / (heightmap.Length);
// our "offset" will be how many dThetas we are
double offset = angle / dTheta;
// Figure out two reference points in heightmap
// THESE MAY BE THE SAME POINT, if angle ends up
// landing on a heightmap index!
int lowerAngle = (int)offset;
int upperAngle = (int)Math.Round(
offset,
0,
MidpointRounding.AwayFromZero);
// find closest heightmap points to angle, wrapping
// around if we go under 0 or over max
int closestPointIndex = MathHelper.WrapClamp(
lowerAngle,
0,
heightmap.Length-1);
int nextPointIndex = MathHelper.WrapClamp(
upperAngle,
0,
heightmap.Length-1);
//find heights
double height1 = heightmap[closestPointIndex];
double height2 = heightmap[nextPointIndex];
// percent is (distance from angle to closest angle) / (angle "step" per heightmap point)
double percent = (angle - (closestPointIndex * dTheta)) / dTheta;
// find lerp height = firstvalue + (diff between values) * percent
double lerp = Math.Abs(height1 + (height2 - height1) * percent);
// Show what we're doing
Console.WriteLine("Delta ang:{0:f3}, Offset={1:f3} => compare indices:[{2}, {3}]",
dTheta,
offset,
closestPointIndex,
nextPointIndex);
Console.WriteLine("Lerping {0:p} between heights {1:f4} and {2:f4} - lerped height:{3:f4}",
percent,
height1,
height2,
lerp);
return lerp;
}