Index expression must be constant - WebGL/GLSL error - arrays

I'm having trouble accessing an array in a fragment shader using a non-constant int as the index. I've removed the formula as it wouldn't make much sense here anyway, but my code is meant to calculate the tileID based on the current pixel and use that to determine the color.
Here's my code:
int tileID = <Insert formula here>;
vec3 colorTest;
int arrayTest[1024];
for (int x = 0; x < 1024; x++) {
if (x == 1) arrayTest[x] = 1;
else arrayTest[x] = 2;
}
if (arrayTest[tileID] == 1) colorTest = vec3(0.0, 1.0, 0.0);
else if (arrayTest[tileID] == 2) colorTest = vec3(1.0, 0.0, 0.0);
else colorTest = vec3(0.0, 0.0, 0.0);
Apparently GLSL doesn't like this and I get the error:
'[]' : Index expression must be constant
Does anyone know how I would fix this? Thanks.

As background -- GLSL looks a lot like C, but compiles a bit different. Things are very unrolled, and conditionals may be executed in parallel and switched at the end, that sort of thing. Depends on the hardware...
You can use loop indices or constants to index into arrays. The assignment in your loop is ok, but the access by tileID isn't.
WebGL Shader language is from GLES, documented
http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf
The Appendix, section 5, discusses:
Indexing of Arrays, Vectors and Matrices
Definition:
constant-index-expressions are a superset of constant-expressions. Constant-index-expressions can include loop indices as defined in Appendix A section 4.
The following are constant-index-expressions:
• Constant expressions
• Loop indices as defined in section 4
• Expressions composed of both of the above
When used as an index, a constant-index-expression must have integral type.
Hope that helps!
Oh, as for fixing it, in the exact example above... looks like you could compute from tileID rather than precompute and index.
Or, precompute whatever array you like, and pass it in as a texture. A texture, of course, can be indexed however you like.
Here's a javascript helper method I use, to pass floats down to the shaders:
function glSetupStuff() { ...
...
if(!gl.getExtension("OES_texture_float")) // <<-- enables RGBA float values, handy!
alert("cant pass in floats, use 8-bit values instead.");
... }
/*
* Pass in an array of rgba floats,
* for example: var data = new Float32Array([0.1,0.2,0.3,1, .5,.5,1.0,1]);
*/
function textureFromFloats(gl,width,height,float32Array)
{
var oldActive = gl.getParameter(gl.ACTIVE_TEXTURE);
gl.activeTexture(gl.TEXTURE15); // working register 31, thanks.
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
width, height, 0,
gl.RGBA, gl.FLOAT, float32Array);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.activeTexture(oldActive);
return texture;
}
Note the use of gl.NEAREST, so it doesn't "blur" your values! Then you can set it up before the gl.drawXxx call, with something like
textureUnit = 3; // from 0 to 15 is ok
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, texture);
var z = gl.getUniformLocation(prog, "uSampler");
gl.uniform1i(z, textureUnit);
And in the shader (I believe fragment or vertex; some earlier webgl's didn't support vertex textures...)
uniform sampler2D uSampler;
...
vec4 value = texture2D(uSampler, vec2(xValueBetween0And1,yValueBetween0And1));
So, you have to index appropriately for the array-as-texture size, within range of 0 to 1. Try to sample from the middle of each value/pixel. Like, if the array is 2 values wide, index by 0.25 and 0.75.
That's the gist of it!

Tested in Safari 9.1.2 on OS X 10.11.6
uniform float data[32];
float getData(int id) {
for (int i=0; i<32; i++) {
if (i == id) return data[i];
}
}
void main(void) {
float f = getData(yourVariable);
}

I hit this error because I was attempting to use an integer variable to take the nth texture from an array of textures:
// this doesn't compile!
varying vec2 vUv; // uv coords
varying float vTexture; // texture index in array of textures
uniform sampler2D textures[3]; // identify that there are 3 textures
void main() {
int textureIndex = int(floor(vTexture));
gl_FragColor = texture2D(textures[textureIndex], vUv);
}
The solution was to break out the texture indexing into a sequence of conditionals:
// this compiles!
varying vec2 vUv; // uv coords
varying float vTexture; // texture index in array of textures
uniform sampler2D textures[3]; // identify that there are 3 textures
void main() {
int textureIndex = int(floor(vTexture));
if (textureIndex == 0) {
gl_FragColor = texture2D(textures[0], vUv);
} else if (textureIndex == 1) {
gl_FragColor = texture2D(textures[1], vUv);
} else if (textureIndex == 2) {
gl_FragColor = texture2D(textures[2], vUv);
}
}

