-1

I am trying to draw a network topology consists of many nodes to be like a 3D nodes using the idea of projection, but when I click the button that should implement the code nothing appears!!!! the following is the main code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Media;
using System.Text;
using System.Windows.Forms;
using AUV_Topology.AUV_Topology;
using AUV_Topology;

namespace AUV_Topology
{
    public partial class Form2 : Form
    {
        private int iSetupDisplay = -1; // used for making drawing on the graphics 
        static int NodeNum = 0; // an int number is given for each node 
        static bool deploymentButtonWasClicked = false; // flag to check if button "Deploy" is clicked 
        static Sink sink; // sink object from sink class 
        static SensorNode[] SNs;  // array of SNs   
        static int[,] SNsLocation; // to store SN locations 
        static int[,] SNsNighbors; // to store each SN nighbors 
        static int[] SinkNighbors; // to store each SN nighbors 
        static int transmissionRange; // the transmission range for each sensor node
        static double sensorNodeIntialEnergy; // in order to store the energy filled in the text box in the GUI 
        System.IO.StreamWriter writer; // declare a writer object from the straem writer class 
        static Random r = new Random(); // in order to generate a random number 
        static AUV[] auv; // array of AUVs objects from AUV class 
        static int rows, columns; // to get the number of rows and columns from the text boxes in the GUI 
        static int[,] cellsCenters; // an array to store the indexes (col & row) for each center for each cell in the grid topology
        static int[] columnsXs; // the index of each X cordinates in each column 
        static int[] rowsYs; // the index of each Y cordinates
        static int numOfCells; // compute the number of cells by multpling the rows with columns
        static int cellSide; // to compute the length of each side for each cell 

        public Form2()
        {
            InitializeComponent();
            // Create a file for output named TraceFile.txt.
            Stream myFile = File.Create("C:/Users/AMCT/Desktop/TraceFile2.txt");
            TextWriterTraceListener myTextListener = new TextWriterTraceListener(myFile);
            Trace.Listeners.Add(myTextListener);
        }

        private void Form2_Load(object sender, EventArgs e)
        {

        }

        public delegate void UpdateControlsDelegate();

        [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) : this()
            {
                this.X = x;
                this.Y = y;
                this.Z = z;
            }
        }

        public static SoundPlayer player = new SoundPlayer("C:/Users/AMCT/Desktop/project/C#-Code-VisualStudio-2010/GAAPS/Sonar_pings.wav");
        public static int tr;

        //***Deployment***//

        #region nodesDeployment

        private void nodesDeployment_Click_1(object sender, EventArgs e)
        {
            //chromosome size
            rows = 10;
            columns = 10;
            int numOfGridCells = rows * columns; // Calculate the total number of the cells for the grid topology 
            tr = Convert.ToInt32(TransRange.Text);
            calculateCellsCenters(200, 200, 700, 700, numOfGridCells, 500, 500); // Compute all the center points (Xc,Yc) for all cells of the partitioned region (grid) 
            if (NumSN.Text != string.Empty) // to check that the user has entered the number of nodes for this topology 
            {
                topology2.Visible = true;
                NodeNum = Convert.ToInt32(NumSN.Text); // get the number from the text box
                sensorNodeIntialEnergy = Convert.ToDouble(SNEnergy.Text);
                Point3D[] auvLocation2D = new Point3D[4];
                for (int i = 0; i < 4; i++)
                    auvLocation2D[i] = new Point3D(cellsCenters[0, 0], cellsCenters[1, 8], 700 - i * 100);
                Point3D p3d = new Point3D(542, 600, 210);
                PointF point2Dsink = Project(p3d);
                sink = new Sink("Sink", 1, point2Dsink.X, point2Dsink.Y, 100);
                auv = new AUV[4];
                for (int i = 0; i < 4; i++)
                {
                    PointF point2D = Project(auvLocation2D[i]);
                    int d = 700 - i * 100;
                    auv[i] = new AUV(point2D.X, point2D.Y, d, sink, i);
                }
                SNs = new SensorNode[NodeNum];
                for (int i = 0; i < NodeNum; i++)
                    SNs[i] = new SensorNode(i, "SN" + i, r.Next(355, 750) % (topology2.Width), sensorNodeIntialEnergy, sink, auv[0], 0);
                nodesCoordinations(); // choose XY coordinates for each sensor node 
                generateRoutingTable(); // build the routing table for each sensor node
                deploymentButtonWasClicked = true;
            }
            makeDrawing(); // Draw
        }

        #endregion

        //***Nodes Coordinates***//

        #region Nodes Coordinates ( used by deployment process ) 

