How to plot a 3D Graph to represent an object in space - c#

I have a robot that outputs x,y,z position in space. My problem is that I can only find 2D plot in windows forms using chart.
I want to plot my robot in 3D space. Any tools I can use??
Something similar to this:
I need a free software solution for this
EDIT:
My 2D graph atm:
chart1.ChartAreas[0].AxisX.Minimum = 0;
chart1.ChartAreas[0].AxisX.Maximum = 12;
chart1.ChartAreas[0].AxisX.Interval = 1;
chart1.ChartAreas[0].AxisY.Minimum = 0;
chart1.ChartAreas[0].AxisY.Maximum = 7;
chart1.ChartAreas[0].AxisY.Interval = 1;
//example
posicao_atual_master.X = 10;
posicao_atual_master.Y = 5;
chart1.Series[0].Points.Clear();
chart1.Series[0].Points.AddXY(posicao_atual_master.X, posicao_atual_master.Y);
DESIGNER:
// chart1
//
chartArea1.AxisX.MajorGrid.Enabled = false;
chartArea1.AxisX.MajorTickMark.Enabled = false;
chartArea1.AxisY.MajorGrid.Enabled = false;
chartArea1.AxisY.MajorTickMark.Enabled = false;
chartArea1.Name = "ChartArea1";
chartArea1.Position.Auto = false;
chartArea1.Position.Height = 100F;
chartArea1.Position.Width = 90F;
this.chart1.ChartAreas.Add(chartArea1);
legend1.BackColor = System.Drawing.Color.Transparent;
legend1.BorderColor = System.Drawing.Color.Transparent;
legend1.Font = new System.Drawing.Font("Microsoft Sans Serif", 4F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Millimeter, ((byte)(1)), true);
legend1.IsTextAutoFit = false;
legend1.Name = "legen";
legend1.TableStyle = System.Windows.Forms.DataVisualization.Charting.LegendTableStyle.Tall;
this.chart1.Legends.Add(legend1);
this.chart1.Location = new System.Drawing.Point(543, 49);
this.chart1.Name = "chart1";
series1.ChartArea = "ChartArea1";
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Point;
series1.Color = System.Drawing.Color.Transparent;
series1.Legend = "legen";
series1.MarkerBorderColor = System.Drawing.Color.Black;
series1.MarkerImage = "C:\\Users\\Tiago\\Desktop\\CODIGO_TESE_FINAL_BACKUP1408_BOM\\C# - AR.Drone SDK\\AR.Dron" +
"e\\icone_drone_verde.png";
series1.MarkerImageTransparentColor = System.Drawing.Color.Red;
series1.Name = "Master";
series2.ChartArea = "ChartArea1";
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Point;
series2.Legend = "legen";
series2.MarkerImage = "C:\\Users\\Tiago\\Desktop\\CODIGO_TESE_FINAL_BACKUP1408_BOM\\Fotos dos Relatórios\\icon" +
"e_drone_vermelho.png";
series2.Name = "Slave";
this.chart1.Series.Add(series1);
this.chart1.Series.Add(series2);
this.chart1.Size = new System.Drawing.Size(1159, 359);
this.chart1.TabIndex = 7;
this.chart1.Text = "chart1";
this.chart1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.chart1_MouseDown);
this.chart1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.chart1_MouseMove);
this.chart1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.chart1_MouseUp);
EDIT: IMAGE

