Note: Many of the constructs already exist in one form or another, here I'm showing you how to do it from scratch.
Same control, same data, different perspective
Since your data is 3-dimensional, you need store your positional data 3-dimensional so you don't have to calculate everything by hand every time you change the perspective:
[TypeConverter(typeof(ExpandableObjectConverter))]
public struct Point3D
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
public Point3D(int x, int y, int z)
{
X = x;
Y = y;
Z = z;
}
}
To project these 3D-points to screen coordinates (2D) you need some kind of projection/camera system. Usually you would do this with Matrix/Vector math, but for this example the following orthographic approach will suffice.
The perspective handles the transformation from 3D to 2D. Depending on the parameters your graphic will skew/roatate/translate etc.
[TypeConverter(typeof(ExpandableObjectConverter))]
public class Perspective
{
public float X_shift { get; set; } = 0.0f;
public float Y_shift { get; set; } = 0.0f;
public float X_x { get; set; } = 1.0f;
public float X_y { get; set; } = 0.0f;
public float X_z { get; set; } = 0.0f;
public float Y_x { get; set; } = 0.0f;
public float Y_y { get; set; } = 1.0f;
public float Y_z { get; set; } = 0.0f;
public PointF Project(Point3D p)
{
return new PointF(X_shift + X_x * p.X + X_y * p.Y + X_z * p.Z, Y_shift + Y_x * p.X + Y_y * p.Y + Y_z * p.Z);
}
}
All you need to do now, is draw everything as usual, but with 3D-coordinates translated to 2D. The following control draws a grid (at depth 400) and two sensors.
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public class PerspectiveGrid : Control
{
private Perspective _perspective;
public Perspective Perspective
{
get { return _perspective; }
set
{
_perspective = value;
Invalidate();
}
}
public PerspectiveGrid()
{
Perspective = new Perspective
{
X_shift = 100,
Y_shift = 10,
X_x = -0.2f,
X_y = 1.0f,
X_z = 0.0f,
Y_x = 0.2f,
Y_y = 0.0f,
Y_z = 1.0f,
};
}
/// <summary>
/// Paints a Grid at Z = 400 and two Sensors
/// </summary>
protected override void OnPaint(PaintEventArgs e)
{
DrawGrid(10,40,400,e.Graphics);
DrawSensor(new Point3D(80, 120, 400), new Point3D(80, 120, 200), e.Graphics);
DrawSensor(new Point3D(240, 240, 400), new Point3D(240, 240, 120), e.Graphics);
}
/// <summary>
/// Draws a sensor at the specified position(s)
/// </summary>
private void DrawSensor(Point3D from, Point3D to, Graphics gr)
{
DrawLine(gr, Pens.Black, from, to);
DrawSphere(gr, Pens.Black, Brushes.Orange, to, 6);
}
/// <summary>
/// Draws a sphere as a Circle at the specified position
/// </summary>
private void DrawSphere(Graphics gr, Pen outline, Brush fill, Point3D center, float radius)
{
PointF center2D = Project(center);
gr.FillEllipse(fill, center2D.X - radius, center2D.Y - radius, radius * 2, radius * 2);
gr.DrawEllipse(outline, center2D.X - radius, center2D.Y - radius, radius * 2, radius * 2);
}
/// <summary>
/// Draws the grid at the specified depth
/// </summary>
private void DrawGrid(int numOfCells, int cellSize, int depth, Graphics gr)
{
Pen p = Pens.SteelBlue;
for (int i = 0; i <= numOfCells; i++)
{
// Vertical
DrawLine(gr, p, new Point3D(i * cellSize, 0 , depth), new Point3D(i * cellSize, numOfCells * cellSize, depth));
// Horizontal
DrawLine(gr, p, new Point3D(0, i * cellSize, depth), new Point3D(numOfCells * cellSize, i * cellSize, depth));
}
}
/// <summary>
/// Draws a line from one 3DPoint to another
/// </summary>
private void DrawLine(Graphics graphics, Pen pen, Point3D p1, Point3D p2)
{
PointF pointFrom = Project(p1);
PointF pointTo = Project(p2);
graphics.DrawLine(pen, pointFrom, pointTo);
}
/// <summary>
/// Projects a Point3D to a PointF
/// </summary>
private PointF Project(Point3D p)
{
return Perspective.Project(p);
}
}
}
A couple of links that might help you build on these concepts:
Orthographic projection
Quaternion
Math Library with Matrix and Vector support