13

I drew the following grid:

Grid (Top-View)

The above grid is drawn using the following two methods, one to calculate the grid and the other to calculate the centers for each cell:

//makes grid in picture box
private void drawGrid(int numOfCells, int cellSize, Graphics gr)
{
    Pen p = new Pen(Color.SteelBlue);

    for (int i = 0; i < Math.Sqrt(numOfCells) + 1; i++)
    {
        // Vertical
        gr.DrawLine(p, i * cellSize + 300, 200, i * cellSize + 300, 700);
        // Horizontal
        gr.DrawLine(p, 300, i * cellSize+200, 800, i * cellSize+200);
    }

    this.topology.SendToBack();
}


//draw the center point for each cell of the grid 
private void drawCenters(Graphics gr)
{
    for (int j = 0; j < rows; j++)
    {
        for (int i = 0; i < columns; i++)
        {
            gr.FillRectangle(Brushes.IndianRed, cellsCenters[0, i], cellsCenters[1, j], 3, 3);
        }
    }
}

My question is how to make this grid appear as in the following picture and how to place the nodes at different cells (random deployment) in such grid.

Expected result

I need the grid to be drawn in a 3D view in which I have z as well as x and y!

Rose
  • 349
  • 3
  • 17
  • Asuming you are going for a orthographic projection: Simply choose a vector for Z (dX, dY) and add Z*that vector that to whatever you are drawing. – Manfred Radlwimmer Jul 20 '17 at 10:06
  • It's **really** simple: Imagine you have a 3D point (x3,y3,z3) that you want to project onto a plane (convert it to a 2D point (x2,y2)), in this case your screen. If you look at it from the top (as you do right now) you ignore Z and use (x2,y2) = (x3,y3). If you want to look at it from the side (almost like in your second picture) you would ignore x and use (x2,y2) = (y3,z3). you want something in between: (x2,y2) so you need to choose a certain factor to multiply. For example: (x2,y2) = (y3+x3*0.2,z3+x3*0.1) – Manfred Radlwimmer Jul 20 '17 at 10:13
  • If you can provide a [mcve] of your code, I might be able to modify it – Manfred Radlwimmer Jul 20 '17 at 10:14

1 Answers1

15

Note: Many of the constructs already exist in one form or another, here I'm showing you how to do it from scratch. enter image description here 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

Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
  • 1
    @Rose Can't really help much without knowing your code (works in my example) but this error occurs when you try to access a method via the class name instead of the instance. In my example `Perspective` is the name of the class **and** the property so it works - instead of `AUV_Topology.Perspective` you need to use whatever name you gave it. Perspective needs instance values to work with, so making it static wouldn't make sense. Alternatively you can of course create a static instance in one of your classes but I recommend you just add an instance of Perspective to your Control instead. – Manfred Radlwimmer Jul 21 '17 at 07:22
  • @Rose Adjust the `X_shift` and `Y_shift` parameters of your Projection to match the previously hard-coded offsets, or adjust the coordinates of the grid in code. – Manfred Radlwimmer Jul 21 '17 at 09:43
  • If your sensors float beside the grid, change their coordinates. If the grid is not in the right spot, adjust the DrawGrid method (add an offset to the drawn positions). – Manfred Radlwimmer Jul 21 '17 at 20:02
  • The Perspective is the logic that turns a 3D coordinate into a 2D coordinate. X/Y_shift is a constant offset that will move every single point by that amount without distorting the image. X_x is a factor that signifies how much the 3D X coordinate will influence the 2D X coordinate, X_y how much the 3D y coordinate influences the 2D X coordinate, etc. X_x to Y_z determine the Point of view. When you are looking directly from the front/back/left/right/top or bottom, one of the _x,_y,_z will be 1 all others 0. – Manfred Radlwimmer Jul 22 '17 at 15:12
  • The Perspective in your latest screenshot is looking good, but you need to make sure the grid coordinates have the same origin as your sensors (as mentioned before). Have you tried adding a fixed offset to the x/y parameters of the Point3D constructors in the DrawGrid method? – Manfred Radlwimmer Jul 22 '17 at 15:14
  • Have you read any of my comments? The sensor positions and the grid origins don't match up. Tinkering with the perspective won't change that. Re-read my last 3 comments and adjust what I mentioned there. – Manfred Radlwimmer Jul 22 '17 at 15:28
  • I don't even understand your questions anymore ... Read the code, understand the code, adjust the code. I'm sure you'll figure it out. – Manfred Radlwimmer Jul 22 '17 at 15:31
  • As explained the projection is orthographic (since the grid in your sketch is too). If you want objects that are further away from the screen to be smaller, you need to create a different projection. [Details about the differences here](https://blender.stackexchange.com/questions/648/what-are-the-differences-between-orthographic-and-perspective-views) – Manfred Radlwimmer Jul 22 '17 at 15:44
  • I've got what I want almost - Thank U - : https://www.dropbox.com/s/kn3iofltryyqgtv/plane.png?dl=0 – Rose Jul 22 '17 at 22:57
  • I read them ... why don't you just use the `Project` method to project the centers? You don't have to calculate the projection yourself. Just put the 3D coordinates in there and use the 2D output. It's that simple. Also you might want to adjust the order in which you draw (Grid, then centers, then sensors back to front) to avoid unwanted overlap. – Manfred Radlwimmer Jul 23 '17 at 10:46
  • Then this is a great opportunity to rise to the challenge. I'm sure if you read and understand the code to figure out the final details yourself, then this experience will be a lot more valuable for you than if someone (me) just gives you a finished solution. StackOverflow is not a code-writing service. It's meant to help you improve your coding and help others that face the same problems. I will not write your code for you - I've given you the basis for a working solution, several links to articles on this and related topics and 11(!) answers to follow-up questions - this should suffice. – Manfred Radlwimmer Jul 23 '17 at 16:02
  • Yes, it is very simple ... I drew it : [ https://www.dropbox.com/s/lqnlmnseyhrkknk/with_centers.png?dl=0 ]. Many thanks, It is very nice and strong answer (y). – Rose Jul 28 '17 at 16:29