You are correct, there is no proper way to use a real z-axis in the Chart control.
It does have a 3D style though, which can be used for a reasonably nice ChartArea.
You will have to do the painting of the graph in code though, as the built-in z-axis only support as many, or rather as few discret values as you have Series in the chart.
This is ok for some things, like a color cube, but when you need arbitryry data values it just won't do.
Instead you can do this:
Store the z-value of each DataPoint along with the Y-value in the YValues array.
For this you need a ChartType that supports several YValues
Code one of the xxxPaint events to draw the graphics
For this you need a conversion from values to pixels
First we prepare the chart. Many details are up to your needs;
void prepare3dChart(Chart chart, ChartArea ca)
{
ca.Area3DStyle.Enable3D = true; // set the chartarea to 3D!
ca.AxisX.Minimum = -250;
ca.AxisY.Minimum = -250;
ca.AxisX.Maximum = 250;
ca.AxisY.Maximum = 250;
ca.AxisX.Interval = 50;
ca.AxisY.Interval = 50;
ca.AxisX.Title = "X-Achse";
ca.AxisY.Title = "Y-Achse";
ca.AxisX.MajorGrid.Interval = 250;
ca.AxisY.MajorGrid.Interval = 250;
ca.AxisX.MinorGrid.Enabled = true;
ca.AxisY.MinorGrid.Enabled = true;
ca.AxisX.MinorGrid.Interval = 50;
ca.AxisY.MinorGrid.Interval = 50;
ca.AxisX.MinorGrid.LineColor = Color.LightSlateGray;
ca.AxisY.MinorGrid.LineColor = Color.LightSlateGray;
// we add two series:
chart.Series.Clear();
for (int i = 0; i < 2; i++)
{
Series s = chart.Series.Add("S" + i.ToString("00"));
s.ChartType = SeriesChartType.Bubble; // this ChartType has a YValue array
s.MarkerStyle = MarkerStyle.Circle;
s["PixelPointWidth"] = "100";
s["PixelPointGapDepth"] = "1";
}
chart.ApplyPaletteColors();
addTestData(chart);
}
Here we add some test data:
void addTestData(Chart chart)
{
Random rnd = new Random(9);
for (int i = 0; i < 100; i++)
{
double x = Math.Cos(i/10f )*88 + rnd.Next(5);
double y = Math.Sin(i/11f)*88 + rnd.Next(5);
double z = Math.Sqrt(i*2f)*88 + rnd.Next(5);
AddXY3d( chart.Series[0], x, y, z);
AddXY3d( chart.Series[1], x-111, y-222, z);
}
}
The DataPoints are added with this routine:
int AddXY3d(Series s, double xVal, double yVal, double zVal)
{
int p = s.Points.AddXY(xVal, yVal, zVal);
// the DataPoint are transparent to the regular chart drawing:
s.Points[p].Color = Color.Transparent;
return p;
}
If this Paint event we draw the data as we like it. Here are either Lines or Points:
private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
Chart chart = sender as Chart;
if (chart .Series.Count < 1) return;
if (chart .Series[0].Points.Count < 1) return;
ChartArea ca = chart .ChartAreas[0];
e.ChartGraphics.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
List<List<PointF>> data = new List<List<PointF>>();
foreach (Series s in chart .Series)
data.Add(GetPointsFrom3D(ca, s, s.Points.ToList(), e.ChartGraphics));
renderLines(data, e.ChartGraphics.Graphics, chart , true); // pick one!
renderPoints(data, e.ChartGraphics.Graphics, chart , 6); // pick one!
}
The coodinates are calculated using axis methods:
List<PointF> GetPointsFrom3D(ChartArea ca, Series s,
List<DataPoint> dPoints, ChartGraphics cg)
{
var p3t = dPoints.Select(x => new Point3D((float)ca.AxisX.ValueToPosition(x.XValue),
(float)ca.AxisY.ValueToPosition(x.YValues[0]),
(float)ca.AxisY.ValueToPosition(x.YValues[1]))).ToArray();
ca.TransformPoints(p3t.ToArray());
return p3t.Select(x => cg.GetAbsolutePoint(new PointF(x.X, x.Y))).ToList();
}
The actual drawing happens in these routines; one draws lines the other dots:
void renderLines(List<List<PointF>> data, Graphics graphics, Chart chart, bool curves)
{
for (int i = 0; i < chart.Series.Count; i++)
{
if (data[i].Count > 1)
using (Pen pen = new Pen(Color.FromArgb(64, chart.Series[i].Color), 2.5f))
if (curves) graphics.DrawCurve(pen, data[i].ToArray());
else graphics.DrawLines(pen, data[i].ToArray());
}
}
void renderPoints(List<List<PointF>> data, Graphics graphics, Chart chart, float width)
{
for (int s = 0; s < chart.Series.Count; s++)
{
Series S = chart.Series[s];
for (int p = 0; p < S.Points.Count; p++)
using (SolidBrush brush = new SolidBrush(Color.FromArgb(64, S.Color)))
graphics.FillEllipse(brush, data[s][p].X-width/2,
data[s][p].Y-width/2,width, width);
}
}
Other drawing routines like meshes or areas can be coded just as well.. Simply add new routines using user GDI+ methods like DrawCurve or FillPolygon or maybe even DrawImage..
You can set the ChartArea.Area3DStyle.Rotation and the ChartArea.Area3DStyle.Inclination for different views, as can be seen in the animation.
Edit I have update the PostPaint method to minimze dependencies.

