I want to calculate the lean angle when I ride my motorcycle at a track and think about using my lumia 920 for that. The product that got my interest was http://leanometer.com . I found out that using only a gyroscope is bad as it will drift over time so using a complementary filter with the accelerometer seemed the way to go. I found this code for doing this from http://www.pieter-jan.com/node/11:
#define ACCELEROMETER_SENSITIVITY 8192.0
#define GYROSCOPE_SENSITIVITY 65.536
#define M_PI 3.14159265359
#define dt 0.01 // 10 ms sample rate!
void ComplementaryFilter(short accData[3], short gyrData[3], float *pitch, float *roll)
{
float pitchAcc, rollAcc;
// Integrate the gyroscope data -> int(angularSpeed) = angle
*pitch += ((float)gyrData[0] / GYROSCOPE_SENSITIVITY) * dt; // Angle around the X-axis
*roll -= ((float)gyrData[1] / GYROSCOPE_SENSITIVITY) * dt; // Angle around the Y-axis
// Compensate for drift with accelerometer data if !bullshit
// Sensitivity = -2 to 2 G at 16Bit -> 2G = 32768 && 0.5G = 8192
int forceMagnitudeApprox = abs(accData[0]) + abs(accData[1]) + abs(accData[2]);
if (forceMagnitudeApprox > 8192 && forceMagnitudeApprox < 32768)
{
// Turning around the X axis results in a vector on the Y-axis
pitchAcc = atan2f((float)accData[1], (float)accData[2]) * 180 / M_PI;
*pitch = *pitch * 0.98 + pitchAcc * 0.02;
// Turning around the Y axis results in a vector on the X-axis
rollAcc = atan2f((float)accData[0], (float)accData[2]) * 180 / M_PI;
*roll = *roll * 0.98 + rollAcc * 0.02;
}
}
I have converted this code to c# but I have problems to get something accurate with this. One thing is that I do not know the gyro/accelerometer sensitivity or how to get it. After a while I started to google some more on acclerometer and angle and found this: http://www.hobbytronics.co.uk/accelerometer-info which is a bit different from the angle above from the accelerometer but it seemed to work. When using the algorithm from hobbytronics and putting it into the code above I got a strange behavior so that it Always tried to get near -1.4 degrees angle.
I got the real code on Another computer but this is how I do it:
var lastReading = new DateTime();
var angleX = 0.0;
var gyro = new Gyroscope();
gyro.TimeBetweenUpdates = 20ms;
gyro.CurrentValueChanged += ValueChanged;
var acc = new Accelerometer();
acc.TimeBetweenUpdates = 20ms;
gyro.Start(); acc.Start();
void ValueChanged(SensorReading reading)
{
if(lastReading < 1 sec old)
{
var dt = lastReading - reading.Timestamp;
if(reading.X > 0.05 || Reading.X < -0.05) // Got so much errors if I did not use this, maybe too high?
{
var x = reading.X * dt.TotalSeconds;
var accData = acc.CurrentValue();
var accX = atan(accData.X / Math.Sqrt(accData.Y*accData.Y+accData.Z*accData.Z));
angleX = 0.98*(angleX + x *180 / Math.PI) + 0.02 * (accX *180 /Math.PI);
TxtAngleX.Text = "x:" + angleX.ToString("F");
}
}
lastReading = reading.Timestamp;
}
What am I missing? One way to make it better could be to set the angle from accelerometer only, when the lastReading is older then one sec but I know that that is not the problem
Related
My Program will ideally create a series of stacked squares based on 2 variables. 1 Is the amount of squares per series and the other is how many series there will be. each series is branched out from the center of the screen at different angles spaced evenly apart. If there are 2 series, one will be facing 0 degrees and the other will be facing 180 degrees. If there is 3, the first will be facing 0 degrees, second will face 120, and third will face 240. Somewhere in my math or my method of making this effect, I messed up bad.
The current result is, for some reason, the Fibonacci spiral. Here is the Are the suspected functions below (with all variable definitions)
also, button is a class I made and not a typo:
public button Led; //Object used to create series
public Slider ledNumber;// controls how many LEDS there are per series
public Slider IncrementsPerRotation;//Controls how many series there are
public Text ledText;//text above ledNumber slider
public Text incrementText;//text above IncrementsPerRotation Slider
public Transform canvas;//Canvas where objects will be created
List<List<button>> LedConfig = new List<List<button>>();//where all the LEDs will be stored
private float buttonW, middleX,middleY; // the width of the LED and the middle values of the screen
private int prevLed = 0, prevIncrements = 0;// previous values for both sliders
// Start is called before the first frame update
void Start()
{
buttonW = Led.transform.localScale.x;// set LED width
Vector2 temp = Camera.main.ScreenToWorldPoint(new Vector2 (Screen.width / 2, Screen.height / 2));// get middle values
//assign middle values
middleX = temp.x;
middleY = temp.y;
}
// Update is called once per frame
void Update()
{
if (prevLed != (int)ledNumber.value && (int)ledNumber.value > 0)//has the slider changed and is it positive
{
Debug.Log("prevLED Changed");
if (prevLed < ledNumber.value)// am I removing or adding buttons
{
Debug.Log("Adding LED");
for (int x = 0; x < (int)ledNumber.value - prevLed; ++x)//for the difference in the number changed
{
for (int y = 0; y < IncrementsPerRotation.value; y++)//add a led to the top row
{
float angle = 360 / IncrementsPerRotation.value * y;// get angle
// if there isnt enough lists, add another one;
if(LedConfig.Count-1 < y)
{
LedConfig.Add(new List<button>());
++prevIncrements;
}
//object: LED Where: (see GetPos) What angle: angle converted to radians
LedConfig[y].Add(Instantiate(Led, GetPos(y, angle), new Quaternion(Quaternion.identity.x, Quaternion.identity.y, angle * (Mathf.PI / 180f), Quaternion.identity.w),canvas));
Debug.Log("got past instantiate");
}
++prevLed;
Debug.Log("end of outer for loop");
}
}
else
{
Debug.Log("Removing LED");
for (int x = 0; x < prevLed - (int)ledNumber.value; ++x)//for the difference in the number changed
{
for (int y = 0; y < LedConfig.Count; y++)//delete the top row of LEDs
{
LedConfig[y][LedConfig[y].Count - 1].delete();//delete the LED
LedConfig[y].RemoveAt(LedConfig[y].Count - 1);//remove it from the list
}
--prevLed;
Debug.Log("end of outer for loop");
}
}
}
if (prevIncrements != (int)IncrementsPerRotation.value && (int)IncrementsPerRotation.value > 0)
{
deleteAll();
prevLed = 0;
}
}
void deleteAll()
{
for (int x = 0; x < LedConfig.Count; ++x)//for the difference in the number changed
{
for (int y = 0; y < LedConfig[x].Count; y++)//delete the top row of LEDs
{
LedConfig[y][LedConfig[y].Count - (y + 1)].delete();//delete the LED
LedConfig[y].RemoveAt(LedConfig[y].Count - (y + 1));//remove it from the list
}
}
}
private Vector2 GetPos(int a,float angle)
{
float angleInRadians = angle * (Mathf.PI / 180f);
++a;
Debug.Log("x " + (buttonW * a) * (Mathf.Sin(angleInRadians) * 180 / Mathf.PI));
Debug.Log("y " + (buttonW * a) * (Mathf.Cos(angleInRadians) * 180 / Mathf.PI));
Debug.Log("middleX " + middleX);
Debug.Log("middleY " + middleY);
Debug.Log("tot X " + (middleX + buttonW * a * Mathf.Sin(angleInRadians)));
Debug.Log("tot Y " + (middleY + buttonW * a * Mathf.Cos(angleInRadians)));
return new Vector2(middleX + buttonW * a * Mathf.Sin(angleInRadians), middleY + buttonW * a * (Mathf.Cos(angleInRadians)));
}
The code is having many errors:
Moving the ledNumber creates multiple squares on the same spot
Moving the IncrementsPerRotations causes an index out of range error which pauses the game, if the game is continued (by pressing the pause button) the Fibonacci spiral is produced with the squares and the squares are at random angles.
This is not all the code, only the part which is related to the error.
Edit: Here is a visual aid of what the desired output should look like at increments per rotation values 1-4 and led number at 3
Background
I currently have an object that I am displaying that will always be at the origin. I have a function which increments my x and y angles and then calculates the new x,y,z coordinate for the camera:
Public Sub update_rotation()
If cam.manual_lookat = True Then
If camangley >= 360 Then
camangley = camangley - 360
End If
If camanglex >= 360 Then
camanglex = camanglex - 360
End If
If camangley < 0 Then
camangley = 360 + camangley
End If
If camanglex < 0 Then
camanglex = 360 + camanglex
End If
If camangley > 90 And camangley <= 270 Then
cam.invert_y = True
Else
cam.invert_y = False
End If
camx = distance * -Sin(camanglex * (PI / 180)) * Cos((camangley) * (PI / 180))
camy = distance * -Sin((camangley) * (PI / 180))
camz = distance * Cos((camanglex) * (PI / 180)) * Cos((camangley) * (PI / 180))
cam.Position.X = camx
cam.Position.Y = camy
cam.Position.Z = camz
cam.lookat.X = 0
cam.lookat.Y = 0
cam.lookat.Z = 0
' Label2.Text = camanglex & "," & camangley
End If
End Sub
I have this set up to use keyboard events.. the X button adds to the camanglex variable, the Y button adds to the camangley variable, and the Z button adds to the distance variable.
Everything works well doing it this way, using the keyboard.
Problem
I am trying to now use the mouse to handle the rotation instead of the keyboard. I believe this is simply a math question but how do I go about calculating either the new camanglex and camangley variables or directly calculating the new camx,camy,camz ones to establish my cameras new location?
I have a mouse function which will capture the mouse coordinates but am having trouble with the calculation part.
If you want to orbit around object you could trace the path your mouse goes on fake sphere (which is called trackball sometimes). There is a working example of orbit controls.
And here is a pseudocode sketch for something similiar:
mouseDownEventFunction(event) {
computeDragPoint (event.mouseX, event.mouseY, &oldDragPoint);
isMouseDown = true;
}
mouseUpEventFunction(event) {
isMouseDown = false;
}
mouseMoveEventFunction(event) {
if (isMouseDown) {
computeDragPoint (event.mouseX, event.mouseY, &newDragPoint);
rotateCamera (oldDragPoint, newDragPoint);
oldDragPoint = newDragPoint;
}
}
Here we find a point on trackball:
/* we want to ray trace a point on face side of fake sphere.
dragPoint* is our result*/
computeDragPoint(int x, int y, Vector3* dragPoint) {
/* normalize x and y to [-1, 1] so they match flat circle position on screen.
And assign this to dragPoint->x and dragPoint->y.
This part depends on what you want to achieve and input units of x, y.
Keep in mind aspect ratio */
dragPoint->x = (2*x/screenWidth - 0.5) * (screenHeight/screenWidth);
dragPoint->y = 2*y/screenHeight - 0.5;
dragPoint->x /= sqrt(dragPoint->x*dragPoint->x + dragPoint->y*dragPoint->y);
dragPoint->y /= sqrt(dragPoint->x*dragPoint->x + dragPoint->y*dragPoint->y);
/* Then having two values in [-1,1] compute corresponding z */
float tmp = dragPoint->x*dragPoint->x + dragPoint->y*dragPoint->y;
if (tmp > 1) {
dragPoint.x /= sqrt(tmp);
dragPoint.y /= sqrt(tmp);
}
dragPoint->z = -sqrt (1 - dragPoint->x^2 - dragPoint->y^2) || 0;
}
And rotation math can be done with quaternions (or just matrices from Euler angles if you don't want to use quats):
rotateCamera(oldDragPoint, newDragPoint) {
Quaternion quat = new Quaternion (Config.AngleX, Config.AngleY, Config.AngleZ);
quat.normalize();
Quaternion deltaQuat = new Quaternion();
deltaQuat = deltaQuat.setFromUnitVectors(oldDragPoint, newDragPoint);
quat = quat.multiply(deltaQuat);
quat.normalize();
Vector3 resultAngles = Vector3();
resultAngles.setFromQuaternion(quat);
setCameraPositionFromAngles(resultAngles);
}
And in setCameraPositionFromAngles(resultAngles) set your final X, Y, Z coordinates by applying three basic rotations to your initial camera position. So your main parameters are resultAngles. You renew angles and then set position.
Maybe this working example will explain better.
If I have two points p1 and p2 where p1 is the pivot point and p2 is the original direction the user was headed and they have a number of possible directions to go p3...pn in random sequence. How do I get the angles between the choices and the segment formed by p1,p2 as clockwise(right hand) positive values between 0 and 360 so that I can sort them from least to greatest?
Also the points p1...pn will be in any quadrant, I can’t assume they will always be in the positive x,y direction. The grid is a standard Cartesian grid not screen coordinates so Y gets smaller as you go down not larger.
So in this example (sorry for the poor drawing but Paint was all I had on my laptop) I need to get the angles:
(p2-p1-p3)
( p2-p1-p4)
( p2-p1-p5)
( p2-p1-p6)
In this order(smallest right hand turn to largest right hand turn):
[( p2-p1-p4), ( p2-p1-p6), ( p2-p1-p5), (p2-p1-p3)]
The points in my case are a class called Vertex:
public class Vertex
{
public double X = 0;
public double Y = 0;
public Vertex() { }
public Vertex(double x, double y)
{
X = x;
Y = y;
}
}
And the code for getting the angles and sorting looks like this right now but has a problem:
private static IEnumerable<Vertex> SortByAngle(Vertex original, Vertex pivot, List<Vertex> choices)
{
choices.Sort((v1, v2) => GetTurnAngle(original, pivot, v1).CompareTo(GetTurnAngle(original, pivot, v2)));
return choices;
}
private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
{
var a = original.X - pivot.X;
var b = original.Y - pivot.Y;
var c = choice.X - pivot.X;
var d = choice.Y - pivot.Y;
var rads = Math.Acos(((a * c) + (b * d)) / ((Math.Sqrt(a * a + b * b)) * (Math.Sqrt(c * c + d * d))));
return (180 / Math.PI * rads);
}
The problem is the above is if I check it for:
original 66,-66
pivot 280,-191
choice 200,-180
I get an angle of 22.460643124 instead of 337.539356876 which means it went counter-clockwise from the original direction to get that angle. I need it to always go clockwise to get the angle.
What am I doing wrong and how do I fix it?
Update: OK so according to what you guys are saying I can probably use some cross product like math to determine CW vs CCW so the new method would look like this:
private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
{
var a = original.X - pivot.X;
var b = original.Y - pivot.Y;
var c = choice.X - pivot.X;
var d = choice.Y - pivot.Y;
var angle = Math.Acos(((a * c) + (b * d)) / ((Math.Sqrt(a * a + b * b)) * (Math.Sqrt(c * c + d * d))));
angle = (180 / Math.PI * angle);
var z = (choice.X - pivot.X) * (original.Y - pivot.Y) - (choice.Y - pivot.Y) * (original.X - pivot.X);
if (z < 0)
{
return 360 - angle;
}
return angle;
}
Update 2:
Using the accepted solution it now looks like so:
private static double GetTurnAngle(Vertex original, Vertex pivot, Vertex choice)
{
var angle1 = Math.Atan2(original.Y - pivot.Y, original.X - pivot.X);
var angle2 = Math.Atan2(choice.Y - pivot.Y, choice.X - pivot.X);
var angleDiff = (180 / Math.PI * (angle2 - angle1));
if (angleDiff > 0)//It went CCW so adjust
{
return 360 - angleDiff;
}
return -angleDiff;//I need the results to be always positive so flip sign
}
So far as I can tell that works great so far. Thank you guys for the help!
Take a look at atan2 function. It takes delta y and delta x, so can distinguish all angles.
angle1 = atan2(p1.y-p0.y, p1.x-p0.x);
angle2 = atan2(p2.y-p0.y, p2.x-p0.x);
angle = angle2 - angle1;
If angle is negative, then CW, if positive CCW (or other way around depending on your axis orientation). Note |angle| may be > 180, in which case you may want to do 360-|angle| and reverse the CW CCW conclusion if you're after the shortest route.
You find the Dn=direction from p1 to pn (x=pn.x-p1.x and y=pn.y-p1.y) by the formula:
Dn=f(x,y)=180-90*(1+sign(y))* (1-sign(x^2))-45*(2+sign(y))*sign(x)
-180/pi()*sign(x*y)*atan((abs(y)-abs(x))/(abs(y)+abs(x)))
So the angles are Angle(p2-p1-pn)=Dn-D2.
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;
}
I'm working on a metrology laboratory and i need to develop a software in c# for a roundness-meter equipment, I've already started and I've found a problem, I need the software to show real-time graphics from the measurement that is being made, for that I'll need to use a library like Mscharts or Zedgraph, that is really fast for refreshing the information and has support for round graphs like Polar or radar, especially polar charts.
The problem I've seen in most libraries is that they all lack support for round graphs and are relatively slow.
Does anyone has a sugestion of a lybrary I could use?
Thank you for your help.
PS:The Software should show graphics like these ones:
I would render them with GDI(+) (in winforms app).
Yes that is basic line drawing, but it will be powerful enough for the examples you gave. You need to refresh your highschool mathematics, but it will give you lots of control over the output and it will be fast.
Not sure if this will help or not. Xceed Charts in their advanced section talks about doing polar charts. Unfortunately they did not provide any images so you should talk to their sales people and see if you can get an eval copy to evaluate.
Consider using the roguewave IMSL Numerical .NET library
homepage of the IMSL numerical .NET library
Examples of graph, resembling what you have posted above
Especially the Polar plot seems to be what you need here.
I was surprised that ZedGraph doesn't support polar graphs out-of-the-box and there are very few examples online. Using this guildline, I created my own polar graph with ZedGraph in C#. I hope that WillKraemer has solved his problem already (4 years passed) and someone else finds my implementation useful.
First is the ZedGraphControl initialization:
myZg = new ZedGraphControl();
GraphPane myPane = myZg.GraphPane;
// Init lists
RadarPointList dseries1 = new RadarPointList();
RadarPointList dseries2 = new RadarPointList();
// Maximize available space in pane
myPane.Legend.Position = LegendPos.InsideTopLeft;
myPane.Title.IsVisible = false;
myPane.XAxis.IsVisible = false;
myPane.YAxis.IsVisible = false;
myPane.Border.IsVisible = false;
myPane.Chart.Border.IsVisible = false;
myPane.Margin.All = 0;
// Create concentric grid with 30 degrees spacing & add corresponding labels
for (double i = 0; i < 36; i+=3.0)
{
TextObj gridlbs = new TextObj((i * 10.0).ToString("0°"), (radius + 10.0) * Math.Cos((i * 10.0 * Math.PI) / 180.0), (radius + 10.0) * Math.Sin((i * 10.0 * Math.PI) / 180.0));
gridlbs.FontSpec.Border.IsVisible = false;
LineObj gridlns = new LineObj(0, 0, radius * Math.Cos((i * 10.0 * Math.PI) / 180.0), radius * Math.Sin((i * 10.0 * Math.PI) / 180.0));
myPane.GraphObjList.Add(gridlbs);
myPane.GraphObjList.Add(gridlns);
}
// Draw circular grid, 5 should look okay
for (double i = (radius / 5.0); i <= radius; i += (radius / 5.0))
{
EllipseObj gridcrl = new EllipseObj(-i, i, 2.0 * i, 2.0 * i);
gridcrl.ZOrder = ZOrder.E_BehindCurves;
myPane.GraphObjList.Add(gridcrl);
}
// Make sure the pane is big enough to fit the labels around the polar plot
myPane.XAxis.Scale.Min = -(radius + 20.0);
myPane.XAxis.Scale.Max = (radius + 20.0);
myPane.YAxis.Scale.Min = -(radius + 20.0);
myPane.YAxis.Scale.Max = (radius + 20.0);
_selectedRadius = radius;
// Keep X & Y axis in the correct ratio to avoid distorting polar circle
myZg_Resize((object)"Startup", EventArgs.Empty);
myZg.Resize += new EventHandler(myZg_Resize);
myZg.ZoomEvent += new ZedGraphControl.ZoomEventHandler(myZg_ZoomEvent2);
// Draw snailly curves (example)
for (int i = 0; i < 360; i++)
{
double r = (double)i/360.0 * radius;
PointPair pt = new PointPair(PointPair.Missing, r, null);
dseries1.Add(pt);
PointPair pt2 = new PointPair(PointPair.Missing, radius - r, null);
dseries2.Add(pt2);
}
// Curves are somple LineItem
FirstCurve = myPane.AddCurve("Snail", dseries1, Color.Blue, SymbolType.None);
SecondCurve = myPane.AddCurve("antiSnail", dseries2, Color.Red, SymbolType.None);
// Rotate the lists to aling with labels
dseries1.Rotation = 0;
dseries2.Rotation = 0;
I had to make sure that the graph is not distorted when the form/control resizes, so I added this in the Resize Event:
protected void myZg_Resize(object sender, EventArgs e)
{
GraphPane pane = myZg.GraphPane;
myZg.AxisChange();
bool IsXMin = ( pane.Rect.Width < pane.Rect.Height ) ? true : false;
if (IsXMin)
{
// Scale based on X (width)
pane.XAxis.Scale.Max = (radius + 20.0); pane.XAxis.Scale.Min = -(radius + 20.0);
double xPixPerUnit = (double)pane.Chart.Rect.Width / (pane.XAxis.Scale.Max - pane.XAxis.Scale.Min);
pane.YAxis.Scale.Max = (double)pane.Chart.Rect.Height / xPixPerUnit / 2.0;
pane.YAxis.Scale.Min = -pane.YAxis.Scale.Max;
myZg.AxisChange();
}
else
{
// Scale based on Y (height)
pane.YAxis.Scale.Max = (radius + 20.0); pane.YAxis.Scale.Min = -(radius + 20.0);
double yPixPerUnit = (double)pane.Chart.Rect.Height / (pane.YAxis.Scale.Max - pane.YAxis.Scale.Min);
pane.XAxis.Scale.Max = (double)pane.Chart.Rect.Width / yPixPerUnit / 2.0;
pane.XAxis.Scale.Min = -pane.XAxis.Scale.Max;
myZg.AxisChange();
}
}
Also, I decided to block the user from any zooming actions:
protected void myZg_ZoomEvent2(ZedGraphControl sender, ZoomState oldState, ZoomState newState)
{
myZg_Resize("zoomevent", EventArgs.Empty);
}
The output looks like the picture below:
Suggestions always welcome!