Java - Rotating points on a circle - java

Have been playing around with some basic 3D. I'm trying to keep the math relatively simple as I'm only in year 9.
Background info:
I have 3 int[] variables called xpoints, ypoints and zpoints. I was thinking about putting them all in one List called nodes but decided it's easier not to.
The first forumla I tried was:
xpoints[i] = (int) (r*Math.cos(Math.toRadians(d)));
ypoints[i] = (int) (r*Math.sin(Math.toRadians(d)));
This worked perfectly for rotating the cube around one axis but not any others. I found a formula that looked promising for multiple axis but can't get it to work. The faces seem to shrink slightly during each loop, ending up in a single point (the centre).
xpoints[i] = (int) (x * cos_t - y * sin_t)
ypoints[i] = (int) (y * cos_t + x * sin_t)
The full function applying the math:
public void rotateZ(double theta){
theta = Math.toRadians(theta);
double cos_t = Math.cos(theta);
double sin_t = Math.sin(theta);
for (int i = 0; i < xpoints.length; i++){
double x = xpoints[i] - x_off;
double y = ypoints[i] - y_off - ypoints_mod[i];
xpoints[i] = (int) (x * cos_t - y * sin_t) + x_off;
ypoints[i] = (int) (y * cos_t + x * sin_t) + y_off + ypoints_mod[i];
System.out.println(x+", "+y);
}
}
If you need anymore info please let me know (wasn't quite sure how to word this)
Thanks,

amazing work for a 9 years old boy!! Great!!
search for euler rotation for rotating a point or vector on all 3-axis.

Well... Don't you just love computer programming :). As shown in the code I was rounding the result to an Integer for convenience. Turns out this breaks it completely, removed that and it works fine.
DON'T ROUND WHEN DOING MATH ON COMPUTERS!!! :P
Never again

Related

Floorcasting not scrolling in raycasting engine?

I'm currently working on a raycaster in Java, and so far, I have the floor correctly textured. The problem, however, is that the floor doesn't scroll. In other words, when I move the camera in the projection, the floor stays the same, yet the walls move as expected. I'm really not sure what I'm doing wrong. I took almost all the code from this reference. Note that I took some liberties when pasting the code in that I used some pseudocode.
I tried applying a player offset to the tileX and tileY variables, e.g., tileX += player.x, and all I got was a floor that scrolls far too quickly and incorrectly.
for every ray:
... // other stuff relating to the walls above here.
int start = (int)(wallY + wallHeight + 1);
double directionCos = cos(rad(ray.getAngle()));
double directionSin = sin(rad(ray.getAngle()));
int textureDim = 16;
for (int y = start; y < screenHeight; y++) {
double distance = screenHeight / (2.f * y - screenHeight);
distance /= cos(rad(player.getAngle()) - rad(ray.getAngle()));
// The source I grabbed the code from actually appends the player's x and y to the tileX and tileY variables, but this completely messes up the textures when I try to.
double tileX = distance * directionCos;
double tileY = distance * directionSin;
int textureX = Math.floorMod((int)(tileX * textureDim), textureDim);
int textureY = Math.floorMod((int)(tileY * textureDim), textureDim);
int rgb = floorTexture.getRGB(textureX, textureY);
projectionFloor.setRGB((int)wallX, y, rgb);
}
Below is an image of the floor.
Below is an animation visualizing the problem.
Below is an animation visualizing what happens if I try to apply a player position offset:
Fixed it on my own. Turns out that, yes, you do have to account for the player's position (shocker!); the source I got the code from just didn't do it correctly.
DTPP = distance to projection plane.
for every pixel y from wallY + wallHeight + 1 to projectionHeight:
double r = y - this.getPreferredSize().height / 2.f;
double d = (CAMERA_HEIGHT * DTPP / r) / ANGLE;
double tileX = CAMERA_X + d * RAY_COSANGLE;
double tileY = CAMERA_Y + d * RAY_SINANGLE;
int textureX = Math.floorMod((int) (tileX * TEXTURE_SIZE /
TEXTURE_SCALE), TEXTURE_SIZE);
int textureY = Math.floorMod((int) (tileY * TEXTURE_SIZE /
TEXTURE_SCALE), TEXTURE_SIZE);
... (drawing occurs here)

How to calculate points on a line at intervals

I am coding a game and want a projectile to go from one location to the next moving at intervals every frame.
I've been playing around with the slope-intercept method of determining things and I'm getting close, but I am stuck.
Here is my code so far:
animationFrame = refresh;
double x, y, xPerF; //Values for drawing
double m, b; //Value for slope and y-intercept
double x1, x2, y1, y2; //Values for the targets
x1 = getCenterX();
x2 = Canvas.target[shotTarget].getCenterX();
y1 = getCenterY();
y2 = Canvas.target[shotTarget].getCenterY();
xPerF = Point2D.distance(x1, y1, x2, y2)/animationSpeed;
//Calculate slope
if(x2>x1) m = (y2-y1)/(x2-x1);
else if(x2<x1) m = (y1-y2)/(x1-x2);
else m = 0;
//Calculate the y-intercept
b = m * x1 - y1;
if(b<0) b = -b + Canvas.myHeight;
else {
b -= Canvas.myHeight;
if(b<0) b = -b;
}
//Calculate the x value
if(x1>x2) x = x1 - (xPerF * animationFrame);
else if(x1<x2) x = x1 + (xPerF * animationFrame);
else x = x1;
//Calculate the y value
if(m!=0) y = (m * x + b) - Canvas.myHeight;
else {
if(y1>y2) y = y1 - (xPerF * animationFrame);
else y = y1 + (xPerF * animationFrame);
}
g.fillOval((int) x - 15, (int) y - 15, 30, 30);
//Debugging
System.out.println("Frame " + animationFrame + " of " + animationSpeed + " | " + y + " = " + m + " * " + x + " + " + b + " | at speed of " + xPerF);
Updated
I expect the animation to end at the target location, but it always either overshoots or is right on target. It mainly overshoots when the target is pretty kind of straight above the tower, give or take a few x co-ordinates. I have worked this out to be a quadrant 1 x-y plane and I believe the problem I have now lies with how I am calculating my slope. Thanks!
Outdated
Here is a mini applet to demonstrate: https://drive.google.com/file/d/1fCTFJzulY1fcBUmdV6AXOd7Ol1g9B3lo/view?usp=sharing
Click on each target to target it
I believe your approach is fundamentally flawed. It is prone to rounding errors which might be a source for overshoots. It is also hard to make work well under real world where your application is not the only one so CPU might be in high demand and some frames might be skipped, and so on. The better approach is to use time rather than frames as the main driver of the events. Your main method drawScene accepts current time as one of its arguments. When any animation starts, you save the time when it started. Then, the job becomes much easier. For example for linear animation it would be something like this:
double animationPart = (currentTime - animationStartTime) / totalAnimationDuration;
// fix rounding error
if (animationPart > 1.0)
animationPart = 1.0;
double xCur = xStart * (1.0 - animationPart) + xEnd * animationPart;
double yCur = yStart * (1.0 - animationPart) + yEnd * animationPart;
P.S. the "time" doesn't have to be real time, it might be some other "game time" if it somehow makes more sense but still this approach is IMHO much easier to implement correctly.
Update
I'd say that the overall code quality is rather bad. Concerns are badly separated, there are a lot of magic numbers, and global static things. For example, having to two bullets in flight will be not easy to implement in this code.
There are also some real bugs in animation code. Some obvious bugs are:
xPerF is calculated wrongly. You divide the Euclidean distance instead of just difference in the X-coordinate.
Logic for the y is flawed. At least you should add m * (x - x1) instead of m * x. But it still won't cover the case of a vertical shoot (i.e. the case when the X-coordinate is not changed at all). If you want to go this way, you should use xPerF and yPerF and get rid of the m and the related if's.
This might or might not fix the animation issues (at list you still will have a potential for rounding errors). I'd still say that changing your shoot to something like
public void shootTarget(int target) {
shotTarget = target;
shotTime = animationFrame;
}
and then using
double animationPart = ((double) (animationFrame - shotTime)) / animationSpeed;
as suggested above is a better way. Note: this is only a stub because in your real code you for some reason regularly assign 0 to refresh and thus to animationFrame so it won't work that easy.
Answer
I figured it out. Instead of calculating the coordinates with slope-intercept method, I simply calculated the intervals I would have to increment y and x per frame and incremented them based on the frame of the animation.
double x, y, xPerF, yPerF; //Values for drawing
double x1, x2, y1, y2; //Values for the targets
x1 = getCenterX();
x2 = Canvas.target[shotTarget].getCenterX();
y1 = getCenterY();
y2 = Canvas.target[shotTarget].getCenterY();
xPerF = (Math.max(x1, x2) - Math.min(x1, x2))/animationSpeed;
yPerF = (Math.max(y1, y2) - Math.min(y1, y2))/animationSpeed;
if(x1>x2) x = x1 - xPerF * animationFrame;
else if(x1<x2) x = x1 + xPerF * animationFrame;
else x = x1;
if(y1>y2) y = y1 - yPerF * animationFrame;
else if(y1<y2) y = y1 + yPerF * animationFrame;
else y = y1;
g.fillOval((int) x - 15, (int) y - 15, 30, 30);

Java: My object won't travel at an angle between 0 and 45 degrees

I started learning java just over a year ago so i'm still fairly new.
I'm trying to make an object travel from one point to another at a constant net velocity no matter where the second point is in the frame. Currently it's working fairly well as long as I run the method every few frames.
The only problem is that it it will only move horizontally unless the second point is approximately between 45 and 135 degrees or between 225 and 315 degrees (1/2π and 3/2π or 5/2π and 7/2π).
It may be because of the 'if' statements meant to stop it from dividing by 0 but it doesn't seem like it. Also if there is any way to simplify those equations or remove 'if' statements I wouldn't mind some advice there too.
Note: vel is the net velocity the objects travel at and Prime.mx and Prime.my is the location of the target point.
public void target()
{
if (Prime.mx > x)
{
if (Math.abs(x-Prime.mx) != 0)
x = Math.round(Math.round((x + (vel*Math.cos(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.mx < x)
{
if (Math.abs(x-Prime.mx) != 0)
x = Math.round(Math.round((x - (vel*Math.cos(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.my > y)
{
if (Math.abs(x-Prime.mx) != 0)
y = Math.round(Math.round((y + (vel*Math.sin(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.my < y)
{
if (Math.abs(x-Prime.mx) != 0)
y = Math.round(Math.round((y - (vel*Math.sin(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
}
I use Math.round twice because the first brings it to a float from a double and the second makes it an int. I need the x and y as ints so the paint method can draw the objects.
I found a few simillar problems on the site but the closest one was in python and and the anwer didn't seem applicable to my problem.
I believe you are overcomplicating this. If your starting point is (sx, sy) and your destination is (dx, dy) then you can easily calculate any point (x, y) that is p distance along the line (0.0 <= p <= 1.0). You can use this to move at velocity v. So I suggest finding your end point and then using simple arithmetic to move on the x and y axis.
float dx = dist * Math.cos(angle);
float dy = dist * Math.sin(angle);
for (float p = 0.0; p <= 1.0; p = Math.min(1.0, p + dist / v) {
x = sx + p * (dx - sx);
y = sy + p * (dy - sy);
}
The Math.min expression in the for loop ensures that you end up exactly at the destination point.
If you already have the destination point then it's just as easy. Instead of finding dx and dy from dist and angle you find dist from dx and dy using pythagoras.
More than solution these are some advices.
First, implement all you coordinate variables as floats to prevent rounding precision loss errors and round only right before painting.
Second, define float dx = Prime.mx - x; float dy = Prime.my - y; distance to target from current point (to use later). I would use Math.atan2(dy,dx) to compute angle between current point and target. Then use that angle to increment coordinates like this:
x += Math.cos(angle)*vel;
y += Math.sin(angle)*vel;
Third, check if your object is at target using (dx*dx + dy*dy <= radius*radius) for suitable radius (can be 1).
Also note that if the y axis goes down, then the angle will be CW (clock-wise) instead of CCW (counter-clock-wise).

Moving an object towards a point android

I have this in the initialization of a bullet object:
x = startX;
y = startY;
double distance = Math.sqrt(((endX - x) ^ 2) + ((endY - y) ^ 2));
speedX = (6 * (endX - x)) / distance;
speedY = (6 * (endY - y)) / distance;
It goes to where I touch on the screen, but the further away I touch, the faster it goes. This works fine on paper, I've tried it with different lengths and it should work, but bullets need to move 6 pixels on the line from the player to the point touched every step. And its update method moves of course. But why do bullets move at different speeds?
If I remember my Java operators...
Replace
double distance = Math.sqrt(((endX - x) ^ 2) + ((endY - y) ^ 2));
with
double distance = Math.sqrt(Math.pow(endX - x, 2) + Math.pow(endY - y, 2));
Assuming that all measurements are in pixels and you want the speed to be 6 pixels per step, then you can calculate the velocity by using a little bit of trig:
double theta = Math.atan2(endY - startY, endX - startX);
velX = 6 * Math.cos(theta);
velY = 6 * Math.sin(theta);
Note that I am using the terms "speed" and "velocity" as a physicist would; speed is a scalar value and velocity is a vector with magnitude and direction.

How to draw a smooth line through a set of points using Bezier curves?

I need to draw a smooth line through a set of vertices. The set of vertices is compiled by a user dragging their finger across a touch screen, the set tends to be fairly large and the distance between the vertices is fairly small. However, if I simply connect each vertex with a straight line, the result is very rough (not-smooth).
I found solutions to this which use spline interpolation (and/or other things I don't understand) to smooth the line by adding a bunch of additional vertices. These work nicely, but because the list of vertices is already fairly large, increasing it by 10x or so has significant performance implications.
It seems like the smoothing should be accomplishable by using Bezier curves without adding additional vertices.
Below is some code based on the solution here:
http://www.antigrain.com/research/bezier_interpolation/
It works well when the distance between the vertices is large, but doesn't work very well when the vertices are close together.
Any suggestions for a better way to draw a smooth curve through a large set of vertices, without adding additional vertices?
Vector<PointF> gesture;
protected void onDraw(Canvas canvas)
{
if(gesture.size() > 4 )
{
Path gesturePath = new Path();
gesturePath.moveTo(gesture.get(0).x, gesture.get(0).y);
gesturePath.lineTo(gesture.get(1).x, gesture.get(1).y);
for (int i = 2; i < gesture.size() - 1; i++)
{
float[] ctrl = getControlPoint(gesture.get(i), gesture.get(i - 1), gesture.get(i), gesture.get(i + 1));
gesturePath.cubicTo(ctrl[0], ctrl[1], ctrl[2], ctrl[3], gesture.get(i).x, gesture.get(i).y);
}
gesturePath.lineTo(gesture.get(gesture.size() - 1).x, gesture.get(gesture.size() - 1).y);
canvas.drawPath(gesturePath, mPaint);
}
}
}
private float[] getControlPoint(PointF p0, PointF p1, PointF p2, PointF p3)
{
float x0 = p0.x;
float x1 = p1.x;
float x2 = p2.x;
float x3 = p3.x;
float y0 = p0.y;
float y1 = p1.y;
float y2 = p2.y;
float y3 = p3.y;
double xc1 = (x0 + x1) / 2.0;
double yc1 = (y0 + y1) / 2.0;
double xc2 = (x1 + x2) / 2.0;
double yc2 = (y1 + y2) / 2.0;
double xc3 = (x2 + x3) / 2.0;
double yc3 = (y2 + y3) / 2.0;
double len1 = Math.sqrt((x1-x0) * (x1-x0) + (y1-y0) * (y1-y0));
double len2 = Math.sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1));
double len3 = Math.sqrt((x3-x2) * (x3-x2) + (y3-y2) * (y3-y2));
double k1 = len1 / (len1 + len2);
double k2 = len2 / (len2 + len3);
double xm1 = xc1 + (xc2 - xc1) * k1;
double ym1 = yc1 + (yc2 - yc1) * k1;
double xm2 = xc2 + (xc3 - xc2) * k2;
double ym2 = yc2 + (yc3 - yc2) * k2;
// Resulting control points. Here smooth_value is mentioned
// above coefficient K whose value should be in range [0...1].
double k = .1;
float ctrl1_x = (float) (xm1 + (xc2 - xm1) * k + x1 - xm1);
float ctrl1_y = (float) (ym1 + (yc2 - ym1) * k + y1 - ym1);
float ctrl2_x = (float) (xm2 + (xc2 - xm2) * k + x2 - xm2);
float ctrl2_y = (float) (ym2 + (yc2 - ym2) * k + y2 - ym2);
return new float[]{ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y};
}
Bezier Curves are not designed to go through the provided points! They are designed to shape a smooth curve influenced by the control points.
Further you don't want to have your smooth curve going through all data points!
Instead of interpolating you should consider filtering your data set:
Filtering
For that case you need a sequence of your data, as array of points, in the order the finger has drawn the gesture:
You should look in wiki for "sliding average".
You should use a small averaging window. (try 5 - 10 points). This works as follows: (look for wiki for a more detailed description)
I use here an average window of 10 points:
start by calculation of the average of points 0 - 9, and output the result as result point 0
then calculate the average of point 1 - 10 and output, result 1
And so on.
to calculate the average between N points:
avgX = (x0+ x1 .... xn) / N
avgY = (y0+ y1 .... yn) / N
Finally you connect the resulting points with lines.
If you still need to interpolate between missing points, you should then use piece - wise cubic splines.
One cubic spline goes through all 3 provided points.
You would need to calculate a series of them.
But first try the sliding average. This is very easy.
Nice question. Your (wrong) result is obvious, but you can try to apply it to a much smaller dataset, maybe by replacing groups of close points with an average point. The appropriate distance in this case to tell if two or more points belong to the same group may be expressed in time, not space, so you'll need to store the whole touch event (x, y and timestamp). I was thinking of this because I need a way to let users draw geometric primitives (rectangles, lines and simple curves) by touch
What is this for? Why do you need to be so accurate? I would assume you only need something around 4 vertices stored for every inch the user drags his finger. With that in mind:
Try using one vertex out of every X to actually draw between, with the middle vertex used for specifying the weighted point of the curve.
int interval = 10; //how many points to skip
gesture.moveTo(gesture.get(0).x, gesture.get(0).y);
for(int i =0; i +interval/2 < gesture.size(); i+=interval)
{
Gesture ngp = gesture.get(i+interval/2);
gesturePath.quadTo(ngp.x,ngp.y, gp.x,gp.y);
}
You'll need to adjust this to actually work but the idea is there.

Categories