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;
}
}
Related
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;
}
}
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 am attempting to simulate a sphere, and shade it realistically given an origin vector for the light, and the sphere being centered around the origin. Moreover, the light's vector is the normal vector on a larger invisible sphere at a chosen point. The sphere looks off.
https://imgur.com/a/IDIwQQF
The problem, is that it is very difficult to bug fix this kind of program. Especially considering that I know how I want it to look in my head, but when looking at the numbers in my program there is very little meaning attached to them.
Since I don't know where the issue is, I'm forced to paste all of it here.
public class SphereDrawing extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ADJ = 320;
private static final double LIGHT_SPHERE_RADIUS = 5;
private static final double LIGHT_X = 3;
private static final double LIGHT_Y = 4;
private static final double LIGHT_Z = 0;
private static final double DRAWN_SPHERE_RADIUS = 1;
private static final int POINT_COUNT = 1000000;
private static Coord[] points;
private static final double SCALE = 200;
public SphereDrawing() {
setPreferredSize(new Dimension(640, 640));
setBackground(Color.white);
points = new Coord[POINT_COUNT];
initializePoints();
for (int i = 0; i < points.length; i++) {
points[i].scale();
}
new Timer(17, (ActionEvent e) -> {
repaint();
}).start();
}
public void initializePoints() { //finding the points on the surface of the sphere (hopefully somewhat equidistant)
double random = Math.random() * (double)POINT_COUNT;
double offset = 2/(double)POINT_COUNT;
double increment = Math.PI * (3 - Math.sqrt(5));
for (int i = 0; i < POINT_COUNT; i++) {
double y = ((i * offset) - 1) + (offset / 2);
double r = Math.sqrt(1 - Math.pow(y, 2));
double phi = ((i + random) % (double)POINT_COUNT) * increment;
double x = Math.cos(phi) * r;
double z = Math.sin(phi) * r;
points[i] = new Coord(x, y, z);
}
}
public void drawSphere(Graphics2D g) {
g.translate(ADJ, ADJ); //shifting from origin for drawing purposes
Arrays.sort(points); //sorting points by their z coordinates
double iHat = -2 * LIGHT_X;
double jHat = -2 * LIGHT_Y; //Light vector
double kHat = -2 * LIGHT_Z;
double angL1 = 0;
if (Math.abs(iHat) != 0.0)
angL1 = Math.atan(jHat / iHat); //converting light vector to spherical coordinates
else
angL1 = Math.PI/2;
double angL2 = Math.atan(Math.sqrt(Math.pow(iHat, 2) + Math.pow(jHat, 2))/ kHat);
double maxArcLength = LIGHT_SPHERE_RADIUS * Math.PI; // maximum arc length
for (int i = 0; i < points.length; i++) {
if(points[i].checkValid()) {
double siHat = -2 * points[i].x;
double sjHat = -2 * points[i].y; //finding normal vector for the given point on the sphere
double skHat = -2 * points[i].z;
double angSF1 = -1 * Math.abs(Math.atan(sjHat / siHat)); // converting vector to spherical coordinates
double angSF2 = Math.atan(Math.sqrt(Math.pow(siHat, 2) + Math.pow(sjHat, 2))/ skHat);
double actArcLength = LIGHT_SPHERE_RADIUS * Math.acos(Math.cos(angL1) * Math.cos(angSF1) + Math.sin(angL1) * Math.sin(angSF1) * Math.cos(angL2 - angSF2)); //calculating arc length at this point
double comp = actArcLength / maxArcLength; // comparing the maximum arc length to the calculated arc length for this vector
int col = (int)(comp * 255);
col = Math.abs(col);
g.setColor(new Color(col, col, col));
double ovalDim = (4 * Math.PI * Math.pow(DRAWN_SPHERE_RADIUS, 2))/POINT_COUNT; //using surface area to determine how large size of each point should be drawn
if (ovalDim < 1) // if it too small, make less small
ovalDim = 2;
g.fillOval((int)points[i].x, (int)points[i].y, (int)ovalDim, (int)ovalDim); //draw this oval
}
}
}
#Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawSphere(g);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Sphere");
f.setResizable(false);
f.add(new SphereDrawing(), BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
#SuppressWarnings("rawtypes")
private class Coord implements Comparable {
public double x;
public double y;
public double z;
public Coord(double x2, double y2, double z2) {
x = x2;
y = y2;
z = z2;
}
public void scale() {
x *= SCALE;
y *= SCALE; //drawing purposes
z *= SCALE;
}
public String toString() {
return x + " " + y + " " + z;
}
public int compareTo(Object c) {
double diff = this.z - ((Coord)c).z;
if (diff < 0)
return -1;
else if (diff > 0) //for sorting the array of points
return 1;
else
return 0;
}
public boolean checkValid() {
return (z > 0); //checks if need to draw this point
}
}
}
I was hoping to at least draw a realistic looking sphere, even if not completely accurate, and I couldn't tell you what exactly is off with mine
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;
}
}
I have a class that when clicked draws one point, when clicked again it draws another point and draws the line between them.
public class SlopeComponent extends JComponent
{
private static final long serialVersionUID = 1L;
public SlopeComponent()
{
point1 = null;
point2 = null;
class MouseSpy extends MouseAdapter
{
public void mousePressed(MouseEvent event)
{
double x1 = rxPixel(event.getX());
double y1 = ryPixel(event.getY());
point1 = new Point2D.Double(x1, y1);
double x2 = rxPixel(event.getX());
double y2 = ryPixel(event.getY());
point2 = new Point2D.Double(x2, y2);
repaint();
}
}
MouseSpy listener = new MouseSpy ();
addMouseListener(listener);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Axes axes = new Axes(xPixel(XMIN), xPixel(XMAX), yPixel(YMIN), yPixel(YMAX),
xPixel(0), yPixel(0), sWidth(1), sHeight(1));
axes.drawAxes(g2); //draw the axes
axes.drawTicks(g2);
if(point1 != null || point2 != null)
{
plotPoint(g2, point1);
plotPoint(g2, point2);
double x1 = point1.getX();
double y1 = point1.getY();
double x2 = point2.getX();
double y2 = point2.getY();
drawSlope(g2, x1, y1, x2, y2);
}
}
public void drawSlope(Graphics2D g2, double x1, double y1, double x2, double y2) //draw the lines
{
Point2D.Double p1 = new Point2D.Double(xPixel(x1), yPixel(y1));
Point2D.Double p2 = new Point2D.Double(xPixel(x2), yPixel(y2));
Line2D.Double line = new Line2D.Double(p1, p2);
g2.draw(line);
}
public void plotPoint(Graphics2D g2, Point2D.Double p) //plot the point
{
double x = p.getX();
double y = p.getY();
double radius = 5 * (XMAX - XMIN) / getWidth();
Ellipse2D.Double point = new Ellipse2D.Double(xPixel(x - radius), yPixel(y + radius),
sWidth(2 * radius), sHeight(2 * radius));
g2.fill(point);
double xR = Rounding.round(x, 1);
double yR = Rounding.round(y, 1);
double gap = 6 * (XMAX - XMIN) / getWidth();
g2.drawString("(" + xR + ", " + yR + ")", (float)xPixel(x + gap), (float)yPixel(y + gap));
}
public double rxPixel(double x)
{
return x * (XMAX - XMIN) / (getWidth() - 1) + XMIN;
}
public double ryPixel(double y)
{
return y * (YMIN - YMAX) / (getHeight() - 1) + YMAX;
}
public double xPixel(double xuser)
{
return (xuser - XMIN) * (getWidth() - 1) / (XMAX - XMIN);
}
public double yPixel(double yuser)
{
return (yuser - YMAX) * (getHeight() - 1) / (YMIN - YMAX);
}
public double sHeight(double yuser)
{
return yuser * (getHeight() - 1) / (YMAX - YMIN);
}
public double sWidth(double xuser)
{
return xuser * (getWidth() - 1) / (XMAX - XMIN);
}
private static final double XMIN = -10;
private static final double XMAX = 10;
private static final double YMIN = -10;
private static final double YMAX = 10;
private Point2D.Double point1;
private Point2D.Double point2;
}
However, when I try to run this, it draws both points on top of each other, then the line on top of that. I know that in my constructor I am calling getX() and getY() on the same event so that point1 and point2 have the coordinates. How do I call multiple events so that this does not happen.
You can count the clicks, you can do something like this
int count = 0;
public void mousePressed(MouseEvent event)
{
if(count == 0){
double x1 = rxPixel(event.getX());
double y1 = ryPixel(event.getY());
point1 = new Point2D.Double(x1, y1);
count++;
}
if(count == 1){
double x2 = rxPixel(event.getX());
double y2 = ryPixel(event.getY());
point2 = new Point2D.Double(x2, y2);
repaint();
}
}