I have two vectors and i want to know where these vectors will intersect independent from direction or length. So lets just say i would draw an infinite line in either direction and i want to know where those two lines will intersect and get the coordinates. See image below for clarification:
So i want to know the coordinates of the pink X. But i can only find formulas for calculating the intersection point of two lines with an stard and end point which i dont have :( So i am looking for some help on how to approach this properly.
I have calculated the normalized direction of the blue lines: like so:
PVector norm12 = new PVector(-dir12.y, dir12.x);
PVector norm23 = new PVector(dir23.y, -dir23.x);
Some context on why i want to do this:
I am trying to find the center point of a circle created from 3 points.
All this is in 2D
If extra information is needed i am happy to provide.
If you've a endless line which is defined by a point P and a normalized direction R and a second endless line, which is defined by a point Q and a direction S, then the intersection point of the endless lines X is:
alpha ... angle between Q-P and R
beta ... angle between R and S
gamma = 180° - alpha - beta
h = | Q - P | * sin(alpha)
u = h / sin(beta)
t = | Q - P | * sin(gamma) / sin(beta)
t = dot(Q-P, (S.y, -S.x)) / dot(R, (S.y, -S.x)) = determinant(mat2(Q-P, S)) / determinant(mat2(R, S))
u = dot(Q-P, (R.y, -R.x)) / dot(R, (S.y, -S.x)) = determinant(mat2(Q-P, R)) / determinant(mat2(R, S))
X = P + R * t = Q + S * u
This can be calculated by the use of PVector, as follows:
// Intersect 2 endless lines
// line 1: "P" is on endless line, the direction is "dir1" ("R")
// line 2: "Q" is on endless line, the direction is "dir2" ("S")
PVector Intersect( PVector P, PVector dir1, PVector Q, PVector dir2) {
PVector R = dir1.copy();
PVector S = dir2.copy();
R.normalize();
S.normalize();
PVector QP = PVector.sub(Q, P);
PVector SNV = new PVector(S.y, -S.x);
float t = QP.dot(SNV) / R.dot(SNV);
PVector X = PVector.add(P, PVector.mult(R, t));
return X;
}
See the example:
void setup() {
size(500,500);
}
void draw() {
background(0, 0, 0);
stroke(255);
fill(255, 0, 0);
PVector l1p1 = new PVector(250, 150);
PVector l1p2 = new PVector(300, 300);
PVector l2p1 = new PVector(200, 180);
PVector l2p2 = new PVector(300, 220);
PVector l3p1 = new PVector(200, 300);
PVector l3p2 = new PVector(250, 280);
line(l1p1.x, l1p1.y, l1p2.x, l1p2.y);
line(l2p1.x, l2p1.y, l2p2.x, l2p2.y);
line(l3p1.x, l3p1.y, l3p2.x, l3p2.y);
PVector dir1 = PVector.sub(l1p2, l1p1);
PVector dir2 = PVector.sub(l2p2, l2p1);
PVector dir3 = PVector.sub(l3p2, l3p1);
PVector x1 = Intersect(l1p1, dir1, l2p1, dir2);
circle(x1.x, x1.y, 10);
PVector x2 = Intersect(l1p1, dir1, l3p1, dir3);
circle(x2.x, x2.y, 10);
PVector x3 = Intersect(l2p1, dir2, l3p1, dir3);
circle(x3.x, x3.y, 10);
}
Note, if the lines are parallel then the scalars of the returned point (PVector object) are infinit. This can be evaluated by Float.isInfinite. e.g:
if (!Float.isInfinite(x1.x) || !Float.isInfinite(x1.y))
circle(x1.x, x1.y, 10);
Related
This project is written entirely from scratch in Java. I've just been bored ever since Covid started, so I wanted something that would take up my time, and teach me something cool. I've been stuck on this problem for about a week now though. When I try to use my near plane clipping method it skews the new vertices to the opposite side of the screen, but sometimes times it works just fine.
Failure Screenshot
Success Screenshot
So my thought is maybe that since it works sometimes, I'm just not doing the clipping at the correct time in the pipeline?
I start by face culling and lighting,
Then I apply a Camera View Transformation to the Vertices,
Then I clip on the near plane
Finally I apply the projection matrix and Clip any remaining off screen Triangles
Code:
This calculates the intersection points. Sorry if it's messy or to long I'm not very experienced in coding, my major is physics, not CS.
public Vertex vectorIntersectPlane(Vector3d planePos, Vector3d planeNorm, Vector3d lineStart, Vector3d lineEnd){
float planeDot = planeNorm.dotProduct(planePos);
float startDot = lineStart.dotProduct(planeNorm);
float endDot = lineEnd.dotProduct(planeNorm);
float midPoint = (planeDot - startDot) / (endDot - startDot);
Vector3d lineStartEnd = lineEnd.sub(lineStart);
Vector3d lineToIntersect = lineStartEnd.scale(midPoint);
return new Vertex(lineStart.add(lineToIntersect));
}
public float distanceFromPlane(Vector3d planePos, Vector3d planeNorm, Vector3d vert){
float x = planeNorm.getX() * vert.getX();
float y = planeNorm.getY() * vert.getY();
float z = planeNorm.getZ() * vert.getZ();
return (x + y + z - (planeNorm.dotProduct(planePos)));
}
//When a triangle gets clipped it has 4 possible outcomes
// 1 it doesn't actually need clipping and gets returned
// 2 it gets clipped into 1 new triangle, for testing these are red
// 3 it gets clipped into 2 new triangles, for testing 1 is green, and 1 is blue
// 4 it is outside the view planes and shouldn't be rendered
public void clipTriangles(){
Vector3d planePos = new Vector3d(0, 0, ProjectionMatrix.fNear, 1f);
Vector3d planeNorm = Z_AXIS.clone();
final int length = triangles.size();
for(int i = 0; i < length; i++) {
Triangle t = triangles.get(i);
if(!t.isDraw())
continue;
Vector3d[] insidePoint = new Vector3d[3];
int insidePointCount = 0;
Vector3d[] outsidePoint = new Vector3d[3];
int outsidePointCount = 0;
float d0 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[0]);
float d1 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[1]);
float d2 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[2]);
//Storing distances from plane and counting inside outside points
{
if (d0 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[0];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[0];
outsidePointCount++;
}
if (d1 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[1];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[1];
outsidePointCount++;
}
if (d2 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[2];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[2];
}
}
//Triangle has 1 point still inside view, remove original triangle add new clipped triangle
if (insidePointCount == 1) {
t.dontDraw();
Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[1]);
vertices.add(newVert1);
vertices.add(newVert2);
//Triangles are stored with vertex references instead of the actual vertex object.
Triangle temp = new Triangle(t.getVertKeys()[0], vertices.size() - 2, vertices.size() - 1, vertices);
temp.setColor(1,0,0, t.getBrightness(), t.getAlpha());
triangles.add(temp);
continue;
}
//Triangle has two points inside remove original add two new clipped triangles
if (insidePointCount == 2) {
t.dontDraw();
Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[1], outsidePoint[0]);
vertices.add(newVert1);
vertices.add(newVert2);
Triangle temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 1, vertices);
temp.setColor(0, 1, 0, t.getBrightness(), t.getAlpha());
triangles.add(temp);
temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 2, vertices);
temp.setColor(0, 0, 1, t.getBrightness(), t.getAlpha());
triangles.add(temp);
continue;
}
}
}
I figured out the problem, The new clipped triangles were not being given the correct vertex references. they were just being given the first vertex of the triangle irregardless of if that was inside the view or not.
Trying to make a copy of the game Tank Trouble and I have no idea how to do collision detection with the bullets and the walls/ players. The bullets are generated when the mouse is pressed but I don't know how to make them despawn once they hit an object.
Tried to make them blend into the background upon contact but game started lagging hard. Have also tried the backwards loop thing that people use to remove items from arrayLists to no avail.
PVector player = new PVector (300, 400);
ArrayList <Bullet> bullets = new ArrayList <Bullet> ();
float maxSpeed = 3; //speed of bullets
void setup() {
size(800, 600);
fill(0);
}
void draw() {
background(255);
line(20, 200, 400, 200);
rect(300, 400, 50, 50);
//creates an aiming tool for the players
PVector mouse = new PVector(mouseX, mouseY);
fill(255);
ellipse(mouse.x, mouse.y, 8, 8);
if (frameCount%5==0 && mousePressed) {
PVector dir = PVector.sub(mouse, player);
dir.normalize();
dir.mult(maxSpeed*3);
Bullet b = new Bullet(player, dir);
bullets.add(b);
}
for (Bullet b : bullets) {
b.update();
b.display();
}
}
class Bullet extends PVector {
PVector vel;
Bullet(PVector loc, PVector vel) {
super(loc.x, loc.y);
this.vel = vel.get();
}
void update() {
add(vel);
}
void display() {
fill(0);
ellipse(x, y, 5, 5);
}
float bulletX() {
return x;
}
}
Basically want the bullets to bounce 3-4 times before despawning on the last touch. If it touches the player at any point, they should both despawn.
Add method to the class Bullet, which verified if a bullet is out of the window:
class Bullet extends PVector {
// [...]
boolean outOfBounds() {
return this.x<0 || this.x>width || this.y<0 || this.y>height;
}
}
Add a collision check with a line to the class Bullet. To check if the bullet hits the lien, you've to calculate the nearest point on the line and to verify if the distance to the line is less than the velocity of the bullet and if the bullet doesn't miss the line at its sides.
If you have a line, given by a point (O) and an direction (D), then the nearest point on the line, to a point p can be calculated as follows
X = O + D * dot(P-O, D);
The dot product of 2 vectors is equal the cosine of the angle between the 2 vectors multiplied by the magnitude (length) of both vectors.
dot( A, B ) == | A | * | B | * cos( alpha )
The dot product of V and D is equal the cosine of the angle between the line (O, D) and the vector V = P - O, multiplied by the amount (length) of V, because D is a unit vector (the lenght of D is 1.0),
Applying this to your code, leads to the following method:
class Bullet extends PVector {
// [...]
boolean collideline(float x1, float y1, float x2, float y2) {
PVector O = new PVector(x1, y1);
PVector L2 = new PVector(x2, y2);
float len = O.dist(L2);
PVector D = L2.sub(O).normalize();
PVector P = this;
PVector X = add(O, mult(D, sub(P, O).dot(D)));
// distance to the line has to be less than velocity
float distX = X.dist(P);
if (distX > this.vel.mag())
return false;
// very if bullet doesn't "miss" the line
PVector VX = X.sub(O);
float distO = VX.dot(D);
return distO > -5 && distO < len+5;
}
}
Remove the bullet form the list by its index (in reverse order), if they are out of bounds or collide to the line:
void draw() {
// [...]
for (int j = bullets.size()-1; j >= 0; j--) {
if (bullets.get(j).outOfBounds() || bullets.get(j).collideline(20, 200, 400, 200))
bullets.remove(j);
}
}
I have a problem that I can't seem to get a working algorithm for, I've been trying to days and get so close but yet so far.
I want to draw a triangle defined by 3 points (p0, p1, p2). This triangle can be any shape, size, and orientation. The triangle must also be filled inside.
Here's a few things I've tried and why they've failed:
1
Drawing lines along the triangle from side to side
Failed because the triangle would have holes and would not be flat due to the awkwardness of drawing lines across the angled surface with changing locations
2
Iterate for an area and test if the point falls past the plane parallel to the triangle and 3 other planes projected onto the XY, ZY, and XZ plane that cover the area of the triangle
Failed because for certain triangles (that have very close sides) there would be unpredictable results, e.g. voxels floating around not connected to anything
3
Iterate for an area along the sides of the triangle (line algorithm) and test to see if a point goes past a parallel plane
Failed because drawing a line from p0 to p1 is not the same as a line from p1 to p0 and any attempt to rearrange either doesn't help, or causes more problems. Asymmetry is the problem with this one.
This is all with the intent of making polygons and flat surfaces. 3 has given me the most success and makes accurate triangles, but when I try to connect these together everything falls apart and I get issues with things not connecting, asymmetry, etc. I believe 3 will work with some tweaking but I'm just worn out from trying to make this work for so long and need help.
There's a lot of small details in my algorithms that aren't really relevant so I left them out. For number 3 it might be a problem with my implementation and not the algorithm itself. If you want code I'll try and clean it up enough to be understandable, it will take me a few minutes though. But I'm looking for algorithms that are known to work. I can't seem to find any voxel shape making algorithms anywhere, I've been doing everything from scratch.
EDIT:
Here's the third attempt. It's a mess, but I tried to clean it up.
// Point3i is a class I made, however the Vector3fs you'll see are from lwjgl
public void drawTriangle (Point3i r0, Point3i r1, Point3i r2)
{
// Util is a class I made with some useful stuff inside
// Starting values for iteration
int sx = (int) Util.min(r0.x, r1.x, r2.x);
int sy = (int) Util.min(r0.y, r1.y, r2.y);
int sz = (int) Util.min(r0.z, r1.z, r2.z);
// Ending values for iteration
int ex = (int) Util.max(r0.x, r1.x, r2.x);
int ey = (int) Util.max(r0.y, r1.y, r2.y);
int ez = (int) Util.max(r0.z, r1.z, r2.z);
// Side lengths
float l0 = Util.distance(r0.x, r1.x, r0.y, r1.y, r0.z, r1.z);
float l1 = Util.distance(r2.x, r1.x, r2.y, r1.y, r2.z, r1.z);
float l2 = Util.distance(r0.x, r2.x, r0.y, r2.y, r0.z, r2.z);
// Calculate the normal vector
Vector3f nn = new Vector3f(r1.x - r0.x, r1.y - r0.y, r1.z - r0.z);
Vector3f n = new Vector3f(r2.x - r0.x, r2.y - r0.y, r2.z - r0.z);
Vector3f.cross(nn, n, n);
// Determines which direction we increment for
int iz = n.z >= 0 ? 1 : -1;
int iy = n.y >= 0 ? 1 : -1;
int ix = n.x >= 0 ? 1 : -1;
// Reorganize for the direction of iteration
if (iz < 0) {
int tmp = sz;
sz = ez;
ez = tmp;
}
if (iy < 0) {
int tmp = sy;
sy = ey;
ey = tmp;
}
if (ix < 0) {
int tmp = sx;
sx = ex;
ex = tmp;
}
// We're we want to iterate over the end vars so we change the value
// by their incrementors/decrementors
ex += ix;
ey += iy;
ez += iz;
// Maximum length
float lmax = Util.max(l0, l1, l2);
// This is a class I made which manually iterates over a line, I already
// know that this class is working
GeneratorLine3d g0, g1, g2;
// This is a vector for the longest side
Vector3f v = new Vector3f();
// make the generators
if (lmax == l0) {
v.x = r1.x - r0.x;
v.y = r1.y - r0.y;
v.z = r1.z - r0.z;
g0 = new GeneratorLine3d(r0, r1);
g1 = new GeneratorLine3d(r0, r2);
g2 = new GeneratorLine3d(r2, r1);
}
else if (lmax == l1) {
v.x = r1.x - r2.x;
v.y = r1.y - r2.y;
v.z = r1.z - r2.z;
g0 = new GeneratorLine3d(r2, r1);
g1 = new GeneratorLine3d(r2, r0);
g2 = new GeneratorLine3d(r0, r1);
}
else {
v.x = r2.x - r0.x;
v.y = r2.y - r0.y;
v.z = r2.z - r0.z;
g0 = new GeneratorLine3d(r0, r2);
g1 = new GeneratorLine3d(r0, r1);
g2 = new GeneratorLine3d(r1, r2);
}
// Absolute values for the normal
float anx = Math.abs(n.x);
float any = Math.abs(n.y);
float anz = Math.abs(n.z);
int i, o;
int si, so;
int ii, io;
int ei, eo;
boolean maxx, maxy, maxz,
midy, midz, midx,
minx, miny, minz;
maxx = maxy = maxz =
midy = midz = midx =
minx = miny = minz = false;
// Absolute values for the longest side vector
float rnx = Math.abs(v.x);
float rny = Math.abs(v.y);
float rnz = Math.abs(v.z);
int rmid = Util.max(rnx, rny, rnz);
if (rmid == rnz) midz = true;
else if (rmid == rny) midy = true;
midx = !midz && !midy;
// Determine the inner and outer loop directions
if (midz) {
if (any > anx)
{
maxy = true;
si = sy;
ii = iy;
ei = ey;
}
else {
maxx = true;
si = sx;
ii = ix;
ei = ex;
}
}
else {
if (anz > anx) {
maxz = true;
si = sz;
ii = iz;
ei = ez;
}
else {
maxx = true;
si = sx;
ii = ix;
ei = ex;
}
}
if (!midz && !maxz) {
minz = true;
so = sz;
eo = ez;
}
else if (!midy && !maxy) {
miny = true;
so = sy;
eo = ey;
}
else {
minx = true;
so = sx;
eo = ex;
}
// GeneratorLine3d is iterable
Point3i p1;
for (Point3i p0 : g0) {
// Make sure the two 'mid' coordinate correspond for the area inside the triangle
if (midz)
do p1 = g1.hasNext() ? g1.next() : g2.next();
while (p1.z != p0.z);
else if (midy)
do p1 = g1.hasNext() ? g1.next() : g2.next();
while (p1.y != p0.y);
else
do p1 = g1.hasNext() ? g1.next() : g2.next();
while (p1.x != p0.x);
eo = (minx ? p0.x : miny ? p0.y : p0.z);
so = (minx ? p1.x : miny ? p1.y : p1.z);
io = eo - so >= 0 ? 1 : -1;
for (o = so; o != eo; o += io) {
for (i = si; i != ei; i += ii) {
int x = maxx ? i : midx ? p0.x : o;
int y = maxy ? i : midy ? p0.y : o;
int z = maxz ? i : midz ? p0.z : o;
// isPassing tests to see if a point goes past a plane
// I know it's working, so no code
// voxels is a member that is an arraylist of Point3i
if (isPassing(x, y, z, r0, n.x, n.y, n.z)) {
voxels.add(new Point3i(x, y, z));
break;
}
}
}
}
}
You could use something like Besenham's line algorithm, but extended into three dimensions. The two main ideas we want to take from it are:
rotate the initial line so its slope isn't too steep.
for any given x value, find an integer value that is closest to the ideal y value.
Just as Bresenham's algorithm prevents gaps by performing an initial rotation, we'll avoid holes by performing two initial rotations.
Get the normal vector and point that represent the plane your triangle lies on. Hint: use the cross product of (line from p0 to p1) and (line from p0 to p2) for the vector, and use any of your corner points for the point.
You want the plane to be sufficiently not-steep, to avoid holes. You must satisfy these conditions:
-1 >= norm.x / norm.y >= 1
-1 >= norm.z / norm.y >= 1
Rotate your normal vector and initial points 90 degrees about the x axis and 90 degrees about the z axis until these conditions are satisfied. I'm not sure how to do this in the fewest number of rotations, but I'm fairly sure you can satisfy these conditions for any plane.
Create a function f(x,z) which represents the plane your rotated triangle now lies on. It should return the Y value of any pair of X and Z values.
Project your triangle onto the XZ plane (i.e., set all the y values to 0), and use your favorite 2d triangle drawing algorithm to get a collection of x-and-z coordinates.
For each pixel value from step 4, pass the x and z values into your function f(x,z) from step 3. Round the result to the nearest integer, and store the x, y, and z values as a voxel somewhere.
If you performed any rotations in step 2, perform the opposite of those rotations in reverse order on your voxel collection.
Start with a function that checks for triangle/voxel intersection. Now you can scan a volume and find the voxels that intersect the triangle - these are the ones you're interested in. This is a lousy algorithm but is also a regression test for anything else you try. This test is easy to implement using SAT (separating axis theorem) and considering the triangle a degenerate volume (1 face, 3 edges) and considering the voxels symmetry (only 3 face normals).
I use octtrees, so my preferred method is to test a triangle against a large voxel and figure out which of the 8 child octants it intersects. Then use recursion on the intersected children until the desired level of subdivision is attained. Hint: at most 6 of the children can be intersected by the triangle and often fewer than that. This is tricky but will produce the same results as the first method but much quicker.
Rasterization in 3d is probably fastest, but IMHO is even harder to guarantee no holes in all cases. Again, use the first method for comparison.
Is there any function that will give me the intersection point of a Polygon and Line2D ?
I have a Polygon and a line segment that I know intersect I want the actual value of the intersection point not a boolean answer.
Here you are. The interesting methods are getIntersections and getIntersection. The former parses over all polygon segments and checks for intersections, the latter does the actual calculation. Do keep in mind that the calculation can be seriously optimized and doesn't check for division by 0. This will also work only for polygons. It could be adapted to work with other shapes if you introduce calculations for cubic and quadratic curves. It is assumed that Line2D.Double is used instead of Line2D.Float. A Set is used to avoid duplicate points (might occur on polygon corner intersections).
Please don't use this without extensive testing, since I've just hacked it together quickly and am not sure it's completely sound.
package quickpolygontest;
import java.awt.Polygon;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Main {
public static void main(String[] args) throws Exception {
final Polygon poly = new Polygon(new int[]{1,2,2,1}, new int[]{1,1,2,2}, 4);
final Line2D.Double line = new Line2D.Double(2.5, 1.3, 1.3, 2.5);
final Set<Point2D> intersections = getIntersections(poly, line);
for(Iterator<Point2D> it = intersections.iterator(); it.hasNext();) {
final Point2D point = it.next();
System.out.println("Intersection: " + point.toString());
}
}
public static Set<Point2D> getIntersections(final Polygon poly, final Line2D.Double line) throws Exception {
final PathIterator polyIt = poly.getPathIterator(null); //Getting an iterator along the polygon path
final double[] coords = new double[6]; //Double array with length 6 needed by iterator
final double[] firstCoords = new double[2]; //First point (needed for closing polygon path)
final double[] lastCoords = new double[2]; //Previously visited point
final Set<Point2D> intersections = new HashSet<Point2D>(); //List to hold found intersections
polyIt.currentSegment(firstCoords); //Getting the first coordinate pair
lastCoords[0] = firstCoords[0]; //Priming the previous coordinate pair
lastCoords[1] = firstCoords[1];
polyIt.next();
while(!polyIt.isDone()) {
final int type = polyIt.currentSegment(coords);
switch(type) {
case PathIterator.SEG_LINETO : {
final Line2D.Double currentLine = new Line2D.Double(lastCoords[0], lastCoords[1], coords[0], coords[1]);
if(currentLine.intersectsLine(line))
intersections.add(getIntersection(currentLine, line));
lastCoords[0] = coords[0];
lastCoords[1] = coords[1];
break;
}
case PathIterator.SEG_CLOSE : {
final Line2D.Double currentLine = new Line2D.Double(coords[0], coords[1], firstCoords[0], firstCoords[1]);
if(currentLine.intersectsLine(line))
intersections.add(getIntersection(currentLine, line));
break;
}
default : {
throw new Exception("Unsupported PathIterator segment type.");
}
}
polyIt.next();
}
return intersections;
}
public static Point2D getIntersection(final Line2D.Double line1, final Line2D.Double line2) {
final double x1,y1, x2,y2, x3,y3, x4,y4;
x1 = line1.x1; y1 = line1.y1; x2 = line1.x2; y2 = line1.y2;
x3 = line2.x1; y3 = line2.y1; x4 = line2.x2; y4 = line2.y2;
final double x = (
(x2 - x1)*(x3*y4 - x4*y3) - (x4 - x3)*(x1*y2 - x2*y1)
) /
(
(x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4)
);
final double y = (
(y3 - y4)*(x1*y2 - x2*y1) - (y1 - y2)*(x3*y4 - x4*y3)
) /
(
(x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4)
);
return new Point2D.Double(x, y);
}
}
There is java.awt.geom.Area.intersect(Area) using the constructor Area(Shape) with your Polygon and passing your Line2D as an Area to intersect will give you the Area which is intersected.
With great success, i used this approach:
Area a = new Area(shape1);
Area b = new Area(shape2);
b.intersect(a);
if (!b.isEmpty()) {
//Shapes have non-empty intersection, so do you actions.
//In case of need, actual intersection is in Area b. (its destructive operation)
}
You need to bear in mind that it might intersect at multiple places.
Let's call the line segment of the polygon P and the real line segment L.
We find the slope of each line (slope is m)
ml = (ly1-ly2) / (lx1-lx2);
mp = (ply-pl2) / (px1-px2);
Find the y intercept of each line
// y = mx+b where b is y-intercept
bl = ly1 - (ml*lx1);
bp = py1 - (pl*px1);
You can solve for the x value with:
x = (bp - bl) / (ml - mp)
Then plug that X into one of the equations to get the Y
y = ml * x + bl
Here's an implemented version of the algorithm
class pointtest {
static float[] intersect(float lx1, float ly1, float lx2, float ly2,
float px1, float py1, float px2, float py2) {
// calc slope
float ml = (ly1-ly2) / (lx1-lx2);
float mp = (py1-py2) / (px1-px2);
// calc intercept
float bl = ly1 - (ml*lx1);
float bp = py1 - (mp*px1);
float x = (bp - bl) / (ml - mp);
float y = ml * x + bl;
return new float[]{x,y};
}
public static void main(String[] args) {
float[] coords = intersect(1,1,5,5,1,4,5,3);
System.out.println(coords[0] + " " + coords[1]);
}
}
and results:
3.4 3.4
If you are not restricted to use the Polygon and Line2D Objects I would recommend to use JTS.
Create LinearRing geometry (your polygon).
Create LineString geometry.
Create intersection Point(s) using the intersection method.
Simple code example:
// create ring: P1(0,0) - P2(0,10) - P3(10,10) - P4(0,10)
LinearRing lr = new GeometryFactory().createLinearRing(new Coordinate[]{new Coordinate(0,0), new Coordinate(0,10), new Coordinate(10,10), new Coordinate(10,0), new Coordinate(0,0)});
// create line: P5(5, -1) - P6(5, 11) -> crossing the ring vertically in the middle
LineString ls = new GeometryFactory().createLineString(new Coordinate[]{new Coordinate(5,-1), new Coordinate(5,11)});
// calculate intersection points
Geometry intersectionPoints = lr.intersection(ls);
// simple output of points
for(Coordinate c : intersectionPoints.getCoordinates()){
System.out.println(c.toString());
}
Result is:
(5.0, 0.0, NaN)
(5.0, 10.0, NaN)
I have two GPS coordinates which link together to make a line. I also have a GPS point which is near to, but never exactly on, the line. My question is, how do I find the nearest point along the line to the given point?
Game Dev has an answer to this, it is in C++ but it should be easy to port over. Which CarlG has kindly done (hopefully he does not mind me reposting):
public static Point2D nearestPointOnLine(double ax, double ay, double bx, double by, double px, double py,
boolean clampToSegment, Point2D dest) {
// Thanks StackOverflow!
// https://stackoverflow.com/questions/1459368/snap-point-to-a-line-java
if (dest == null) {
dest = new Point2D.Double();
}
double apx = px - ax;
double apy = py - ay;
double abx = bx - ax;
double aby = by - ay;
double ab2 = abx * abx + aby * aby;
double ap_ab = apx * abx + apy * aby;
double t = ap_ab / ab2;
if (clampToSegment) {
if (t < 0) {
t = 0;
} else if (t > 1) {
t = 1;
}
}
dest.setLocation(ax + abx * t, ay + aby * t);
return dest;
}
Try this:
ratio = (((x1-x0)^2+(y1-y0)^2)*((x2-x1)^2 + (y2-y1)^2) - ((x2-x1)(y1-y0) - (x1-x0)(y2-y1))^2)^0.5
-----------------------------------------------------------------------------------------
((x2-x1)^2 + (y2-y1)^2)
xc = x1 + (x2-x1)*ratio;
yc = y1 + (y2-y1)*ratio;
Where:
x1,y1 = point#1 on the line
x2,y2 = point#2 on the line
x0,y0 = Another point near the line
xc,yx = The nearest point of x0,y0 on the line
ratio = is the ratio of distance of x1,y1 to xc,yc and distance of x1,y1 to x2,y2
^2 = square
^0.5 = square root
The formular is derived after we find the distant from point x0,y0 to line (x1,y1 -> x2,y3).
See here
I've test this code here (this particular one I gave you above) but I've used it similar method years ago and it work so you may try.
You can use JTS for that.
Create a LineSegment (your line)
Create a Coordinate (the point you want to snap to the line)
Get Point on the line by using the closestPoint method
Very simple code example:
// create Line: P1(0,0) - P2(0,10)
LineSegment ls = new LineSegment(0, 0, 0, 10);
// create Point: P3(5,5)
Coordinate c = new Coordinate(5, 5);
// create snapped Point: P4(0,5)
Coordinate snappedPoint = ls.closestPoint(c);