i ran into a strange problem when trying to flip the y-axis of a coordinate system that im creating:
private AffineTransform getTransform() {
if (transform == null) {
transform = new AffineTransform();
double scaleX = (double) this.getWidth() / (coordinateSystem.getMaxX() - coordinateSystem.getMinY());
double scaleY = (double) this.getHeight() / (coordinateSystem.getMaxY() - coordinateSystem.getMinY());
transform.setToScale(scaleX, scaleY);
double deltaX = (coordinateSystem.getMaxX() - coordinateSystem.getMinX()) / 2;
double deltaY = (coordinateSystem.getMaxY() - coordinateSystem.getMinY()) / 2;
transform.translate(deltaX, deltaY);
}
return transform;
}
The AffineTransform is set to scaling and translation. and everything works fine except that my y-values are inverted (max value is a the bottom of the coordinate system, min value is at the top). I tried switching this by inverting the scale factor for the y axis. but this was not working.
Do i have to let the Transform rotate by PI, to achieve the flipped y axis?
Shouldn't multiplying the scale factor for the y axis by minus 1 be the same?
You have a typo on
double scaleX = (double) this.getWidth() / (coordinateSystem.getMaxX() - coordinateSystem.getMinY());
(Last Y should be an X.) Perhaps that's it.
Rotating by PI is actually NOT a right solution at all, since it will flip the X axis as well as the Y.
Related
The issue I have is that I'm attempting to add drag to an object in this basic physics simulation (Java [Processing]), but once I add the appropriate formula, it causes the objects velocity to increase drastically in the opposite direction. Of course the problem is that drag for some reason is being calculated too high but I'm not sure why thats happening as I'm using the real world equation.
void setup(){size(1280,720);}
class Circle{
float x,y,r,m,dx,dy,ax,ay,fx,fy;
Circle(float xPos, float yPos, float Radius, float Mass){
x = xPos;
y = yPos;
r = Radius;
m = Mass;
}
void ADD_DRAG(){
fx -= 0.5 * 1.225 * dx * dx * 0.5 * r * PI;
fy -= 0.5 * 1.225 * dy * dy * 0.5 * r * PI;
}
void update(){
ADD_DRAG();
ax = fx / m;
ay = fy / m;
dx += ax / frameRate;
dy += ay / frameRate;
x += dx / frameRate;
y += dy / frameRate;
}
}
Circle[] SceneObjects = {new Circle(50,50,20,20000),new Circle(50,50,2,20)};
void draw(){
background(51);
for (Circle c : SceneObjects){
c.update();
circle(c.x * 3,c.y * 3,c.r * 3);
}
}
void mouseClicked(){
if(SceneObjects[1].fx != 2000)
SceneObjects[1].fx = 2000;
else
SceneObjects[1].fx = 0;
}
This is the code, essentially there is a Circle class which stores the objects properties and then the forces applies are updated each draw loop. The mouseClicked void is just for testing by adding a force to the objects. All and any help is appreciated, thanks!
Maths I am Using:
Rearranged F=ma for ax = fx / m;
Acceleration * time = Speed for dx += ax / frameRate; (frameRate is 1/time)
Distance = Speed * time = for x += dx / frameRate; (same as above)
For drag im using this equation https://www.grc.nasa.gov/WWW/K-12/rocket/drageq.html with the constants eg air density etc added as seen.
There are a couple of things wrong here.
You haven't given us numbers (or a minimal complete example), but the vector algebra is off.
Yes, the acceleration is f = -kv2, and |v|2 = vx2 + vy2, but that doesn't mean that you can decompose f into fx=kvx2 and fy=kvy2. Not only is your magnitude off, but your acceleration is now not (in general) aligned with the motion; the path of your projectile will tend to curve toward a diagonal between the axes (e.g. x=y).
Also, your code always gives acceleration in the negative x and negative y directions. If your projectile happens to start out going that way, your version of air resistance will speed it up.
Finally, your time interval may simply be too large.
There is a better way. The differential equation is v' = -k v|v|, and the exact solution is v = (1/kt) z, (with appropriate choice of the starting time) where z is the unit direction vector. (I don't know how to put a caret over a letter.) This leads to v(t) = (1/t)v(t=1.0)
So you can either work out a fictional time t0 and calculate each new velocity using 1/(kt), or you can calculate the new velocity from the previous velocity: vn+1 =vn/(kd vn + 1), where d is the time interval. (And then of course you have to decompose v into vx and vy properly.)
If you're not familiar with vector algebra, this may seem confusing, but you can't get an air-resistance sim to work without learning the basics.
The following is a Processing sketch.
Dead centre coordinates are 0,0.
This means the far-left x coord is -250 and the far right x coord is 250. Similar dimension for y.
I want to move the mouse around the centre of the screen and have the relative coordinate appear on the radius, (i.e. mouse at coordinates 45,0 should mark point at 90,0).
The below code works but only for the right side of the screen, in short, angles up to 180. What is missing for this to work for the right hand side?
void draw(){
background(0);
fill(255);
stroke(255);
strokeWeight(5);
translate(width/2,height/2);
// mark center
point(0,0);
strokeWeight(12);
// mark mouse position (follow with point)
float x = mouseX - width/2;
float y = mouseY - height/2;
point(x,y);
// trace point on radius of circle same radius as width
float radius = width/2;
float sinAng = y/radius;
float cosAng = x/radius;
float m = sinAng/cosAng;
float angle = atan(m);
float boundaryX = cos(angle)*width/2;
float boundaryY = sin(angle)*height/2;
stroke(255,0,0);
point(boundaryX,boundaryY);
}
You're loosing the quadrant when calculating m...
-x/-y = x/y
Just correct the angle to the right quadrant by using the sign of x and y.
You can do this in fewer steps by using the atan2() function.
The atan2() function takes two parameters: the y distance between two points, and the x distance between two points, and it returns the angle between those points:
angle = atan2(y1-y0, x1-x0);
You can get rid of a few lines in your program by just doing this:
float x = mouseX - width/2;
float y = mouseY - height/2;
float angle = atan2(y, x);
float boundaryX = cos(angle)*width/2;
float boundaryY = sin(angle)*height/2;
No need to calculate the sinAng, cosAng, or m variables.
I have a Camera class that basically follows the player around the map and keeps him centered on the screen. The math im applying works great until the scale(Zooming in and OUt) of the camera is altered. Here it is:
x = -cell.x - cell.mass/2 + Game.width/2 / sX;
// Where x is the Camera's X, Cell is the Player and sX is the scale factor
I've been playing around with different equations but they all fail once the scale is altered. I can't seem to wrap my head around this and I could really use some insight on how to factor it in.
Here are some bits of the Camera Class:
public void set(Graphics bbg){
Graphics2D g2 = (Graphics2D)bbg;
g2.translate(x, y);
g2.scale(sX, sY);
}
public void unset(Graphics bbg){
Graphics2D g2 = (Graphics2D)bbg;
g2.translate(-x, -y);
}
public void scale(double sx, double sy){
sX = sx;
sY = sy;
}
public void Update(Cell cell){
scale(0.9,0.9);
x = -cell.x - cell.mass/2 + Game.width/2 / sX;
y = -cell.y - cell.mass/2 + Game.height/2 / sY;
}
public double toWorldX(int x){
return x - this.x / sX;
}
public double toWorldY(int y){
return y - this.y / sY;
}
The first image displays the result when the scale factor is 1(Normal Zoom). The second image displays the result when the scale factor is 0.9(Zoomed Out).
I'm having a little difficulty in determining what some of your variables mean (such as cell.mass, I'm assuming it is the size) and I assume that Game.width is the actual width of the window. It would help to know what EXACTLY happen when the zoom is changed (like is the "center" of the zoom at a particular corner of the screen).
Now for an answer, without know what happens to the actual zoom... have you tried the addition of parenthesis like this...
x = ((cell.x + cell.mass/2) - Game.width/2) / sX;
or (because you use '-' a lot, I'm not sure how your coordinates work)
x = ((-cell.x - cell.mass/2) + Game.width/2) / sX;
Just an idea.
The working equation for making the camera follow a player while factoring a scale factor is:
x =((cell.x + cell.mass * 0.5) - Game.width/sX * 0.5);
i have a custom view of width = 500dpi and height = 600dpi , and i have drawn a point at (x = 230 ,y = 300) both are taken from MotionEvent.getX() , MotionEvent.getY() ,within onTouchEvent() of custom view.This coordinate is actually a point drawn on canvas of custom view.
Now how can i make this coordinates generic for different screen.So that i can store (x = 230 ,y = 300) these points and use them again in future on different screens ?
You can make it by storing coordinates as ratio.
First, you get width and height of the View:
int viewWidth = view.getWidth(); // ex. 500dpi in your explanation
int viewHeight = view.getHeight(); // ex. 600dpi in your explanation
Second, you convert x-y coordinates to ratio from the upper-left corner of screen:
double pointX = (double) MotionEvent.getX() / viewWidth;
double pointY = (double) MotionEvent.getY() / viewHeight;
Then, store these pointX and pointY.
The following code rotates the line towards the mouse position using a simple ease function, but the problem is that the atan2() methods works form -PI to PI, making the line to rotate backwards when the angle reach either of the limits, I can make it to rotate from 0 to TWO_PI but there is no different as the line will rotate backwards until it reach the targetAngle, if I dont use the easing calculation works fine because there jump from -PI to PI is unnoticeable, so how can I ease my rotation and avoid this problem?
float angle = 0;
float targetAngle = 0;
float easing = 0.1;
void setup() {
size(320, 240);
}
void draw() {
background(200);
noFill();
stroke( 0 );
// get the angle from the center to the mouse position
angle = atan2( mouseY - height/2, mouseX - width/2 );
// check and adjust angle to go from 0 to TWO_PI
if ( angle < 0 ) angle = TWO_PI + angle;
// ease rotation
targetAngle += (angle - targetAngle) * easing;
pushMatrix();
translate( width/2, height/2 );
rotate( targetAngle );
line( 0, 0, 60, 0 );
popMatrix();
}
Thanks
Not sure if I understood the problem correctly... Do you mean that if the mouse position is slightly less than +PI, and targetAngle is slightly greater than -PI, then the line rotates away from the mouse? The thing is that even if both values are in the same range (-PI, PI), they still can be quite far away from each other. You must adjust angle to fit the PI-neighbourhood of the current targetAngle value.
// get the angle from the center to the mouse position
angle = atan2( mouseY - height/2, mouseX - width/2 );
// check and adjust angle to be closer to targetAngle
if ( angle < targetAngle - PI ) angle = angle + TWO_PI;
if ( angle > targetAngle + PI ) angle = angle - TWO_PI;
This will work if targetAngle is in the range (-TWO_PI, TWO_PI). It seems that it will work for you. If targetAngle can have any value very far away from the working range, then you can use something like this:
// get the angle from the center to the mouse position
angle = atan2( mouseY - height/2, mouseX - width/2 );
// calculate the shortest rotation direction
float dir = (angle - targetAngle) / TWO_PI;
dir = dir - Math.round(dir);
dir = dir * TWO_PI;
// ease rotation
targetAngle += dir * easing;