how to generate cubes randomly in opengl / c# - c#

I'm trying to build a game 'blocks' which sees the matches cubes and delete them.
This code makes two cubes and moves them along the y-axis. Then, it supposed to disappear and 100 cube in different places to appear. My problem is I don't know how to make the cubes look disappearing nor how to write the function 'cube generation' , I'm writing in 3d.
Any help of what should I do.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Tao.OpenGl;
using System.Threading;
using Tao.FreeGlut;
namespace TAOSample
{
class VertexDania{
double vx, vy , vz;
public double X{
get{return vx;}
set{ vx = value;}
}
public double Y{
get{return vy;}
set{ vy = value;}
}
public double Z{
get{return vz;}
set{ vz = value;}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
simpleOpenGlControl1.InitializeContexts();
Gl.glClearColor(1, 1, 0, 0);
Gl.glClearDepth(1.0);
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Glu.gluPerspective(45, simpleOpenGlControl1.Height /(double)simpleOpenGlControl1.Width, 0.1, 1000);
Gl.glViewport(0, 0, simpleOpenGlControl1.Width, simpleOpenGlControl1.Height);
Tao.FreeGlut.Glut.glutInit();
}
private void simpleOpenGlControl1_Paint(object sender, PaintEventArgs e)
{
Gl.glEnable(Gl.GL_LIGHTING);
Gl.glEnable(Gl.GL_LIGHT0);
Gl.glEnable(Gl.GL_COLOR_MATERIAL);
Gl.glEnable(Gl.GL_DEPTH_TEST);
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
Gl.glLoadIdentity();
VertexDania[] points = new VertexDania[]
{
new VertexDania{X=0 , Y=0.73 , Z=-0.75},
new VertexDania{X=0.1 , Y=0.85 , Z=-0.75},
new VertexDania{X=-0.01 , Y=0.95 , Z=-0.75},
new VertexDania{X=-0.1 , Y=0.80 , Z=-0.75},
new VertexDania{X=0.1 , Y=0.65 , Z=-1},
new VertexDania{X=0 , Y=0.5 , Z=-0.75},
new VertexDania{X=-0.1 , Y=0.6 , Z=-1}
};
VertexDania[] point = new VertexDania[]
{
new VertexDania{X=0, Y=0.23, Z=-0.75},
new VertexDania{X=0.1 , Y=0.35 , Z=-0.75},
new VertexDania{X=-0.01 , Y=0.45 , Z=-0.75},
new VertexDania{X=-0.1 , Y=0.30 , Z=-0.75},
new VertexDania{X=0.1 , Y=0.1 , Z=-0.75},
new VertexDania{X=0 , Y=0.0 , Z=-0.75},
new VertexDania{X=-0.1, Y=0.1, Z=-0.75}
};
double[,] normals = new double[,]
{
{0,0,1},
{0,0,-1},
{0,0,-1}
};
int[,] faces = new int[,]
{
{0 , 1 ,4 , 5},
{0 , 3 , 6 , 5},
{0, 3 , 2 , 1}
};
Gl.glColor3d(1, 0, 0);
Gl.glTranslated(0, ymove, 0.1);
for (int i = 0; i < faces.GetLength(0); i++)
{
Gl.glNormal3d(normals[i, 0], normals[i, 1], normals[i, 2]);
Gl.glBegin(Gl.GL_QUADS);
for (int j = 0; j < 4; j++)
{
Gl.glVertex3d(points[faces[i, j]].X, points[faces[i, j]].Y, points[faces[i, j]].Z);
}
Gl.glEnd();
Gl.glNormal3d(normals[i, 0], normals[i, 1], normals[i, 2]);
Gl.glBegin(Gl.GL_QUADS);
for (int j = 0; j < 4; j++)
{
Gl.glVertex3d(point[faces[i, j]].X, point[faces[i, j]].Y, point[faces[i, j]].Z);
}
Gl.glEnd();
}
if (ymove >= -3)
ymove -= 0.03;
}
void redrawThread()
{
while (true)
{
Thread.Sleep(50);
simpleOpenGlControl1.Invoke(new Action(delegate()
{
simpleOpenGlControl1.Draw();
}));
}
}
private void Form1_Load(object sender, EventArgs e)
{
Thread t = new Thread(redrawThread);
t.IsBackground = true;
t.Start();
}
private void simpleOpenGlControl1_Resize(object sender, EventArgs e)
{
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Glu.gluPerspective(45, simpleOpenGlControl1.Width / (double)simpleOpenGlControl1.Height, 0.1, 100);
Gl.glViewport(0, 0, simpleOpenGlControl1.Width, simpleOpenGlControl1.Height);
// Gl.glMatrixMode(Gl.GL_MODELVIEW);
}
}
}

This is about data structure / organization than anything else. Since you know how to draw a cube you can start by making a Cube class. This is fairly trivial with immediate mode OpenGL (what you are using)
Since you know how to draw a cube, this class should be fairly easy :
class Cube
{
// Member variables
x,y,z position
r,g,b color
// Functions
public Cube(position, color); // Constructor
public draw(); // draw the cube
}
Then you need a structure that keeps the information about what you should be drawing.
class CubeGroup
{
// Member variables
private Cube[] cubes; // cube array
// Member functions
public AddCube(Cube in_cube); // Adds a cube
public DeleteCube(int index); // Removes a cube
public draw(); // Draws this group of cubes
....
}
You need at least some structures to help you achieving your goal. This makes you able to at least manage one set of cubes. You need to carefully think about what operations you want to do on your data and make structures that makes sense for this. It seems you want your data to change over time as well. Then you need some other structure to deal with that.
This is just pure speculation at this point, but hopefully it might lead you in the right direction. The classes here are just examples and in no way meant to be "the solution".