Related

C# Moving the vertical line annotation moving on chart control with 16000 points too slow

I have list XY points are including 16000 points, I deployed it to 2 chart type waveform chart and arc graph. I used the class name is VerticalLineAnnotation to design the vertical line on waveform chart and add series chart type line on arc graph to reflect any the last X point of the veritcal line on arc graph. When move the vertical line to left-right, it work very well but when I leave mouse out the line, the charts (waveforma and arc graph) are redraw and take to long time. How can I prevent it (redraw event) or speed up?.
This is source code
ChartArea CA;
Series S1;
VerticalLineAnnotation VA;
double degree = 0;
int test = 0;
Dictionary<int, PointXY> xyMap = new Dictionary<int, PointXY>();
private void DrawGraph(DataTable dtExcel)
{
Series series1 = new Series("Series2");
series1.ChartArea = "ChartArea1";
series1.ChartType = SeriesChartType.Spline;
series1.Color = Color.White;
chart1.ChartAreas["ChartArea1"].AxisY.Minimum = 1000;
chart1.ChartAreas["ChartArea1"].AxisY.Maximum = 4500;
chart1.ChartAreas["ChartArea1"].AxisY.Interval = 500;
chart1.ChartAreas["ChartArea1"].AxisX.Minimum = 0;
chart1.ChartAreas["ChartArea1"].AxisX.Maximum = 16000;
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 1000;
Series series2 = new Series("Series2");
series2.ChartArea = "ChartArea1";
series2.ChartType = SeriesChartType.Spline;
series2.Color = Color.White;
chart2.ChartAreas["ChartArea1"].AxisY.Minimum = -100;
chart2.ChartAreas["ChartArea1"].AxisY.Maximum = 100;
chart2.ChartAreas["ChartArea1"].AxisY.Interval = 10;
chart2.ChartAreas["ChartArea1"].AxisX.Minimum = -100;
chart2.ChartAreas["ChartArea1"].AxisX.Maximum = 100;
chart2.ChartAreas["ChartArea1"].AxisX.Interval = 10;
foreach (DataRow item in dtExcel.Rows)
{
series1.Points.AddXY(int.Parse(item[0].ToString()), item[1].ToString());
CaculatePointXY(item, series2);
}
chart1.Series.Add(series1);
chart1.ChartAreas["ChartArea1"].AxisX.MajorGrid.LineColor = Color.Green;
chart1.ChartAreas["ChartArea1"].AxisY.MajorGrid.LineColor = Color.Green;
chart1.ChartAreas["ChartArea1"].AxisX.LabelAutoFitStyle = LabelAutoFitStyles.DecreaseFont;
chart2.Series.Add(series2);
chart2.ChartAreas["ChartArea1"].AxisX.MajorGrid.LineColor = Color.Green;
chart2.ChartAreas["ChartArea1"].AxisY.MajorGrid.LineColor = Color.Green;
chart2.ChartAreas["ChartArea1"].AxisX.LabelAutoFitStyle = LabelAutoFitStyles.DecreaseFont;
//Draw vertical line
CA = chart1.ChartAreas["ChartArea1"];
VA = new VerticalLineAnnotation();
VA.AxisX = CA.AxisX;
VA.AllowMoving = true;
VA.IsInfinitive = true;
VA.ClipToChartArea = CA.Name;
VA.Name = "myLine";
VA.LineColor = Color.Red;
VA.LineWidth = 2; // use your numbers!
VA.X = 8000;
chart1.Annotations.Add(VA);
chart2.Series.Add("Line");
chart2.Series["Line"].Points.Add(new DataPoint(0, 0));
chart2.Series["Line"].Color = Color.Red;
chart2.Series["Line"].BorderWidth = 2;
chart2.Series["Line"].Points.Add(new DataPoint(0, 100));
chart2.Series["Line"].ChartType = SeriesChartType.Line;
}
private void CaculatePointXY(DataRow item, Series series2)
{
double distance = (int.Parse(item[1].ToString()) - 819) * 0.0030525;
distance += 95;
double x = distance * Math.Cos((degree * Math.PI / 180));
double y = distance * Math.Sin((degree * Math.PI / 180));
series2.Points.AddXY(x, y);
xyMap.Add(int.Parse(item[0].ToString()), new PointXY()
{
X = x,
Y = y
});
degree += 0.0225;
}
private void chart1_Customize(object sender, EventArgs e)
{
foreach (var label in chart1.ChartAreas[0].AxisY.CustomLabels)
{
label.Text = ((int)double.Parse(label.Text) / 100).ToString();
}
foreach (var label in chart1.ChartAreas[0].AxisX.CustomLabels)
{
label.Text = ((int)double.Parse(label.Text) / 100).ToString();
}
}
private void chart1_MouseDoubleClick(object sender, MouseEventArgs e)
{
var xVal = chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X);
var yVal = chart1.ChartAreas[0].AxisY.PixelPositionToValue(e.Y);
ZoomWaveFormGraph(xVal, yVal);
}
private void ZoomWaveFormGraph(double xVal, double yVal)
{
ZoomArcGraph((int)xVal);
var YMin = Math.Round(yVal - 1000, 0);
var YMax = Math.Round(yVal + 1000, 0);
chart1.ChartAreas["ChartArea1"].AxisY.Minimum = YMin;
chart1.ChartAreas["ChartArea1"].AxisY.Maximum = YMax;
chart1.ChartAreas["ChartArea1"].AxisY.Interval = 100;
var XMin = Math.Round(xVal - 1000, 0);
var XMax = Math.Round(xVal + 1000, 0);
chart1.ChartAreas["ChartArea1"].AxisX.Minimum = XMin;
chart1.ChartAreas["ChartArea1"].AxisX.Maximum = XMax;
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 200;
}
private void ZoomArcGraph(double xVal)
{
int a = (int)xVal;
var b = xyMap[a];
chart2.ChartAreas["ChartArea1"].AxisY.Minimum = Math.Round(b.Y - 20, 0);
chart2.ChartAreas["ChartArea1"].AxisY.Maximum = Math.Round(b.Y + 20, 0);
chart2.ChartAreas["ChartArea1"].AxisY.Interval = 2;
chart2.ChartAreas["ChartArea1"].AxisX.Minimum = Math.Round(b.X - 20, 0);
chart2.ChartAreas["ChartArea1"].AxisX.Maximum = Math.Round(b.X + 20, 0);
chart2.ChartAreas["ChartArea1"].AxisX.Interval = 2;
}
private double RadianToDegree(double angle)
{
return angle * (180.0 / Math.PI);
}
private void chart1_AnnotationPositionChanging(object sender, AnnotationPositionChangingEventArgs e)
{
test = (int)Math.Round(e.NewLocationX, 0);
Console.WriteLine("X : " + e.NewLocationX.ToString());
}
private void chart1_AnnotationPositionChanged(object sender, EventArgs e)
{
var a = xyMap[test];
chart2.Series["Line"].Points.ElementAt(1).SetValueXY(a.X, a.Y);
chart2.Refresh();
}

