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.
Related
I'm writing a code for a game and I have 2 lines, Line A which I calculated it's length and angle and I have Line B whose x2 and y2 are gotten from line A and angle at which Line A is, my program works to some extent. It only works when the border sign is moved to NorthEast and SouthWest but doesn't work at NorthWest and SouthEast. Here is my code
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
public class Main extends JPanel {
public static void main(String args[]) throws Exception {
JFrame f = new JFrame("Shapes");
f.setSize(600, 300);
f.setLocation(300, 300);
f.setResizable(true);
JPanel p = new JPanel() {
int x2 = 250;
int y2 = 175;
//for line
int x1 = 250;
int y1 = 100;
//Direction line x2 and y2
int lastx2 = 250;
int lasty2 = 1000;
//Middle Positive Sign
private Shape lineMp = null;
private Shape lineMn = null;
//Border Positive Sign
private Shape lineBp = null;
private Shape lineBn = null;
//Line
private Shape line = null;
//Direction Line
private Shape linelast = null;
private Shape lineNorth = null;
private Shape lineWest = null;
{
//North and South
lineNorth = new Line2D.Double(250,1,250,470);
lineWest = new Line2D.Double(1,175,470,175);
//Middle positive
lineMn = new Line2D.Double(235,175,265,175);
lineMp = new Line2D.Double(250,160,250,190);
//Border sign
lineBn = new Line2D.Double(235,100,265,100);
lineBp = new Line2D.Double(250,100,250,115);
//Line
line = new Line2D.Double(x1,y1,x2,y2);
//Direction Line
linelast = new Line2D.Double(x1,y1,lastx2,lasty2);
MouseAdapter mouseAdapter = new MouseAdapter() {
private Point prevPoint;
#Override
public void mousePressed(MouseEvent e) {
prevPoint = e.getPoint();
System.out.println("Prev Point=" + prevPoint.toString());
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
int dx = 0;
int dy = 0;
dx = (int) (prevPoint.x - e.getPoint().getX());
dy = (int) (prevPoint.y - e.getPoint().getY());
//Middle Positive sign
Line2D shapeMn = (Line2D) lineMn;
Line2D shapeMp = (Line2D) lineMp;
//Border sign
Line2D shapeBn = (Line2D) lineBn;
Line2D shapeBp = (Line2D) lineBp;
//Line
Line2D shapeline = (Line2D) line;
//Direction Line
Line2D shapelastline = (Line2D) linelast;
int length = (int) Math.sqrt((shapeline.getY2() - shapeline.getY1()) * (shapeline.getY2() - shapeline.getY1()) + (shapeline.getX2() - shapeline.getX1()) * (shapeline.getX2() - shapeline.getX1()));
int angle = (int) Math.toDegrees(Math.atan2(Math.abs(shapeline.getY1() - shapeline.getY2()), Math.abs(shapeline.getX1() - shapeline.getX2())));
//Middle Positive sign
int nx1 = (int) (shapeMn.getX1() - dx);
int ny1 = (int) (shapeMn.getY1() - dy);
int nx2 = (int) (shapeMn.getX2() - dx);
int ny2 = (int) (shapeMn.getY2() - dy);
int px1 = (int) (shapeMp.getX1() - dx);
int py1 = (int) (shapeMp.getY1() - dy);
int px2 = (int) (shapeMp.getX2() - dx);
int py2 = (int) (shapeMp.getY2() - dy);
//Border sign
int Bnx1 = (int) (shapeBn.getX1() - dx);
int Bny1 = (int) (shapeBn.getY1() - dy);
int Bnx2 = (int) (shapeBn.getX2() - dx);
int Bny2 = (int) (shapeBn.getY2() - dy);
int Bpx1 = (int) (shapeBp.getX1() - dx);
int Bpy1 = (int) (shapeBp.getY1() - dy);
int Bpx2 = (int) (shapeBp.getX2() - dx);
int Bpy2 = (int) (shapeBp.getY2() - dy);
//Line
int x2 = (int) (shapeline.getX2() - dx);
int y2 = (int) (shapeline.getY2() - dy);
int ex1 = (int) (shapeline.getX1() - dx);
int ey1 = (int) (shapeline.getY1() - dy);
//Direction Line
int x1 = (int) (shapelastline.getX1() - dx);
int y1 = (int) (shapelastline.getY1() - dy);
int lastx2 = ((int) (shapelastline.getX1() - (length + 700) * Math.cos(Math.toRadians(angle))));
int lasty2 = ((int) (shapelastline.getY1() - (length + 700) * Math.sin(Math.toRadians(angle))));
int elastx2 = ((int) (shapelastline.getX1() - (length + 700) * Math.cos(Math.toRadians(angle))));
int elasty2 = ((int) (shapelastline.getY1() - (length + 700) * Math.sin(Math.toRadians(angle))));
Point startPointMn = new Point(nx1, ny1);
Point endPointMn = new Point(nx2, ny2);
Point startPointMp = new Point(px1, py1);
Point endPointMp = new Point(px2, py2);
Point startPointBn = new Point(Bnx1, Bny1);
Point endPointBn = new Point(Bnx2, Bny2);
Point startPointBp = new Point(Bpx1, Bpy1);
Point endPointBp = new Point(Bpx2, Bpy2);
Point startPoint = new Point((int)shapeline.getX1(), (int)shapeline.getY1());
Point endPoint = new Point(x2, y2);
Point estartPoint = new Point(ex1, ey1);
Point eendPoint = new Point((int)shapeline.getX2() , (int)shapeline.getY2());
//Start and End point of Direction line
Point StartLastPoint = new Point(x1, y1);
Point endLastPoint = new Point(lastx2, lasty2);
Point eendLastPoint = new Point(elastx2, elasty2);
GeneralPath Mp = new GeneralPath();
Mp.append(lineMn, false);
Mp.append(lineMp, false);
GeneralPath Bp = new GeneralPath();
Bp.append(lineBn, false);
Bp.append(lineBp, false);
if (Mp.getBounds().contains(prevPoint)) {
shapelastline.setLine(startPoint, eendLastPoint);
shapeline.setLine(startPoint,endPoint);
shapeMn.setLine(startPointMn, endPointMn);
shapeMp.setLine(startPointMp, endPointMp);
prevPoint = e.getPoint();
repaint();
return;
}
if (Bp.getBounds().contains(prevPoint)) {
shapelastline.setLine(StartLastPoint, endLastPoint);
shapeline.setLine(estartPoint, eendPoint);
shapeBn.setLine(startPointBn, endPointBn);
shapeBp.setLine(startPointBp, endPointBp);
prevPoint = e.getPoint();
repaint();
return;
}
}
#Override
public void mouseReleased(MouseEvent e) {
repaint();
}
};
addMouseListener(mouseAdapter);
addMouseMotionListener(mouseAdapter);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(Color.YELLOW);
g2d.setStroke(new java.awt.BasicStroke(7));
g2d.draw(lineBn);
g2d.draw(lineBp);
g2d.draw(lineMn);
g2d.draw(lineMp);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(Color.BLUE);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (lineMn != null){
g2d.draw(linelast);
g2d.draw(line);
g2d.draw(lineNorth);
g2d.draw(lineWest);
}
}};
f.add(p);
p.setLayout(null);
f.setVisible(true);
}
}
This is where I set the x2 and y2 for line B:
int length = (int) Math.sqrt((shapeline.getY2() - shapeline.getY1()) * (shapeline.getY2() - shapeline.getY1()) + (shapeline.getX2() - shapeline.getX1()) * (shapeline.getX2() - shapeline.getX1()));
int angle = (int) Math.toDegrees(Math.atan2(Math.abs(shapeline.getY1() - shapeline.getY2()), Math.abs(shapeline.getX1() - shapeline.getX2())));
int lastx2 = ((int) (shapelastline.getX1() - (length + 700) * Math.cos(Math.toRadians(angle))));
int lasty2 = ((int) (shapelastline.getY1() - (length + 700) * Math.sin(Math.toRadians(angle))));
int elastx2 = ((int) (shapelastline.getX1() - (length + 700) * Math.cos(Math.toRadians(angle))));
int elasty2 = ((int) (shapelastline.getY1() - (length + 700) * Math.sin(Math.toRadians(angle))));
I have tried to follow the Wikipedia page on 3d projection to draw my own 3d line using java and AWT with Swing.
The output file that is created as a result of the rendering doesn't contain anything.
What have I done wrong?
Why isn't this producing an image file that has points on it?
Are my formulas off?
I was following this Wikipedia page as my reference: https://en.wikipedia.org/wiki/3D_projection#Perspective_projection
I have a set of points which are generated using a loop.
I then calculate the position of the 3d coordinate to 2d without using matrices, like one of the suggested method in the link.
Have I miss-interpreted something or not implemented something?
Any help and feedback are welcome.
I known nothing is drawn to the window. That isn't a priority at the moment. The priority it's getting the algorithms to work.
public class Window {
JFrame f = new JFrame();
public Window() {
JPanel p = new JPanel();
render();
f.add(p);
f.setSize(500, 500);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
Window w = new Window();
}
void render() {
BufferedImage image = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(Color.red);
for (int i = 0; i < 10; i++) {
Vector3 point = new Vector3(1 + i, 2, 1 + i);
int x = calculateBX(point, new Vector3(0, 0, 0));
int y = calculateBY(point, new Vector3(1, 1, 1));
g.drawLine(x, y, x, y);
}
try {
File outputfile = new File("saved.png");
ImageIO.write(image, "png", outputfile);
} catch (IOException e) {
e.printStackTrace();
}
}
double calculatDX(Vector3 v, Vector3 camera) {
double cx = Math.cos(0);
double cy = Math.cos(0);
double cz = Math.cos(0);
double sx = Math.sin(0);
double sy = Math.sin(0);
double sz = Math.sin(0);
int x = v.getX() - camera.getX();
int y = v.getY() - camera.getY();
int z = v.getZ() - camera.getZ();
double dx = cy * ((sz) * (y) + (cz) * (x)) - (sy) * (z);
return dx;
}
double calculatDY(Vector3 v, Vector3 camera) {
double cx = Math.cos(0);
double cy = Math.cos(0);
double cz = Math.cos(0);
double sx = Math.sin(0);
double sy = Math.sin(0);
double sz = Math.sin(0);
int x = v.getX() - camera.getX();
int y = v.getY() - camera.getY();
int z = v.getZ() - camera.getZ();
double dy = sx * ((cy) * (z) + (sy) * ((sz) * (y) + (cz) * (x))) + (cx) * (cz * (y) - sz * x);
return dy;
}
double calculatDZ(Vector3 v, Vector3 camera) {
double cx = Math.cos(0);
double cy = Math.cos(0);
double cz = Math.cos(0);
double sx = Math.sin(0);
double sy = Math.sin(0);
double sz = Math.sin(0);
int x = v.getX() - camera.getX();
int y = v.getY() - camera.getY();
int z = v.getZ() - camera.getZ();
double dz = cx * ((cy) * (z) + (sy) * ((sz) * (y) + (cz) * (x))) - (sx) * (cz * (y) - sz * x);
return dz;
}
int calculateBX(Vector3 v, Vector3 camera) {
int ez = camera.getZ();
int ex = camera.getX();
double dz = calculatDZ(v, camera);
double dx = calculatDX(v, camera);
return (int) ((ez / dz) * dx) - ex;
}
int calculateBY(Vector3 v, Vector3 camera) {
int ez = camera.getZ();
int ey = camera.getY();
double dz = calculatDZ(v, camera);
double dy = calculatDY(v, camera);
return (int) ((ez / dz) * dy) - ey;
}
}
Vector Class
public class Vector3 {
private int x, y, z;
public Vector3(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getZ() {
return z;
}
}
I need to create a GUI for a game and I will need to display on the screen the weapon and the shield. I need to modify the program so it displays the weapon and shield, however their properties are in another class. I know I will have to extend the JPanel class and overrite its paintComponent() method.
public class GraphicsUtil {
private static final int MUZZLE_FRACTION = 3;
private static final Color MUZZLE_COLOR = Color.MAGENTA;
public static void drawShield(IShield shield, Graphics g, GameSpace gameSpace, double origHealth) {
drawPiece(shield, g, gameSpace, origHealth);
}
public static void drawWeapon(IWeapon weapon, Graphics g,
GameSpace gameSpace, double origHealth) {
Color oldColor = g.getColor();
drawPiece(weapon, g, gameSpace, origHealth);
double orientation = weapon.getOrientation();
int x1 = gameSpace.convertToScreenX(weapon.getXPos());
int y1 = gameSpace.convertToScreenY(weapon.getYPos());
int radius = gameSpace.convertToScreenDistance(weapon.getRadius());
int x2 = gameSpace.convertToScreenX(weapon.getXPos()
+ weapon.getRadius() * Math.cos(weapon.getOrientation()));
int y2 = gameSpace.convertToScreenY(weapon.getYPos()
+ weapon.getRadius() * Math.sin(weapon.getOrientation()));
double frac = 1 - 1f / MUZZLE_FRACTION;
int muzzleCentreX
= x1 + (int) Math.round(frac * radius * Math.cos(orientation));
int muzzleCentreY
= y1 - (int) Math.round(frac * radius * Math.sin(orientation));
int muzzleRadius = radius / MUZZLE_FRACTION;
int topMuzzleX = muzzleCentreX - muzzleRadius;
int topMuzzleY = muzzleCentreY - muzzleRadius;
g.setColor(MUZZLE_COLOR);
g.fillOval(topMuzzleX, topMuzzleY, 2 * muzzleRadius, 2 * muzzleRadius);
g.setColor(Color.BLACK);
g.drawLine(x1, y1, x2, y2);
g.setColor(oldColor);
}
public static boolean isInMuzzle(IPiece piece, double x, double y) {
boolean result;
if (piece instanceof IWeapon) {
IWeapon weapon = (IWeapon) piece;
double radius = weapon.getRadius();
double orientation = weapon.getOrientation();
double frac = 1 - 1f / MUZZLE_FRACTION;
double cx = piece.getXPos() + frac * radius * Math.cos(orientation);
double cy = piece.getYPos() + frac * radius * Math.sin(orientation);
double dx = x - cx;
double dy = y - cy;
double dist = Math.sqrt(dx * dx + dy * dy);
result = dist <= radius / MUZZLE_FRACTION;
} else {
result = false;
}
return result;
}
public static double getAngle(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
double hypotenuse = Math.sqrt(dx * dx + dy * dy);
double angle;
if (dy > 0) {
angle = Math.acos(dx / hypotenuse);
} else {
angle = 2 * Math.PI - Math.acos(dx / hypotenuse);
}
return angle;
}
private static void drawPiece(IPiece piece, Graphics g, GameSpace gameSpace,
double maxHealth) {
Color oldColor = g.getColor();
int centreX = gameSpace.convertToScreenX(piece.getXPos());
int centreY = gameSpace.convertToScreenY(piece.getYPos());
int displayRadius = gameSpace.convertToScreenDistance(piece.getRadius());
int topX = centreX - displayRadius;
int topY = centreY - displayRadius;
if (piece.getOwner() == 0) {
g.setColor(Color.CYAN);
} else {
g.setColor(Color.PINK);
}
g.drawOval(topX, topY, 2 * displayRadius, 2 * displayRadius);
int healthRadius
= (int) Math.round(displayRadius * piece.getHealth() / maxHealth);
int topHealthX = centreX - healthRadius;
int topHealthY = centreY - healthRadius;
g.fillOval(topHealthX, topHealthY, 2 * healthRadius, 2 * healthRadius);
String name = piece.getName();
if (name != null) {
g.setColor(Color.BLACK);
g.drawString(name, centreX, centreY);
}
g.setColor(oldColor);
}
}
This is my main method:
public class Main extends JPanel {
public Main(){
setSize(new Dimension(400,400));
setPreferredSize(new Dimension(400,400));
GraphicsUtil object = new GraphicsUtil();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
}
public static void main(String[] args) {
Main window = new Main();
JFrame frame = new JFrame("RICOCHET");
frame.add(window);
frame.pack();
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
How do I call those methods? What do I need to do in order to they appear in my frame? Thanks.
Presumably you have created one or more weapons or pieces. In your paintComponent() method, just call
GraphicsUtil.drawWeapon( myWeapon, g, ... );
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
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.