Trouble calculating the distance function in bezier clipping - java

I'm attempting to implement a curve interesection algorithm known as bezier clipping, which is described in a section towards the end of this article (though the article calls it "fat line clipping"). I've been following through the article and source code of the example (available here).
Note: Additional sources include this paper. More will be posted if I can find them.
A central part of this algorithm is calculating a "distance function" between curve1 and a "baseline" of curve2 (which is a line from one end point of curve2 to another). So I'd have something to compare my results to, I used the curves from the source code of the first example. I managed to replicate the shape of the distance function from the example, but the distance location of the function was off. Upon trying another curve, the distance function was nowhere near the other two curves, despite both clearly intersecting. I might be naive to the workings of this algorithm, but I think that would result in no intersection being detected.
From what I understand (which could quite possibly be wrong), the process of defining the distance function involves expressing the baseline of curve 2 in the form xa + yb + c = 0, where a2 + b2 = 1. The coefficients were obtained by rearranging the terms of the line in the form y = ux + v, where u is equal to the slope, and x and y are any points on the baseline. The formula can be rearranged to give v: v = y - ux. Rearranging the formula again, we obtain -u*x + 1*y - v = 0, where a = -u, b = 1, and c = -v. To assure the condition a2 + b2 = 1, the coefficients are divided by a scalar of Math.sqrt(uu + 1). This representation of the line is then substituted into the function of the other curve (the one the baseline isn't associated with) to get the distance function. This distance function is represented as a bezier curve, with yi = aPi x + b*Pi y + c and xi = (1 - t)x1 + tx2, where t is equal to 0, 1/3, 2/3, and 3m x1 and x2 are the endpoints of the baseline, and Pi are the control points of the curve1.
Below are a few cuts of the source code of the example program (written in the language processing) involved with calculating the distance function, which, oddly, uses a slightly different approach to the above paragraph for calculating the alternative representation of the baseline.
/**
* Set up four points, to form a cubic curve, and a static curve that is used for intersection checks
*/
void setupPoints()
{
points = new Point[4];
points[0] = new Point(85,30);
points[1] = new Point(180,50);
points[2] = new Point(30,155);
points[3] = new Point(130,160);
curve = new Bezier3(175,25, 55,40, 140,140, 85,210);
curve.setShowControlPoints(false);
}
...
flcurve = new Bezier3(points[0].getX(), points[0].getY(),
points[1].getX(), points[1].getY(),
points[2].getX(), points[2].getY(),
points[3].getX(), points[3].getY());
...
void drawClipping()
{
double[] bounds = flcurve.getBoundingBox();
// get the distances from C1's baseline to the two other lines
Point p0 = flcurve.points[0];
// offset distances from baseline
double dx = p0.x - bounds[0];
double dy = p0.y - bounds[1];
double d1 = sqrt(dx*dx+dy*dy);
dx = p0.x - bounds[2];
dy = p0.y - bounds[3];
double d2 = sqrt(dx*dx+dy*dy);
...
double a, b, c;
a = dy / dx;
b = -1;
c = -(a * flcurve.points[0].x - flcurve.points[0].y);
// normalize so that a² + b² = 1
double scale = sqrt(a*a+b*b);
a /= scale; b /= scale; c /= scale;
// set up the coefficients for the Bernstein polynomial that
// describes the distance from curve 2 to curve 1's baseline
double[] coeff = new double[4];
for(int i=0; i<4; i++) { coeff[i] = a*curve.points[i].x + b*curve.points[i].y + c; }
double[] vals = new double[4];
for(int i=0; i<4; i++) { vals[i] = computeCubicBaseValue(i*(1/3), coeff[0], coeff[1], coeff[2], coeff[3]); }
translate(0,100);
...
// draw the distance Bezier function
double range = 200;
for(float t = 0; t<1.0; t+=1.0/range) {
double y = computeCubicBaseValue(t, coeff[0], coeff[1], coeff[2], coeff[3]);
params.drawPoint(t*range, y, 0,0,0,255); }
...
translate(0,-100);
}
...
/**
* compute the value for the cubic bezier function at time=t
*/
double computeCubicBaseValue(double t, double a, double b, double c, double d) {
double mt = 1-t;
return mt*mt*mt*a + 3*mt*mt*t*b + 3*mt*t*t*c + t*t*t*d; }
And here is the class (an extension of javax.swing.JPanel) I wrote to recreate the above code:
package bezierclippingdemo2;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class ReplicateBezierClippingPanel extends JPanel {
CubicCurveExtended curve1, curve2;
public ReplicateBezierClippingPanel(CubicCurveExtended curve1, CubicCurveExtended curve2) {
this.curve1 = curve1;
this.curve2 = curve2;
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(1));
g2d.setColor(Color.black);
drawCurve1(g2d);
drawCurve2(g2d);
drawDistanceFunction(g2d);
}
public void drawCurve1(Graphics2D g2d) {
double range = 200;
double t = 0;
double prevx = curve1.x1*(1 - t)*(1 - t)*(1 - t) + 3*curve1.ctrlx1*(1 - t)*(1 - t)*t + 3*curve1.ctrlx2*(1 - t)*t*t + curve1.x2*t*t*t;
double prevy = curve1.y1*(1 - t)*(1 - t)*(1 - t) + 3*curve1.ctrly1*(1 - t)*(1 - t)*t + 3*curve1.ctrly2*(1 - t)*t*t + curve1.y2*t*t*t;
for(t += 1.0/range; t < 1.0; t += 1.0/range) {
double x = curve1.x1*(1 - t)*(1 - t)*(1 - t) + 3*curve1.ctrlx1*(1 - t)*(1 - t)*t + 3*curve1.ctrlx2*(1 - t)*t*t + curve1.x2*t*t*t;
double y = curve1.y1*(1 - t)*(1 - t)*(1 - t) + 3*curve1.ctrly1*(1 - t)*(1 - t)*t + 3*curve1.ctrly2*(1 - t)*t*t + curve1.y2*t*t*t;
g2d.draw(new LineExtended(prevx, prevy, x, y));
prevx = x;
prevy = y;
}
}
public void drawCurve2(Graphics2D g2d) {
double range = 200;
double t = 0;
double prevx = curve2.x1*(1 - t)*(1 - t)*(1 - t) + 3*curve2.ctrlx1*(1 - t)*(1 - t)*t + 3*curve2.ctrlx2*(1 - t)*t*t + curve2.x2*t*t*t;
double prevy = curve2.y1*(1 - t)*(1 - t)*(1 - t) + 3*curve2.ctrly1*(1 - t)*(1 - t)*t + 3*curve2.ctrly2*(1 - t)*t*t + curve2.y2*t*t*t;
for(t += 1.0/range; t < 1.0; t += 1.0/range) {
double x = curve2.x1*(1 - t)*(1 - t)*(1 - t) + 3*curve2.ctrlx1*(1 - t)*(1 - t)*t + 3*curve2.ctrlx2*(1 - t)*t*t + curve2.x2*t*t*t;
double y = curve2.y1*(1 - t)*(1 - t)*(1 - t) + 3*curve2.ctrly1*(1 - t)*(1 - t)*t + 3*curve2.ctrly2*(1 - t)*t*t + curve2.y2*t*t*t;
g2d.draw(new LineExtended(prevx, prevy, x, y));
prevx = x;
prevy = y;
}
}
public void drawDistanceFunction(Graphics2D g2d) {
double a = (curve1.y2 - curve1.y1)/(curve1.x2 - curve1.x1);
double b = -1;
double c = -(a*curve1.x1 - curve1.y1);
double scale = Math.sqrt(a*a + b*b);
a /= scale;
b /= scale;
c /= scale;
double y1 = a*curve2.x1 + b*curve2.y1 + c;
double y2 = a*curve2.ctrlx1 + b*curve2.ctrly1 + c;
double y3 = a*curve2.ctrlx1 + b*curve2.ctrly2 + c;
double y4 = a*curve2.x2 + b*curve2.y2 + c;
double range = 200;
double t = 0;
double prevx = t*range;
double prevy = (1 - t)*(1 - t)*(1 - t)*y1 + 3*(1 - t)*(1 - t)*t*y2 + 3*(1 - t)*t*t*y3 + t*t*t*y4;
for(t += 1.0/range; t < 1.0; t += 1.0/range) {
double x = t*range;
double y = (1 - t)*(1 - t)*(1 - t)*y1 + 3*(1 - t)*(1 - t)*t*y2 + 3*(1 - t)*t*t*y3 + t*t*t*y4;
g2d.draw(new LineExtended(prevx, prevy, x, y));
prevx = x;
prevy = y;
}
}
}
Where CubicCurveExtended and LineExtended are minor extensions of java.awt.geom.CubicCurve2D.Double and java.awt.geom.Line2D.Double. Before the curves are passed into the constructor, the curves are rotated uniformly so curve1's endpoints are level, resulting in a slope of zero for the baseline.
For an input of (485, 430, 580, 60, 430, 115, 530, 160) for curve 1 and (575, 25, 455, 60, 541, 140, 486, 210) for curve2 (keep in mind that these values are rotated by the negative angle between the endpoints of curve1), the result is shown below (the distance function is the relatively smooth looking curve off in the distance):
I'm really not sure what I got wrong. The y values seem to be arranged in the right pattern, but are distant from the two curves it's based on. I realize it's possible I have the x values might be arranged at intervals along the curve rather than the baseline, but the y values are what I'm really confused about. If someone can take a look at this and explain what I got wrong, I'd really appreciate it. Thanks for taking the time to read this rather lengthy question. If more details are needed, feel free to tell me in comments.
Update: I've tested the representation of the line I've calculated. The ax + by + c = 0 representation apparently still represents the same line, as I can still plug in x1 and get y1. Additionally, for any two coordinate pairs plugged into the function, f(x, y) = 0 holds. Furthermore, I've found both the representation described in the article and the one actually used in the source code interchangeably represent the same line. From all this, I can assume the problem doesn't lie in calculating the line. An additional point of interest