Related

Dereference UnsafeMutablePointer<UnsafeMutableRawPointer>

I have a block that is passing data in that I'd like to convert to an array of array of floats -- e.g. [[0.1,0.2,0.3, 1.0], [0.3, 0.4, 0.5, 1.0], [0.5, 0.6, 0.7, 1.0]]. This data is passed to me in the form of data:UnsafeMutablePointer<UnsafeMutableRawPointer> (The inner arrays are RGBA values)
fwiw -- the block parameters are from SCNParticleEventBlock
How can I dereference data into a [[Float]]? Once I have the array containing the inner arrays, I can reference the inner array (colorArray) data with:
let rgba: UnsafeMutablePointer<Float> = UnsafeMutablePointer(mutating: colorArray)
let count = 4
for i in 0..<count {
print((rgba+i).pointee)
}
fwiw -- this is Apple's example Objective-C code for referencing the data (from SCNParticleSystem handle(_:forProperties:handler:) )
[system handleEvent:SCNParticleEventBirth
forProperties:#[SCNParticlePropertyColor]
withBlock:^(void **data, size_t *dataStride, uint32_t *indices , NSInteger count) {
for (NSInteger i = 0; i < count; ++i) {
float *color = (float *)((char *)data[0] + dataStride[0] * i);
if (rand() & 0x1) { // Switch the green and red color components.
color[0] = color[1];
color[1] = 0;
}
}
}];
You can actually subscript the typed UnsafeMutablePointer without having to create an UnsafeMutableBufferPointer, as in:
let colorsPointer:UnsafeMutableRawPointer = data[0] + dataStride[0] * i
let rgbaBuffer = colorsPointer.bindMemory(to: Float.self, capacity: dataStride[0])
if(arc4random_uniform(2) == 1) {
rgbaBuffer[0] = rgbaBuffer[1]
rgbaBuffer[1] = 0
}
Were you ever able to get your solution to work? It appears only a handful of SCNParticleProperties can be used within an SCNParticleEventBlock block.
Based on this answer, I've written the particle system handler function in swift as:
ps.handle(SCNParticleEvent.birth, forProperties [SCNParticleSystem.ParticleProperty.color]) {
(data:UnsafeMutablePointer<UnsafeMutableRawPointer>, dataStride:UnsafeMutablePointer<Int>, indicies:UnsafeMutablePointer<UInt32>?, count:Int) in
for i in 0..<count {
// get an UnsafeMutableRawPointer to the i-th rgba element in the data
let colorsPointer:UnsafeMutableRawPointer = data[0] + dataStride[0] * i
// convert the UnsafeMutableRawPointer to a typed pointer by binding it to a type:
let floatPtr = colorsPointer.bindMemory(to: Float.self, capacity: dataStride[0])
// convert that to a an UnsafeMutableBufferPointer
var rgbaBuffer = UnsafeMutableBufferPointer(start: floatPtr, count: dataStride[0])
// At this point, I could convert the buffer to an Array, but doing so copies the data into the array and any changes made in the array are not reflected in the original data. UnsafeMutableBufferPointer are subscriptable, nice.
//var rgbaArray = Array(rgbaBuffer)
// about half the time, mess with the red and green components
if(arc4random_uniform(2) == 1) {
rgbaBuffer[0] = rgbaBuffer[1]
rgbaBuffer[1] = 0
}
}
}
I'm really not certain if this is the most direct way to go about this and seems rather cumbersome compared to the objective-C code (see above question). I'm certainly open to other solutions and/or comments on this solution.

Scenekit giving buffer size error while passing array data to uniform array in openGL shader

