We are about to switch to a new forum software. Until then we have removed the registration on this forum.
I'm trying to make a brownian motion with a fixed stepping size on the surface of a 3D sphere. I want to produce a result similar to the animations on this page.
Turns out to be a real headscratcher. Best bet so far is the equations found in the section "Destination point given distance and bearing from start point" on this rather excellent page. Implementing these, I notice that the random walker seems to get attracted towards the poles on the sphere, leaving the impression that this is not at all a random walk.
Any tips, tricks, links, explanations or feedback/improvements to my code (provided below) is highly appreciated!
Crawler c;
void setup() {
size(800, 800, P3D);
c = new Crawler(200.0, 10.0);
}
void draw() {
background(0);
translate(width/2, height/2, 0);
rotateY(frameCount * 0.01);
c.display();
c.update();
}
class Crawler {
ArrayList<Point3D> positions;
float radius, distance;
float theta, phi;
float bearing;
Crawler(float _radius, float _stepSize) {
radius = _radius;
distance = _stepSize/radius;
positions = new ArrayList<Point3D>();
theta = random(-PI, PI);
phi = random(-PI, PI);
bearing = random(-PI, PI);
}
void update() {
// Calculate new theta and phi values
float phi2 = asin(sin(phi)*cos(distance)+cos(phi)*sin(distance)*cos(bearing));
float theta2 = theta+atan2(sin(bearing)*sin(distance)*cos(phi), cos(distance)-sin(phi)*sin(phi2));
// Calculate new bearing on destination
float deltaTheta = theta-theta2;
float tempy = sin(deltaTheta)*cos(phi);
float tempx = cos(phi2)*sin(phi)-sin(phi2)*cos(phi)*cos(deltaTheta);
float delta = atan2(tempy, tempx);
float finalBearing = delta+PI;
// Checking that the theta value are within the -PI to PI range
if (theta2 < -PI) theta2 += TWO_PI;
if (theta2 > PI) theta2-= TWO_PI;
// Convert lat/lon to x/y/z positions and push position into array
float x = radius * sin(theta2) * cos(phi2);
float y = radius * sin(theta2) * sin(phi2);
float z = radius * cos(theta2);
positions.add(new Point3D(x, y, z));
// Update theta and phi with new values
theta = theta2;
phi = phi2;
// Update bearing with new value, add a random course and keep within 0 to TWO_PI range
bearing = finalBearing + random(-HALF_PI, HALF_PI); // random(TWO_PI) = any direction possible
if (bearing<0) bearing += TWO_PI;
if (bearing>TWO_PI) bearing -= TWO_PI;
}
void display() {
stroke(255);
noFill();
int counter = 0;
float prevx = 0, prevy = 0, prevz = 0;
for (Point3D p : positions) {
if (counter > 1) {
line(prevx, prevy, prevz, p.x, p.y, p.z);
}
prevx = p.x;
prevy = p.y;
prevz = p.z;
counter++;
}
}
class Point3D {
float x, y, z;
Point3D(float _x, float _y, float _z) {
x = _x;
y = _y;
z = _z;
}
}
}
Answers
Looks really nice.
The angular distance between points in the east-west position at the poles will be smaller that that at the equator for the same change in longitude. So it will take longer for a walker to leave a pole. You could overcome this using a torus instead of a sphere.
Rather than a random walk you might try the wander steering behaviour
Qualified input. Thank you quark.
While I understand the difference between a torus and a sphere, I'm not sure how this would solve the problem points clumping together at certain parts of the torus and still cause "polar drift". Have you got any examples that involve using a torus instead of a sphere?
I'll look into the wander behaviour as you suggest.
point 3D is PVector in processing (see reference) btw.
Imagine a 2D grid of regularly spaced points and you wrap it round a sphere. The points near the poles are closer together than at the equater, and at the poles they are touching each other.
If you wrap the grid of the toruus the points on the inside of the ring are closer together than the ones on the outside but to a lesser degree provided the ring radius is significantly larger than the tube radius.
This is a simplified version of your code but modified to work on a torus.
can't we compensate the pole effect by modyfing a factor for random?
When closer to pole, less factor?
Appreciate you taking the time to modify my example, quark. Very useful. Only reason I've chosen to reject this as an answer, is because my initial question was about a sphere not a torus. If, by some clever topological math, you can transform the torus shape into a perfect sphere and still retain the even spread in the crawlers path, I'll be chuffed to bits.
Chrisir, you might be on to something here. I've thought about this myself, but as soon as I start to fiddle with the formulas, I very quickly screw things up. At least when using a combination of spherical/cartesian coordinates. That's why I'm considering switching to using vectors, but that's a whole new ballgame to me. Found this example by Daniel Shiffman that I'm currently trying to wrangle into doing what I've described above.
Not sure if this will help, but I think your question essentially boils down to placing equidistant points on a sphere (as points otherwise get closer at each pole as quark has already pointed out). Anyway, from what I can tell, this is quite hard to achieve (in a purely mathematical sense) but it seems there are ways to approximate this. See code below...at least to me this seems to give a more even distribution of points around a sphere. Perhaps you can adapt to your crawler?
I work around the projection sphere in Polar and Cartesian mode maybe this piece of code can help you. You can remplace the class Vec3 by PVector or downlod the full code here, if you want : https://github.com/StanLepunK/Math
Thank you G_D! The formula you found on physicsforum.com was exactly what I needed. For anyone that might be interested, I'm sharing a sloppy yet working version my code that solves my initial question. Thank you all for your input. Internet high fives!
Well done!
You could do it with a PMatrix and its axis / angle rotate method too. Basically start with a point offset from the center by radius. Apply the rotation and for each additional point use the rotated position of the previous point as the next point.
Wow, rbrauer, that's truly impressive! It never occurred to me that I could use PMatrix3D. I'll be spending the next couple of hours dissecting your code to try and understand how it works. Tip of the hat to you. And thank you for sharing.