C# Find Nearby Points on a XY Grid - c#

Say I have an grid of 100by100.
Now I have a location on 23,70.
How can I find all XY points on this grid within a distance of 5?

Your question is a little vague, but there might be some simple methods to do it quickly depending on your requirements and definitions.
Assuming that we're talking about a unit-square grid there are 3 primary ways to define the distance:
Number of steps in the 8 directions: n, ne, e, se, s, sw, w, nw
Number of steps in the 4 directions: n, e, s, w
Linear distance between the cells (Pythagoras)
The first is the simplest, being simply a square from [x - 5, y - 5] to [x + 5, y + 5]. Quick enough to calculate on the fly.
Second option is a diamond shape which satisfies the following function:
static bool InRange(Point C, Point P) => (Math.Abs(P.X - C.X) + Math.Abs(P.Y - C.Y)) <= 5;
And finally, linear distance uses Pythagoras to find the distance between the cells and compares that to your threshold value. The final shape will depend on how you handle overlap. You could use the following test function:
static bool InRange(Point C, Point P) => ((P.X - C.X) * (P.X - C.X)) + ((P.Y - C.Y) * (P.Y - C.Y)) <= 25;
Note that in all cases the possible solution range falls in the square defined by the first solution, so you don't have to test every single cell on your grid. At most there are 24 tests (for a distance of 5) modified by the size of the grid.
Here's a general solution building on the above, using LINQ because I like it:
public static IEnumerable<Point> PointsInRange(this Point C, int range, int method)
{
// select filter function
Func<int, int, bool> filter = (x, y) => true;
if (method == 1)
filter = (x, y) => (Math.Abs(x) + Math.Abs(y)) <= range;
else if (method == 2)
filter = (x, y) => (x * x + y * y) <= (range * range);
// apply filter to all cells in potential range
var validOffsets =
from dx in Enumerable.Range(-range, range * 2 + 1)
from dy in Enumerable.Range(-range, range * 2 + 1)
where (dx != 0 || dy != 0) && filter(dx, dy)
select new { dx, dy };
// offset from center and clip to grid
var result =
from o in validOffsets
let x = C.x + o.dx
let y = C.y + o.dy
where x >= 0 && x < 100 && y >= 0 && y < 100
select new Point(x, y);
return result;
}
There are a few speed-ups you can apply, as always.
Of course if you only ever want to get the valid cells for a specific range (5 for instance) then you can reduce it further by specifying the valid offsets in an array instead of calculating them as above. You can cut the whole start of the method and replace it with a constant array with the shape you want. Or combine the two ideas and cache the answers so that you have the flexibility of arbitrary range and the speed of computing the valid offsets only once.

Related

Get Vector2 that has distance x to two vectors

