So I have this triangle class I need to create using an abstract class. It will also be drawn by a tester class. I am part of the way through it but I am having serious issues with the math portion. I have set the coordinates in the tester class, I have no idea of how to get the pen to turn a certain degree to draw the next side of the triangle. Attached is all the classes and I have so far. Any help will be appreciated.
Tester class
import TurtleGraphics.*;
public class TestShapes1 {
public static void main (String[] args) {
// Declare and instantiate a pen, a circle and a wheel
Pen p = new StandardPen();
//Shape s1 = new Circle1 (20, 20, 20);
//Shape s2 = new Wheel1 (-20, -20, 20, 6);
Shape1 t2 = new Triangle1 (0, 0, 50, 0, 0, 30);
// Draw the circle and wheel
//s1.draw (p);
t2.draw (p);
}
}
Shape Class
import TurtleGraphics.Pen;
public interface Shape1 {
public double area();
public void draw (Pen p);
public double getXPos();
public double getYPos();
public void move (double xLoc, double yLoc);
public void stretchBy (double factor);
public String toString();
}
Triangle Class
import TurtleGraphics.Pen;
public class Triangle1 implements Shape1 {
private double x1, y1, x2, y2, x3, y3;
private double s1, s2, s3;
private double d1, d2;
//private double height, width;
public Triangle1() {
x1 = 0;
y1 = 0;
x2 = 1;
y2 = 0;
x3 = 0;
y3 = 1;
//height = 1;
//width = 1;
}
public Triangle1 (double xLoc1, double yLoc1, double xLoc2, double yLoc2, double xLoc3, double yLoc3) {
x1 = xLoc1;
y1 = yLoc1;
x2 = xLoc2;
y2 = yLoc2;
x3 = xLoc3;
y3 = yLoc3;
//height = h;
//width = w;
}
public double area() {
return (Math.abs(x1*y2-x2*y1+x2*y3-x3*y2+x3*y1-x1*y3))/2.0;
}
public void draw (Pen p) {
s1 = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
s2 = Math.sqrt((x2-x3)*(x2-x3)+(y2-y3)*(y2-y3));
s3 = Math.sqrt((x3-x1)*(x3-x1)+(y3-y1)*(y3-y1));
p.up();
p.move (x1, y1);
p.down();
p.setDirection (0);
p.move (s1);
d1 = (Math.acos((s2*s2+s3*s3-s1*s1)/(2.0*s2*s3)))*180/Math.PI;
p.turn (180 - d1);
p.move (s2);
d2 = (Math.acos((s3*s3+s1*s1-s2*s2)/(2.0*s3*s1)))*180/Math.PI;
p.turn (180 - d2);
p.move (s3);
p.turn (-90);
//p.move ();
}
public double getXPos() {
return x1;
}
public double getYPos() {
return y1;
}
public void move (double xLoc, double yLoc) {
x1 = x1 + xLoc;
y1 = y1 + yLoc;
x2 = x2 + xLoc;
y2 = y2 + yLoc;
x3 = x3 + xLoc;
y3 = y3 + yLoc;
}
public void stretchBy (double factor) {
x1 *= factor;
y1 *= factor;
}
public String toString() {
String str = "TRIANGLE\n";
// + "Width & Height: " + width + " & " + height +"\n"
// + "(X,Y) Position: (" + xPos + "," + yPos + ")\n"
// + "Area: " + area();
return str;
}
}
You don't need any math. Just pass the degrees to p.turn(). So use
p.turn(180);
instead of
d1 = (Math.acos((s2*s2+s3*s3-s1*s1)/(2.0*s2*s3)))*180/Math.PI;
p.turn (180 - d1);
See the documentation for reference:
The degrees can be an integer or floating-point number. Example:
pen.turn(-45); Rotate the pen 45 degrees clockwise.
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 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();
}
}
Currently I have an ArrayList of vertices in a 3-dimensional cartesian coordinates system. The polygon is random. It can be a car, a cup or even a dragon.
Assuming the density does not change, how to calculate the centre of mass (x,y,z) of this 3D object?
I am storing the faces and vertices in ArrayList.
public ArrayList<stlFace> StlFaces = new ArrayList<stlFace>();
public ArrayList<VertexGeometric> VertexList = new ArrayList<VertexGeometric>();
I was using this for calculating surface which is proportional to mass of each face or triangle. And to calculate center off mass of each triangle and center of mass of whole object I was using this. I added helper methods getCenter() and getSurface() to Face class to encapsulate calculations specific to just one face/triangle.
public static class Vertex {
public float x = 0;
public float y = 0;
public float z = 0;
public Vertex(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
}
public static class Face {
public Vertex v1;
public Vertex v2;
public Vertex v3;
public Face(Vertex v1, Vertex v2, Vertex v3) {
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
}
public Vertex getCenter() {
Vertex triangleCenter = new Vertex(0, 0, 0);
triangleCenter.x += v1.x;
triangleCenter.x += v2.x;
triangleCenter.x += v3.x;
triangleCenter.y += v1.y;
triangleCenter.y += v2.y;
triangleCenter.y += v3.y;
triangleCenter.z += v1.z;
triangleCenter.z += v2.z;
triangleCenter.z += v3.z;
triangleCenter.x /= 3;
triangleCenter.y /= 3;
triangleCenter.z /= 3;
return triangleCenter;
}
public float getSurface() {
float x1 = v1.x - v2.x;
float x2 = v1.y - v2.y;
float x3 = v1.z - v2.z;
float y1 = v1.x - v3.x;
float y2 = v1.y - v3.y;
float y3 = v1.z - v3.z;
return (float) Math.sqrt(
Math.pow(x2 * y3 - x3 * y2, 2) +
Math.pow(x3 * y1 - x1 * y3, 2) +
Math.pow(x1 * y2 - x2 * y1, 2)
) / 2f;
}
}
public static Vertex calculateMassCenter(List<Face> faces) {
Vertex massCenter = new Vertex(0, 0, 0);
float mass = 0;
for (Face face : faces) {
Vertex triangleCenter = face.getCenter();
float faceMass = face.getSurface();
mass += faceMass;
massCenter.x += faceMass * triangleCenter.x;
massCenter.y += faceMass * triangleCenter.y;
massCenter.z += faceMass * triangleCenter.z;
}
massCenter.x /= mass;
massCenter.y /= mass;
massCenter.z /= mass;
return massCenter;
}
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;
}
}