Reputation: 3020
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
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