        public void nodesCoordinations()
        {
            SNsLocation = new int[2, NodeNum]; // build an array of size n which is equal to the number of nodes that was chosen by the user to store all SNs locations

            # region SNs Locations

            for (int i = 0; i < NodeNum; i++)
                if (NodeNum != 0)
                {
                    SNsLocation[0, i] = r.Next(300, 800) % (topology2.Width); // choose random X coordinate between 300 & 800 pixel
                    SNsLocation[1, i] = r.Next(260, 725) % (topology2.Width); // choose random Y coordinate between 260 & 725 pixel 
                }
        }

        # endregion

        public void generateRoutingTable()
        {

            /// Size of SNsNighbors Array = [node #, Nighbors #]
            double D; // Euclidean distance 
            SNsNighbors = new int[NodeNum, NodeNum];
            SinkNighbors = new int[NodeNum];
            transmissionRange = Convert.ToInt32(TransRange.Text);

            /// This Method estimate the Euclidean distance between SNs = (D)
            /// Suppose that the transmission rane for all nodes = 150
            /// if d <= Transmission Rang of SNi, SNi can communicate with SNj 
            for (int i = 0; i < NodeNum; i++) // For all SNs
                for (int j = 0; j < NodeNum; j++) // For all SNs
                {
                    if (i != j)
                    {
                        double Xn = SNsLocation[0, i]; // 0 for x - axis 
                        double Yn = SNsLocation[1, i]; // 1 for y - axis 
                        double Xs = SNsLocation[0, j]; // 0 for x - axis
                        double Ys = SNsLocation[1, j]; // 1 for y - axis
                        D = Math.Pow((Math.Pow(Xn - Xs, 2) + Math.Pow(Yn - Ys, 2)), 0.5);
                        // the transmission range is 150 
                        if (D <= transmissionRange)
                            SNsNighbors[i, j] = 1; // SNj is nighbore for SNi
                        else
                            SNsNighbors[i, j] = 0; // SNj is NOT nighbore for SNi
                    }
                    else
                        SNsNighbors[i, j] = 0; // SNj is NOT nighbore for SNi
                }
        }

        #endregion nodesDeployment

        //***Paint  Handlers***//

        #region Paint  handlers

        private void topology_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Font font = new Font("Times New Roman", 7.0f); // choose the type of font and its size
            StringFormat format = new StringFormat(); // declare string format
            format.Alignment = StringAlignment.Center; // declare string alignment
            Graphics g = e.Graphics;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; // declare the quality for the drawings
            Pen transmissionRangeColor = Pens.LightCyan; // the color used to draw the ellipse which repesents the transmission range for each node 
            Pen nodeNieghbors = Pens.LightSeaGreen; // the color used to draw the green lines between sensor nodes which indicates direct connection between nodes 
            Pen sinkNieghbors = Pens.Azure;
            Brush nodeName = Brushes.White; // the color used for strings used to name the nodes 
            Brush auvSendMsg = Brushes.Red; // the color used for strings used to name the nodes 

            #region Display Sink

