SharpGL: Can't draw all lines from List - c#

I have:
float[,] nodesN = null; //indexes:
//number of node;
//value index 0->x, 1->y, 2->temperature
int[,] elements = null; //indexes:
//indexof element (triangle)
//1, 2, 3 - vertexes (from nodesN)
List<Pair> edges = null; //Pair is a class containing two int values which are
//indexes of nodesN
And function which is supposed do all elements and edges on SharpGL.OpenGLCtrl
private void openGLCtrl1_Load(object sender, EventArgs e)
{
gl = this.glCtrl.OpenGL;
gl.ClearColor(this.BackColor.R / 255.0f,
this.BackColor.G / 255.0f,
this.BackColor.B / 255.0f, 1.0f);
gl.Clear(OpenGL.COLOR_BUFFER_BIT | OpenGL.DEPTH_BUFFER_BIT);
}
private void openGLControl1_OpenGLDraw(object sender, PaintEventArgs e)
{
gl.Clear(OpenGL.COLOR_BUFFER_BIT | OpenGL.DEPTH_BUFFER_BIT);
gl.LoadIdentity();
gl.Translate(0.0f, 0.0f, -6.0f);
if (!draw) return;
bool drawElements = false;
if (drawElements)
{
gl.Begin(OpenGL.TRIANGLES);
for (int i = 0; i < elementNo; i++)
{
for (int j = 0; j < 3; j++)
{
float x, y, t;
x = nodesN[elements[i, j], 0];
y = nodesN[elements[i, j], 1];
t = nodesN[elements[i, j], 2];
gl.Color(t, 0.0f, 1.0f - t);
gl.Vertex(x, y, 0.0f);
}
}
gl.End();
}
gl.Color(0f, 0f, 0f);
gl.Begin(OpenGL.LINES);
//for(int i=edges.Count-1; i>=0; i--)
for(int i=0; i<edges.Count; i++)
{
float x1, y1, x2, y2;
x1 = nodesN[edges[i].First, 0];
y1 = nodesN[edges[i].First, 1];
x2 = nodesN[edges[i].Second, 0];
y2 = nodesN[edges[i].Second, 1];
gl.Vertex(x1, y1, 0.0f);
gl.Vertex(x2, y2, 0.0f);
}
gl.End();
}
But it doesn't draw all the edges. If i change drawElements it draws different number of edges. Changing for(int i=0; i<edges.Count; i++) to for(int i=edges.Count-1; i>=0; i--) shows that esges are generated correctly, but they are not drawn.
Images:
for(int i=0; i<edges.Count; i++)
drawElements=false
http://img225.imageshack.us/img225/9295/noup.jpg
for(int i=edges.Count-1; i>=0; i--)
drawElements=false
http://img828.imageshack.us/img828/9595/nodown.jpg
for(int i=0; i<edges.Count; i++)
drawElements=true
http://img64.imageshack.us/img64/4929/withup.jpg
for(int i=edges.Count-1; i>=0; i--)
drawElements=true
http://img833.imageshack.us/img833/9167/withdown.jpg
What is wrong with this? How can I draw all edges?
EDIT:
Never mind, I dropped SharpGL and wrote exactly the same code in OpenTK. It works excelent without me wondering what was wrong. This was a good call because SharpGL uses enormously big amount of memory.

Once I had a very similar problem.
It was due to z-buffer. If you have a plane and want to draw it's wireframe than the coordinates overlap and artifacts like those arise. It's numerically undefined behavior - drawing two objects on the same depth. You never know which one comes on top.
One solution is to offset the wireframe a bit. I noticed that in some 3d modelling packages. In game engines it's also common to offset sprites on geometry (gunshot marks on a wall). Another might be disabling the z-buffer and manually occlude hidden lines.
The case when you disable drawing of elements might be to another issue with z-buffer. It's bounded by far and near clipping planes. Most probably you draw the lines exactly at the depth of one of them (my guess is far one).
EDIT. I read Your code a bit. One I'd like to see is how You construct the projection matrix. If you didn't touch it at all than (If I recall correctly) the near and far clipping planes are at -1.0 and 1.0 respectively. But, I might be wrong since You draw at z=-6.0...
The other thing, try replacing:
gl.Vertex(x1, y1, 0.0f);
gl.Vertex(x2, y2, 0.0f);
With something along the lines of:
gl.Vertex(x1, y1, 0.01f);
gl.Vertex(x2, y2, 0.01f);

