Collision Response method - java

Hi I try to implement a simple Sphere collision detection algorithm as explained here: http://wp.freya.no/3d-math-and-physics/simple-sphere-sphere-collision-detection-and-collision-response/
So I implement the method to check if Collision occurs:
private boolean advancedSphereSphere(Sphere a, Sphere b) {
Vector2D s = a.getPos().sub(b.getPos());
Vector2D v = a.getVelocity().sub(b.getVelocity());
float r = a.getRadius() + b.getRadius();
double c1 = s.dot(s) - r*r;
if(c1 < 0.0) {
timeToCollision = .0f;
return true;
}
double a1 = v.dot(v);
if(a1 < 0.00001f) {
return false;
}
double b1 = v.dot(s);
if(b1 >= 0.0) {
return false;
}
double d1 = b1*b1 - a1*c1;
if(d1 < 0.0) {
return false;
}
timeToCollision = (float) (-b1 - Math.sqrt(d1) / a1);
return true;
}
Then I have a onDraw method that loops to draw all necessary elements, like this:
protected void onDraw(Canvas canvas) {
flowPhysics(false);
for(Sphere s : mSpheres) {
s.draw(canvas);
}
invalidate();
}
and the problem occurs in flowPhysics(boolean) method, at this line:
for(int i=0; i < mSpheres.size(); ++i) {
for(int j=i+1; j < mSpheres.size(); ++j) {
Sphere a = mSpheres.get(i);
Sphere b = mSpheres.get(j);
if(advancedSphereSphere(a, b) || step) {
if(timeToCollision < dt && !step) {
flowPhysics(true);
}
if(step) {
sphereCollisionResponse(a, b);
}
}
}
}
When the advancedSphereSphere() is called, the first Sphere disappears, I checked and noticed that the problem is in that method at the first line:
Vector2D s = a.getPos().sub(b.getPos());
if I put something else here and don't subtract the b from a vectors, it draws the balls (but no collision occurs). The Java code above is mostly ported code from here: http://wp.freya.no/websvn/filedetails.php?repname=Public&path=%2Fopengl%2Fcollisiondetect%2Fcollisiondetect.cpp
Can u give me an idea on what is the problem?
Thanks
UPDATE
private void sphereCollisionResponse(Sphere a, Sphere b)
{
double m1, m2, x1, x2;
Vector2D v1, v2, v1x, v2x, v1y, v2y;
Vector2D x = new Vector2D(a.getPos().sub(b.getPos()));
x.normalize();
v1 = new Vector2D(a.getVelocity());
x1 = x.dot(v1);
v1x = new Vector2D(x.multiply(x1));
v1y = new Vector2D(v1.sub(v1x));
m1 = a.getMass();
x = new Vector2D(x.multiply(-1));
v2 = new Vector2D(b.getVelocity());
x2 = x.dot(v2);
v2x = new Vector2D(x.multiply(x2));
v2y = new Vector2D(v2.sub(v2x));
m2 = b.getMass();
Vector2D nn = new Vector2D(v1x.multiply(m1-m2));
Vector2D mm = new Vector2D(nn.divide(m1+m2));
Vector2D tt = new Vector2D(v2x.multiply(2*m2));
Vector2D rr = new Vector2D(tt.divide(m1+m2));
Vector2D gg = new Vector2D(mm.add(rr));
Vector2D ss = new Vector2D(gg.add(v1y));
Vector2D nva = ss;
a.setVelocity(nva);
Vector2D nvb = new Vector2D(v1x.multiply(2*m1).divide(m1+m2).add(v2x.multiply(m2-m2).divide(m1+m2).add(v2y)));
b.setVelocity(nvb);
}

If the problem is really in this line
Vector2D s = a.getPos().sub(b.getPos());
then I suspect the problem may lie in your Vector2D class. You should check that the sub method just returns a new Vector2D and does not modify the coordinates of the original vector a. Keeping your objects immutable as far as possible makes them safer to use.
Failing that, the answer is unit testing. Learning to use JUnit or TestNG will pay off in the long run, but even a few ad-hoc print statements might help you for now. You should be able to check the mathematics is correct independent of how the spheres appear on your screen, and doing this consistently will probably guide you towards a clearer design.
Just by testing that the position of each sphere is unchanged after the collision, and that the velocities are as expected in a few simple cases, you are quite likely to discover the bug in your code. For example, if both velocities are zero beforehand then they should be zero afterwards. If the collision is elastic and one sphere (A) collides head-on with another stationary sphere (B), sphere A should be stationary afterwards and sphere B should have the velocity that A started with.
I haven't checked the maths in your code at http://pastie.org/2499691 but when I set up a few simple classes to fill the dependencies and give it some reasonable starting conditions, it doesn't conserve momentum, so I can believe for some values the balls could end up flying off the screen at the speed of light.
Once you have the physics right, you can work on any remaining problems with the display of the spheres secure in the knowledge that the problem must indeed lie there.