            if (deploymentButtonWasClicked) // chech if the "Deploy" button was clicked
            {

                DrawImage("C:/Users/AMCT/Desktop/project/C#-Code-VisualStudio-2010/GAAPS/Sink.png", e.Graphics, sink.sinkX, sink.sinkY, 35, 45);
                DrawString("Sink", e.Graphics, font, nodeName, new Point3D(600, 600 + 34, 233), 5);// Display the sink name

                #region Draw Grid

                topology2.Invalidate();
                DrawGrid(numOfCells, cellSide, 800, e.Graphics);
                DrawGrid(numOfCells, cellSide, 700, e.Graphics);
                DrawGrid(numOfCells, cellSide, 600, e.Graphics);
                DrawGrid(numOfCells, cellSide, 500, e.Graphics);
                DrawGrid(numOfCells, cellSide, 400, e.Graphics);
                for (int j = 0; j < rows; j++)
                    for (int i = 0; i < columns; i++)
                    {
                        DrawRectangle(e.Graphics, Brushes.SteelBlue, new Point3D(cellsCenters[0, i], cellsCenters[1, j], 800));
                        DrawRectangle(e.Graphics, Brushes.SteelBlue, new Point3D(cellsCenters[0, i], cellsCenters[1, j], 700));
                        DrawRectangle(e.Graphics, Brushes.SteelBlue, new Point3D(cellsCenters[0, i], cellsCenters[1, j], 600));
                        DrawRectangle(e.Graphics, Brushes.SteelBlue, new Point3D(cellsCenters[0, i], cellsCenters[1, j], 500));
                        DrawRectangle(e.Graphics, Brushes.SteelBlue, new Point3D(cellsCenters[0, i], cellsCenters[1, j], 400));
                    }
                base.OnPaint(e);

                #endregion

                #region Display Sensor Nodes

                /// node[0,i] means >> x-axis 
                /// node [1,i] means >> y-axis

                for (int n = 0; n < NodeNum; n++)
                {
                    DrawSensor(new Point3D(SNsLocation[0, n], SNsLocation[1, n], 760), new Point3D(SNsLocation[0, n], SNsLocation[1, n], SNs[n].snDepth), e.Graphics);
                    //DrawTransmissionRange(e.Graphics, transmissionRangeColor, new Point3D(SNsLocation[0, n],SNsLocation[1, n],SNs[n].snDepth), transmissionRange);
                    DrawString("SN" + (n + 1), e.Graphics, font, nodeName, new Point3D(SNsLocation[0, n] - 30, SNsLocation[1, n] - 10, SNs[n].snDepth), 12); // display node name
                    /*for (int i = 0; i < NodeNum - 1; i++)
                    {
                        if (SNsNighbors[n, i] == 1) // Green line between nieghbors
                            DrawLine(e.Graphics, nodeNieghbors, new Point3D(SNsLocation[0, n] + 5, SNsLocation[1, n] + 5, SNs[n].snDepth), new Point3D(SNsLocation[0, i] + 5, SNsLocation[1, i] + 5,SNs[i].snDepth));
                    }*/
                }

                #endregion

                #region AUV-Animation

                for (int i = 0; i < 4; i++)
                {
                    DrawImage("C:/Users/AMCT/Desktop/project/C#-Code-VisualStudio-2010/GAAPS/AUVs-Icon.png", e.Graphics, auv[i].auvX, auv[i].auvY, 45, 20);
                    base.OnPaint(e);
                }
            }

            #endregion

            # endregion