Related

Raycast not capturing all Vector coordinates

I have a gameobject that occupies the whole screen just for testing purposes. I'm drawing a line btw. What I'm trying to achieve is if the mouse position hits a gameobject it will store the vector2 coordinates in a list. But raycast is not storing all the coordinates. Below is my code
private void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 mousePos = Input.mousePosition;
Vector2 Pos = _camera.ScreenToWorldPoint(mousePos);
if(!mousePositions.Contains(Pos))
mousePositions.Add(Pos);
if (Physics.Raycast(Camera.main.ScreenPointToRay(mousePos), out RaycastHit hit))
{
Vector2 textureCoord = hit.textureCoord;
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
Vector2Int paintPixelPosition = new Vector2Int(pixelX, pixelY);
if (!linePositions.Contains(paintPixelPosition))
linePositions.Add(paintPixelPosition);
foreach (Vector2Int pos in linePositions)
{
int pixelXOffset = pos.x - (_brush.width / 2);
int pixelYOffset = pos.y - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
_templateDirtMask.SetPixel(
pixelXOffset + x,
pixelYOffset + y,
Color.black
);
}
}
}
_templateDirtMask.Apply();
}
}
}
Everytime I checked the element count mousePositions are always greater than linePositions. I don't know what's causing this
the element count mousePositions are always greater than linePosition
well it is quite simple: In
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
you are casting to int and cut off any decimals after the comma (basically like doing Mathf.FloorToInt).
So you can totally have multiple mouse positions which result in float pixel positions like e.g.
1.2, 1.2
1.4, 1.7
1.02, 1.93
...
all these will map to
Vector2Int paintPixelPosition = new Vector2Int(1, 1);
Besides, you might want to look at some better line drawing algorithms like e.g. this simple one
And then note that calling SetPixel repeatedly is quite expensive. You want to do a single SetPixels call like e.g.
var pixels = _templateDirtMask.GetPixels();
foreach (Vector2Int pos in linePositions)
{
int pixelXOffset = pos.x - (_brush.width / 2);
int pixelYOffset = pos.y - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
pixels[(pixelXOffset + x) + (pixelYOffset + y) * _templateDirtMask.width] = Color.black;
}
}
}
_templateDirtMask.SetPixels(pixels);
_templateDirtMask.Apply();
It happens because there is really could be a case, when several elements from mousePositions are associated with one elment from linePositions.
Rough example: your texture resolution is only 1x1px. In this case you linePositons will contain only one element. And this element will be associated with all elements from mosePositions.
So, relation of the number of elements in these lists depends on relation of your texture and screen resolutions.

GDI+ curve "overflowing"

