Zooming to Mouse position in LWJGL - java

I am trying to implement a zoom to an isometric map using LWJGL. Currently I have the functions
public static void setCameraPosition(float x, float y) {
x *= zoom;
y *= zoom;
cameraX = x;
cameraY = y;
x -= (Display.getWidth() / 2) * zoom;
y -= (Display.getHeight() / 2) * zoom;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLU.gluLookAt(x, y, 1f, x, y, 0, 0.0f, 1.0f, 0.0f);
}
which sets the camera center to a point (x, y),
public static Point getMouseCoordinates() {
float x = Mouse.getX() * getZoom() + getCameraLeft();
float y = (Display.getHeight() - Mouse.getY()) * getZoom() + getCameraTop();
return new Point((int) x, (int) y);
}
which returns the current mouse coordinates, and
public static void setZoom(int newZoom) {
if (newZoom >= 4) newZoom = 4;
else if (newZoom <= 1) newZoom = 1;
if (zoom == newZoom) return;
float x = ?; <-----
float y = ?; <-----
zoom = newZoom;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 0+Display.getWidth() * zoom , 0+Display.getHeight() * zoom, 0, 1, -1);
setCameraPosition((int) x, (int) y);
}
which is supposed to set the zoom to an integer value between 1 and 4. As you can see, I would like to set the camera position after changing the zoom to a certain point - and that point needs to be calculated so that the current mouse position does not change (aka zooming in to the mouse position, which is for example what Google Maps does). I have been trying for a good 2 days now, I've tried so many things, but I just couldn't figure out the equation to calculate x and y.
Please note that all points returned and entered are relative to the position of the map, specifically to the top piece of the map (whose top corner point is (0, 0)). The values getCameraLeft() and getCameraTop() in the getMouseCoordinates() function return
public static float getCameraLeft() {
return cameraX - zoom * (Display.getWidth() / 2);
}
and
public static float getCameraTop() {
return cameraY - zoom * (Display.getHeight() / 2);
}
.
Any help would be appreciated. I'm hoping, I did not express myself too complicated.

I finally found the correct equation:
float x = getMouseCoordinates().getX() + (getCameraX() - getMouseCoordinates().getX()) * (float) newZoom / (float) zoom;
float y = getMouseCoordinates().getY() + (getCameraY() - getMouseCoordinates().getY()) * (float) newZoom / (float) zoom;
Thank you anyways, I'm sure eventually someone would have given me the correct answer :)

Related

Rotate player towards mouse

I want my player rotate towards mouse. Here's the code calculating the angle:
float angle = (float) Math.atan2(MouseInput.getMousePos().y - transform.position.y + transform.size.y / 2,
MouseInput.getMousePos().x - transform.position.x + transform.size.x / 2);
angle = (float) (angle * (180 / Math.PI));
if (angle < 0) {
angle = 360 + angle;
}
transform.rotation = 180 + angle;
And getMousePos() method (it just returns mouse pos relative to window):
public static Vector2 getMousePos() {
Point p = MouseInfo.getPointerInfo().getLocation();
return new Vector2(p.x - Game.w.getAccessToWindow(Acces.WINDOW_JFRAME_ACCES).getLocation().x,
p.y - Game.w.getAccessToWindow(Acces.WINDOW_JFRAME_ACCES).getLocation().y);
}
Can you tell me what's wrong with this code? Player isn't rotating properly.
I tried following this article: https://gamefromscratch.com/gamedev-math-recipes-rotating-to-face-a-point/
Update:
I found this post: Java 2d rotation in direction mouse point
Now I've updated my code to this:
int centerX = (int) (transform.size.x / 2);
int centerY = (int) (transform.size.x / 2);
int mouseX = (int) MouseInput.getMousePos().x;
int mouseY = (int) MouseInput.getMousePos().y;
double angle = Math.atan2(centerY - mouseY, centerX - mouseX) - Math.PI / 2;
transform.rotation = angle;
But still something is off. Try this code for yourself. Maybe I did something wrong somewhere else.

How do I do anti-aliasing in java? (WITHOUT built in methods)

I am going to try my best to give context for the below code. This is a method used to draw a circle and its center point in a 50x50 white square background. The following variables were used:
xc,yx - the center coordinates used to compute the circle
r - the radius of the circle
STEP - how often a new point is drawn on the circumference of the circle
x,y - the coordinates of each point that will make up the circle
Right now, my method uses a for loop to compute each points R,G, and B coordinates along the circumference of the circle based on the center point and the radius. What I am trying to do is anti-alias my output circle so that the round parts are not as jagged. However, I want to do this using only math and variables and I do not want to use any of Java's build in methods. Thank you to anyone who can help or point me in the right direction.
Below is my routine:
protected void proc_21() {
info = "Draw anti-aliased circle";
int xc = (int) rand(1, imgW - 2);
int yc = (int) rand(1, imgH - 2);
int r = (int) rand(4, 0.35f * (imgW + imgH));
int STEP = (2 * (int) Math.PI * r) * 57;
System.out.printf("circle centered at (%d,%d), radius = %d, draw in %d steps. \n", xc,yc,r,STEP);
for (int i = 0; i < STEP; i++) {
int x = (int) Math.round(xc + r * Math.cos(i));
int y = (int) Math.round(yc + r * Math.sin(i));
if (0 <= x && x < imgW) {
if ( 0 <= y && y < imgH) {
imgNew.setR(x, y, 0);
imgNew.setG(x, y, 0);
imgNew.setB(x, y, 1);
}
}
}
// set center to red
imgNew.setR(xc, yc, 1);
imgNew.setG(xc, yc, 0);
imgNew.setB(xc, yc, 0);
}