this is the surface shader which I use to make a trail on floor surface.
#pragma arguments
uniform vec2 trailPoints[5];
uniform float count;
#pragma body
float trailRadius = 10.0;
float x = _surface.diffuseTexcoord.x;
float x100 = float(x * 100);
float y = _surface.diffuseTexcoord.y;
float y100 = float(y * 100);
for (int i = 0; i < int(count); i++) {
vec2 position = trailPoints[i];
if ((x100 > position.x - trailRadius && x100 < position.x + trailRadius) && (y100 > position.y - trailRadius && y100 < position.y + trailRadius)) {
_surface.diffuse.rgb = vec3(0.0, 10.0 ,0.0);
}
}
and this is the swift side code which I use to pass vector data to surface shader.
if let geometry = self.floorNode.geometry {
if let material = geometry.firstMaterial {
// this is the temporary data which I use to find the problem.
// this data will be dynamic later on.
let myValueArray:[float2] = [float2(x:80, y:80),float2(x:60, y:60),float2(x:40, y:40),float2(x:20, y:20),float2(x:0, y:0)]
// Passing array count to shader. There is no problem here.
var count = Float(myValueArray.count)
let countData = Data(buffer: UnsafeBufferPointer(start: &count, count: 1))
material.setValue(countData, forKey: "count")
// and here is the problem start.
// myValueArray converted to data with its size.
let valueArrayData = Data(buffer: UnsafeBufferPointer(start: myValueArray, count: myValueArray.count))
material.setValue(valueArrayData, forKey: "trailPoints")
}
}
When I build and run the project the following error occurred and no data passed to the "trailPoints" in shader.
Error: arguments trailPoints : mismatch between the NSData and the buffer size 40 != 8
When I change the array count to 1 while converting array to data,
let valueArrayData = Data(buffer: UnsafeBufferPointer(start: myValueArray, count: 1))
the errors dissapear but only the first member of the array will passing to shader.
so, the problem is,
how can I pass the all array members to the shader?
I think, the answer of this question this:
I recently realized, the OpenGl ES 2.0 only allow the following array definitions:
float myValue[3];
myValue[0] = 1.0;
myValue[1] = 2.0;
myValue[2] = 3.0;
But as far as I can tell, it is not possible to do this using the SCNShaderModifierEntryPoint with following way.
material.setValue (1.0, forKey: "myValue[0]")
material.setValue (2.0, forKey: "myValue[1]")
material.setValue (3.0, forKey: "myValue[2]")
And finally I found a way to pass the array to fragment shader with SCNProgram handleBinding method.
let myValue:[Float] = [1.0, 2.0, 3.0]
material.handleBinding(ofSymbol:"myValue", handler: { (programId:UInt32, location:UInt32, node:SCNNode?, renderer:SCNRenderer) in
for (index, v) in myValue.enumerated() {
var v1 = v
let aLoc = glGetUniformLocation(programId, String(format: "myValue[%i]", index))
glUniform1fv(GLint(aLoc), 1, &v1)
}
})
But, SCNProgram is completly rid off the default swift shader program and use yours.
The default shader program of swift is highly complex and do lots of things to your place.
default vertex shader of swift
default fragment shader of swift
So maybe its not a good idea to use SCNProgram for only pass the arrays to shader.
And one interesting thing, SCNProgram does not work on SCNFloor geometry.

QImage(uchar *data) how to format array?

