display 2 options side by side from array in gamemaker? - arrays

Recently I purchased gamemaker and followed Shaun Spalding's menu tutorial to get set up, but I've come across something that I want to change with the code. In the tutorial the options from the array are positioned so that they are one above the other, however for my game I want them to be right next to each other so that option 1 can be positioned on the left side of the screen, and option 2 on the right side of the screen on the same 'line' (but still able to switch between selecting either).
This is what it looks like currently.
As you can see, they are above each other, when really I want them side by side.
This is the code I have:
'Create' Event:
instruction[0] = "Back";
instruction[1] = "Start Game";
space = 100;
ipos = 0;
'Step' Event:
var move = 0;
move -= max(keyboard_check_pressed(vk_left),keyboard_check_pressed(ord("A")),0);
move += max(keyboard_check_pressed(vk_right),keyboard_check_pressed(ord("D")),0);
if (move != 0)
{
ipos += move;
if (ipos < 0) ipos = array_length_1d(instruction) - 1;
if (ipos > array_length_1d(instruction) - 1) ipos = 0;
}
var push;
push = max(keyboard_check_released(vk_enter),keyboard_check_released(vk_shift),keyboard_check_released(vk_space),0);
if (push == 1) scr_instructions();
'Draw' Event:
draw_set_halign(fa_left);
draw_set_valign(fa_middle);
draw_set_font(fnt_options);
draw_set_color(c_white);
var m;
for (m = 0; m < array_length_1d(instruction); m += 1)
{
draw_text(x + space, y + (m * space),string(instruction[m]))
}
draw_sprite(sprite_index, -1, x + 16, y + ipos * space - 21);
Anyone know what I need to change to get this to work?

Your draw_text seems to increase the y coordinate for each iteration in the loop.
Try to increase x using m instead.
Example:
draw_text(x + (m * space), y + space,string(instruction[m]))
You will likely have to adapt the spacing to get desired look.
Easiest is probably to hard code the coordinates instead of using a loop.
Do the same with draw_sprite.
draw_sprite(sprite_index, -1, x + 16 + ipos * space, y - 21);

Related

Passing array's element values to lowest or highest functions in pine script

I am trying to pass dynamic values to lowest and highest built-in functions in pine script from arrays. Below is the code
int[] pArray = array.new_int(0)
pArray is populated with int elements. Here is the code below that works.
fromInd = array.min(pArray)
lowest = ta.lowest(MACDHist,array.get(pArray,0))
Debug = label.new(bar_index+1, close, 'pArray: ' + str.tostring(pArray) + str.tostring(fromInd))
The output of above code on the chart is
pArray: [2, 15, 40]2
2 is the 0th index value of pArray that is printed outside of the array in the end. However when I change the label to output 'lowest' value (shown below) nothing prints to chart but everything compiles well with no errors or warnings.
Debug = label.new(bar_index+1, close, 'pArray: ' + str.tostring(pArray) + str.tostring(lowest))
The output of 'array.get(id, index)' is series int because pArray is initialized to int. I also typecasted the 'array.get(id, index)' argument by enclosing it with 'int()' but nothing is working for the last line code except when hard coded.
'ta.lowest(source, length)' accepts series int arguments for length and it can also accept dynamic lengths as shown by tradingview https://www.tradingview.com/blog/en/pine-functions-support-dynamic-length-arguments-20554/
What am I doing wrong here? Thanks in advance.
I tried a variation of a solution posted by first user below with code
//#version=5
indicator("Dynamic INT array length", overlay = false, max_bars_back = 5000)
[a, b, hist] = ta.macd(close, 26, 12, 9)
int[] lengths = array.new_int()
int[] nlengths = array.new_int()
for i = 0 to 10
if hist[i] > 0
array.unshift(lengths, i)
else
array.unshift(nlengths,i)
lowest = ta.lowest(hist, array.get(lengths,0))
plot(lowest)
plot(hist, color = color.red)
debug = label.new(x = bar_index, y = 0, style = label.style_label_left, text = str.tostring(lowest) + " " + str.tostring(lengths) + " " + str.tostring(array.get(lengths,0)))
label.delete(debug[1])
and Im getting an error when using 'ta.lowest(hist, array.min(lengths))' where it says
Invalid value of the 'length' argument (0.0) in the "lowest" function. It must be > 0.
I tried all variations of the condition hist[i] > 0 within the for loop but nothing is printing and it has the same error. Let me know if there is an easy fix for this. Thanks.
It should be working, at a guess, normally I'd say there must be something going on with how the array is populated, but it doesn't seem to be the case here given the array output. It's unlikely, but possible you've found a bug, or an even possibly an exception that isn't being picked up by the compiler.
//#version=5
indicator("Dynamic INT array length", overlay = false, max_bars_back = 5000)
int[] lengths = array.new_int()
for i = 0 to 10
array.unshift(lengths, int(math.random(30, 50)))
[a, b, hist] = ta.macd(close, 26, 12, 9)
lowest = ta.lowest(hist, array.min(lengths))
plot(lowest)
plot(hist, color = color.red)
debug = label.new(x = bar_index, y = 0, style = label.style_label_left, text = str.tostring(lowest) + " " + str.tostring(lengths) + " " + str.tostring(array.min(lengths)))
label.delete(debug[1])
Follow up answer :
There's actually two things you need to take care of. If you consider when hist has been < 0 for a while (ie at least 10 bars). The first time hist crosses the zero line, your lengths array will only have one value : zero, and it can't be used in ta.lowest(). The second issue you will have is, if hist has been under zero the entire 10 bar window, the lengths array will be empty and array.get(lengths, 0) will give you the out of bounds indexing error.
You'll have to decide how to handle these specific circumstances, but the following of how you could account for it. For example using a length of 5 when the array has only one value of 0 and populating the arrays with na values to avoid the empty array problem.
//#version=5
indicator("Dynamic INT array length", overlay = false, max_bars_back = 5000)
[a, b, hist] = ta.macd(close, 26, 12, 9)
int[] lengths = array.new_int()
int[] nlengths = array.new_int()
for i = 0 to 10
if hist[i] > 0
array.unshift(lengths, i)
array.unshift(nlengths, na)
else
array.unshift(nlengths,i)
array.unshift(lengths, na)
int len = na
if array.get(lengths, 0) > 0
len := array.get(lengths, 0)
else
len := 5
lowest = ta.lowest(hist, len)
plot(lowest)
plot(hist, color = color.red)
debug = label.new(x = bar_index, y = 0, style = label.style_label_left, text = str.tostring(lowest) + " " + str.tostring(lengths) + " " + str.tostring(array.get(lengths,0)))
label.delete(debug[1])

