Resizing a square in Java - 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();
}

Related

JavaFX Mouse Move or Drag events "skip" pixels - can this be prevented?

Below example demonstrates "gaps" in drawing due to mouse events arriving too slowly (I presume). I'd like to capture all "mouseovered" pixels, without drawing lines from the last pixel written to the current one. Doing this assumes the mouse moved in a straight line, and that may not be the case for very fast movements.
Do we have any workarounds to get the missing events?
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.awt.*;
public class DrawTest extends Application {
private static Point point;
#Override
public void start(Stage stage) {
Canvas canvas = new Canvas(800, 600);
AnchorPane anchorPane = new AnchorPane(canvas);
GraphicsContext gc = canvas.getGraphicsContext2D();
point = MouseInfo.getPointerInfo().getLocation();
gc.setFill(Color.WHITE);
gc.fillRect(0, 0, 800, 600);
canvas.setOnMouseDragged((event) -> {
gc.getPixelWriter().setColor((int) event.getX(), (int) event.getY(), Color.BLACK);
System.out.println("X " + event.getX() + " Y " + event.getY());
});
stage.setScene(new Scene(anchorPane));
stage.show();
}
public static void main(String[] args) {
Application.launch(DrawTest.class, args);
}
}
This is an example of some of the event coordinates that get rendered with large gaps:
X 189.0 Y 248.0
X 193.0 Y 248.0
X 199.0 Y 248.0
X 204.0 Y 247.0
X 211.0 Y 244.0
X 225.0 Y 240.0
I've tried using this flag but it didn't help (on Linux): -Djavafx.animation.fullspeed=true
I'd be willing to try limited Swing options or maybe even more "native" workarounds, but obviously would prefer a JFX resolution or at least an explanation why JFX doesn't do this. Would need a Linux compatible solution, Windows too I guess. :P
This question is very similar, but the solutions proposed are still of a "connect the dots" nature and not exactly suitable if you need to accurately record or react to the pixel-by-pixel mouse path, so please don't close this as a duplicate - it's not.
How to draw a continuous line with mouse on JavaFX canvas?
Edit:
Well... it turns out I guess JavaFX is vindicated after all.
After adding the following bit in the middle of the example above, if you'll forgive the expected thread racing, it does look to me like JFX is matching AWT event for event with this crude polling method. Probably the best that can be done is to fill in the gaps somehow.
Thread t = new Thread(() -> {
while (true) {
Point p = MouseInfo.getPointerInfo().getLocation();
if (point.x != p.x || point.y != p.y) {
point = p;
System.out.println(point);
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
});
t.setDaemon(true);
t.start();
Output:
java.awt.Point[x=2890,y=301]
X 2890.0 Y 301.0
java.awt.Point[x=2884,y=303]
X 2884.0 Y 303.0
java.awt.Point[x=2870,y=308]
X 2870.0 Y 308.0
X 2848.0 Y 314.0
java.awt.Point[x=2848,y=314]
java.awt.Point[x=2822,y=322]
X 2822.0 Y 322.0
X 2790.0 Y 330.0
java.awt.Point[x=2790,y=330]
java.awt.Point[x=2760,y=338]
X 2760.0 Y 338.0
X 2726.0 Y 344.0
java.awt.Point[x=2726,y=344]
X 2694.0 Y 350.0
java.awt.Point[x=2694,y=350]
X 2668.0 Y 356.0
java.awt.Point[x=2668,y=356]
java.awt.Point[x=2646,y=360]
X 2646.0 Y 360.0
java.awt.Point[x=2631,y=366]
X 2631.0 Y 366.0
X 2623.0 Y 367.0
java.awt.Point[x=2623,y=367]
X 2620.0 Y 369.0
java.awt.Point[x=2620,y=369]
java.awt.Point[x=2621,y=369]
X 2621.0 Y 369.0
X 2622.0 Y 368.0
java.awt.Point[x=2622,y=368]
Since I've asked if 1) this can be prevented (presumably not) and 2) for workarounds, I feel like I should at least supply the basis for the workaround to this that I'll probably be settling on.
Since a "connect the dots" method is the only way, but I don't want to draw directly to the Canvas, I can't use the GraphicsContext line drawing calls in the other linked answer, which appear to invoke external native code for drawing lines and such. So we'll have to implement our own line drawing algorithm (maybe there's some library out there to do this?)...
This early draft workaround involves drawing an integer color to a generic buffer in memory (this could be a WritableImage in JFX or just a one dimensional int[] buffer etc.) so that the written values can easily be read back later, rather than drawing directly to a Canvas.
/*
* Bresenham's algorithm
*/
public static void drawLine(RasterLayer layer, int x0, int y0, int x1, int y1, int color) {
if (Math.abs(y1 - y0) < Math.abs(x1 - x0)) {
if (x0 > x1) {
plotLineLow(layer, x1, y1, x0, y0, color);
} else {
plotLineLow(layer, x0, y0, x1, y1, color);
}
} else {
if (y0 > y1) {
plotLineHigh(layer, x1, y1, x0, y0, color);
} else {
plotLineHigh(layer, x0, y0, x1, y1, color);
}
}
}
private static void plotLineLow(RasterLayer layer, int x0, int y0, int x1, int y1, int color) {
int dx = x1 - x0;
int dy = y1 - y0;
int yi = 1;
if (dy < 0) {
yi = -1;
dy = -dy;
}
int D = (2 * dy) - dx;
int y = y0;
for (int x = x0; x < x1; x++) {
layer.setPixel(x, y, color);
if (D > 0) {
y = y + yi;
D = D + (2 * (dy - dx));
} else {
D = D + 2 * dy;
}
}
}
private static void plotLineHigh(RasterLayer layer, int x0, int y0, int x1, int y1, int color) {
int dx = x1 - x0;
int dy = y1 - y0;
int xi = 1;
if (dx < 0) {
xi = -1;
dx = -dx;
y1++;
}
int D = (2 * dx) - dy;
int x = x0;
for (int y = y0; y < y1; y++) {
layer.setPixel(x, y, color);
if (D > 0) {
x = x + xi;
D = D + (2 * (dx - dy));
} else {
D = D + 2 * dx;
}
}
}
To make the drawn line thicker I suppose I'll just have to draw additional adjacent lines by offsetting the x or y coordinates for the additional lines, selected depending on the slope of the line. (Draw additional adjacent lines with offset y values for more horizontal lines, and offset x values for more vertical lines.) Testing so far this seems to work reasonably well. Obviously you could also get fancy with anti-aliased lines and such...

(Java / Processing) How to create several instances of an object on different positions on the screen?

I am unable to create several instances of the waveClock object even though I have put it in an array and marked the centre positions for each object. I would like to create 4 objects in one window, all responding to different sound frequencies/beat onsets etc
Could someone shed some light on how to go about this? I believe it may be an issue with the centerX and centerY variables in the waveClock class
ArrayList<waveClock> waveClocks = new ArrayList<waveClock>();
//global variables
float angnoise, radiusnoise;
float xnoise, ynoise;
float angle = -PI/6;
float radius;
float strokeCol = 254;
int strokeChange = -1;
int speed; //changes speed of visualisation once beat is detected?
void setup()
//for every waveClock we need 180 pixels width, then add 20 pixels for first gap
size(740, 650);
background(255);
//code is called
waveClocks.add(new waveClock(100, height/2, minRadius, bassColour, lowBassBand, highBassBand, numberOfLowOnsetsThreshold));
waveClocks.add(new waveClock(280, height/2, minRadius, midColour, lowMidBand, highMidBand, numberOfMidOnsetsThreshold));
waveClocks.add(new waveClock(460, height/2, minRadius, highColour, lowHighBand, highHighBand, numberOfHighOnsetsThreshold));
waveClocks.add(new waveClock(640, height/2, minRadius, veryHighColour, lowVeryHighBand, highVeryHighBand, numberOfVeryHighOnsetsThreshold));
//set the min and max radius of each of the viz circles
/* for (int i = 0; i < waveClocks.size(); i++) {
//go through the arraylist of waveClocks and set the min and max radius of each circle
waveClocks.get(i).setMinMaxRadius(minRadius, maxRadius);
}*/
song.play();
beat = new BeatDetect(song.bufferSize(), song.sampleRate());
bl = new BeatListener(beat, song);
}
void draw() {
//clear the screen by painting it black
//background(0);
for (int i = 0; i < waveClocks.size(); i++) {
//has there been a beat in the range? get(circle ID).low band, high band etc.
if (beat.isRange(waveClocks.get(i).getLowBand(), waveClocks.get(i).getHighBand(), waveClocks.get(i).getOnsetThreshold())) {
waveClocks.get(i).setMaxRadius();
}
//waveClocks.get(i).drawCircle();
waveClocks.get(i).drawWaveClock();
}
}
waveClock class in a separate tab
//class is an architecture blueprint
//objects are the actual buildings built from the methods (can make as many as you like)
//constructor is the builder/constructor literally
class waveClock {
float centerX; //co-ordinates of circle's position
float centerY; //co-ordinates of circle's position
float radius; //avg radius
// float minRadius; //smallest size it can be
// float maxRadius; //biggest size it can be
color col; //colour
int onsetThreshold; //
int lowBand; //looks at lowest band of frequency and makes circle sensitive to it
int highBand; //looks at highest band of frequency and makes circle sensitive to it
boolean onset; //has there been an onset (beat has occurred or not?)
//the constructor
waveClock(float x, float y, float r, color c, int lb, int hb, int t) {
centerX = x;
centerY = y;
radius = r;
col = c;
lowBand = lb;
highBand = hb;
onsetThreshold = t;
}
void drawWaveClock() {
radiusnoise += 0.005;
radius = (noise(radiusnoise)*350) + 1;
angnoise += 0.005;
angle += (noise(angnoise)*6) - 3;
if (angle > 360) {
angle -= 360;
} else if (angle < 0) {
angle += 360;
}
xnoise += 0.01;
ynoise =+ 0.01;
float centerX = width/2 + (noise(xnoise)*100) - 50;
float centerY = height/2 + (noise(ynoise)*100) - 50;
float rad = radians(angle);
float x1 = centerX + (radius*cos(rad));
float y1 = centerY + (radius*sin(rad));
float opprad = rad + PI;
float x2 = centerX + (radius*cos(opprad));
float y2 = centerY + (radius*sin(opprad));
strokeCol += strokeChange;
if (strokeCol > 354) {
strokeChange = -1;
} else if (strokeCol < 0) {
strokeChange = 1;
}
stroke(strokeCol, 60);
strokeWeight(1);
line(x1, y1, x2, y2);
}
}
You aren't ever using the class-level centerX and centerY variables. Instead, you're recalculating a new centerX and centerY in the drawWaveClock() function.
float centerX = width/2 + (noise(xnoise)*100) - 50;
float centerY = height/2 + (noise(ynoise)*100) - 50;
These are all drawn from the center of the screen, so the waves will end up in the same position.
In the future, please try to narrow your problem down to a MCVE that demonstrates the problem. Also please use proper naming conventions- classes start with an upper-case letter, for example. Good luck.

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.