I want to calculate the Position(s) that have the same distance to PositonA and PositionB.
Example:
PositionA: (3,2)
PositionB: (5,4)
Distance: 5
If I'm not mistaken there are two possibilities, but I don't know how to calculate either mathematically.
Update:
Based on mrk's answer, I adapted the question, although it was not clear to me before that it is then a circle in three-dimensional space. Thank you very much for that.
I have added an image to clarify the two-dimensional variant.
The goal is to calculate the best escape route based on the positions of the two attackers. In this example the distance is fixed at 5, but later variable.
From your conditions you get three equations with three variables (X,
Y, Z) that are the coordinates of the points P for which the conditions
hold. Based on your points A (A1, A2, A3) and B (B1, B2, B3).
The first equation reflects the fact, that the Point P has the same distance to both A and B
Math.Pow(X - A1, 2) + Math.Pow(Y - A2, 2) = Math.Pow(X - B1, 2) + Math.Pow(Y - B2, 2)
The second equations reflects the fact, that the Point P has a distance d (= 5) from Point A
Math.Pow(X - A1, 2) + Math.Pow(Y - A2, 2) + Math.Pow(Z, 2) = Math.Pow(d, 2)
The second equations reflects the fact, that the Point P has a distance d (= 5) from Point B
Math.Pow(X - B1, 2) + Math.Pow(Y - B2, 2) + Math.Pow(Z, 2) = Math.Pow(d, 2)
This leaves you with a system of quadratic equations. You can now solve it by either solving equation 1 for X and inserting into equation 2 and solving this equation for Y then inserting the terms for X and Y into equation 3, in this manner you can solve for Z. Now you go the same way back, inserting the solution for Z in equation 2 and solving for Y and finally inserting the solution for Y and Z in equation 1 solving for X. (This might sound confusing at first, here is a more detailed description of the approach with an easier hands-on example)
An example how a system of quadratic equations is solved in C# can be found here.
Note:
The distance of 5 can be thought of as a sphere around your points A and B. In 2D this would result in 2 solutions where the circles cross each other. In 3D, as you can see in the picture, this will result in an infinite number of possible solutions, since the 2 spheres overlap, resulting in a circle.
This might help you to choose an appropriate approach to your task, since you will need to add at least one more condition to get the specific coordinates of a point.
Update:
The task now changed to finding the best escape route, given two
attackers. One on either side of our player.
Without caring much about the values of the distances, it is obvious, that we want to escape orthogonal to the connection of Attacker 1 (A) and Attacker 2 (B), to maximize the distance to both:
The vector for the escape route is thus given by the scalar product:
(A1-B1) * (X-0.5*(B1-A1)) + (A2-B2) * (Y-0.5*(B2-A2)) = 0
Since we only care about the direction to escape you can set X or Y as you like and solve for the other variable.
In case you do care, introduce a second equation that includes the condition you care about, such as length of your new vector and solve for both X and Y
With the help of mrk's answer the result given in the form of C# and less mathematics.
public static Vector2 GetEscapeVector(this float distance, Vector2 one, Vector2 two)
{
var center = (one + two) / 2;
// rotate by 90° which equals to 1.5708F radians
var rotated = Vector2.Transform(center, Matrix3x2.CreateRotation(1.5708F));
var escapeVector = Vector2.Normalize(rotated) * distance;
return escapeVector;
}
Is later used like this:
currentPosition + 20F.GetEscapeVector(one, two)
or
currentPosition - 20F.GetEscapeVector(one, two)
Pythagorean theorem:
The squared distance of a point (x,y,z) to PositionA is
(x-3)² + (y-2)² + z²
Similarly, the squared distance to PositionB is
(x-5)² + (y-4)² + z²
You know that both are equal to 5²
Can you take it from there?
If your problem is limited to 2D this method does what you're looking for:
private Tuple<PointF, PointF> SameDistancePoints(PointF p1, PointF p2, float distance)
{
// Calculate the coefficients for the equation
// x^2 + y^2 + a1x + b1y + c = 0
// which represents the circle of all the points that are at distance d from p1:
// (x-p1.X)^2 + (y-p1.Y)^2 = distance^2
// which is
// x^2 -2p1.X*x + (p1.X)^2 + y^2 -2p1.Y*y + (p1.Y)^2 - distance^2 = 0
float a1 = -2 * p1.X;
float b1 = -2 * p1.Y;
float c1 = (float)Math.Pow(p1.X, 2) + (float)Math.Pow(p1.Y, 2) - (float)Math.Pow(distance, 2);
// do the same for p2
float a2 = -2 * p2.X;
float b2 = -2 * p2.Y;
float c2 = (float)Math.Pow(p2.X, 2) + (float)Math.Pow(p2.Y, 2) - (float)Math.Pow(distance, 2);
// Now we have the system with the 2 equations:
// x^2 + y^2 + a1x + b1y + c1 = 0
// x^2 + y^2 + a2x + b2y + c2 = 0
// subtracting the second equation from the first we get
// (a1-a2)x + (b1-b2)y + c1 - c2 = 0
// from which
// y = (c2-c1)/(b1-b2) - (a1-a2)x/(b1-b2)
// y = d - ex
float d = (c2 - c1) / (b1 - b2);
float e = (a1 - a2) / (b1 - b2);
// replacing the last equation in the first one of the system:
// x^2 + (d-ex)^2 + a1x + b1(d-ex) + c1 = 0;
// x^2 + d^2 - 2dex +(ex)^2 + a1x + b1d - b1ex + c1 = 0
// (1+e^2)x^2 + (a1-2de-b1e)x + d^2 + b1d + c1 = 0
// which can be written as
// a3x^2 + b3x + c3 = 0
// where
float a3 = 1 + (float)Math.Pow(e, 2);
float b3 = a1 - 2 * d * e - b1 * e;
float c3 = (float)Math.Pow(d, 2) + b1 * d + c1;
// now it's simlple
float delta = (float)Math.Pow(b3, 2) - 4 * a3 * c3;
if (delta < 0)
return null;
float x1 = (-b3 + (float)Math.Sqrt(delta)) / (2 * a3);
float y1 = d - e * x1;
float x2 = (-b3 - (float)Math.Sqrt(delta)) / (2 * a3);
float y2 = d - e * x2;
return new Tuple<PointF, PointF>(new PointF(x1, y1), new PointF(x2, y2));
}