I'm currently using GDI+ to draw a line graph, and using Graphics.DrawCurve to smooth out the line. The problem is that the curve doesn't always match the points I feed it, and that makes the curve grow out of the graph frame in some points, as seen below(red is Graphics.DrawLines, green is Graphics.DrawCurve).
How would I go about solving this?
The simplest solution is to set a tension:
The green curve is drawn with the default tension, the blue one set a tension of 0.1f:
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.DrawLines(Pens.Red, points.ToArray());
e.Graphics.DrawCurve(Pens.Green, points.ToArray());
e.Graphics.DrawCurve(Pens.Blue, points.ToArray(), 0.1f);
}
You will need to test what is the best compromise, 0.2f is still ok, 0.3f is already overdrawing quite a bit..
For a really good solution you will need to use DrawBeziers. This will let you draw curves that can go through the points without any overdrawing and with full control of the radius of the curves; but to to so you will need to 'find', i.e. calculate good control points, which is anything but trivial..:
This result is by no means perfect but already complicated enough.. I have displayed the curve points and their respective control points in the same color. For each point there is an incoming and an outgoing control point. For a smooth curve they need to have the same tangents/gradients in their curve points.
I use a few helper functions to calculate a few things about the segments:
A list of gradients
A list of signs of the gradients
A list of segment lengths
Lists of horizontal and of vertical gaps between points
The main function calculates the array of bezier points, that is the curve points and between each pair the previous left and the next right control points.
In the Paint event it is used like this:
List<PointF> bezz = getBezz(points);
using (Pen pen = new Pen(Color.Black, 2f))
e.Graphics.DrawBeziers(pen, bezz.ToArray());
Here are the functions I used:
List<float> getGradients(List<PointF> p)
{
List<float> grads = new List<float>();
for (int i = 0; i < p.Count - 1; i++)
{
float dx = p[i + 1].X - p[i].X;
float dy = p[i + 1].Y - p[i].Y;
if (dx == 0) grads.Add(dy == 0 ? 0 : dy > 0 ?
float.PositiveInfinity : float.NegativeInfinity);
else grads.Add(dy / dx);
}
return grads;
}
List<float> getLengths(List<PointF> p)
{
List<float> lengs = new List<float>();
for (int i = 0; i < p.Count - 1; i++)
{
float dx = p[i + 1].X - p[i].X;
float dy = p[i + 1].Y - p[i].Y;
lengs.Add((float)Math.Sqrt(dy * dy + dx * dx));
}
return lengs;
}
List<float> getGaps(List<PointF> p, bool horizontal)
{
List<float> gaps = new List<float>();
for (int i = 0; i < p.Count - 1; i++)
{
float dx = p[i + 1].X - p[i].X;
float dy = p[i + 1].Y - p[i].Y;
gaps.Add(horizontal ? dx : dy);
}
return gaps;
}
List<int> getSigns(List<float> g)
{
return g.Select(x => x > 0 ? 1 : x == 0 ? 0 : -1).ToList();
}
And finally the main function; here I make a distinction: Extreme points ( minima & maxima) should have their control points on the same height as the points themselves. This will prevent vertical overflowing. They are easy to find: The signs of their gradients will always altenate.
Other points need to have the same gradient for incoming and outcoming control points. I use the average between the segments' gradients. (Maybe a weighed average would be better..) And I weigh their distance according to the segment lengths..
List<PointF> getBezz(List<PointF> points)
{
List<PointF> bezz = new List<PointF>();
int pMax = points.Count;
List<float> hGaps = getGaps(points, true);
List<float> vGaps = getGaps(points, false);
List<float> grads = getGradients(points);
List<float> lengs = getLengths(points);
List<int> signs = getSigns(grads);
PointF[] bezzA = new PointF[pMax * 3 - 2];
// curve points
for (int i = 0; i < pMax; i++) bezzA[i * 3] = points[i];
// left control points
for (int i = 1; i < pMax; i++)
{
float x = points[i].X - hGaps[i - 1] / 2f;
float y = points[i].Y;
if (i < pMax - 1 && signs[i - 1] == signs[i])
{
float m = (grads[i-1] + grads[i]) / 2f;
y = points[i].Y - hGaps[i-1] / 2f * m * vGaps[i-1] / lengs[i-1];
}
bezzA[i * 3 - 1] = new PointF(x, y);
}
// right control points
for (int i = 0; i < pMax - 1; i++)
{
float x = points[i].X + hGaps[i] / 2f;
float y = points[i].Y;
if (i > 0 && signs[i-1] == signs[i])
{
float m = (grads[i-1] + grads[i]) / 2f;
y = points[i].Y + hGaps[i] / 2f * m * vGaps[i] / lengs[i];
}
bezzA[i * 3 + 1] = new PointF(x, y);
}
return bezzA.ToList();
}
Note that I didn't code for the case of points with the same x-coordinate. So this is ok for 'functional graphs' but not for, say figures, like e.g. stars..
Maybe you just want to look at the "overshooting the bounds" problem as not a problem with the overshoot, but with the bounds. In which case, you can determine the actual bounds of a curve using the System.Drawing.Drawing2D.GraphicsPath object:
GraphicsPath gp = new GraphicsPath();
gp.AddCurve(listOfPoints);
RectangleF bounds = gp.GetBounds();
You can draw that GraphicsPath directly:
graphics.DrawPath(Pens.Black, gp);
As far as solving the bounds problem, the line necessarily overshoots the vertex on some axis. It's easier to see this fact when the lines are aligned to the bounds.
Given these points:
In order for them to be curved, they must exceed their bounds in some way:
If you never want to exceed their vertical bounds, you could simply ensure that the bezier handles have the same Y value as the vertex, but they will overshoot on the X:
Or vice-versa:
You could deliberately undershoot just enough to avoid the way curves can overshoot. This can be done by swapping the bezier handles, which would maybe be at the line-centers, with the vertices:

Draw line from text input in SharpGL

I want to draw line in SharpGl but this code does not work !
void Line_DDA(OpenGL gl,int X0, int Y0, int Xend, int Yend)
{
gl.LineWidth(2.5f);
gl.Color(1.0, 0.0, 0.0);
gl.Begin(OpenGL.GL_LINES);
int dx = Xend - X0;
int dy = Yend - Y0;
int steps, k;
float Xinc, Yinc;
float x = X0;
float y = Y0;
if (Math.Abs(dx) > Math.Abs(dy))
steps = Math.Abs(dx);
else
steps = Math.Abs(dy);
float fdx = (float)dx;
float fdy = (float)dy;
float fsteps = (float)steps;
Xinc = fdx / fsteps;
Yinc = fdy / fsteps;
gl.Vertex((int)x, (int)y);
for (k = 0; k < steps; k++)
{
x += Xinc;
y += Yinc;
gl.Vertex((int)x, (int)y);
}
gl.End();
}
and when I use
gl.Vertex(10, 100);
gl.Vertex(110, 110);
It's work!
EDIT:
This is call block in my code:
private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e)
{
// Get the OpenGL object.
OpenGL gl = openGLControl.OpenGL;
// Clear the color and depth buffer.
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
// Load the identity matrix.
gl.LoadIdentity();
Line_DDA(gl, int.Parse(txtLineX1.Text), int.Parse(txtLineY1.Text), int.Parse(txtLineX2.Text), int.Parse(txtLineY2.Text));
//drawLine(gl, 110, 120, 100, 100);
}
Why this happen?
When you draw lines using GL_LINES, you need to supply 2 vertices for each line segment. Instead, your code supply a single vertex for each line segment, as if the line should be connected with the previous vertex.
Indeed, the solution would be to set the draw mode to GL_LINE_STRIP or to supply two vertices for each line segment.
However, I don't understand why you want to draw a straight line with multiple vertices when when they are needed only two points.

Trouble drawing 3d-style effect