Taking an array option out for one cycle

Ok so I have this code where spawn locations are put into an array and then one of the locations is picked at random with this code:
let randx = spawnLocations[Int(arc4random_uniform(UInt32(spawnLocations.count)))]
obstacle.position = CGPoint(x: randx, y: 0)
Object Spawning code:
var spawnLocations:[CGFloat] = []
func getObjectSpawnLocation() {
//Create 5 possible spawn locations
let numberOfNodes = 5
// Spacing between nodes will change if: 1) number of nodes is changed, 2) screen width is changed, 3) node's size is changed.
for i in 0...numberOfNodes - 1 {
// spacing used to space out the nodes according to frame (which changes with screen width)
var xPosition = (frame.maxX /*- thePlayer.size.width*/) / CGFloat((numberOfNodes - 1)) * CGFloat(i)
//add a half of a player's width because node's anchor point is (0.5, 0.5) by default
xPosition += thePlayer.size.width/2
//I have no idea what this does but it works.
xPosition -= frame.maxX/1.6
spawnLocations.append( xPosition )
}
()
}
But I have a problem because sometimes the game spawns the objects like in the picture below and it does not let my player advance any further without them dying and so my question is:
Is there anyway I can stop it from doing this?
maybe take one of the spawning locations out of the array temporally?
I should also note that each of the objects (Skulls) are spawned one after the other not all at once and the skulls can spawn at any of the 5 horizontal locations.
The player can only be trapped between the skulls and the screen's edge. You should keep track whether or not you are currently "wedging" the player in or not, for example:
//Keep instance variables to track your current state
BOOL lineFromEdge; //YES if you're currently drawing a "line" of skulls from an edge
BOOL leftEdge; //YES if the line originates from the left edge, NO if from the right
int previousIndex;
Then you determine the value as follows:
- (int) randomIndex {
int randIndex = Int(arc4random_uniform(UInt32(spawnLocations.count)));
// This expression tells you if your current index is at an edge
if (randIndex == 0 || randIndex == (spawnLocations.count - 1)) {
lineFromEdge = YES;
leftEdge = randIndex == 0;
}
//Check if you left a gap
BOOL didLeaveGap = abs(randIndex - previousIndex) > 1
if (didLeaveGap) lineFromEdge = NO;
if ((lineFromEdge && leftEdge && randomIndex == spawnLocations.count) ||
(lineFromEdge && !leftEdge && randomIndex == 0)) {
//You have drawn a line from one edge to the other without leaving a gap;
//Calculate another index and perform the same checks again
return [self randomIndex];
}
//Your index is valid
previousIndex = randIndex;
return randIndex;
}
Note: Your algorithm must return 0 or spawnLocations.count as very first index for this to work. Else your skulls may start at the center and still wedge the player in without you realizing it.