            // finish painting
        }

        /// <summary>
        /// Draws a sensor at the specified position(s)
        /// </summary>
        private void DrawSensor(Point3D from, Point3D to, Graphics gr)
        {
            DrawLine(gr, Pens.Maroon, from, to);
            DrawSphere(gr, Pens.Black, Brushes.Gold, to, 6);
        }

        /// <summary>
        /// Draws a sensor at the specified position(s)
        /// </summary>
        private void DrawSendingSensor(Point3D from, Point3D to, Graphics gr)
        {
            DrawLine(gr, Pens.Maroon, from, to);
            DrawSphere(gr, Pens.Black, Brushes.Black, 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 < Math.Sqrt(numOfCells) + 1; i++)
            {
                // Vertical
                DrawLine(gr, p, new Point3D(i * cellSize + 200, 200, depth), new Point3D(i * cellSize + 200, 700, depth));
                // Horizontal
                DrawLine(gr, p, new Point3D(200, i * cellSize + 200, depth), new Point3D(700, i * cellSize + 200, 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>
        /// Draws a small Rectangle to represent the center point for each cell in the grid 
        /// </summary>
        private void DrawRectangle(Graphics graphics, Brush brush, Point3D center)
        {
            PointF center2D = Project(center);
            graphics.FillRectangle(brush, center2D.X, center2D.Y, 2, 2);
        }

        /// <summary>
        /// Projects a Point3D to a PointF
        /// </summary>
        /// 
        /// <summary>
        /// Draws a string at the specified position
        /// </summary>
        private void DrawString(String s, Graphics gr, Font f, Brush fill, Point3D center, float radius)
        {
            PointF center2D = Project(center);
            gr.DrawString(s, f, fill, center2D.X - radius, center2D.Y - radius);
        }

        /// <summary>
        /// Draws a string at the specified position
        /// </summary>
        private void DrawImage(String path, Graphics gr, float x, float y, int rectX, int rectY)
        {
            gr.DrawImage(new Bitmap(path), new Rectangle((int)x, (int)y, rectX, rectY)); // draw AUV
        }

        /// <summary>
        /// Draws a transmission range for each node at the specified position
        /// </summary>
        private void DrawTransmissionRange(Graphics gr, Pen color, Point3D center, int tRange)
        {
            PointF center2D = Project(center);
            gr.DrawEllipse(color, center2D.X - (tRange / 2) + 5, center2D.Y - (tRange / 2) + 5, tRange, tRange); // draw the tranmission range of node  
        }

        /// <summary>
        /// Converts from 3D point to 2D point
        /// </summary>

        private PointF Project(Point3D p)
        {
            Perspective per = new Perspective();
            return per.Project(p);
        }

        #endregion

        #region Cells Center Points 

        public void calculateCellsCenters(int intialPointX, int intialPointY, int lastPointX, int lastPointY, int cells, int squareGridX, int squareGridY)
        {
            double diagonalLengthEachCell;
            numOfCells = cells;
            int netArea = squareGridX * squareGridY;
            double cellArea = netArea / numOfCells;
            FileStream fs = new FileStream("C:/Users/AMCT/Desktop/testPositions.txt", FileMode.Append, FileAccess.Write);
            cellSide = (int)Math.Sqrt(cellArea);
            int centerPointEachCell;
            columnsXs = new int[columns + 1];
            cellsCenters = new int[2, rows]; // 0 for the point on the x axis and 1 for the point on the y axis so this is why the first element of size 2
            rowsYs = new int[rows + 1];
            diagonalLengthEachCell = cellSide * Math.Sqrt(2);
            centerPointEachCell = (int)diagonalLengthEachCell / 3;
            columnsXs[0] = intialPointX; //  to let the first point for the first column equal to 300 later on

            //Calculate The Columns Points
            for (int k = 1; k < (columns + 1); k++)
                columnsXs[k] = columnsXs[k - 1] + cellSide;
            rowsYs[0] = intialPointY; // its equal to 175 in our case to let the first pint for the first column equal to 200 later on

            //Calculate The Rows Points
            for (int s = 1; s < (rows + 1); s++)

                rowsYs[s] = rowsYs[s - 1] + cellSide;

            /***calculate-centers***/
            using (StreamWriter sw = new StreamWriter(fs))
            {
                for (int i = 0; i < rows; i++)
                {
                    cellsCenters[1, i] = rowsYs[i] + centerPointEachCell;
                    for (int j = 0; j < columns; j++)
                    {
                        cellsCenters[0, j] = columnsXs[j] + centerPointEachCell;
                        sw.Write("({0},{1}) ", cellsCenters[0, j], cellsCenters[1, i]);
                    }
                    sw.WriteLine();
                }
            }
        }

        #endregion

        public void makeDrawing()
        {
            iSetupDisplay = 0;
            if (iSetupDisplay != -1)
            {
                iSetupDisplay += 10;
                if (iSetupDisplay >= topology2.Width)
                    iSetupDisplay = -1;
                topology2.Refresh();
            }
        }
    }
}

I used this method in the previous code to enable the drawing:

public void makeDrawing()
{
    iSetupDisplay = 0;
    if (iSetupDisplay != -1)
    {
        iSetupDisplay += 10;
        if (iSetupDisplay >= topology2.Width)
            iSetupDisplay = -1;
        topology2.Refresh();
    }
}

But I can't get the things painted on the picture box as I want! The following code is the code I use to make the projection:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using AUVtopology;

namespace AUV_Topology
{
    [TypeConverter(typeof(ExpandableObjectConverter))]

    public class Perspective
    {
        public float X_shift { get; set; }
        public float Y_shift { get; set; }
        public float X_x { get; set; }
        public float X_y { get; set; }
        public float X_z { get; set; }
        public float Y_x { get; set; }
        public float Y_y { get; set; }
        public float Y_z { get; set; }
        public Perspective()
        {
            this.X_shift = 420;
            this.Y_shift = -400;
            this.X_x = -0.4f;
            this.X_y = 1.0f;
            this.X_z = 0.0f;
            this.Y_x = 0.4f;
            this.Y_y = 0.0f;
            this.Y_z = 1.0f;
        }

        public PointF Project(Form1.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);
        }

        public PointF Project(Form2.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);
        }
    }

    public class PerspectiveGrid : Control
    {
        private Perspective _perspective;
        public Perspective Perspective
        {
            get { return _perspective; }
            set
            {
                _perspective = value;
                Invalidate();
            }
        }

        public PerspectiveGrid()
        {
            Perspective = new Perspective
            {
                X_shift = 420,
                Y_shift = -400,
                X_x = -0.4f,
                X_y = 1.0f,
                X_z = 0.0f,
                Y_x = 0.4f,
                Y_y = 0.0f,
                Y_z = 1.0f,
            };
        }
    }
}

Is the problem in the method that I use to enable drawing or something else?!

