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
Related
Hello, I've got a question which I cannot solve so I need a bit help.
In the picture above you can see an Oriented Bounding Box specified by 4 points (A, B, C, D). There is also a point in space called P. If I cast a ray from P against the OBB the ray is going to intersect the OBB at some point. This point of intersection is called Q in the picture. By the way the ray is always going to be x-axis aligned which means its directional vector is either (1, 0) or (-1,0) if normalized. My goal is to find the point of intersection - Q. Is there a way (if possible computationaly inexpensive) to do so?
Thanks in advance.
One way to do this is to consider each side of the bounding box to be a linear equation of the form y = ax + b, where a is the slope and b is the y-intercept. Then consider the ray from P to be an equation of the form y = c, where c is a constant. Then compare this equation to each of the four other equations to see where it intersects each one. One of these intersections will be our Q, if a Q exists; it's possible that the ray will miss the bounding box entirely. We will need to do a few checks:
Firstly, eliminate all potential Q's that are on the wrong side of P.
Secondly, check each of the four intersections to make sure they are within the bounds of the lines that they represent, and eliminate the ones that are not.
Finally, if any potential Q's remain, the one closest to P will be our Q. If no potential Q's remain, this means that the ray from P misses the bounding box entirely.
For example...
The line drawn from D to B would have a slope equal to (B.y - D.y) / (B.x - D.x) and a y-intercept equal to B.y - B.x * slope. Then the entire equation is y = (B.y - D.y) / (B.x - D.x) * x + B.y - B.x * (B.y - D.y) / (B.x - D.x). Set this equation equal to y = P.y and solve for x:
x = (P.y - B.y + B.x*(B.y - D.y)/(B.x - D.x))*(B.x - D.x)/(B.y - D.y)
The result of this equation will give you the x-value of the intersection. The y-value is P.y. Do this for each of the other 3 lines as well: A-B, A-C, C-D. I will refer to these intersections as Q(D-B), Q(A-B), Q(A-C), and Q(C-D) respectively.
Next, eliminate candidate-Q's that are on the wrong side of P. In our example, this eliminates Q(A-B), since it is way off to the right side of the screen. Mathematically, Q(A-B).x > P.x.
Then eliminate all candidate-Q's that are not on the line they represent. We can do this check by finding the lowest and highest x-values and y-values given by the two points that represent the line. For example, to check that Q(A-C) is on the line A-C, check that C.x <= Q(A-C).x <= A.x and C.y <= Q(A-C).y <= A.y. Q(A-C) passes the test, as well as Q(D-B). However, Q(C-D) does not pass, as it is way off to the left side of the screen, far from the box. Therefore, Q(C-D) is eliminated from candidacy.
Finally, of the two points that remain, Q(A-C) and Q(D-B), we choose Q(D-B) to be our winner, because it is closest to P.
We now can say that the ray from P hits the bounding box at Q(D-B).
Of course, when you implement this in code, you will need to account for divisions by zero. If a line is perfectly vertical, there does not exist a point-slope equation of the line, so you will need to create a separate formula for this case. If a line is perfectly horizontal, it's respective candidate-Q should be automatically eliminated from candidacy, as the ray from P will never touch it.
Edit:
It would be more efficient to only do this process with lines whose two points are on vertically opposite sides of point P. If both points of a line are above P, or they are both below P, they would be eliminated from candidacy from the beginning.
Find the two sides that straddle p on Y. (Test of the form (Ya < Yp) != (Yb < Yp)).
Then compute the intersection points of the horizontal by p with these two sides, and keep the first to the left of p.
If the ray points to the left(right) then it must intersect an edge that connects to the point in the OOB with max(min) x-value. We can determine which edge by simply comparing the y-value of the ray with the y value of the max(min) point and its neighbors. We also need to consider OBBs that are actually axis-aligned, and thus have two points with equal max(min) x-value. Once we have the edge it's simple to confirm that the ray does in fact intersect the OBB and calculate its x-value.
Here's some Java code to illustrate (ideone):
static double nearestX(Point[] obb, int y, int dir)
{
// Find min(max) point
int n = 0;
for(int i=1; i<4; i++)
if((obb[n].x < obb[i].x) == (dir == -1)) n = i;
// Determine next or prev edge
int next = (n+1) % 4;
int prev = (n+3) % 4;
int nn;
if((obb[n].x == obb[next].x) || (obb[n].y < y) == (obb[n].y < obb[next].y))
nn = next;
else
nn = prev;
// Check that the ray intersects the OBB
if(Math.abs(y) > Math.abs(obb[nn].y)) return Double.NaN;
// Standard calculation of x from y for line segment
return obb[n].x + (y-obb[n].y)*(obb[nn].x-obb[n].x)/(obb[nn].y-obb[n].y);
}
Test:
public static void main(String[] args)
{
test("Diamond", new Point[]{p(0, -2), p(2, 0), p(0, 2), p(-2,0)});
test("Square", new Point[]{p(-2, -2), p(2, -2), p(2, 2), p(-2,2)});
}
static void test(String label, Point[] obb)
{
System.out.println(label + ": " + Arrays.toString(obb));
for(int dir : new int[] {-1, 1})
{
for(int y : new int[] {-3, -2, -1, 0, 1, 2, 3})
System.out.printf("(% d, % d) = %.0f\n", y , dir, nearestX(obb, y, dir));
System.out.println();
}
}
Output:
Diamond: [(0,-2), (2,0), (0,2), (-2,0)]
(-3, -1) = NaN
(-2, -1) = 0
(-1, -1) = 1
( 0, -1) = 2
( 1, -1) = 1
( 2, -1) = 0
( 3, -1) = NaN
(-3, 1) = NaN
(-2, 1) = 0
(-1, 1) = -1
( 0, 1) = -2
( 1, 1) = -1
( 2, 1) = 0
( 3, 1) = NaN
Square: [(-2,-2), (2,-2), (2,2), (-2,2)]
(-3, -1) = NaN
(-2, -1) = 2
(-1, -1) = 2
( 0, -1) = 2
( 1, -1) = 2
( 2, -1) = 2
( 3, -1) = NaN
(-3, 1) = NaN
(-2, 1) = -2
(-1, 1) = -2
( 0, 1) = -2
( 1, 1) = -2
( 2, 1) = -2
( 3, 1) = NaN
A line L lies on the plane: x + y + z + 1 = 0, the line direction is (a, 1,0) for some value
a ∈ ℝ:
I don't know how to answer this questions, would appreciate your help:
Can we determine the line equation from the given details above? How many possible lines exist?
If the line also passes through the point (1, −1, −1), can we determine the line equation? How many lines exist?
If point (x0, y0, z0) belongs to the plane, then point (x0 + a , y0 + 1, z0) should belong to this plane too, so
x0 + a + y0 + 1 + z0 = 1
(x0 + y0 + z0) + a + 1 = 1
1 + a + 1 = 1
a = -1
Another way - line belonging the plane must be perpendicular to the plane normal, so dot product of normal and direction vector must be zero:
1 * a + 1 * 1 + 1 * 0 = 0
a = -1
We know only direction vector, there is infinite number of parallel lines in given plane.
If we fix one point, we can get unique line
base = (1, −1, −1)
dir = (-1, 1, 0)
L = (1, −1, −1) + t * (-1, 1, 0)
This is parametric equation of the line
I am trying to determine if a point lies between two bearings from a central point.
The diagram below attempts to explain things
I have a central point labelled A
I have two points (labelled B & C) which provide the boundaries of the search area (based on bearing only - there is no distance element required).
I'm trying to determine if point D is within the sector formed by A-B and A-C
I've calculated the bearings from A to each B & C
In my real scenario the angle created between the bearings can be anything from 0 to 360.
There are some similar questions & answers
however in my case I'm not interested in restricting my search to the radius of a circle. And there seems to be some implementation issues around angle size and the location of the points in terms of clockwise vs counter-clockwise
It seems so simple in theory but my maths is clearly not up to scratch :(
Any advice or pseudo-code would be greatly appreciated.
Here would be my approach:
calculate first bearing angle X
calculate second bearing angle Y
calculate angle Z towards point D
if X < Z < Y, return true; otherwise, return false
In your example it looks like you'd calculate Z ~ 90deg and find 45 < 90 < 135 (is your picture wrong? is says 315).
You can use something like the "atan2" function in whatever language you're using. This is an extension of the basic arctangent function which takes not just the slope but both the rise and run and instead of returning an angle from only a 180-degree range, it returns the true angle from a 360-degree range. So
Z = atan2(Dy, Dx)
Should give you the angle (possibly in radians; be careful) that you can compare to your bearings to tell whether you're inside the search. Note that the order of X and Y matter since the order is what defines which of the two sections is in the search area (X to Y gives ~90 deg in your picture, but Y to X gives ~270 deg).
You can calculate and compare the cross products of the vectors (AB X BD), and (AC X CD).
if (AB X BD) > 0, you have a counter clock wise turn
if (AC X CD) < 0, you have a clock wise turn
If both above tests are true, then the point D is in the sector BAC
This allows you to completely avoid using expensive trig functions.
class Point:
"""small class for point arithmetic convenience
"""
def __init__(self, x: float = 0, y: float = 0) -> None:
self.x = x
self.y = y
def __sub__(self, other: 'Point') -> 'Vector':
return Vector(self.x - other.x, self.y - other.y)
class Vector:
"""small class for vector arithmetic convenience
"""
def __init__(self, x: float = 0, y: float = 0) -> None:
self.x = x
self.y = y
def cross(self, other: 'Vector') -> float:
return (self.x * other.y) - (self.y * other.x)
def in_sector(A: Point, B: Point, C: Point, D: Point) -> bool:
# construct vectors:
ab = B - A
bd = D - B
ac = C - A
cd = D - C
print(f'ab x bc = {ab.cross(bd)}, ac x cd = {ac.cross(cd)}')
return ab.cross(bd) > 0 and ac.cross(cd) < 0
if __name__ == '__main__':
A = Point(0, 0)
B = Point(1, 1)
C = Point(-1, 1)
D = Point(0, 1)
print(f'D in sector ABC: {in_sector(A, B, C, D)}', end='\n\n')
print(f'D in sector ACB: {in_sector(A, C, B, D)}') # inverting the sector definition so D is now outside
Output:
ab x bc = 1, ac x cd = -1
D in sector ABC: True
ab x bc = -1, ac x cd = 1
D in sector ACB: False
i'm programming a tree to divide the 2d-space.
therefore i need to split the current set of points in two equal parts according to a given angle. so the black line from the image attached below goes through the calculated median.
As we can see in the image, the line (defined by the given angle and the computed median) is far away from dividing the set in two equal parts. the line should cut the x-axis more to the right as now only ~25 points are on the left of the line and ~55 points on the right.
i already know, i have to use the angles of the point for my median computation.
my first approach for calculating the median was rotating all points by the given angle and then use their angles to the x-axis, but that's obviously not enough.
I'm not concretly stuck on the transformations i have to apply to the point in order to have a correct median computation, so any help/hint would be appreciated,
thanks a lot.
Normal form of line equation is
x * Cos(Theta) + y * Sin(Theta) - p = 0
where
Theta = Pi/2 + Fi
and Fi is your given angle.
You have to find appropriate p value to divide point set to two equal parts.
Signed distance from this line to point Q is
D = Q.X * Cos(Theta) + Q.Y * Sin(Theta) - p
Sort your point array by D[i] value and find median.
If point number is odd, get D[median] as p for line equation, otherwise you may choose any p value between D[leftmedian] and D[rightmedian]
Examples:
5 points, D = (-3, -1, 4, 5, 8) - get p = 4 (line passes through one point)
6 points, D = (-3, -1, 2, 5, 7, 9) - get any p in range ]2..5[ (not including these values)
Edit: two examples for odd and even point numbers:
code (Delphi):
var
Pt: TArray<TPoint>;
D: TArray<Double>;
N, i: Integer;
Fi, Theta, p: Double;
begin
Randomize;
N := 7 + Random(2); // number of points, 7 or 8
Fi := Pi / 4; //line slope angle
Theta := Pi / 2 + Fi;
SetLength(Pt, N); //point cloud
SetLength(D, N); //distance params
Canvas.FillRect(ClientRect);
//generate random points and draw them
for i := 0 to N - 1 do begin
Pt[i] := Point(Random(400), Random(400));
Canvas.Rectangle(Pt[i].X - 2, Pt[i].Y - 2, Pt[i].X + 3, Pt[i].Y + 3);
end;
//fill param array
for i := 0 to N - 1 do
D[i] := Pt[i].X * Cos(Theta) + Pt[i].Y * Sin(Theta);
//sort distance parameters
TArray.Sort<Double>(D);
//get line parameter as median of array
if Odd(N) then
p := D[N div 2]
else
p := (D[N div 2 - 1] + D[N div 2]) / 2;
//draw calculated line
Canvas.MoveTo(0, Round(p / Sin(Theta)));
Canvas.LineTo(500, Round((p - 500 * Cos(Theta)) / Sin(Theta)));
I have a square bitmap of a circle and I want to compute the normals of all the pixels in that circle as if it were a sphere of radius 1:
The sphere/circle is centered in the bitmap.
What is the equation for this?
Don't know much about how people program 3D stuff, so I'll just give the pure math and hope it's useful.
Sphere of radius 1, centered on origin, is the set of points satisfying:
x2 + y2 + z2 = 1
We want the 3D coordinates of a point on the sphere where x and y are known. So, just solve for z:
z = ±sqrt(1 - x2 - y2).
Now, let us consider a unit vector pointing outward from the sphere. It's a unit sphere, so we can just use the vector from the origin to (x, y, z), which is, of course, <x, y, z>.
Now we want the equation of a plane tangent to the sphere at (x, y, z), but this will be using its own x, y, and z variables, so instead I'll make it tangent to the sphere at (x0, y0, z0). This is simply:
x0x + y0y + z0z = 1
Hope this helps.
(OP):
you mean something like:
const int R = 31, SZ = power_of_two(R*2);
std::vector<vec4_t> p;
for(int y=0; y<SZ; y++) {
for(int x=0; x<SZ; x++) {
const float rx = (float)(x-R)/R, ry = (float)(y-R)/R;
if(rx*rx+ry*ry > 1) { // outside sphere
p.push_back(vec4_t(0,0,0,0));
} else {
vec3_t normal(rx,sqrt(1.-rx*rx-ry*ry),ry);
p.push_back(vec4_t(normal,1));
}
}
}
It does make a nice spherical shading-like shading if I treat the normals as colours and blit it; is it right?
(TZ)
Sorry, I'm not familiar with those aspects of C++. Haven't used the language very much, nor recently.
This formula is often used for "fake-envmapping" effect.
double x = 2.0 * pixel_x / bitmap_size - 1.0;
double y = 2.0 * pixel_y / bitmap_size - 1.0;
double r2 = x*x + y*y;
if (r2 < 1)
{
// Inside the circle
double z = sqrt(1 - r2);
.. here the normal is (x, y, z) ...
}
Obviously you're limited to assuming all the points are on one half of the sphere or similar, because of the missing dimension. Past that, it's pretty simple.
The middle of the circle has a normal facing precisely in or out, perpendicular to the plane the circle is drawn on.
Each point on the edge of the circle is facing away from the middle, and thus you can calculate the normal for that.
For any point between the middle and the edge, you use the distance from the middle, and some simple trig (which eludes me at the moment). A lerp is roughly accurate at some points, but not quite what you need, since it's a curve. Simple curve though, and you know the beginning and end values, so figuring them out should only take a simple equation.
I think I get what you're trying to do: generate a grid of depth data for an image. Sort of like ray-tracing a sphere.
In that case, you want a Ray-Sphere Intersection test:
http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter1.htm
Your rays will be simple perpendicular rays, based off your U/V coordinates (times two, since your sphere has a diameter of 2). This will give you the front-facing points on the sphere.
From there, calculate normals as below (point - origin, the radius is already 1 unit).
Ripped off from the link above:
You have to combine two equations:
Ray: R(t) = R0 + t * Rd , t > 0 with R0 = [X0, Y0, Z0] and Rd = [Xd, Yd, Zd]
Sphere: S = the set of points[xs, ys, zs], where (xs - xc)2 + (ys - yc)2 + (zs - zc)2 = Sr2
To do this, calculate your ray (x * pixel / width, y * pixel / width, z: 1), then:
A = Xd^2 + Yd^2 + Zd^2
B = 2 * (Xd * (X0 - Xc) + Yd * (Y0 - Yc) + Zd * (Z0 - Zc))
C = (X0 - Xc)^2 + (Y0 - Yc)^2 + (Z0 - Zc)^2 - Sr^2
Plug into quadratic equation:
t0, t1 = (- B + (B^2 - 4*C)^1/2) / 2
Check discriminant (B^2 - 4*C), and if real root, the intersection is:
Ri = [xi, yi, zi] = [x0 + xd * ti , y0 + yd * ti, z0 + zd * ti]
And the surface normal is:
SN = [(xi - xc)/Sr, (yi - yc)/Sr, (zi - zc)/Sr]
Boiling it all down:
So, since we're talking unit values, and rays that point straight at Z (no x or y component), we can boil down these equations greatly:
Ray:
X0 = 2 * pixelX / width
Y0 = 2 * pixelY / height
Z0 = 0
Xd = 0
Yd = 0
Zd = 1
Sphere:
Xc = 1
Yc = 1
Zc = 1
Factors:
A = 1 (unit ray)
B
= 2 * (0 + 0 + (0 - 1))
= -2 (no x/y component)
C
= (X0 - 1) ^ 2 + (Y0 - 1) ^ 2 + (0 - 1) ^ 2 - 1
= (X0 - 1) ^ 2 + (Y0 - 1) ^ 2
Discriminant
= (-2) ^ 2 - 4 * 1 * C
= 4 - 4 * C
From here:
If discriminant < 0:
Z = ?, Normal = ?
Else:
t = (2 + (discriminant) ^ 1 / 2) / 2
If t < 0 (hopefully never or always the case)
t = -t
Then:
Z: t
Nx: Xi - 1
Ny: Yi - 1
Nz: t - 1
Boiled farther still:
Intuitively it looks like C (X^2 + Y^2) and the square-root are the most prominent figures here. If I had a better recollection of my math (in particular, transformations on exponents of sums), then I'd bet I could derive this down to what Tom Zych gave you. Since I can't, I'll just leave it as above.