Returning list that satisfy conditions

I have two lists filled with (x, y) data points; one of the lists being the main list and the other being a list I want to compare against.
So for example, in the main list I try to find "gaps" in the data; say I'm looking at two x data points x1 = 21 and x2 = 24. The difference between those two points is greater than deltaX = 1. So what I'd like to do, is look through the second list, and find all data points between x1 = 21 and x2 = 24 to "fill in the gap".
I have something like this:
double diffX = Math.Abs(datapoints1[i + 1].X - datapoints1[i].X);
if (diffX > deltaX)
{
datapoints1.AddRange(LookBetween(datapoints1[i + 1].X, datapoints1[i].X);
}
.
.
.
private IEnumerable<PointXY> LookBetween(double upperLimit, double lowerLimit)
{
return datapoints2.Where(x => x.X < upperLimit && x.X > lowerLimit);
}
LookBetween seems to return a boolean because nothing gets added to the main list. How can I get it to return a list of the x, y values that match the criteria based on the gap of x?
It seems like you're trying to do a pairwise iteration through neighbors in datapoints1.
Using the function linked above, you might have something like:
var pairs = datapoints1.Pairwise((d1, d2) => new
{
x1 = d1.X,
x2 = d2.X,
diffX = Math.Abs(d1.X - d2.X),
});
var gaps = from pair in pairs
where pair.diffX > deltaX
from dp2 in datapoints2
where pair.x1 < dp2.X && dp2.X < pair.x2
select dp2;
Or using your LookBetween function...
var gaps = from pair in pairs
where pair.diffX > deltaX
from dp2 in LookBetween(pair.x2, pair.x1)
select dp2;

B-spline algorithm

I have code for drawing Bezier curves. Is it posible to modify this code for drawing B-Spline curves?
Here is my code using DeCasteljau algorithm:
private Point getPoint(int r, int i, double t)
{
if (r == 0) return points[i];
Point p1 = getPoint(r - 1, i, t);
Point p2 = getPoint(r - 1, i + 1, t);
return new Point((int)((1 - t) * p1.X + t * p2.X), (int)((1 - t) * p1.Y + t * p2.Y));
}
I found this code for B-Spline curves. It looks similar to my code, but I have XY points and there are only numbers. I don't know how to modify my code. I tried something but it doesn't work.
private double BasisFunction(int k, int i, ParameterCollection u, double t){
if(k==0)
{
if((u[i]<=t) && (t<=u[i+1]))
return 1;
else
return 0;
}
else
{
double memb1, memb2;
if(u[i+k]==u[i])
memb1 = 0;
else
memb1 = ((t-u[i])/(u[i+k]-u[i]))*BasisFunction(k-1, i, u, t);
if(u[i+k+1]==u[i+1])
memb2 = 0;
else
memb2 = ((u[i+k+1]-t)/(u[i+k+1]-u[i+1]))*BasisFunction(k-1, i+1, u, t);
return memb1+memb2;
}
}
Please help.
The function BasisFunction() is for computing the value of B-spline basis function N(n,i)(t), where n is degree and i ranges from 0 to (m-1) with m is the number of control points. So, to use this function, you need to define the following for your B-spline:
degree.
m control points, denoting them as P[i][2] with i=0~(m-1)
knot sequence. This is the input "ParameterCollection" to the BasisFunction. You need to have (m+degree+1) knots in the knot sequence and the knot values need to be monotonically non-decreasing. An example of knot sequence for degree 3 B-spline with 5 control points is [0,0,0,0,u0,1,1,1,1], where u0 is any value between [0,1].
Then you can evaluate any point on the B-spline curve at parameter t by something like:
double point[2]={0.0}; // point on the B-spline curve
for (int ii=0; ii < m; ii++) // loop thru all control points
{
double basisVal = BasisFunction(degree, ii, knotSequence, t);
point[0] += P[ii][0]*basisVal;
point[1] += P[ii][1]*basisVal;
}

How to "flatten" or "index" 3D-array in 1D array?

I am trying to flatten 3D array into 1D array for "chunk" system in my game. It's a 3D-block game and basically I want the chunk system to be almost identical to Minecraft's system (however, this isn't Minecraft clone by any measure). In my previous 2D-games I have accessed the flattened array with following algorithm:
Tiles[x + y * WIDTH]
However, this obviously doesn't work with 3D since it's missing the Z-axis. I have no idea how to implement this sort of algorithm in 3D-space. Width, height and depth are all constants (and width is just as large as height).
Is it just x + y*WIDTH + Z*DEPTH ? I am pretty bad with math and I am just beginning 3D-programming so I am pretty lost :|
PS. The reason for this is that I am looping and getting stuff by index from it quite a lot. I know that 1D arrays are faster than multi-dimensional arrays (for reasons I cant remember :P ). Even though this may not be necessary, I want as good performance as possible :)
Here is a solution in Java that gives you both:
from 3D to 1D
from 1D to 3D
Below is a graphical illustration of the path I chose to traverse the 3D matrix, the cells are numbered in their traversal order:
Conversion functions:
public int to1D( int x, int y, int z ) {
return (z * xMax * yMax) + (y * xMax) + x;
}
public int[] to3D( int idx ) {
final int z = idx / (xMax * yMax);
idx -= (z * xMax * yMax);
final int y = idx / xMax;
final int x = idx % xMax;
return new int[]{ x, y, z };
}
The algorithm is mostly the same. If you have a 3D array Original[HEIGHT, WIDTH, DEPTH] then you could turn it into Flat[HEIGHT * WIDTH * DEPTH] by
Flat[x + WIDTH * (y + DEPTH * z)] = Original[x, y, z]
As an aside, you should prefer arrays of arrays over multi-dimensional arrays in .NET. The performance differences are significant
I think the above needs a little correction. Lets say you have a HEIGHT of 10, and a WIDTH of 90, single dimensional array will be 900. By the above logic, if you are at the last element on the array 9 + 89*89, obviously this is greater than 900. The correct algorithm is:
Flat[x + HEIGHT* (y + WIDTH* z)] = Original[x, y, z], assuming Original[HEIGHT,WIDTH,DEPTH]
Ironically if you the HEIGHT>WIDTH you will not experience an overflow, just complete bonkers results ;)
x + y*WIDTH + Z*WIDTH*DEPTH. Visualize it as a rectangular solid: first you traverse along x, then each y is a "line" width steps long, and each z is a "plane" WIDTH*DEPTH steps in area.
You're almost there. You need to multiply Z by WIDTH and DEPTH:
Tiles[x + y*WIDTH + Z*WIDTH*DEPTH] = elements[x][y][z]; // or elements[x,y,z]
TL;DR
The correct answer can be written various ways, but I like it best when it can be written in a way that is very easy to understand and visualize. Here is the exact answer:
(width * height * z) + (width * y) + x
TS;DR
Visualize it:
someNumberToRepresentZ + someNumberToRepresentY + someNumberToRepresentX
someNumberToRepresentZ indicates which matrix we are on (depth). To know which matrix we are on, we have to know how big each matrix is. A matrix is 2d sized as width * height, simple. The question to ask is "how many matrices are before the matrix I'm on?" The answer is z:
someNumberToRepresentZ = width * height * z
someNumberToRepresentY indicates which row we are on (height). To know which row we are on, we have to know how big each row is: Each row is 1d, sized as width. The question to ask is "how many rows are before the row I'm on?". The answer is y:
someNumberToRepresentY = width * y
someNumberToRepresentX indicates which column we are on (width). To know which column we are on we simply use x:
someNumberToRepresentX = x
Our visualization then of
someNumberToRepresentZ + someNumberToRepresentY + someNumberToRepresentX
Becomes
(width * height * z) + (width * y) + x
The forward and reverse transforms of Samuel Kerrien above are almost correct. A more concise (R-based) transformation maps are included below with an example (the "a %% b" is the modulo operator representing the remainder of the division of a by b):
dx=5; dy=6; dz=7 # dimensions
x1=1; y1=2; z1=3 # 3D point example
I = dx*dy*z1+dx*y1+x1; I # corresponding 2D index
# [1] 101
x= I %% dx; x # inverse transform recovering the x index
# [1] 1
y = ((I - x)/dx) %% dy; y # inverse transform recovering the y index
# [1] 2
z= (I-x -dx*y)/(dx*dy); z # inverse transform recovering the z index
# [1] 3
Mind the division (/) and module (%%) operators.
The correct Algorithm is:
Flat[ x * height * depth + y * depth + z ] = elements[x][y][z]
where [WIDTH][HEIGHT][DEPTH]
To better understand description of 3D array in 1D array would be ( I guess Depth in best answer is meant Y size)
IndexArray = x + y * InSizeX + z * InSizeX * InSizeY;
IndexArray = x + InSizeX * (y + z * InSizeY);
m[x][y][z] = data[xYZ + yZ + z]
x-picture:
0-YZ
.
.
x-YZ
y-picture
0-Z
.
.
.
y-Z
summing up, it should be : targetX*YZ + targetY*Z + targetZ
In case, somebody is interested to flatten an nD (2D, 3D, 4D, ...) array to 1D, I wrote the below code. For example, if the size of the array in different dimensions is stored in the sizes array:
# pseudo code
sizes = {size_x, size_y, size_z,...};
This recursive function gives you the series of {1, size_x, size_x*size_y, size_x*size_y*size_z, ...}
// i: number of the term
public int GetCoeff(int i){
if (i==0)
return 1;
return sizes[i-1]*GetCoeff(i-1);
}
So, we have to multiply nD indexes by their corresponding series term and sum them to get {ix + iy*size_x + iz*size_x*size_y, ...}:
// indexNd: {ix, iy, iz, ...}
public int GetIndex1d(int[] indexNd){
int sum =0;
for (var i=0; i<indexNd.Length;i++)
sum += indexNd[i]*GetCoeff(i);
return sum;
}
In this code I assumed, the nD array is contiguous in memory along firstly x, then y, z, ... . So probably you call your array-like arr[z,y,x]. But, if you call them the other way, arr[x,y,z] then z is the fastest index and we like to calculate iz + iy*size_z + ix* size_z*size_y. In this case, the below function gives us the series {1, size_z, size_z*size_y, ...}:
// Dims is dimension of array, like 3 for 3D
public int GetReverseCoeff(int i){
if (i==0)
return 1;
return sizes[Dims-i]*GetReverseCoeff(i-1);
}
The coefficients are stored in the right order:
public void SetCoeffs(){
for (int i=0;i<Dims;i++)
coeffs[Dims-i-1] = GetReverseCoeff(i);
}
The 1D index is calculated the same as before except coeffs array is used:
// indexNd: {ix, iy, iz, ...}
public int GetIndex1d(int[] indexNd){
int sum =0;
for (var i=0; i<indexNd.Length;i++)
sum += indexNd[i]*coeffs[i];
return sum;
}
Samuel Kerrien's answer to python :
def to1D(crds,dims):
x,y,z=crds
xMax,yMax,zMax=dims
return (z * xMax * yMax) + (y * xMax) + x
def to3D(idx,dims):
xMax,yMax,zMax=dims
z = idx // (xMax * yMax)
idx -= (z * xMax * yMax)
y = idx // xMax
x = idx % xMax
return x, y, z

