@johnhenry/math-sample 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +244 -2
- package/dist/budget.d.ts +47 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +44 -0
- package/dist/budget.js.map +1 -0
- package/dist/complex.d.ts +79 -0
- package/dist/complex.d.ts.map +1 -0
- package/dist/complex.js +236 -0
- package/dist/complex.js.map +1 -0
- package/dist/expr.d.ts +38 -0
- package/dist/expr.d.ts.map +1 -0
- package/dist/expr.js +264 -0
- package/dist/expr.js.map +1 -0
- package/dist/field2d.d.ts +58 -0
- package/dist/field2d.d.ts.map +1 -0
- package/dist/field2d.js +199 -0
- package/dist/field2d.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/mesh.d.ts +90 -0
- package/dist/mesh.d.ts.map +1 -0
- package/dist/mesh.js +294 -0
- package/dist/mesh.js.map +1 -0
- package/dist/ode.d.ts +147 -0
- package/dist/ode.d.ts.map +1 -0
- package/dist/ode.js +466 -0
- package/dist/ode.js.map +1 -0
- package/dist/sample.d.ts +114 -0
- package/dist/sample.d.ts.map +1 -0
- package/dist/sample.js +308 -0
- package/dist/sample.js.map +1 -0
- package/dist/series.d.ts +53 -0
- package/dist/series.d.ts.map +1 -0
- package/dist/series.js +158 -0
- package/dist/series.js.map +1 -0
- package/dist/svg.d.ts +37 -0
- package/dist/svg.d.ts.map +1 -0
- package/dist/svg.js +82 -0
- package/dist/svg.js.map +1 -0
- package/dist/ticks.d.ts +17 -0
- package/dist/ticks.d.ts.map +1 -0
- package/dist/ticks.js +47 -0
- package/dist/ticks.js.map +1 -0
- package/dist/viewport.d.ts +59 -0
- package/dist/viewport.d.ts.map +1 -0
- package/dist/viewport.js +111 -0
- package/dist/viewport.js.map +1 -0
- package/package.json +70 -4
- package/src/budget.ts +72 -0
- package/src/complex.ts +327 -0
- package/src/expr.ts +283 -0
- package/src/field2d.ts +259 -0
- package/src/index.ts +112 -0
- package/src/mesh.ts +376 -0
- package/src/ode.ts +633 -0
- package/src/sample.ts +391 -0
- package/src/series.ts +209 -0
- package/src/svg.ts +124 -0
- package/src/ticks.ts +46 -0
- package/src/viewport.ts +150 -0
package/src/mesh.ts
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { assertCount, assertRange, type Budget, type Cancellable, Meter, type Progress, progress } from "./budget.ts";
|
|
2
|
+
import type { Domain } from "./sample.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Triangle mesh as typed arrays, ready for a GPU buffer.
|
|
6
|
+
*
|
|
7
|
+
* - `positions` and `normals` hold `vertexCount * 3` floats (x, y, z per vertex).
|
|
8
|
+
* - `indices` holds triangles (3 per triangle), counter-clockwise seen from the side the normals point to.
|
|
9
|
+
* - A grid vertex whose position is not finite (or is outside `zRange`) is a hole: its position is `NaN`, its normal
|
|
10
|
+
* is `0, 0, 0`, `valid[v]` is 0, and no triangle references it, so a hole never produces a spike.
|
|
11
|
+
* - `bounds` covers the valid vertices only (`NaN`s if there are none).
|
|
12
|
+
* - `columns` x `rows` is the vertex grid (vertex `(i, j)` is `j * columns + i`), for callers that want to build wireframes.
|
|
13
|
+
*/
|
|
14
|
+
export interface Mesh {
|
|
15
|
+
positions: Float32Array;
|
|
16
|
+
normals: Float32Array;
|
|
17
|
+
indices: Uint32Array;
|
|
18
|
+
valid: Uint8Array;
|
|
19
|
+
vertexCount: number;
|
|
20
|
+
columns: number;
|
|
21
|
+
rows: number;
|
|
22
|
+
bounds: { min: [number, number, number]; max: [number, number, number] };
|
|
23
|
+
progress: Progress;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface MeshOptions extends Budget, Cancellable {
|
|
27
|
+
/** Vertices along the first parameter (x or u). Default 64. */
|
|
28
|
+
columns?: number;
|
|
29
|
+
/** Vertices along the second parameter (y or v). Default 64. */
|
|
30
|
+
rows?: number;
|
|
31
|
+
/** Vertices with z outside this range are holes (a pole's huge finite shoulders). `z = f(x, y)` only. */
|
|
32
|
+
zRange?: { min: number; max: number };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type Fn2 = (x: number, y: number) => number;
|
|
36
|
+
export type ParametricSurfaceFn = (u: number, v: number) => readonly [number, number, number];
|
|
37
|
+
export type CurveFn3 = (t: number) => readonly [number, number, number];
|
|
38
|
+
export type VectorFn3 = (x: number, y: number, z: number) => readonly [number, number, number];
|
|
39
|
+
|
|
40
|
+
const fin = (a: number, b: number, c: number) => Number.isFinite(a) && Number.isFinite(b) && Number.isFinite(c);
|
|
41
|
+
|
|
42
|
+
function build(
|
|
43
|
+
evalAt: (u: number, v: number) => readonly [number, number, number],
|
|
44
|
+
uDom: Domain,
|
|
45
|
+
vDom: Domain,
|
|
46
|
+
options: MeshOptions,
|
|
47
|
+
rangeZ: MeshOptions["zRange"],
|
|
48
|
+
flip: boolean,
|
|
49
|
+
): Mesh {
|
|
50
|
+
const columns = options.columns ?? 64;
|
|
51
|
+
const rows = options.rows ?? 64;
|
|
52
|
+
assertCount("columns", columns);
|
|
53
|
+
assertCount("rows", rows);
|
|
54
|
+
assertRange("u domain", uDom);
|
|
55
|
+
assertRange("v domain", vDom);
|
|
56
|
+
const meter = new Meter(options, options);
|
|
57
|
+
const pos = new Float64Array(columns * rows * 3).fill(Number.NaN);
|
|
58
|
+
const ok = new Uint8Array(columns * rows);
|
|
59
|
+
let done = 0;
|
|
60
|
+
let reason: "cells" | "time" | undefined;
|
|
61
|
+
for (let j = 0; j < rows; j++) {
|
|
62
|
+
reason = meter.check(columns);
|
|
63
|
+
if (reason) break;
|
|
64
|
+
const v = j === rows - 1 ? vDom.max : vDom.min + ((vDom.max - vDom.min) * j) / (rows - 1);
|
|
65
|
+
for (let i = 0; i < columns; i++) {
|
|
66
|
+
const u = i === columns - 1 ? uDom.max : uDom.min + ((uDom.max - uDom.min) * i) / (columns - 1);
|
|
67
|
+
const p = evalAt(u, v);
|
|
68
|
+
const k = j * columns + i;
|
|
69
|
+
if (fin(p[0], p[1], p[2]) && !(rangeZ && (p[2] < rangeZ.min || p[2] > rangeZ.max))) {
|
|
70
|
+
pos[3 * k] = p[0];
|
|
71
|
+
pos[3 * k + 1] = p[1];
|
|
72
|
+
pos[3 * k + 2] = p[2];
|
|
73
|
+
ok[k] = 1;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
meter.spend(columns);
|
|
77
|
+
done++;
|
|
78
|
+
}
|
|
79
|
+
const usedRows = done;
|
|
80
|
+
const vertexCount = columns * usedRows;
|
|
81
|
+
const positions = new Float32Array(vertexCount * 3);
|
|
82
|
+
const normals = new Float32Array(vertexCount * 3);
|
|
83
|
+
const valid = ok.slice(0, vertexCount);
|
|
84
|
+
const min: [number, number, number] = [Infinity, Infinity, Infinity];
|
|
85
|
+
const max: [number, number, number] = [-Infinity, -Infinity, -Infinity];
|
|
86
|
+
for (let k = 0; k < vertexCount; k++) {
|
|
87
|
+
for (let c = 0; c < 3; c++) {
|
|
88
|
+
const val = pos[3 * k + c] as number;
|
|
89
|
+
positions[3 * k + c] = val;
|
|
90
|
+
if (valid[k]) {
|
|
91
|
+
if (val < (min[c] as number)) min[c] = val;
|
|
92
|
+
if (val > (max[c] as number)) max[c] = val;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// normals: cross of central/one-sided differences over valid neighbours
|
|
97
|
+
const at = (i: number, j: number) => 3 * (j * columns + i);
|
|
98
|
+
const isOk = (i: number, j: number) =>
|
|
99
|
+
i >= 0 && i < columns && j >= 0 && j < usedRows && valid[j * columns + i] === 1;
|
|
100
|
+
for (let j = 0; j < usedRows; j++) {
|
|
101
|
+
for (let i = 0; i < columns; i++) {
|
|
102
|
+
if (!isOk(i, j)) continue;
|
|
103
|
+
const iA = isOk(i - 1, j) ? i - 1 : i;
|
|
104
|
+
const iB = isOk(i + 1, j) ? i + 1 : i;
|
|
105
|
+
const jA = isOk(i, j - 1) ? j - 1 : j;
|
|
106
|
+
const jB = isOk(i, j + 1) ? j + 1 : j;
|
|
107
|
+
const du = [0, 1, 2].map((c) => (pos[at(iB, j) + c] as number) - (pos[at(iA, j) + c] as number));
|
|
108
|
+
const dv = [0, 1, 2].map((c) => (pos[at(i, jB) + c] as number) - (pos[at(i, jA) + c] as number));
|
|
109
|
+
let nx = (du[1] as number) * (dv[2] as number) - (du[2] as number) * (dv[1] as number);
|
|
110
|
+
let ny = (du[2] as number) * (dv[0] as number) - (du[0] as number) * (dv[2] as number);
|
|
111
|
+
let nz = (du[0] as number) * (dv[1] as number) - (du[1] as number) * (dv[0] as number);
|
|
112
|
+
const len = Math.hypot(nx, ny, nz);
|
|
113
|
+
if (len > 0 && Number.isFinite(len)) {
|
|
114
|
+
const s = (flip ? -1 : 1) / len;
|
|
115
|
+
nx *= s;
|
|
116
|
+
ny *= s;
|
|
117
|
+
nz *= s;
|
|
118
|
+
const o = at(i, j);
|
|
119
|
+
normals[o] = nx;
|
|
120
|
+
normals[o + 1] = ny;
|
|
121
|
+
normals[o + 2] = nz;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// valid vertices with a degenerate normal (the poles of a sphere parameterisation): borrow from a neighbour
|
|
126
|
+
for (let j = 0; j < usedRows; j++) {
|
|
127
|
+
for (let i = 0; i < columns; i++) {
|
|
128
|
+
const o = at(i, j);
|
|
129
|
+
if (!isOk(i, j) || normals[o] !== 0 || normals[o + 1] !== 0 || normals[o + 2] !== 0) continue;
|
|
130
|
+
for (const [di, dj] of [
|
|
131
|
+
[0, 1],
|
|
132
|
+
[0, -1],
|
|
133
|
+
[1, 0],
|
|
134
|
+
[-1, 0],
|
|
135
|
+
[0, 2],
|
|
136
|
+
[0, -2],
|
|
137
|
+
] as const) {
|
|
138
|
+
if (!isOk(i + di, j + dj)) continue;
|
|
139
|
+
const q = at(i + di, j + dj);
|
|
140
|
+
if (normals[q] !== 0 || normals[q + 1] !== 0 || normals[q + 2] !== 0) {
|
|
141
|
+
normals[o] = normals[q] as number;
|
|
142
|
+
normals[o + 1] = normals[q + 1] as number;
|
|
143
|
+
normals[o + 2] = normals[q + 2] as number;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!valid.some((x) => x === 1)) {
|
|
150
|
+
for (let c = 0; c < 3; c++) {
|
|
151
|
+
min[c] = Number.NaN;
|
|
152
|
+
max[c] = Number.NaN;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const tri: number[] = [];
|
|
156
|
+
for (let j = 0; j + 1 < usedRows; j++) {
|
|
157
|
+
for (let i = 0; i + 1 < columns; i++) {
|
|
158
|
+
const a = j * columns + i;
|
|
159
|
+
const b = a + 1;
|
|
160
|
+
const c = a + columns;
|
|
161
|
+
const d = c + 1;
|
|
162
|
+
const [va, vb, vc, vd] = [valid[a], valid[b], valid[c], valid[d]];
|
|
163
|
+
// counter-clockwise in (u, v): a, b, d and a, d, c
|
|
164
|
+
if (va && vb && vd) tri.push(a, b, d);
|
|
165
|
+
if (va && vd && vc) tri.push(a, d, c);
|
|
166
|
+
// a quad with exactly one hole corner still yields its one good triangle via the complementary split
|
|
167
|
+
if (!(va && vb && vd) && !(va && vd && vc)) {
|
|
168
|
+
if (va && vb && vc) tri.push(a, b, c);
|
|
169
|
+
else if (vb && vd && vc) tri.push(b, d, c);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
positions,
|
|
175
|
+
normals,
|
|
176
|
+
indices: Uint32Array.from(tri),
|
|
177
|
+
valid,
|
|
178
|
+
vertexCount,
|
|
179
|
+
columns,
|
|
180
|
+
rows: usedRows,
|
|
181
|
+
bounds: { min, max },
|
|
182
|
+
progress: progress(usedRows, rows, reason),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Sample the surface `z = f(x, y)` on a regular grid. Normals point up (`+z` side). Non-finite values, and
|
|
188
|
+
* values outside `options.zRange`, become holes. When the budget ends early, `rows` is the number of grid
|
|
189
|
+
* rows built and `progress` says why.
|
|
190
|
+
*/
|
|
191
|
+
export function sampleSurface(f: Fn2, xDomain: Domain, yDomain: Domain, options: MeshOptions = {}): Mesh {
|
|
192
|
+
return build((x, y) => [x, y, f(x, y)], xDomain, yDomain, options, options.zRange, false);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Sample a parametric surface `(u, v) -> [x, y, z]`. Normals are `dP/du x dP/dv`, so swap the parameters (or
|
|
197
|
+
* negate a component) to flip them. Any non-finite coordinate makes the vertex a hole.
|
|
198
|
+
*/
|
|
199
|
+
export function sampleParametricSurface(
|
|
200
|
+
f: ParametricSurfaceFn,
|
|
201
|
+
uDomain: Domain,
|
|
202
|
+
vDomain: Domain,
|
|
203
|
+
options: MeshOptions = {},
|
|
204
|
+
): Mesh {
|
|
205
|
+
return build(f, uDomain, vDomain, { ...options, zRange: undefined }, undefined, false);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export interface Curve3 {
|
|
209
|
+
/** `count * 3` doubles. A gap row is `NaN, NaN, NaN`; gaps are never first, last or adjacent to another gap. */
|
|
210
|
+
positions: Float64Array;
|
|
211
|
+
/** The parameter value of each row. */
|
|
212
|
+
t: Float64Array;
|
|
213
|
+
count: number;
|
|
214
|
+
progress: Progress;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface CurveOptions extends Budget, Cancellable {
|
|
218
|
+
/** Samples. Default 512. */
|
|
219
|
+
samples?: number;
|
|
220
|
+
/** Segments longer than this (Euclidean) become gaps, which cuts a curve at a jump. Default: no limit. */
|
|
221
|
+
maxStep?: number;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Sample a space curve `t -> [x, y, z]`; non-finite points become gaps rather than spikes. */
|
|
225
|
+
export function sampleSpaceCurve(f: CurveFn3, tDomain: Domain, options: CurveOptions = {}): Curve3 {
|
|
226
|
+
const n = options.samples ?? 512;
|
|
227
|
+
assertCount("samples", n);
|
|
228
|
+
assertRange("t domain", tDomain);
|
|
229
|
+
const meter = new Meter(options, options);
|
|
230
|
+
const rows: number[] = [];
|
|
231
|
+
const ts: number[] = [];
|
|
232
|
+
let done = 0;
|
|
233
|
+
let reason: "cells" | "time" | undefined;
|
|
234
|
+
const CHUNK = 256;
|
|
235
|
+
for (let k = 0; k < n; k++) {
|
|
236
|
+
if (k % CHUNK === 0) {
|
|
237
|
+
reason = meter.check(Math.min(CHUNK, n - k));
|
|
238
|
+
if (reason) break;
|
|
239
|
+
}
|
|
240
|
+
const t = k === n - 1 ? tDomain.max : tDomain.min + ((tDomain.max - tDomain.min) * k) / (n - 1);
|
|
241
|
+
const p = f(t);
|
|
242
|
+
ts.push(t);
|
|
243
|
+
rows.push(fin(p[0], p[1], p[2]) ? p[0] : Number.NaN, p[1], p[2]);
|
|
244
|
+
meter.spend(1);
|
|
245
|
+
done++;
|
|
246
|
+
}
|
|
247
|
+
// gap rule: NaN rows, with maxStep cuts, no leading/trailing/adjacent gaps
|
|
248
|
+
const outP: number[] = [];
|
|
249
|
+
const outT: number[] = [];
|
|
250
|
+
let prev: number | undefined; // index into rows of previous valid row
|
|
251
|
+
let pendingGap = false;
|
|
252
|
+
for (let k = 0; k < done; k++) {
|
|
253
|
+
const x = rows[3 * k] as number;
|
|
254
|
+
if (Number.isNaN(x)) {
|
|
255
|
+
pendingGap = outP.length > 0;
|
|
256
|
+
prev = undefined;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const y = rows[3 * k + 1] as number;
|
|
260
|
+
const z = rows[3 * k + 2] as number;
|
|
261
|
+
let gap = pendingGap;
|
|
262
|
+
if (prev !== undefined && options.maxStep !== undefined) {
|
|
263
|
+
const d = Math.hypot(
|
|
264
|
+
x - (rows[3 * prev] as number),
|
|
265
|
+
y - (rows[3 * prev + 1] as number),
|
|
266
|
+
z - (rows[3 * prev + 2] as number),
|
|
267
|
+
);
|
|
268
|
+
if (d > options.maxStep) gap = true;
|
|
269
|
+
}
|
|
270
|
+
if (gap && outP.length > 0) {
|
|
271
|
+
outP.push(Number.NaN, Number.NaN, Number.NaN);
|
|
272
|
+
outT.push(Number.NaN);
|
|
273
|
+
}
|
|
274
|
+
outP.push(x, y, z);
|
|
275
|
+
outT.push(ts[k] as number);
|
|
276
|
+
prev = k;
|
|
277
|
+
pendingGap = false;
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
positions: Float64Array.from(outP),
|
|
281
|
+
t: Float64Array.from(outT),
|
|
282
|
+
count: outT.length,
|
|
283
|
+
progress: progress(done, n, reason),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Index ranges `[start, end)` of consecutive data rows in a {@link Curve3}. */
|
|
288
|
+
export function curve3Runs(c: Curve3): Array<[number, number]> {
|
|
289
|
+
const runs: Array<[number, number]> = [];
|
|
290
|
+
let start = -1;
|
|
291
|
+
for (let k = 0; k < c.count; k++) {
|
|
292
|
+
if (Number.isNaN(c.positions[3 * k] as number)) {
|
|
293
|
+
if (start >= 0) runs.push([start, k]);
|
|
294
|
+
start = -1;
|
|
295
|
+
} else if (start < 0) start = k;
|
|
296
|
+
}
|
|
297
|
+
if (start >= 0) runs.push([start, c.count]);
|
|
298
|
+
return runs;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export interface VectorField3 {
|
|
302
|
+
/** `count * 3` floats: arrow tails. */
|
|
303
|
+
origins: Float32Array;
|
|
304
|
+
/** `count * 3` floats: the vector at each tail. */
|
|
305
|
+
vectors: Float32Array;
|
|
306
|
+
magnitudes: Float32Array;
|
|
307
|
+
maxMagnitude: number;
|
|
308
|
+
/** Arrows kept: grid points where the field was finite. */
|
|
309
|
+
count: number;
|
|
310
|
+
/** Grid points skipped because the field was non-finite there. */
|
|
311
|
+
skipped: number;
|
|
312
|
+
progress: Progress;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export interface VectorField3Options extends Budget, Cancellable {
|
|
316
|
+
/** Points per axis, `[nx, ny, nz]` or one number for all. Default 8. */
|
|
317
|
+
grid?: number | [number, number, number];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Sample a 3D vector field on a regular grid of the box `x` by `y` by `z`. `progress.rows` counts z-slabs. */
|
|
321
|
+
export function sampleVectorField3(
|
|
322
|
+
f: VectorFn3,
|
|
323
|
+
xDomain: Domain,
|
|
324
|
+
yDomain: Domain,
|
|
325
|
+
zDomain: Domain,
|
|
326
|
+
options: VectorField3Options = {},
|
|
327
|
+
): VectorField3 {
|
|
328
|
+
const g = options.grid ?? 8;
|
|
329
|
+
const [nx, ny, nz] = typeof g === "number" ? [g, g, g] : g;
|
|
330
|
+
assertCount("grid", nx);
|
|
331
|
+
assertCount("grid", ny);
|
|
332
|
+
assertCount("grid", nz);
|
|
333
|
+
assertRange("x domain", xDomain);
|
|
334
|
+
assertRange("y domain", yDomain);
|
|
335
|
+
assertRange("z domain", zDomain);
|
|
336
|
+
const meter = new Meter(options, options);
|
|
337
|
+
const o: number[] = [];
|
|
338
|
+
const vs: number[] = [];
|
|
339
|
+
const mags: number[] = [];
|
|
340
|
+
let skipped = 0;
|
|
341
|
+
let slabs = 0;
|
|
342
|
+
let reason: "cells" | "time" | undefined;
|
|
343
|
+
const lerp = (d: Domain, k: number, n: number) => (k === n - 1 ? d.max : d.min + ((d.max - d.min) * k) / (n - 1));
|
|
344
|
+
for (let kz = 0; kz < nz; kz++) {
|
|
345
|
+
reason = meter.check(nx * ny);
|
|
346
|
+
if (reason) break;
|
|
347
|
+
for (let jy = 0; jy < ny; jy++) {
|
|
348
|
+
for (let ix = 0; ix < nx; ix++) {
|
|
349
|
+
const x = lerp(xDomain, ix, nx);
|
|
350
|
+
const y = lerp(yDomain, jy, ny);
|
|
351
|
+
const z = lerp(zDomain, kz, nz);
|
|
352
|
+
const v = f(x, y, z);
|
|
353
|
+
if (!fin(v[0], v[1], v[2])) {
|
|
354
|
+
skipped++;
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
o.push(x, y, z);
|
|
358
|
+
vs.push(v[0], v[1], v[2]);
|
|
359
|
+
mags.push(Math.hypot(v[0], v[1], v[2]));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
meter.spend(nx * ny);
|
|
363
|
+
slabs++;
|
|
364
|
+
}
|
|
365
|
+
let maxMagnitude = 0;
|
|
366
|
+
for (const m of mags) if (m > maxMagnitude) maxMagnitude = m;
|
|
367
|
+
return {
|
|
368
|
+
origins: Float32Array.from(o),
|
|
369
|
+
vectors: Float32Array.from(vs),
|
|
370
|
+
magnitudes: Float32Array.from(mags),
|
|
371
|
+
maxMagnitude,
|
|
372
|
+
count: mags.length,
|
|
373
|
+
skipped,
|
|
374
|
+
progress: progress(slabs, nz, reason),
|
|
375
|
+
};
|
|
376
|
+
}
|