Your distance function should not necessarily be anywhere near your two original curves: It's using a completely different coordinate system, i.e. t vs D, as opposed to your original curves using x and y. [edit] i.e. t only goes up to 1.0, and measures how far along, as a ratio of the total length, you are along your curve, and D measuring the distance your curve2 is from curve1's baseline.
Also, when you say ""distance function" between curve1 and a "baseline" of curve2" I think you've mixed up curve1 and curve2 here as in your code you are clearly using the baseline of curve1.
Also the algorithm assumes that "every Bézier curve is fully contained by the polygon that connects all the start/control/end points, known as its "convex hull"" which [edit] in your case for curve1 is a triangle, where the control point for the second starting value is not a vertex. I'm not sure how this affects the algorithm though.
Otherwise, it looks like your distance calculations are fine (although you could really do with optimising things a bit :) ).

Related

How to find a point in a line in java?

I am trying to do some experiments with path. I have two points (x0,y0) and (x2,y2). Now I have to find a point (x1,y1) that should be some distance from the final point (x2,y2).
For example
Start Point (0,0) End Point (0,5)
Point i want to find is (0,2)
For a line between
The point at distance d from the first point, (positive) in the direction of the second point, is given by:
Where L is the distance between the two points defining the line:
(For your case just take L - d instead of d)
Imagine the two points define two rectangle triangles. The bigger triangle has sides with sizes x1, y1. the smaller has sides of sizes xt, yt.
1) Now apply Pythagoras' Theorem two calculate the bigger hypotenuse, h, using the equation h^2 = x1^2 + y1^2; (where h^2 means h power of two)
2) the difference ( h - the distance) is the hypotenuse of the smaller triangle. let's call it ht.
3) Calculate xt and yt as directly proportional to hypot bigger/hypot smaller.
x1/xt = h/ht
y1/yt = h/ht
/* I used user3235832 formula and write this code and its solve my problem.
Code in java.
sourcePoint(x,y);
destinationPoint(w,h);*/
//--------------------------------------------------------------------
double l = Math.sqrt(Math.pow((w - x), 2) + Math.pow((h - y), 2));
double d = l / 10;//you can use your own value for d.
int newX = (int) (w + (((x - w) / (l) * d)));
int newy = (int) (h + (((y - h) / (l) * d)));
//--------------------------------------------------------------------
/* if you use like me Graphic2D and use g2d.fillOval() function for draw a oval
on the line you need line slop. code for this: */
double m = Double.POSITIVE_INFINITY;//line slop
if (w - x != 0) {
m = (h - y) / (w - x);
}
int r = 6;//size of Oval
if (m == 0) {
g2d.fillOval(newX, (newy - (r/2)), r, r);
} else if (m == Double.POSITIVE_INFINITY) {
g2d.fillOval((newX - (r/2)), newy, r, r);
} else if (m < 0) {
g2d.fillOval((newX - (r/2)), (newy - (r/2)), r, r);
} else {
g2d.fillOval(newX, newy, r, r);
}

