Computing a polygon that surrounds a multi-point line - java

I am trying to compute a polygon that surrounds a line connecting multiple points (e.g. a GPX track).
The image below shows an example with the track as red line and the desired polygon in blue.
As simplification the red points are denoted by x and y - not by latitude/longitude.
How do I compute such an environment (light blue polygon) if I only have the list of the three points specifying the path?
Partial solutions (e.g. for only two points) or hints about mathematical libraries (in Java) that provide algorithms for such a computation would also bring me one step forward.
Further assumptions:
The track is intersection free.
Update:
Using the approach as presented by Rogach and xan I ran into some problems if the angle between the lines is smaller than 90 degree or larger than 270 degree:
As you can see the polygon gets intersects itself which leads to a serious problem.
From my point of view using an java.awt.geom.Area is the better approach:
My solution (based on the code by Rogach):
For each line connecting two points of the track I compute a surrounding polygon. Afterwards I add (area union) the computed polygon to an Area which does all the necessary computation for me. As the Area strictly uses the "or" algorithm on adding new polygons I do not have to care about "self intersections" of the polygon as presented in the update above.
Area area = new Area();
for (int i = 1; i < points.size(); i++) {
Point2D point1 = points.get(i - 1);
Point2D point2 = points.get(i);
Line2D.Double ln = new Line2D.Double(point1.getX(), point1.getY(), point2.getX(), point2.getY());
double indent = 15.0; // distance from central line
double length = ln.getP1().distance(ln.getP2());
double dx_li = (ln.getX2() - ln.getX1()) / length * indent;
double dy_li = (ln.getY2() - ln.getY1()) / length * indent;
// moved p1 point
double p1X = ln.getX1() - dx_li;
double p1Y = ln.getY1() - dy_li;
// line moved to the left
double lX1 = ln.getX1() - dy_li;
double lY1 = ln.getY1() + dx_li;
double lX2 = ln.getX2() - dy_li;
double lY2 = ln.getY2() + dx_li;
// moved p2 point
double p2X = ln.getX2() + dx_li;
double p2Y = ln.getY2() + dy_li;
// line moved to the right
double rX1_ = ln.getX1() + dy_li;
double rY1 = ln.getY1() - dx_li;
double rX2 = ln.getX2() + dy_li;
double rY2 = ln.getY2() - dx_li;
Path2D p = new Path2D.Double();
p.moveTo(lX1, lY1);
p.lineTo(lX2, lY2);
p.lineTo(p2X, p2Y);
p.lineTo(rX2, rY2);
p.lineTo(rX1_, rY1);
p.lineTo(p1X, p1Y);
p.lineTo(lX1, lY1);
area.add(new Area(p));
}

As I see, this problem is similar to polygon buffering problem.
I think following approach can help you:
For each segment of your track, find two lines - one to the left and one to the right.
Then, iterate for over your ofsetted lines, and resolve intersections. For example:
http://img25.imageshack.us/img25/7660/temprhk.png
Add caps to ends, and you're done! :)
And some code:
Moving a line to the left:
Line2D l;
double indent; // distance from central line
double dx = ln.getX2() - ln.getX1();
double dy = ln.getY2() - ln.getY1();
double length = ln.getP1().distance(ln.getP2());
double newX1 = l.getX1() - indent*(dy/length);
double newY1 = l.getY1() + indent*(dx/length);
double newX2 = l.getX2() - indent*(dy/length);
double newY2 = l.getY2() + indent*(dx/length);
Line2D leftLine = new Line2D.Double(newX1, newY1, newX2, newY2);
For moving it to the right, change "+" to "-" and vice versa in the last 4 lines of code.
About working with intersections - if two line segment intersect, you just output the intersection point. If they do not, then situation is a bit more complicated - you can, of course, still output the intersection, but in case of rapidly turning track, there will be strange outbursts. I inserted an arc segment in similar situation, but the code is to big and scattered, so I can't paste it here.
Or, you can do as you show on your picture - just connect end points.
And, by the way, if speed is not a big issue, you can use even better way - for each line of track, find left and right lines, add caps, pack it all into Path2D, then create Area from Path2D.
In such case, you can make this "line with caps" as intersection of three areas: rectangle, whose points are just end points of right and left line, and two circles with centers on original track segment ends.
When you compute Areas for all lines, just intersect them using Area add() method.
This approach deals with just any situations, even self-intersections and breaks in the track.

See my answer to a similar question, "How to draw an outline around any line."
Same idea as Rogach provides here, but perhaps different drawings and explanations will help clarify it.

If you don't want to write the code for the buffering as described by Rogach, JTS could do the magic for you.
See the developer guide for a quick introduction.