Related

Open GL DrawArrays only draws one triangle/line/point (OpenTK / C#)

In this basic rendering test I've got it loading up data for 32 vertices from a file. When it draws it will only draw a single primitive and ignores the rest of the array. For example, if I call GL.DrawArrays(PrimitiveType.Triangles, i, 3) it will draw element i.
The array does contain much more than one triangle worth of data, as the example code here will draw a different triangle every time you click, but again only one each time it renders. If I make multiple calls to DrawArrays with a different int first parameter it will also draw an additional triangle for each call. (I'm pretty sure the purpose of putting all the data into a buffer on the GPU isn't so that you can make a zillion draw calls.)
I've tried using drawelements as an alternative to drawarrays, but no matter what I put for the parameters I get a System.AccessViolationException. All the tutorials I can find always just draw one triangle and/or use drawelements, so I haven't been able to find code samples to help me figure out what is or might be different about drawing multiple primitives.
Form1 is just a blank form with a glcontrol on it. I'm using NuGet packages OpenTK 3.1.0 and OpenTK.GLControl 3.1.0.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenTK.Graphics.OpenGL4;
using OpenTK;
namespace _3dRenderingTesting
{
public partial class Form1 : Form
{
int program;
int myVAO;
int i = 0;
private void glControl1_MouseClick(object sender, MouseEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Enable(EnableCap.DepthTest);
GL.UseProgram(program);
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
GL.DrawArrays(PrimitiveType.Triangles, i, 3);
glControl1.SwapBuffers();
i++;
}
private void Form1_Load(object sender, EventArgs e)
{
program = compileShaders();
getMeshFromFile();
}
public Form1()
{
InitializeComponent();
}
private int compileShaders()
{
string vShader;
using (System.IO.StreamReader file = new System.IO.StreamReader(#"vertexshader.txt"))
{
vShader = file.ReadToEnd();
}
string fShader = "";
using (System.IO.StreamReader file = new System.IO.StreamReader(#"fragmentshader.txt"))
{
fShader = file.ReadToEnd();
}
int vertexShader = GL.CreateShader(ShaderType.VertexShader);
GL.ShaderSource(vertexShader, vShader);
GL.CompileShader(vertexShader);
int fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
GL.ShaderSource(fragmentShader, fShader);
GL.CompileShader(fragmentShader);
int program = GL.CreateProgram();
GL.AttachShader(program, vertexShader);
GL.AttachShader(program, fragmentShader);
GL.LinkProgram(program);
GL.DeleteShader(vertexShader);
GL.DeleteShader(fragmentShader);
return program;
}
private int vertBuffer;
private int vertLength;
private void getMeshFromFile()
{
List<string> fileContents = new List<string>();
List<float> fVerts = new List<float>();
List<int> fFaces = new List<int>();
System.IO.StreamReader file = new System.IO.StreamReader(#"C:\Users\abc\Desktop\32 Vertex Sphere.obj");
while (!file.EndOfStream)
{
string ts = file.ReadLine().Trim().ToLower();
//find all lines that begin with "v"
//these are vertices
if (ts.Length > 0)
{
if (ts.Substring(0, 1) == "v")
{
const string reduceMultiSpace = #"[ ]{2,}";
string[] tSplit = System.Text.RegularExpressions.Regex.Replace(ts.Replace(" ", ",").Replace("\t", ","), reduceMultiSpace, ",").Split(',');
if (tSplit.Length < 4)
{
MessageBox.Show("Vertex list failure (< 3 vertices)");
Application.Exit();
}
fVerts.Add(float.Parse(tSplit[1]));
fVerts.Add(float.Parse(tSplit[2]));
fVerts.Add(float.Parse(tSplit[3]));
}
if (ts.Substring(0, 1) == "f")
{
const string reduceMultiSpace = #"[ ]{2,}";
string[] tSplit = System.Text.RegularExpressions.Regex.Replace(ts.Replace(" ", ",").Replace("\t", ","), reduceMultiSpace, ",").Split(',');
if (tSplit.Length < 4)
{
MessageBox.Show("Face list failure (< 3 vertices)");
Application.Exit();
}
fFaces.Add(int.Parse(tSplit[1]));
fFaces.Add(int.Parse(tSplit[2]));
fFaces.Add(int.Parse(tSplit[3]));
}
}
}
file.Close();
float[] fVArray = new float[fVerts.Count];
for (int i = 0; i < fVerts.Count; i++) fVArray[i] = fVerts[i];
GL.CreateBuffers(1, out vertBuffer);
GL.BindBuffer(BufferTarget.ArrayBuffer, vertBuffer);
GL.NamedBufferStorage(vertBuffer, sizeof(float) * fVerts.Count, fVArray, BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapReadBit);
vertLength = fVerts.Count;
GL.CreateVertexArrays(1, out myVAO);
GL.BindVertexArray(myVAO);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
GL.EnableVertexAttribArray(0);
}
}
}
The vertex file:
v 0 -1 0
v -0.4924088 0.7946551 -0.3550447
v 0.5536816 0.7946597 -0.2489026
v -0.06729457 -0.74536 0.6632572
v -0.9165487 0.3333308 -0.220973
v 0.06729444 0.7453622 -0.6632546
v 0.2669053 0.3333269 0.9042426
v -0.06128498 0.7946486 0.6039683
v 0.8958825 -0.1875861 -0.4027478
v -5.442639E-06 1 9.193043E-06
v -0.6496407 -0.3333396 0.6832653
v 0.6080519 -0.7453524 -0.2733544
v -0.5536865 -0.7946557 0.2489048
v -0.8958843 0.1875851 0.4027444
v 0.4430935 -0.3333268 -0.8322027
v 0.9422514 0.3333381 -0.03236934
v -0.5407486 -0.7453555 -0.3899181
v -0.09915181 -0.1875999 0.9772283
v 0.4924095 -0.7946548 0.3550446
v -0.9422525 -0.333336 0.03236436
v 0.0612843 -0.7946532 -0.6039625
v 0.91655 -0.3333305 0.2209681
v 0.4991637 -0.3333373 0.7998261
v -0.4430951 0.3333244 0.8322028
v -0.2669008 -0.3333296 -0.9042429
v -0.7967249 -0.1875918 -0.5744899
v 0.5407484 0.7453554 0.3899185
v 0.7967286 0.1875919 0.5744848
v 0.09915482 0.1876006 -0.9772278
v 0.6496406 0.3333421 -0.6832644
v -0.6080542 0.7453504 0.2733551
v -0.4991595 0.3333374 -0.7998286
The simple v&f shaders:
#version 450 core
layout (location = 0) in vec4 position;
void main(void)
{
gl_Position = position;
}
#version 450 core
out vec4 FragColor;
void main(void)
{
FragColor = vec4(1.0,1.0,1.0,1.0);
}
The ELEMENT_ARRAY_BUFFER is stated in the Vertex Array Object, so the vertex array object has to be bound, before the element array buffer can be bound. See Index buffers
# create vertex buffer
GL.CreateBuffers(1, out vertBuffer);
GL.NamedBufferStorage(vertBuffer, sizeof(float) * fVerts.Count, fVArray,
BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapReadBit);
vertLength = fVerts.Count;
# create index buffer
GL.CreateBuffers(1, out indexBuffer);
GL.NamedBufferStorage(indexBuffer, sizeof(int) * fFaces.Count, fFaces ,
BufferStorageFlags.None);
indexCount = fFaces.Count;
# bind vertex array object
GL.CreateVertexArrays(1, out myVAO);
GL.BindVertexArray(myVAO);
# specify array of generic vertex attribute data for currently bound vertex array object
GL.BindBuffer(BufferTarget.ArrayBuffer, vertBuffer);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
GL.EnableVertexAttribArray(0);
# associate index buffer to currently bound vertex array object
GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);
Drawing:
GL.DrawElements(BeginMode.Triangles, indexCount, DrawElementsType.UnsignedInt, 0);

Placing buffered matrix rotated graphic on a fixed image

I am trying to put a movable needle (pointer) on a fixed graphic of a gauge (meter). The needle is moved by using a matrix rotate on a buffered graphics. I can get the fixed graphic and the needle to show. But when I render to the screen the last placed image deletes the prior graphic. I am using a timer to get the needle animation and a track bar input to produce the movement. The needle does the exact movement I am looking for.
I just cannot get the fixed background and needle to appear at the same time.
Any ideas?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Dial01
{
public partial class dial01Form : Form
{
// Establishes timer for graphics animation
private Timer timer01 = new Timer();
/* Establishes a graphic buffer to write to
* prior display on screen */
private Graphics myGraphics;
private BufferedGraphics myBufferedGraphics1;
// Establishes manager for embedded resources (Images)
private System.Resources.ResourceManager myRM = new
System.Resources.ResourceManager("Resources.resx",
System.Reflection.Assembly.GetExecutingAssembly());
int y = 0; // Rotation value
Graphics g,g1; // Graphics objects
public dial01Form()
{
// Establishes size of Dial01Form
this.Width = 500;
this.Height = 500;
// Gets reference to the current BufferedGraphicsContext
BufferedGraphicsContext myContext1 = BufferedGraphicsManager.Current;
// Specifically sets maximum buffer size
myContext1.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
// Sets the buffer size
myBufferedGraphics1 = myContext1.Allocate(this.CreateGraphics(),
new Rectangle(0, 0, this.Width, this.Height));
// Actvates timer and sets interval
timer01.Enabled = true;
timer01.Tick += onTimer;
timer01.Interval = 20;
timer01.Start();
// Initializes form components
InitializeComponent();
}
private void onTimer(object sender, System.EventArgs e)
{
myGraphics = this.CreateGraphics();
// Initializes graphics buffer variable
g1 = myBufferedGraphics1.Graphics;
// Clears graphic buffer with a color
g1.Clear(SystemColors.Control);
// Initializes an image variable for Dial Outline
Image dial01Outline = Dial01.Properties.Resources.DialOutline250x250;
// Draw Dial Outline to graphics buffer
myGraphics.DrawImage(dial01Outline, (ClientSize.Width / 2) - 100,
(ClientSize.Height / 2) - 100);
// Goto drawPointer method passing trackBar1 value
drawPointer(trackBar1.Value);
// Render buffered graphics to screen
// myBufferedGraphics.Render(Graphics.FromHwnd(this.Handle));
myBufferedGraphics1.Render();
}
public int drawPointer(int trkBarValue)
{
int x = trkBarValue;
y = 0;
if (225 + x <= 360) { y = 222 + x; }
else if (225 + x > 360) { y = x - 135; }
// These two labels are for testing purposes
label1.Text = ("Trk Bar Val = " + x).ToString();
label2.Text = ("Ptr value = " + y).ToString();
y = y + 180;
// Matrix rotation to pointer
Matrix myMatrix = new Matrix();
myMatrix.Rotate(y, MatrixOrder.Append);
myMatrix.Translate(this.ClientSize.Width / 2,
this.ClientSize.Height / 2, MatrixOrder.Append);
g1.Transform = myMatrix;
// Pointer polygon
PointF point1 = new PointF(0.0F, 0.0F);
PointF point2 = new PointF(0.0F, 50.0F);
PointF point3 = new PointF(3.0F, 55.0F);
PointF point4 = new PointF(7.0F, 50.0F);
PointF point5 = new PointF(7.0F, 0.0F);
PointF[] polyPoints =
{
point1,
point2,
point3,
point4,
point5
};
g1.FillPolygon(Brushes.Black, polyPoints);
return y;
}
private void dial01Form_Load(object sender, EventArgs e)
{
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
}
}
}
The general graphics approach you've taken is not appropriate for a winforms app.
The way graphics works in winforms, whenever the form is covered/uncovered/resized/etc, Windows tells it to repaint itself. Anything you've done with CreateGraphics will be overwritten at this point. This is why you shouldn't call CreateGraphics.
Instead, you should intercept the repainting process via the Paint event, and do all your custom painting there. You can still repaint on a timer, you just call Invalidate() inside the timer, which causes the form to repaint as soon as it can.
This is the general shape of the "right way" to do it:
public partial class dial01Form : Form
{
private Timer timer01 = new Timer();
int y = 0; // Rotation value
public dial01Form()
{
// Establishes size of Dial01Form
this.Width = 500;
this.Height = 500;
// Actvates timer and sets interval
timer01.Enabled = true;
timer01.Tick += onTimer;
timer01.Interval = 20;
timer01.Start();
// handle the paint event
this.Paint += OnPaint;
// Initializes form components
InitializeComponent();
}
private void OnPaint(object sender, PaintEventArgs e)
{
// all painting here, targeting e.Graphics
e.Graphics.Clear(SystemColors.Control);
Image dial01Outline = Dial01.Properties.Resources.DialOutline250x250;
e.Graphics.DrawImage(dial01Outline, (ClientSize.Width / 2) - 100,
(ClientSize.Height / 2) - 100);
drawPointer(e.Graphics, trackBar1.Value);
}
private void onTimer(object sender, System.EventArgs e)
{
this.Invalidate();
}
public int drawPointer(Graphics g1, int trkBarValue)
{
// elided: same code as before, but using the g1 parameter instead of a field
}
}
You shouldn't have problems with flickering, I think - double-buffering is enabled by default. Make sure your form's DoubleBuffered property is set to True though.

How can I draw a Hilbert Curve Fractal recursively using C# GDI+ Graphics and Windows Forms?

I am working on a project in which I need to use recursion to draw a Hilbert Curve fractal in a Windows Forms application in C#. I must use GDI+ graphics for this, but I am new to GDI+ graphics. Below is my entire code for the Form class that actually draws the curve. At the end of this post, I have included photos demonstrating my erroneous output and the expected output.
The DrawRelative() function is supposed to draw the next line segment from the current [x,y] coordinates to the new [x,y] coordinates, which are calculated by adding the xDistance and yDistance values passed into the DrawRelative() function to the xCurrent and yCurrent class properties.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace HilbertCurveFractal
{
public partial class FractalDisplay : Form
{
public int MaxDepth { get; set; }
public int CurveType { get; set; }
public int xCurrent { get; set; }
public int yCurrent { get; set; }
public int xLength { get; set; }
public int yLength { get; set; }
public FractalDisplay(int DepthValue, int SelectedCurve)
{
InitializeComponent();
MaxDepth = DepthValue;
CurveType = SelectedCurve;
xCurrent = 250;
yCurrent = 250;
xLength = 0;
yLength = 2;
}
private void FractalDisplay_Load(object sender, EventArgs e)
{
this.DoubleBuffered = true;
if (CurveType == 1) // Run the Hilbert Curve Generator
{
GenerateHilbertCurve(MaxDepth, xLength, yLength);
}
else if (CurveType == 2) // Run the Koch Curve Generator
{
}
else if (CurveType == 3) // Run the Sierpinski Curve Generator
{
}
else
{
MessageBox.Show("Error! - No Curve Type Selected. Ending Program.");
Application.Exit();
}
}
private void GenerateHilbertCurve(int depth, int xDistance, int yDistance)
{
//if (depth == 0) // Base Case
//{
// return;
//}
//else { }
if (depth > 0)
{
GenerateHilbertCurve(depth - 1, yDistance, xDistance);
}
else { }
// Draw Part of Curve Here
DrawRelative(xDistance, yDistance);
if (depth > 0)
{
GenerateHilbertCurve(depth - 1, xDistance, yDistance);
}
else { }
// Draw Part of Curve Here
DrawRelative(yDistance, xDistance);
if (depth > 0)
{
GenerateHilbertCurve(depth - 1, xDistance, yDistance);
}
else { }
// Draw Part of Curve Here
DrawRelative((- 1 * xDistance), (-1 * yDistance));
if (depth > 0)
{
GenerateHilbertCurve(depth - 1, (-1 * yDistance), (-1 * xDistance));
}
else { }
}
// Create a New Paint Event Handler
private void DrawRelative(int xDistance, int yDistance)
{
xLength = xDistance;
yLength = yDistance;
this.Paint += new PaintEventHandler(HilbertCurve_Paint);
}
// Perform the Actual Drawing
private void HilbertCurve_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
// Discover where the new X and Y points will be
int xNew, yNew;
xNew = xCurrent + xLength;
yNew = yCurrent + yLength;
// Paint from the current position of X and Y to the new positions of X and Y
e.Graphics.DrawLine(Pens.Red, xCurrent, yCurrent, xNew, yNew);
// Update the Current Location of X and Y
xCurrent = xNew;
yCurrent = yNew;
}
}
}
The first photo (below) is the incorrect output from the Hilbert Curve function given a MaxDepth of 1.
The second photo (below) represents what I should be getting from this set of functions (given a MaxDepth value of 1 passed in).
Because it seems like the algorithm for recursion is coded correctly, I suspect that I am not using the GDI+ graphics in the proper way, or my class properties are being updated/set incorrectly somewhere in the recursive calls. What can I do to fix my drawing algorithm? Thank you in advance.
To be honest, I didn't initially understand the implementation you have for generating the points for the Hilbert curve. I'm familiar with a couple of different approaches, neither of which look like that.
But, that's an entirely different question. Your main issue at hand is really just that you don't understand how the drawing mechanism in Winforms works. Briefly: there's a Paint event, which your code should handle by drawing what needs to be drawn. Subscribing to the Paint event doesn't cause anything to happen; it's just a way of registering to be notified when drawing is supposed to occur.
Typically, one would use the Designer to subscribe to the event, by navigating to the "Events" tab of the Properties pane for an object in the Designer (e.g. your Form) and selecting the appropriate event handler (or double-clicking in the empty box next to the event to have the Designer automatically insert an empty handler for you to fill in). You can also, when handling the Paint event in your own object, simply override the OnPaint() method.
In either case, the correct technique is to establish the prerequisites for drawing, then call Invalidate() which causes the framework to then raise the Paint event, at which time you can actually draw what you want to draw.
Note that between commenter TaW and me, we have suggested two different approaches to drawing: I suggested pre-computing all of the necessary data for drawing, and then just draw that when the Paint event is raised; TaW has suggested calling the recursive method from the Paint event handler, and drawing directly as you traverse the recursive algorithm.
Both techniques are fine, but of course there are pros and cons to either, having mostly to do with the classic trade-off of time and space. With the former technique, the cost to generate the curve is incurred only once, when the parameters for the curve change. Drawing occurs more quickly, because all the code has to do is draw the pre-existing data. With the latter technique, there is no need to store the data, as each new point generated is used immediately, but of course this means all of the points have to be regenerated every time the window is redrawn.
For this particular application, in practice I don't think it matters much. At typical screen resolutions, you won't be able to make out the features of the curve long before you start to hit the limits of data storage for the points to draw. Similarly, the execution of the algorithm is so fast that there's really no harm in recalculating the points each time the window needs to be redrawn. Just keep in mind that these are trade-offs you may have to judge more closely in other scenarios.
Okay, so what's all that mean? Well, when I converted it to something that used the Graphics class correctly, I couldn't get your implementation to draw a Hilbert curve, so I changed that part of the code to use an implementation I know works. You can find a detailed discussion on how this particular implementation works here: Hilbert Curve
Concepts & Implementation
Below, I have provided two different versions of that particular Hilbert curve implementation, the first using the "retained" approach (i.e. generate the data, then draw it), and the second using the "immediate" approach (i.e. generate the data every time you want to draw the window, as the drawing is occurring):
"Retained" method:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DoubleBuffered = true;
}
private PointF[] _points;
private void FractalDisplay_Load(object sender, EventArgs e)
{
Redraw();
}
private void Redraw()
{
List<PointF> points = new List<PointF>();
GenerateHilbert(0, 0, 1, 0, 0, 1, (int)numericUpDown1.Value, points);
_points = points.ToArray();
Invalidate();
}
private void GenerateHilbert(PointF origin, float xi, float xj, float yi, float yj, int depth, List<PointF> points)
{
if (depth <= 0)
{
PointF current = origin + new SizeF((xi + yi) / 2, (xj + yj) / 2);
points.Add(current);
}
else
{
GenerateHilbert(origin, yi / 2, yj / 2, xi / 2, xj / 2, depth - 1, points);
GenerateHilbert(origin + new SizeF(xi / 2, xj / 2), xi / 2, xj / 2, yi / 2, yj / 2, depth - 1, points);
GenerateHilbert(origin + new SizeF(xi / 2 + yi / 2, xj / 2 + yj / 2), xi / 2, xj / 2, yi / 2, yj / 2, depth - 1, points);
GenerateHilbert(origin + new SizeF(xi / 2 + yi, xj / 2 + yj), -yi / 2, -yj / 2, -xi / 2, -xj / 2, depth - 1, points);
}
}
// Perform the Actual Drawing
private void HilbertCurve_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
if (_points != null)
{
float scale = Math.Min(ClientSize.Width, ClientSize.Height);
e.Graphics.ScaleTransform(scale, scale);
using (Pen pen = new Pen(Color.Red, 1 / scale))
{
e.Graphics.DrawLines(pen, _points);
}
}
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
Redraw();
}
protected override void OnClientSizeChanged(EventArgs e)
{
base.OnClientSizeChanged(e);
Invalidate();
}
}
"Immediate" method:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DoubleBuffered = true;
}
private void Redraw()
{
Invalidate();
}
private PointF GenerateHilbert(PointF origin, float xi, float xj, float yi, float yj, int depth,
PointF? previous, Graphics graphics, Pen pen)
{
if (depth <= 0)
{
PointF current = origin + new SizeF((xi + yi) / 2, (xj + yj) / 2);
if (previous != null)
{
graphics.DrawLine(pen, previous.Value, current);
}
return current;
}
else
{
previous = GenerateHilbert(origin, yi / 2, yj / 2, xi / 2, xj / 2, depth - 1, previous, graphics, pen);
previous = GenerateHilbert(origin + new SizeF(xi / 2, xj / 2), xi / 2, xj / 2, yi / 2, yj / 2, depth - 1, previous, graphics, pen);
previous = GenerateHilbert(origin + new SizeF(xi / 2 + yi / 2, xj / 2 + yj / 2), xi / 2, xj / 2, yi / 2, yj / 2, depth - 1, previous, graphics, pen);
return GenerateHilbert(origin + new SizeF(xi / 2 + yi, xj / 2 + yj), -yi / 2, -yj / 2, -xi / 2, -xj / 2, depth - 1, previous, graphics, pen);
}
}
// Perform the Actual Drawing
private void HilbertCurve_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
float scale = Math.Min(ClientSize.Width, ClientSize.Height);
e.Graphics.ScaleTransform(scale, scale);
using (Pen pen = new Pen(Color.Red, 1 / scale))
{
GenerateHilbert(new PointF(), 1, 0, 0, 1, (int)numericUpDown1.Value, null, e.Graphics, pen);
}
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
Redraw();
}
protected override void OnClientSizeChanged(EventArgs e)
{
base.OnClientSizeChanged(e);
Invalidate();
}
}
In both examples I've made some other changes which are not strictly needed for the purpose of illustrating the techniques, but which are still useful:
The curve itself is computed in unit space (i.e. a square of side length of 1), and then drawn by scaling the drawing to fit the window.
Where it makes sense, individual coordinates are passed as whole PointF values instead. This simplifies reuse of the values and adding new offsets to the X and Y values.
Since the drawing is now scaled to the window, the window is redrawn if its size changes.
For simplicity, this Form is self-contained, with a NumericUpDownControl that determines the recursion depth. I didn't include instantiation of this control; I presume you can add the appropriate control yourself in the Designer, to make the above compile.
Addendum:
I've had a chance to look over the other examples on the Internet of the algorithm that you tried to implement. Now that I understand what the basic mechanism of the algorithm is, I was able to fix your version so that it works (the main problem was that you were using instance fields to store the deltas for the algorithm, but also using the same fields to initialize the algorithm, so once the algorithm ran once, subsequent executions wouldn't work). So for the sake of completeness, here is a second "retained" version of the code, using your preferred algorithm instead of the one I used above:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DoubleBuffered = true;
}
private PointF _previousPoint;
private PointF[] _points;
private void FractalDisplay_Load(object sender, EventArgs e)
{
Redraw();
}
private void Redraw()
{
List<PointF> points = new List<PointF>();
// Start here, to provide a bit of margin within the client area of the window
_previousPoint = new PointF(0.025f, 0.025f);
points.Add(_previousPoint);
int depth = (int)numericUpDown1.Value;
float gridCellCount = (float)(Math.Pow(2, depth) - 1);
// Use only 95% of the available space in the client area. Scale
// the delta for drawing to fill that 95% width/height exactly,
// according to the number of grid cells the given depth will
// produce in each direction.
GenerateHilbert3(depth, 0, 0.95f / gridCellCount, points);
_points = points.ToArray();
Invalidate();
}
private void GenerateHilbert(int depth, float xDistance, float yDistance, List<PointF> points)
{
if (depth < 1)
{
return;
}
GenerateHilbert(depth - 1, yDistance, xDistance, points);
DrawRelative(xDistance, yDistance, points);
GenerateHilbert(depth - 1, xDistance, yDistance, points);
DrawRelative(yDistance, xDistance, points);
GenerateHilbert(depth - 1, xDistance, yDistance, points);
DrawRelative(-xDistance, -yDistance, points);
GenerateHilbert(depth - 1, -yDistance, -xDistance, points);
}
private void DrawRelative(float xDistance, float yDistance, List<PointF> points)
{
// Discover where the new X and Y points will be
PointF currentPoint = _previousPoint + new SizeF(xDistance, yDistance);
// Paint from the current position of X and Y to the new positions of X and Y
points.Add(currentPoint);
// Update the Current Location of X and Y
_previousPoint = currentPoint;
}
// Perform the Actual Drawing
private void HilbertCurve_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
if (_points != null)
{
float scale = Math.Min(ClientSize.Width, ClientSize.Height);
e.Graphics.ScaleTransform(scale, scale);
using (Pen pen = new Pen(Color.Red, 1 / scale))
{
e.Graphics.DrawLines(pen, _points);
}
}
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
Redraw();
}
protected override void OnClientSizeChanged(EventArgs e)
{
base.OnClientSizeChanged(e);
Invalidate();
}
}
As before, I've modified your implementation slightly, so that the drawing is scaled to fit within the window at all depths. This involves drawing into the unit square and then setting the transform appropriately according to the window size.
In addition to fixing the basic usage of Graphics, and the issue with the xLength and yLength fields, I also fixed a minor bug in your code (where you were recursing one level too deep) and cleaned up the recursion a bit (there's no need to repeat the depth checkā€¦just do it once, at the beginning of the recursive method).
It is of course possible to implement this in the "immediate" style as well. I think between this new code example, and the "immediate" method example above, I can leave that exercise to the reader. :)
Here is the fractal generator I came up with after heeding the advice of #Peter Duniho - The code shown does not include the form that actually gets the depth level (maxDepth) of recursion requested by the user.
public partial class HilbertDisplay : Form
{
private int maxDepth;
private int xCurrent = 0;
private int yCurrent = 0;
private int xNew = 0;
private int yNew = 0;
public HilbertDisplay(int depthEntered)
{
InitializeComponent();
maxDepth = depthEntered;
}
private void HilbertDisplay_Load(object sender, EventArgs e)
{
this.DoubleBuffered = true;
this.Update();
}
// Perform the Drawing
private void HilbertDisplay_Paint(object sender, PaintEventArgs e)
{
// Run the Hilbert Curve Generator
// Use a line segment length of 10 for Y
GenerateHilbertCurve(maxDepth, 0, 10, e);
}
// The Recursive Hilbert Curve Generator
private void GenerateHilbertCurve(int depth, int xDistance, int yDistance, PaintEventArgs e)
{
if (depth < 1)
{
return;
}
else
{
GenerateHilbertCurve(depth - 1, yDistance, xDistance, e);
// Paint from the current position of X and Y to the new positions of X and Y
FindPointRelative(xDistance, yDistance);
e.Graphics.DrawLine(Pens.Red, xCurrent, yCurrent, xNew, yNew); // Draw Part of Curve Here
UpdateCurrentLocation();
GenerateHilbertCurve(depth - 1, xDistance, yDistance, e);
// Paint from the current position of X and Y to the new positions of X and Y
FindPointRelative(yDistance, xDistance);
e.Graphics.DrawLine(Pens.Blue, xCurrent, yCurrent, xNew, yNew); // Draw Part of Curve Here
UpdateCurrentLocation();
GenerateHilbertCurve(depth - 1, xDistance, yDistance, e);
// Paint from the current position of X and Y to the new positions of X and Y
FindPointRelative(-xDistance, -yDistance);
e.Graphics.DrawLine(Pens.Green, xCurrent, yCurrent, xNew, yNew); // Draw Part of Curve Here
UpdateCurrentLocation();
GenerateHilbertCurve(depth - 1, (-1 * yDistance), (-1 * xDistance), e);
}
}
private void FindPointRelative(int xDistance, int yDistance)
{
// Discover where the new X and Y points will be
xNew = xCurrent + xDistance;
yNew = yCurrent + yDistance;
return;
}
private void UpdateCurrentLocation()
{
// Update the Current Location of X and Y
xCurrent = xNew;
yCurrent = yNew;
return;
}
}
This code, unlike that of #Peter Duniho, does not account for the form's size. This depicts a Hilbert Curve fractal up to a recursion depth of 6 or 7 on my laptop (due to limitations on window size made by my laptop screen size/resolution).
I know that my solution is not as elegant as that of #Peter Duniho, but as this is for an assignment I did not want to simply copy his code. I made edits based on his suggestions, especially in regard to the Paint event.

