-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
executable file
·91 lines (83 loc) · 2.85 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
* Remap number
*
* @private
* @param {number} n
* @param {number} from1
* @param {number} to1
* @param {number} from2
* @param {number} to2
* @returns {number}
*/
const remap = (n, from1, to1, from2, to2) =>
from2 + ((to2 - from2) * (n - from1)) / (to1 - from1);
/**
* A data structure and lookup for 3D vector fields (flow fields).
*
* @property {import("./types.js").VectorFieldDirectionFn} directionFn
* @property {import("./types.js").vec3} steps
* @property {import("./types.js").vec3} bounds
* @property {import("./types.js").vec3} halfBounds
* @property {import("./types.js").VectorFieldCell[]} field
*/
class VectorField {
/**
* Creates an instance of VectorField.
* @param {import("./types.js").VectorFieldDirectionFn} directionFn The custom function to compute the cell direction (often a noise function)
* @param {number|import("./types.js").vec3} [steps=10] The number of steps on each dimension (all positive integer). Use integer for identical dimensions.
* @param {number|import("./types.js").vec3} [bounds=1] The size of a containing box for the field. Is divided into steps for each dimension (all positive). Use integer for identical dimensions.
*/
constructor(directionFn, steps = 10, bounds = 1) {
this.directionFn = directionFn;
this.steps = Array.isArray(steps) ? steps : [steps, steps, steps];
this.bounds = Array.isArray(bounds) ? bounds : [bounds, bounds, bounds];
this.halfBounds = this.bounds.map((bound) => bound * 0.5);
this.field = [];
}
/**
* Create/update the field according to the provided noise function.
*/
update() {
for (let x = 0; x < this.steps[0]; x++) {
this.field[x] ||= [];
for (let y = 0; y < this.steps[1]; y++) {
this.field[x][y] ||= [];
for (let z = 0; z < this.steps[2]; z++) {
this.field[x][y][z] ||= {};
this.field[x][y][z].position = [
this.bounds[0] * (x / this.steps[0]),
this.bounds[1] * (y / this.steps[1]),
this.bounds[2] * (z / this.steps[2]),
];
this.field[x][y][z].direction = this.directionFn(
this.field[x][y][z].position,
[x, y, z],
);
}
}
}
}
/**
* Find a `VectorFieldCell` at specified position. Useful to compute a particle's velocity for instance.
*
* @param {import("./types.js").vec3} cell [cx, cy, cz]
* @returns {VectorFieldCell|undefined}
*/
lookup([cx, cy, cz]) {
return this.field[
Math.round(
remap(cx, -this.halfBounds[0], this.halfBounds[0], 0, this.steps[0]),
)
]?.[
Math.round(
remap(cy, -this.halfBounds[1], this.halfBounds[1], 0, this.steps[1]),
)
]?.[
Math.round(
remap(cz, -this.halfBounds[2], this.halfBounds[2], 0, this.steps[2]),
)
];
}
}
export default VectorField;
export * from "./types.js";