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?!
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:
I am using the below code to draw the circles(reading centres from a csv file) with increasing radius. The increase in radius is 5 units per circle.
namespace MATLAB_file
{
public partial class Form1 : Form
{
string[] read;
float th;
int c = 0;
int r;
public List<PointF> circleCoordinates = new List<PointF>();
int rl;
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
Pen linePen = new Pen(System.Drawing.Color.CornflowerBlue);
Graphics grphx = this.CreateGraphics();
grphx.Clear(this.BackColor);
foreach (PointF point in this.circleCoordinates)
{
Pen redPen1 = new Pen(Color.Red, 100);
e.Graphics.DrawArc(Pens.Red, point.X, point.Y, 1, 1, 0, 120F);
}
linePen.Dispose();
base.OnPaint(e);
}
private void Form1_Load(object sender, EventArgs e)
{
double xx, yy;
int i;
int n = 0;
float[] centre1 = new float[1000];
System.IO.StreamReader sr;
sr = new System.IO.StreamReader("centers.txt", true);
char[] seperators = { ',' };
string data = sr.ReadLine();
read = data.Split(new Char[] { ',' });
rl = read.Length;
int a1 = rl / 2;
for (c = 0; c < rl; c++)
{
centre1[c] = float.Parse(read[c]);
}
while (r < 200)
{
for (i = 0; i < a1; i++)
{
while (th < 360)
{
xx = r * Math.Cos(th) + centre1[2 * i] + 100;
xx1 = (float)xx;
yy = r * Math.Sin(th) + centre1[2 * i + 1] + 100;
yy1 = (float)yy;
this.circleCoordinates.Add(new PointF(xx1, yy1));
this.Invalidate();
th = th + .360F;
}
th = 0;
}
r = r + 5;
}
}
}
}
The above code is displaying all the circles but I do not want all circles to be displayed on canvas, rather only one circle should show with gradual increase in radius
Please suggest how to delete the previous drawn circle on drawing new one. Is there any other way to do it, if my later use includes removal of certain section of circles based on "th" values?
If you move the Clear call within the foreach you will see only the last drawn circle, though this could be achieved with drawing only the Last circleCoordinates too.
foreach (PointF point in this.circleCoordinates)
{
grphx.Clear(this.BackColor);
Pen redPen1 = new Pen(Color.Red, 100);
e.Graphics.DrawArc(Pens.Red, point.X, point.Y, 1, 1, 0, 120F);
}
An alternative interpretation:
You want animation, which generates the onPaint events (by timer ticks or on user input) and you increase a counter to select the circle to draw.
For that, you will need a new member in your program, like int Index; and you could select the circle based on this, using something the following code snippet (assuming you always have at least 1 circleCoordinates, animation restarts after it finished):
PointF point = this.circleCoordinates[Index % circleCoordinates.Length];
or (last circle remain on the screen after the animation)
PointF point = this.circleCoordinates[Math.Min(Index, circleCoordinates.Length - 1)];
I've been trying to create a bitmap, and use said bitmap to create an image which needs to be shown inside a picturebox. So far Google hasn't been of any help. The bitmap needs to be filled with black/white pixels defined in an array, but I've used Aliceblue for now.
When I run the code I get the error "value cant be null" at this line
Bitmap afbeelding = new Bitmap(resolutie, resolutie, g);
Here's what I've tried:
public void draw(Array array)
{
Bitmap afbeelding = new Bitmap(resolutie, resolutie, g);
for(int x = 0; x < array.Length; x++)
{
for (int y = 0; y < array.Length; y++)
{
afbeelding.SetPixel(x, y, Color.AliceBlue);
}
}
pictureBox1.Image = afbeelding;
//afbeelding = pictureBox1.CreateGraphics();
}
Does anyone know how to solve this? I'm not sure how to fill g since there isn't a DrawPixel function in Graphics
Assuming the array contains the definition of an image, you should probably fill the rows of the image with a chunk of the array, instead of filling the image horizontally and vertically with the array.
Let's suppose the array is for a 10 x 10 image, which would make the array 100 bytes long. You need to assign the first 10 bytes to the first row of the image, and so on. You also need to check the value of the array member whether to draw a pixel or not.
Example:
public void draw(bool[] array)
{
Bitmap afbeelding = new Bitmap(resolutieX, resolutieY);
for(int y = 0; y < resolutieY; y++)
{
for (int x = 0; x < resolutieX; x++)
{
if (array[y * resolutieX + x] == true)
afbeelding.SetPixel(x, y, Color.Black);
else
afbeelding.SetPixel(x, y, Color.White);
}
}
pictureBox1.Image = afbeelding;
}
To test this (assuming you have a button1 on the form):
int resolutieX = 100;
int resolutieY = 100;
Random R = new Random();
private void button1_Click(object sender, EventArgs e)
{
bool[] bArray = new bool[resolutieX * resolutieY];
for (int i = 0; i < bArray.Length; i++)
bArray[i] = R.Next(0, 2) == 1 ? true : false;
draw(bArray);
}
public void draw(int[] array)
{
Bitmap afbeelding = new Bitmap(11, 11);
for (int i = 0; i < array.Length; i++)
{
afbeelding.SetPixel(array[i], array[i], Color.Black);
}
pictureBox1.Image = afbeelding;
//afbeelding = pictureBox1.CreateGraphics();
}
private void Form1_Load(object sender, EventArgs e)
{
draw(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
}
Why not locking the pixel array? Check this out, it is faster:
BitmapData Class
I'm looking for a way to improve the performance of some drawing I am doing. Currently it is a 32x32 grid of tiles that I am drawing. Using the following code to draw onto the drawing context
for (int x = startX; x < endX; x++)
{
for (int y = startY; y < endY; y++)
{
dg.Children.Add(
new ImageDrawing(_mapTiles[GameWorldObject.GameMap[x, y].GraphicsTile.TileStartPoint],
new Rect(CountX * 8, CountY * 8, 8, 8)
));
dg.Children.Add(
new GeometryDrawing(
null,
new Pen(
new SolidColorBrush(
Color.FromRgb(255, 0, 20)), .3),
new RectangleGeometry(
new Rect(CountX * 8, CountY * 8, 8, 8)
)
)
);
CountY++;
}
CountY = 0;
CountX++;
}
dc.DrawDrawing(dg);
The Image I am drawing is a CachedBitmap. Even using a CachedBitmap, I still have a delay of about a half second each time I need to redraw the Canvas.
Not sure if there is a more performant way to handle drawing to this grid. Eventually I want to expand control to function as a mini-map, so I need to keep that in mind.
Also, I tried previously to just draw each bitmap directly to the drawing context but that seems a bit slower.
I added DrawingGroup.Freeze() before drawing, and it seemed to help with the performance.
If it's mostly static minimap draw it into an Image, and draw that image. Or you can do a big image, where you draw the whole map into it, and just draw the current visible part of it.
Edit: And maybe this blog post worth a check, whether you are drawing it with software or hardware acceleration on.
Here's a example using WriteableBitmap, the performance of this is mainly related to the size of the whole map whereas your original method is more dependent on the amount of tiles. You could alter it to have an alpha-blended border between the tiles but leaving a gap between them would be easier and more performant. You won't need the code randomising the tiles but you should have some dirty flag so you only redraw the bitmap when its changed.
You may also want to look my answer and the others to this question. That said you don't have as many items and 32x32 using your method wasn't slow for me.
<local:Map x:Name="map" />
<RepeatButton Click="Button_Click" Content="Change" />
private void Button_Click(object sender, RoutedEventArgs e)
{
map.seed++;
map.InvalidateVisual();
}
public class Map : FrameworkElement
{
private int[][] _mapTiles;
public Map()
{
_mapTiles = Directory.GetFiles(#"C:\Users\Public\Pictures\Sample Pictures", "*.jpg").Select(x =>
{
var b = new BitmapImage(new Uri(x));
var transform = new TransformedBitmap(b, new ScaleTransform((1.0 / b.PixelWidth)*tileSize,(1.0 / b.PixelHeight)*tileSize));
var conv = new FormatConvertedBitmap(transform, PixelFormats.Pbgra32, null, 0);
int[] data = new int[tileSize * tileSize];
conv.CopyPixels(data, tileSize * 4, 0);
return data;
}).ToArray();
bmp = new WriteableBitmap(w * tileSize, h * tileSize, 96, 96, PixelFormats.Pbgra32, null);
destData = new int[bmp.PixelWidth * bmp.PixelHeight];
}
const int w = 64, h = 64, tileSize = 8;
public int seed = 72141;
private int oldSeed = -1;
private WriteableBitmap bmp;
int[] destData;
protected override void OnRender(DrawingContext dc)
{
if(seed != oldSeed)
{
oldSeed = seed;
int startX = 0, endX = w;
int startY = 0, endY = h;
Random rnd = new Random(seed);
for(int x = startX; x < endX; x++)
{
for(int y = startY; y < endY; y++)
{
var tile = _mapTiles[rnd.Next(_mapTiles.Length)];
var rect = new Int32Rect(x * tileSize, y * tileSize, tileSize, tileSize);
for(int sourceY = 0; sourceY < tileSize; sourceY++)
{
int destY = ((rect.Y + sourceY) * (w * tileSize)) + rect.X;
Array.Copy(tile, sourceY * tileSize, destData, destY, tileSize);
}
}
}
bmp.WritePixels(new Int32Rect(0, 0, w * tileSize, h * tileSize), destData, w * tileSize * 4, 0);
}
dc.DrawImage(bmp,new Rect(0,0,w*tileSize,h*tileSize));
}
}