如何绘制3D图以表示空间中的对象 [英] How to plot a 3D Graph to represent an object in space

查看:99
本文介绍了如何绘制3D图以表示空间中的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个在空间输出x,y,z位置的机器人。我的问题是只能使用图表在Windows窗体中找到2D图。



我想在3D空间中绘制机器人。我可以使用的任何工具?



与此类似的东西:





为此我需要一个免费的软件解决方案



编辑:



我的2D图形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;

//示例
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);

设计者:

  // 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 =新的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 =新的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 =大师;
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 =奴隶;
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 + =新的System.Windows.Forms.MouseEventHandler(this.chart1_MouseDown);
this.chart1.MouseMove + =新的System.Windows.Forms.MouseEventHandler(this.chart1_MouseMove);
this.chart1.MouseUp + =新的System.Windows.Forms.MouseEventHandler(this.chart1_MouseUp);

编辑:



首先,我们准备图表。许多细节都可以满足您的需求;

  void prepare3dChart(图表图表,ChartArea ca)
{
ca.Area3DStyle.Enable3D = true; //将图表区域设置为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;

//我们添加了两个系列:
chart.Series.Clear();
for(int i = 0; i< 2; i ++)
{
Series s = chart.Series.Add( S + i.ToString( 00));
s.ChartType = SeriesChartType.Bubble; //此ChartType具有一个YValue数组
s.MarkerStyle = MarkerStyle.Circle;
s [ PixelPointWidth] = 100;
s [ PixelPointGapDepth] = 1;
}
chart.ApplyPaletteColors();

addTestData(chart);
}

在这里我们添加一些测试数据:

  void addTestData(图表)
{
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);
}
}

DataPoints 与此例程一起添加:

  int AddXY3d(Series s,double xVal,double yVal,double zVal)
{
int p = s.Points.AddXY(xVal,yVal,zVal);
// DataPoint对常规图表是透明的:
s.Points [p] .Color = Color.Transparent;
return p;
}

如果发生此Paint事件,我们将根据需要绘制数据。以下是线或点:

  private void chart1_PostPaint(object sender,ChartPaintEventArgs e)
{
图表图表=发送者为图表;

如果(图表.Series.Count< 1)返回;
if(chart .Series [0] .Points.Count< 1)返回;

ChartArea ca = chart .ChartAreas [0];
e.ChartGraphics.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

List< List< PointF>>数据=新的List< List< PointF>>>();
foreach(图表.Series中的Series)
data.Add(GetPointsFrom3D(ca,s,s.Points.ToList(),e.​​ChartGraphics));

renderLines(data,e.ChartGraphics.Graphics,chart,true); // 选一个!
renderPoints(data,e.ChartGraphics.Graphics,chart,6); // 选一个!
}

余弦值是使用轴方法计算的:

  List< PointF> GetPointsFrom3D(ChartArea ca,Series s,
List< DataPoint> dPoints,ChartGraphics cg)
{
var p3t = dPoints.Select(x =>新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());

返回p3t.Select(x => cg.GetAbsolutePoint(new PointF(x.X,x.Y)))。ToList();
}

实际绘制发生在这些例程中;一个会画线其他点:

  void renderLines(List< List< PointF>>数据,图形,图表,布尔曲线)
{
for(int i = 0; i< chart.Series.Count; i ++)
{
if(data [i] .Count> 1 )
使用(钢笔=​​ new Pen(Color.FromArgb(64,chart.Series [i] .Color],2.5f))
if(曲线)graphics.DrawCurve(pen,data [i ] .ToArray());
else graphics.DrawLines(pen,data [i] .ToArray());
}
}

void renderPoints(List< List< PointF>>数据,图形图形,统计图,浮动宽度)
{
for (int s = 0; s< chart.Series.Count; s ++)
{
Series S = chart.Series [s];
for(int p = 0; p< S.Points.Count; p ++)
使用(SolidBrush笔刷= new SolidBrush(Color.FromArgb(64,S.Color)))
graphics.FillEllipse(画笔,data [s] [p] .X-width / 2,
data [s] [p] .Y-width / 2,width,width);
}
}

其他绘图例程(例如网格物体或区域)仅可以编码只需使用用户GDI +方法(例如DrawCurve或FillPolygon甚至是DrawImage)添加新例程。



您可以设置 ChartArea.Area3DStyle旋转 ChartArea.Area3DStyle.Inclination 用于不同视图,如动画所示。



编辑我已经更新了 PostPaint 方法以最小化依赖项。




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.

这篇关于如何绘制3D图以表示空间中的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