How to zoom into mouse position with correct translation - java

I'm trying to zoom a grid in Processing and I am having trouble with applying the correct translation such that zooming is centered around the mouse position. I have searched the web for a while but nothing I try seems to work.
The screen size is width and height and the mouse position is mouseX and mouseY.
The code I have at the moment is below, but it zooms the grid (controlled by player.zoom) from the top left corner which is not what I want. To update the translation of the grid, player has the 2d vector player.translate.
void mouseWheel(MouseEvent event) {
float zoomFactor = 200.0f;
float scroll = event.getCount();
player.zoom -= scroll * player.zoom / zoomFactor;
// player.translate.x += something;
// player.translate.y += something;
}
If you need more details to answer I can link the repo with the source code.

I have created a very simple mock-up for you which will hopefully point you in the right direction into applying this to your player.
So this little demo shows the zooming in to the centre of an ellipse whilst keeping it as the central focus.
float scale = 1;
// displacement left/right
float xPan = 720;
// displacement up/down
float yPan = 450;
boolean zoomIn = true;
void setup() {
size(1440, 900);
}
void draw() {
// allows us to zoom into the center of the screen rather than the corner
translate(width/2, height/2);
scale(scale);
translate(-xPan, -yPan);
background(200);
// draws the ellipse in the center
ellipse(width/2, height/2, 100, 100);
// does the zooming
if (zoomIn) {
scale *= 1.01;
}
}
I suggest you to copy this into a new project and then comment out specific lines to try to understand what's going on and how you can transfer this over to your own project.
The same principles still apply, I didn't do this with mouse input in the effort of making the program as simple as possible.

Related

How do I adjust Processing/Minim waveform scale?

