Need help understanding instance variables - java

I have these instance variables in my class:
private int x1 = 0;
private int y1 = 0;
private int x2 = 0;
private int y2 = 0;
private int height = y2 - y1;
private int width = x2 - x1;
In my program I use methods that change the xs and ys, so I expect height and width to change as well. However, they don't. Can someone explain why this is the case, and how I can make their values "update" after a method call that changes the xs and ys?

Your expression private int height = y2 - y1; is evaluated when the instance variable is initialised: i.e. when the class is instantiated. From that point on it is does nothing - it is not "linked" in any way to the source parts of the expression and does not update when they update.
You probably want a method on the class (public if it is used outside the class, private otherwise) which would be something like the below. You can get rid of your height and width fields:
public int getHeight() { return this.y2 - this.y1; }
However if you decide you still need the width and height internally I would change this to a private method named calculateHeight. Methods called getXYZ are usually accessors for fields and not mutation methods. You can then call this (or the equivalent calculateWidth()) whenever you change the field values for y2, y1, x2, x1.
public int getHeight() { return this.height; }
private int calculateHeight() { return this.y2 - this.y1; }
...
this.y2 = this.y2 + 10;
this.height = calculateHeight();
As an aside I would argue that width and height are positive numbers regardless of y2 being more or less than y1. You can use Math.abs to remove the sign on the subtraction result:
public int getHeight() { return Math.abs(y2 - y1); }
My preference would be to use a single method to return the height and width as a dimension. The two values are really a single piece of data at a point in time. You could use java.awt.Dimension for this:
public Dimension getDimension() {
new Dimension(Math.abs(x2 - x1), Math.abs(y2 - y1));
}

Your calculations are only being performed once: at the initialization of the height and width fields. After changing the x's and y's, you need to repeat the calculations for the height and width, to update their values.
Ex:
x2 = 55;
width = x2 - x1;
When performing a math calculation, the variable's values are only used once. Once the calculation is done, (height = y2 - y1), the height variable does not relate to the y2 and y1 variables.
Solution: Manually update the height and width values, by repeating the calculation, when the x's and y's values are changed.
Solution example:
public void setX1(int x1) {
this.x1 = x1;
height = x2 - x1;
}
Or an even better example is not to have the height and width fields, and use this:
public int getHeight() {
return y2 - y1;
}
public int getWidth() {
return x2 - x1;
}

Related

Resizing a square in Java

