package com.austinv11.collectiveframework.utils.math.geometry; import com.austinv11.collectiveframework.utils.math.ThreeDimensionalVector; import com.austinv11.collectiveframework.utils.math.TwoDimensionalVector; import java.util.ArrayList; import java.util.List; /** * Base unit for shapes */ public class Line { private ThreeDimensionalVector start, end; /** * Two dimensional constructor for a line * @param startCoord Start coord of the line * @param endCoord End coord of the line */ public Line(TwoDimensionalVector startCoord, TwoDimensionalVector endCoord) { this(startCoord.to3D(), endCoord.to3D()); } /** * Three dimensional constructor for a line * @param startCoord Start coord of the line * @param endCoord End coord of the line */ public Line(ThreeDimensionalVector startCoord, ThreeDimensionalVector endCoord) { start = startCoord; end = endCoord; } /** * Gets the starting x coordinate of the line * @return The x coordinate */ public double getStartX() { return start.getX(); } /** * Gets the starting y coordinate of the line * @return The y coordinate */ public double getStartY() { return start.getY(); } /** * Gets the starting z coordinate of the line * @return The z coordinate */ public double getStartZ() { return start.getZ(); } /** * Gets the ending x coordinate of the line * @return The x coordinate */ public double getEndX() { return end.getX(); } /** * Gets the ending y coordinate of the line * @return The y coordinate */ public double getEndY() { return end.getY(); } /** * Gets the ending z coordinate of the line * @return The z coordinate */ public double getEndZ() { return end.getZ(); } /** * Gets the 2D starting point for the line * @return The point */ public TwoDimensionalVector get2DStart() { return start.to2D(); } /** * Gets the 2D ending point for the line * @return The point */ public TwoDimensionalVector get2DEnd() { return end.to2D(); } /** * Gets the 3D starting point for the line * @return The point */ public ThreeDimensionalVector get3DStart() { return start; } /** * Gets the 3D ending point for the line * @return The point */ public ThreeDimensionalVector get3DEnd() { return end; } /** * Gets the 2D slope for the line * @return The slope or NaN if it is undefined */ public double get2DSlope() { if (end.getX() - start.getX() == 0) return Double.NaN; return (end.getY() - start.getY())/(end.getX() - start.getX()); } private TwoDimensionalVector find2DPoint(double x, double slope) { if (Double.isNaN(slope)) { return new TwoDimensionalVector(x, start.getY() > end.getY() ? end.getY() : start.getY()); } double yInt = start.getY() - (start.getX() * slope); return new TwoDimensionalVector(x, (x * slope) + yInt); } /** * Plugs in the given x coordinate to the line to calculate a 2D point from its equation * @param x The x coordinate * @return The calculated coordinate. NOTE: y will be the minimum y-value of the line if the slope of the line is undefined */ public TwoDimensionalVector find2DPoint(double x) { return find2DPoint(x, get2DSlope()); } /** * Checks if the point is valid on a 2D plane * @param coord The coord to check * @return If it's on the line */ public boolean isPointValid(TwoDimensionalVector coord) { if (Double.isNaN(get2DSlope())) return ((coord.getY() <= start.getY() && coord.getY() >= end.getY()) || (coord.getY() >= start.getY() && coord.getY() <= end.getY())); return ((coord.getX() <= start.getX() && coord.getX() >= end.getX()) || (coord.getX() >= start.getX() && coord.getX() <= end.getX())) && //If point is within bounds of the line (find2DPoint(coord.getX()).getY() == coord.getY()); //If the point follows the rules of the line } /** * Returns all points with whole number x values on the line * @return The points */ public List<TwoDimensionalVector> getAll2DPoints() { List<TwoDimensionalVector> vectors = new ArrayList<TwoDimensionalVector>(); TwoDimensionalVector start = getSorted2DCoords()[0]; TwoDimensionalVector end = getSorted2DCoords()[1]; int x = start.getRoundedX(); if (isPointValid(new TwoDimensionalVector(start.getRoundedX(), start.getRoundedY()))) vectors.add(new TwoDimensionalVector(start.getRoundedX(), start.getRoundedY())); if (Double.isNaN(get2DSlope())) { if (isPointValid(new TwoDimensionalVector(x, start.getY()))) { int startY = isPointValid(new TwoDimensionalVector(x, start.getRoundedY())) ? start.getRoundedY() : (int)Math.ceil(start.getY()); for (int y = startY; y <= end.getRoundedY(); y++) { if (isPointValid(new TwoDimensionalVector(x, y))) vectors.add(new TwoDimensionalVector(x, y)); } } } else { while (!(x > end.getRoundedX())) { x++; vectors.add(find2DPoint(x)); } } return vectors; } private TwoDimensionalVector[] getSorted2DCoords() { TwoDimensionalVector[] array = new TwoDimensionalVector[2]; if (start.getX() < end.getX()) { array[0] = start.to2D(); array[1] = end.to2D(); } else { array[0] = end.to2D(); array[1] = start.to2D(); } return array; } private double get2DSlopeForZ() { if (end.getX() - start.getX() == 0) return Double.NaN; return (end.getZ() - start.getZ())/(end.getX() - start.getX()); } /** * Plugs in the given x coordinate to the line to calculate a 3D point from its equation * @param x The x coordinate * @return The calculated coordinate */ public ThreeDimensionalVector find3DPoint(double x) { //Basically does two 2D calculations for y, where the first calculation is actually y and the second z double y = find2DPoint(x, get2DSlope()).getY(); double z = find2DPoint(x, get2DSlopeForZ()).getY(); return new ThreeDimensionalVector(x, y, z); } /** * Checks if the point is valid on a 3D plane * @param coord The coord to check * @return If it's on the line */ public boolean isPointValid(ThreeDimensionalVector coord) { return ((coord.getX() <= start.getX() && coord.getX() >= end.getX()) || (coord.getX() >= start.getX() && coord.getX() <= end.getX())) && //If point is within bounds of the line (find3DPoint(coord.getX()).getY() == coord.getY() && find3DPoint(coord.getX()).getZ() == coord.getZ()); //If the point follows the rules of the line } /** * Returns all points with whole number x values on the line * @return The points */ public List<ThreeDimensionalVector> getAll3DPoints() { List<ThreeDimensionalVector> vectors = new ArrayList<ThreeDimensionalVector>(); ThreeDimensionalVector start = getSorted3DCoords()[0]; ThreeDimensionalVector end = getSorted3DCoords()[1]; int x = start.getRoundedX(); if (isPointValid(new ThreeDimensionalVector(start.getRoundedX(), start.getRoundedY(), start.getRoundedZ()))) vectors.add(new ThreeDimensionalVector(start.getRoundedX(), start.getRoundedY(), start.getRoundedZ())); if (Double.isNaN(get2DSlope()) || Double.isNaN(get2DSlopeForZ())) { if (isPointValid(new ThreeDimensionalVector(x, start.getY(), start.getZ()))) { int startY = isPointValid(new ThreeDimensionalVector(x, start.getRoundedY(), start.getZ())) ? start.getRoundedY() : (int)Math.ceil(start.getY()); for (int y = startY; y <= end.getRoundedY(); y++) { int startZ = isPointValid(new ThreeDimensionalVector(x, y, start.getRoundedZ())) ? start.getRoundedZ() : (int)Math.ceil(start.getZ()); for (int z = startZ; z <= end.getRoundedZ(); z++) { if (isPointValid(new ThreeDimensionalVector(x, y, z))) vectors.add(new ThreeDimensionalVector(x, y, z)); } } } } else { while (!(x > end.getRoundedX())) { x++; vectors.add(find3DPoint(x)); } } return vectors; } private ThreeDimensionalVector[] getSorted3DCoords() { ThreeDimensionalVector[] array = new ThreeDimensionalVector[2]; if (start.getX() < end.getX()) { array[0] = start; array[1] = end; } else { array[0] = end; array[1] = start; } return array; } /** * Gets the length of the line * @return The length */ public double getLength() { return start.distanceTo(end); } /** * Creates a new line as a copy of this line but with the set start coord * @param coord The new start coord * @return The new, modified line */ public Line setStart(TwoDimensionalVector coord) { return setStart(coord.to3D()); } /** * Creates a new line as a copy of this line but with the set start coord * @param coord The new start coord * @return The new, modified line */ public Line setStart(ThreeDimensionalVector coord) { return new Line(coord, end); } /** * Creates a new line as a copy of this line but with the set end coord * @param coord The new end coord * @return The new, modified line */ public Line setEnd(TwoDimensionalVector coord) { return setEnd(coord.to3D()); } /** * Creates a new line as a copy of this line but with the set end coord * @param coord The new end coord * @return The new, modified line */ public Line setEnd(ThreeDimensionalVector coord) { return new Line(coord, end); } /** * Rotates the line around a point * @param point The point to rotate around * @param angle The angle to rotate by * @return The new line */ public Line rotate2D(TwoDimensionalVector point, double angle) { TwoDimensionalVector newStart = start.to2D().rotate(point, angle); TwoDimensionalVector newEnd = end.to2D().rotate(point, angle); return new Line(newStart, newEnd); } /** * Rotates the line about the origin * @param angle The angle to rotate by * @return The new line */ public Line rotate2D(double angle) { return rotate2D(new TwoDimensionalVector(0,0), angle); } /** * Rotates the line across the x-axis around a point * @param point The point to rotate around * @param angle The angle to rotate by * @return The new line */ public Line rotate3DX(ThreeDimensionalVector point, double angle) { ThreeDimensionalVector newStart = start.rotateX(point, angle); ThreeDimensionalVector newEnd = end.rotateX(point, angle); return new Line(newStart, newEnd); } /** * Rotates the line across the x-axis about the origin * @param angle The angle to rotate by * @return The new line */ public Line rotate3DX(double angle) { return rotate3DX(new ThreeDimensionalVector(0, 0, 0), angle); } /** * Rotates the line across the y-axis around a point * @param point The point to rotate around * @param angle The angle to rotate by * @return The new line */ public Line rotate3DY(ThreeDimensionalVector point, double angle) { ThreeDimensionalVector newStart = start.rotateY(point, angle); ThreeDimensionalVector newEnd = end.rotateY(point, angle); return new Line(newStart, newEnd); } /** * Rotates the line across the y-axis about the origin * @param angle The angle to rotate by * @return The new line */ public Line rotate3DY(double angle) { return rotate3DY(new ThreeDimensionalVector(0, 0, 0), angle); } /** * Rotates the line across the z-axis around a point * @param point The point to rotate around * @param angle The angle to rotate by * @return The new line */ public Line rotate3DZ(ThreeDimensionalVector point, double angle) { ThreeDimensionalVector newStart = start.rotateZ(point, angle); ThreeDimensionalVector newEnd = end.rotateZ(point, angle); return new Line(newStart, newEnd); } /** * Rotates the line across the z-axis about the origin * @param angle The angle to rotate by * @return The new line */ public Line rotate3DZ(double angle) { return rotate3DZ(new ThreeDimensionalVector(0, 0, 0), angle); } @Override public boolean equals(Object other) { if (other instanceof Line) return ((Line) other).start.equals(start) && ((Line) other).end.equals(end); return false; } @Override public String toString() { return "Line(X1:"+getStartX()+" Y1:"+getStartY()+" Z1:"+getStartZ()+"X2:"+getEndX()+" Y2:"+getEndY()+" Z2:"+getEndZ()+")"; } }