WinForms: highlighting X and Y axes

I'm making a program to graph parabolas, and I want to make the X and Y axes (the ones from (0,0)) a different color. I haven't found any options to do so, and the only solution I've found is to make a large grid and set its increment to half the graph's size. Is there an alternative?
I used the default chart control. I'd expect something like:
You can set Crossing for axis to move the axis to center of chart. Also you can set LineWidth for axis to make it thicker. Also you can set ArrowStyle to have an arrow at the end of axis.
For example, to have a chart like this:
Use such code:
private void Form1_Load(object sender, EventArgs e)
{
//Set Chart Margins
this.chart1.ChartAreas[0].Position.Auto = false;
this.chart1.ChartAreas[0].Position.X = 10;
this.chart1.ChartAreas[0].Position.Y = 10;
this.chart1.ChartAreas[0].Position.Width = 80;
this.chart1.ChartAreas[0].Position.Height = 80;
//Configure X Axis
this.chart1.ChartAreas[0].AxisX.Crossing = 0;
this.chart1.ChartAreas[0].AxisX.Interval = 1;
this.chart1.ChartAreas[0].AxisX.LabelStyle.Enabled = false;
this.chart1.ChartAreas[0].AxisX.LineWidth = 2;
this.chart1.ChartAreas[0].AxisX.ArrowStyle =
System.Windows.Forms.DataVisualization.Charting.AxisArrowStyle.Lines;
//Configure Y Axis
this.chart1.ChartAreas[0].AxisY.Crossing = 0;
this.chart1.ChartAreas[0].AxisY.Interval = 5;
this.chart1.ChartAreas[0].AxisY.LineWidth = 2;
this.chart1.ChartAreas[0].AxisY.LabelStyle.Enabled = false;
this.chart1.ChartAreas[0].AxisY.ArrowStyle =
System.Windows.Forms.DataVisualization.Charting.AxisArrowStyle.Lines;
//Set Chart Type
this.chart1.Series[0].ChartType =
System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Spline;
//Set Data
var p = new List<PointF>();
for (int i = -5; i <= 5; i++)
{
p.Add(new PointF(i, i * Math.Abs(i)));
}
this.chart1.DataSource = p;
this.chart1.Series[0].XValueMember = "X";
this.chart1.Series[0].YValueMembers = "Y";
this.chart1.Series[0].IsVisibleInLegend = false;
}

