Coordinates of centre, equilateral triangle - java

My current triangle class looks like:
public class TriangleEquilateral {
private Point cornerA;
private Point cornerB;
private Point cornerC;
private double x1 = 0;
private double y1 = 0;
private double x2 = 10;
private double y2 = 0;
private double x3 = 5;
private double y3 = Math.sqrt(75);
public TriangleEquilateral(){
cornerA = new Point(x1,y1);
cornerB = new Point(x2,y2);
cornerC = new Point(x3,y3);
}
public TriangleEquilateral(double X1,double Y1,double X2,double Y2,double X3,double Y3){
x1 = X1;
y1 = Y1;
x2 = X2;
y2 = Y2;
x3 = X3;
y3 = Y3;
cornerA = new Point(X1,Y1);
cornerB = new Point(X2,Y2);
cornerC = new Point(X3,Y3);
}
public boolean isEquilateral(){
double lengthAB = Math.sqrt(Math.pow(x1-x2,2) + Math.pow(y1-y2,2));
double lengthBC = Math.sqrt(Math.pow(x2-x3,2) + Math.pow(y2-y3,2));
double lengthCA = Math.sqrt(Math.pow(x3-x1,2) + Math.pow(y3-y1,2));
boolean isEquilateral = false;
if(lengthAB == lengthBC && lengthBC == lengthCA && lengthCA == lengthAB){
isEquilateral = true;
}
System.out.println(lengthAB);
System.out.println(lengthBC);
System.out.println(lengthCA);
return isEquilateral;
}
public double sideLength(){
double sL = 0;
if(this.isEquilateral() == true){
sL = Math.sqrt(Math.pow(x1-x2,2) + Math.pow(y1-y2,2));
}
return sL;
}
How would I determine the coordinates of the midpoint of an equilateral triangle? I know that the midpoint is x = (base/2), y = heigth/2 but this only works if the base is horizontal(two corners have the same y value)

For equilateral triangle, coordinates of the triangle's center are the same as the coordinates of the center of its incircle.
Look up the formula for the incircle's center on Wikipedia:
{ (aXa+bXb+cXc)/(a+b+c), (aYa+bYb+cYc)/(a+b+c) }
Since a = b = c, it is easy to see that the coordinates of the center of an equilateral triangle are simply
{ (x0+x1+x2)/3, (y0+y1+y2)/3 }

This is more of a Math question than a java question. Anyway, to find the barycenter:
x = (x1 + x2 + x3) / 3
y = (y1 + y2 + y3) / 3

Related

How do I use a JOptionPane input from a test class in another class?

I currently have a Triangle class with all of my calculations.
public class Triangle
{
private double x1;
private double y1;
private double x2;
private double y2;
private double x3;
private double y3;
private double lengthA;
private double lengthB;
private double lengthC;
private double angleA;
private double angleB;
private double angleC;
private double perimeter;
private double height;
private double area;
public double calcArea()
{
area = .5 * lengthC * height;
return area;
}
public double calcPerimeter()
{
perimeter = lengthA + lengthB + lengthC;
return perimeter;
}
public double lengthA()
{
lengthA = Math.sqrt(Math.pow((x2 - x3),2) + Math.pow(height,2));
return lengthA;
}
public double lengthB()
{
lengthB = Math.sqrt(Math.pow((x3 - x1),2) + Math.pow(height,2));
return lengthB;
}
public double lengthC()
{
lengthC = x2 - x1;
return lengthC;
}
public double getHeight()
{
height = y3 - y1;
return height;
}
public double angleA()
{
angleA = Math.abs(Math.toDegrees(Math.asin(height / lengthB)));
return angleA;
}
public double angleB()
{
angleB = Math.abs(Math.toDegrees(Math.asin(height / lengthA)));
return angleB;
}
public double angleC()
{
angleC = 180 - angleA - angleB;
return angleC;
}
}
I also have a TriangleTester class that uses JOptionPane to get coordinates for the triangle.
import javax.swing.*;
public class TriangleTester
{
public static void main (String [] args)
{
double x1;
double y1;
double x2;
double y2;
double x3;
double y3;
String v1;
String v2;
String v3;
String v4;
String v5;
String v6;
v1 = JOptionPane.showInputDialog("Enter x1 for point A");
v2 = JOptionPane.showInputDialog("Enter y1 for point A");
v3 = JOptionPane.showInputDialog("Enter x2 for point B");
v4 = JOptionPane.showInputDialog("Enter y2 for point B");
v5 = JOptionPane.showInputDialog("Enter x3 for point C");
v6 = JOptionPane.showInputDialog("Enter y3 for point C");
x1 = Integer.parseInt(v1);
y1 = Integer.parseInt(v2);
x2 = Integer.parseInt(v3);
y2 = Integer.parseInt(v4);
x3 = Integer.parseInt(v5);
y3 = Integer.parseInt(v6);
Triangle tri = new Triangle();
double lengthA = tri.lengthA();
double lengthB = tri.lengthB();
double lengthC = tri.lengthC();
double angleA = tri.angleA();
double angleB = tri.angleB();
double angleC = tri.angleC();
double perimeter = tri.calcPerimeter();
double height = tri.getHeight();
double area = tri.calcArea();
System.out.printf("Set up triangle with coordinates (" + x1 + "," + y1 + "), (" + x2 + "," + y2 + "), (" + x3 + "," + y3 + ")");
System.out.printf("\nArea:\t\t\t\t" + area);
System.out.printf("\nPerimeter:\t\t" + perimeter);
System.out.printf("\nLength side a:\t" + lengthA);
System.out.printf("\nLength side b:\t" + lengthB);
System.out.printf("\nLength side c:\t" + lengthC);
System.out.printf("\nHeight h:\t\t" + height);
System.out.printf("\nAngle A:\t\t\t" + angleA);
System.out.printf("\nAngle B:\t\t\t" + angleB);
System.out.printf("\nAngle C:\t\t\t" + angleC);
}
}
When I run the tester code, it prints the coordinates with the correct x and y values, but everything else ends up printing as 0. I believe this is because I have not made any connection between the x and y values of the Triangle class and the TriangleTester class. How can I get the Triangle class to use the inputted x and y values from the TriangleTester class to calculate the answers? Thanks.
The main idea here was you were using a getter as a setter.
You were not passing the data for the setter methods between classes
Main program
package triangles;
import javax.swing.*;
public class Triangles {
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
double x1;
double y1;
double x2;
double y2;
double x3;
double y3;
String v1;
String v2;
String v3;
String v4;
String v5;
String v6;
v1 = JOptionPane.showInputDialog("Enter x1 for point A");
v2 = JOptionPane.showInputDialog("Enter y1 for point A");
v3 = JOptionPane.showInputDialog("Enter x2 for point B");
v4 = JOptionPane.showInputDialog("Enter y2 for point B");
v5 = JOptionPane.showInputDialog("Enter x3 for point C");
v6 = JOptionPane.showInputDialog("Enter y3 for point C");
x1 = Integer.parseInt(v1);
y1 = Integer.parseInt(v2);
x2 = Integer.parseInt(v3);
y2 = Integer.parseInt(v4);
x3 = Integer.parseInt(v5);
y3 = Integer.parseInt(v6);
Triangle tri = new Triangle();
//set all needed data
tri.setLengthA(x2,x3);
tri.setLengthB(x3,x1);
tri.setLengthC(x2,x1);
tri.setHeight(y3,y1);
// set calculations off the data
tri.setAngleA();
tri.setAngleB();
tri.setAngleC();
double perimeter = tri.calcPerimeter();
double area = tri.calcArea();
System.out.printf("Set up triangle with coordinates (" + x1 + "," + y1 + "), (" + x2 + "," + y2 + "), (" + x3 + "," + y3 + ")");
System.out.printf("\nArea:\t\t\t\t" + area);
System.out.printf("\nPerimeter:\t\t" + perimeter);
System.out.printf("\nLength side a:\t" + tri.lengthA);
System.out.printf("\nLength side b:\t" + tri.lengthB);
System.out.printf("\nLength side c:\t" + tri.lengthC);
System.out.printf("\nHeight h:\t\t" + tri.height);
System.out.printf("\nAngle A:\t\t\t" + tri.angleA);
System.out.printf("\nAngle B:\t\t\t" + tri.angleB);
System.out.printf("\nAngle C:\t\t\t" + tri.angleC);
}
}
and your class
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package triangles;
/**
*
* #author jstil
*/
public class Triangle {
private double x1;
private double y1;
private double x2;
private double y2;
private double x3;
private double y3;
public double lengthA;
public double lengthB;
public double lengthC;
public double angleA;
public double angleB;
public double angleC;
private double perimeter;
public double height;
private double area;
public double calcArea()
{
area = .5 * lengthC * height;
return area;
}
public double calcPerimeter()
{
perimeter = lengthA + lengthB + lengthC;
return perimeter;
}
public void setLengthA( double x2 , double x3)
{
lengthA = Math.sqrt(Math.pow((x2 - x3),2) + Math.pow(height,2));
}
public void setLengthB(double x3, double x1)
{
lengthB = Math.sqrt(Math.pow((x3 - x1),2) + Math.pow(height,2));
}
public void setLengthC(double x2, double x1)
{
lengthC = x2 - x1;
}
public void setHeight(double y3, double y1)
{
height = y3 - y1;
}
public void setAngleA()
{
angleA = Math.abs(Math.toDegrees(Math.asin(height/ lengthB)));
}
public void setAngleB()
{
angleB = Math.abs(Math.toDegrees(Math.asin(height / lengthA)));
}
public void setAngleC()
{
angleC = 180 - angleA - angleB;
}
}