How to tell whether a point is to the right or left side of a line

I have a set of points. I want to separate them into 2 distinct sets. To do this, I choose two points (a and b) and draw an imaginary line between them. Now I want to have all points that are left from this line in one set and those that are right from this line in the other set.
How can I tell for any given point z whether it is in the left or in the right set? I tried to calculate the angle between a-z-b – angles smaller than 180 are on the right hand side, greater than 180 on the left hand side – but because of the definition of ArcCos, the calculated angles are always smaller than 180°. Is there a formula to calculate angles greater than 180° (or any other formula to chose right or left side)?
Try this code which makes use of a cross product:
public bool isLeft(Point a, Point b, Point c){
return ((b.X - a.X)*(c.Y - a.Y) - (b.Y - a.Y)*(c.X - a.X)) > 0;
}
Where a = line point 1; b = line point 2; c = point to check against.
If the formula is equal to 0, the points are colinear.
If the line is horizontal, then this returns true if the point is above the line.
Use the sign of the determinant of vectors (AB,AM), where M(X,Y) is the query point:
position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax))
It is 0 on the line, and +1 on one side, -1 on the other side.
You look at the sign of the determinant of
| x2-x1 x3-x1 |
| y2-y1 y3-y1 |
It will be positive for points on one side, and negative on the other (and zero for points on the line itself).
The vector (y1 - y2, x2 - x1) is perpendicular to the line, and always pointing right (or always pointing left, if you plane orientation is different from mine).
You can then compute the dot product of that vector and (x3 - x1, y3 - y1) to determine if the point lies on the same side of the line as the perpendicular vector (dot product > 0) or not.
Using the equation of the line ab, get the x-coordinate on the line at the same y-coordinate as the point to be sorted.
If point's x > line's x, the point is to the right of the line.
If point's
x < line's x, the point is to the left of the line.
If point's x == line's x, the point is on the line.
I implemented this in java and ran a unit test (source below). None of the above solutions work. This code passes the unit test. If anyone finds a unit test that does not pass, please let me know.
Code: NOTE: nearlyEqual(double,double) returns true if the two numbers are very close.
/*
* #return integer code for which side of the line ab c is on. 1 means
* left turn, -1 means right turn. Returns
* 0 if all three are on a line
*/
public static int findSide(
double ax, double ay,
double bx, double by,
double cx, double cy) {
if (nearlyEqual(bx-ax,0)) { // vertical line
if (cx < bx) {
return by > ay ? 1 : -1;
}
if (cx > bx) {
return by > ay ? -1 : 1;
}
return 0;
}
if (nearlyEqual(by-ay,0)) { // horizontal line
if (cy < by) {
return bx > ax ? -1 : 1;
}
if (cy > by) {
return bx > ax ? 1 : -1;
}
return 0;
}
double slope = (by - ay) / (bx - ax);
double yIntercept = ay - ax * slope;
double cSolution = (slope*cx) + yIntercept;
if (slope != 0) {
if (cy > cSolution) {
return bx > ax ? 1 : -1;
}
if (cy < cSolution) {
return bx > ax ? -1 : 1;
}
return 0;
}
return 0;
}
Here's the unit test:
#Test public void testFindSide() {
assertTrue("1", 1 == Utility.findSide(1, 0, 0, 0, -1, -1));
assertTrue("1.1", 1 == Utility.findSide(25, 0, 0, 0, -1, -14));
assertTrue("1.2", 1 == Utility.findSide(25, 20, 0, 20, -1, 6));
assertTrue("1.3", 1 == Utility.findSide(24, 20, -1, 20, -2, 6));
assertTrue("-1", -1 == Utility.findSide(1, 0, 0, 0, 1, 1));
assertTrue("-1.1", -1 == Utility.findSide(12, 0, 0, 0, 2, 1));
assertTrue("-1.2", -1 == Utility.findSide(-25, 0, 0, 0, -1, -14));
assertTrue("-1.3", -1 == Utility.findSide(1, 0.5, 0, 0, 1, 1));
assertTrue("2.1", -1 == Utility.findSide(0,5, 1,10, 10,20));
assertTrue("2.2", 1 == Utility.findSide(0,9.1, 1,10, 10,20));
assertTrue("2.3", -1 == Utility.findSide(0,5, 1,10, 20,10));
assertTrue("2.4", -1 == Utility.findSide(0,9.1, 1,10, 20,10));
assertTrue("vertical 1", 1 == Utility.findSide(1,1, 1,10, 0,0));
assertTrue("vertical 2", -1 == Utility.findSide(1,10, 1,1, 0,0));
assertTrue("vertical 3", -1 == Utility.findSide(1,1, 1,10, 5,0));
assertTrue("vertical 3", 1 == Utility.findSide(1,10, 1,1, 5,0));
assertTrue("horizontal 1", 1 == Utility.findSide(1,-1, 10,-1, 0,0));
assertTrue("horizontal 2", -1 == Utility.findSide(10,-1, 1,-1, 0,0));
assertTrue("horizontal 3", -1 == Utility.findSide(1,-1, 10,-1, 0,-9));
assertTrue("horizontal 4", 1 == Utility.findSide(10,-1, 1,-1, 0,-9));
assertTrue("positive slope 1", 1 == Utility.findSide(0,0, 10,10, 1,2));
assertTrue("positive slope 2", -1 == Utility.findSide(10,10, 0,0, 1,2));
assertTrue("positive slope 3", -1 == Utility.findSide(0,0, 10,10, 1,0));
assertTrue("positive slope 4", 1 == Utility.findSide(10,10, 0,0, 1,0));
assertTrue("negative slope 1", -1 == Utility.findSide(0,0, -10,10, 1,2));
assertTrue("negative slope 2", -1 == Utility.findSide(0,0, -10,10, 1,2));
assertTrue("negative slope 3", 1 == Utility.findSide(0,0, -10,10, -1,-2));
assertTrue("negative slope 4", -1 == Utility.findSide(-10,10, 0,0, -1,-2));
assertTrue("0", 0 == Utility.findSide(1, 0, 0, 0, -1, 0));
assertTrue("1", 0 == Utility.findSide(0,0, 0, 0, 0, 0));
assertTrue("2", 0 == Utility.findSide(0,0, 0,1, 0,2));
assertTrue("3", 0 == Utility.findSide(0,0, 2,0, 1,0));
assertTrue("4", 0 == Utility.findSide(1, -2, 0, 0, -1, 2));
}
First check if you have a vertical line:
if (x2-x1) == 0
if x3 < x2
it's on the left
if x3 > x2
it's on the right
else
it's on the line
Then, calculate the slope: m = (y2-y1)/(x2-x1)
Then, create an equation of the line using point slope form: y - y1 = m*(x-x1) + y1. For the sake of my explanation, simplify it to slope-intercept form (not necessary in your algorithm): y = mx+b.
Now plug in (x3, y3) for x and y. Here is some pseudocode detailing what should happen:
if m > 0
if y3 > m*x3 + b
it's on the left
else if y3 < m*x3 + b
it's on the right
else
it's on the line
else if m < 0
if y3 < m*x3 + b
it's on the left
if y3 > m*x3+b
it's on the right
else
it's on the line
else
horizontal line; up to you what you do
I wanted to provide with a solution inspired by physics.
Imagine a force applied along the line and you are measuring the torque of the force about the point. If the torque is positive (counterclockwise) then the point is to the "left" of the line, but if the torque is negative the point is the "right" of the line.
So if the force vector equals the span of the two points defining the line
fx = x_2 - x_1
fy = y_2 - y_1
you test for the side of a point (px,py) based on the sign of the following test
var torque = fx*(py-y_1)-fy*(px-x_1)
if torque>0 then
"point on left side"
else if torque <0 then
"point on right side"
else
"point on line"
end if
Assuming the points are (Ax,Ay) (Bx,By) and (Cx,Cy), you need to compute:
(Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax)
This will equal zero if the point C is on the line formed by points A and B, and will have a different sign depending on the side. Which side this is depends on the orientation of your (x,y) coordinates, but you can plug test values for A,B and C into this formula to determine whether negative values are to the left or to the right.
basically, I think that there is a solution which is much easier and straight forward, for any given polygon, lets say consist of four vertices(p1,p2,p3,p4), find the two extreme opposite vertices in the polygon, in another words, find the for example the most top left vertex (lets say p1) and the opposite vertex which is located at most bottom right (lets say ). Hence, given your testing point C(x,y), now you have to make double check between C and p1 and C and p4:
if cx > p1x AND cy > p1y ==> means that C is lower and to right of p1
next
if cx < p2x AND cy < p2y ==> means that C is upper and to left of p4
conclusion, C is inside the rectangle.
Thanks :)
#AVB's answer in ruby
det = Matrix[
[(x2 - x1), (x3 - x1)],
[(y2 - y1), (y3 - y1)]
].determinant
If det is positive its above, if negative its below. If 0, its on the line.
Here's a version, again using the cross product logic, written in Clojure.
(defn is-left? [line point]
(let [[[x1 y1] [x2 y2]] (sort line)
[x-pt y-pt] point]
(> (* (- x2 x1) (- y-pt y1)) (* (- y2 y1) (- x-pt x1)))))
Example usage:
(is-left? [[-3 -1] [3 1]] [0 10])
true
Which is to say that the point (0, 10) is to the left of the line determined by (-3, -1) and (3, 1).
NOTE: This implementation solves a problem that none of the others (so far) does! Order matters when giving the points that determine the line. I.e., it's a "directed line", in a certain sense. So with the above code, this invocation also produces the result of true:
(is-left? [[3 1] [-3 -1]] [0 10])
true
That's because of this snippet of code:
(sort line)
Finally, as with the other cross product based solutions, this solution returns a boolean, and does not give a third result for collinearity. But it will give a result that makes sense, e.g.:
(is-left? [[1 1] [3 1]] [10 1])
false
Issues with the existing solution:
While I found Eric Bainville's answer to be correct, I found it entirely inadequate to comprehend:
How can two vectors have a determinant? I thought that applied to matrices?
What is sign?
How do I convert two vectors into a matrix?
position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax))
What is Bx?
What is Y? Isn't Y meant to be a Vector, rather than a scalar?
Why is the solution correct - what is the reasoning behind it?
Moreover, my use case involved complex curves rather than a simple line, hence it requires a little re-jigging:
Reconstituted Answer
Point a = new Point3d(ax, ay, az); // point on line
Point b = new Point3d(bx, by, bz); // point on line
If you want to see whether your points are above/below a curve, then you would need to get the first derivative of the particular curve you are interested in - also known as the tangent to the point on the curve. If you can do so, then you can highlight your points of interest. Of course, if your curve is a line, then you just need the point of interest without the tangent. The tangent IS the line.
Vector3d lineVector = curve.GetFirstDerivative(a); // where "a" is a point on the curve. You may derive point b with a simple displacement calculation:
Point3d b = new Point3d(a.X, a.Y, a.Z).TransformBy(
Matrix3d.Displacement(curve.GetFirstDerivative(a))
);
Point m = new Point3d(mx, my, mz) // the point you are interested in.
The Solution:
return (b.X - a.X) * (m.Y - a.Y) - (b.Y - a.Y) * (m.X - a.X) < 0; // the answer
Works for me! See the proof in the photo above. Green bricks satisfy the condition, but the bricks outside were filtered out! In my use case - I only want the bricks that are touching the circle.
Theory behind the answer
I will return to explain this. Someday. Somehow...
An alternative way of getting a feel of solutions provided by netters is to understand a little geometry implications.
Let pqr=[P,Q,R] are points that forms a plane that is divided into 2 sides by line [P,R]. We are to find out if two points on pqr plane, A,B, are on the same side.
Any point T on pqr plane can be represented with 2 vectors: v = P-Q and u = R-Q, as:
T' = T-Q = i * v + j * u
Now the geometry implications:
i+j =1: T on pr line
i+j <1: T on Sq
i+j >1: T on Snq
i+j =0: T = Q
i+j <0: T on Sq and beyond Q.
i+j: <0 0 <1 =1 >1
---------Q------[PR]--------- <== this is PQR plane
^
pr line
In general,
i+j is a measure of how far T is away from Q or line [P,R], and
the sign of i+j-1 implicates T's sideness.
The other geometry significances of i and j (not related to this solution) are:
i,j are the scalars for T in a new coordinate system where v,u are the new axes and Q is the new origin;
i, j can be seen as pulling force for P,R, respectively. The larger i, the farther T is away from R (larger pull from P).
The value of i,j can be obtained by solving the equations:
i*vx + j*ux = T'x
i*vy + j*uy = T'y
i*vz + j*uz = T'z
So we are given 2 points, A,B on the plane:
A = a1 * v + a2 * u
B = b1 * v + b2 * u
If A,B are on the same side, this will be true:
sign(a1+a2-1) = sign(b1+b2-1)
Note that this applies also to the question: Are A,B in the same side of plane [P,Q,R], in which:
T = i * P + j * Q + k * R
and i+j+k=1 implies that T is on the plane [P,Q,R] and the sign of i+j+k-1 implies its sideness. From this we have:
A = a1 * P + a2 * Q + a3 * R
B = b1 * P + b2 * Q + b3 * R
and A,B are on the same side of plane [P,Q,R] if
sign(a1+a2+a3-1) = sign(b1+b2+b3-1)
equation of line is y-y1 = m(x-x1)
here m is y2-y1 / x2-x1
now put m in equation and put condition on y < m(x-x1) + y1 then it is left side point
eg.
for i in rows:
for j in cols:
if j>m(i-a)+b:
image[i][j]=0
A(x1,y1) B(x2,y2) a line segment with length L=sqrt( (y2-y1)^2 + (x2-x1)^2 )
and a point M(x,y)
making a transformation of coordinates in order to be the point A the new start and B a point of the new X axis
we have the new coordinates of the point M
which are
newX = ((x-x1)(x2-x1)+(y-y1)(y2-y1)) / L
from (x-x1)*cos(t)+(y-y1)*sin(t) where cos(t)=(x2-x1)/L, sin(t)=(y2-y1)/L
newY = ((y-y1)(x2-x1)-(x-x1)(y2-y1)) / L
from (y-y1)*cos(t)-(x-x1)*sin(t)
because "left" is the side of axis X where the Y is positive, if the newY (which is the distance of M from AB) is positive, then it is on the left side of AB (the new X axis)
You may omit the division by L (allways positive), if you only want the sign

Categories