I am having an annoying problem trying to resize a draggable square in Java. To resize the square I am checking to see if the mouse cursor is inside a rectangular area surrounding the bottom right hand corner of the square - if it is, then the square resizes when I drag the mouse. This code works fine, BUT if I then click on the square to drag it around the screen, it jumps back to the default size of 100 x 100.
My class starts off like this:
public class DragPanel extends JPanel implements MouseListener,
MouseMotionListener, MouseWheelListener
{
Graphics2D g2;
Rectangle2D square;
Color colour;
double x1, y1, x2, y2, sizex, sizey;
double offsetX, offsetY;
double oldx, oldy;
boolean dragging = false;
boolean resizing = false;
public DragPanel()
{
x1 = 10.0;
y1 = 10.0;
sizex = 100.0;
sizey = 100.0;
x2 = x1 + sizex;
y2 = y1 + sizey;
square = new Rectangle2D.Double(x1, y1, sizex, sizey);
colour = Color.BLUE;
setFocusable(true);
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
this.requestFocus();
}
Here are my Mouse Pressed and Mouse Dragged Methods:
Mouse Pressed:
#Override
public void mousePressed(MouseEvent ev)
{
double mx = ev.getX();
double my = ev.getY();
if (mx > x1 && mx < x2 && my > y1 && my < y2)
{
dragging = true;
offsetX = mx - x1;
offsetY = my - y1;
}
if (mx > x2 - 3 && mx < x2 + 3 && my > y2 - 3 && my < y2 + 3)
{
oldx = mx;
oldy = my;
resizing = true;
}
}
Mouse Dragged:
#Override
public void mouseDragged(MouseEvent ev)
{
if (dragging)
{
double mx = ev.getX();
double my = ev.getY();
x1 = mx - offsetX;
y1 = my - offsetY;
x2 = x1 + sizex;
y2 = y1 + sizey;
square = new Rectangle2D.Double(x1, y1, sizex, sizey);
repaint();
}
if (resizing)
{
double mx = ev.getX();
double my = ev.getY();
double diffx, diffy;
diffx = oldx - mx;
diffy = oldy - my;
square = new Rectangle2D.Double(x1, y1, sizex - diffx, sizey - diffy);
repaint();
}
}
The code you see above does what I want it to do in terms of resizing, but as I already explained, DOESN'T work properly when I try and drag the square after resizing. And I know why - it is because of THIS line:
square = new Rectangle2D.Double(x1, y1, sizex - diffx, sizey - diffy);
So my solution was to change the last part of the if (resizing) block as follows:
diffx = oldx - mx;
diffy = oldy - my;
sizex -= diffx;
sizey -= diffy;
square = new Rectangle2D.Double(x1, y1, sizex, sizey);
repaint();
When I do this however, the thing stops working! I can still drag the square, but if I try and resize, the movement is erratic and extreme.
WHAT am I doing wrong?
Seems to me that the conditions for dragging and resizing are not mutually exclusive, particularly close to the borders. Assuming you are putting these two flags to false on mouse release, a single mouse click or move could still go into both modes at once.
I would put else if (mx > x2 - 3 && mx < x2 + 3 && my > y2 - 3 && my < y2 + 3) and else if (resizing) to avoid any intended consequences. This would make any dragging a priority over any resizing.
Being a graphical thing, hard to say if this is your problem (or part of it), but give it can't hurt to clear this up.
Edit: Of course, you might want to do the resize conditions take priority over (go before) the drag condition, since the area for resizing is much smaller than the area for dragging.
Edit 2: BTW, when you resize you are modifying your square, but not your sizex and sizey variables. Naturally, when you ask for those values when dragging they are the same as before (100). Store the changes and it should work. On that note, it is probably a bad idea to have multiple representations of the same information. It causes redundancies and synchronization issues like this one. If possible, remove these variables: double x1, y1, x2, y2, sizex, sizey; and read/write then from the current state of square (you probably don't need a new square every time either).
I treat these a two separate functions:
Check out the Component Mover
and the Component Resizer
The code in these classes is more complex than you need but you may want to consider the separation of the logic for each function in your final solution.
I found the solution, or rather, I found a solution. I changed my if (resizing) statement within the mouseDragged method as follows:
Old:
if (resizing)
{
double mx = ev.getX();
double my = ev.getY();
double diffx, diffy;
diffx = oldx - mx;
diffy = oldy - my;
square = new Rectangle2D.Double(x1, y1, sizex - diffx, sizey - diffy);
repaint();
}
New:
if (resizing)
{
double mx = ev.getX();
double my = ev.getY();
x2 = mx - rOffX;
y2 = my - rOffX;
sizex = x2 - x1;
sizey = sizex;
square.setRect(x1, y1, sizex, sizey);
repaint();
}

Fast Java Image Collision Detection Algorithm

I am working on a Java 2D video game, and I am looking for a faster Collision Detection algorithm than what you see below. I am trying to detect a collision between a torpedo and a ship. The algorithm I am using seriously impacts the performance of my main Graphics 2D dispatch loop, slowing down my screen repaints. Was wondering if anyone could recommend a better algorithm for how to handle this, that was quicker to detect a collision? Pointers to sample code would be great!
Here is the slow algorithm that I am using that goes Pixel by Pixel..
private boolean isPixelCollide(double x1, double y1, VolatileImage image1,
double x2, double y2, VolatileImage image2) {
double width1 = x1 + image1.getWidth() -1,
height1 = y1 + image1.getHeight() -1,
width2 = x2 + image2.getWidth() -1,
height2 = y2 + image2.getHeight() -1;
int xstart = (int) Math.max(x1, x2),
ystart = (int) Math.max(y1, y2),
xend = (int) Math.min(width1, width2),
yend = (int) Math.min(height1, height2);
// intersection rect
int toty = Math.abs(yend - ystart);
int totx = Math.abs(xend - xstart);
for (int y=1;y < toty-1;y++){
int ny = Math.abs(ystart - (int) y1) + y;
int ny1 = Math.abs(ystart - (int) y2) + y;
for (int x=1;x < totx-1;x++) {
int nx = Math.abs(xstart - (int) x1) + x;
int nx1 = Math.abs(xstart - (int) x2) + x;
try {
if (((image1.getSnapshot().getRGB(nx,ny) & 0xFF000000) != 0x00) &&
((image2.getSnapshot().getRGB(nx1,ny1) & 0xFF000000) != 0x00)) {
// collide!!
return true;
}
} catch (Exception e) {
// System.out.println("s1 = "+nx+","+ny+" - s2 = "+nx1+","+ny1);
}
}
}
return false;
}
You can define bounds of each object on your scene with Rectangle class and then use Rectangle#intersect(Rectangle rect) method. So your intersect method can look like this:
private boolean intersect(double x1, double y1, VolatileImage image1, double x2, double y2, VolatileImage image2) {
return (new Rectangle(x1, y1, image1.getWidth(), image1.getHeight()).intersect(new Rectangle(x2, y2, image2.getWidth(), image2.getHeight()));
}
That's a lot of getRGB(). Have you considered representing the torpedo and a ship as boxes first and use it for some early reject mechanism?
Also try dividing the pixel tests by 2. Test half the resolution by skipping along every other pixel. Half resolution shouldn't be much of a problem depending on your picture.

Float calculation keeps returning 0.0

I'm writing a method to return a List of Points between 2 Points. Somehow the slope (y2-y1)/(x2-x1) keeps giving me 0.0 all the time regardless startPoint and endPoint positions.
Here is the method:
public ArrayList<Point> calculatePath(Point startPoint, Point endPoint) {
ArrayList<Point> calculatedPath = new ArrayList<>();
int x1 = startPoint.x;
int y1 = startPoint.y;
int x2 = endPoint.x;
int y2 = endPoint.y;
System.out.println("Run");
if ((x2 - x1) != 0) {
float ratio = ((y2 - y1) / (x2 - x1));
System.out.println(ratio);
int width = x2 - x1;
for (int i = 0; i < width; i++) {
int x = Math.round(x1 + i);
int y = Math.round(y1 + (ratio * i));
calculatedPath.add(new Point(x, y));
}
} else {
if (y1 < y2) {
while (y1 == y2) {
calculatedPath.add(new Point(x1, y1));
y1++;
}
} else {
while (y1 == y2) {
calculatedPath.add(new Point(x1, y1));
y1--;
}
}
}
return calculatedPath;
}
Can anyone point out what i'm doing wrong? Thanks
Try casting your ints into floats as well
During your caluclation you need to cast at least one element to float:
float ratio = ((float)(y2 - y1) / (float)(x2 - x1));
That is because:
float a = integer / integer
^^^^^^^^^^^^^^^^^ - The result will be an integer.
Therefore u need to cast at least one of the
to float
This examples shows it easly:
public static void main(String[] args)
{
float resultWithoutCast = 5 / 3;
float resultWithCast = (float)5 /3 ;
System.out.println(resultWithoutCast);
System.out.println(resultWithCast);
}
It will print
1.0
1.6666666
You forgot to cast your int during division. Try something like this:-
float ratio = ((float)(y2 - y1) / (x2 - x1));
When performing arithmetic you need to make sure you use types which allow for the expected result. For example, the issue with your code is you are looking for a floating point result but using int - the problem here is int will simply truncate any floating point.
There are a couple of ways to solving this problem - as already suggested you could use a cast
float ratio = ((float)(y2 - y1) / (x2 - x1));
Or you could use float variables, it makes for more readable code e.g.
float x1 = (float)startPoint.X;
float y1 = (float)startPoint.Y;
...
float ratio = (y2 - y1) / (x2 - x1);
However, this results in more casting.
Alternatively, you could swap out Point for PointF and eliminate casting completely.

Two ways to get value of Point object?

How come you can get the x and y values from a java.awt.Point class by using a method and referencing the value?
Point p = new Point(10,20);
int x0 = p.getX();
int y0 = p.getY();
int x1 = p.x;
int y1 = p.y;
System.out.println(x0+"=="+x1+"and"+y0+"=="+y1);
Did the people who made this class forget to make x and y private?
Looking at the javadoc, these seem to return different types. p.x returns an int while p.getX() returns a double.
The source code of Point shows this:
public int x;
//...
public double getX() {
return x;
}
So it looks like that's its only purpose. getX() is a more convenient way to get the coordinates as a double.
Change to
double x0 = p.getX();
// getX returns the X coordinate of this Point2D in double precision

Need a function to limit a line (known by its coordinates) in its length

I need a function which takes a line (known by its coordinates)
and return a line with same angle, but limited to certain length.
My code gives correct values only when the line is turned 'right'
(proven only empirically, sorry).
Am I missing something?
public static double getAngleOfLine(int x1, int y1, int x2, int y2) {
double opposite = y2 - y1;
double adjacent = x2 - x1;
if (adjacent == Double.NaN) {
return 0;
}
return Math.atan(opposite / adjacent);
}
// returns newly calculated destX and destY values as int array
public static int[] getLengthLimitedLine(int startX, int startY,
int destX, int destY, int lengthLimit) {
double angle = getAngleOfLine(startX, startY, destX, destY);
return new int[]{
(int) (Math.cos(angle) * lengthLimit) + startX,
(int) (Math.sin(angle) * lengthLimit) + startY
};
}
BTW: I know that returning arrays in Java is stupid,
but it's just for the example.
It would be easier to just treat it as a vector. Normalize it by dividing my its magnitude then multiply by a factor of the desired length.
In your example, however, try Math.atan2.
In Python because I don't have a Java compiler handy:
import math
def getLengthLimitedLine(x1, y1, x2, y2, lengthLimit):
length = math.sqrt((x2-x1)**2 + (y2-y1)**2)
if length > lengthLimit:
shrink_factor = lengthLimit / length
x2 = x1 + (x2-x1) * shrink_factor
y2 = y1 + (y2-y1) * shrink_factor
return x2, y2
print getLengthLimitedLine(10, 20, 25, -5, 12)
# Prints (16.17, 9.71) which looks right to me 8-)
It's an easy problem if you understand something about vectors.
Given two points (x1, y1) and (x2, y2), you can calculate the vector from point 1 to 2:
v12 = (x2-x1)i + (y2-y2)j
where i and j are unit vectors in the x and y directions.
You can calculate the magnitude of v by taking the square root of the sum of squares of the components:
v = sqrt((x2-x2)^2 + (y2-y1)^2)
The unit vector from point 1 to point 2 equals v12 divided by its magnitude.
Given that, you can calculate the point along the unit vector that's the desired distance away by multiply the unit vector times the length and adding that to point 1.
Encapsulate Line in a class, add a unit method and a scale method.
public class Line {
private float x;
private float y;
public Line(float x1, float x2, float y1, float y2) {
this(x2 - x1, y2 - y1);
}
public Line(float x, float y) {
this.x = x;
this.y = y;
}
public float getLength() {
return (float) Math.sqrt((x * x) + (y * y));
}
public Line unit() {
return scale(1 / getLength());
}
public Line scale(float scale) {
return new Line(x * scale, y * scale);
}
}
Now you can get a line of arbitrary length l by calling
Line result = new Line(x1, x2, y1, y2).unit().scale(l);
No need to use trig, which can have some nasty edge cases. Just use similar triangles:
public static int[] getLengthLimitedLine(int startX, int startY,
int destX, int destY, int lengthLimit)
{
int deltaX = destX - startX;
int deltaY = destY - startY;
int lengthSquared = deltaX * deltaX + deltaY * deltaY;
// already short enough
if(lengthSquared <= lengthLimit * lengthLimit)
return new int[]{destX, destY};
double length = Math.sqrt(lengthSquared);
double newDeltaX = deltaX * lengthLimit / length;
double newDeltaY = deltaY * lengthLimit / length;
return new int[]{(int)(startX + newDeltaX), (int)(startY + newDeltaY)};
}
Just use the Pythagorean theorem, like so:
public static int[] getLengthLimitedLine(int start[], int dest[], int lengthLimit) {
int xlen = dest[0] - start[0]
int ylen = dest[1] - start[1]
double length = Math.sqrt(xlen * xlen + ylen * ylen)
if (length > lengthLimit) {
return new int[] {start[0], start[1],
start[0] + xlen / lengthLimit,
start[1] + ylen / lengthLimit}
} else {
return new int[] {start[0], start[1], dest[0], dest[1];}
}
}

Categories