Change orientation in JFrame

I am writing a simple application that draws the NYC subway map. I have succeeded in doing so and my maps prints in JFrame object. However, it prints on the wrong side because I assume it references the 0 0 point on the top left corner. How can I make it reference the bottom left corner so it will print the right way?
private final List<Shape> shapes;
private double maxLat = 40.903125;
private double maxLon = -73.755405;
private double minLat = 40.512764;
private double minLon = -74.251961;
private final double latLength;
private final double lonLength;
public Shapes() throws IOException {
this.shapes = new ArrayList<Shape>();
CSVReader in = new CSVReader(new FileReader(
"src/charnetskaya/subwaymap/shapes.txt"));
String[] line;
in.readNext();
while ((line = in.readNext()) != null) {
double lat = Double.valueOf(line[1]);
double lon = Double.valueOf(line[2]);
Shape shape = new Shape(line[0], lat, lon);
shapes.add(shape);
this.maxLat = Math.max(this.maxLat, shape.getLat());
this.maxLon = Math.max(this.maxLon, shape.getLon());
this.minLat = Math.min(this.minLat, shape.getLat());
this.minLon = Math.min(this.minLon, shape.getLon());
}
this.latLength = Math.abs(this.maxLat - this.minLat);
this.lonLength = Math.abs(this.maxLon - this.minLon);
System.out.println(latLength + " " + lonLength);
}
graphics method
public void paintComponent(Graphics pen) {
System.out.println("Tring to draw");
Graphics2D pen2D = (Graphics2D) pen;
int width = getWidth();
int height = getHeight();
System.out.println(width + " | " + height);
double minLat = this.shapes.getMinLat();
double minLon = this.shapes.getMinLon();
double latLength = this.shapes.getLatLength();
double lonLength = this.shapes.getLonLength();
List<String> shapeIds = this.shapes.getShapeIds();
for (String shapeId : shapeIds) {
List<Shape> list = this.shapes.getShapes(shapeId);
Trip trip = this.trips.getTrip(shapeId);
if (trip != null) {
Color color = this.routes.getColor(trip.getRouteId());
pen2D.setColor(color);
for (int i = 1; i < list.size(); i++) {
Shape a = list.get(i - 1);
Shape b = list.get(i);
int x1 = (int) ((a.getLat() - minLat) / latLength * height);
int y1 = (int) ((a.getLon() - minLon) / lonLength * height);
int x2 = (int) ((b.getLat() - minLat) / latLength * height);
int y2 = (int) ((b.getLon() - minLon) / lonLength * height);
// if ((x1 != x2) || (y1 != y2)) {
pen2D.drawLine(x1, y1, x2, y2);
// }
}
}
}
Change
int x1 = (int) ((a.getLat() - minLat) / latLength * height);
int y1 = (int) ((a.getLon() - minLon) / lonLength * height);
to
int x1 = (int) ((a.getLon() - minLon) / lonLength * width);
int y1 = (int) ((maxLat - a.getlat()) / latLength * height);
and similarly for x2 and y2.

How to make a parallelogram shaped vertex in JGraphx?

I'm trying to display a flowchart via JGraphx, and I need a parallelogram for input/output block. But JGraphx doesn't seem to know "shape=parallelogram". It seems odd not to have a parallelogram in a library for graphs and flowcharts (and it even has "actor" shape, how come it doesn't have a parallelogram?). Maybe it's only named some other way? Or in the case there is indeed no predefined parallelogram shape, what do I do to make a vertex into parallelogram?
Finally, I've found a way to make a parallelogram, yay! Here is how I made it work.
First of all, to make a custom shape I had to create my own class extending mxBasicShape and override the createShape method.
public class Parallelogram extends mxBasicShape {
public Shape createShape(mxGraphics2DCanvas canvas, mxCellState state){
mxCell cell = (mxCell)state.getCell();
Polygon polygon = new Polygon();
if(cell != null && cell.getGeometry() != null) {
mxGeometry g = cell.getGeometry();
int dx = (int) (cell.getGeometry().getHeight()/4.0);
polygon.addPoint((int)(g.getX()+dx), (int)g.getY());
polygon.addPoint((int)(g.getX()+g.getWidth()+dx), (int)g.getY());
polygon.addPoint((int)(g.getX()+g.getWidth()-dx), (int)(g.getY()+g.getHeight()));
polygon.addPoint((int)((int)g.getX()-dx), (int)(g.getY()+g.getHeight()));
}
return polygon;
}
}
The second step is adding it to a list of shapes which appeared to be stored in mxGraphics2DCanvas.
mxGraphics2DCanvas.putShape("parallelogram", new Parallelogram());
And now "shape=parallelogram" works just fine!
UPD
It appeared creating just a shape is not enough and a perimeter is also to be created. This is how I've done it:
public class ParallelogramPerimeter implements mxPerimeterFunction {
#Override
public mxPoint apply(mxRectangle bounds, mxCellState vertex, mxPoint next,
boolean orthogonal) {
double cx = bounds.getCenterX();
double cy = bounds.getCenterY();
double nx = next.getX();
double ny = next.getY();
double pi = Math.PI;
double pi2 = Math.PI/2.0;
double h = bounds.getHeight();
double w = bounds.getWidth();
double alpha = Math.atan2(h/2.0, w/2.0+h/4.0);
double beta = Math.atan2(h/2.0, w/2.0-h/4.0);
double t = Math.atan2(ny-cy, nx-cx);
mxPoint p = new mxPoint();
//Left
if (t > pi - alpha || t < (-pi)+beta){
Line border = new Line(cx-w/2.0+h/4.0, cy-h/2.0, cx-w/2.0-h/4.0, cy+h/2.0);
Line line = new Line(cx, cy, nx, ny);
p = Line.intersection(border, line);
}
//Top
else if (t > (-pi)+beta && t < (0-alpha)){
p.setY(cy-h/2.0);
p.setX(cx + h/2.0*Math.tan(pi2+t));
}
//Right
else if (t > (0-alpha) && t < beta){
Line border = new Line(cx+w/2.0+h/4.0, cy-h/2.0, cx+w/2.0-h/4.0, cy+h/2.0);
Line line = new Line(cx, cy, nx, ny);
p = Line.intersection(border, line);
}
//Bottom
else {
p.setY(cy+h/2.0);
p.setX(cx + h/2.0*Math.tan(pi2-t));
}
if (orthogonal){
//Top
if (nx >= cx-w/2.0+h/4.0 && nx <= cx+w/2.0+h/4.0 && ny <= cy-h/2.0){
p.setX(nx);
}
//Bottom
else if (nx >= cx - w/2.0-h/4.0 && nx <= cx+w/2.0-h/4.0 && ny >= cy+h/2.0){
p.setX(nx);
}
//Left or right
else{
Line left = new Line(cx-w/2.0+h/4.0, cy-h/2.0, cx-w/2.0-h/4.0, cy+h/2.0);
Line right = new Line(cx+w/2.0+h/4.0, cy-h/2.0, cx+w/2.0-h/4.0, cy+h/2.0);
p = left.projection(nx, ny);
mxPoint p1 = right.projection(nx, ny);
boolean r = false;
if (distance(nx, ny, p.getX(), p.getY()) > distance(nx, ny, p1.getX(), p1.getY()))
{
p = p1;
r = true;
}
//Upper corners
if (p.getY() < cy-h/2.0){
p.setY(cy-h/2.0);
if(r){
p.setX(cx+w/2.0+h/4.0);
}
else
{
p.setX(cx-w/2.0+h/4.0);
}
}
//Lower corners
else if (p.getY() > cy+h/2.0){
p.setY(cy+h/2.0);
if(r){
p.setX(cx+w/2.0-h/4.0);
}
else
{
p.setX(cx-w/2.0-h/4.0);
}
}
}
}
return p;
}
private double distance(double x1, double y1, double x2, double y2){
return Math.sqrt(Math.pow(x2-x1, 2)+Math.pow(y2-y1, 2));
}
}
class Line{
private double a;
private double b;
private double c;
Line(double x1, double y1, double x2, double y2){
a = y1-y2;
b = x2-x1;
c = x1*y2-x2*y1;
}
private Line(double a, double b, double c){
this.a = a;
this.b = b;
this.c = c;
}
static private double determinant(double i, double j, double k, double l){
return i*l - k*j;
}
static mxPoint intersection(Line first, Line second){
double x,y;
double denominator = determinant(first.a, first.b, second.a, second.b);
x = 0 - determinant(first.c, first.b, second.c, second.b)/denominator;
y = 0 - determinant(first.a, first.c, second.a, second.c)/denominator;
return new mxPoint(x,y);
}
mxPoint projection(double x, double y){
double a,b,c;
if (this.b!=0){
a=1;
b=-(this.a*a)/this.b;
}
else{
b = 1;
a=-(this.b*b)/this.a;
}
c = -(a*x+b*y);
Line line = new Line(a,b,c);
return intersection(this, line);
}
}
Then I had to add it to the perimeters in use, whose list appeared to be not in the same place as with shapes, but in mxStyleRegistry:
mxStyleRegistry.putValue("parallelogramPerimeter", new ParallelogramPerimeter());
And finally I've used "shape=parallelogram;perimeter=parallelogramPerimeter" for a style, which now works not only for displaying the parallelogram, but also for connecting edges to it properly.
For completeness: equilateral parallelogram is predefined: SHAPE_RHOMBUS.

Cant figure out why x and y coordinates on my triangle class are are switched

I dont know if I did something wrong in my code, im testing my triangle class and for some reason cornerC of my triangle constructor switches the x with the y.
public class TriangleIsosceles {
private Point cornerA;
private Point cornerB;
private Point cornerC;
private int x1;
private int y1;
private int x2;
private int y2;
private int x3;
private int y3;
public TriangleIsosceles(){
cornerA = new Point(x1,y1);
cornerB = new Point(x2,y2);
cornerC = new Point(x3,y3);
}
public TriangleIsosceles(int X1,int Y1,int X2,int Y2,int X3,int Y3){
x1 = X1;
y1 = Y1;
x2 = X2;
y2 = Y2;
x3 = X3;
y3 = Y3;
cornerA = new Point(X1,Y1);
cornerB = new Point(X2,Y2);
cornerC = new Point(X3,Y3);
}
public boolean isIsosceles(){
double lengthAB = Math.sqrt(Math.pow(x1-x2,2) + Math.pow(y1-y2,2));
double lengthBC = Math.sqrt(Math.pow(x2-x3,2) + Math.pow(y2-y3,2));
double lengthCA = Math.sqrt(Math.pow(x3-x1,2) + Math.pow(y3-y1,2));
boolean isIsosceles = false;
if(lengthAB == lengthBC || lengthBC == lengthCA || lengthCA == lengthAB){
isIsosceles = true;
}
System.out.println(lengthAB);
System.out.println(lengthBC);
System.out.println(lengthCA);
return isIsosceles;
}
}
In my tester class I tried
TriangleIsosceles t2 = new TriangleIsosceles(0, 0, 0, 10, 0, 5);
System.out.println(t2.isIsosceles());
The output was 10.0
5.0
5.0
true
But when I try
TriangleIsosceles t2 = new TriangleIsosceles(0, 0, 0, 10, 5, 0);
System.out.println(t2.isIsosceles());
The output is 10.0
10.295630140987
5.0990195135927845
false
Your first test does not actually create a triangle. The 3 points (0,0), (0,10) and (0,5). All of them have X coordinates on the same plane - this is a line. So your triangle class doesn't validate it is a valid triangle, so you end with a line that is returning true for isIsoceles.
Your second triangle isn't actually isosceles. The points are (0,0), (0,10) and (5,0), which does not have two equal sides.

Application not working as expected

I have a standalone Java application below that is:
Generating a random line
Applied to a 2D grid where each cell value is the distance along the line perpindicular to the line
Finds the rise/run and attempts to calculate the original linear equation from the grid
Applies new line to another grid and prints out the greatest difference compared to the first grid
I expected the two grids to have identical values. The gradient lines may be different since the lines can extend outside the area of the grid, but should be similar and in two cases identical.
So is the problem a poor understanding of math, a bug in my code or a misunderstanding of floating point values?
import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.util.Iterator;
import java.util.ArrayList;
public final class TestGradientLine {
private static int SIZE = 3;
public TestGradientLine() {
super();
}
//y = mx + b
//b = y - mx
//m is rise / run = gradient
//width and height of bounding box
//for a box 10x10 then width and height are 9,9
public static Line2D getGradientLine(double run, double rise, double width, double height, double x, double y) {
if (run == 0 && rise == 0) {
return new Line2D.Double(x, y, x + width, y + height);
}
//calculate hypotenuse
//check for a vertical line
if (run == 0) {
return new Line2D.Double(x, y, x, y + height);
}
//check for a horizontal line
if (rise == 0) {
return new Line2D.Double(x, y, x + width, y);
}
//calculate gradient
double m = rise / run;
Point2D start;
Point2D opposite;
if (m < 0) {
//lower left
start = new Point2D.Double(x, y + height);
opposite = new Point2D.Double(x + width, y);
} else {
//upper left
start = new Point2D.Double(x, y);
opposite = new Point2D.Double(x + width, y + height);
}
double b = start.getY() - (m * start.getX());
//now calculate another point along the slope
Point2D next = null;
if (m > 0) {
next = new Point2D.Double(start.getX() + Math.abs(run), start.getY() + Math.abs(rise));
} else {
if (rise < 0) {
next = new Point2D.Double(start.getX() + run, start.getY() + rise);
} else {
next = new Point2D.Double(start.getX() - run, start.getY() - rise);
}
}
final double actualWidth = width;
final double actualHeight = height;
final double a = Math.sqrt((actualWidth * actualWidth) + (actualHeight * actualHeight));
extendLine(start, next, a);
Line2D gradientLine = new Line2D.Double(start, next);
return gradientLine;
}
public static void extendLine(Point2D p0, Point2D p1, double toLength) {
final double oldLength = p0.distance(p1);
final double lengthFraction =
oldLength != 0.0 ? toLength / oldLength : 0.0;
p1.setLocation(p0.getX() + (p1.getX() - p0.getX()) * lengthFraction,
p0.getY() + (p1.getY() - p0.getY()) * lengthFraction);
}
public static Line2D generateRandomGradientLine(int width, int height) {
//so true means lower and false means upper
final boolean isLower = Math.random() > .5;
final Point2D start = new Point2D.Float(0, 0);
if (isLower) {
//change origin for lower left corner
start.setLocation(start.getX(), height - 1);
}
//radius of our circle
double radius = Math.sqrt(width * width + height * height);
//now we want a random theta
//x = r * cos(theta)
//y = r * sin(theta)
double theta = 0.0;
if (isLower) {
theta = Math.random() * (Math.PI / 2);
} else {
theta = Math.random() * (Math.PI / 2) + (Math.PI / 2);
}
int endX = (int)Math.round(radius * Math.sin(theta));
int endY = (int)Math.round(radius * Math.cos(theta)) * -1;
if (isLower) {
endY = endY + (height - 1);
}
final Point2D end = new Point2D.Float(endX, endY);
extendLine(start, end, radius);
return new Line2D.Float(start, end);
}
public static Point2D getNearestPointOnLine(Point2D end, Line2D line) {
final Point2D point = line.getP1();
final Point2D start = line.getP2();
double a = (end.getX() - point.getX()) * (start.getX() - point.getX()) + (end.getY() - point.getY()) * (start.getY() - point.getY());
double b = (end.getX() - start.getX()) * (point.getX() - start.getX()) + (end.getY() - start.getY()) * (point.getY() - start.getY());
final double x = point.getX() + ((start.getX() - point.getX()) * a)/(a + b);
final double y = point.getY() + ((start.getY() - point.getY()) * a)/(a + b);
final Point2D result = new Point2D.Double(x, y);
return result;
}
public static double length(double x0, double y0, double x1, double y1) {
final double dx = x1 - x0;
final double dy = y1 - y0;
return Math.sqrt(dx * dx + dy * dy);
}
public static void main(String[] args) {
final Line2D line = generateRandomGradientLine(SIZE, SIZE);
System.out.println("we're starting with line " + line.getP1() + " " + line.getP2());
double[][] region = new double[SIZE][SIZE];
//load up the region with data from our generated line
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, line);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() + 1,
nearestPoint.getY() + 1);
region[x][y] = distance;
}
}
//now figure out what our line is from the region
double runTotal = 0;
double riseTotal = 0;
double runCount = 0;
double riseCount = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
if (x < SIZE - 1) {
runTotal += region[x + 1][y] - region[x][y];
runCount++;
}
if (y < SIZE - 1) {
riseTotal += region[x][y + 1] - region[x][y];
riseCount++;
}
}
}
double run = 0;
if (runCount > 0) {
run = runTotal / runCount;
}
double rise = 0;
if (riseCount > 0) {
rise = riseTotal / riseCount;
}
System.out.println("rise is " + rise + " run is " + run);
Line2D newLine = getGradientLine(run, rise, SIZE - 1, SIZE - 1, 0, 0);
System.out.println("ending with line " + newLine.getP1() + " " + newLine.getP2());
double worst = 0.0;
int worstX = 0;
int worstY = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, newLine);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() + 1,
nearestPoint.getY() + 1);
final double diff = Math.abs(region[x][y] - distance);
if (diff > worst) {
worst = diff;
worstX = x;
worstY = y;
}
}
}
System.out.println("worst is " + worst + " x: " + worstX + " y: " + worstY);
}
}
I think I have fixed your program.
a) I took out the integer cast.
b) I removed all the 'x + 1' and 'x - 1' fudges you had used.
I think when dealing with floats and doubles, subtracting '1' from the end of a line is a No-No! What is 1 anyway? - it's ok to do this just before you plot it on the screen once it's an integer. But not while calculating! line length is a 'zero-based' quantity.
This version returns approx 4E-16 always.
import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.awt.geom.QuadCurve2D;
import java.util.Iterator;
import java.util.ArrayList;
public final class TestGradientLine {
private static int SIZE = 3;
public TestGradientLine() {
super();
}
//y = mx + b
//b = y - mx
//m is rise / run = gradient
//width and height of bounding box
//for a box 10x10 then width and height are 9,9
public static Line2D getGradientLine(double run, double rise, double width, double height, double x, double y) {
if (run == 0 && rise == 0) {
return new Line2D.Double(x, y, x + width, y + height);
}
//calculate hypotenuse
//check for a vertical line
if (run == 0) {
return new Line2D.Double(x, y, x, y + height);
}
//check for a horizontal line
if (rise == 0) {
return new Line2D.Double(x, y, x + width, y);
}
//calculate gradient
double m = rise / run;
Point2D start;
Point2D opposite;
if (m < 0) {
//lower left
start = new Point2D.Double(x, y + height);
opposite = new Point2D.Double(x + width, y);
} else {
//upper left
start = new Point2D.Double(x, y);
opposite = new Point2D.Double(x + width, y + height);
}
double b = start.getY() - (m * start.getX());
//now calculate another point along the slope
Point2D next = null;
if (m > 0) {
next = new Point2D.Double(start.getX() + Math.abs(run), start.getY() + Math.abs(rise));
} else {
if (rise < 0) {
next = new Point2D.Double(start.getX() + run, start.getY() + rise);
} else {
next = new Point2D.Double(start.getX() - run, start.getY() - rise);
}
}
final double actualWidth = width;
final double actualHeight = height;
final double a = Math.sqrt((actualWidth * actualWidth) + (actualHeight * actualHeight));
extendLine(start, next, a);
Line2D gradientLine = new Line2D.Double(start, next);
return gradientLine;
}
public static void extendLine(Point2D p0, Point2D p1, double toLength) {
final double oldLength = p0.distance(p1);
final double lengthFraction =
oldLength != 0.0 ? toLength / oldLength : 0.0;
p1.setLocation(p0.getX() + (p1.getX() - p0.getX()) * lengthFraction,
p0.getY() + (p1.getY() - p0.getY()) * lengthFraction);
}
public static Line2D generateRandomGradientLine(int width, int height) {
//so true means lower and false means upper
final boolean isLower = Math.random() > .5;
final Point2D start = new Point2D.Float(0, 0);
if (isLower) {
//change origin for lower left corner
start.setLocation(start.getX(), height );
}
//radius of our circle
double radius = Math.sqrt(width * width + height * height);
//now we want a random theta
//x = r * cos(theta)
//y = r * sin(theta)
double theta = 0.0;
if (isLower) {
theta = Math.random() * (Math.PI / 2);
} else {
theta = Math.random() * (Math.PI / 2) + (Math.PI / 2);
}
float endX = (float)(radius * Math.sin(theta));
float endY = (float)(radius * Math.cos(theta)) * -1;
if (isLower) {
endY = endY + (height );
}
final Point2D end = new Point2D.Float(endX, endY);
extendLine(start, end, radius);
return new Line2D.Float(start, end);
}
public static Point2D getNearestPointOnLine(Point2D end, Line2D line) {
final Point2D point = line.getP1();
final Point2D start = line.getP2();
double a = (end.getX() - point.getX()) * (start.getX() - point.getX()) + (end.getY() - point.getY()) * (start.getY() - point.getY());
double b = (end.getX() - start.getX()) * (point.getX() - start.getX()) + (end.getY() - start.getY()) * (point.getY() - start.getY());
final double x = point.getX() + ((start.getX() - point.getX()) * a)/(a+b);
final double y = point.getY() + ((start.getY() - point.getY()) * a)/(a+b);
final Point2D result = new Point2D.Double(x, y);
return result;
}
public static double length(double x0, double y0, double x1, double y1) {
final double dx = x1 - x0;
final double dy = y1 - y0;
return Math.sqrt(dx * dx + dy * dy);
}
public static void main(String[] args) {
final Line2D line = generateRandomGradientLine(SIZE, SIZE);
System.out.println("we're starting with line " + line.getP1() + " " + line.getP2());
double[][] region = new double[SIZE][SIZE];
//load up the region with data from our generated line
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, line);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() ,
nearestPoint.getY() );
region[x][y] = distance;
}
}
//now figure out what our line is from the region
double runTotal = 0;
double riseTotal = 0;
double runCount = 0;
double riseCount = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
if (x < SIZE - 1) {
runTotal += region[x + 1][y] - region[x][y];
runCount++;
}
if (y < SIZE - 1) {
riseTotal += region[x][y + 1] - region[x][y];
riseCount++;
}
}
}
double run = 0;
if (runCount > 0) {
run = runTotal / runCount;
}
double rise = 0;
if (riseCount > 0) {
rise = riseTotal / riseCount;
}
System.out.println("rise is " + rise + " run is " + run);
Line2D newLine = getGradientLine(run, rise, SIZE, SIZE , 0, 0);
System.out.println("ending with line " + newLine.getP1() + " " + newLine.getP2());
double worst = 0.0;
int worstX = 0;
int worstY = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, newLine);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() ,
nearestPoint.getY() );
final double diff = Math.abs(region[x][y] - distance);
if (diff > worst) {
worst = diff;
worstX = x;
worstY = y;
}
}
}
System.out.println("worst is " + worst + " x: " + worstX + " y: " + worstY);
}
}
why do you multiply by -1 at the end of this line?
int endY = (int)Math.round(radius * Math.cos(theta)) * -1;
this means that endY is always negative except radius is below 0. (cosinus always returns positive value)
is this intended or am i getting something wrong?
regards
You probably misunderstand float and/or double. This is a common problem with any language that implements the ieee spec for floats and doubles, which Java, C, C++ and just about every other language does.
Essentially
double val = 0;
for(int i=0;i<10;i++) {
val+=0.1;
System.out.println(val);
}
results in
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
And sometimes even worse. Either use BigDecimal, which alleviates a lot of the problem, or use integers.

Categories

Resources