Related

Perlin Noise repeating pattern

My problem that my perlin noise is repeating itself very obviously in very small spaces. Here is an image of what it going on. I know that this does happen after a certain point with all perlin noise, but it seems to be happening almost immediately with mine. I believe that it is caused by my really awful pseudorandom gradient generator, but Im not sure. My code is below.
As a side note, my perlin noise seems to generate very small values, between -.2 and positive .2 and I think this is also caused by my pseudorandom gradient generator.
If anyone has any advice on improving this part of my code, please feel free to tell me. Any ideas would be helpful right now.
Thanks to everyone in advance!
public class Perlin {
int[] p = new int[255];
public Perlin() {
for(int i = 0; i < p.length; i++)
p[i] = i;
shuffle(p);
}
int grads[][] = {
{1,0},{0,1},{-1,0},{0,-1},
{1,1},{-1,1},{1,-1},{-1,-1}
};
public double perlin (double x, double y) {
int unitX = (int)Math.floor(x) & 255; // decide unit square
int unitY = (int)Math.floor(y) & 255; // decide unit square
double relX = x-Math.floor(x); // relative x position
double relY = y-Math.floor(y); // relative y position
// bad pseudorandom gradient -- what i think is causing the problems
int units = unitX+unitY;
int[] gradTL = grads[p[(units)]%(grads.length)];
int[] gradTR = grads[p[(units+1)]%(grads.length)];
int[] gradBL = grads[p[(units+1)]%(grads.length)];
int[] gradBR = grads[p[(units+2)]%(grads.length)];
// distance from edges to point, relative x and y inside the unit square
double[] vecTL = {relX,relY};
double[] vecTR = {relX-1,relY};
double[] vecBL = {relX,relY-1};
double[] vecBR = {relX-1,relY-1};
// dot product
double tl = dot(gradTL,vecTL);
double tr = dot(gradTR,vecTR);
double bl = dot(gradBL,vecBL);
double br = dot(gradBR,vecBR);
// perlins fade curve
double u = fade(relX);
double v = fade(relY);
// lerping the faded values
double x1 = lerp(tl,tr,u);
double y1 = lerp(bl,br,u);
// ditto
return lerp(x1,y1,v);
}
public double dot(int[] grad, double[] dist) {
return (grad[0]*dist[0]) + (grad[1]*dist[1]);
}
public double lerp(double start, double end, double rate){
return start+rate*(end-start);
}
public double fade(double t) {
return t*t*t*(t*(t*6-15)+10);
}
public void shuffle(int[] p) {
Random r = new Random();
for(int i = 0; i < p.length; i++) {
int n = r.nextInt(p.length - i);
// do swap thing
int place = p[i];
p[i] = p[i+n];
p[i+n] = place;
}
}
}
A side note on my gradient generator, I know Ken Perlin used 255 because he was using bits, I just randomly picked it. I dont think it has any effect on the patterns if it is changed.
Your intuition is correct. You calculate:
int units = unitX+unitY;
and then use that as the base of all your gradient table lookups. This guarantees that you get the same values along lines with slope -1, which is exactly what we see assuming (0, 0) is the upper-left corner.
I would suggest using a real hash function to combine your coordinates: xxHash, Murmur3, or even things like CRC32 (which isn't meant to be a hash) would be much better than what you're doing. You could also implement Perlin's original hash function, although it has known issues with anisotropy.

plotting points along a straight line from a random start position

I am looking for some help with some game code i have inherited from a flight sim. The code below simulates bombs exploding on the ground, it works fine but i am trying to refine it.
At the moment it takes a random value for x and y as a start point and then adds another random value between -20 and 20 to this. It works ok, but doesn't simulate bombs dropping very well as the pattern does not lay along a straight line/
What i would like to achieve though is all x and y points after the first random values, to lay along a straight line, so that the effects called for all appear to lay in a line. It doesn't matter which way the line is orientated.
Thanks for any help
slipper
public static class BombUnit extends CandCGeneric
{
public boolean danger()
{
Point3d point3d = new Point3d();
pos.getAbs(point3d);
Vector3d vector3d = new Vector3d();
Random random = new Random();
Aircraft aircraft = War.GetNearestEnemyAircraft(this, 10000F, 9);
if(counter > 10)
{
counter = 0;
startpoint.set(point3d.x + (double)(random.nextInt(1000) - 500), point3d.y + (double)(random.nextInt(1000) - 500), point3d.z);
}
if(aircraft != null && (aircraft instanceof TypeBomber) && aircraft.getArmy() != myArmy)
{
World.MaxVisualDistance = 50000F;
counter++;
String s = "weapon.bomb_std";
startpoint.x += random.nextInt(40) - 20;
startpoint.y += random.nextInt(40) - 20;
Explosions.generate(this, startpoint, 7F, 0, 30F, !Mission.isNet());
startpoint.z = World.land().HQ(startpoint.x, startpoint.y);
MsgExplosion.send(this, s, startpoint, getOwner(), 0.0F, 7F, 0, 30F);
Engine.land();
int i = Landscape.getPixelMapT(Engine.land().WORLD2PIXX(startpoint.x), Engine.land().WORLD2PIXY(startpoint.y));
if(firecounter < 100 && i >= 16 && i < 20)
{
Eff3DActor.New(null, null, new Loc(startpoint.x, startpoint.y, startpoint.z + 5D, 0.0F, 90F, 0.0F), 1.0F, "Effects/Smokes/CityFire3.eff", 300F);
firecounter++;
}
super.setTimer(15);
}
return true;
}
private static Point3d startpoint = new Point3d();
private int counter;
private int firecounter;
public BombUnit()
{
counter = 11;
firecounter = 0;
Timer1 = Timer2 = 0.05F;
}
}
The code in the question is a mess, but ignoring this and trying to focus on the relevant parts: You can generate a random position for the first point, and a random direction, and then walk along this direction in several steps.
(This still raises the question of whether the direction is really not important. Wouldn't it matter if only the first bomb was dropped in the "valid" area, and the remaining ones outside of the screen?)
However, the relevant code could roughly look like this:
class Bombs
{
private final Random random = new Random(0);
int getScreenSizeX() { ... }
int getScreenSizeY() { ... }
// Method to drop a single bomb at the given position
void dropBombAt(double x, double y) { ... }
void dropBombs(int numberOfBombs, double distanceBetweenBombs)
{
// Create a random position in the screen
double currentX = random.nextDouble() * getScreenSizeX();
double currentY = random.nextDouble() * getScreenSizeY();
// Create a random step size
double directionX = random.nextDouble();
double directionY = random.nextDouble();
double invLength = 1.0 / Math.hypot(directionX, directionY);
double stepX = directionX * invLength * distanceBetweenBombs;
double stepY = directionY * invLength * distanceBetweenBombs;
// Drop the bombs
for (int i=0; i<numberOfBombs; i++)
{
dropBombAt(currentX, currentY);
currentX += stepX;
currentY += stepY;
}
}
}
I am assuming your startpoint is a StartPoint class with x,y,z coordinates as integers in it.
I hope I have understood your problem correctly. It looks like you either want to create a vertical explosion or a horizontal explosion. Since an explosion always occurs on ground, the z coordinate will be zero. Now you can vary one of x or y coordinate to give you a random explosion along a straight line. Whether you choose x or y could be fixed or could be randomized itself. A potential randomized solution below:
public boolean danger() {
// stuff
int orientation = Random.nextInt(2);
if(aircraft != null && (aircraft instanceof TypeBomber) && aircraft.getArmy() != myArmy)
{
// stuff
startPoint = randomizeStartPoint(orientation, startPoint);
// stuff
}
}
StartPoint randomizeStartPoint(int orientation, StartPoint startPoint) {
if(orientation == 0) {
startPoint.x += random.nextInt(40) - 20;
}
else {
startPoint.y += random.nextInt(40) - 20;
}
return startPoint;
}
In response to the image you uploaded, it seems that the orientation of the explosion need not necessarily be horizontal or vertical. So the code I posted above gives a limited solution to your problem.
Since you want any random straight line, your problem boils down to two sub parts:
1. Generate a random straight line equation.
2. Generate random point along this line.
Now, a straight line equation in coordinate geometry is y = mx + c where m is the slope and c is the constant where the line crosses the y-axis. The problem with c is that it gives rise to irrational coordinates. I am assuming you are looking for integer coordinates only, since this will ensure that your points are accurately plotted. (You could do with rational fractions, but then a fraction like 1/3 will still result in loss of accuracy). The best way to get rid of this irrational problem is to get rid of c. So now your straight line always looks like y = mx. So for step one, you have to generate a random m.
Then for step 2, you can either generate a random x or random y. It doesn't matter which one, since either one will result in random coordinates.
Here is a possible code for the solution:
int generateRandomSlope() {
return Random.nextInt(100); // arbitrarily chose 100.
}
int randomizeStartPoint(int m, StartPoint startPoint) { // takes the slope we generated earlier. without the slope, your points will never be on a straight line!
startPoint.x += random.nextInt(40) - 20;
startPoint.y += x * m; // because a line equation is y = mx
return startPoint;
}
public boolean danger() {
// stuff
int m = generateRandomSlope(); // you may want to generate this elsewhere so that it doesn't change each time danger() is called.
if(aircraft != null && (aircraft instanceof TypeBomber) && aircraft.getArmy() != myArmy)
{
// stuff
startPoint = randomizeStartPoint(m, startPoint);
// stuff
}
}
Again, this is not a complete or the best solution.

Point inside triangle 2d

So I to create a method that checks if the point " ponto " is inside the triangle given by the points A,B,C as vertices.
So far I have this :
final double PRECISION = 0.1;
public boolean dentroDoTriangulo (Point A, Point B, Point C, Point ponto){
//total
double ABC = Math.abs ((A.x*(B.y-C.y)+B.x*(C.y-A.y)+ C.x*(A.y-B.y))/2);
//parcial
double ABP = Math.abs((A.x*(B.y-ponto.y)+B.x*(ponto.y-A.y)+ponto.x*(A.y-B.y))/2);
double APC = Math.abs (A.x*(ponto.y-C.y)+ponto.x*(C.y-A.y)+C.x*(A.y-ponto.y))/2;
double PBC = Math.abs (ponto.x*(B.y-C.y)+B.x*(C.y-ponto.y)+C.x*(ponto.y-B.y))/2;
double parciais = ABP + APC + PBC ;
return Math.abs(ABC - parciais) <= PRECISION;
}
I've already tried doing it like :
ABP +APC +PBC == ABC
Nothing either, can someone help me understand what am I doing wrong here ?
EDIT: Your code seems to work OK. Here's a Point class example in case that's where you are crashing.
public class Point {
float x;
float y;
Point() {
x = 0;
y = 0;
}
Point(float x, float y) {
this.x = x;
this.y = y;
}
}
Just in case, I propose another solution:
Check out this algorithm for determining if the point is inside a triangle.
I cannot see your Point class so I do not know if there is a bug there. I assume it is two floats for x&y coordinates. We'll assume it's ok.
Can you explain your algorithm a bit better? I looks like you are taking three areas of the sub-triangles and adding them up to compare against the original triangle (with PRECISION tolerance). Try the "SameSide" function three times as mentioned in hyperlink if you want--which takes two cross products and then the dot product of the results of the cross products to determine if a point is on the "correct side" of a line between any two of the vertices (there are three such lines).
Code snippet for algorithm:
boolean SameSide(p1, p2, a, b) {
int[] cp1 = CrossProduct(b - a, p1 - a);
int[] cp2 = CrossProduct(b - a, p2 - a);
if (DotProduct(cp1, cp2) >= 0) {
return true;
}
return false;
}
boolean PointInTriangle(p, a, b, c) {
if (SameSide(p, a, b, c) && SameSide(p, b, a, c) &&
SameSide(p, c, a, b) {
return true;
}
return false;
}
Where a, b, c are the vertices and p is the point. I recommend int [] to hold the vectors. You will need to write the helper functions to calculate vector manipulations.
I could not follow your algorithm, but you could send out horizontal (or any direction for that matter) rays in two directions from the point. If it hits the triangle in both directions, then the point will be inside the triangle. You will need to be careful of special conditions where the ray hits a vertex. That would be considered as hitting two lines on one side. But as long as the ray hits the triangle in both directions, the point will be inside.
So I used your code for method dentroDoTriangulo and stuck is inside a class. I used the Point class given above. Then I called your method using the quick and dirty code given below. It seems to work without any problems. Like #TNT says above, a test case would be helpful.
public class Caller {
public static void main(String[] args) {
Triangle myTraingle = new Triangle();
boolean isInside = myTraingle.dentroDoTriangulo(new Point(1,0), new Point(3,0), new Point(2,2), new Point(2,1));
if (isInside) {
System.out.println("Point is inside");
} else {
System.out.println("Point is outside");
}
}
}

Random "walk" around a central location in a limited area?

I'm not sure if I can express this question correctly, but here it goes..
I want to code an example, where small dots have a velocity according to which they move - but also, there is a random motion superimposed to the "proper" motion. Using the Processing code below, I get the following animation:
The right dot is supposed to be going towards the bottom right corner, and I'm OK with how it behaves. The problem is the left dot, which is supposed to be "static" - so it would only show the "random" motion "in place"; however, as the animated .gif shows, it tends to end up veering some distance away from its original location. The random velocity is calculated with:
this.randspeed.set(random(0,1)-0.5, random(0,1)-0.5);
I would have guessed that random(0,1)-0.5 doesn't give me a Gaussian-like "normal distribution" centered around (or converging? to) zero; but then again, even if it was a "proper" Gaussian, I still could have such "luck" so that say, positive values [0:0.5) are returned for a whole day, and then negative values [-0.5:0) are returned the next day, and in the end, it would still be a proper Gaussian.
So, I guess, I'm looking for a way to convert a (pseudo?)-random sequence (as the one generated by random(0,1)-0.5) to a pseudo-random one, but in which the average sum of N samples (say, 10) is 0. I'm not sure how to call this - a random sequence periodically converging to zero, I guess??
Note that I've been trying below with both changing position directly; and saving position with changing finalpos instead - changing the position seems more like a "natural", smoothed motion (especially with the modulo frame operation, so a new random velocity isn't assigned every frame); but then, it also allows that the random noise accumulates, and "pushes" the dot away from its central location. Also, note that it took me a few takes until I could reproduce this on the .gif, running the program "live" seems to cause the dot to diverge from the original location more quickly (I had read something about hardware events like hard-disk writes being used for changing entropy for /dev/random on Linux, but I don't really know if it's related).
Also, I thought of setting some sort of a virtual border around the dot position, and having a collision detection for the random motion going out of the border - but that seems to me like too much work (and CPU cycles for vector operations) for this kind of thing; I would have hoped that the random function can somehow be "tempered" in an easier manner, instead.
So, would there be a recommended way to approach this kind of random motion around a central location in a limited area?
marbles.pde:
import java.util.*; // added for Iterator;
ArrayList<Marble> marbles = new ArrayList<Marble>();
Iterator<Marble> imarb;
color mclr = #0000FF;
int RNDLIMIT = 2;
int framecount = 0;
void setup() {
size(600,400,P2D);
Marble m_moving = new Marble(width/2, height/2, 2, 2);
marbles.add(m_moving);
Marble m_stopped = new Marble(width/2-100, height/2, 0, 0);
marbles.add(m_stopped);
}
void draw() {
background(255);
strokeWeight(1);
stroke(mclr);
fill(mclr);
imarb = marbles.iterator();
while (imarb.hasNext()) {
Marble m = imarb.next();
m.update();
ellipse(m.finalpos.x, m.finalpos.y, m.radius*2, m.radius*2);
}
framecount++;
//~ saveFrame("marbles-######.png");
}
class Marble {
PVector position = new PVector(0,0);
PVector finalpos = new PVector(0,0);
PVector speed = new PVector(0,0);
PVector randspeed = new PVector(0,0);
float radius=4;
public Marble() {
}
public Marble(float inx, float iny, float invx, float invy) {
this.position.set(inx, iny);
this.speed.set(invx, invy);
}
public void update() {
this.position.add(this.speed);
if (framecount % 4 == 0) {
this.randspeed.set(random(0,1)-0.5, random(0,1)-0.5);
this.randspeed.setMag(RNDLIMIT);
}
int algoTry = 1; // 0
switch(algoTry) {
case 0:
this.finalpos.set(PVector.add(this.position, this.randspeed));
break;
case 1:
this.position.set(PVector.add(this.position, this.randspeed));
this.finalpos.set(this.position);
break;
}
}
}
A typical 'random walk' will always meander away, because statistics don't 'balance'. Moving a lot to the left will not be corrected with movement to the right. So quality of the randomness isn't the issue.
If you want the dot to stay around a specific location, you should store that location and make the "proper" motion (as you called it) always move towards that location. Some subtraction of current location from target location should get you the correct "proper" motion. With this solution, the dot will always be inclined to head back to where is started.
Well, I think I got somewhere; thanks to the comment by #Teepeemm, I learned about Ornstein - Uhlenbeck process, and also that Brownian motion: "is described by the Wiener process ... one of the best known Lévy processes". Rereading Ornstein - Uhlenbeck process ("Over time, the process tends to drift towards its long-term mean ... is a prototype of a noisy relaxation process ...the length x(t) of the spring will fluctuate stochastically around the spring rest length x0;"), I realized it is not what I want - it would have caused my dot eventually to settle in the central position, and then I would have had to "ping" it every once in a while.
Just as I realized that it would take me forever to fist understand, and then code, those processes - I found this:
Generation of noise with given PSD - Newsreader - MATLAB Central
I want to generate noise data with especific frequency
characteristics: That is, the power spectral density (PSD) has to be
proportional to f^0, f, f^2 etc.
f^0 -- use randn
f^(-2) -- low-pass filter the f^0 time series, or integrate with cumsum
f^2 -- differentiate, as with diff
... so I thought, maybe I can somehow process the raw random numbers, to get a "distribution" as I want. So I came up with a Processing patch, which you'll find below as rndquest2.pde. Processing makes it easy to use alpha colors for points, and if the background is not erased, they accumulate - so it's easier to see what is the actual distribution of a random output being tweaked. I got this image:
The "choice 0" seems to point out that random() generates a sequence with uniform distribution (white noise). For instance, "choice 1" would cause the dot to tend to stick on the edges; "choice 2" quite obviously shows folding ; and I'd prefer a circle, too. In the end, I got something most resembling a Gauss (most frequent in the center, and slowly diminishing to the edges) on "choice 9", by something like a radial folding, I guess. There's still a visible threshold border on "choice 9", but if it is implemented in the code above in OP, then I get something like this:
... which is, actually, as I wanted it! (not sure why the start came out as it did, though) The trick is that the random vector, once limited/processed, should be interpreted as a position (or rather, should be added to the position, to obtain a new position, used to calculate a new velocity for finalpos); it should not be directly added to the speed/velocity!
So, only these changes need to be added in the OP code:
...
float r1 =0, r2 = 0;
PVector rv = new PVector(r1, r2);
float radius = 10;
float pr1 =0; int pr3 =0;
...
int signum(float f) {
if (f > 0) return 1;
if (f < 0) return -1;
return 0;
}
float getRandom() {
float ret;
ret = random(-radius,radius);
return ret;
}
void getRandomVect() {
r1 = getRandom();
r2 = getRandom();
rv.set(r1,r2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
rv.set(r1,r2);
}
pr1 = rv.mag()-radius/2;
pr3 = int(radius-rv.mag());
pr3 = (pr3 == 0) ? 1 : pr3;
if (pr1>0) {
r1 = rv.x - random(1)*2*signum(rv.x)*pr3;
r2 = rv.y - random(1)*2*signum(rv.y)*pr3;
}
rv.set(r1,r2);
}
...
public void update() {
this.position.add(this.speed);
if (framecount % 4 == 0) {
getRandomVect();
this.randspeed.set(PVector.div(PVector.sub(PVector.add(this.position, rv), this.finalpos), 4));
}
this.finalpos.set(PVector.add(this.finalpos, this.randspeed));
}
...
... to get it working as shown on the gif in this post.
Well, hope this helps someone,
Cheers!
rndquest2.pde
PVector mainpos = new PVector(200.0, 200.0);
float radius = 50;
float x1 =0, y1 = 0;
float r1 =0, r2 = 0;
float pr1 =0, pr2 = 0;
int pr3 =0, pr4 = 0;
PVector rv = new PVector(r1, r2);
color clr = color(0,0,255,30);
int choice = 0;
int framecount = 0;
void setup() {
size(600,400,P2D);
background(255);
textSize(14);
textAlign(LEFT, TOP);
}
void draw() {
try {
strokeWeight(2);
stroke(clr); // #0000FF web colors only
fill(clr);
point(mainpos.x, mainpos.y);
r1 = getRandom();
r2 = getRandom();
switch(choice) {
case 0:
x1 = mainpos.x + r1;
y1 = mainpos.y + r2;
println("0"); // these help trigger the draw(), apparently..
break;
case 1:
rv.set(r1,r2);
if(rv.mag() > radius) {
rv.setMag(radius);
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("1");
break;
case 2:
rv.set(r1,r2);
if(rv.mag() > radius) {
rv.sub(PVector.mult(rv,0.1*(rv.mag()-radius)));
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("2");
break;
case 3:
rv.set(r1,r2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
rv.set(r1,r2);
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("3");
break;
case 4:
pr1 = rv.x;
pr2 = rv.y;
rv.set(r1-pr1,r2-pr2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
rv.set(r1-pr1,r2-pr2);
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("4");
break;
case 5:
pr1 = rv.x;
pr2 = rv.y;
rv.set(r1-pr1,r2-pr2);
if(rv.mag() > radius) {
rv.mult(1.0/(rv.mag()-radius));
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("5");
break;
case 6:
pr1 = (pr1 + r1)/2.0;
pr2 = (pr2 + r2)/2.0;
rv.set(pr1,pr2);
if(rv.mag() > radius) {
rv.mult(1.0/(rv.mag()-radius));
}
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("6");
break;
case 7:
r1 = (pr1 + r1)/2.0;
r2 = (pr2 + r2)/2.0;
rv.set(r1,r2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
r1 = (pr1 + r1)/2.0;
r2 = (pr2 + r2)/2.0;
rv.set(r1,r2);
}
pr1 = rv.x;
pr2 = rv.y;
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("7");
break;
case 8:
rv.set(r1,r2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
rv.set(r1,r2);
}
//~ pr1 = abs(rv.x)-radius/2;
//~ pr2 = abs(rv.y)-radius/2;
pr1 = rv.mag()-radius/2;
//~ pr3 = int(radius-abs(rv.x));
//~ pr4 = int(radius-abs(rv.y));
pr3 = int(radius-pr1);
pr3 = (pr3 == 0) ? 1 : pr3;
//~ pr4 = (pr4 == 0) ? 1 : pr4;
if (pr1>0)
r1 = rv.x - random(1)*2*signum(rv.x)*pr1; //framecount ; b2i(int(random(radius)) % pr3 == 0)*
if (pr1>0) //(pr2>0)
r2 = rv.y - random(1)*2*signum(rv.y)*pr1;//pr2;
rv.set(r1,r2);
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("8");
break;
case 9:
rv.set(r1,r2);
while(rv.mag() > radius) {
r1 = getRandom();
r2 = getRandom();
rv.set(r1,r2);
}
pr1 = rv.mag()-radius/2;
pr3 = int(radius-rv.mag()); //pr1);
pr3 = (pr3 == 0) ? 1 : pr3;
if (pr1>0) {
r1 = rv.x - random(1)*2*signum(rv.x)*pr3; //framecount ; b2i(int(random(radius)) % pr3 == 0)*
r2 = rv.y - random(1)*2*signum(rv.y)*pr3;//pr2;
//~ r1 = rv.x - 2*signum(rv.x)*pr3; //like an X for pr3 = int(radius-pr1);
//~ r2 = rv.y - 2*signum(rv.y)*pr3;
}
rv.set(r1,r2);
x1 = mainpos.x + rv.x;
y1 = mainpos.y + rv.y;
println("9");
break;
}
// note: patch does not draw point(mainpos.x + getRandom(), ..)
point(x1, y1);
fill(255);
stroke(255); //~ stroke(255,0,0);
rect(mainpos.x-radius,100,mainpos.x-radius+100,20);
fill(0,0,255);
stroke(clr);
text(String.format("choice %d (f:%d)", choice, framecount), mainpos.x-radius, 100);
framecount++;
if (framecount % 5000 == 0) {
saveFrame(String.format("rndquest2-%d-%d-######.png", choice, framecount));
}
} catch(Exception e) {
e.printStackTrace();
}
}
int signum(float f) {
if (f > 0) return 1;
if (f < 0) return -1;
return 0;
}
int b2i(boolean inb) {
if (inb) return 1;
else return 0;
}
float getRandom() {
float ret;
ret = random(-radius,radius);
return ret;
}
void mousePressed() {
choice = (choice + 1) % 10;
background(255);
framecount = 0;
}
If you want random movement within a certain distance of an "actual" point, you could try having a fixed, maximum-distance from the "actual" location, and not allowing the ball outside of that radius.
If you don't want a hard limit, you could add some kind of force that attracts the object toward its "actual" location, and make it increase with the distance from that point linearly, quadratically, or by some other function of your choosing. Then the object would be free to move around its "actual" location, but still be kept relatively nearby.
You are simulating a random walk. Generally, a random walk after n steps will be on the order of sqrt(n) from where it started (more specifically, it will obey the Law of the Iterated Logarithm, so that its magnitude after n steps is O(sqrt(n log log n))). Which is a long way of saying that the walk will wander away as time goes on (but because it's two dimensional, it will eventually return to the origin).
To solve this, you want to have a drift back toward the origin. One random process which has this property is the Ornstein - Uhlenbeck process, which has a drift toward the origin that is proportional to its distance from the origin. (And the random part of the random walk would still cause it to wiggle around its origin.)
This could be accomplished in your original code by something along the lines of
double driftScale = .01;
double wiggleScale = 1;
Point origin = new Point(0,0);
...
this.randspeed.set(driftScale*(origin.x-this.position.x)+wiggleScale*(random(0,1)-.5),
driftScale*(origin.y-this.position.y)+wiggleScale*(random(0,1)-.5));
It would be better to replace random(0,1)-.5 with a standard normal Gaussian, but I don't know how noticeable that affect would be. The biggest difference is that with the current code, there is a maximum distance the point can get from its start. With a Gaussian, it could theoretically get arbitrarily far (but it would still drift back to the origin).
I'm also not quite sure how much this matches with your eventual solution. I'm having trouble following your logic (using PVector and 10 cases didn't help).

Creating a Squircle

I'm a first year programmer. I'm trying to create a squircle. (square with round corners).
So far i have managed to get. I have been given the constants of a,b and r. If anyone could help i would be really thankful. I'm a total noob to this. So be nice :)
package squircle;
import java.awt.*;
import javax.swing.*;
import java.lang.Math;
public class Main extends javax.swing.JApplet {
public void paint(Graphics g){
// (x-a)^4 + (y-b)^4 = r^4
// y = quadroot( r^4 - (x-a)^4 + b)
// x values must fall within a-r < x < a+r
int[] xPoints = new int[200];
int[] yPoints = new int[200];
int[] mypoints = new int[200];
for(int c = 0; c <200; c++){
int a = 100;
int r = 100;
int b = 100;
double x = c ;
double temp = (r*r*r*r);
double temp2 = x-a;
double temp3 = ((temp2)*(temp2)*(temp2)*(temp2));
double temp6 = Math.sqrt(temp-temp3);
double y = (Math.sqrt(temp6) + b );
double z = (y*-1)+300;
mypoints[c]=(int)z;
// if (c>100){
// y = y*1;
// }
// else if(c<100){
// y = y*1;
// }
xPoints[c]=(int)x;
yPoints[c]=(int)y;
// change the equation to find x co-ordinates
// change it to find y co-ordinates.
// r is the minor radius
// (a,b) is the location of the centre
// a = 100
// b = 100
// r = 100
// x value must fall within 0 or 200
}
g.drawPolygon(xPoints, yPoints, xPoints.length);
g.drawPolygon(xPoints, (mypoints), xPoints.length);
}
}
Is it homework or is there some other reason why you're not using Graphics#drawRoundRect()?
If you are submitting this as homework there are some elements of style that may help you. What are the roles of 200, 100 and 300? These are "magic constants" which should be avoided. Are they related or is it just chance that they have these values? Suggest you use symbols such as:
int NPOINTS = 200;
or
double radius = 100.0
That would reveal whether the 300 was actually the value you want. I haven't checked.
Personally I wouldn't write
y*-1
but
-y
as it's too easy to mistype the former.
I would also print out the 200 points as floats and see if you can tell by eye where the error is. It's highly likely that the spurious lines are either drawn at the start or end of the calculation - it's easy to make "end-effect" errors where exactly one point is omitted or calculated twice.
Also it's cheap to experiment. Try iterating c from 0 to 100. or 0 to 10, or 0 to 198 or 1 to 200. Does your spurious line/triangle always occur?
UPDATE Here is what I think is wrong and how to tackle it. You have made a very natural graphics error and a fence-post error (http://en.wikipedia.org/wiki/Off-by-one_error) and it's hard to detect what is wrong because your variable names are poorly chosen.
What is mypoints? I believe it is the bottom half of the squircle - if you had called it bottomHalf then those replying woulod have spotted the problem quicker :-).
Your graphics problem is that you are drawing TWO HALF-squircles. Your are drawing CLOSED curves - when you get to the last point (c==199) the polygon is closed by drawing back to c==0. That makes a D-shape. You have TWO D-shapes, one with the bulge UP and one DOWN. Each has a horizontal line closing the polygon.
Your fence-post error is that you are drawing points from 0 to 199. For the half-squircle you want to draw from 0 to 200. That's 201 points! The loss of one point means that you have a very slightly sloping line. The bottom lines slopes in tghe opposite direction from the top. That gives you a very then wedge shape, which you refer to as a triangle. I'm guessing that your triangle is not actually closed but like a slice from a pie but very then/sharp.
(The code below could be prettier and more compact. However it is often useful to break symmetrical problems into quadrants or octants. It would also be interesting to use an anngle to sweep out the polygon).
You actually want ONE polygon. The code should be something like:
int NQUADRANT = 100;
int NPOINTS = 4*NQUADRANT ; // closed polygon
double[] xpoints = new double[NPOINTS];
double[] ypoints = new double[NPOINTS];
Your squircle is at 100, 100 with radius 100. I have chosen different values here
to emphasize they aren't related. By using symbolic names you can easily vary them.
double xcenter = 500.0;
double ycentre = 200.0;
double radius = 100.;
double deltax = radius/(double) NQUADRANT;
// let's assume squircle is centered on 0,0 and add offsets later
// this code is NOT complete or correct but should show the way
// I might have time later
for (int i = 0; i < NPOINTS; i++) {
if (i < NQUADRANT) {
double x0 = -radius + i* deltax;
double y0 = fourthRoot(radius, x0);
x[i] = x0+xcenter;
y[i] = y0+ycenter;
}else if (i < 2*NQUADRANT) {
double x0 = (i-NQUADRANT)* deltax;
double y0 = fourthRoot(radius, x0);
x[i] = x0+xcenter;
y[i] = y0+ycenter;
}else if (i < 3*NQUADRANT) {
double x0 = (i-2*NQUADRANT)* deltax;
double y0 = -fourthRoot(radius, x0);
x[i] = x0+xcenter;
y[i] = y0+ycenter;
}else {
double x0 = -radius + (i-3*NQUADRANT)* deltax;
double y0 = -fourthRoot(radius, x0);
x[i] = x0+xcenter;
y[i] = y0+ycenter;
}
}
// draw single polygon
private double fourthRoot(double radius, double x) {
return Math.sqrt(Math.sqrt(radius*radius*radius*radius - x*x*x*x));
}
There is a javascript version here. You can view the source and "compare notes" to potentially see what you are doing wrong.
Ok, upon further investigation here is why you are getting the "triangle intersecting it". When you drawPolygon the points are drawn and the last point connects the first point, closing the points and making the polygon. Since you draw one half it is drawn (then connected to itself) and then the same happens for the other side.
As a test of this change your last couple lines to this:
for( int i = 0; i < yPoints.length; i++ ) {
g.drawString( "*", xPoints[ i ], yPoints[ i ] );
}
for( int i = 0; i < mypoints.length; i++ ) {
g.drawString( "*", xPoints[ i ], mypoints[ i ] );
}
// g.drawPolygon( xPoints, yPoints, xPoints.length );
// g.drawPolygon( xPoints, ( mypoints ), xPoints.length );
It is a little crude, but I think you'll get the point. There are lots of solutions out there, personally I would try using an array of the Point class and then sort it when done, but I don't know the specifics of what you can and can not do.
Wow, are you guys overthinking this, or what! Why not just use drawLine() four times to draw the straight parts of the rectangle and then use drawArc() to draw the rounded corners?

Categories