Move Object in direction of mouse

I have a bullet class and an algorithm that will move my bullet to where I pressed, but how would I have the bullet continuing on past the mouse_x and mouse_y when it was clicked?
In My Update method:
float xSpeed = (MoveToX - x) / 9;
float ySpeed = (MoveToY - y) / 9;
this.x += xSpeed;
this.y += ySpeed;
And this is when I first create the bullet:
Bullet(int Mx, int My){
c = Color.red;
MoveToX = Mx;
MoveToY = My;
MoveToX += Board.cam.camX;
MoveToY += Board.cam.camY;
Mx is the mouses x when it was clicked. Same with the y.
Edit:
This is my final product: everything works as it should
Bullet(int Mx, int My){
c = Color.red;
MoveToX = Mx + Board.cam.camX;
MoveToY = My + Board.cam.camY;
int speed = 5;
float distance = (float) Math.sqrt(Math.pow(MoveToX - x, 2) + Math.pow(MoveToY - y, 2));
amountToMoveX = (((MoveToX - x) / distance) * speed);
amountToMoveY = (((MoveToY - y) / distance) * speed);
}
public void update(){
x += amountToMoveX;
y += amountToMoveY;
}
The instance variables of your bullet shouldn't be moveTo_, but velocity and direction.
Calculate the direction (i.e. the angle) in the constructor, from the bullet position and the target position. velocity may also be a static constant, depending on your use case.
If it is an option, I would strongly recommend to use a physics- or game-engine. Those kind of problems were already solved a hundred times in those engines. They relieve you from those basic tasks and help you concentrate on your actual problem.

Custom View drawArc,detect user touch on draw path of arc

I am creating a custom view which is a kind of arc slider progress view.I can draw more or less of the arc based on where the user touches(on the x axis) by calculating the sweep, i do this by first calculating the percetage where the user touched along the x axis..0% would be all the way to the left and 100% would be all the way to the right.
I want to take this a step further, instead off drawing the arc based on the x coordinate that the user presses, I want to make it move only when the user touches on the actual arc draw path, so its more realistic. I am still new to custom views and my maths is limited but if I get some tips I would be grateful thanks
class ArcProgress extends View {
Context cx;
float width;
float height;
float center_x, center_y;
final RectF oval = new RectF();
final RectF touchArea = new RectF();
float sweep = 0;
float left, right;
int percent = 0;
public ArcProgress(Context context) {
super(context);
cx = context;
}
public int getPercentage() {
return percent;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setBackgroundColor(0xfff0ebde);
width = (float) getWidth();
height = (float) getHeight();
float radius;
if (width > height) {
radius = height / 3;
} else {
radius = width / 3;
}
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(0xffd2c8b6);
paint.setStrokeWidth(35);
paint.setStyle(Paint.Style.STROKE);
center_x = width / 2;
center_y = height / 2;
left = center_x - radius;
float top = center_y - radius;
right = center_x + radius;
float bottom = center_y + radius;
oval.set(left, top, right, bottom);
//this is the background arc, it remains constant
canvas.drawArc(oval, 180, 180, false, paint);
paint.setStrokeWidth(10);
paint.setColor(0xffe0524d);
//this is the red arc whichhas its sweep argument manipulated by on touch
canvas.drawArc(oval, 180, sweep, false, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
float xPosition = event.getX();
float yPosition = event.getY();
if (oval.contains(xPosition, yPosition)) {
float x = xPosition - left;
float s = x * 100;
float b = s / oval.width();
percent = Math.round(b);
sweep = (180 / 100.0f) * (float) percent;
invalidate();
} else {
if (xPosition < left) {
percent = 0;
sweep = (180 / 100.0f) * (float) percent;
invalidate();
}
if (xPosition > right) {
percent = 100;
sweep = (180 / 100.0f) * (float) percent;
invalidate();
}
}
}
return true;
}
}
I want to make it move only when the user touches on the actual arc
draw path
At the beginning of onTouchEvent() you need to check whether xPosition and yPosition are fulfilling some condition. If yes, you do the stuff, which you are doing now. If no, return true.
Condition:
We want to check whether x, y are in that grey arc background:
Let's calculate a distance from (x, y) to that point (a, b) in the center:
final dist = distance(x, y, a, b)
distance() is a simple Euclidean distance between points (x,y) and (a,b):
double distance(int x, int y, int a, int b)
{
return Math.sqrt((x - a) * (x - a) + (y - b) * (y - b));
}
x, y are in that grey arc background, if y > Y && dist >= r && dist <= R.
Does this work for you?
You don't need a lot of Maths. You can calculate the distance of the touch point from the center of your arc (it's a circle so it's easy) and the compare that with the radius you are using. That will tell you if the point is on the arc (almost, see below for full case).
Point touchEv = ...;
Point circleCenter = ...;
//the radius of the circle you used to draw the arc
float circleRadius = ...;
//how far from the arc should a touch point treated as it's on the arc
float maxDiff = getResources().getDimension(R.dimen.max_diff_dp);
//calculate the distance of the touch point from the center of your circle
float dist = Math.pow(touchEv.x-circleCenter.x,2) + Math.pow(touchEv.y- circleCenter.y,2)
dist = Math.sqrt(dist);
//We also need the bounding rect of the top half of the circle (the visible arc)
Rect topBoundingRect = new Rect(circleCenter.x - circleRadius,
circleCenter.y - circleRadius,
circleCenter.x + circleRadius,
circleCenter.y);
if (Math.abs(dist - circleRadius) <= maxDiff &&
topBoundingRect.contains(touchEv.x, touchEv.y)) {
// the user is touching the arc
}

Categories