Can anybody tell me how i can format my array so i can load it to a QImage?
For now i have a 2d-char-array:
uchar dataArray[400][400];
time_t t;
srand(time(&t));
int x, y;
for(x=0; x< 400; x++)
{
for(y=0; y<400; y++)
{
dataArray[x][y] = rand()%1001;
}
}
QPainter MyPainter(this);
scene = new QGraphicsScene(this);
scene->addEllipse(200, 200, 20, 20);
ui.graphicsView->setScene(scene);
*image = new QImage(*dataArray, 400, 400, QImage::Format_Mono);
image->setColorCount(2);
image->setColor(1, qRgb(255, 0, 0));//red
image->setColor(0, Qt::transparent);
scene->addPixmap(QPixmap::fromImage(*image));
When the content of the array is 0 I want the one color and content > 0 the other color. So i want to load the array into a monochromatic QImage. Obviously this array does not work. How does the Array need to be formatted for my QImage to load properly?
The doc just says following, but i do not really get what that means...
data must be 32-bit aligned, and each scanline of data in the image must also be 32-bit aligned.
I want a QImage with Format_Mono like this Where "x" and "+" represent single pixels with different color (red and transparent):
xxxx+xxx++xxx
xxx++xx++xxxx
++x+x+xxxxxxx
+xxx+x+x+x+xx
For this i have an array (dataArray as in code above) with same pattern where x is above a specified value and + is under or equal (the value is 0 at the moment).
How do i convert this array to an array wich can be used by QImage with Format_Mono so i can see the correct pattern?
Assuming that dataArray is declared as uchar dataArray[400][400];
QImage tmpImg(dataArray[0], 400, 400, QImage::Format_Grayscale8);
// this image shares memory with dataArray and this is not safe
// for inexperienced developer
QImage result(tmpImg); // shallow copy
result.bits(); // enforce a deep copy of image
Found the solution to convert my dataArray into a working imageArray:
Its pretty easy as soon as you figure out how it is done (and it should be obviously i do not know why i dind't get it in first place...). I just needed to convert every data-point to the new array bit-wise, also I had to figure out, that the axis of the imageArray have to be in [y][x]-order not the other way round (who the hell would do that in this order?!).
Many time wasted to figure that out...
uchar dataArray[400][400]; //first index is x-axis, second one is y-axis
uchar imageArray[400][52]; //first index is y-axis, second one is x-axis
time_t t;
srand(time(&t));
int x, y;
for(y=0; y<400; y++)
{
for(x=0; x<400; x++)
{
dataArray[x][y] = rand()%1001;
}
}
for(y=0; y<400; y++)
{
for(x=0; x<400; x++)
{
if(dataArray[x][y] > 0)
imageArray[y][x/8] |= 1 << 7-x%8; //changing bit to 1
//7- because msb is left but we start counting left starting with 0
else
imageArray[y][x/8] &= ~(1 << 7-x%8); //changing bit to 0
}
}
scene = new QGraphicsScene(this);
ui.graphicsView->setScene(scene);
QImage *image = new QImage(*imageArray, 400, 400, QImage::Format_Mono);

Point in Polygon Algorithm

I saw the below algorithm works to check if a point is in a given polygon from this link:
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
I tried this algorithm and it actually works just perfect. But sadly I cannot understand it well after spending some time trying to get the idea of it.
So if someone is able to understand this algorithm, please explain it to me a little.
Thank you.
The algorithm is ray-casting to the right. Each iteration of the loop, the test point is checked against one of the polygon's edges. The first line of the if-test succeeds if the point's y-coord is within the edge's scope. The second line checks whether the test point is to the left of the line (I think - I haven't got any scrap paper to hand to check). If that is true the line drawn rightwards from the test point crosses that edge.
By repeatedly inverting the value of c, the algorithm counts how many times the rightward line crosses the polygon. If it crosses an odd number of times, then the point is inside; if an even number, the point is outside.
I would have concerns with a) the accuracy of floating-point arithmetic, and b) the effects of having a horizontal edge, or a test point with the same y-coord as a vertex, though.
Edit 1/30/2022: I wrote this answer 9 years ago when I was in college. People in the chat conversation are indicating it's not accurate. You should probably look elsewhere. 🤷‍♂️
Chowlett is correct in every way, shape, and form.
The algorithm assumes that if your point is on the line of the polygon, then that is outside - for some cases, this is false. Changing the two '>' operators to '>=' and changing '<' to '<=' will fix that.
bool PointInPolygon(Point point, Polygon polygon) {
vector<Point> points = polygon.getPoints();
int i, j, nvert = points.size();
bool c = false;
for(i = 0, j = nvert - 1; i < nvert; j = i++) {
if( ( (points[i].y >= point.y ) != (points[j].y >= point.y) ) &&
(point.x <= (points[j].x - points[i].x) * (point.y - points[i].y) / (points[j].y - points[i].y) + points[i].x)
)
c = !c;
}
return c;
}
I changed the original code to make it a little more readable (also this uses Eigen). The algorithm is identical.
// This uses the ray-casting algorithm to decide whether the point is inside
// the given polygon. See https://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm
bool pnpoly(const Eigen::MatrixX2d &poly, float x, float y)
{
// If we never cross any lines we're inside.
bool inside = false;
// Loop through all the edges.
for (int i = 0; i < poly.rows(); ++i)
{
// i is the index of the first vertex, j is the next one.
// The original code uses a too-clever trick for this.
int j = (i + 1) % poly.rows();
// The vertices of the edge we are checking.
double xp0 = poly(i, 0);
double yp0 = poly(i, 1);
double xp1 = poly(j, 0);
double yp1 = poly(j, 1);
// Check whether the edge intersects a line from (-inf,y) to (x,y).
// First check if the line crosses the horizontal line at y in either direction.
if ((yp0 <= y) && (yp1 > y) || (yp1 <= y) && (yp0 > y))
{
// If so, get the point where it crosses that line. This is a simple solution
// to a linear equation. Note that we can't get a division by zero here -
// if yp1 == yp0 then the above if will be false.
double cross = (xp1 - xp0) * (y - yp0) / (yp1 - yp0) + xp0;
// Finally check if it crosses to the left of our test point. You could equally
// do right and it should give the same result.
if (cross < x)
inside = !inside;
}
}
return inside;
}
To expand on the "too-clever trick". We want to iterate over all adjacent vertices, like this (imagine there are 4 vertices):
i
j
0
1
1
2
2
3
3
0
My code above does it the simple obvious way - j = (i + 1) % num_vertices. However this uses integer division which is much much slower than all other operations. So if this is performance critical (e.g. in an AAA game) you want to avoid it.
The original code changes the order of iteration a bit:
i
j
0
3
1
0
2
1
3
2
This is still totally valid since we're still iterating over every vertex pair and it doesn't really matter whether you go clockwise or anticlockwise, or where you start. However now it lets us avoid the integer division. In easy-to-understand form:
int i = 0;
int j = num_vertices - 1; // 3
while (i < num_vertices) { // 4
{body}
j = i;
++i;
}
Or in very terse C style:
for (int i = 0, j = num_vertices - 1; i < num_vertices; j = i++) {
{body}
}
This might be as detailed as it might get for explaining the ray-tracing algorithm in actual code. It might not be optimized but that must always come after a complete grasp of the system.
//method to check if a Coordinate is located in a polygon
public boolean checkIsInPolygon(ArrayList<Coordinate> poly){
//this method uses the ray tracing algorithm to determine if the point is in the polygon
int nPoints=poly.size();
int j=-999;
int i=-999;
boolean locatedInPolygon=false;
for(i=0;i<(nPoints);i++){
//repeat loop for all sets of points
if(i==(nPoints-1)){
//if i is the last vertex, let j be the first vertex
j= 0;
}else{
//for all-else, let j=(i+1)th vertex
j=i+1;
}
float vertY_i= (float)poly.get(i).getY();
float vertX_i= (float)poly.get(i).getX();
float vertY_j= (float)poly.get(j).getY();
float vertX_j= (float)poly.get(j).getX();
float testX = (float)this.getX();
float testY = (float)this.getY();
// following statement checks if testPoint.Y is below Y-coord of i-th vertex
boolean belowLowY=vertY_i>testY;
// following statement checks if testPoint.Y is below Y-coord of i+1-th vertex
boolean belowHighY=vertY_j>testY;
/* following statement is true if testPoint.Y satisfies either (only one is possible)
-->(i).Y < testPoint.Y < (i+1).Y OR
-->(i).Y > testPoint.Y > (i+1).Y
(Note)
Both of the conditions indicate that a point is located within the edges of the Y-th coordinate
of the (i)-th and the (i+1)- th vertices of the polygon. If neither of the above
conditions is satisfied, then it is assured that a semi-infinite horizontal line draw
to the right from the testpoint will NOT cross the line that connects vertices i and i+1
of the polygon
*/
boolean withinYsEdges= belowLowY != belowHighY;
if( withinYsEdges){
// this is the slope of the line that connects vertices i and i+1 of the polygon
float slopeOfLine = ( vertX_j-vertX_i )/ (vertY_j-vertY_i) ;
// this looks up the x-coord of a point lying on the above line, given its y-coord
float pointOnLine = ( slopeOfLine* (testY - vertY_i) )+vertX_i;
//checks to see if x-coord of testPoint is smaller than the point on the line with the same y-coord
boolean isLeftToLine= testX < pointOnLine;
if(isLeftToLine){
//this statement changes true to false (and vice-versa)
locatedInPolygon= !locatedInPolygon;
}//end if (isLeftToLine)
}//end if (withinYsEdges
}
return locatedInPolygon;
}
Just one word about optimization: It isn't true that the shortest (and/or the tersest) code is the fastest implemented. It is a much faster process to read and store an element from an array and use it (possibly) many times within the execution of the block of code than to access the array each time it is required. This is especially significant if the array is extremely large. In my opinion, by storing each term of an array in a well-named variable, it is also easier to assess its purpose and thus form a much more readable code. Just my two cents...
The algorithm is stripped down to the most necessary elements. After it was developed and tested all unnecessary stuff has been removed. As result you can't undertand it easily but it does the job and also in very good performance.
I took the liberty to translate it to ActionScript-3:
// not optimized yet (nvert could be left out)
public static function pnpoly(nvert: int, vertx: Array, verty: Array, x: Number, y: Number): Boolean
{
var i: int, j: int;
var c: Boolean = false;
for (i = 0, j = nvert - 1; i < nvert; j = i++)
{
if (((verty[i] > y) != (verty[j] > y)) && (x < (vertx[j] - vertx[i]) * (y - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
c = !c;
}
return c;
}
This algorithm works in any closed polygon as long as the polygon's sides don't cross. Triangle, pentagon, square, even a very curvy piecewise-linear rubber band that doesn't cross itself.
1) Define your polygon as a directed group of vectors. By this it is meant that every side of the polygon is described by a vector that goes from vertex an to vertex an+1. The vectors are so directed so that the head of one touches the tail of the next until the last vector touches the tail of the first.
2) Select the point to test inside or outside of the polygon.
3) For each vector Vn along the perimeter of the polygon find vector Dn that starts on the test point and ends at the tail of Vn. Calculate the vector Cn defined as DnXVn/DN*VN (X indicates cross product; * indicates dot product). Call the magnitude of Cn by the name Mn.
4) Add all Mn and call this quantity K.
5) If K is zero, the point is outside the polygon.
6) If K is not zero, the point is inside the polygon.
Theoretically, a point lying ON the edge of the polygon will produce an undefined result.
The geometrical meaning of K is the total angle that the flea sitting on our test point "saw" the ant walking at the edge of the polygon walk to the left minus the angle walked to the right. In a closed circuit, the ant ends where it started.
Outside of the polygon, regardless of location, the answer is zero.
Inside of the polygon, regardless of location, the answer is "one time around the point".
This method check whether the ray from the point (testx, testy) to O (0,0) cut the sides of the polygon or not .
There's a well-known conclusion here: if a ray from 1 point and cut the sides of a polygon for a odd time, that point will belong to the polygon, otherwise that point will be outside the polygon.
To expand on #chowlette's answer where the second line checks if the point is to the left of the line,
No derivation is given but this is what I worked out:
First it helps to imagine 2 basic cases:
the point is left of the line . / or
the point is right of the line / .
If our point were to shoot a ray out horizontally where would it strike the line segment. Is our point to the left or right of it? Inside or out? We know its y coordinate because it's by definition the same as the point. What would the x coordinate be?
Take your traditional line formula y = mx + b. m is the rise over the run. Here, instead we are trying to find the x coordinate of the point on that line segment that has the same height (y) as our point.
So we solve for x: x = (y - b)/m. m is rise over run, so this becomes run over rise or (yj - yi)/(xj - xi) becomes (xj - xi)/(yj - yi). b is the offset from origin. If we assume yi as the base for our coordinate system, b becomes yi. Our point testy is our input, subtracting yi turns the whole formula into an offset from yi.
We now have (xj - xi)/(yj - yi) or 1/m times y or (testy - yi): (xj - xi)(testy - yi)/(yj - yi) but testx isn't based to yi so we add it back in order to compare the two ( or zero testx as well )
I think the basic idea is to calculate vectors from the point, one per edge of the polygon. If vector crosses one edge, then the point is within the polygon. By concave polygons if it crosses an odd number of edges it is inside as well (disclaimer: although not sure if it works for all concave polygons).
This is the algorithm I use, but I added a bit of preprocessing trickery to speed it up. My polygons have ~1000 edges and they don't change, but I need to look up whether the cursor is inside one on every mouse move.
I basically split the height of the bounding rectangle to equal length intervals and for each of these intervals I compile the list of edges that lie within/intersect with it.
When I need to look up a point, I can calculate - in O(1) time - which interval it is in and then I only need to test those edges that are in the interval's list.
I used 256 intervals and this reduced the number of edges I need to test to 2-10 instead of ~1000.
Here's a php implementation of this:
<?php
class Point2D {
public $x;
public $y;
function __construct($x, $y) {
$this->x = $x;
$this->y = $y;
}
function x() {
return $this->x;
}
function y() {
return $this->y;
}
}
class Point {
protected $vertices;
function __construct($vertices) {
$this->vertices = $vertices;
}
//Determines if the specified point is within the polygon.
function pointInPolygon($point) {
/* #var $point Point2D */
$poly_vertices = $this->vertices;
$num_of_vertices = count($poly_vertices);
$edge_error = 1.192092896e-07;
$r = false;
for ($i = 0, $j = $num_of_vertices - 1; $i < $num_of_vertices; $j = $i++) {
/* #var $current_vertex_i Point2D */
/* #var $current_vertex_j Point2D */
$current_vertex_i = $poly_vertices[$i];
$current_vertex_j = $poly_vertices[$j];
if (abs($current_vertex_i->y - $current_vertex_j->y) <= $edge_error && abs($current_vertex_j->y - $point->y) <= $edge_error && ($current_vertex_i->x >= $point->x) != ($current_vertex_j->x >= $point->x)) {
return true;
}
if ($current_vertex_i->y > $point->y != $current_vertex_j->y > $point->y) {
$c = ($current_vertex_j->x - $current_vertex_i->x) * ($point->y - $current_vertex_i->y) / ($current_vertex_j->y - $current_vertex_i->y) + $current_vertex_i->x;
if (abs($point->x - $c) <= $edge_error) {
return true;
}
if ($point->x < $c) {
$r = !$r;
}
}
}
return $r;
}
Test Run:
<?php
$vertices = array();
array_push($vertices, new Point2D(120, 40));
array_push($vertices, new Point2D(260, 40));
array_push($vertices, new Point2D(45, 170));
array_push($vertices, new Point2D(335, 170));
array_push($vertices, new Point2D(120, 300));
array_push($vertices, new Point2D(260, 300));
$Point = new Point($vertices);
$point_to_find = new Point2D(190, 170);
$isPointInPolygon = $Point->pointInPolygon($point_to_find);
echo $isPointInPolygon;
var_dump($isPointInPolygon);
I modified the code to check whether the point is in a polygon, including the point is on an edge.
bool point_in_polygon_check_edge(const vec<double, 2>& v, vec<double, 2> polygon[], int point_count, double edge_error = 1.192092896e-07f)
{
const static int x = 0;
const static int y = 1;
int i, j;
bool r = false;
for (i = 0, j = point_count - 1; i < point_count; j = i++)
{
const vec<double, 2>& pi = polygon[i);
const vec<double, 2>& pj = polygon[j];
if (fabs(pi[y] - pj[y]) <= edge_error && fabs(pj[y] - v[y]) <= edge_error && (pi[x] >= v[x]) != (pj[x] >= v[x]))
{
return true;
}
if ((pi[y] > v[y]) != (pj[y] > v[y]))
{
double c = (pj[x] - pi[x]) * (v[y] - pi[y]) / (pj[y] - pi[y]) + pi[x];
if (fabs(v[x] - c) <= edge_error)
{
return true;
}
if (v[x] < c)
{
r = !r;
}
}
}
return r;
}

GPC Polygon Initialization

I am using the GPC Polygon Clipping lib and want to create a polygon programatically. I only see code for how to create one from a file. How can I do the initialization in my code?
Read better from your link, find the doc page and read; in particular gpc_add_contour function is likely what you need. The struct gpc_vertex_list holds a pointer to gpc_vertex-s and the number of vertex, and is what you must fill in. Like
gpc_polygon p = {0, NULL, NULL}; // "void" polygon
gpc_vertex v[] = { {0.0, 0.0}, {10.0, 0.}, {10.0, 10.10}, {0.0, 10.0} };
gpc_vertex_list vl = {
4, v
};
//...
gpc_add_contour(&p, &vl, 0);
The doc is not too much clear, but you can deduce the use, and testing (try-error loops) is your friend (I won't install gpc to do it anyway, so my code could be wrong). The proposed code snippet should create a square. Several other gpc_add_countour with the same &p but different vertex list can be used to create a more complex polygon, and of course vl can be changed to have at the beginning a more complex polygon. The third parameter should be 1 if you want the defined contour to be a "hole" in the current (p) polygon.
gpc_polygon subject;
int w = 100, h = 100, verticesCnt = 30;
//setup a gpc_polygon container and fill it with random vertices ...
subject.num_contours = 1;
subject.hole = 0;
subject.contour = new gpc_vertex_list; //ie just a single polygon here
subject.contour->num_vertices = verticesCnt;
subject.contour->vertex = new gpc_vertex [verticesCnt];
for (i = 0; i < verticesCnt; i++){
subject.contour[0].vertex[i].x = random(w);
subject.contour[0].vertex[i].y = random(h);
}
//do stuff with it here, then ...
gpc_free_polygon(&subject);

Resources