I'm a total beginner so forgive me if this is probably silly or improper of me to ask.
I'm trying to make my own virtual oscillograph in processing. I don't really know how to explain it, but I want to "zoom out" from where I am getting the peaks in waveforms, which is the window size. I'm not sure what I'm doing wrong here or what's wrong with my code. I've tried changing the buffer size, and changing the multiplier for x/y. My sketch is adapted from a minim example Sketch.
All Help is greatly appreciated.
import ddf.minim.*;
Minim minim;
AudioInput in;
int frames;
int refresh = 7;
float fade = 32;
void setup()
{
size(800, 800, P3D);
minim = new Minim(this);
ellipseMode(RADIUS);
// use the getLineIn method of the Minim object to get an AudioInput
in = minim.getLineIn(Minim.STEREO);
println (in.bufferSize());
//in.enableMonitoring();
frameRate(1000);
background(0);
}
void draw()
{
frames++; //same saying frames = frames+1
if (frames%refresh == 0){
fill (0, 32, 0, fade);
rect (0, 0, width, height);
}
float x;
float y;
stroke (0, 0);
fill (0,255,0);
// draw the waveforms so we can see what we are monitoring
for(int i = 0; i < in.bufferSize() - 1; i++)
{
x = width/2 + in.left.get(i) * height/2;
y = height/2- in.right.get(i) * height/2;
ellipse(x, y, .5, .5);
}
}
Thanks
Edit: you don't need push and pop matrix here. Guess my understanding of it is lacking too. You can just use translate.
You can use matrices to create a camera object, there is tons of material out there that you can read up on to understand the math behind this and implement it anywhere.
However, there might be an easier solution here. You can use pushMatrix and popMatrix in combination with translate. Push and popping the matrix will manipulate the matrix stack - you create a new "frame" where you can play around with translations, then pop back the original frame (so you don't get lost by applying new translations on each frame).
push the matrix, translate the z coordinate once before drawing everything you want zoomed out, pop the matrix. You can set up a variable for the translation so that you can control this with your mouse.
Here's a crude example (I don't have all those libraries so couldn't add it to your code):
float scroll = 0;
float scroll_multiplier = 10;
void setup()
{
size(800, 800, P3D);
frameRate(1000);
background(0);
}
void draw()
{
background(0);
//draw HUD - things that don't zoom.
fill(255,0,0);
rect(400,300,100,100);
//We don't want to mess up our coordinate system, we push a new "frame" on the matrix stack
pushMatrix();
//We can now safely translate the Y axis, creating a zoom effect. In reality, whatever we pass to translate gets added to the coordinates of draw calls.
translate(0,0,scroll);
//Draw zoomed elements
fill(0,255,0);
rect(400,400,100,100);
//Pop the matrix - if we don't push and pop around our translation, the translation will be applied every frame, making our drawables dissapear in the distance.
popMatrix();
}
void mouseWheel(MouseEvent event) {
scroll += scroll_multiplier * event.getCount();
}

Zoom in on point in canvas, where everything is based off of a certain point

Alright, bit of a strange one. I'll do my best to explain.
I'm building a game on Android (Java). And I'm just working out the views first. The first thing I'm working on is a solar system view (top down, 2d, no tilt). I've got all the planets and the star displaying properly, and in proportion. I can pan, and zoom. The only issue is, the way I've set up all the circles to draw, they're all based on one point (where the star is), and when I 'zoom'/'pan' I'm not actually zooming/panning. For the pan, I'm just moving the point where everything revolves, and for the zoom, I'm not scaling the whole canvas (tried that, didn't work out) I'm adjusting the size of the elements I'm drawing, in accordance with the zoom.
So I've got a 'star' that starts out in the middle of the screen, the point is called orbitLocation.
And a zoom variable that starts at 1f.
The star is drawn very simply - canvas.drawCircle((float)orbitLocation.x, (float)orbitLocation.y, this.radius * zoom, this.paint);
Each of the planets are positioned like so:
canvas.drawCircle(getPlanetX(angle, orbitLocation, zoom), getPlanetY(angle, orbitLocation, zoom), this.radius * zoom, this.paint);
getPlanetX and getPlanetY look like this:
private float getPlanetX(double angle, Point orbitingPoint, float zoom) {
return orbitingPoint.x + (this.distance * Constants.AU * zoom) * (float)Math.sin(angle);
}
private float getPlanetY(double angle, Point orbitingPoint, float zoom) {
return orbitingPoint.y + (this.distance * Constants.AU * zoom) * (float)Math.cos(angle);
}
Constants.AU is a constant int 11507 in place for Astronomical Unit - for proper scaling.
Now the problem I'm having with zoom, is if I pan, and then zoom, the whole thing scales from the orbitLocation, disregarding where I'm trying to zoom in on, so whatever was right in the middle of my screen, quickly gets thrown way off screen.
Desired outcome. Have whatever is in the center of the screen (or between fingers in pinch, either way), stay in the center of the screen, and the orbit point adjust itself in accordance.
Here's the relevant part of my touch event:
if (touchCount == 2) { // if pinch
if (startZoomTouchPoint != null) {
float newDistance = getDistance(event, 0, 1);
float distanceDifference = (startZoomDistance - newDistance);
zoom -= distanceDifference * (zoom / 100);
if (zoom < MIN_ZOOM) {
zoom = MIN_ZOOM;
}
// Point zoomCenter = getZoomCenter(event);
// int yOrbitOffset = orbitLocation.y - zoomCenter.y;
// int xOrbitOffset = orbitLocation.x - zoomCenter.x;
// orbitLocation.x += xOrbitOffset + Constants.AU * zoom;
// orbitLocation.y += yOrbitOffset + Constants.AU * zoom;
startZoomDistance = newDistance;
updateStartZoomTouchPoint(event);
}
}
Those commented lines in the middle are my latest attempt to achieve my goal.
Every time I try, I either move the orbitLocation way too much, or not enough, or it moves around weirdly.
getZoomCenter literally just gets the point between the 2 fingers. We can just use the center of the screen if that's easier.
Alright, that's all I've got. Let me know if you need any more info.
Please look at the following code snippet
private ScaleGestureDetector mScaleDetector;
private float zoom = 1.f;
public MyCustomView(Context mContext){
...
// View code goes here
...
mScaleDetector = new ScaleGestureDetector(context, new
ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
return true;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(zoom, zoom);
...
// onDraw() code goes here
...
canvas.restore();
}
private class ScaleListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
zoom = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
Basically, you need to scale the canvas based on the scale value obtained from the touch event. You can do that by
Save the canvas
Scale the canvas
Draw what you were drawing
Restore the canvas
and you can set the zoom on the response of scale gesture detector. Call invalidate afterwards.
For more information visit https://developer.android.com/training/gestures/scale

Android: How to properly scale mouse/touch input?

I have an android game where the canvas is scaled to look the same on all devices using this code:
canvas.scale((float)(getWidth()/(double)WIDTH), (float)(getHeight()/(double)HEIGHT))
where WIDTH and HEIGHT are 1920 and 1080 respectively. My problem is that for all my touch collisions (i.e. a user touching a shape) is handled using Paths and Regions:
public static boolean collided(Path a, Path b) {
Region clip = new Region(0,0,3000, 3000);
Region rA = new Region();
rA.setPath(a, clip);
Region rB = new Region();
rB.setPath(b, clip);
return !rA.quickReject(rB) && rA.op(rB, Region.Op.INTERSECT);
}
Now I also have another scale method (that barely has any effect from what I can tell, but on occasion does have an influence) to scale coordinates and dimensions:
public static double scale(double x) {
return dpToPx(x)/Resources.getSystem().getDisplayMetrics().density;
}
My problem is that with all of this scaling I can't seem to get the mouse to be in the correct position. Here is the code I use to create the mouse Path that handles collision between the mouse and other shapes:
mouse.set(x, y);
mousePath.reset();
mousePath.addRect(mouse.x - 10, mouse.y - 10, mouse.x + 10, mouse.y + 10, Path.Direction.CW);
I draw mousePath to see where the mousePath is and this is the result I get (it is the box in green and where the mouse actually is in the general area of the blue circle)
This is the much more severe point, as it seems the closer I get to (0,0) the closer mousePath gets to being at where the mouse actually is.
So how do I get the mouse to be in the correct location?
After more searching it turns out this question is a duplicate of: https://stackoverflow.com/a/24895485/4188097.
The browser always returns the mouse position in untransformed coordinates. Your drawings have been done in transformed space. If you want to know where your mouse is in transformed space, you can convert untransformed mouse coordinates to transformed coordinates like this:
var mouseXTransformed = (mouseX-panX) / scaleFactor;
var mouseYTransformed = (mouseY-panY) / scaleFactor;

Trouble drawing with MouseDragged in Java

there's a problem drawing or Setting size of a JPanel when i drag the mouse,
the location i setted where i click and the size depending of the drag position(X and Y) drawing a resizable rectangle(JPanel).
private void panelMouseDragged(java.awt.event.MouseEvent evt) {
rSX = (int)MouseInfo.getPointerInfo().getLocation().getX();
rSY = (int)MouseInfo.getPointerInfo().getLocation().getY();
rectanguloDefault.setBounds(rX,rY,rSX-rX,rSY-rY);
}
private void panelMousePressed(java.awt.event.MouseEvent evt) {
rX = (int)MouseInfo.getPointerInfo().getLocation().getX();
rY = (int)MouseInfo.getPointerInfo().getLocation().getY();
rectanguloDefault.setLocation(rX,rY);
}
but when i drag mouse in a negative coordinate of the click(Start of drawing) it disappear.
here a better explain
http://i.picasion.com/resize80/49c88c55d4c11c53c020acfcc4fc2f45.png
but when i drag mouse in a negative coordinate
rectanguloDefault.setBounds(rX,rY,rSX-rX,rSY-rY);
Your width/height calculation always assumes you drag the mouse in a positive direction.
You need to use the absolute value of the two points:
int width = Math.abs(rSX - rX);
int height = Math.abs(rSY - rY);
rectanguloDefault.setBounds(rX, rY, width, height);
Your x/y values will also need to be the minimum of (rX and rSX) and (ry and rSY). You can use the Math.min(...) method for this.

Java Slick2d - How to translate mouse coordinates to world coordinates

I am translating in my main class' render. How do I get the mouse position based on the translation?
public void render(GameContainer gc, Graphics g)
throws SlickException
{
float centerX = 800/2;
float centerY = 600/2;
g.translate(centerX, centerY);
g.translate(-player.playerX, -player.playerY);
gen.render(g);
player.render(g);
}
playerX = 800 /2 - sprite.getWidth();
playerY = 600 /2 - sprite.getHeight();
I update the player position on keydown by .2f * delta
Picture to help with explanation
i92.photobucket.com/albums/l22/occ31191/Untitled-4.png
World coordinates = camera position + mouse position
Camera position is calculated/explained in my answer to this question: Slick2D and JBox2D. How to draw
You're making a tile-based game, where each tile seems to have the same size. For this case, you don't need a generalized unprojection.
Imagine the complete map. The viewport shows only a portion of it; somewhere you need to store the (x,y) offets of the viewport into the complete map. Since the mouse coordinates are relative to the viewport, you need to add this offset to the mouse coordinates. Now, you can easily get the tile coordinates by using modulo operations on the shifted mouse coordinates with the tile's width and height.
Effectively, this is a coordinate transformation of window coordinates to tile coordinates.

Categories