While using smooth, curve color is being changed

I have a curve defined like below
zgc.MasterPane.PaneList[0].XAxis.Scale.Max = listACSignal[listACSignal.Count - 1].X / 2;
zgc.MasterPane.PaneList[0].XAxis.Scale.MinAuto = false;
zgc.MasterPane.PaneList[0].YAxis.Scale.MinAuto = true;
zgc.MasterPane.PaneList[0].YAxis.Scale.MaxAuto = true;
zgc.MasterPane.PaneList[0].XAxis.Type = AxisType.Linear;
zgc.MasterPane.PaneList[0].XAxis.Scale.Format = "";
zgc.MasterPane.PaneList[0].XAxis.Scale.Min = 0;
zgc.MasterPane.PaneList[0].TitleGap = 0;
myCurveACSignal = zgc.MasterPane.PaneList[0].AddCurve(null, listACSignal, Color.Lime, SymbolType.None);
myCurveACSignal.Line.Width = lineWidth;
Fill fillACSignal = new Fill(Color.Blue, Color.Red);
fillACSignal.RangeMin = 1;
fillACSignal.RangeMax = 2;
fillACSignal.Type = FillType.GradientByZ;
myCurveACSignal.Line.GradientFill = fillACSignal;
myCurveACSignal.Line.IsSmooth = true;
myCurveACSignal.Line.SmoothTension = 0.2F;
I want to sharpen intersection of lines so I have used the IsSmooth property. However it affects the color of the curve. I want my curve blue but if I use IsSmooth it shows purple lines. Is there anyway to prevent color changes?

Analog clock rotate transform