Processing, making an analog clock, the numbers

so, as an assaignment i want to make an analog clock, and i am pretty far. I only need the numbers around the clock, but i cant figure out how to make these. I have made some dots right now, but i want to replace theese with the numbers 1-12.. does any1 know an easy and fast way of doing this? my code is as following:
int cx, cy;
float secondsRadius;
float minutesRadius;
float hoursRadius;
float clockDiameter;
void setup() {
size(1366,768);
stroke(255);
float radius = min(width/1.2, height/1.2) / 2;
secondsRadius = radius * 0.72;
minutesRadius = radius * 0.60;
hoursRadius = radius * 0.50;
clockDiameter = radius * 1.8;
cx = width / 2;
cy = height / 2;
}
void draw() {
background(random(0,255),random(0,255),random(0,255));
// Draw the clock background
fill(0);
noStroke();
ellipse(cx, cy, clockDiameter, clockDiameter);
// Angles for sin() and cos() start at 3 o'clock;
// subtract HALF_PI to make them start at the top
float s = map(second(), 0, 60, 0, TWO_PI) - HALF_PI;
float m = map(minute() + norm(second(), 0, 60), 0, 60, 0, TWO_PI) - HALF_PI;
float h = map(hour() + norm(minute(), 0, 60), 0, 24, 0, TWO_PI * 2) - HALF_PI;
// Draw the hands of the clock
stroke(255);
strokeWeight(1);
line(cx, cy, cx + cos(s) * secondsRadius, cy + sin(s) * secondsRadius);
strokeWeight(2);
line(cx, cy, cx + cos(m) * minutesRadius, cy + sin(m) * minutesRadius);
strokeWeight(4);
line(cx, cy, cx + cos(h) * hoursRadius, cy + sin(h) * hoursRadius);
// Draw the dots arround the clock
strokeWeight(2);
beginShape(POINTS);
for (int a = 0; a < 360; a+=30) {
float angle = radians(a);
float x = cx + cos(angle) * secondsRadius;
float y = cy + sin(angle) * secondsRadius;
vertex(x, y);
}
endShape();
textSize(40);
text("Dank Clock", 570,40);
}
You already have a loop that goes around the clock and places dots at the clock positions. Now all you need is some logic that draws the hour at those positions.
Processing has a text() function that allows you to draw text (or numbers) to the screen. You can just call that instead of vertex() to draw the hours.
To get the hours to draw, just use an int variable that you increment each time through the loop. Something like this:
int hour = 3;
for (int a = 0; a < 360; a+=30) {
float angle = radians(a);
float x = cx + cos(angle) * secondsRadius;
float y = cy + sin(angle) * secondsRadius;
vertex(x, y);
fill(255);
text(hour, x, y);
hour++;
if(hour > 12){
hour = 1;
}
}
Notice that I'm starting at 3 because your angle starts at 0, which points all the way to the right. When the loop goes over the 12, I just start hour back over at 1.
You could probably also figure out a simple formula that maps from a to an hour, that way you don't have to do the incrementing yourself.

How to move waves(made with sine) from top of the picture to the bottom?

I'm trying to draw waves onto the sea, but I got no idea how to moves those from top to bottom. No matter what I change, they stay at the top of the picture.
Here's the code I'm using to draw the waves:
Dimension d = getSize();
int x, y, winWidth = d.width, winHeight = d.height;
int halfHeight = 10;
int lastX = 0, lastY = halfHeight;
double trajectory = 2 * Math.PI;
double factor = trajectory / 100;
for (x = 1; x <= winWidth; x++) {
double sine = Math.sin (x * factor) * halfHeight;
y = halfHeight - (int)sine;
graafika.drawLine (x, y, lastX, lastY);
lastX = x; lastY = y;
}
Here's a picture, how it looks at the moment.
Thanks for the help!
Use height?
graafika.drawLine (x, y+winHeight-20, lastX, lastY+winHeight-20);
You can just "mirror" the placement of the wave by replacing
y = halfHeight - (int)sine;
with
y = winHeight - (halfHeight - (int)sine);
This first places your sine wave at the very bottom, then moving it up a half sine wave, allowing the whole wave to be visible.

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