Half-baked suggestion: Calculate the normal to each segment. Then, for each vertex V_i, interpolate the normals from its adjacent segments to get n_i (normalise it again) and add two vertices at V_i +/- a*n_i where a is some scaling factor.
If you join these points, you won't get exactly your blue polygon, but it might be good enough.
You may have to keep track of which "side" the new vertices are on. If you can close the curve without self-intersections this just becomes a point in polygon test for each vertex.

Related

find angle of reflection relative to x-axis

I am working on a project that displays a laser beam and it's path through reflections. When an entity is hit by a laser it calls the entities alterLaser(Laser r) method. In order to create a reflecting ray I must call Laser.createNew(x,y,angle counter clockwise of (+x)). My problem is that I can find the angle of reflection easily but I don't know how to find that angle relative to the x-axis.
I first tried finding the acute angle in between the two vectors laser and mirror but I do not know if there is a direct relationship between that and the x-axis. After searching the internet I found this formula:
r = i + 2s - 180
where r is the angle the reflected ray makes with the X-axis. i is the angle the initial ray makes with the x-axis and s is the angle the reflected ray makes with the x-axis;
I found this formula wasn't working, but in the cases I tried it was giving theta in a different quadrant than the intended quadrant. But that poses the new problem of finding which quadrant it is in.
here is a look at my current code:
#Override
protected void alterLaser(Laser r) {
Vec laser = new Vec(r.getStartX(),r.getStartY(),r.getEndX(),r.getEndY());
Vec mirror = new Vec(this.getStartX(),this.getStartY(),this.getEndX(),this.getEndY());
double theta,thetaq2,thetaq3,thetaq4;
theta = laser.angle() + (2 * mirror.absAngle()) - 180;
theta = Vec.absTheta(theta);
thetaq2 = 180-theta;
thetaq3 = theta+180;
thetaq4 = 360-theta;
Laser.createNew(r.getEndX(),r.getEndY(),theta,null,this);
Laser.createNew(r.getEndX(),r.getEndY(),thetaq2,null,this);
Laser.createNew(r.getEndX(),r.getEndY(),thetaq3,null,this);
Laser.createNew(r.getEndX(),r.getEndY(),thetaq4,null,this);
}
}
The easiest way in Java to find the angle a vector makes counterclockwise with respect to positive x axis is to use the Math.atan2 function. https://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#atan2(double,%20double)
This will put the value on the range -pi to pi so you don't have to worry about special casing the different quadrants.

Basic trigonometry (drawing a line)

I'm trying to draw a recursive tree, but I'm already stuck at the beginning. I've been trying to figure this out, for a while now, and I just can't seem to get it right.
I am using the StdDraw library. Here's what I want to do:
I've already drawn the trunk (black line). But I need to draw the red line too.
In the main function I've set the canvas size to 500x500px and I've set the x and y limit from 0-500.
StdDraw.setCanvasSize(500, 500);
StdDraw.setScale(0, 500);
The tree parameters that I receive are the tree length (d) and the angle (sine). There are also some others (needed for recursion, but not relevant for this simple problem)
I've drawn the trunk using the following code:
StdDraw.line(250, 150, 250, 150+d);
If you're not familiar with StdDraw it's: StdDraw.line(x0, y0, x1, y1)
Now for the red line I've been struggling with some basic trigonometry, but I don't know where I'm wrong. Here's the code:
//The length of the red line is 3/4 the length of the trunk(given in instructions)
double hypotenuse = (3/4)*d;
//We get the opposite by multiplying the sine * hypotenuse, correct?
double opposite = Math.sin(alpha) * hypotenuse;
//Pythagorean Theorem to get the adjacent
double adjacent = Math.sqrt(Math.pow(hypotenuse, 2)-Math.pow(opposite, 2));
//We draw the line from the last x position minus adjacent to move to the left
//and from last y position upwards by 150+d(previous position)+opposite
StdDraw.line(250,150+d,250-adjacent,150+d+opposite);
I create a new Tree object in main with d=110, alpha=40 to test it.
If I remove the trunk and only try to draw the red line (using StdDraw.line(250,150+d,250-adjacent,150+d+opposite)). I only get a black dot, like this:
All your sides are getting set to zero, because here:
double hypotenuse = (3/4)*d;
(3/4) equals 0. Change to:
double hypotenuse = (3/4.0)*d;
or:
double hypotenuse = 0.75 * d;

How do I draw thick lines with closely spaced points properly with Java2D graphics?