How to send data from one form to another to create a chart

Is it possible to calculate numbers in one form and use it to make a chart in a second form?
I just want to show a line graph which gets the data from a set of numbers being calculated.
I have only been using c# for a week, the only way I know how to make a chart is using numericUpDown on the current form which is not what I want.
like so ...
Point[] pts = new Point[1000];
int count = 0;
pts[count++] = new Point((int)numericUpDown1.Value, (int)numericUpDown2.Value);
for (int i = 0; i < count; i++)
{
if (i != 0)
{
this.CreateGraphics().DrawLine(new Pen(Brushes.Red, 4), pts[i - 1], pts[i]);
}
else
{
this.CreateGraphics().DrawLine(new Pen(Brushes.Red, 4), pts[i], pts[i]);
}
}
You can pass data to a new form, and then draw the graph from there. One way would be in the constructor, when you create the new form to draw the graph on, for example:
Form to calculate graph points (or whatever data you need to draw the graph)
public class CalculationForm
{
public CalculationForm()
{
InitializeComponent();
Point[] points = CalculatePoints();
GraphForm graphForm = new GraphForm(points);
graphForm.Show();
}
private Point[] CalculatePoints()
{
// method to generate points
return points;
}
}
Then in the form you want to draw the graph in:
public class GraphForm
{
public GraphForm(Point[] points)
{
InitializeComponent();
DrawGraph(points);
}
private void DrawGraph(Point[] points)
{
// Code to draw your graph goes here
}
}