AbdelAziz AbdelLatef
  • 3,650
  • 6
  • 24
  • 52
  • _I cant get the things painted on the picture box as I want_ Meaning what: Nothing is showing or what you see seeems wrong? Do use the debugger to check on the numbers you use. Maybe all is drawn outside the pbox surface. (You can test by scaling down the Graphics in the Paint event with `g.scaleTransform(0.1f, 0.1f)` or even more.. – TaW Sep 01 '19 at 06:47
  • 2
    Also: This is __waaay too much code__ to check for us. Do make a smaller appication for testing! – TaW Sep 01 '19 at 18:03
  • Try to focus on the part with the problem. – AbdelAziz AbdelLatef Sep 02 '19 at 04:31
  • please post your layout file – John Lord Sep 03 '19 at 04:22
  • We need to see which controls are involved! We see `form2`, a `topology` and a `topology2` mentionend but can't tell which is which.I hope and a `topology2` is a real control with its own drawing code in its Paint event and not just a reference to `topology` !? – TaW Sep 03 '19 at 06:55

1 Answers1

0

It's easy. You just need to get graphics context of the image box.

Graphics g = Graphics.FromImage(this.pictureBox1.Image);

After you do that, you use the "g" in my example to do any line drawing, etc that you want to do. Not that this will still require you to invalidate the picture box after drawing to it, but you don't have to have your drawing code in Form_Paint, and you don't need to mess with buffering settings. Personally i prefer this method to drawing in the Form_paint since it is persistent and doesn't have to be redrawn every frame (manually).

To prove it works: enter image description here

John Lord
  • 1,941
  • 12
  • 27
  • 1
    Drawing into a Bitmap instead of onto a control's surface is always an option to consider. But it will help with nothing here and which option to use depends on other questions not part of OP. How would you implement an undo/redo? – TaW Sep 01 '19 at 07:36
  • 1
    1) it's not my job to implement functions the OP didn't even ask for. 2) how is showing him how to write to a picture (which he asked to do) not helpful? 3) I have an entire graphics program i wrote that works this way because it's faster than redrawing every single thing in the onpaint event and forcing an invalidate. So i'm not sure why it's getting bad votes since it does what he asked. At least you (taw) gave some feedback even though it's kind of unclear. I did add a screenshot of it in use though. – John Lord Sep 03 '19 at 03:00
  • 1
    Your post is not helpful because it doesn't identify or fix the issue. Most likely if nothing is showing his way nothing will show your way. The decision between the two is mainly a matter of what is being drawn and how and of no importance when we don't know either.. – TaW Sep 03 '19 at 04:05
  • it's because he's doing it wrong. He's got all of his drawing code in the form paint event so it's going to be hidden behind the picture box which has its own drawbuffer automatically. It looks obvious to me. Now if he were to simply draw directly onto the form itself instead of even having that picturebox control, it would probably work. – John Lord Sep 03 '19 at 04:13
  • Now admittedly he didn't include his layout, but assuming topology2 is the picturebox, he's invalidating it inside the paint event which would (untested) cause the form to redraw again. But leaving that aside, you can NOT draw directly to a picturebox normally because it has a built-in buffer and will immediately overwrite your changes. This is why you write to the image buffer, which my code sample does. – John Lord Sep 03 '19 at 04:21
  • You __may__ have a point with OP using the wrong Paint event but we can't really see. Note that he has a `form2`, a `topology` and a `topology2`. – TaW Sep 03 '19 at 06:52
  • In any case: _you can NOT draw directly to a picturebox normally because it has a built-in buffer and will immediately overwrite your changes_ This is __utter nonsense!__ PBox is ideal for drawing onto __because__ it is __doublebuffered__, so drawing onto it will not cause any flicker. I do it all the time, in fact PBox has __three layers__ and they all get combined nicely!! - And drawing thousands of elements will not cause a performance issue either. When drawing a lot more I always suggest to add in caching in a Bitmap; but that would depend on the situation. – TaW Sep 03 '19 at 06:53
  • a form is doublebuffered too. It's just turned off by default. You can enable it in code. – John Lord Sep 03 '19 at 15:20
  • Um, yes, I know. So?? In fact one can turn on [DoubleBuffered](https://stackoverflow.com/questions/44185298/update-datagridview-very-frequently/44188565#44188565) for just about any control. But only PictureBox and Label have it exposed and on by default. These two are ideal and meant for drawing on. – TaW Sep 03 '19 at 15:28
  • fair enough. What i was attempting to say though is it's possible to draw to the surface instead of the buffer and you won't even know until part of the image is invalidated. That's what i meant by the buffer will overwrite it. – John Lord Sep 03 '19 at 15:38
  • I see what you mean and why you thought that this might help. But that is not what happens. It is the other way around: When you draw the result always shows immediately; doublebuffering just collects the drawing in a buffer to prevent flicker. But: No (proper) drawing code gets executed unless the Paint event is triggered. - Be that as it may, we still don't know what OP's issue is, since he won't answer. Time to close and move on.. – TaW Sep 03 '19 at 16:06