gromiczek
gromiczek

Reputation: 3020

How to define a cloud of particles relative to a vector path with Three.js?

I've been researching and playing with examples of particle clouds in Three.js. Most use shape geometries to define a field of particles, or parameters for distributing them randomly throughout the field of view. What I would like to do is create a particle cloud in which each particle has a relative proximity to an invisible vector path. For example, if I defined a lightly curved vector path, all the particles might float within a consistent radius along that invisible and then maybe taper toward the ends to form a hotdog-shaped cloud of particles. So, I know how to create particles and I know how to create vector paths, how do I link these two things together? Thanks!

Upvotes: 0

Views: 616

Answers (1)

Nikola Dimitroff
Nikola Dimitroff

Reputation: 6237

You can define the path using two points. Let those points be p and q and let v = p - q. Any point M lying on the path must satisfy the vector equation

M = (1 - lambda) * p + lambda * q

for some 0 <= lambda <= 1. Thus, you can generate a random point on the path by generating a random lambda and using its value in the equation above:

// p and q are instances of THREE.Vector3
function pointOnPath(p, q) {
   var lambda = Math.random();
   var scaledp = (new THREE.Vector3()).copy(p).multiplyScalar(1 - lambda);
   var scaleq = (new THREE.Vector3()).copy(q).multiplyScalar(lambda);
   var result = (new THREE.Vector3()).addVectors(scaledp, scaledq);
   return result;
} 

Next, you want to modify the computed coordinates with some small radius so that they circle around the path. You do that by adding a small vector offset. How do we compute that vector then?

The vector we are after lies in a plane that's perpendicular to the line from p to q. There are an infinite number of vectors satisfying the above condition, two of them being e1 = (v.y, -v.x, 0) and e2 = (v.z, 0, -v.x). Any vector of the form lambda * e1 + mu * e2 will also be perpendicular to the v. Thus, we need only generate lambda and mu and everything's ready.

NOTE: lambda and mu must be random numbers in the interval [-1; 1], not [0; 1]. Since we are normalizing the offset vector, the interval [-0.5; 0.5] will suffice because normalization will map it to [-1; 1]

function getVectorOffset(p, q, radius) {
    var v = (new THREE.Vector3()).subVectors(q, p);
    v.normalize();
    var e1 = new THREE.Vector3(v.y, -v.x, 0),
        e2 = new THREE.Vector3(v.z, 0, -v.x);
    e1.normalize();
    e2.normalize();

    var lambda = Math.random() - 0.5,
        mu = Math.random() - 0.5;
    var offset = e1.multiplyScalar(lambda).add(e2.multiplyScalar(mu));
    offset.normalize();
    offset.multiplyScalar(radius) // multiply the compute offset by the radius you'd like it to circle around

    return offset;

}

Finally, to generate your desired point:

function pointOnHotDog(p, q, radius) {
    return pointOnPath(p, q).add(getVectorOffset(p, q, radius));
}

Here's a working jsfiddle

Upvotes: 2

Related Questions