I'm trying to draw maps using Java2D. When my map is zoomed out my roads are full of drawing artefacts. This is a small part of the screen when drawing a complete US state:
This is a similar section of the road when zoomed closer in:
The line style used is a solid blue line with width scaled to be equivalent to 2 pixels.
I've tried various rendering hints and line joining rules and nothing seems to help.
I'm using Open JDK 1.7 on a Mac running the OS/X 10.8 and this is also reproducible on a Linux machine with a Sun JDK 1.6.
All shapes and transforms are double precision as far as possible with Java2D.
The geometry of the line has many closely spaced points and I suspect that the cause of the drawing artefacts is that the renderer is getting confused by consecutive points that are closer than a single pixel.
Is there a way to improve the appearance of the zoomed out shapes without thinning the points?
Edit
The drawing artefacts are at the points where separate line segments meet, so the missing pixels are something to do with the line caps (ends) not meeting, even when the end points are identical. This image shows the meeting point between two line segments. I have highlighted each line segment in a 7 pixel scaled line style (XOR-ed with white) but if you look very closely you can still see part of the original blue line (this is due to the rounded caps overlapping and the XOR draw mode.) At ordinary scales the ends seem to overlap, but when zoomed out and back in ordinary paint mode there is a broken line effect.
One workaround would be to join all the contiguous line segments together before drawing them, but I would still like to know the real cause of the drawing artefacts.
I am unable to recreate the situation you have using the OS X 1.6 JDK, but I still have some suggestions for you.
If you are just using this to outline states, consider using the GeneralPath class. You can use the lineTo(x,y) method to establish each of your points on the line. Again, because I can't recreate your problem using Line2D.Double, I don't know if this will actually be any different.
Second, and possibly more importantly, is how you are zooming in and out. I am using an AffineTransform (with setScaleTo(x,y)) on my Graphics2D object, and everything is working swimmingly. Compared to the alternative of scaling the points in your data by a zoom factor (or whatever else you could do), this is fairly easy. You'll also have to adjust the stroke of the lines by the factor, because it will scale everything down. I can post screenshots if you'd like.
Please check Xiaolin Wu's line algorithm it should answer you question!
Basic Concept
function plot(x, y, c) is
plot the pixel at (x, y) with brightness c (where 0 ≤ c ≤ 1)
function ipart(x) is
return integer part of x
function round(x) is
return ipart(x + 0.5)
function fpart(x) is
return fractional part of x
function rfpart(x) is
return 1 - fpart(x)
function drawLine(x1,y1,x2,y2) is
dx = x2 - x1
dy = y2 - y1
if abs(dx) < abs(dy) then
swap x1, y1
swap x2, y2
swap dx, dy
end if
if x2 < x1
swap x1, x2
swap y1, y2
end if
gradient = dy / dx
// handle first endpoint
xend = round(x1)
yend = y1 + gradient * (xend - x1)
xgap = rfpart(x1 + 0.5)
xpxl1 = xend // this will be used in the main loop
ypxl1 = ipart(yend)
plot(xpxl1, ypxl1, rfpart(yend) * xgap)
plot(xpxl1, ypxl1 + 1, fpart(yend) * xgap)
intery = yend + gradient // first y-intersection for the main loop
// handle second endpoint
xend = round (x2)
yend = y2 + gradient * (xend - x2)
xgap = fpart(x2 + 0.5)
xpxl2 = xend // this will be used in the main loop
ypxl2 = ipart (yend)
plot (xpxl2, ypxl2, rfpart (yend) * xgap)
plot (xpxl2, ypxl2 + 1, fpart (yend) * xgap)
// main loop
for x from xpxl1 + 1 to xpxl2 - 1 do
plot (x, ipart (intery), rfpart (intery))
plot (x, ipart (intery) + 1, fpart (intery))
intery = intery + gradient
end function

Find degree at intersection between vectors in convex polygon

It's a bit of a homework question but I've been derping around for a while and haven't been able to get a 100% accurate answer. Given a polygon, I have to find the internal angle of any random vertex within that polygon. What I have been doing is taking the vertex before that and the vertex after it and then calculating the angle of incidence (say I treat my vertex as B), I make the edges AB and BC, then find the magnitude of each, then divide the dot product of the two by the magnitude of each.
I'm still off, particularly in the instance where I have vectors (0,10), (0,0), (10,0). Obviously the interior angle on that middle vector is 90 degrees, but when I compute it using magnitude and dot product I get 45 degrees for some weird reason.
Here is my code
double dx21 = one.x - two.x;
double dx31 = one.x - three.x;
double dy21 = one.y - two.y;
double dy31 = one.y - three.y;
double m12 = Math.sqrt(dx21*dx21 + dy21*dy21);
double m13 = Math.sqrt(dx31*dx31 + dy31*dy31);
double theta = Math.acos((dx21*dx31 + dy21*dy31)/ (m12 * m13));
System.out.println(theta);
System.out.println(Math.toDegrees(theta));
Is there anything blindingly obvious that I've missed? I'm traversing the vertexes counter-clockwise, as that is how the set is organised.
Your code is using point 'one' as the centre point, and then calculates the angle between 'two' and 'three' from that. So, if you put , in the vertices (0,0), (0,10), (10,0), you would get an angle of 90. The actual calculation is fine and works, you just have your vertex order messed up.

