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);
}
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());
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).
I'm working with ARCore in Android Studio using java and am trying to implement ray intersection with an object.
I started with Google's provided sample (as found here: https://developers.google.com/ar/develop/java/getting-started).
Upon touching the screen, a ray gets projected and when this ray touches a Plane, a PlaneAttachment (with an Anchor/a Pose) is created in the intersection point.
I would then like to put a 3D triangle in the world attached to this Pose.
At the moment I create my Triangle based on the Pose's translation, like this:
In HelloArActivity, during onDrawFrame(...)
//Code from sample, determining the hits on planes
MotionEvent tap = mQueuedSingleTaps.poll();
if (tap != null && frame.getTrackingState() == TrackingState.TRACKING) {
for (HitResult hit : frame.hitTest(tap)) {
// Check if any plane was hit, and if it was hit inside the plane polygon.
if (hit instanceof PlaneHitResult && ((PlaneHitResult) hit).isHitInPolygon()) {
mTouches.add(new PlaneAttachment(
((PlaneHitResult) hit).getPlane(),
mSession.addAnchor(hit.getHitPose())));
//creating a triangle in the world
Pose hitPose = hit.getHitPose();
float[] poseCoords = new float[3];
hitPose.getTranslation(poseCoords, 0);
mTriangle = new Triangle(poseCoords);
}
}
}
Note: I am aware that the triangle's coordinates should be updated every time the Pose's coordinates get updated. I left this out as it is not part of my issue.
Triangle class
public class Triangle {
public float[] v0;
public float[] v1;
public float[] v2;
//create triangle around a given coordinate
public Triangle(float[] poseCoords){
float x = poseCoords[0], y = poseCoords[1], z = poseCoords[2];
this.v0 = new float[]{x+0.0001f, y-0.0001f, z};
this.v1 = new float[]{x, y+ 0.0001f, z-0.0001f};
this.v2 = new float[]{x-0.0001f, y, z+ 0.0001f};
}
After this, upon tapping the screen again I create a ray projected from the tapped (x,y) part of the screen, using Ian M his code sample provided in the answer to this question: how to check ray intersection with object in ARCore
Ray Creation, in HelloArActivity
/**
* Returns a world coordinate frame ray for a screen point. The ray is
* defined using a 6-element float array containing the head location
* followed by a normalized direction vector.
*/
float[] screenPointToWorldRay(float xPx, float yPx, Frame frame) {
float[] points = new float[12]; // {clip query, camera query, camera origin}
// Set up the clip-space coordinates of our query point
// +x is right:
points[0] = 2.0f * xPx / mSurfaceView.getMeasuredWidth() - 1.0f;
// +y is up (android UI Y is down):
points[1] = 1.0f - 2.0f * yPx / mSurfaceView.getMeasuredHeight();
points[2] = 1.0f; // +z is forwards (remember clip, not camera)
points[3] = 1.0f; // w (homogenous coordinates)
float[] matrices = new float[32]; // {proj, inverse proj}
// If you'll be calling this several times per frame factor out
// the next two lines to run when Frame.isDisplayRotationChanged().
mSession.getProjectionMatrix(matrices, 0, 1.0f, 100.0f);
Matrix.invertM(matrices, 16, matrices, 0);
// Transform clip-space point to camera-space.
Matrix.multiplyMV(points, 4, matrices, 16, points, 0);
// points[4,5,6] is now a camera-space vector. Transform to world space to get a point
// along the ray.
float[] out = new float[6];
frame.getPose().transformPoint(points, 4, out, 3);
// use points[8,9,10] as a zero vector to get the ray head position in world space.
frame.getPose().transformPoint(points, 8, out, 0);
// normalize the direction vector:
float dx = out[3] - out[0];
float dy = out[4] - out[1];
float dz = out[5] - out[2];
float scale = 1.0f / (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
out[3] = dx * scale;
out[4] = dy * scale;
out[5] = dz * scale;
return out;
}
The result of this however is that, no matter where I tap the screen, it always counts as a hit (regardless of how much distance I add between the points, in Triangle's constructor).
I suspect this has to do with how a Pose is located in the world, and using the Pose's translation coordinates as a reference point for my triangle is not the way to go, so I'm looking for the correct way to do this, but any remarks regarding other parts of my method are welcome!
Also I have tested my method for ray-triangle intersection and I don't think it is the problem, but I'll include it here for completeness:
public Point3f intersectRayTriangle(CustomRay R, Triangle T) {
Point3f I = new Point3f();
Vector3f u, v, n;
Vector3f dir, w0, w;
float r, a, b;
u = new Vector3f(T.V1);
u.sub(new Point3f(T.V0));
v = new Vector3f(T.V2);
v.sub(new Point3f(T.V0));
n = new Vector3f(); // cross product
n.cross(u, v);
if (n.length() == 0) {
return null;
}
dir = new Vector3f(R.direction);
w0 = new Vector3f(R.origin);
w0.sub(new Point3f(T.V0));
a = -(new Vector3f(n).dot(w0));
b = new Vector3f(n).dot(dir);
if ((float)Math.abs(b) < SMALL_NUM) {
return null;
}
r = a / b;
if (r < 0.0) {
return null;
}
I = new Point3f(R.origin);
I.x += r * dir.x;
I.y += r * dir.y;
I.z += r * dir.z;
return I;
}
Thanks in advance!
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 have this method for rotating points in 3D using quaternions, but it seems not to work properly:
public static ArrayList<Float> rotation3D(ArrayList<Float> points, double angle, int xi, int yi, int zi)
{
ArrayList<Float> newPoints = new ArrayList<>();
for (int i=0;i<points.size();i+=3)
{
float x_old = points.get(i);
float y_old = points.get(i+1);
float z_old = points.get(i+2);
double w = Math.cos(angle/2.0);
double x = xi*Math.sin(angle/2.0);
double y = yi*Math.sin(angle/2.0);
double z = zi*Math.sin(angle/2.0);
float x_new = (float) ((1 - 2*y*y -2*z*z)*x_old + (2*x*y + 2*w*z)*y_old + (2*x*z-2*w*y)*z_old);
float y_new = (float) ((2*x*y - 2*w*z)*x_old + (1 - 2*x*x - 2*z*z)*y_old + (2*y*z + 2*w*x)*z_old);
float z_new = (float) ((2*x*z + 2*w*y)*x_old + (2*y*z - 2*w*x)*y_old + (1 - 2*x*x - 2*y*y)*z_old);
newPoints.add(x_new);
newPoints.add(y_new);
newPoints.add(z_new);
}
return newPoints;
}
If i make this call rotation3D(list, Math.toRadians(90), 0, 1, 0); where points is (0,0,10), the output is (-10.0, 0.0, 2.220446E-15), but it should be (-10,0,0), right? Could someone take a look at my code and tell me if is there somethig wrong?
Here are 4 screens that represent the initial position of my object, and 3 rotations with -90 degrees (the object is not properly painted, that's a GL issue, that i will work on later):
I haven't studied the code but what you get from it is correct: Assuming a left-handed coordinate system, when you rotate the point (0,0,10) 90 degrees around the y-axis (i.e. (0,1,0)) you end up with (-10,0,0).
If your coordinate system is right-handed I think you have to reverse the sign of the angle.