bool function IsPointInPolygon(float[] polyX, float[] polyY, float x, float y)
Attempts to determine if a given point (x, y) lies inside the bounds of a polygon described as a series of ordered pairs described in the polyX[] and polyY[] arrays.
Spoiler
bool function IsPointInPolygon(float[] polyX, float[] polyY, float x, float y) ;-----------\ ;Description \ ;---------------------------------------------------------------- ;Attempts to determine if a given point (x, y) lies inside the bounds of a polygon described as a series ;of ordered pairs described in the polyX[] and polyY[] arrays. ;If (x, y) lies exactly on one of the line segments, this functiom may return True or False. ;From http://alienryderflex.com/polygon/, converted to Papyrus by Chesko ;-------------\ ;Return Values \ ;---------------------------------------------------------------- ; True = Point is inside polygon ; False = Point lies outside polygon OR polygon arrays are of different lengths ;float[] polyX = array that describes the polygon's x coordinates ;float[] polyY = array that describes the polygon's y coordinates ;float x = the x coordinate under test ;float y = the y coordinate under test ;Polygon arrays must be the same length if polyX.Length != polyY.Length return false endif int polySides = polyX.Length int i = 0 int j = polySides - 1 bool oddNodes = false while i < polySides if (((polyY[i] < y && polyY[j] >= y) || (polyY[j] < y && polyY[i] >= y)) && (polyX[i] <= x || polyX[j] <= x)) if (polyX[i] + (y- polyY[i]) / (polyY[j] - polyY[i]) * (polyX[j] - polyX[i])) < x oddNodes = !oddNodes endif endif j = i i += 1 endWhile return oddNodesendFunction
The function returns true if the point given by x, y is inside the polygon described by the arrays. It will return false if the point is not inside, or if the arrays that you provided as parameters are not the same length. Returning false in the latter case was more ideal to me than dumping a ton of (array index out of range) errors in your papyrus log in the off chance that this occurred.
This function may return true or false if the point is directly on top of a vertex or segment. The function values speed over high levels of accuracy.
Example scenario:
- Let's say I want to know if the player is inside a section of river near Riverwood. http://i.imgur.com/zrMHp.jpg (in red) that we care about.
- We want to describe the area as a polygon by http://i.imgur.com/Jlp2V.jpg. It ends up being a 10-sided polygon.
- Next, we can take those coordinates and plug them into a script, such as this example:
Spoiler
Scriptname _TEST_myPointInPolygonRiverTest extends QuestActor property PlayerRef autofloat[] myRiverPolyXfloat[] myRiverPolyYimport debugEvent OnInit() ;Initialize the array myRiverPolyX = new float[10] myRiverPolyY = new float[10] ;Populate array values that describe the polygon myRiverPolyX[0] = 14889.85 myRiverPolyX[1] = 13869.10 myRiverPolyX[2] = 16938.22 myRiverPolyX[3] = 17743.47 myRiverPolyX[4] = 16763.15 myRiverPolyX[5] = 16811.10 myRiverPolyX[6] = 17797.28 myRiverPolyX[7] = 17823.37 myRiverPolyX[8] = 16988.59 myRiverPolyX[9] = 16098.66 myRiverPolyY[0] = -46124.05 myRiverPolyY[1] = -45167.48 myRiverPolyY[2] = -43011.81 myRiverPolyY[3] = -43976.84 myRiverPolyY[4] = -45001.36 myRiverPolyY[5] = -45582.18 myRiverPolyY[6] = -45775.87 myRiverPolyY[7] = -46529.61 myRiverPolyY[8] = -46465.85 myRiverPolyY[9] = -45956.11 RegisterForSingleUpdate(1)endEventEvent OnUpdate() ;Is the player inside the 10-sided polygon? bool isInRiverPoly = IsPointInPolygon(myRiverPolyX, myRiverPolyY, PlayerRef.GetPositionX(), PlayerRef.GetPositionY()) if isInRiverPoly notification("I'm inside the river polygon!") else notification("I'm not inside the river polygon!") endif RegisterForSingleUpdate(1)endEventbool function IsPointInPolygon(float[] polyX, float[] polyY, float x, float y) ;-----------\ ;Description \ ;---------------------------------------------------------------- ;Attempts to determine if a given point (x, y) lies inside the bounds of a polygon described as a series ;of ordered pairs described in the polyX[] and polyY[] arrays. ;If (x, y) lies exactly on one of the line segments, this functiom may return True or False. ;From http://alienryderflex.com/polygon/, converted to Papyrus by Chesko ;-------------\ ;Return Values \ ;---------------------------------------------------------------- ; True = Point is inside polygon ; False = Point lies outside polygon OR polygon arrays are of different lengths ;float[] polyX = array that describes the polygon's x coordinates ;float[] polyY = array that describes the polygon's y coordinates ;float x = the x coordinate under test ;float y = the y coordinate under test ;Polygon arrays must be the same length if polyX.Length != polyY.Length return false endif int polySides = polyX.Length int i = 0 int j = polySides - 1 bool oddNodes = false while i < polySides if (((polyY[i] < y && polyY[j] >= y) || (polyY[j] < y && polyY[i] >= y)) && (polyX[i] <= x || polyX[j] <= x)) if (polyX[i] + (y- polyY[i]) / (polyY[j] - polyY[i]) * (polyX[j] - polyX[i])) < x oddNodes = !oddNodes endif endif j = i i += 1 endWhile return oddNodesendFunction
We then attach this script to a quest that will run our script for us and see what happens. As you can see, when the player is in the river, http://i.imgur.com/NDask.jpg, and when he's not, http://i.imgur.com/Rzv9j.jpg.
Since this area could easily be described as a set of carefully placed trigger boxes, this probably isn't the best example, but if you expand the size of the polygon to encompass a very large space (multiple cells), then you can see how this might become useful. For example, you could create a space that runs the entire length of a river across many cells to determine if the player is standing in it. Or whatever large-scale application you may need something like this for.
This function should accommodate severely concave polygons, but since the polygon is described as a series of ordered pairs, it will not solve for polygons with http://docs.oracle.com/html/A88805_01/sdo_objb.gif (since there is no way to describe the hole in a polygon described as a series of connected verticies). A possible way to do this is to describe the hole as a second polygon, and do a test for "if inside "hole" polygon, do x, elseif inside "actual" polygon but not "hole", do y".
Also, because of the maximum size restriction of Papyrus arrays, a polygon could have no more than 128 sides.
Enjoy, I hope someone finds this useful.