picturebox Control is not working C# .NET (Visual Studio)

I'm doing a Piano Keyboard which also has a Music Staff with it so that when you press a Key on the Keyboard, according to the duration a music note (crotchet, semi-breve etc) pops up on the Music Staff.
I have a Form (called Form1) which in it it has a Panel (Panel1) which contains the Keyboard Keys made of Buttons. I also have a Music Staff made of a PictureBox which holds the staff (or stave don't know what it's called) and a Test Text Box which holds the pitches (basically the note played as they are mapped)
The problem is this. I need to make the music notes show on the staff (pictureBox1) so in Form1 which is acting as the main class but whenever I write
mn = new MusicNote (nPos,nPitch, duration,nShape);
pictureBox1.Controls.Add(this.mn);
pictureBox1.Controls[pictureBox1.Controls.Count - 1].BringToFront(); //Bring the notes OVER the Stave
It basically doesn't work BUT when I replace every pictureBox1 with just this OR panel1 (e.g.)
this.Controls.Add(this.mn);
it shows the Music Note either on the FORM1 (the grey space) or on the Keyboard itself (see further down to see the Keyboard). The problem is that with this, it doesn't actually adds to the PictureBox but to the Form1/Panel
Do you have any idea how to fix this and make the music Notes actually PART of the PictureBox1? because I also need some methods to work on that pictureBox like when I click one of the Notes they actually play that sound with that duration (in which I still need to figure out how to pass the Controls From Form1.cs to MusicNote.cs)
Part of the coding for MusicNote.cs which has to do with the "adding" of the image is this below:
public class MusicNote : PictureBox
{
public int pitch;
public int duration;
public string noteShape;
string nShape;
bool isDragging = false;
public string rootFolder = #"..\..\\bin\\Debug\\images\\";
ArrayList al = new ArrayList();
SoundPlayer sp = new SoundPlayer();
Timer t1 = new Timer();
public MusicNote(int x, int mPitch, int mDuration,string nShape)
: base()
{
pitch = mPitch;
duration = mDuration;
noteShape = nShape;
Location = new Point(x, 100);
this.Size = new Size(35, 40);
//--------- Get the Image of Note ----------
Bitmap bmp = new Bitmap(#rootFolder + nShape + ".bmp");
this.Image = bmp;
this.BackColor = Color.Transparent;
//-------Mouse Events-------
this.MouseDown += new MouseEventHandler(StartDrag);
this.MouseUp += new MouseEventHandler(StopDrag);
this.MouseMove += new MouseEventHandler(NoteDrag);
}
etc
...
...
...
}
Form1.cs Coding (posting all as all are connected from one method to another)
The Problem lies in private void onMouseUp (object sender, MouseEventArgs e) which is the last method
using System;
using System.Threading;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Media;
using System.Collections;
namespace Piano
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Objects of Music Note, Piano Keys, and all variables surronding the Keyboard and Notes
WhiteKey wk;
BlackKey bk;
public int pitch;
public int duration = 0;
string nShape = "";
public const int xOff = 35;
int count = 0;
int nPos = 0;
public string rootFolder = #"..\..\\bin\\Debug\\images\\";
MusicNote mn;
MusicStaff ms;
SoundPlayer sp = new SoundPlayer();
Stopwatch sw = new Stopwatch();
//--------------- White and Black Keys Creation both Buttons and Position------------------
public int[] wKeys = { 1, 3, 5, 6, 8, 10, 12, 13, 15, 17, 18, 20, 22, 24, 25 }; //White Keys notes numbers (pitch)
public int[] bKeys = { 0, 2, 0, 4, 0, 0, 7, 0, 9, 0, 11, 0, 0, 14, 0, 16, 0, 0, 19, 0, 21, 0, 23, 0, 0 }; //Black Keys notes numbers (pitch)
int[] wPos = { 35, 70, 105, 140, 175, 210, 245, 280, 315, 350, 385, 420, 455, 490, 525 }; // Position of White Keys on the Panel
int[] bPos = { 0, 57, 0, 92, 0, 0, 162, 0, 197, 0, 232, 0, 0, 302, 0, 337, 0, 0, 407, 0, 442, 0, 477, 0, 1 }; //Position of the Black Keys in the Panel
private void Form1_Load(object sender, System.EventArgs e)
{
for (int i = 0; i < 15; i++)
{
WhiteKey wk = new WhiteKey(wKeys[i], wPos[i]-35,0); //create a new white Key with [i] Pitch, at that x position and at y =0 position
wk.MouseDown += onMouseDown; //Plays the Key and starts Timer
wk.MouseUp += onMouseUp; // Holds the data like Time and shape and so
this.panel1.Controls.Add(wk); //Give it control (to play and edit)
}
for (int i = 0; i < 25; i++) //same for the Black Keys but instead we use 25 keys and those denoted at 0 are where the WHITE KEYS should be placed
{
if (bKeys[i] != 0)
{
//Same as above but for Black Key which is inherits from WhiteKey
bk = new BlackKey(bKeys[i], bPos[i]-35, 0);
bk.MouseDown += onMouseDown;
bk.MouseUp += onMouseUp;
this.panel1.Controls.Add(bk);
this.panel1.Controls[this.panel1.Controls.Count - 1].BringToFront(); //Make the Black Keys show OVER the white
}
}
}
//Method showing what happens when you do a MouseDown Event
private void onMouseDown(object sender, MouseEventArgs e)
{
wk = sender as WhiteKey; //gets the WhiteKey Controls
pitch = wk.pitch; //assign pitch
sp.SoundLocation = #"..\\..\\bin\\Debug\\sound\\mapped\\" + pitch + ".wav"; //find that pressed note
sp.Play(); //play it
sw.Reset(); //Reset Stop Watch
sw.Start(); //Start Time
}
private void timeTick(object sender, EventArgs e)
{
duration++;
}
private void onMouseUp (object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
sw.Stop(); //Stop time
duration = (int)sw.ElapsedMilliseconds / 100;
textBox.Text += (string)(pitch + " , "); //add the Pitch used to the textbox
// Will determine the Music Note Image
if (duration >= 0 && duration < 2)
{
nShape = "Quaver";
}
else if (duration >= 2 && duration < 5)
{
nShape = "Crotchet";
}
else if (duration >= 5 && duration < 8)
{
nShape = "minim";
}
else if (duration >= 8 && duration < 10)
{
nShape = "DotMin";
}
else if (duration >= 10)
{
nShape = "SemiBreve";
}
count++; //will help Determine the 'x' coordiante of the Music Note
nPos = xOff * count; //moves the x-coordinate by 35 pixels each note
mn = new MusicNote(nPos,pitch,duration,nShape); //Creation of a new MusicNote
pictureBox1.Controls.Add(this.mn); //PROBLEM --- Doesn't add to the PictureBox (Does Nothing)
pictureBox1.Controls[pictureBox1.Controls.Count - 1].BringToFront(); //Brought to front of stave to make sure it doesn't get hidden in background
}
}
}
}
Any Idea how I can add CONTROL to the PictureBox1 and make the Music Note Show? Because I managed to make it Show on the Form1 and the Panel1 but failed on a pictureBox
How the Piano Looks with music notes on FORM1
According to the Visual Studio DEBUGGER [Piano.Form1]
http://postimg.org/image/vdu0x1gv1/
N.B Sorry for the long post but I don't know exactly how to explain the problem.
I think the Program was just messing with me.. I tried changing something to png and back to bmp and Now it's working perfectly.. I do not know why but It's the same Code there is up there
Thanks to those who have seen the question.
To play sound in picture box do this
[DllImport("winmm.dll")]
private static extern bool PlaySound(string lpszName, int hModule, int dwFlags);
and PlaySound("your sound loc ", 1, 0x0011);

Categories

Resources