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).
Related
I get the x- and y-orientation from an RFID-Tag and want to animate the movement in a JavaFX application. It's my first java project so I'm sorry if there are stupid mistakes.
I rotate an box-node in the way of this and this thread.
Two pictures of how the green node rotates in front of the RFID-reader image in the background
xAxis=red, yAxis=green, zAxis=blue
.
I call the rotateNode method like that:
// calculate necessary variables:
delta_x = -x_angle + x_angle_old;
delta_y = -y_angle + y_angle_old;
delta_x_radians = Math.toRadians(delta_x);
delta_y_radians = Math.toRadians(delta_y);
pitch_rad = delta_y_radians;
yaw_rad = 0d; // not used at the moment
roll_rad = delta_x_radians;
if (!((roll_rad == 0d) && (pitch_rad == 0d) && (yaw_rad == 0d))) {
rotateNode(model3D, pitch_rad, yaw_rad, roll_rad);
}
My box-node has the position (0,0,-200) at the beginning and the center of the object should stay in that position the hole time. Just the orientation in two directions should change. My rotateNode method looks like this:
public static void rotateNode(Group n, double pitch_rad, double yaw_rad, double roll_rad) {// , TranslateTransition
// tt_z) {
double A11 = Math.cos(roll_rad) * Math.cos(yaw_rad);
double A12 = Math.cos(pitch_rad) * Math.sin(roll_rad)
+ Math.cos(roll_rad) * Math.sin(pitch_rad) * Math.sin(yaw_rad);
double A13 = Math.sin(roll_rad) * Math.sin(pitch_rad)
- Math.cos(roll_rad) * Math.cos(pitch_rad) * Math.sin(yaw_rad);
double A21 = -Math.cos(yaw_rad) * Math.sin(roll_rad);
double A22 = Math.cos(roll_rad) * Math.cos(pitch_rad)
- Math.sin(roll_rad) * Math.sin(pitch_rad) * Math.sin(yaw_rad);
double A23 = Math.cos(roll_rad) * Math.sin(pitch_rad)
+ Math.cos(pitch_rad) * Math.sin(roll_rad) * Math.sin(yaw_rad);
double A31 = Math.sin(yaw_rad);
double A32 = -Math.cos(yaw_rad) * Math.sin(pitch_rad);
double A33 = Math.cos(pitch_rad) * Math.cos(yaw_rad);
double d = Math.acos((A11 + A22 + A33 - 1d) / 2d);
if (d != 0d) {
double den = 2d * Math.sin(d);
if (den != 0d) {
Point3D p = new Point3D((A32 - A23) / den, (A13 - A31) / den, (A21 - A12) / den);
x_pos_node = (n.localToScene(n.getBoundsInLocal()).getMaxX()
+ n.localToScene(n.getBoundsInLocal()).getMinX()) / 2d;
y_pos_node = (n.localToScene(n.getBoundsInLocal()).getMaxY()
+ n.localToScene(n.getBoundsInLocal()).getMinY()) / 2d;
z_pos_node = (n.localToScene(n.getBoundsInLocal()).getMaxZ()
+ n.localToScene(n.getBoundsInLocal()).getMinZ()) / 2d;
r.setPivotX(x_pos_node);
r.setPivotY(y_pos_node);
r.setPivotZ(z_pos_node);
r.setAxis(p);
r.setAngle(Math.toDegrees(d));
n.getTransforms().add(r);
Transform all = n.getLocalToSceneTransform();
n.getTransforms().clear();
n.getTransforms().add(all);
}
}
}
Printing the following variables shows that the node moves in y although I don't want that to happen. Also I see, that slowly with time the pivot point of the rotation isn't in the center of the node anymore and when I turn the RFID-Tag it doesn't spin around the middle of the node it spins in a circle which gets bigger and bigger..
from:
x_pos_node: 0,00
y_pos_node: 0,39
z_pos_node: -200,00
MaxX: 199,00
MinX: -199,00
MaxY: 2,78
MinY: -2,00
MaxZ: -176,12
MinZ: -223,88
Depth: 47,76
Height: 4,78
Width: 398,00
to:
x_pos_node: 0,00
y_pos_node: 15,52
z_pos_node: -200,00
MaxX: 198,51
MinX: -198,51
MaxY: 38,35
MinY: -7,31
MaxZ: -130,85
MinZ: -269,15
Depth: 138,30
Height: 45,67
Width: 397,02
Picture from the side that shows how the green node moves under blue z-Axis / zero line:
.
Where is my mistake? Why does the object slowly moves instead of just rotating?
It is possible to fix the wrong position when I add an Translation:
n.getTransforms().add(new Translate(0, -y_pos_node, 0));
But that's just an hotfix and you can see how the object moves down and up again.. I think there is an error in the calculations or the positioning of the pivot point. It also turns a bit around the green y-Axis although "yaw_rad" is set to 0;
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).
my math isnt too great but im trying to learn though..
What im trying to do is give my games missiles a helix rocket effect, but i dont know how to work the Sin and Cos to make the helix play out in the right direction..
This is a 3D game by the way:
The problem is, depending on which direction the missile faces, the helix looks warped or flat..
Whats the best way to mathematically calculate a helix based on the missiles X,Y,Z/direction?, ive been trying to figure it out for a long time :/
Thanks alot!
double x = radius * Math.cos(theta);
double y = radius * Math.sin(theta);
double z = radius * Math.cos(theta);
location.add(x,y,z);
missile.shootFlame(location,2);
location.subtract(x,y,z);
Basis vectors
you need the overall direction of missile as 3D vector let call it W. From it you need to get 2 another vectors U,V which are all perpendicular to each other. To get them you can exploit cross product. So:
make W unit vector
Just W = W/|W| so:
l=sqrt(Wx*Wx+Wy*Wy+Wz*Wz);
Wx/=l;
Wy/=l;
Wz/=l;
choose U as any direction non parallel to W
so start with U=(1.0,0.0,0.0) and if U==W choose U=(0.0,1.0,0.0). If you got anything to lock to use that as U direction so the coordinate system will not rotate with time (like Up,North,Sun ...)
U should be unit so if not normalize it just like in #1
compute V
It should be perpendicular to U,W so use cross product:
V = W x U
Cross product of unit vectors is also unit vector so no need to normalize.
recompute U
As we choose the U manually we need to make sure it is also perpendicular to V,W so:
U = V x W
Now we have 3 perpendicular basis vectors where U,V lies in plane of the helix screws and W is the overall movement direction.
If you do not know how to compute the cross product see:
Understanding 4x4 homogenous transform matrices look for [edit2].
Now the Helix is easy:
Defines
so we have U,V,W on top of that we need radius r [units], movement speed v [units/s], angular speed o [rad/s] time t>=0.0 [s] and start position P0.
Helix equation
What we need is equation returning actual position for time so:
ang = o*t;
P(t) = P0 + U*r*cos(ang) + V*r*sin(ang) + W*v*t;
rewritten to scalars:
ang = o*t;
x = P0x + Ux*r*cos(ang) + Vx*r*sin(ang) + Wx*v*t;
y = P0y + Uy*r*cos(ang) + Vy*r*sin(ang) + Wy*v*t;
z = P0z + Uz*r*cos(ang) + Vz*r*sin(ang) + Wz*v*t;
[edit1] as you are incompetent to copy paste and or changing my code correctly...
Vector w = loc.getDirection();
double wX = w.getX();
double wY = w.getY();
double wZ = w.getZ();
double l = Math.sqrt((wX * wX) + (wY * wY) + (wZ * wZ));
wX = wX / l;
wY = wY / l;
wZ = wZ / l;
w = new Vector(wX,wY,wZ); // you forget to change W and use it latter ...
Vector u = new Vector(0, 1.0, 0);
if (Math.abs(wX)<1e-3) // if U and W are the same chose different U
if (Math.abs(wZ)<1e-3)
u = new Vector(1.0, 0.0, 0);
Vector v = w.crossProduct(u);
u = v.crossProduct(w);
double radius = 10; // helix radius [units]
double speed = 2.00; // movement speed [unit/s]
double omega = 0.628; // angular speed [rad/s]
//double omega = 36.0; // angular speed [deg/s]
for (double i = 0; i < 100; i += 1.0) // time [s]
{
double angle = omega * i; // actual screw position [rad] or [deg]
double x = u.getX() * radius * Math.cos(angle) + v.getX() * radius * Math.sin(angle) + wX * speed * i;
double y = u.getY() * radius * Math.cos(angle) + v.getY() * radius * Math.sin(angle) + wY * speed * i;
double z = u.getZ() * radius * Math.cos(angle) + v.getZ() * radius * Math.sin(angle) + wZ * speed * i;
loc.add(x,y,z); // What is this? should not you set the x,y,z instead of adding?
//do work
loc.subtract(x,y,z); // what is this?
}
This should provide you with helix points with traveled linear distance
speed*imax = 2.0*100.0 = 200.0 units
And screws:
omega*imax/(2*Pi) ~= 0.628*100.0/6.28 ~= 10 screws // in case of sin,cos want [rad]
omega*imax/360.0 = 36.0*100.0/360 = 10.0 screws // in case of sin,cos want [deg]
Do not forget to rem in/out the correct omega line (I choose [rad] as that is what I am used that my math libs use). Not sure If I translated to your environment correctly there may be bugs like abs has different name or u = new Vector(1.0, 0.0, 0); can be done on intermediate or declaration of variable only etc which I do not know as I do not code in it.
I have draw a quardrativecurve using html5 canvas and its draw properly. I am attaching the output image here
Code for draw this curve
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20);
ctx.quadraticCurveTo(20,100,200,20);
ctx.stroke();
But now I want to split this quardrativecurve into number of straight line means number of points using java. How can I achieve this? Please help me.
You can split quadratic into multiple line by using below code. For splitting quadratic curve into multiple line you just need to following information
1) Start point of quadratic curve
2) Control point of quadratic curve.
3) End point of quadratic curve.
List<Coordinate> coordinates = new ArrayList<Coordinate>();
Coordinate startPoint = QuadraticCurveTo.getStartPoint();
Coordinate controlPoint = QuadraticCurveTo.getControlPoint();
Coordinate endPoint_ = QuadraticCurveTo.getEndPoint();
double s = 0;
double t = 1;
while (s < t) {
s += 0.1;
double controlParameter = (1 - s);
Coordinate Q_0 = new Coordinate(controlParameter * startPoint.getX(), controlParameter * startPoint.getY()).offset(new Coordinate(s * controlPoint.getX(), s * controlPoint.getY()));
Coordinate Q_1 = new Coordinate(controlParameter * controlPoint.getX(), controlParameter * controlPoint.getY()).offset(new Coordinate(s * endPoint_.getX(), s * endPoint_.getY()));
Coordinate R_0 = new Coordinate(controlParameter * Q_0.getX(), controlParameter * Q_0.getY()).offset(new Coordinate(s * Q_1.getX(), s * Q_1.getY()));
coordinates.add(R_0);
}
You have quadratic Bezier curve. It is possible to divide curve to two smaller curves and repeat this process until curve segments are flat enough to be represented by line segments. This process is known as recursive subdivision. For quadratic curves it is rather simple:
initial curve B with control points P0, P1, P2
new curves B' and B'' have control points
P0' = P0
P1' = 1/2 * (P1 - P0)
P2' = 1/2 * P1 + 1/4 * (P0 + P2) //correction here
P0'' = P2' //cut point of subdivision
P1'' = 1/2 * (P2 - P1)
P2'' = P2
One can estimate curve flatness through the area of triangle P0P1P2 divided by the squared norm of P0P2 (euclidean or Manhattan distance).
BTW, Windows GDI has method FlattenPath, which transforms curves to sequence of line segments. Probably, FlatteningPathIterator or other Java method does the same trick
So in Java it looks like there's a built in function to split the Quadratic curve into line segments using a PathIterator. Here's the gist of it:
//Create a quadratic curve
double x1 = lineInfo[1];
double y1 = lineInfo[2];
double cx = lineInfo[3]; //spline control point
double cy = lineInfo[4]; //spline control point
double x2 = lineInfo[5];
double y2 = lineInfo[6];
QuadCurve2D quadCurve = new QuadCurve2D.Double(x1, y1, cx, cy, x2, y2);
//Split the curve into line strings - adjust the tolerance as needed (e.g. 0.5)
ArrayList<double[]> coordinates = new ArrayList<>();
PathIterator it = quadCurve.getPathIterator(null, 0.5);
while (!it.isDone()){
double[] coords = new double[2];
it.currentSegment(coords);
coordinates.add(coords);
it.next();
}
//Draw line segments to verify
for (int i=1; i<coordinates.size(); i++){
double[] prevCoord = coordinates.get(i-1);
double[] currCoord = coordinates.get(i);
int x1 = (int) Math.round(prevCoord[0]);
int y1 = (int) Math.round(prevCoord[1]);
int x2 = (int) Math.round(currCoord[0]);
int y2 = (int) Math.round(currCoord[1]);
g2.drawLine(x1, y1, x2, y2);
}
I've implemented a particle system. I'm drawing their textures on billboards that should be rotated towards the camera.
This works fine except for the case when the angle between particle->camera and the normal comes near to 180 degrees. Then the particle starts rotating around itself many times.
The angle is calculated using cos(angle) = dot(a, b) / (length(a) * length(b), the length are both 1 cause the Vectors are normalized.
The axis is calculated using the cross product of those two vectors.
glDisable(GL_CULL_FACE);
//calculate rotation
Vector3f normal = new Vector3f(0, 0, 1);
Vector3f dir = Vector3f.sub(new Vector3f(GraphicsData.camera.x, GraphicsData.camera.y, GraphicsData.camera.z), new Vector3f(x, y, z), null);
if(dir.length() == 0)
{
glEnable(GL_CULL_FACE);
return;
}
dir = (Vector3f) dir.normalise();
float angle = (float) Math.toDegrees(Math.acos(Vector3f.dot(normal, dir)));
Vector3f rotationAxis = Vector3f.cross(normal, dir, null);
rotationAxis = (Vector3f) rotationAxis.normalise();
System.out.println("Angle: + " + angle + " Axis: " + rotationAxis);
glBindTexture(GL_TEXTURE_2D, ParticleEngine.particleTextures.get(typeId).texture.getTextureID());
glColor4f(1f,1f,1f, time >= lifeTime - decayTime ? ((float)lifeTime - (float)time) / ((float)lifeTime - (float)decayTime) : 1f);
shaderEngine.createModelMatrix(new Vector3f(x, y, z), new Vector3f(angle * rotationAxis.x, angle * rotationAxis.y, angle * rotationAxis.z), new Vector3f(sx, sy, sz));
shaderEngine.loadModelMatrix(shaderEngine.particle);
glCallList(ParticleEngine.particleTextures.get(typeId).displayListId + textureIndex);
glEnable(GL_CULL_FACE);
What am i doing wrong calculating the rotation?
public static void createModelMatrix(Vector3f pos, Vector3f rot, Vector3f scale)
{
GraphicsData.camera.modelMatrix = new Matrix4f();
GraphicsData.camera.modelMatrix.setIdentity();
GraphicsData.camera.modelMatrix.translate(pos);
GraphicsData.camera.modelMatrix.rotate((float) Math.toRadians(rot.x), new Vector3f(1,0,0));
GraphicsData.camera.modelMatrix.rotate((float) Math.toRadians(rot.y), new Vector3f(0,1,0));
GraphicsData.camera.modelMatrix.rotate((float) Math.toRadians(rot.z), new Vector3f(0,0,1));
GraphicsData.camera.modelMatrix.scale(scale);
}
More a long comment or perhaps a partial answer to the problem:
If you are computing the cross product anyway, then use that
norm( a × b ) = sin(angle) * norm(a)*norm(b)
dot(a,b) = cos(angle) * norm(a)*norm(b)
to determine
angle = atan2( norm(a×b), dot(a,b) )