I am working on a project in Java. I am trying to move the points p2, p3, p4 just outside the circumference of the circle in the opposite direction to the point p1. Below is the image, which describes the problem, I am trying to solve.
//given two points, calculates the angle
public static double calcAngle(Point2D.Double p1, Point2D.Double p2) {
double deltaX = p2.x - p1.x;
double deltaY = p2.y - p1.y;
return (Math.atan2(deltaY, deltaX) * 180 / Math.PI);
}
//calculates a point on a circle given the angle, center of the circle and the radius
public static Point2D.Double pointOnCircle(Point2D.Double point, double radius , double angle) {
double x = Math.abs(point.x + (radius * Math.cos(angle * Math.PI / 180F)));
double y = Math.abs(point.y + (radius * Math.sin(angle * Math.PI / 180F)));
return new Point2D.Double(x,y);
}
How do I calculate the angle in Java coordinate system and destination co-ordinates for each of the points p2, p3, p4?
I am yet to try the code above and would like to know if my approach is right before proceeding, since it is a part of the bigger project. Thanks in advance!
Your general idea seems workable but overly complicated. There is no need to convert from x/y-vector to angle and then back. SImply scaling vectors will be enough.
Point2D p = p2; // likewise for p3, p4
double factor = radius / p.distance(p1);
p.setLocation(p1.getX() + (p.getX() - p1.getX())*factor,
p1.getY() + (p.getY() - p1.getY())*factor);
This takes the vector (p - p1), i.e. the vector pointing from p1 towards p, scales it by factor and adds it to the position of p1. The factor is chosen such that the new distance is equal to radius.
All of this will fail if p1 and p are the same, since in this case you'll have a division by zero. If this can be a problem for you, you might want to ensure that factor is a finite number, e.g. using Double.isFinite(double).
Related
I'm making a 2D topdown view shooter game with Java Swing. I want to calculate what angle the mouse pointer is compared to the center of the screen so some of my Sprites can look toward the pointer and so that I can create projectiles described by an angle and a speed. Additionally If the pointer is straight above the middle of the screen, I want my angle to be 0°, if straight to its right, 90°, if straight below 180°, and straight left 270°.
I have made a function to calculate this:
public static float calculateMouseToPlayerAngle(float x, float y){
float mouseX = (float) MouseInfo.getPointerInfo().getLocation().getX();
float mouseY = (float)MouseInfo.getPointerInfo().getLocation().getY();
float hypotenuse = (float) Point2D.distance(mouseX, mouseY, x, y);
return (float)(Math.acos(Math.abs(mouseY-y)/hypotenuse)*(180/Math.PI));
}
The idea behind it is that I calculate the length of the hypotenuse then the length of the side opposite of the angle in question. The fraction of the 2 should be a cos of my angle, so taking that result's arc cos then multiplying that by 180/Pi should give me the angle in degrees. This does work for above and to the right, but straight below returns 0 and straight left returns 90. That means that I currently have 2 problems where the domain of my output is only [0,90] instead of [0,360) and that it's mirrored through the y (height) axis. Where did I screw up?
You can do it like this.
For a window size of 500x500, top left being at point 0,0 and bottom right being at 500,500.
The tangent is the change in Y over the change in X of two points. Also known as the slope it is the ratio of the sin to cos of a specific angle. To find that angle, the arctan (Math.atan or Math.atan2) can be used. The second method takes two arguments and is used below.
BiFunction<Point2D, Point2D, Double> angle = (c,
m) -> (Math.toDegrees(Math.atan2(c.getY() - m.getY(),
c.getX() - m.getX())) + 270)%360;
BiFunction<Point2D, Point2D, Double> distance = (c,
m) -> Math.hypot(c.getY() - m.getY(),
c.getX() - m.getX());
int screenWidth = 500;
int screenHeight = 500;
int ctrY = screenHeight/2;
int ctrX = screenWidth/2;
Point2D center = new Point2D.Double(ctrX,ctrY );
Point2D mouse = new Point2D.Double(ctrX, ctrY-100);
double straightAbove = angle.apply(center, mouse);
System.out.println("StraightAbove: " + straightAbove);
mouse = new Point2D.Double(ctrX+100, ctrY);
double straightRight = angle.apply(center, mouse);
System.out.println("StraightRight: " + straightRight);
mouse = new Point2D.Double(ctrX, ctrY+100);
double straightBelow = angle.apply(center, mouse);
System.out.println("StraightBelow: " + straightBelow);
mouse = new Point2D.Double(ctrX-100, ctrY);
double straightLeft = angle.apply(center, mouse);
System.out.println("Straightleft: " + straightLeft);
prints
StraightAbove: 0.0
StraightRight: 90.0
StraightBelow: 180.0
Straightleft: 270.0
I converted the radian output from Math.atan2 to degrees. For your application it may be more convenient to leave them in radians.
Here is a similar Function to find the distance using Math.hypot
BiFunction<Point2D, Point2D, Double> distance = (c,m) ->
Math.hypot(c.getY() - m.getY(),
c.getX() - m.getX());
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.
I'm using bezier curves as paths for my spaceships to travel along when they are coming into dock at a station. I have a simple algorithm to calculate where the ship should be at time t along a cubic bezier curve:
public class BezierMovement{
public BezierMovement(){
// start docking straight away in this test version
initDocking();
}
private Vector3 p0;
private Vector3 p1;
private Vector3 p2;
private Vector3 p3;
private double tInc = 0.001d;
private double t = tInc;
protected void initDocking(){
// get current location
Vector3 location = getCurrentLocation();
// get docking point
Vector3 dockingPoint = getDockingPoint();
// ship's normalised direction vector
Vector3 direction = getDirection();
// docking point's normalised direction vector
Vector3 dockingDirection = getDockingDirection();
// scalars to multiply normalised vectors by
// The higher the number, the "curvier" the curve
float curveFactorShip = 10000.0f;
float curveFactorDock = 2000.0f;
p0 = new Vector3(location.x,location.y,location.z);
p1 = new Vector3(location.x + (direction.x * curveFactorShip),
location.y + (direction.y * curveFactorShip),
location.z + (direction.z * curveFactorShip));
p2 = new Vector3(dockingPoint.x + (dockingDirection.x * curveFactorDock),
dockingPoint.y + (dockingDirection.y * curveFactorDock),
dockingPoint.z + (dockingDirection.z * curveFactorDock));
p3 = new Vector3(dockingPoint.x, dockingPoint.y, dockingPoint.z);
}
public void incrementPosition() {
bezier(p0, p1, p2, p3, t, getCurrentLocation());
// make ship go back and forth along curve for testing
t += tInc;
if(t>=1){
tInc = 0-tInc;
} else if(t<0){
tInc = 0-tInc;
}
}
protected void bezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, double t, Vector3 outputVector){
double a = (1-t)*(1-t)*(1-t);
double b = 3*((1-t)*(1-t))*t;
double c = 3*(1-t)*(t*t);
double d = t*t*t;
outputVector.x = a*p0.x + b*p1.x + c*p2.x + d*p3.x;
outputVector.y = a*p0.y + b*p1.y + c*p2.y + d*p3.y;
outputVector.z = a*p0.z + b*p1.z + c*p2.z + d*p3.z;
}
}
The curve start point is the spaceship location, and end point is the entrance to the docking bay (red dots on diagram). The spaceship has a normalised vector for its direction, and the docking bay has another normalised vector to indicate the direction the ship must be traveling in so as to be aligned straight on to the docking bay when it arrives (the yellow lines on the diagram)
The green line is a possible path of the spaceship, and the purple circle, the spaceship's radius. Finally, the black box is the bounding box for the station.
I have two problems:
The spaceship is supposed to only be able to turn at r radians per second
The spaceship can't fly through the station
I assume that this translates into:
a). Finding the "curve factors" (control point lengths) that will give a path where the ship doesn't have to turn too tightly
b). Finding the spaceship location/direction from which it can't avoid colliding with the station (and creating a path to guide it out of that state, so it can get on with part a))
However, with both of these, I haven't had much luck finding a solution. I already have code to detect intersections between vectors, boxes, points and spheres, but not bezier curves yet. I also have functions to let me find the distance between two points.
Any help would be most appreciated
Thanks,
James
Finding the exact intersections of a Cubic Bezier Curve involves solving a 5th or 6th degree polynomial. More feasible solutions are either using numerical methods, or subdividing the Bezier Curve.
protected void subdivide(
Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3,
Vector3 q0, Vector3 q1, Vector3 q2, Vector3 q3,
Vector3 q4, Vector3 q5, Vector3 q6) {
q0.x = p0.x; q0.y = p0.y; q0.z = p0.z;
q6.x = p3.x; q6.y = p3.y; q6.z = p3.z;
q1.x = (p0.x + p1.x) * 0.5;
q1.y = (p0.y + p1.y) * 0.5;
q1.z = (p0.z + p1.z) * 0.5;
q5.x = (p2.x + p3.x) * 0.5;
q5.y = (p2.y + p3.y) * 0.5;
q5.z = (p2.z + p3.z) * 0.5;
double x3 = (p1.x + p2.x) * 0.5;
double y3 = (p1.y + p2.y) * 0.5;
double z3 = (p1.z + p2.z) * 0.5;
q2.x = (q1.x + x3) * 0.5;
q2.y = (q1.y + y3) * 0.5;
q2.z = (q1.z + z3) * 0.5;
q4.x = (x3 + q1.x) * 0.5;
q4.y = (y3 + q1.y) * 0.5;
q4.z = (z3 + q1.z) * 0.5;
q3.x = (q2.x + q4.x) * 0.5;
q3.y = (q2.y + q4.y) * 0.5;
q3.z = (q2.z + q4.z) * 0.5;
}
q1..q3 becomes the first segment. q3..q6 becomes the second segment.
Subdivide the curve 2-5 times, and use the control-points as a polyline.
The curvature could be calculated at the end-points of each segment:
protected double curvatureAtStart(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) {
double dx1 = p1.x - p0.x;
double dy1 = p1.y - p0.y;
double dz1 = p1.z - p0.z;
double A = dx1 * dx1 + dy1 * dy1 + dz1 * dz1;
double dx2 = p0.x - 2*p1.x + p2.x;
double dy2 = p0.y - 2*p1.y + p2.y;
double dz2 = p0.z - 2*p1.z + p2.z;
double B = dx1 * dx2 + dy1 * dy2 + dz1 * dz2;
double Rx = (dx2 - dx1*B/A)/A*2/3;
double Ry = (dy2 - dy1*B/A)/A*2/3;
double Rz = (dz2 - dz1*B/A)/A*2/3;
return Math.sqrt(Rx * Rx + Ry * Ry + Rz * Rz);
}
To solve Problem 1, subdivide the curve a few times, and calculate the curvature at each segment's endpoint. This will just be an approximation, but you could selectively subdivide segments with high curvature to get a better approximation in that region.
To solve Problem 2, you could subdivide three curves:
One with velocity zero at both endpoints (C0). This would produce a straight line.
One with velocity zero at the first endpoint, and one at the second (C1).
One with velocity one at the first endpoint, and zero at the second (C2).
If you subdivide all curves in the same way, you could quickly evaluate the control-points of the final curve. You blend the corresponding control-points, parametrized by the velocities at the end-points:
C[i] = C0[i] + (C1[i] - C0[i])*v1 + (C2[i] - C0[i])*v2
You could with this find valid parameter-ranges, so that no segment (evaluated as a straight line-segment) intersects the station. (v1 and v2 can go above 1.0).
I need help to calculate the tangets of a circle in 3D space, this is what I have so far
Tangents are represented by the blue lines, and this is the method I got from a friend to calculate them
Vec3D getTangentBetweenTwoPoint( Vec3D p1, Vec3D p2 ) {
Vec3D r = new Vec3D( p1.x - p2.x,
p1.y - p2.y,
p1.z - p2.z );
r.normalize();
return r;
}
void getTangents() {
Vec3D p0, p1;
for ( int i = 1; i < curve_length + 1; i++ ) {
p0 = points[i % curve_length];
p1 = points[(i+1) % curve_length];
tangents[i % curve_length] = getTangentBetweenTwoPoint( p0, p1 );
}
}
Any help will be much appreciated
Basically, you'd find the vector from the point you need the tangent for to the circle's center and take the cross product of that vector as well as the circle's normal (which you get by taking 2 points of the circle plus the center resulting in a plane equation).
If you normalize that cross product you get the normal/tangent vector for that point.
Replace i with i-1 in your code here:
p0 = points[(i-1) % curve_length];
I am assuming your points are equally spaced on the circle, so the line between the previous point and the next point will be parallel to the tangent at the current point.
Hey, I'm trying to write a method that takes a starting Cartesian coordinate(x,y) an angle (in degrees), a length and a number of sides and draws a shape to an applet. So far this is what I have but, I cant figure out what I'm doing wrong. I plan on using line transformations for the actual angle change and that's not written in yet but the logic for drawing a line at an angle should work but isn't as far as I can tell. Could I get a couple of new eyes to look at this and tell me if I'm missing something.
public void paint(Graphics g)
{
g.setColor(Color.BLACK);
Point startPt = new Point(0,0);
//Function in question
drawRegularPolygon(g, startPt, 5,60,50);
}
public static void drawRegularPolygon(Graphics g, Point2D startPoint, int numOfSides, int angle, int length)
{
Point2D current = startPoint;
for(int i=0; i<numOfSides; i++)
{
drawAngularLine(g, current, angle, length);
current = getEndPoint(current ,length,angle);
}
}
public static void drawAngularLine(Graphics g, Point2D startPoint, int angle, int length)
{
g.setColor(Color.BLACK);
Point2D endPoint = getEndPoint(startPoint, length, angle);
((Graphics2D) g).draw(new Line2D.Double(startPoint, endPoint));
}
private static Point2D getEndPoint(Point2D p, int length, int angle)
{
//Starting point you know (x1, x2),
//end point is (x1 + l * cos(ang), y1 + l * sin(ang))
//where l is the length and ang is the angle.
Point2D retVal = p;
double x = Math.cos(Math.toRadians(angle)*length+p.getX());
double y = Math.sin(Math.toRadians(angle)*length+p.getY());
retVal.setLocation(x,y);
return retVal;
}
A couple things. The first is to be careful about what you're taking sin/cosine of. It's not cos(angle*length) but rather length*cos(angle).
The second point is to think about coordinate systems. It might help to do the math assuming the initial point is (0,0), and then translate to the screen coordinates. This helps avoid the confusion of the y-axis seeming to be upside-down (values increase from top to bottom).
So assuming we just want a point that's length,angle away from the origin in a standard right-handed system, we'd get:
x1 = length * cos(angle)
y1 = length * sin(angle)
But since negative-y is up, we actually want
x2 = length * cos(angle)
y2 = -length * sin(angle)
To mentally check this, picture that you're doing this math at the origin (0,0) which is in the upper left, and have an angle of 45°. If y2 were positive, we'd end up seeing an angle that looks to us like -45°.
Now translate the origin to our starting point (x_i, y_i), to get our final values:
x_f = x_i + length * cos(angle)
y_f = y_i + (-length * cos(angle)) = y_i - length * cos(angle)
Alternatively, if it makes more sense to work in a standard right-handed coordinate system, you probably could get away with doing all the math as if (0,0) were in the center, and then applying a translation and a y-axis mirror transformation, but this screen coordinate system isn't too difficult to work within once you get used to flipping the y values around.
You are drawing a line with the same start point and end point - so nothing is drawn.
Java objects are passed by reference, so:
private static Point2D getEndPoint(Point2D p, int length, int angle){
Point2D retVal = p;
retVal.setLocation(x,y);
return retVal;
}
is also changing the starting point p. So it draws a line of length 1 (does it show a dot on the screen?).
Try using:
Point2D retVal = p.clone();