I've been racking my brain trying to figure out how to animate an effect. This is related to a question I asked on math.stackexchange.com.
https://math.stackexchange.com/questions/91120/equal-division-of-rectangles-to-make-total/
As a side note, I didn't implement the drawing algorithm that was defined on the question above -- instead using my own in order to change the perspective to make it look more condensed.
I've been able to draw a stationary 3d style effect, but I am having trouble wrapping my brain around the logic to make the lines below look like they are coming towards you.
My code is as follows,
List<double> sizes = new List<double>();
private void Form1_Load(object sender, EventArgs e)
{
for (int y = 1; y < 10; y++)
{
double s = ((240 / 2) / y) / 4;
sizes.Add(s);
}
sizes.Add(0);
}
int offset = 0;
private void button1_Click(object sender, EventArgs e)
{
Bitmap b = new Bitmap(320, 480);
Graphics g = Graphics.FromImage(b);
Color firstColor = Color.DarkGray;
Color secondColor = Color.Gray;
Color c = firstColor;
int yOffset = 0;
for(int i = 0; i < sizes.Count; i++)
{
c = (i % 2 == 0) ? firstColor : secondColor;
int y = (int)Math.Round(b.Height - yOffset - sizes[i]);
int height = (int)Math.Round(sizes[i]);
g.FillRectangle(new SolidBrush(c), new Rectangle(0, y + offset, b.Width, height + offset));
yOffset += (int)sizes[i];
}
this.BackgroundImage = b;
offset+=1;
}
Each button click should cause the rectangles to resize and move closer. However, my rectangles aren't growing as they should. My logic draws fine, but simply doesn't work as far as moving goes.
So my question is:
Is there an existing algorithm for this effect that I am not aware of, or is this something pretty simple that I'm over thinking? Any help in correcting my logic or pointing me in the right direction would be very appreciated.
Interesting...
(video of the answer here: http://youtu.be/estq62yz7v0)
I would do it like that:
First, drop all RECTANGLE drawing and draw your effect line by line. Like so:
for (int y=start;y<end;y++)
{
color = DetermineColorFor(y-start);
DrawLine(left, y, right, y, color);
}
This is of course pseudo-code not to be troubled with GDI+ or something.
Everything is clear here, except on how to code DetermineColorFor() method. That method will have to return color of the line at specified PROJECTED height.
Now, on the picture, you have:
you point of view (X) - didn't know how to draw an eye
red line (that's your screen - projection plane)
your background (alternating stripes at the bottom)
and few projecting lines that should help you devise the DetermineColorFor() method
Hint - use triangle similarity to go from screen coordinates to 'bar' coordinates.
Next hint - when you are in 'bar' coordinates, use modulo operator to determine color.
I'll add more hints if needed, but it would be great if you solved this on your own.
I was somehow inspired by the question, and have created a code for the solution. Here it is:
int _offset = 0;
double period = 20.0;
private void timer1_Tick(object sender, EventArgs e)
{
for (int y = Height / 3; y < Height; y++)
{
using (Graphics g = CreateGraphics())
{
Pen p = new Pen(GetColorFor(y - Height / 3));
g.DrawLine(p, 0, y, Width, y);
p.Dispose();
}
}
_offset++;
}
private Color GetColorFor(int y)
{
double d = 10.0;
double h = 20.0;
double z = 0.0;
if (y != 0)
{
z = d * h / (double)y + _offset;
}
double l = 128 + 127 * Math.Sin(z * 2.0 * Math.PI / period);
return Color.FromArgb((int)l, (int)l, (int)l);
}
Experiment with:
d - distance from the eye to the projection screen
h - height of the eye from the 'bar'
period - stripe width on the 'bar'
I had a timer on the form and event properly hooked. Timer duration was 20ms.
Considering that you're talking here about 2D rendering, as much as I understodd, to me it seems that you're gonna to reenvent the wheel. Cause what you need, IMHO; is use Matrix Transformations already available in GDI+ for 2D rendering.
Example of aplying it in GDI+ : GDI+ and MatrixTranformations
For this they use System.Drawing.Drawing2D.Matrix class, which is inside Graphics.
The best ever 2D rendering framework I ever used is Piccolo2D framework which I used with great success in big real production project. Definitely use this for your 2D rendering projects, but first you need to study it little bit.
Hope this helps.

Draw simple circle in XNA

I want to draw a 2d, filled, circle. I've looked everywhere and cannot seem to find anything that will even remotely help me draw a circle. I simply want to specify a height and width and location on my canvas.
Anyone know how?
Thanks!
XNA doesn't normally have an idea of a canvas you can paint on. Instead you can either create a circle in your favorite paint program and render it as a sprite or create a series vertexes in a 3D mesh to approximate a circle and render that.
You could also check out the sample framework that Jeff Weber uses in Farseer:
http://www.codeplex.com/FarseerPhysics
The demos have a dynamic texture generator that let's him make circles and rectangles (which the samples then use as the visualization of the physics simulation). You could just re-use that :-)
Had the same problem, as others already suggested you need to draw a square or rectangle with a circle texture on it. Here follows my method to create a circle texture runtime. Not the most efficient or fancy way to do it, but it works.
Texture2D createCircleText(int radius)
{
Texture2D texture = new Texture2D(GraphicsDevice, radius, radius);
Color[] colorData = new Color[radius*radius];
float diam = radius / 2f;
float diamsq = diam * diam;
for (int x = 0; x < radius; x++)
{
for (int y = 0; y < radius; y++)
{
int index = x * radius + y;
Vector2 pos = new Vector2(x - diam, y - diam);
if (pos.LengthSquared() <= diamsq)
{
colorData[index] = Color.White;
}
else
{
colorData[index] = Color.Transparent;
}
}
}
texture.SetData(colorData);
return texture;
}
Out of the box, there's no support for this in XNA. I'm assuming you're coming from some GDI background and just want to see something moving around onscreen. In a real game though, this is seldom if ever needed.
There's some helpful info here:
http://forums.xna.com/forums/t/7414.aspx
My advice to you would be to just fire up paint or something, and create the basic shapes yourself and use the Content Pipeline.
Another option (if you want to use a more complex gradient brush or something) is to draw a quad aligned to the screen and use a pixel shader.
What I did to solve this was to paint a rectangular texture, leaving the area of the rectangle which doesn't contain the circle transparent. You check to see if a point in the array is contained within a circle originating from the center of the rectangle.
Using the color data array is a bit weird because its not a 2D array. My solution was to bring in some 2D array logic into the scenario.
public Texture2D GetColoredCircle(float radius, Color desiredColor)
{
radius = radius / 2;
int width = (int)radius * 2;
int height = width;
Vector2 center = new Vector2(radius, radius);
Circle circle = new Circle(center, radius,false);
Color[] dataColors = new Color[width * height];
int row = -1; //increased on first iteration to zero!
int column = 0;
for (int i = 0; i < dataColors.Length; i++)
{
column++;
if(i % width == 0) //if we reach the right side of the rectangle go to the next row as if we were using a 2D array.
{
row++;
column = 0;
}
Vector2 point = new Vector2(row, column); //basically the next pixel.
if(circle.ContainsPoint(point))
{
dataColors[i] = desiredColor; //point lies within the radius. Paint it.
}
else
{
dataColors[i] = Color.Transparent; //point lies outside, leave it transparent.
}
}
Texture2D texture = new Texture2D(GraphicsDevice, width, height);
texture.SetData(0, new Rectangle(0, 0, width, height), dataColors, 0, width * height);
return texture;
}
And here's the method to check whether or not a point is contained within your circle:
public bool ContainsPoint(Vector2 point)
{
return ((point - this.Center).Length() <= this.Radius);
}
Hope this helps!
public Texture2D createCircleText(int radius, GraphicsDevice Devise,Color color,int tickenes)
{
Texture2D texture = new Texture2D(Devise, radius, radius);
Color[] colorData = new Color[radius * radius];
if (tickenes >= radius) tickenes = radius - 5;
float diam = radius / 2f;
float diamsq = diam * diam;
float intdiam = (radius-tickenes) / 2f;
float intdiamsq = intdiam * intdiam;
for (int x = 0; x < radius; x++)
{
for (int y = 0; y < radius; y++)
{
int index = x * radius + y;
Vector2 pos = new Vector2(x - diam, y - diam);
if (pos.LengthSquared() <= diamsq)
{
colorData[index] = color;
}
else
{
colorData[index] = Color.Transparent;
}
if (pos.LengthSquared() <= intdiamsq)
{
colorData[index] = Color.Transparent;
}
}
}
texture.SetData(colorData);
return texture;
}

Categories