Making a circle in an array (Tile based game light map)

I am making a tiled based game in java and I want to make a light map.
I am having some issues. I have the lightmap array that has lights placed on it that affect the array. Lights emit in a circle shape. It seems ok so far but its not exactly what I wanted.
Here is my code so far:
for(float i = 0; i < strength + 1; i++){
for(double u = 0.0f; u < 360; u += 0.5){
double angle = u * Math.PI / 180;
int x2 = (int)(x + i * Math.cos(angle));
int y2 = (int)(y + i * Math.sin(angle));
if(map[y2][x2] > 1 - 1 / i)
map[y2][x2] = 1 - 1 / i;
}
}
Result:
As you can see in the result, it seems as though the light is expanding too much on the bottom left side (red x's). How do I fix this?
Background info:
Strength:
The radius of how far the light reaches. This also
determines how bright the light will be at each tile of the array.
The Array "map" is a 2D float array. The engine I am using uses float
values for the alpha channel. The range is 0 (completely transparent)
to 1 (completely opaque).
Solution (Thanks to Gene):
for(int x2 = -strength; x2 <= strength; x2++){
for (int y2 = -strength; y2 <= strength; y2++) {
double r = Math.sqrt(x2 * x2 + y2 * y2);
double inv_rad = r <= strength + 1 ? 1 / r : 0;
if(map[y + y2][x + x2] > 1 - (float) inv_rad)
map[y + y2][x + x2] = 1 - (float) inv_rad;
}
}
Your algorithm suffers from integer truncation of the map indicies. Try it the other away around. Compute the distance from each pixel in a square surrounding the center to the center. From this distance calculate what the intensity ought to be. It will be something like this:
for (x = -R; x <= R; x++)
for (y = -R; y <= R; y++) {
double r = Math.sqrt(x * x + y * y);
double inv_rad = r <= R ? 1 / r : 0; // truncate outside radius R
map[yc + y][xc + x] = 1 - inv_rad;
}
Here xc and yc are the integer center coordinates. R is the half-size of the box around the center.
when i try to add this to my project i only get o.o back
the values i entered where 500, 500,50
private float map[][] = new float[1000][1000];
public void test(int x, int y, float strength){
public void addLight(int x,int y,int strength ){
for(int x2 = -strength; x2 <= strength; x2++){
for (int y2 = -strength; y2 <= strength; y2++) {
double r = Math.sqrt(x2 * x2 + y2 * y2);
double inv_rad = r <= strength + 1 ? 1 / r : 0;
if(map[y + y2][x + x2] > 1 - (float) inv_rad)
map[y + y2][x + x2] = 1 - (float) inv_rad;
System.out.println(map[y + y2][x + x2]);
}
}
}

Point on Line by distance to first point

I want to calculate a point on a line by the distance to the first point.
Because I dont have any coordinates of the new point, i can't use the linear interpolation...
I thought like this:
Example Drawing
(Sorry, I'm a new user and I am not allowed to post images)
But actually it doesnt work, so I ask you for help.
Here is the actual code in java:
public static PointDouble interpolationByDistance(Line l, double d) {
double x1 = l.p1.x, x2 = l.p2.x;
double y1 = l.p1.y, y2 = l.p2.y;
double ratioP = ratioLine_x_To_y(l);
double disP = l.p1.distance(l.p2);
double ratioDis = d / disP;
PointDouble pn = l.p2.getLocation();
pn.multi(ratioDis);
System.out.println("dis: " + d);
System.out.println("new point dis: " + l.p1.distance(pn));
return pn;
}
Thank you.
As a programmer you should love changing a problem to a one you have already solved. Find the ratio and then use the linear interpolation:
public static PointDouble interpolationByDistance(Line l, double d) {
double len = l.p1.distance(l.p2);
double ratio = d/len;
double x = ratio*l.p2.x + (1.0 - ratio)*l.p1.x;
double y = ratio*l.p2.y + (1.0 - ratio)*l.p1.y;
System.out.println(x + ", " + y);
...
}
The basics of this are quite simple:
f = 0.3;
xp = f * x1 + (1-f) * x2;
yp = f * y1 + (1-f) * y2;
To understand this, consider:
if f==0, then xp = x2, yp=y2
if f==1, then xp = x1, yp=y1
for any value of f between 0..1, you get a point between (x1,y1)..(x2,y2)
I'm not sure what you exactly intend to calculate. This takes a value f in the range 0..1. If you have d as an absolute length, do f=d/disP

Oval collision detection not working properly

So I'm trying to implement a test where a oval can connect with a circle, but it's not working.
edist = (float) Math.sqrt(
Math.pow((px + ((pwidth/2) )) - (bx + (bsize/2)), 2 ) +
Math.pow(-((py + ((pwidth/2)) ) - (bx + (bsize/2))), 2 )
);
and here is the full code (requires Slick2D):
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
public class ColTest extends BasicGame{
float px = 50;
float py = 50;
float pheight = 50;
float pwidth = 50;
float bx = 200;
float by = 200;
float bsize = 200;
float edist;
float pspeed = 3;
Input input;
public ColTest()
{
super("ColTest");
}
#Override
public void init(GameContainer gc)
throws SlickException {
}
#Override
public void update(GameContainer gc, int delta)
throws SlickException
{
input = gc.getInput();
try{
if(input.isKeyDown(Input.KEY_UP))
py-=pspeed;
if(input.isKeyDown(Input.KEY_DOWN))
py+=pspeed;
if(input.isKeyDown(Input.KEY_LEFT))
px-=pspeed;
if(input.isKeyDown(Input.KEY_RIGHT))
px+=pspeed;
}
catch(Exception e){}
}
public void render(GameContainer gc, Graphics g)
throws SlickException
{
g.setColor(new Color(255,255,255));
g.drawString("col: " + col(), 10, 10);
g.drawString("edist: " + edist + " dist: " + dist, 10, 100);
g.fillRect(px, py, pwidth, pheight);
g.setColor(new Color(255,0,255));
g.fillOval(px, py, pwidth, pheight);
g.setColor(new Color(255,255,255));
g.fillOval(200, 200, 200, 200);
}
public boolean col(){
edist = (float) Math.sqrt(Math.pow((px + ((pwidth/2) )) - (bx + (bsize/2)), 2) + Math.pow(-((py + ((pwidth/2)) ) - (bx + (bsize/2))), 2));
if(edist <= (bsize/2) + (px + (pwidth/2)))
return true;
else
return false;
}
public float rotate(float x, float y, float ox, float oy, float a, boolean b)
{
float dst = (float) Math.sqrt(Math.pow(x-ox,2.0)+ Math.pow(y-oy,2.0));
float oa = (float) Math.atan2(y-oy,x-ox);
if(b)
return (float) Math.cos(oa + Math.toRadians(a))*dst+ox;
else
return (float) Math.sin(oa + Math.toRadians(a))*dst+oy;
}
public static void main(String[] args)
throws SlickException
{
AppGameContainer app =
new AppGameContainer( new ColTest() );
app.setShowFPS(false);
app.setAlwaysRender(true);
app.setTargetFrameRate(60);
app.setDisplayMode(800, 600, false);
app.start();
}
}
Is using ovals an absolute requirement? You can approximate collisions between fancier shapes by representing them with multiple circles. That way you can use very a simple collision detection between circles and still achieve a high level of accuracy for the viewer.
collision(c1, c2) {
dx = c1.x - c2.x;
dy = c1.y - c2.y;
dist = c1.radius + c2.radius;
return (dx * dx + dy * dy <= dist * dist)
}
(source: strd6.com)
Finding the intersection is harder than you think. Your col() method is a bit off, but that approach will at best be able to tell you if a single point is within the circle. It won't be able to really detect intersections.
I Googled up some code for computing the actual intersections. I found one in JavaScript that's really interesting and really complicated. Take a look at the source.
If you wanted something a bit simpler (but less accurate), you could check a few points around the ellipse to see if they're within the circle.
private boolean isInCircle(float x, float y) {
float r = bsize / 2;
float center_x = bx + r;
float center_y = by + r;
float dist = (float) Math.sqrt(Math.pow(x - center_x, 2) + Math.pow(y - center_y, 2));
return dist < r;
}
public boolean col() {
return
isInCircle(px + pwidth / 2, py ) || // top
isInCircle(px + pwidth , py + pheight / 2) || // right
isInCircle(px + pwidth / 2, py + pheight ) || // bottom
isInCircle(px , py + pheight / 2); // left
}
If you plan on implementing more shapes and/or need the minimum distance between your shapes, you could start using GJK : you would only need to implement the support functions for each new shape. If computation time is also critical, GJK is definitely something you should look at, but it would surely require some more programming on your side.
If you can find your foci you can check for collision with the pseudo code below.
WARNING this only works for two ellipse collisions (ellipse and circle collisions work also).
r = length of semi major axis
a_x = x coordinate of foci 1 of the first ellipse
a_y = y coordinate of foci 1 of the first ellipse
b_x = x coordinate of foci 2 of the first ellipse
b_y = y coordinate of foci 2 of the first ellipse
c_x = x coordinate of foci 1 of the second ellipse
c_y = y coordinate of foci 1 of the second ellipse
d_x = x coordinate of foci 2 of the second ellipse
d_y = y coordinate of foci 2 of the second ellipse
p_x = (a_x+b_x+c_x+d_x)/4 // i.e. the average of the foci x values
p_y = (a_y+b_y+c_y+d_y)/4 // i.e. the average of the foci y values
if r >= ( sqrt( (p_x + a_x)^2+(p_y + a_y)^2 ) + sqrt( (p_x + a_x)^2+(p_y + a_y)^2 ) )
then collision
If you really want the derivation of this let me know and I'll provide it. But it uses the idea that the sum of the distances between the foci of an ellipse and any point on the edge of an ellipse are a set distance apart (the semi major axis). And solves for a point that is on the edge of both ellipsoids and if one exist then their is a collision.

Calculating the distance between two points

I need to create a class which calculates the distance between two points. I am stuck and I am a total beginner. Here are my classes:
package org.totalbeginner.tutorial;
public class Point {
public double x;
public double y;
Point(double xcoord, double ycoord){
this.x = xcoord;
this.y = ycoord;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
}
The second class.
package org.totalbeginner.tutorial;
public class Line {
double x;
double y;
Point p1 = new Point(2.0,2.0);
Point p2 = new Point(4.0,4.0);
Point mp = new Point(x,y);
public void midpoint() {
x = (p1.getX() + p2.getX()) / 2;
y = (p1.getY() + p2.getY()) / 2;
}
}
I am not sure how to get a point object (the middle point) between both defined points.
I can create point objects but I am not sure how to return a point object through my midpoint() method that lies between those two point objects.
The distance between two points (x1,y1) and (x2,y2) on a flat surface is:
____________________
/ 2 2
\/ (y2-y1) + (x2-x1)
But, if all you want is the midpoint of your two points, you should change your midpoint function to:
public Point midpoint (Point p1, Point p2) {
return new Point ((p1.getX() + p2.getX()) / 2, (p1.getY() + p2.getY()) / 2);
}
This will return a brand new point object with the points set to the middle of the given two points (without having to concern yourself with any other math). And, since your second class is a line, you only need the two end points to describe it, so I'd make some minor changes.
First Point.java:
class Point {
double x, y;
Point (double xcoord, double ycoord) {
this.x = xcoord;
this.y = ycoord;
}
public double getX() { return x; }
public double getY() { return y; }
}
Then Line.java:
public class Line {
Point p1, p2;
Line (Point point1, Point point2) {
this.p1 = point1;
this.p2 = point2;
}
public Point midpoint() {
return new Point ((p1.getX()+p2.getX())/2, (p1.getY()+p2.getY())/2);
}
public double abstand() {
return Math.sqrt(
(p1.getX() - p2.getX()) * (p1.getX() - p2.getX()) +
(p1.getY() - p2.getY()) * (p1.getY() - p2.getY())
);
}
static public void main (String args[]) {
Line s = new Line (new Point(2.0, 2.0), new Point(5.0, 6.0));
Point mp = s.midpoint();
System.out.println ("Midpoint = (" + mp.getX() + "," + mp.getY() + ")");
double as = s.abstand();
System.out.println ("Length = " + as);
}
}
These two files, when compiled and run with the endpoints 2,2 and 5,6 (the hypotenuse of a classic 3/4/5 right-angled triangle), generate the correct:
Midpoint = (3.5,4.0)
Length = 5.0
Simple Pythag... root(dx^2 + dy^2)
Math.sqrt(Math.pow((p2.getX() - p1.getX()), 2) + Math.pow((p2.getY() - p1.getY()), 2))
X
+
|\
| \
a| \c
| \
| \
+-----+
b Y
Imagine X and Y are your points on a flat surface. Then a is X.y - Y.y and b is Y.x - X.x . The length of c is their distance, and is the length of the hypotenuse of that triangle. It is calculated using
sqrt(a^2 + b^2);
Since you see we are squaring a and b, the sign of them isn't relevant - it will come down to the same. So this method always works, where ever the points lie.
Lookup the Pythagorean theorem
Do you really need the distance, or are you trying to just get the midpoint? Because from your code snippet, it kind of looks like you just want to create a new point that is half-way between two existing points.
If you're really just after the midpoint, you don't really need an entire 2nd class (i.e., 'Line') to accomplish that. Since the thing you are trying to find is also a point, it makes sense to add a constructor to your existing Point class, like so ..
Point(Point a, Point b)
{
x = (a.x + b.x) / 2;
y = (a.y + b.y) / 2;
}
.. then, elsewhere let's say you already have a couple of points you want to use this on, you use the constructor thus:
Point p1 = new Point(2,2);
Point p2 = new Point(4,4);
Point midpoint = new Point(p1, p2);
and if you really want distance between two points, that's not really an attribute of either point, so it makes sense to use a static method for that, like so
public static double distance(Point a, Point b)
{
double dx = a.x - b.x;
double dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
and back in the calling code, you can use it this way:
Point p1 = new Point(2,2);
Point p2 = new Point(4,4);
System.out.println("Distance between them is " + Point.distance(p1, p2));
You can use a Maths function for this:
public Point midpoint() {
//Calculate the difference between the old and new x/y
double dx = p1.getX() - p2.getX();
double dy = p1.getY() - p2.getY();
double newX = Math.pow(dx, 2D);
double newY = Math.pow(dz, 2D);
return new Point(newX, newZ);
}
Math.pow handles the issues with negative values and etc. For you,
using Math.pow gives you a safe method because it has a lot of checks built inside.
You can use the Pythagorean Theorem, as other said. Here is a visually demostration from the Wolfram Demostration Project.
alt text http://demonstrations.wolfram.com/DistanceBetweenTwoPoints/HTMLImages/index.en/popup_5.jpg
In your second class, it looks like you're trying to set the values of x and y that are used to construct your mp variable. All your formulas are correct, but you need to consider the order that everything is executed. In the code as it is, it's creating the x and y variables, which start out as 0, then the various Points. x and y are still 0, so mp is set to Point(0, 0).
What you probably want to do is change the return type of midpoint to Point, so that when you call that function, you get back a Point. Then you can create a new Point object with the values you calculate. It should look more like this:
public Point midpoint() {
// calculate the middle x and y
double x = (p1.getX() + p2.getX()) / 2;
double y = (p1.getY() + p2.getY()) / 2;
// now make a new Point with those values, and return it
return new Point(x, y);
}

Categories

Resources