Java 2D: Moving a point P a certain distance closer to another point?

What is the best way to go about moving a Point2D.Double x distance closer to another Point2D.Double?
Edit: Tried to edit, but so went down for maintenance. No this is not homework
I need to move a plane (A) towards the end of a runway (C) and point it in the correct direction (angle a).
alt text http://img246.imageshack.us/img246/9707/planec.png
Here is what I have so far, but it seems messy, what is the usual way to go about doing something like this?
//coordinate = plane coordinate (Point2D.Double)
//Distance = max distance the plane can travel in this frame
Triangle triangle = new Triangle(coordinate, new Coordinate(coordinate.x, landingCoordinate.y), landingCoordinate);
double angle = 0;
//Above to the left
if (coordinate.x <= landingCoordinate.x && coordinate.y <= landingCoordinate.y)
{
angle = triangle.getAngleC();
coordinate.rotate(angle, distance);
angle = (Math.PI-angle);
}
//Above to the right
else if (coordinate.x >= landingCoordinate.x && coordinate.y <= landingCoordinate.y)
{
angle = triangle.getAngleC();
coordinate.rotate(Math.PI-angle, distance);
angle = (Math.PI*1.5-angle);
}
plane.setAngle(angle);
The triangle class can be found at http://pastebin.com/RtCB2kSZ
Bearing in mind the plane can be in in any position around the runway point
You can minimize the difference along both axis by a percent (that depends on how much you want to move the points).
For example:
Point2D.Double p1, p2;
//p1 and p2 inits
// you don't use abs value and use the still point as the first one of the subtraction
double deltaX = p2.getX() - p1.getX();
double deltaY = p2.getY() - p1.getY();
// now you know how much far they are
double coeff = 0.5; //this coefficient can be tweaked to decice how much near the two points will be after the update.. 0.5 = 50% of the previous distance
p1.setLocation(p1.getX() + coeff*deltaX, p1.getY() + coeff*deltaY);
So you moved p1 halfway toward p2. The good thing avoid abs is that, if you choose which point will be moved and which one will stand still you can avoid if tests and just use the raw coefficient.
The shortest distance between two points is a line, so simply move that point x units along the line that connects the two points.
Edit: I didn't want to give away the specifics of the answer if this is homework, but this is simple enough that it can be illustrated without being too spoiler-y.
Let us assume you have two points A = (x1, y1) and B = (x2, y2). The line that includes these two points has the equation
(x1, y1) + t · (x2 - x1, y2 - y1)
where t is some parameter. Notice that when t = 1, the point specified by the line is B, and when t = 0, the point specified by the line is A.
Now, you would like to move B to B', a point which is a new distance d away from A:
A B' B
(+)---------------------(+)-----------(+)
<========={ d }=========>
The point B', like any other point on the line, is also governed by the equation we showed earlier. But what value of t do we use? Well, when t is 1, the equation points to B, which is |AB| units away from A. So the value of t that specifies B' is t = d/|AB|.
Solving for |AB| and plugging this into the above equation is left as an exercise to the reader.
Vectors to the rescue!
Given points A and B. Create a vector V from A to B (by doing B-A). Normalize vector V into a unit vector and then just multiply it with the distance, d, you want and finally add the resulting vector to point A. ie:
A_moved = A + |(B-A)|*d
Java(ish)
Vector2D a_moved = a.add(b.subtract(a).norm().multiply(d));
No angles, no nasty trig needed.
double angle = Math.atan2(landingCoordinate.y-coordinate.y, landingCoordinate.x-coordinate.x);
coordinate.x += Math.cos(angle)*distance;
coordinate.y += Math.sin(angle)*distance;
//Add 90 degress to the plane angle
plane.setAngle(angle + 1.57079633);
(point A is to be moved closer to B)
if (A.x > B.x)
//decrement A.x
if (A.x < B.x)
//increment A.x
that might be the basic idea in pseudocode, but there's a lot more to it than that. How do you define 'best' way?
Certain things need to be considered:
Is there a specific measure/ratio you want the points to be apart from each other, or do you just want them closer?
Should both coordinates always be modified? (e.g. if you want to move (1,50) closer to (0,0), do you make it (.5, 25) or just (1,25)?
if the point is to be 1 unit away, should it be 1 unit horizontally? directly diagonally? 1 unit away on the line between the 2 points?
Give us a little more detail and we'll see what we've got. :D
In this game, integral coordinates are used to represent squares in a grid. The method move(int row, int col) moves toward the specified row and column by advancing one square in one of eight semi-cardinal directions, as seen here.

Categories