Adding a point to ScreenSpaceLines3D results in NullReferenceException

I am using SharpenLines3d in my projects to draw lines to connect objects in my Viewport3D. Problem is, when I'm adding points to ScreenSpaceLined3D, the program crashes, like this:
It crashes not in these lines when I'm adding points to ScreenSpaceLines3D, so it's something inside the library, I guess.
Here is my code:
for (int i = 0; i < MAX + 1; i++)
{
...
var bounds = this.points[i].Bounds;
var x = bounds.X + (bounds.SizeX / 2);
var y = bounds.Y + (bounds.SizeY / 2);
var z = bounds.Z + (bounds.SizeZ / 2);
coords[i] = new Point3D(x, y, z);
for (int j = 0; j < i; j++)
{
lines[i, j] = new ScreenSpaceLines3D();
lines[i, j].Color = Colors.Red;
lines[i, j].Thickness = 6;
lines[i, j].Points.Add(coords[i]);
lines[i, j].Points.Add(coords[j]);
}
}
I am sure that I am not passing null as argument to Add function (I tried to replace my Add line with this: lines[i, j].Points.Add(new Point3D(0, 0, 0));, and it still didn't work).
And I commented out all code that uses lines variable, so this variable is used only when a bunch of new ScreenSpaceLines3D is created. But my program crashes not on initializing.
When I comment out 2 strings of code where I'm adding points to lines, program starts working okay, but when I'm adding it again, result is as in screenshot.
How to deal with it?
UPD: Adding MainViewPort.Children.Add(lines[i, j]); after adding points made it work. It is getting more and more strage.
Somehow adding MainViewPort.Children.Add(lines[i, j]); solved the problem. Without it, program crashes, but with it, everything works okay.
I don't know how this works but this is it.

Arrays in Processing

I'm quite new to programming and have recently started in Processing.
In my code, the collide function sets the touch boolean to be true but by arraying it, it only tests true for the final array and not the ones before it. Where am I going wrong here? I hope my question is clear enough.
edit:
Sorry, let me try again.
I guess my problem is finding out how to array the collide function properly. I cant seem to add a [i] for the collide in the array.
At the moment, the code works but it only tests true for the last array and not for the ones before it.
The array code:
for(int i = 0 ; i < lineDiv; i++){
collide(xPts[i], yPts[i], vecPoints.xPos, vecPoints.yPos, myDeflector.Thk, vecPoints.d);
The collide function:
void collide(float pt1x, float pt1y, float pt2x, float pt2y, int size1, int size2){
if (pt1x + size1/2 >= pt2x - size2/2 &&
pt1x - size1/2 <= pt2x + size2/2 &&
pt1y + size1/2 >= pt2y - size2/2 &&
pt1y - size1/2 <= pt2y + size2/2) {
touch = true;
}
else{
touch=false;
}
Your "touch" variable is global. Every time you call the collide() function, it overwrites whatever it was set to before. Perhaps you just want to test if touch is true after calling collide(), then exit the for loop?
Alternatively, you may want to make collide() return the touch boolean, avoiding the global.
It looks like what you want to do is to run through a loop, run the function on that element of the array and return a value if any of them are true. This is my best guess, you might want to edit your question to clarify what you are looking to do. So assuming this:
1) change you method to a function
boolean collide(float pt1x, float pt1y, float pt2x, float pt2y, int size1, int size2){
if (pt1x + size1/2 >= pt2x - size2/2 &&
pt1x - size1/2 <= pt2x + size2/2 &&
pt1y + size1/2 >= pt2y - size2/2 &&
pt1y - size1/2 <= pt2y + size2/2) {
return true;
}
else{
return false;
}
2) change your loop and how you are calling it
touch = false; // if you don't set this to false before the loop, it will be the last value taken
for(int i = 0 ; i < lineDiv; i++){
if (collide(xPts[i], yPts[i], vecPoints.xPos, vecPoints.yPos, myDeflector.Thk, vecPoints.d)) touch = true;
Before the action would be that touch might cycle between true and false as you iterate through the array and in processing (because you would likely draw out the data) it is unlikely that you would want this behavior because you wouldn't be able to do anything with it unless you packed that data in another structure like an array.
So now, the "touch" is set to false and will change to true if any function calls return a true. If all are false, it will stay false.
note: you might consider using either xPts.length() or yPts.length() vs lineDiv. This would reduce the possibility of a array out of bounds exception assuming xPts and yPts have the same # of elements.

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;
}

Resources