I am trying to write a Java program to support off-lattice diffusion limited aggration simulation. Basic code to simulate a moving particle is in place, until said particle hits a static center particle. At that point I try to make sure the moving particle is just touching (tangeant to) the static one. However, for unknown reasons, it sometimes fails (the first 2 of 8 particles intersects, the 6 others are fine).
Here is the code:
boolean killed, collide;
double xp, yp, dx, dy, theta, xpp, ypp, length;
int xc = 200;
int yc = 200;
int killRadius = 200;
int releaseRadius = 150;
int partRadius = 14;
int partDiam = 2 * partRadius;
drawCircle(xc, yc, killRadius); // kill
drawCircle(xc, yc, releaseRadius); // release
drawCircle(xc, yc, partRadius); // center particle
//while (true) {
killed = false;
collide = false;
theta = Math.random() * Math.PI * 2;
xp = xc + releaseRadius * Math.cos(theta);
yp = yc + releaseRadius * Math.sin(theta);
while (true) {
theta = Math.random() * Math.PI * 2;
length = partDiam;
xpp = xp;
ypp = yp;
xp = xp + length * Math.cos(theta);
yp = yp + length * Math.sin(theta);
//drawCircle((int) xp, (int) yp, partRadius);
// Should it be killed ? (maybe could use a box to fasten
// computations...
// Would switching the test for kill w test for collision
// improve perf ?
dx = xp - xc;
dy = yp - yc;
if ((dx * dx) + (dy * dy) > killRadius * killRadius) {
killed = true;
break;
}
// Does it collide with center? replace by any particle...
dx = xp - xc;
dy = yp - yc;
if ((dx * dx) + (dy * dy) < (partDiam) * (partDiam)) {
collide = true;
break;
}
}
// Probably something is wrong here...
if (collide) {
// no absolute value because particles move at most by diameter
double depthPenetration = partDiam
- Math.sqrt((dx * dx) + (dy * dy));
dx = xpp - xp;
dy = ypp - yp;
// shorten distance travelled by penetration length to ensure
// that
// particle is tangeant
length = Math.sqrt((dx * dx) + (dy * dy)) - depthPenetration;
xp = xpp + length * Math.cos(theta);
yp = ypp + length * Math.sin(theta);
drawCircle((int) xp, (int) yp, partRadius);
}
//}
Of course I checked many references before asking, but cannot find anything wrong with the code... Help would be appreciated.
I made some simple refactorings through your code, just to get a feeling what it does, what is happening.
Let me mention one thing at start: It's the revival of the sphaghetti-monster, isn't it? Do you like global variables? Long, superpotent let's do it all right now and here methoods?
If you introduce variables as late as possible, the reader of your code needn't search upwards, what this variable has been been before - if something is overridden, for example, if there is a ungood reuse.
If your variables don't change: Make them final. That simplifies reasoning about them. final int killRadius = 200; means, that get the type information, together with the value, and hopefully shortly before first usage, and that it will never get changed. Configuration only in source code. Probably not a too complicated candidate. In contrast to double dx - not initialized, because it get's initialized inside the loop,
static void foo () {
final int xc = 200;
final int yc = 200;
final int killRadius = 200;
final int releaseRadius = 150;
final int partRadius = 14;
drawCircle (xc, yc, killRadius); // kill
drawCircle (xc, yc, releaseRadius); // release
drawCircle (xc, yc, partRadius); // center particle
//while (true) {
boolean killed = false;
boolean collide = false;
double theta = Math.random() * Math.PI * 2;
double xp = xc + releaseRadius * Math.cos (theta);
double yp = yc + releaseRadius * Math.sin (theta);
double dx, dy, xpp, ypp;
while (true) {
theta = Math.random () * Math.PI * 2;
final int partDiam = 2 * partRadius;
final double length = partDiam;
xpp = xp;
ypp = yp;
xp += length * Math.cos (theta);
yp += length * Math.sin (theta);
dx = xp - xc;
dy = yp - yc;
if ((dx * dx) + (dy * dy) > killRadius * killRadius) {
killed = true;
break;
}
// Why again assign dx = xp -xc? Did any of these values change meanwhile?
// I don't think so.
// dx = xp - xc;
// dy = yp - yc;
if ((dx * dx) + (dy * dy) < (partDiam) * (partDiam)) {
collide = true;
break;
}
}
if (collide) {
// no absolute value because particles move at most by diameter
double depthPenetration = partDiam - Math.sqrt((dx * dx) + (dy * dy));
dx = xpp - xp;
dy = ypp - yp;
// shorten distance travelled by penetration length to ensure
// that
// particle is tangeant
final double length = Math.sqrt((dx * dx) + (dy * dy)) - depthPenetration;
xp = xpp + length * Math.cos (theta);
yp = ypp + length * Math.sin (theta);
drawCircle ((int) xp, (int) yp, partRadius);
}
If you structure your code like this, you not only see, that some value like xc is 200 and never changed - in the head of the while loop you see that theta is not declared inside the loop, so either it is used later outside the loop, or it is sequentially modified inside the loop. To do an x += 4; you can't initialize x in ever loop passing.
In the end of the big while, you have two similar blocks:
dx = xp - xc;
dy = yp - yc;
if ((dx * dx) + (dy * dy) (OP) a OP b) {
c = true;
break;
}
but xp, xc and dx aren't changed meanwhile - nor the y-equivalents. Is this a mistake, or why do you assign them again?
Then, you can get rid of your endless while this way: Since both conditions terminate the while, put the conditions into the while - test, and call the second block (without repeating the assignment) only if the first wasn't entered - keyword to do so is else:
while (!killed && !collide) {
// ...
dx = xp - xc;
dy = yp - yc;
if ((dx * dx) + (dy * dy) > killRadius * killRadius) {
killed = true;
}
else if ((dx * dx) + (dy * dy) < (partDiam) * (partDiam)) {
collide = true;
}
}
What does it help in finding the error? Not so much. If two circles are placed wrongly, and the rest fine, a screenshot would be fine, and values, which lead to bad circles, and values which are alright.
I don't think I can debug your code, but I wrote some java.awt.Shape based collision routines many years ago (over a decade ago) that may help... check out the ShapeUtils.java from this:
http://www.cs101.org/psets/breakout/solns/breakout-src.jar
One of the very likely problems you may have is that java circles are spline curves with 4 points, and thus if you check if all the points defining the circle are inside another shape, you will still miss the case where the spline curve bulges into the other shape, but none of the points are actually inside the object. My solution (which might not be good enough for your case was to remake shapes until the distance between the points along their respective edges was smaller than the largest acceptable error:
/**
* Convert any AWT shape into a shape with a specified precision.
* The specified precision indicates the maximum tolerable distance
* between knot points. If the shape is already precise enough then
* it is returned unmodified.
*
* #param aShape the shape to be converted if necessary.
* #param precision the maximum tolerable distance between knot points.
* #return A more precise version of the shape, or the same shape if
* the precision is already satisfied.
*/
public static Shape getPreciseShape(Shape aShape, float precision) {
This precision along with the velocity of the simulated object, told me how often in the simulation I had to check for collisions (the max delta time)... fast moving simulated objects can zip right through solid objects in a single time interval if you don't scale the time interval to be less than the time it takes to travel your desired precision.
Basically in this way it became a direct trade off between calculation cost and precision of the result. In my case I just wanted the game to look good, and so I only needed to be so precise as to ensure that objects never overlapped by more than half a pixel or so.
Edit: Let me summarize my suggestion... in re-reading I realize I got lost in the details and never manage to point out the key concept... Java has lots of routines that work quite well for testing various implementations of Shape (including circles). My suggestion is use the java libraries as much as possible, and get results that you can verify are correct. Once you've got working code and methodology for checking that the code works, if you need more speed, start optimizing portions of it, preferably after profiling the running program (that produces correct results) to see what parts are limiting your performance.
Related
I want to check if the ball and a specific line (zijde) collide. I want to do this by making the line function of the line. Than check if the coordinates that come out of the function are equal to the ball's coordinates. This is the code i'm using so far and I don't know what I'm doing wrong. Zijde z is the line that the ball needs to collide with and has the functions getStartPoint (getStartPunt) and getEndPoint (getEindpunt).
public Boolean CheckCollision(Zijde z)
{
/**
* y = ax + b
* a = delta y / delta x
* b = y - ax
*/
double deltay = z.getEindpunt().getY() - z.getStartPunt().getY();
double deltax = z.getEindpunt().getX() - z.getStartPunt().getX();
double a = deltay / deltax;
double b = z.getEindpunt().getY() - a * z.getEindpunt().getX();
double yf = a * this.x + b;
return yf == this.y;
}
Assuming there are no problems with the math, I'd change the last line to
return Math.abs(yf - this.y) < SOME_SMALL_CONSTANT;
since, given the imprecise nature of floating point arithmetics, your method would probably never return true.
I'd play with the value of SOME_SMALL_NUMBER to see what give you decent results.
I'm looking to draw a wave. I have this so far:
private void drawWave(int yPos, int xPos, int colour, int length, int amplitude, int alpha) {
int pixelY, pixelX;
for(int i = 0; i < length; i++) {
pixelX = xPos + i;
pixelY = (int) (yPos - Math.sin(Math.toRadians(i)) * amplitude);
Rasterizer2D.drawAlphaPixel(colour, pixelY, 1, 1, alpha, pixelX);
}
}
This draws a wave but I cannot specify a wavelength to use, wasn't so sure how I could do that. Thanks a lot everyone.
To modify the wave length, you could use this formula
F (x) = a * sin ( (1/b)*x )
Where a is amplitude, b is wavelength.
Looking at your code, you have amplitude in there. You just need a new parameter to specify b.
Add an argument like float wavelength and change
pixelY = (int) (yPos - Math.sin( 2.0 * Math.pi * Math.toRadians(i) / wavelength) * amplitude);
You have
y(i) = y0 - A sin(i)
My equation gives you
y = y0 - A sin (2 pi i / L)
where L is the wavelength.
Now, this still may not do what you want. It depends on what you want xPos and yPos to represent. Do you want xPos to give you a phase shift? If so, then you need to include it in your expression for pixelY.
My object is currently only going in a straight line at the set angle using the following code:
this.time = this.time + deltaTime;
// Vertical : Speed * Sine * Angle
double vy = (this.speed * Math.sin(this.angle)) + this.ax*this.time ;
// Horizontal : Speed * Cosine * Angle
double vx = (this.speed * Math.cos(this.angle)) + this.ay*this.time;
this.x = this.x + vx*this.time;
this.y = this.y + vy*this.time + this.ay*(this.time*this.time);
vx += this.ax * this.time;
vy += this.ay * this.time;
I'm assuming I have made some kind of math error in relation to the calculations as it seems the x value is correct, though the y value is not coming back down.
Here are my initial values in case you are wondering:
this.time = 0.0;
this.deltaTime = .0001;
this.x = 1.0;
this.y = 10;
this.speed = 60.0;
this.ay = -9.8;
this.angle = 45;
this.ax = 0.0;
Is this a stupid mistake I made causing this, or am I missing some key concept here?
GamePanel.java : https://gist.github.com/Fogest/7080df577d07bfe895b6
GameLogic : https://gist.github.com/Fogest/36aba3e1a7fc30984e4e
You are misapplying the fundamental equations of motion here. In the example above you are in effect, cancelling out the effect of gravity by adding it back in.
The entirety of the effect of gravity is already taken into account by updating your velocity. Additionally, it doesn't make sense to be using absolute time in your incremental calculations and pretty much every instance of this.time should be replaced by deltaTime.
Also on a conceptual level you are evaluating a differential equation, and so this should all be in a loop. Additionally, you are way overcomplicating things.
Fixed example:
this.speed = 60.0;
this.deltaTime = 0.0001;
this.endTime = 10.0;
this.x = 1.0;
this.y = 10;
this.ay = -9.8;
this.ax = 0;
this.angle = 45;
this.vx = this.speed*Math.cos(this.angle*(Math.PI/180.0))
this.vy = this.speed*Math.sin(this.angle*(Math.PI/180.0))
double time = 0.0;
while (time < this.endTime){
time += this.deltaTime;
this.x += this.vx*this.deltaTime;
this.y += this.vy*this.deltaTime;
this.vx += this.ax*this.deltaTime;
this.vy += this.ay*this.deltaTime;
}
Over time that will be a projectile in motion. Let me know if you have any questions.
Oh, looks like you implemented the code mostly right, the error is now in the way you're making your 2d Point and returning from your method. I'm not sure what IDE you're using, but it should really be giving you a compile error.
Point2D p = new Point2D.Double(x,y);
return p;
Should be replaced with:
return new Point2d.Double(this.x, this.y)
You must convert your angle from degrees to radians before using the angle in trigonometric methods. From the Math.sin Javadocs:
Parameters:
a - an angle, in radians.
Multiply by Math.PI and then divide by 180 to convert degrees to radians.
In these lines, I think you accidentally swapped ax and ay
// Vertical : Speed * Sine * Angle
double vy = (this.speed * Math.sin(this.angle)) + this.ax*this.time ;
// Horizontal : Speed * Cosine * Angle
double vx = (this.speed * Math.cos(this.angle)) + this.ay*this.time;
Since ax is zero (due to objects not gaining horizontal velocity in projectile motion), the typo makes vy constant.
Here's the code in question:
public void calculate() {
// Center of circle is at (250, 250).
//THIS ALGORITHM IS NOW PROVEN TO BE WORSE THAN I FEARED...
/* What it does:
* Moves object around in a circle.
* Does not move the object towards the center.
* Object always stays on the rim of the circle.
*
* Algorithm I used. (DOES NOT WORK):
* N is normalized vector.
* R = -2*(V dot N)*N + V
*/
vx += Accelero.X * 0.1;
vy += Accelero.Y * 0.1;
double nx = x - 250;
double ny = y - 250;
double nd = Math.hypot(nx, ny);
if (nd == 0)
nd = 1;
nx /= nd;
ny /= nd;
double dotProduct = vx * nx + vy * ny;
vx += (float) (-2 * dotProduct * nx);
vy += (float) (-2 * dotProduct * ny);
x -= vx * 2;
y -= vy * 2;
vx *= 0.99;
vy *= 0.99;
}
And this is what happens.
The black line you see is where the purple object (box) moves. It just so happens to be right on the circle line I drew with Canvas.drawCircle().
I don't understand why reflection didn't work. If an object is to hit a circular wall, shouldn't it reflect the object's direction of velocity, which is what the algorithm was meant to be? Or I used the wrong algorithm?
Any help is appreciated. Thanks in advance.
Can you do bouncing off a straight wall of arbitrary angle? That should be the first step. (You may find that a polar representation of the velocity vector is easier to work with.)
Once you've got that working, it should be fairly straightforward: bouncing off a circle is like bouncing off a tangent of that circle that touches it at the point of contact.
You can calculate this tangent by observing that it's perpendicular to the radius vector in the point of contact (that is the vector that points from where the object is to the centre of the circle)
Is there a vector-based implementation of this, without relying on angles?
Yes, see 2-Dimensional Elastic Collisions without Trigonometry, illustrated in this KineticModel. Your implementation appears to be missing the tangential component. See Ensemble#collideAtoms() for details.
This is what I've got, and I'm going to share my findings to all of you.
public void calculate() {
// Center of circle is at (250, 250). Radius is 40.
//THIS ALGORITHM IS PROVEN TO BE BETTER THAN I FEARED...
/* What it does:
* Moves object around in a circle, if object is
* inside of circle.
* Does not move the object towards the center,
* nor outwards. This is crucial.
* Object always stays on the rim of the circle,
* if the collision detection allows it to.
*
* Algorithm I used. (DOES WORK, NOT EXPECTING THIS THOUGH.):
* N is normalized vector.
* R = -2*(V dot N)*N + V
*/
double nx = x - 250;
double ny = y - 250;
double nd = Math.hypot(nx, ny);
if (nd < 40){
vx += Accelero.X * 0.1;
vy += Accelero.Y * 0.1;
x -= vx;
y -= vy;
vx *= 0.9;
vy *= 0.9;
return;
}
vx += Accelero.X * 0.1;
vy += Accelero.Y * 0.1;
if (nd == 0)
nd = 1;
nx /= nd;
ny /= nd;
double dotProduct = vx * nx + vy * ny;
vx += (float) (-2 * dotProduct * nx);
vy += (float) (-2 * dotProduct * ny);
x -= vx * 2;
y -= vy * 2;
vx *= 0.99;
vy *= 0.99;
}
I embedded a collision detection inside my function, basically making this function not as efficient as possible. Ignore that, for it's not the main focus.
The circle's radius is 40, the (x,y) position is (250,250).
Only when the object is either on the circle, or further away from the center of circle, should we calculate the collision response, which is given by the algorithm R = -2*(V dot N)*N + V, where normal vector N is already normalized.
The algorithm is indeed correct, it's the boolean condition of my collision detection is what causes the object to stay on the rim of the circle and go round-a-bout on it.
I didn't say the other algorithm, which #trashgod had provided is wrong. It's because of some weird issue that somehow causes the object to move unusually. I would guess it's the API I'm using's fault, which it didn't allow doubles, but I may be incorrect. I just couldn't find the source of the problem. Which I'm also happy to not look into it further anymore.
The collision detection boolean condition itself could change everything, if it was slightly altered. If it wasn't for #n.m. pointing that I somehow seemed to forget a minus sign (in this case, a NOT sign), I probably would never realized how trivial it would be.
I am having an issue with my program; currently it rotates around a set point, and can rotate models around it. Of course, this is a problem as I want it to be a first-person perspective, and currently, it rotates around a point in front of the viewer, instead of the perspective of the viewer. Here is the trigonometric calculations:
protected void drawWireframe(Graphics g) {
double theta = Math.PI * -azimuth / 180.0D;
double phi = Math.PI * elevation / 180.0D;
float cosT = (float) Math.cos(theta);
float sinT = (float) Math.sin(theta);
float cosP = (float) Math.cos(phi);
float sinP = (float) Math.sin(phi);
float cosTcosP = cosT * cosP;
float cosTsinP = cosT * sinP;
float sinTcosP = sinT * cosP;
float sinTsinP = sinT * sinP;
float near = 6.0F;
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
for (int i = 0; i < tiles.size(); i++) {
Point[] points = new Point[vertices.length];
for (int j = 0; j < points.length; j++) {
float x0 = -(tiles.get(i).getX() + xmod + vertices[j]
.getX());
float y0 = (tiles.get(i).getY() + ymod + vertices[j].getY());
float z0 = -(tiles.get(i).getZ() + zmod + vertices[j]
.getZ());
float x1 = cosT * x0 + sinT * z0;
float y1 = -sinTsinP * x0 + cosP * y0 + cosTsinP * z0;
float z1 = cosTcosP * z0 - sinTcosP * x0 - sinP * y0;
if (z1 + near > 0) {
x1 = x1 * near / (z1 + near);
y1 = y1 * near / (z1 + near);
points[j] = new Point((int) (Math.max(getWidth(),
getHeight()) / 2 - (Math.max(getWidth(),
getHeight()) / near) * x1), (int) (Math.max(
getWidth(), getHeight()) / 2 - (Math.max(
getWidth(), getHeight()) / near) * y1));
}
}
}
}
How would I go about moving the rotational point without actually modifying the xmod, ymod and zmod (these are used for movements like jumping, walking, running, crouching... etc)
I know how to figure out how to get the new x, y and z positions, I just don't know how to apply them; if I add them to the mods, it creates a weird loop-d-loop. If I add them to the x1, y1, z1's it doesn't cover the z not rotating from the perspective.
To change the rotation point, you effectively need three transforms:
Translate the coordinate system so that the rotation point becomes the origin.
Perform a rotation around the origin
Translate the coordinate system back again.
This can be factored a number of ways, but that's the basic priniciple: translate->rotate->translate.
The way you "move the rotation point" of an object is by translating the object so that the rotation point is at the origin; do the rotation; then translate the object back. All of this is done in memory, between frames - the user never actually sees the object moving to the origin and back.
By the way, all this stuff is significantly easier if you understand vectors and matrix transformations - as you've seen yourself, without them the code can get out of hand.
Using vectors/matrices, all your code above could be reduced to only a few lines.