So for this project I wish to input an array containing a number of points. (no specific number). I am confused on how I should approach the methods and constructor because some of the methods call for changes such as grabbing the average of the X or Y values. Am I approaching this project in the right way? Should I be using a clone of the point list, or an array list or what... (note I am only showing part of the PolygonImpl class as an example, they all function similarly) The class point contains :
public class Point {
private double x;
private double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public Point translate(double dx, double dy) {
return new Point(x+dx, y+dy);
}
public double distanceTo(Point p) {
return Math.sqrt((p.x - x)*(p.x -x) + (p.y-y)*(p.y-y));
}
}
public class PolygonImpl implements Polygon {
private double xSum=0;
private double ySum=0;
private ArrayList<Point> points;
private Point[] pList;
private Point a;
PolygonImpl(Point[] pList) {
this.pList = pList.clone();
points = new ArrayList<Point>();
for (int index = 0; index < pList.length; index++) {
points.add(pList[index]);
xSum += pList[index].getX();
ySum += pList[index].getY();
}
}
public Point getVertexAverage() {
double xSum = 0;
ArrayList<Point> vlist = new ArrayList<Point> ();
double ySum = 0;
for (int index = 0; index < vlist.size(); index++) {
xSum = xSum + vlist.get(index).getX();
ySum = ySum + vlist.get(index).getY();
}
return new Point(xSum/getNumSides(), ySum/getNumSides());
}
public int getNumSides() {
return pList.length;
}
public void move(Point c) {
Point newCentroid = new Point(a.getX()+ c.getX(), a.getY() +c.getY());
}
public void scale(double factor) {
ArrayList<Point> points = new ArrayList<Point> ();
for (int index = 0; index < pList.length; index++) {
{ double x = pList[index].getX() *factor;
double y = pList[index].getY() * factor;
Point a = new Point(x,y);
points.add(index,a);
}
}
}
In this case, I don't think you need the extra List objects; they are redundant.
Here is the code just using the pList array.
public class PolygonImpl implements Polygon {
private double xSum=0;
private double ySum=0;
private Point[] pList;
private Point a;
PolygonImpl(Point[] pList) {
this.pList = pList.clone();
for (int index = 0; index < pList.length; index++) {
xSum += pList[index].getX();
ySum += pList[index].getY();
}
}
public Point getVertexAverage() {
double xSum = 0;
double ySum = 0;
for (int index = 0; index < pList.length; index++) {
xSum = xSum + pList[index].getX();
ySum = ySum + pList[index].getY();
}
return new Point(xSum/getNumSides(), ySum/getNumSides());
}
public int getNumSides() {
return pList.length;
}
public void move(Point c) {
Point newCentroid = new Point(a.getX()+ c.getX(), a.getY() +c.getY());
}
public void scale(double factor) {
for (int index = 0; index < pList.length; index++)
{
double x = pList[index].getX() *factor;
double y = pList[index].getY() * factor;
Point a = new Point(x,y);
pList[index] = a;
}
}
There are at least three things here that need pointing out.
The first is the difference between "clone" and "copy." .clone is a shallow copy; that means if you change the clone, you also change the original.
The second is regarding your use of collections. Since a polygon is a collection of points, either an array or an ArrayList would be approriate, but there is no point in using bother, or, if there is, think long and hard about whether it's a good point, and then explain in the inline documentation why it matters, else it will come back to bite you in the form of using one when you mean the other.
Third is scope. Your polygon class has instance variables (xSum and ySum) that are occluded by variables with the same name in getVertexAverage. The way they are used in getVertexAverage is appropriate in itself; the instance variables are only useful if you mean to cache the sums, which becomes more questionable, because every operation that changes a point invalidates the instance values.
Instance values are for storing data about an object (in this case, points are reasonable); instance methods are for operating on that data at a given state (in this case, the average).
Keeping this in mind, you can now understand how the move method isn't finished :)
Related
I recently started learning Java and I'm having some trouble understanding how to make arrays to work the way I want them to.
Here I have an assignment of creating a polygon class with different methods inside.
Basically the class represents a convex polygon in a plane.
The array receives user input that consists of x and y coordinates and places them inside. (max number of vertices is 10).
There are some functions that I have no idea how to do and I would really appreciate some help with.
Point Class - which is used to get coordinates
public class Point {
private double _x;
private double _y;
public Point() {
this._x = 0.0D;
this._y = 0.0D;
}
public Point(double x, double y) {
this._x = x;
this._y = y;
}
public Point(Point other) {
this._x = other._x;
this._y = other._y;
}
public double getX() {
return this._x;
}
public double getY() {
return this._y;
}
public void setX(double x) {
if (x >= 0.0D)
this._x = x;
}
public void setY(double y) {
if (y >= 0.0D)
this._y = y;
}
public boolean isAbove(Point other) {
return (this._y > other._y);
}
public boolean isUnder(Point other) {
return other.isAbove(this);
}
public boolean isLeft(Point other) {
return (this._x < other._x);
}
public boolean isRight(Point other) {
return other.isLeft(this);
}
public double distance(Point other) {
double distance = Math.sqrt(Math.pow(this._x - other._x, 2.0D) + Math.pow(this._y - other._y, 2.0D));
return distance;
}
public void move(double dx, double dy) {
double x = this._x + dx;
double y = this._y + dy;
if (x >= 0.0D && y >= 0.0D) {
this._x = x;
this._y = y;
}
}
public boolean equals(Point other) {
return (this._x == other._x && this._y == other._y);
}
public String toString() {
return "(" + this._x + "," + this._y + ")";
}
}
Polygon Class - main class im working on
/**
* Write a description of class Polygon here.
*
* #author [REDACTED]
* #version (Ver 1.0)
*/
public class Polygon {
private Point[] _vertices;
private int _noOfVertices;
public Polygon() {
_vertices = (Point[]) new Point[10];
_noOfVertices = 0;
}
public Polygon(Point[] arr) {
_vertices = (Point[]) new Point[10];
_noOfVertices = 0;
if (arr.length > 10) {
return;
}
// for (Point P : arr)
for (int i = 0; i < arr.length; i++) {
if (arr[i] != null) {
_vertices[i] = arr[i];
_noOfVertices++;
}
}
}
public boolean addVertex(double x, double y) {
if (_noOfVertices >= 10)
return false;
Point p = new Point(x, y);
_vertices[_noOfVertices] = p;
_noOfVertices++;
return true;
}
public Point highestVertex() {
for (int i = 0; i < _noOfVertices; i++) {
}
}
public String toString() {
}
public double calcPerimeter() {
for (int i = 0; i < arr.length; i++) {
}
}
public double caclArea() {
Point ppp = _vertices[zzz]
}
public boolean isBigger(Polygon other) {
}
public int findVertex(Point p) {
for (int i = 0; i < _noOfVertices; i++) {
if (p.equals(_vertices[i])) {
return i;
}
}
return -1;
}
public Point getNextVertex(Point p) {
for (int i = 0; i < _noOfVertices; i++) {
if (p.equals(_vertices[i])) {
if (i == _noOfVertices - 1) {
return new Point(_vertices[0]);
}
return new Point(_vertices[i + 1]);
}
}
return null;
}
public Polygon getBoundingBox() {
}
}
I have no idea how to do these functions:
Line 44: public Point highestVertex() {} - returns a copy of the highest point in the polygon. If there is more than one vertice at the same Y - the method will return the first one it encountered (with the said Y) If there are no vertices aka the array is empty it will return null.
Line 52: public String toString() {} - method that returns a string of points representing the polygon. The string should be in the following format:
The polygon has 5 vertices:
((2.0,1.0),(5.0,0.0),(7.0,5.0),(4.0,6.0),(1.0,4,0))
If there are no vertices the method will return a string in the following format:
The polygon has 0 vertices.
English is not my first language so I apologize for any grammar mistakes in advance.
First, it's not really the best to ask homework questions here, this is a concept that you should learn.
In highestVertex(), they outline the 3 cases for you:
1st case: If point-y is equal to another point-y, return the first vertex that has point-y.
2nd case: if arr has no elements return null.
3rd case: in loop, check each element's y value in the array and compare it to the biggest y so far.
Use this line before your loop:
int max = Integer.MIN_VALUE;
Inside loop:
if (arr[i] > max) max = arr[i]
For toString(), again loop throughout the array, and add each point to a tracker string that you will return.
String str = "";
loop
str += arr[i].toString() + ",";
This works except you need to loop until arr's second to last element, as you will have an extraneous comma after the last point.
I am writing an A* path finding algorithm for class. It works perfectly, I am able to click my character and move them to the correct location. However, after running for about 30 seconds of enemies also calling out to the A* algorithm it throws a java.lang.StackOverflowError.
The call directly before this is to the ArrayList library function grow. I can't imagine what I am doing wrong, as I am instantiating the object and am just calling ArrayList.add(Node);
What I am thinking is happening is that there are many ai characters that rely on this functionality, and after many of them calling to it, the program runs out of memory and surpasses the maximum stack size allocated for the program. However, Eclipse is not saying that Java ran out of memory or anything like this, which I would imagine would happen if the program actually ran out of memory.
Is there something that I am doing wrong? Or am I running out of memory? If so, is there a way to increase my allocated memory (I can see that I have tons of RAM left)? Or is there a better way that I could be storing my data to reduce memory loads?
Thanks! Here is the code I have currently written up, please let me know if there is any more information needed.
public class AStar {
class Node
{
public double x, y, g, h;
public Node parent;
public Node(double _x, double _y, double _g, double _h, Node _parent)
{
x = _x;
y = _y;
g = _g;
h = _h;
parent = _parent;
}
}
double start_x, start_y, goal_x, goal_y;
private double heuristic(double s_x, double s_y)
{
double x = Math.abs(s_x - goal_x);
double y = Math.abs(s_y - goal_y);
return x + y;
}
private Node GenerateRelativeNode(Node origin, double dX, double dY, List<Node> closed, List<Node> open)
{
double newX = origin.x + dX;
double newY = origin.y + dY;
Node temp = new Node(newX, newY, origin.g+1, heuristic(newX, newY), origin);
for(int i = 0; i < closed.size(); ++i)
{
if(closed.get(i).x == temp.x && closed.get(i).y == temp.y)
return null;
}
for(int i = 0; i < open.size(); ++i)
{
if(open.get(i).x == temp.x && open.get(i).y == temp.y)
{
return null;
}
}
return temp;
}
private int GetLowestFIndex(List<Node> set)
{
double min = 1000000;
int index = -1;
for(int i = 0; i < set.size(); ++i)
{
double f = set.get(i).h + set.get(i).g;
if(f < min)
{
min = f;
index = i;
}
}
return index;
}
private List<Pair<Double, Double>> PathFromNode(Node _n)
{
List<Pair<Double, Double>> path = new ArrayList<Pair<Double, Double>>();
List<Node> nodes = new ArrayList<Node>();
Node curr = _n;
while(curr.parent != null)
{
nodes.add(curr);
curr = curr.parent;
}
//now need to reverse the list.
for(int i = nodes.size()-1; i >= 0; --i)
{
Pair<Double, Double> pair = pairFromNode(nodes.get(i));
path.add(pair);
}
return path;
}
private Pair<Double, Double> pairFromNode(Node _n)
{
return new Pair<Double, Double>(new Double(_n.x), new Double(_n.y));
}
public static int pathDistance(double start_x, double start_y, double goal_x, double goal_y,
S3PhysicalEntity i_entity, S3 the_game) {
AStar a = new AStar(start_x,start_y,goal_x,goal_y,i_entity,the_game);
List<Pair<Double, Double>> path = a.computePath();
if (path!=null) return path.size();
return -1;
}
public AStar(double sX, double sY, double gX, double gY,
S3PhysicalEntity i_entity, S3 the_game) {
start_x = sX;
start_y = sY;
goal_x = gX;
goal_y = gY;
}
public List<Pair<Double, Double>> computePath() {
double start_h = heuristic(start_x, start_y);
Node start = new Node(start_x, start_y, 0, start_h, null);
List<Node> OpenSet = new ArrayList<>();
List<Node> ClosedSet = new ArrayList<>();
OpenSet.add(start);
while(!OpenSet.isEmpty())
{
int index = GetLowestFIndex(OpenSet);
Node N = OpenSet.get(index);
OpenSet.remove(index);
if(N.x == goal_x && N.y == goal_y)
{
return PathFromNode(N);
}
ClosedSet.add(N);
Node Up = GenerateRelativeNode(N, 0, 1, ClosedSet, OpenSet);
Node Left = GenerateRelativeNode(N, -1, 0, ClosedSet, OpenSet);
Node Right = GenerateRelativeNode(N, 1, 0, ClosedSet, OpenSet);
Node Down = GenerateRelativeNode(N, 0, -1, ClosedSet, OpenSet);
if(Up != null)
OpenSet.add(Up);
if(Left != null)
OpenSet.add(Left);
if(Right != null)
OpenSet.add(Right);
if(Down != null)
OpenSet.add(Down);
}
return null;
}
}
Here is the stack at the time of the StackOverflow
The WPeasant(WTroop).moveTowardsTarget(S3, int, int) line: 202 gets called thousands of times in a row at this point. The code for this function is not in any kind of while loop or for loop or anything.
This could be getting called multiple times however, due to the game object colliding with something, as I have not taken obstructions into account in the pathfinding.
It is hard to say for sure that this is the issue, as I wrote none of this engine, I only have to implement the A*. I can try and include obstacle avoidance and see if this fixes things.
I'm getting a stack overflow while trying to populate my 3D linked list. I don't understand why it's not stopping at the specified bounds, it just runs forever. It's probably a simple error, i just don't understand.
EDIT: Okay I have now updated the code and removed that silly mistake, however it's still not operating exactly as intended. It does seem to be generating the 10x10x10 list, however its running infinitely. Initialized with (10, 10, 10), it should create 10000 objects and stop. I'm just trying to create a list to represent a 3d coordinate plane and each integer coordinate is one node, accessible by direction pointer north, south, east, west, up, or down.
Any help appreciated
public class Main {
public static void main(String[] args) {
NodeController3D con = new NodeController3D(6,6, 6);
}
}
public class Node3D {
// public Node3D(Node3D... nodes) {
// if (nodes.length != 5) {
// throw new RuntimeException();
// }
// this.nodes = nodes;
// }
public Node3D[] nodes;
public int x, y, z;
public Node3D north() {
return nodes[0];
}
public Node3D south() {
return nodes[1];
}
public Node3D east() {
return nodes[2];
}
public Node3D west() {
return nodes[3];
}
public Node3D up() {
return nodes[4];
}
public Node3D down() {
return nodes[5];
}
}
public class NodeController3D {
public NodeController3D(int length, int width, int height) {
HEAD = new Node3D();
pnc(HEAD, length, width, height);
}
private void pnc(Node3D node, int xMax, int yMax, int zMax) {
if(node.nodes == null) {
node.nodes = new Node3D[5];
}
if (node.x < xMax) {
Node3D newNode = node.nodes[2] = new Node3D();
newNode.x = node.x + 1;
newNode.y = node.y;
newNode.z = node.z;
System.out.println(newNode.x + ", " + newNode.y + ", " + newNode.z);
pnc(newNode, xMax, yMax, zMax);
}
if (node.y < yMax) {
Node3D newNode = node.nodes[0] = new Node3D();
newNode.x = node.x;
newNode.y = node.y + 1;
newNode.z = node.z;
pnc(newNode, xMax, yMax, zMax);
}
if (node.z < zMax) {
Node3D newNode = node.nodes[4] = new Node3D();
newNode.x = node.x;
newNode.y = node.y;
newNode.z = node.z + 1;
pnc(newNode, xMax, yMax, zMax);
}
}
// public NodeController3D(int radius) {
//
// }
public final Node3D HEAD;
}
EDIT: Okay I have now updated the code and removed that silly mistake, however it's still not operating exactly as intended. It does seem to be generating the 10x10x10 list, however its running infinitely. Initialized with (10, 10, 10), it should create 10000 objects and stop. I'm just trying to create a list to represent a 3d coordinate plane and each integer coordinate is one node, accessible by direction pointer.
You are running into an infinite recursion.
So what is happening.
You are creating a new Array.
if(node.nodes == null) {
node.nodes = new Node3D[5];
}
You go on by using a Node3D as a newNode variable. This happens because node.x<xMax will be true. -> Node3D newNode = node.nodes[2] = new Node3D();.
You recursivly call pnc now, with this newNode.
So what happens now, node.y<yMax will be true .
Now you reassign the newNode. Node3D newNode = node.nodes[0] = new Node3D();.
and call pnc recursivly again. But you are running into a problem now. since it is a new Node3D your node.x<xMax will be true again and these two steps are happening again and ininite until you are getting your mentioned error.
To fix this error you might want to copy node.x and node.y into your newly created variable.
By changing the assignment you could jump out of the infinite recursion.
if (node.x < xMax) {
if (node.nodes[2] == null) {
node.nodes[2] = new Node3D();
}
Node3D newNode = node.nodes[2];
newNode.x = node.x + 1;
newNode.y = node.y;
newNode.z = node.z;
pnc(newNode, xMax, yMax, zMax);
}
if (node.y < yMax) {
if (node.nodes[0] == null) {
node.nodes[0] = new Node3D();
}
Node3D newNode = node.nodes[0];
newNode.x = node.x;
newNode.y = node.y + 1;
newNode.z = node.z;
pnc(newNode, xMax, yMax, zMax);
}
if (node.z < zMax) {
if (node.nodes[4] == null) {
node.nodes[4] = new Node3D();
}
Node3D newNode = node.nodes[4];
newNode.x = node.x;
newNode.y = node.y;
newNode.z = node.z + 1;
pnc(newNode, xMax, yMax, zMax);
}
But since i donĀ“t know what you are trying to achive with this specific elements at these specifics index in your array this might be a wrong solution for you.
Whenever you create a new Node3D, the variables x, y and z are not initialized, therefore they will evaluate to 0 if accessed.
In the pnc method, there will always be cases where either x < xMax, y < yMax or z < zMax since those are all set to 1.
A good practice is to make variables private and final where possible:
class Node3D {
private final Node3D[] nodes;
private final int x;
private final int y;
private final int z;
public Node3D(int x, int y, int z) {
this.nodes = new Node3D[6];
this.x = x;
this.y = y;
this.z = z;
}
}
This will prevent these kinds of errors in the future. You can create getter methods for the variables. If you really need to reassign the values for x, y or z, you could instead create a new Node3D so that the object can remain immutable.
You were navigating to nodes in the 3D grid in multiple ways. For example, you went to (1,1,0) both from (1,0,0) and (0,1,0), creating duplicates for the same coordinate. This way, you create a number of nodes equal to the total sum of cube paths for each node.
An other approach is to populate a cube of nodes, and give each node a reference to the cube that they are in. Then when you need to go North, you just let the cube return the right node, using the coordinates of the current node. You could use Apache's MultiKeyMap as a grid:
class Node3D {
private static int instances = 0;
public final int x;
public final int y;
public final int z;
public final int number;
public final MultiKeyMap<Integer, Node3D> gridMap;
public Node3D(MultiKeyMap<Integer, Node3D> gridMap, int x, int y, int z) {
this.gridMap = gridMap;
this.x = x;
this.y = y;
this.z = z;
this.number = instances++;
}
//Add/alter these methods according to your orientation
//Returns null if no value is present, you might want to handle this
public Node3D getNorthNode() {
return gridMap.get(this.x + 1, this.y, this.z);
}
//Other getters omitted
#Override
public String toString() {
return "Node3D#" + number + "[" + x + ", " + y + ", " + z + "]";
}
}
Now initialization can be done as follows:
MultiKeyMap<Integer, Node3D> gridMap = new MultiKeyMap<>();
for(int x = 0; x < xMax; x++) {
for(int y = 0; y < yMax; y++) {
for(int z = 0; z < zMax; z++) {
gridMap.put(x, y, z, new Node3D(gridMap, x, y, z));
}
}
}
System.out.println(gridMap.get(4, 4, 4).getNorthNode());
I am getting a null pointer exception when I try to use the perimeter function in my code. It seems that the Points array (Point is a simple object with an x and y coordinate) is not correctly initialising, I believe I may have declared the array wrongly or the constructor is incorrect.
package shapes;
import static java.lang.Math.*;
public class Triangle {
private int sides = 3;
private Point[] Points = new Point[sides];
public Triangle(Point[] vertices) {
vertices = Points;
}
public double perimeter() {
return Points[0].distance(Points[1]) + Points[1].distance(Points[2]) + Points[2].distance(Points[0]);
}
public double area() {
double semiperimeter = perimeter() / 2;
return sqrt(semiperimeter * (semiperimeter - Points[0].distance(Points[1])) * (semiperimeter - Points[1].distance(Points[2])) * (semiperimeter - Points[2].distance(Points[0])));
}
#Override
public String toString() {
return "Triangle has perimeter of " + perimeter() + " and an area of " + area();
}
public void translate(int dx, int dy) {
for(int i = 0; i < 3; i++) {
Points[i].translate(dx, dy);
}
}
public void scale(int factor) {
for(int i = 0; i < 3; i++) {
Points[i].scale(factor);
}
}
public Point getVertex(int i) {
return Points[i];
}
}
Any help is much appreciated!
You need to reverse this in your constructor:
vertices = Points;
to
Points = vertices ;
You need to initialize your Points array with the input vertices and not the other way around.
package point;
//an array arr is populated with random x,y points using MyPoint then
public static void main(String[] args) throws IOException {
HashMap<MyPoint,Integer> map = new HashMap<MyPoint,Integer>();
Integer val =0;
for(int i = 0; i < arr.length ; i++)
{
map.put(arr[i],val);
}
}
//second file.
package point;
public class MyPoint implements Comparable<MyPoint>{
private int x;
private int y;
public MyPoint(int x1, int y1) {
x = x1;
y = y1;
}
public boolean equals(Object p) {
MyPoint p1;
try {p1 = (MyPoint) p;}
catch (ClassCastException ex) {return false;}
return (x == p1.x) && (y == p1.y);
}
public int hashCode() {
return ((y * 31) ^ x);
}
Here is my code MyPoint stores x y points. I'm using this code to get unique sets of x,y points with no duplicates.
My Question is how do I retrieve the x y values in MyPoint ? Is this an efficient way to use a HashMap to filter unique X,Y points. Also here is the HashCode I made.
Is there a better HashCode I could use?
An easy way would be to make getter methods for x and y:
public int getX(){
return x;
}
public int getY(){
return y;
}
As for a HashMap, you could have it map from X coordinates to a list of MyPoints with that X coordinate and unique Y coordinates (HashMap). However, if all you're looking for is uniqueness, you could just store every MyPoint in a set/ArrayList and see if a similar point already exists, using your .equals() method. Hope that helps!