I'm trying to draw a simple grid on a canvas. First I did this
function start()
{
var x = 0;
var y = 0;
for (x = 0; x < 500; x += 50)
{
line(0 + x, 50 + y, 50 + x, 50 + y, 1, "#111");
line(50 + x, 0 + y, 50 + x, 50 + y, 1, "#111");
if (x == 450)
{
x = -50;
y += 50;
}
if (y == 500)
{
x = 500;
}
}
}
It works fine. But I want to be able to easily change the size of the grid and canvas. So I did this:
function start()
{
var x = 0;
var y = 0;
var cW = canvas.width;
var cH = canvas.hight;
var gS = 50; //gS = gridSpace
for (x = 0; x < cW; x += gS)
{
line(0 + x, gS + y, gS + x, gS + y, 1, "#111");
line(gS + x, 0 + y, gS + x, gS + y, 1, "#111");
if (x == cW - gS)
{
x = -gS;
y += gS;
}
if (y == cH)
{
x = cW;
}
}
}
It does not work! Please help me.
PS. I'm using a library.
`
You might want to approach this a little differently. I'm not entirely sure of what you're trying to accomplish here, but here are some pointers/questions that may guide you toward what you're trying to do:
When drawing a grid, you are drawing a series of horizontal lines and a series of vertical lines. Use two loops to simplify that process.
For the horizontal lines, the y-value varies, but the x-coords for the line endpoints stay the same (e.g. 0 and cW). The converse applies to the vertical lines.
What kind of spacing are you really trying to achieve? Typically you're looking at either dividing the space into a certain number of areas (say 6 rows and 4 columns), or spacing that doesn't adapt to the specific canvas you're drawing on (this is what you're code seems to be trying to do). So, the first will adapt to the size of the canvas while the latter will just display more/fewer rows/columns as the canvas size varies.
I hope that helps you solve your problem, please let me know if you need any more help!
One possibility, is that you have height spelled incorrectly. I believe javascript, if this is javascript, won't complain about incorrectly named variables. (I could be wrong on that).
var cH = canvas.hight;
should be
var cH = canvas.height;
Related
Background:
Heya! I'm trying to generate a circuit board which has a subset of San Francisco printed on it. Most of the pieces of this are done, and I'm generating images that look like this:
The problem is that I am rendering lines which extend outside my hardcoded cutoff boundary (I am rendering lines which one side is in and one side is out of bounds).
Question:
Given a set of lines like this:
# x1,y1, x2,y2
10,10,40,40
80,80,120,120
How can I modify the co-ordinates of each line such that it 'cuts off' at a specific bound?
In the case above, the second line (which in original form) extends to (120,120), should only extend to (100,100) assuming bounds of 100,100.
Thoughts
Based on what I remember from high-school math, I should plug something into the formula y=mx+b yeah? Even then, how would I deal with an infinite gradient or the like?
Thanks for any and all help :D Puesdocode/python/Go preferred, but explanations just as graciously recieved.
<3
Tom
Your best friend is the Cohen–Sutherland line clipping algorithm.
https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
Sat down and worked it out. My rudimentary approach was to:
Compute the slope of the line & the y-intercept
Check both points on all four sides to see if they exceed bounds, and if they do, recompute the necessary co-ordinate by plugging the bound into the formula y=mx+b.
Here is my Go code:
func boundLine(line *kcgen.Line) {
if line.Start.X == line.End.X {
panic("infinite slope not yet supported")
}
slope := (line.End.Y - line.Start.Y) / (line.End.X - line.Start.X)
b := line.End.Y - (slope * line.End.X) //y = mx + b which is equivalent to b = y - mx
if line.Start.X < (-*width/2) {
line.Start.Y = (slope * (-*width/2)) + b
line.Start.X = -*width/2
}
if line.End.X < (-*width/2) {
line.End.Y = (slope * (-*width/2)) + b
line.End.X = -*width/2
}
if line.Start.X > (*width/2) {
line.Start.Y = (slope * (*width/2)) + b
line.Start.X = *width/2
}
if line.End.X > (*width/2) {
line.End.Y = (slope * (*width/2)) + b
line.End.X = *width/2
}
if line.Start.Y < (-*height/2) {
line.Start.Y = -*height/2
line.Start.X = ((-*height/2) - b) / slope //y = mx + b equiv. (y-b)/m = x
}
if line.End.Y < (-*height/2) {
line.End.Y = -*height/2
line.End.X = ((-*height/2) - b) / slope //y = mx + b equiv. (y-b)/m = x
}
if line.Start.Y > (*height/2) {
line.Start.Y = *height/2
line.Start.X = ((*height/2) - b) / slope //y = mx + b equiv. (y-b)/m = x
}
if line.End.Y > (*height/2) {
line.End.Y = *height/2
line.End.X = ((*height/2) - b) / slope //y = mx + b equiv. (y-b)/m = x
}
}
I am trying to figure out where a bunch of line-segments clip into a window around them. I saw the Liang–Barsky algorithm, but that seems to assume the segments already clip the edges of the window, which these do not.
Say I have a window from (0,0) to (26,16), and the following segments:
(7,6) - (16,3)
(10,6) - (19,6)
(13,10) - (21,3)
(16,12) - (19,14)
Illustration:
I imagine I need to extend the segments to a certain X or Y point, till they hit the edge of the window, but I don't know how.
How would I find the points where these segments (converted to lines?) clip into the edge of the window? I will be implementing this in C#, but this is pretty language-agnostic.
If you have two line segments P and Q with points
P0 - P1
Q0 - Q1
The line equations are
P = P0 + t(P1 - P0)
Q = Q0 + r(Q1 - Q0)
then to find out where they intersect after extension you need to solve the following equation for t and r
P0 + t(P1 - P0) = Q0 + r(Q1 - Q0)
The following code can do this. ( Extracted from my own code base )
public static (double t, double r )? SolveIntersect(this Segment2D P, Segment2D Q)
{
// a-d are the entries of a 2x2 matrix
var a = P.P1.X - P.P0.X;
var b = -Q.P1.X + Q.P0.X;
var c = P.P1.Y - P.P0.Y;
var d = -Q.P1.Y + Q.P0.Y;
var det = a*d - b*c;
if (Math.Abs( det ) < Utility.ZERO_TOLERANCE)
return null;
var x = Q.P0.X - P.P0.X;
var y = Q.P0.Y - P.P0.Y;
var t = 1/det*(d*x - b*y);
var r = 1/det*(-c*x + a*y);
return (t, r);
}
If null is returned from the function then it means the lines are parallel and cannot intersect. If a result is returned then you can do.
var result = SolveIntersect( P, Q );
if (result != null)
{
var ( t, r) = result.Value;
var p = P.P0 + t * (P.P1 - P.P0);
var q = Q.P0 + t * (Q.P1 - Q.P0);
// p and q are the same point of course
}
The extended line segments will generally intersect more than one box edge but only one of those intersections will be inside the box. You can check this easily.
bool IsInBox(Point corner0, Point corner1, Point test) =>
(test.X > corner0.X && test.X < corner1.X && test.Y > corner0.Y && test.Y < corner1.Y ;
That should give you all you need to extend you lines to the edge of your box.
I managed to figure this out.
I can extend my lines to the edge of the box by first finding the equations of my lines, then solving for the X and Y of each of the sides to get their corresponding point. This requires passing the max and min Y and the max and min X into the following functions, returning 4 values. If the point is outside the bounds of the box, it can be ignored.
My code is in C#, and is making extension methods for EMGU's LineSegment2D. This is a .NET wrapper for OpenCv.
My Code:
public static float GetYIntersection(this LineSegment2D line, float x)
{
Point p1 = line.P1;
Point p2 = line.P2;
float dx = p2.X - p1.X;
if(dx == 0)
{
return float.NaN;
}
float m = (p2.Y - p1.Y) / dx; //Slope
float b = p1.Y - (m * p1.X); //Y-Intercept
return m * x + b;
}
public static float GetXIntersection(this LineSegment2D line, float y)
{
Point p1 = line.P1;
Point p2 = line.P2;
float dx = p2.X - p1.X;
if (dx == 0)
{
return float.NaN;
}
float m = (p2.Y - p1.Y) / dx; //Slope
float b = p1.Y - (m * p1.X); //Y-Intercept
return (y - b) / m;
}
I can then take these points, check if they are in the bounds of the box, discard the ones that are not, remove duplicate points (line goes directly into corner). This will leave me with one x and one y value, which I can then pair to the corresponding min or max Y or X values I passed into the functions to make 2 points. I can then make my new segment with the two points.
Wiki description of Liang-Barsky algorithm is not bad, but code is flaw.
Note: this algorithm intended to throw out lines without intersection as soon as possible. If most of lines intersect the rectangle, then approach from your answer might be rather effective, otherwise L-B algorithm wins.
This page describes approach in details and contains concise effective code:
// Liang-Barsky function by Daniel White # http://www.skytopia.com/project/articles/compsci/clipping.html
// This function inputs 8 numbers, and outputs 4 new numbers (plus a boolean value to say whether the clipped line is drawn at all).
//
bool LiangBarsky (double edgeLeft, double edgeRight, double edgeBottom, double edgeTop, // Define the x/y clipping values for the border.
double x0src, double y0src, double x1src, double y1src, // Define the start and end points of the line.
double &x0clip, double &y0clip, double &x1clip, double &y1clip) // The output values, so declare these outside.
{
double t0 = 0.0; double t1 = 1.0;
double xdelta = x1src-x0src;
double ydelta = y1src-y0src;
double p,q,r;
for(int edge=0; edge<4; edge++) { // Traverse through left, right, bottom, top edges.
if (edge==0) { p = -xdelta; q = -(edgeLeft-x0src); }
if (edge==1) { p = xdelta; q = (edgeRight-x0src); }
if (edge==2) { p = -ydelta; q = -(edgeBottom-y0src);}
if (edge==3) { p = ydelta; q = (edgeTop-y0src); }
if(p==0 && q<0) return false; // Don't draw line at all. (parallel line outside)
r = q/p;
if(p<0) {
if(r>t1) return false; // Don't draw line at all.
else if(r>t0) t0=r; // Line is clipped!
} else if(p>0) {
if(r<t0) return false; // Don't draw line at all.
else if(r<t1) t1=r; // Line is clipped!
}
}
x0clip = x0src + t0*xdelta;
y0clip = y0src + t0*ydelta;
x1clip = x0src + t1*xdelta;
y1clip = y0src + t1*ydelta;
return true; // (clipped) line is drawn
}
I'm trying to blur QImage alpha channel. My current implementation use deprecated 'alphaChannel' method and works slow.
QImage blurImage(const QImage & image, double radius)
{
QImage newImage = image.convertToFormat(QImage::Format_ARGB32);
QImage alpha = newImage.alphaChannel();
QImage blurredAlpha = alpha;
for (int x = 0; x < alpha.width(); x++)
{
for (int y = 0; y < alpha.height(); y++)
{
uint color = calculateAverageAlpha(x, y, alpha, radius);
blurredAlpha.setPixel(x, y, color);
}
}
newImage.setAlphaChannel(blurredAlpha);
return newImage;
}
I was also trying to implement it using QGraphicsBlurEffect, but it doesn't affect alpha.
What is proper way to blur QImage alpha channel?
I have faced a similar question about pixel read\write access :
Invert your loops. An image is laid out in memory as a succession of rows. So you should access first by height then by width
Use QImage::scanline to access data, rather than expensives QImage::pixel and QImage::setPixel. Pixels in a scan (aka row) are guaranteed to be consecutive.
Your code will look like :
for (int ii = 0; ii < image.height(); ii++) {
uchar* scan = image.scanLine(ii);
int depth =4;
for (int jj = 0; jj < image.width(); jj++) {
//it is in fact an rgba
QRgb* rgbpixel = reinterpret_cast<QRgb*>(scan + jj*depth);
QColor color(*rgbpixel);
int alpha = calculateAverageAlpha(ii, jj, color, image);
color.setAlpha(alpha);
//write
*rgbpixel = color.rgba();
}
}
You can go further and optimize the computation of the alpha average. Lets look at the sum of pixel in a radius. The sum of alpha value at (x,y) in the radius is s(x,y). When you move one pixel in either direction, a single line is added while a single line is removed. lets say you move horizontally. if l(x,y) is the sum of the vertical line of length 2*radius centered around (x,y), you have
s(x + 1, y) = s(x, y) + l(x + r + 1, y) - l(x - r, y)
Which allow you to efficiently compute a matrix of sum (then average, by dividing with the number of pixel) in a first pass.
I suspect this kind of optimization is already implemented in a much better way in libraries such as opencv. So I would encourage you to use existing opencv functions if you wish to save time.
How would you go about changing the steepness as for loops progress. Essentially I've made a terrain with vertices which form a valley. The creation of the data for these vertices to use is here:
// Divides it to a sensible height
const int DIVISOR_NUMBER = 40;
for (int x = 0; x < TerrainWidth; x++)
{
float height = Math.Abs(((float)x - ((float)TerrainWidth / 2))/ (float)DIVISOR_NUMBER);
for (int y = 0; y < TerrainHeight; y++)
{
float copyOfHeight = height;
float randomValue = random.Next(0, 3);
copyOfHeight += randomValue / 10;
HeightData[x, y] = copyOfHeight;
}
}
This works fine. But I now want to make the sides of the valley steeper at the start and end of the first loop and the valley flatten the closer to the center it gets. I'm having a bit of a mental block and can't think of a good way of doing it. Any help would be appreciated.
You can use a squared (aka quadratic) curve for that. Try:
float offset = (float)x - (float)TerrainWidth/2;
float height = offset*offset*SCALE_FACTOR;
If you still want a "crease" at the bottom of the valley, you can make your height a weighted sum:
float height = Math.Abs(offset) * ABS_FACTOR + offset*offset * QUADRATIC_FACTOR;
I am trying to display a mathematical surface f(x,y) defined on a XY regular mesh using OpenGL and C++ in an effective manner:
struct XYRegularSurface {
double x0, y0;
double dx, dy;
int nx, ny;
XYRegularSurface(int nx_, int ny_) : nx(nx_), ny(ny_) {
z = new float[nx*ny];
}
~XYRegularSurface() {
delete [] z;
}
float& operator()(int ix, int iy) {
return z[ix*ny + iy];
}
float x(int ix, int iy) {
return x0 + ix*dx;
}
float y(int ix, int iy) {
return y0 + iy*dy;
}
float zmin();
float zmax();
float* z;
};
Here is my OpenGL paint code so far:
void color(QColor & col) {
float r = col.red()/255.0f;
float g = col.green()/255.0f;
float b = col.blue()/255.0f;
glColor3f(r,g,b);
}
void paintGL_XYRegularSurface(XYRegularSurface &surface, float zmin, float zmax) {
float x, y, z;
QColor col;
glBegin(GL_QUADS);
for(int ix = 0; ix < surface.nx - 1; ix++) {
for(int iy = 0; iy < surface.ny - 1; iy++) {
x = surface.x(ix,iy);
y = surface.y(ix,iy);
z = surface(ix,iy);
col = rainbow(zmin, zmax, z);color(col);
glVertex3f(x, y, z);
x = surface.x(ix + 1, iy);
y = surface.y(ix + 1, iy);
z = surface(ix + 1,iy);
col = rainbow(zmin, zmax, z);color(col);
glVertex3f(x, y, z);
x = surface.x(ix + 1, iy + 1);
y = surface.y(ix + 1, iy + 1);
z = surface(ix + 1,iy + 1);
col = rainbow(zmin, zmax, z);color(col);
glVertex3f(x, y, z);
x = surface.x(ix, iy + 1);
y = surface.y(ix, iy + 1);
z = surface(ix,iy + 1);
col = rainbow(zmin, zmax, z);color(col);
glVertex3f(x, y, z);
}
}
glEnd();
}
The problem is that this is slow, nx=ny=1000 and fps ~= 1.
How do I optimize this to be faster?
EDIT: following your suggestion (thanks!) regarding VBO
I added:
float* XYRegularSurface::xyz() {
float* data = new float[3*nx*ny];
long i = 0;
for(int ix = 0; ix < nx; ix++) {
for(int iy = 0; iy < ny; iy++) {
data[i++] = x(ix,iy);
data[i++] = y(ix,iy);
data[i] = z[i]; i++;
}
}
return data;
}
I think I understand how I can create a VBO, initialize it to xyz() and send it to the GPU in one go, but how do I use the VBO when drawing. I understand that this can either be done in the vertex shader or by glDrawElements? I assume the latter is easier? If so: I do not see any QUAD mode in the documentation for glDrawElements!?
Edit2:
So I can loop trough all nx*ny quads and draw each by:
GL_UNSIGNED_INT indices[4];
// ... set indices
glDrawElements(GL_QUADS, 1, GL_UNSIGNED_INT, indices);
?
1/. Use display lists, to cache GL commands - avoiding recalculation of the vertices and the expensive per-vertex call overhead. If the data is updated, you need to look at client-side vertex arrays (not to be confused with VAOs). Now ignore this option...
2/. Use vertex buffer objects. Available as of GL 1.5.
Since you need VBOs for core profile anyway (i.e., modern GL), you can at least get to grips with this first.
Well, you've asked a rather open ended question. I'd suggest using modern (3.0+) OpenGL for everything. The point of just about any new OpenGL feature is to provide a faster way to do things. Like everyone else is suggesting, use array (vertex) buffer objects and vertex array objects. Use an element array (index) buffer object too. Most GPUs have a 'post-transform cache', which stores the last few transformed vertices, but this can only be used when you call the glDraw*Elements family of functions. I also suggest you store a flat mesh in your VBO, where y=0 for each vertex. Sample the y from a heightmap texture in your vertex shader. If you do this, whenever the surface changes you will only need to update the heightmap texture, which is easier than updating the VBO. Use one of the floating point or integer texture formats for a heightmap, so you aren't restricted to having your values be between 0 and 1.
If so: I do not see any QUAD mode in the documentation for glDrawElements!?
If you want quads make sure you're looking at the GL 2.1-era docs, not the new stuff.