In Java, I have a class Line that has two variables : m and b, such that the line follows the formula mx + b. I have two such lines. How am I to find the x and y coordinates of the intersection of the two lines? (Assuming the slopes are different)
Here is class Line:
import java.awt.Graphics;
import java.awt.Point;
public final class Line {
public final double m, b;
public Line(double m, double b) {
this.m = m;
this.b = b;
}
public Point intersect(Line line) {
double x = (this.b - line.b) / (this.m - line.m);
double y = this.m * x + this.b;
return new Point((int) x, (int) y);
}
public void paint(Graphics g, int startx, int endx, int width, int height) {
startx -= width / 2;
endx -= width / 2;
int starty = this.get(startx);
int endy = this.get(endx);
Point points = Format.format(new Point(startx, starty), width, height);
Point pointe = Format.format(new Point(endx, endy), width, height);
g.drawLine(points.x, points.y, pointe.x, pointe.y);
}
public int get(int x) {
return (int) (this.m * x + this.b);
}
public double get(double x) {
return this.m * x + this.b;
}
}
Lets assume you have these 2 functions:
y = m1*x + b1
y = m2*x + b2
To find the intersection point of the x-axis we do:
m1*x+b1 = m2*x+b2
m1*x-m2*x = b2 - b2
x(m1-m2) = (b2-b1)
x = (b2-b1) / (m1-m2)
To find y, you use of the function expressions and replace x for its value (b2-b1) / (m1-m2).
So:
y = m1 * [(b2-b1) / (m1-m2)] + b1
You have (this.b - line.b), change to (line.b - this.b).
public Point intersect(Line line) {
double x = (line.b - this.b) / (this.m - line.m);
double y = this.m * x + this.b;
return new Point((int) x, (int) y);
}
That's what i got. Couldn't find any exeptions which don't work:
public static Point calculateInterceptionPoint(Point s1, Point d1, Point s2, Point d2) {
double sNumerator = s1.y * d1.x + s2.x * d1.y - s1.x * d1.y - s2.y * d1.x;
double sDenominator = d2.y * d1.x - d2.x * d1.y;
// parallel ... 0 or infinite points, or one of the vectors is 0|0
if (sDenominator == 0) {
return null;
}
double s = sNumerator / sDenominator;
double t;
if (d1.x != 0) {
t = (s2.x + s * d2.x - s1.x) / d1.x;
} else {
t = (s2.y + s * d2.y - s1.y) / d1.y;
}
Point i1 = new Point(s1.x + t * d1.x, s1.y + t * d1.y);
return i1;
}
public static void main(String[] args) {
System.out.println(calculateInterceptionPoint(new Point(3, 5), new Point(0, 2), new Point(1, 2), new Point(4, 0)));
System.out.println(calculateInterceptionPoint(new Point(3, 5), new Point(0, 2), new Point(1, 2), new Point(0, 2)));
System.out.println(calculateInterceptionPoint(new Point(0, 0), new Point(0, 2), new Point(0, 0), new Point(2, 0)));
System.out.println(calculateInterceptionPoint(new Point(0, 0), new Point(0, 2), new Point(0, 0), new Point(0, 2)));
System.out.println(calculateInterceptionPoint(new Point(0, 0), new Point(0, 0), new Point(0, 0), new Point(0, 0)));
}
The proposed solution by #wutzebaer seems not to work, instead try the solution below (code based on the example from: https://rosettacode.org/wiki/Find_the_intersection_of_two_lines#Java).
s1 and s2 are the endpoints of the first line and d1 and d2 are the endpoints of the second line.
public static Point2D.Float calculateInterceptionPoint(Point2D.Float s1, Point2D.Float s2, Point2D.Float d1, Point2D.Float d2) {
double a1 = s2.y - s1.y;
double b1 = s1.x - s2.x;
double c1 = a1 * s1.x + b1 * s1.y;
double a2 = d2.y - d1.y;
double b2 = d1.x - d2.x;
double c2 = a2 * d1.x + b2 * d1.y;
double delta = a1 * b2 - a2 * b1;
return new Point2D.Float((float) ((b2 * c1 - b1 * c2) / delta), (float) ((a1 * c2 - a2 * c1) / delta));
}
public static void main(String[] args) {
System.out.println(calculateInterceptionPoint(new Point2D.Float(3, 5), new Point2D.Float(0, 2), new Point2D.Float(1, 2), new Point2D.Float(4, 0)));
}
The simplest solution:
import java.util.Scanner;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
System.out.println(Arrays.toString(calculateIntersection()));
}
public static double[] calculateIntersection() {
Scanner scanner = new Scanner(System.in);
System.out.println("The program calculates the point of intersection of two lines, given by their equations: ax + b. Please introduce the a and b coefficients of both lines:");
double m1 = scanner.nextInt();
double b1 = scanner.nextInt();
double m2 = scanner.nextInt();
double b2 = scanner.nextInt();
if ((m2 - m1) == 0) {
throw new ArithmeticException("The lines don't have intersection, because they're parallel.");
}
// Intersection [x,y] formula
double crossX = (b1 - b2) / (m2 - m1);
double crossY = (m1 * crossX + b1);
double[] array = new double[] {crossX,crossY};
return array;
}
}
Related
I have two objects in a 2D space. I expect object1 to begin orbiting object2. I derived my methods from the equation
f = G * (m1 * m2 / r*r)
and
dx1 += x1 - x2 * f
etc. However, I am struggling because the object is only moving in the pos pos direction. Here is the class for each object:
Mass.java
import java.awt.Point;
public class Mass {
public static float G = 0.1f;
public Point center;
public float mass, radius, dx = 0, dy = 0;
public boolean locked = false;
public Mass(Point center, float[] vect, float mass) {
this.center = center;
this.dx = vect[0];
this.dy = vect[1];
this.mass = mass;
this.radius = mass;
}
public void moveVector(float[] vector) {
if(!this.locked) {
this.dx += vector[0];
this.dy += vector[1];
}
}
public void lock() {
this.locked = true;
}
public static float distance(Mass obj1, Mass obj2) {
float dX = obj1.center.x - obj2.center.x;
float dY = obj1.center.y - obj2.center.y;
double ans = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));
return (float) ans;
}
public static float force(Mass obj1, Mass obj2) {
double ans = ((obj1.mass * obj2.mass) / Math.pow(distance(obj1, obj2), 2)) * G;
return (float) ans;
}
public static float[] vector(Mass obj1, Mass obj2) {
// total change between the two objects
float force = force(obj1, obj2);
float totalX = Math.abs(obj1.center.x - obj2.center.x);
float totalY = Math.abs(obj1.center.y - obj2.center.y);
float x = totalX * force;
float y = totalY * force;
float[] vector = {x, y};
return vector;
}
}
This is the main class.
Sim.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sim extends JPanel {
private static final long serialVersionUID = -2669101810074157675L;
public static final int PREF_W = 800, PREF_H = 600;
private Mass object1, object2;
private Sim() {
this.setFocusable(true);
this.setBackground(Color.WHITE);
float[] vect1 = {0, -1}, vect2 = {0, 0};
object1 = new Mass(new Point(PREF_W / 2 - 100, PREF_H / 2 - 100), vect1, 10);
object2 = new Mass(new Point(PREF_W / 2 + 100, PREF_H / 2 + 100), vect2, 30);
gameTimer.start();
}
private Timer gameTimer = new Timer(1000/30, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
object1.moveVector(Mass.vector(object1, object2));
object1.center.x += object1.dx;
object1.center.y += object1.dy;
System.out.println("[" + object1.dx + "," + object1.dy + "]");
}
});
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fillOval(
(int) object1.center.x - (int) object1.radius,
(int) object1.center.y - (int) object1.radius,
(int) object1.radius,
(int) object1.radius
);
g2.fillOval(
(int) object2.center.x - (int) object2.radius,
(int) object2.center.y - (int) object2.radius,
(int) object2.radius,
(int) object2.radius
);
g2.drawLine(object1.center.x, object1.center.y, object2.center.x, object2.center.y);
repaint();
}
/* METHODS FOR CREATING JFRAME AND JPANEL */
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Gravity Simulation");
JPanel gamePanel = new Sim();
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
I have it printing out the DX and DY of object1 (the unlocked object) at all times. It seems to get flung super fast, as expected, but it never slows down. Instead, the dx is just increasing slower and slower. I'm not very mathy, but it seems to be logistic. I wonder why this is happening.
So far I have tried rewriting my formula and using a different equation. I have also attempted using different datatypes, and making some things negative. Nothing works, though.
TLDR, the problem:
Objects are not changing DX / DY as expected.
Thank you in advance! Sorry if this was posted somewhere else, I could not find any duplicates.
OK, let's try to derive formulas.
You already have difference vector dX, dY, and make also normalized vector
udX = dX / distance
udY = dY / distance
You also have force magnitude. To get force vector for object 1, just multiply normalized difference components by this magnitude (note minus sign because force direction is TO object2 (while dx, dy is vector FROM object 2))
fx1 = - udX * force
fy1 = - udY * force
(and force vector for object2 if needed)
fx2 = - fx1
fy2 = - fy1
First object velocity vector is (vx1, vy1). At every step you have to modify it with acceleration, where deltaT is time period between cadrs.
vx1 = vx1 + fx1 / mass1 * deltaT
vy1 = vy1 + fy1 / mass1 * deltaT
Now you can modify position with velocity
x1 = x1 + vx * deltaT
y1 = y1 + vy * deltaT
I'm learning 3D rendering with java and I encountered a weird issue.
I'm able to rotate and display 3D objects to the screen. but when I tried to use prospective projection to show depth, the program freaks out. No errors or anything, but the depth seems to stretch incurability long. I narrowed the problem down to the perspective divide in the projection function.
Can anyone help?
public class Renderer extends JPanel{
public Renderer() {
}
double angle = 0;
double a;
double f;
double l;
double offSet;
public void update() {
angle += 0.03;
repaint();
}
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
super.paintComponent(g2d);
for(Triangle element: MidService.mesh)
{
Vertex v1 = rotateY(element.v1);
Vertex v2 = rotateY(element.v2);
Vertex v3 = rotateY(element.v3);
v1 = projection(v1);
v2 = projection(v2);
v3 = projection(v3);
int offSet = (int) (MidService.displayX/2);
g2d.drawLine(offSet + v1.x, offSet + v1.y, offSet + v2.x, offSet + v2.y);
g2d.drawLine(offSet + v2.x, offSet + v2.y, offSet + v3.x, offSet + v3.y);
g2d.drawLine(offSet + v3.x, offSet + v3.y, offSet + v1.x, offSet + v1.y);
}
}
public Vertex projection(Vertex v)
{
a = MidService.displayY / MidService.displayX;
f = 1 / (Math.tan(MidService.fieldOfView/2));
l = MidService.Zfar / (MidService.Zfar - MidService.Znear);
offSet = MidService.Zfar / (MidService.Zfar - MidService.Znear) * MidService.Znear;
double x = (v.x * a * f);
double y = (v.y * f);
double z = (v.z * l - offSet);
double w = v.z;
//the if function below caused the issue
if(w != 0.0) {
x /= w;
y /= w;
z /= w;
}
return new Vertex((int)x, (int)y, (int)z);
}
public Vertex rotateY(Vertex v)
{
double x = v.x * Math.cos(angle) + v.z * Math.sin(angle);
double y = v.y;
double z = v.x * (-Math.sin(angle)) + v.z * Math.cos(angle);
return new Vertex((int)x, (int)y, (int)z);
}
//-------------------------------------------------------
public class MidService {
public static double displayX = 1000;
public static double displayY = 1000;
public static double fieldOfView = 180;
public static double Zfar = 10;
public static double Znear = 1;
public static ArrayList<Triangle> mesh = new ArrayList<>();
}
//---------------------------------------------
public class Driver {
public static void main(String[] args) {
Display display = new Display();
MidService.mesh.add(new Triangle(new Vertex(-100, 100, 100),
new Vertex(-100, -100, 100),
new Vertex(100, -100, 100)));
MidService.mesh.add(new Triangle(new Vertex(-100, 100, 100),
new Vertex(100, -100, 100),
new Vertex(100, 100, 100)));
Here's a video of it:[https://youtu.be/bFJLU5c3JE0]
A follow up to the perspective division issue. It turns out to be the inverse of the difference between camera distance and z coordinate. So instead of division by z, its 1/(distance - z)
Below is modification for Gaba Miau's matrix multiply function
Vertex Mult(float[][] mat,Vertex v){
float ver[] = {v.x,v.y,v.z,v.w};
float ans[] = {0,0,0,0};
for (int i=0;i<mat.length;i++){
for(int j=0;j<mat[0].length;j++){
ans[i] += mat[i][j] * ver[j];
}
}
float temp = 100/(500 - v.z);
if(temp != 0)
{
ans[0] *= temp;
ans[1] *= temp;
ans[2] *= temp;
}
Vertex vans = new Vertex((int)ans[0],(int)ans[1],(int)ans[2]);
vans.w =(int) ans[3];
return vans;
}
P.S. it's ans[i] += mat[i][j] * ver[j]; in the forloop
Firstly you should be using a graphics API like OpenGL or DirectX, secondly you should be using a math library like GLM that contains all the projection matrix formulas and other stuff. Thirdly, you shouldn't be using int as a datatype to store vertex data.
I made a few changes to your code so it doesn't freak out anymore.
The fov should be in the range(0.001,179.999), never 180, it is recommanded using 90 deg. Also the math function tan takes in radians not deg.
package Render;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.sound.midi.MidiDevice;
import javax.swing.JPanel;
import Beans.Triangle;
import Beans.Vertex;
import Utilities.MidService;
public class Renderer extends JPanel{
public Renderer() {
}
double angle = 0;
double a;
double f;
double l;
double offSet;
public void update() {
//angle += 0.03;
repaint();
}
float projMat[][]= {
{1f / ((float)Math.tan(MidService.fieldOfView/2f)),0,0,0},
{0,1f / ((float)Math.tan(MidService.fieldOfView/2f)),0,0},
{0,0,MidService.Zfar / (MidService.Zfar - MidService.Znear),1},
{0,0,-MidService.Zfar* MidService.Znear / (MidService.Zfar - MidService.Znear),0},
};
Vertex Mult(float[][] mat,Vertex v){
float ver[] = {v.x,v.y,v.z,v.w};
float ans[] = {0,0,0,0};
for (int i=0;i<mat.length;i++){
for(int j=0;j<mat[0].length;j++){
ans[i] += mat[i][j] * ver[i];
}
}
Vertex vans = new Vertex((int)ans[0],(int)ans[1],(int)ans[2]);
vans.w =(int) ans[3];
return vans;
}
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
super.paintComponent(g2d);
for(Triangle element: MidService.mesh)
{
Vertex v1 = rotateZ(element.v1);
Vertex v2 = rotateZ(element.v2);
Vertex v3 = rotateZ(element.v3);
v1 = rotateY(v1);
v2 = rotateY(v2);
v3 = rotateY(v3);
v1 = Mult(projMat,v1);
v2 = Mult(projMat,v2);
v3 = Mult(projMat,v3);
int offSet = (int) (MidService.displayX/2);
g2d.drawLine(offSet + (int)v1.x, offSet + (int)v1.y, offSet + (int)v2.x, offSet + (int)v2.y);
g2d.drawLine(offSet + (int)v2.x, offSet + (int)v2.y, offSet + (int)v3.x, offSet + (int)v3.y);
g2d.drawLine(offSet + (int)v3.x, offSet + (int)v3.y, offSet + (int)v1.x, offSet + (int)v1.y);
}
}
public Vertex projection(Vertex v)
{
a = MidService.displayY / MidService.displayX;
f = 1 / (Math.tan(MidService.fieldOfView/2));
l = MidService.Zfar / (MidService.Zfar - MidService.Znear);
offSet = MidService.Zfar / (MidService.Zfar - MidService.Znear) * MidService.Znear;
double x = (v.x * a * f);
double y = (v.y * f);
double z = (v.z * l - offSet);
double w = v.z;
if(w != 0.0) {
x /= w;
y /= w;
z /= w;
}
return new Vertex((int)x, (int)y, (int)z);
}
public Vertex rotateY(Vertex v)
{
double x = v.x * Math.cos(angle) + v.z * Math.sin(angle);
double y = v.y;
double z = v.x * (-Math.sin(angle)) + v.z * Math.cos(angle);
return new Vertex((int)x, (int)y, (int)z);
}
public Vertex rotateZ(Vertex v)
{
double x = v.x * Math.cos(angle) + v.y * (-Math.sin(angle));
double y = v.x * Math.sin(angle) + v.y * Math.cos(angle);
double z = v.z;
return new Vertex((int)x, (int)y, (int)z);
}
}
Here is the MidService class
package Utilities;
import java.util.ArrayList;
import Beans.Triangle;
public class MidService {
public static float displayX = 1000;
public static float displayY = 1000f;
public static float fieldOfView = 3.14f/2f;
public static float Zfar = 100f;
public static float Znear = 0.1f;
public static ArrayList<Triangle> mesh = new ArrayList<>();
}
And Vertex
package Beans;
public class Vertex {
public float x;
public float y;
public float z;
public float w = 1;
public Vertex(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}
The user inputs 3 space separated coordinates that can make up a rectangle in the xy-plane. The algorithm returns what must be the 4th point to form a rectangle.
Example: "5 5", "5 7", and "7 5", newline separated, should return "7 7".
The below algorithm works for the provided test cases, but I am failing other cases, and I can't figure out why. Can anyone suggest a way to make my algorithm include all possible inputs - assuming that the 3 inputs provided do in fact form 3 corners of a rectangle?
import java.io.*;
public class cetvrta {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String point1 = reader.readLine(); // 5 5
String point2 = reader.readLine(); // 5 7
String point3 = reader.readLine(); // 7 5
String[] cord1 = point1.split(" "); // ["5","5"]
String[] cord2 = point2.split(" "); // ["5", "7"]
String[] cord3 = point3.split(" "); // ["7", "5"]
int x4 = 0;
int y4 = 0;
int x1 = Integer.parseInt(cord1[0]); // 5
int y1 = Integer.parseInt(cord1[1]); // 5
int x2 = Integer.parseInt(cord2[0]);
int y2 = Integer.parseInt(cord2[1]);
int x3 = Integer.parseInt(cord3[0]);
int y3 = Integer.parseInt(cord3[1]);
if (y1 == y2) {
if (x3 == x1) {
x4 = x2;
y4 = y3;
}
if (x3 == x2) {
x4 = x1;
y4 = y3;
}
}
if (y3 == y2) {
if (x2 == x3) {
x4 = x1;
y4 = y2;
}
if (x2 == x1) {
x4 = x3;
y4 = y2;
}
}
if (y1 == y3) {
if (x2 == x1) {
x4 = x3;
y4 = y2;
}
if (x2 == x3) {
x4 = x1;
y4 = y2;
}
}
System.out.println(x4 + " " + y4);
}
}
There is no hard and fast rule that "x-coordinates of 2 points of a rectangle has to match and so do the y-coordinates". Consider the image below for better understanding.
We can see that no two points have same x and y coordinates although there exists a perfect rectangle:
Fix:
I would recommend you to slightly change the algorithm as to proceed in the following way. Given the three points, find the point that isn't the corner(the one that does not pass through diagonal based out of other 2 points). From this point, calculate the slope to remaining points and assuming the 4th corner to be (x,y); draw out 2 locii. to satisfy slope1 * slope 2=-1. These 2 locii solved together will give the 4th point.
This is weird, rethink that:
if (y3 == y2) {
if (x2 == x3) { // <---
x4 = x1; // <---
y4 = y2; // <---
}
if (x2 == x1) {
x4 = x3;
y4 = y2; // <---
}
}
It would be better to use basic vector algebra to resolve this task:
calculate vectors between the three known points
define which point of the given three is a vertex of right angle (90°) - if any
this can be done using the fact that the scalar product of two perpendicular vectors is 0: v1.x * v2.x + v1.y * v2.0 == 0
find the fourth point by adding to the right angle vertex two vectors outgoing from this vertex to the other two known points.
Sample implementation could look like this:
// auxiliary classes
class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
Point add(Vector v) {
return new Point(this.x + v.x, this.y + v.y);
}
#Override
public String toString() {
return String.format("(%d, %d)", x, y);
}
}
class Vector {
int x, y;
Vector(int x, int y) {
this.x = x;
this.y = y;
}
Vector(Point p1, Point p2) {
this(p2.x - p1.x, p2.y - p1.y);
}
static boolean isRightAngle(Vector v1, Vector v2) {
return 0 == (v1.x * v2.x + v1.y * v2.y);
}
Vector add(Vector v) {
return new Vector(this.x + v.x, this.y + v.y);
}
}
Method to find the right angle vertex:
static int rightAngleVertexIndex(Point ... p) {
assert p.length == 3;
Vector v01 = new Vector(p[0], p[1]);
Vector v12 = new Vector(p[1], p[2]);
Vector v20 = new Vector(p[2], p[0]);
if (Vector.isRightAngle(v01, v12)) {
return 1;
} else if (Vector.isRightAngle(v12, v20)) {
return 2;
} else if (Vector.isRightAngle(v20, v01)) {
return 0;
} else {
return -1;
}
}
Method to find the 4th point of rectangle (return null if no rectangle is possible):
static Point findFourthVertex(Point ... points) {
assert points.length == 3;
final int[][] otherVertices = {
{1, 2},
{0, 2},
{0, 1},
};
Point result = null;
int rightAngleIx = rightAngleVertexIndex(points);
if (rightAngleIx != -1) {
Point rightAngle = points[rightAngleIx];
Point p1 = points[otherVertices[rightAngleIx][0]];
Point p2 = points[otherVertices[rightAngleIx][1]];
result = rightAngle.add(new Vector(rightAngle, p1).add(new Vector(rightAngle, p2)));
System.out.println("The fourth vertex of the rectangle: " + result);
} else {
System.out.println("No right angle found between any of the points " + Arrays.toString(points));
}
return result;
}
Test:
findFourthVertex(new Point(1, 1), new Point(5, 1), new Point(1, 4));
findFourthVertex(new Point(-1, -1), new Point(5, 0), new Point(6, 5));
Output:
The fourth vertex of the rectangle: (5, 4)
No right angle found between any of the points [(-1, -1), (5, 0), (6, 5)]
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 2 years ago.
I have this problem where I am supposed to write this program where using a Point class to create points, then create a Rectangle class with methods like area,perimeter, and pointInside... My code is always resulting in a Null Pointer Exception and I don't know why.
class Point {
int x, y;
Point() {
x = y = 0;
}
Point(int x0, int y0) {
x = x0;
y = y0;
}
public String toString() {
return "(" + x + "," + y + ")";
}
}
class Rectangle {
Point p1,p2;
Rectangle(int x1,int y1,int x2,int y2)
{
x1 = p1.x;
y1 = p1.y;
x2 = p2.x;
y2 = p2.y;
}
Rectangle(Point p1,Point p2)
{
this.p1 = p1;
this.p2 = p2;
}
public int area ()
{
int width = p2.x - p1.x;
int height = p2.y - p1.y;
int area = width * height;
return area;
}
public int perimeter()
{
int side1 = p2.x - p1.x;
int side2 = p2.y - p1.y;
int perimeter = side1 + side2 + side1 + side2;
return perimeter;
}
public boolean pointInside(Point p)
{
if ((p.x >= p1.x && p.x <= p2.x) && (p.y >= p1.y && p.y <= p2.y))
{
return true;
} else {
return false;
}
}
}
class TestRectangle {
public static void main(String[] args) {
Point a = new Point(1,1);
Point b = new Point(2,2);
Point c = new Point(3,4);
Point d = new Point(8,2);
Rectangle yellow = new Rectangle(a, c);
Rectangle orange = new Rectangle(2, 3, 9, 6);
Rectangle green = new Rectangle(3, 4, 4, 5);
Rectangle blue = new Rectangle(5, 1, 6, 5);
Rectangle red = new Rectangle(7, 3, 9, 5);
System.out.println("Perimeter of the yellow rectangle = " + yellow.perimeter()); // 10
System.out.println("Perimeter of the orange rectangle= " + orange.perimeter()); // 20
System.out.println("Area of the yellow rectangle = " + yellow.area()); // 6
System.out.println("Area of the orange rectangle = " + orange.area()); // 21
System.out.println("Point B inside yellow? " + yellow.pointInside(b)); // true
System.out.println("Point D inside yello? " + yellow.pointInside(d)); // false
}
}
When something calls this constructor
Rectangle(int x1,int y1,int x2,int y2)
{
x1 = p1.x;
y1 = p1.y;
x2 = p2.x;
y2 = p2.y;
}
your variables p1 and p2 haven't been initialized to anything, since before that, all you do is declare those two fields, not actually set them to anything. They are null by default.
What you need to do is
Rectangle(int x1, int y1, int x2, int y2) {
p1 = new Point(x1, y1);
p2 = new Point(x2, y2);
}
I also have no idea why you're modifying the arguments to the constructor in the original.
I have made some progress detecting a specific kind of object. Actually a card, just like any other in your wallet.
Now I'm stuck with deskewing the photo. See:
The blue (rounded) rectangle represents the detected contour.
The purple rotate rectangle represents a RotatedRect extracted from the detected contour.
The green line is just the bounding box.
Well I need neither of those rectangles. The rectangles both have 90 degree corners. Which won't get me the perspective.
My question:
How can I get as accurate as possible all quadrangle corners from a contour?
I have created a class Quadrangle which creates the quadrangle of the 4 most largest connected polygon vertices which will intersect each other at some point. This will work in nearly any case.
If you use this code, remember to adjust the width and height in Quadrangle.warp. Note that it isn't 100% complete, the first and last polygon vertices won't be connected if they may be connect for example.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
class Line {
public Point offset;
public double angle;
public Line(Point offset, double angle) {
this.offset = offset.clone();
this.angle = angle;
}
public Point get(int length) {
Point result = offset.clone();
result.x += Math.cos(angle) * length;
result.y += Math.sin(angle) * length;
return result;
}
public Point getStart() {
return get(-5000);
}
public Point getEnd() {
return get(5000);
}
public void scale(double factor) {
offset.x *= factor;
offset.y *= factor;
}
public static Point intersect(Line l1, Line l2) {
return getLineLineIntersection(l1.getStart().x, l1.getStart().y, l1.getEnd().x, l1.getEnd().y,
l2.getStart().x, l2.getStart().y, l2.getEnd().x, l2.getEnd().y
);
}
public static Point getLineLineIntersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
double det1And2 = det(x1, y1, x2, y2);
double det3And4 = det(x3, y3, x4, y4);
double x1LessX2 = x1 - x2;
double y1LessY2 = y1 - y2;
double x3LessX4 = x3 - x4;
double y3LessY4 = y3 - y4;
double det1Less2And3Less4 = det(x1LessX2, y1LessY2, x3LessX4, y3LessY4);
if (det1Less2And3Less4 == 0){
// the denominator is zero so the lines are parallel and there's either no solution (or multiple solutions if the lines overlap) so return null.
return null;
}
double x = (det(det1And2, x1LessX2,
det3And4, x3LessX4) /
det1Less2And3Less4);
double y = (det(det1And2, y1LessY2,
det3And4, y3LessY4) /
det1Less2And3Less4);
return new Point(x, y);
}
protected static double det(double a, double b, double c, double d) {
return a * d - b * c;
}
}
class LineSegment extends Line implements Comparable {
public double length;
public LineSegment(Point offset, double angle, double length) {
super(offset, angle);
this.length = length;
}
public void melt(LineSegment segment) {
Point point = new Point();
point.x += Math.cos(angle) * length;
point.y += Math.sin(angle) * length;
point.x += Math.cos(segment.angle) * segment.length;
point.y += Math.sin(segment.angle) * segment.length;
angle = Math.atan2(point.y, point.x);
offset.x = (offset.x * length + segment.offset.x * segment.length) / (length + segment.length);
offset.y = (offset.y * length + segment.offset.y * segment.length) / (length + segment.length);
length += segment.length;
}
#Override
public int compareTo(Object other) throws ClassCastException {
if (!(other instanceof LineSegment)) {
throw new ClassCastException("A LineSegment object expected.");
}
return (int) (((LineSegment) other).length - this.length);
}
}
class Quadrangle {
static int
TOP = 0,
RIGHT = 1,
BOTTOM = 2,
LEFT = 3;
public Line[] lines = new Line[4];
public Quadrangle() {
}
private static double getAngle(Point p1, Point p2) {
return Math.atan2(p2.y - p1.y, p2.x - p1.x);
}
private static double getLength(Point p1, Point p2) {
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
private static double roundAngle(double angle) {
return angle - (2*Math.PI) * Math.round(angle / (2 * Math.PI));
}
public static Quadrangle fromContour(MatOfPoint contour) {
List<Point> points = contour.toList();
List<LineSegment> segments = new ArrayList<>();
// Create line segments
for (int i = 0; i < points.size(); i++) {
double a = getAngle(points.get(i), points.get((i + 1) % points.size()));
double l = getLength(points.get(i), points.get((i + 1) % points.size()));
segments.add(new LineSegment(points.get(i), a, l));
}
// Connect line segments
double angleDiffMax = 2 * Math.PI / 100;
List<LineSegment> output = new ArrayList<>();
for (LineSegment segment : segments) {
if (output.isEmpty()) {
output.add(segment);
} else {
LineSegment top = output.get(output.size() - 1);
double d = roundAngle(segment.angle - top.angle);
if (Math.abs(d) < angleDiffMax) {
top.melt(segment);
} else {
output.add(segment);
}
}
}
Collections.sort(output);
Quadrangle quad = new Quadrangle();
for (int o = 0; o < 4; o += 1) {
for (int i = 0; i < 4; i++) {
if (Math.abs(roundAngle(output.get(i).angle - (2 * Math.PI * o / 4))) < Math.PI / 4) {
quad.lines[o] = output.get(i);
}
}
}
return quad;
}
public void scale(double factor) {
for (int i = 0; i < 4; i++) {
lines[i].scale(factor);
}
}
public Mat warp(Mat src) {
Mat result = src.clone();
Core.line(result, lines[TOP].get(-5000), lines[TOP].get(5000), new Scalar(200, 100, 100), 8);
Core.line(result, lines[RIGHT].get(-5000), lines[RIGHT].get(5000), new Scalar(0, 255, 0), 8);
Core.line(result, lines[BOTTOM].get(-5000), lines[BOTTOM].get(5000), new Scalar(255, 0, 0), 8);
Core.line(result, lines[LEFT].get(-5000), lines[LEFT].get(5000), new Scalar(0, 0, 255), 8);
Point p = Line.intersect(lines[TOP], lines[LEFT]);
System.out.println(p);
if (p != null) {
Core.circle(result, p, 30, new Scalar(0, 0, 255), 8);
}
double width = 1400;
double height = width / 2.15;
Point[] srcProjection = new Point[4], dstProjection = new Point[4];
srcProjection[0] = Line.intersect(lines[TOP], lines[LEFT]);
srcProjection[1] = Line.intersect(lines[TOP], lines[RIGHT]);
srcProjection[2] = Line.intersect(lines[BOTTOM], lines[LEFT]);
srcProjection[3] = Line.intersect(lines[BOTTOM], lines[RIGHT]);
dstProjection[0] = new Point(0, 0);
dstProjection[1] = new Point(width - 1, 0);
dstProjection[2] = new Point(0, height - 1);
dstProjection[3] = new Point(width - 1, height - 1);
Mat warp = Imgproc.getPerspectiveTransform(new MatOfPoint2f(srcProjection), new MatOfPoint2f(dstProjection));
Mat rotated = new Mat();
Size size = new Size(width, height);
Imgproc.warpPerspective(src, rotated, warp, size, Imgproc.INTER_LINEAR);
return rotated;
}
}