I have problem with rotate transform in my analog clock.
for (int i = 0; i <= 12; i++)
{
Ellipse teckaa = new Ellipse();
teckaa.Height = 30;
teckaa.Width = 30;
teckaa.Stroke = Brushes.Black;
teckaa.Fill = Brushes.Black;
canvas.Children.Add(teckaa);
Canvas.SetTop(teckaa, 25);
Canvas.SetLeft(teckaa, 215);
RotateTransform otoceni = new RotateTransform(i*30, 230, 230);
canvas.RenderTransform = otoceni;
}
I have this code for hour points, but this is moving only with one point. Is there any way how to change name of the ellipse in cycle for()?
Apply the Transform to the individual hour markers and not the the entire Canvas:
for (int i = 0; i <= 12; i++)
{
Ellipse teckaa = new Ellipse();
teckaa.Height = 30;
teckaa.Width = 30;
teckaa.Stroke = Brushes.Black;
teckaa.Fill = Brushes.Black;
canvas.Children.Add(teckaa);
Canvas.SetTop(teckaa, 25);
Canvas.SetLeft(teckaa, 215);
RotateTransform otoceni = new RotateTransform(i*30, 230, 230);
//canvas.RenderTransform = otoceni;
teckaa.RenderTransform = otoceni;
}
And see Petzold for a really cool all XAML clock.

Why does the x scrollbar get stuck on mschart?

int blockSize = 100;
// generates random data (i.e. 30 * blockSize random numbers)
Random rand = new Random();
var valuesArray = Enumerable.Range(0, blockSize * 30).Select(x => rand.Next(1, 10)).ToArray();
// clear the chart
chart1.Series.Clear();
//chart1.ChartAreas[0].AxisX.Interval = 3.0;
//chart1.ChartAreas[0].AxisX.IntervalType = DateTimeIntervalType.Auto;
DateTime now = DateTime.Now;
chart1.ChartAreas[0].AxisX.LabelStyle.Format = "HH:mm:ss";
chart1.ChartAreas[0].AxisX.Minimum = now.ToOADate();
// fill the chart
var series = chart1.Series.Add("My Series");
series.XValueType = ChartValueType.DateTime;
series.ChartType = SeriesChartType.Line;
//series.XValueType = ChartValueType.Int32;
//DateTime.Now.AddSeconds(i).ToOADate()
for (int i = 0; i < valuesArray.Length; i++)
series.Points.AddXY(now.AddSeconds(i).ToOADate(), valuesArray[i]);
var chartArea = chart1.ChartAreas[series.ChartArea];
// set view range to [0,max]
//chartArea.AxisX.Minimum = 0;
//chartArea.AxisX.IntervalType = DateTimeIntervalType.Seconds;
//chartArea.AxisX.Interval = 10d;
// chartArea.AxisX.Maximum = 100;
// enable autoscroll
chartArea.CursorX.AutoScroll = true;
// let's zoom to [0,blockSize] (e.g. [0,100])
chartArea.AxisX.ScaleView.Zoomable = true;
chartArea.AxisX.ScaleView.SizeType = DateTimeIntervalType.Auto;
int position = 0;
int size = blockSize;
chartArea.AxisX.ScaleView.Zoom(now.AddSeconds(-5).ToOADate(), now.AddSeconds(20).ToOADate());
//chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset();
// disable zoom-reset button (only scrollbar's arrows are available)
chartArea.AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
// set scrollbar small change to blockSize (e.g. 100)
chartArea.AxisX.ScaleView.SmallScrollSize = (new TimeSpan(0,0,10)).TotalSeconds;
With the above code I am unable to drag the scroll bar. I just gets stuck when I click on the scroll bar.I can scroll using the arrows.
chartArea.AxisX.ScaleView.SmallScrollMinSize = .01;
chartArea.AxisX.ScaleView.SmallScrollMinSizeType = DateTimeIntervalType.Seconds;
chartArea.AxisX.ScaleView.SmallScrollSizeType = DateTimeIntervalType.Seconds;
You need to specify the minimum scroll size to get it to work.

Categories