@woosh/meep-engine 2.36.0 → 2.36.4
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/core/collection/queue/Deque.js +1 -1
- package/core/color/Color.d.ts +32 -2
- package/core/color/Color.js +31 -18
- package/core/geom/AABB2.d.ts +2 -0
- package/core/geom/Vector3.js +5 -1
- package/core/process/undo/ActionProcessor.js +31 -3
- package/core/process/undo/ActionProcessor.spec.js +1 -1
- package/engine/animation/curve/AnimationCurve.js +40 -1
- package/engine/animation/curve/Keyframe.js +19 -0
- package/engine/animation/curve/compression/animation_curve_to_float_array.js +35 -0
- package/engine/animation/curve/compression/downsample_float_array.js +50 -0
- package/engine/animation/curve/compression/downsample_float_array_curve_by_error.js +147 -0
- package/engine/animation/curve/compression/downsample_float_array_curve_by_error.spec.js +47 -0
- package/engine/animation/curve/compression/prototypeCurveCompression.js +154 -0
- package/engine/animation/curve/draw/build_curve_editor.js +316 -0
- package/engine/animation/curve/draw/build_plot_entity_from_array.js +47 -0
- package/engine/animation/curve/draw/build_tangent_editor.js +115 -0
- package/engine/animation/curve/draw/draw_grid.js +27 -0
- package/engine/animation/curve/draw/draw_label.js +17 -0
- package/engine/animation/curve/draw/plot_array.js +76 -0
- package/engine/animation/curve/draw/plot_data.js +49 -0
- package/engine/ecs/parent/EntityNode.d.ts +7 -3
- package/engine/ecs/parent/EntityNode.js +21 -0
- package/engine/ecs/parent/entity_node_compute_bounding_box.js +41 -0
- package/engine/graphics/ecs/highlight/HighlightDefinition.d.ts +11 -1
- package/engine/graphics/ecs/highlight/HighlightDefinition.js +18 -8
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +5 -1
- package/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +122 -0
- package/engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.js +148 -0
- package/engine/graphics/ecs/mesh-v2/render/adapters/InstancedRendererAdapter.js +2 -0
- package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.d.ts +4 -0
- package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +34 -3
- package/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.spec.js +24 -0
- package/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +10 -0
- package/engine/graphics/micron/prototypeVirtualGeometry.js +5 -3
- package/engine/graphics/micron/render/v1/MicronRenderContext.js +0 -6
- package/engine/graphics/render/forward_plus/LightManager.js +8 -6
- package/engine/graphics/render/forward_plus/data/channelCountToThreIntegerTextureType.js +3 -3
- package/engine/graphics/texture/channelCountToThreeTextureFormat.js +2 -2
- package/engine/ui/DraggableAspect.js +4 -1
- package/package.json +1 -1
package/core/color/Color.d.ts
CHANGED
|
@@ -7,7 +7,37 @@ export class Color {
|
|
|
7
7
|
g: number
|
|
8
8
|
b: number
|
|
9
9
|
|
|
10
|
-
onChanged: Signal
|
|
10
|
+
readonly onChanged: Signal<number, number, number, number, number, number>
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
parse(text: string): void
|
|
13
|
+
|
|
14
|
+
setRGB(r: number, g: number, b: number): void
|
|
15
|
+
|
|
16
|
+
setHSL(h: number, s: number, l: number): void
|
|
17
|
+
|
|
18
|
+
setHCL(h: number, c: number, l: number): void
|
|
19
|
+
|
|
20
|
+
setHSI(h: number, s: number, i: number): void
|
|
21
|
+
|
|
22
|
+
setHSV(h: number, s: number, v: number): void
|
|
23
|
+
|
|
24
|
+
getHSV(): { h: number, s: number, v: number }
|
|
25
|
+
|
|
26
|
+
toUint(): number
|
|
27
|
+
|
|
28
|
+
fromUint(v: number): void
|
|
29
|
+
|
|
30
|
+
equals(other: Color): boolean
|
|
31
|
+
|
|
32
|
+
hash(): number
|
|
33
|
+
|
|
34
|
+
copy(other: Color): void
|
|
35
|
+
|
|
36
|
+
clone(): Color
|
|
37
|
+
|
|
38
|
+
fromJSON(json: any): void
|
|
39
|
+
|
|
40
|
+
toJSON(): any
|
|
41
|
+
|
|
42
|
+
writeToArray(destination: ArrayLike<number>, destination_offset: number): void
|
|
13
43
|
}
|
package/core/color/Color.js
CHANGED
|
@@ -10,7 +10,7 @@ import { computeHashFloat } from "../math/hash/computeHashFloat.js";
|
|
|
10
10
|
import { rgb2hsv } from "./rgb2hsv.js";
|
|
11
11
|
import { rgb2uint24 } from "./rgb2uint24.js";
|
|
12
12
|
|
|
13
|
-
class Color {
|
|
13
|
+
export class Color {
|
|
14
14
|
/**
|
|
15
15
|
*
|
|
16
16
|
* @param {number} r value from 0 to 1
|
|
@@ -37,6 +37,10 @@ class Color {
|
|
|
37
37
|
*/
|
|
38
38
|
this.b = b;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* @readonly
|
|
42
|
+
* @type {Signal<number,number,number,number,number,number>}
|
|
43
|
+
*/
|
|
40
44
|
this.onChanged = new Signal();
|
|
41
45
|
}
|
|
42
46
|
|
|
@@ -384,30 +388,39 @@ class Color {
|
|
|
384
388
|
this.setRGB(r, g, b);
|
|
385
389
|
}
|
|
386
390
|
|
|
391
|
+
/**
|
|
392
|
+
*
|
|
393
|
+
* @param {string} str CSS-style string
|
|
394
|
+
* @returns {void}
|
|
395
|
+
*/
|
|
387
396
|
parse(str) {
|
|
388
397
|
const c = parseColor(str);
|
|
389
398
|
|
|
390
399
|
this.setRGB(c[0] / 255, c[1] / 255, c[2] / 255);
|
|
391
400
|
}
|
|
392
|
-
}
|
|
393
401
|
|
|
394
|
-
/**
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
402
|
+
/**
|
|
403
|
+
*
|
|
404
|
+
* @param {number} r
|
|
405
|
+
* @param {number} g
|
|
406
|
+
* @param {number} b
|
|
407
|
+
* @returns {Color}
|
|
408
|
+
*/
|
|
409
|
+
static fromRGB(r, g, b) {
|
|
410
|
+
return new Color(r, g, b);
|
|
411
|
+
}
|
|
404
412
|
|
|
405
|
-
Color.parse = function (str) {
|
|
406
|
-
const r = new Color();
|
|
407
413
|
|
|
408
|
-
|
|
414
|
+
/**
|
|
415
|
+
*
|
|
416
|
+
* @param {string} str
|
|
417
|
+
* @return {Color}
|
|
418
|
+
*/
|
|
419
|
+
static parse(str) {
|
|
420
|
+
const r = new Color();
|
|
409
421
|
|
|
410
|
-
|
|
411
|
-
};
|
|
422
|
+
r.parse(str);
|
|
412
423
|
|
|
413
|
-
|
|
424
|
+
return r;
|
|
425
|
+
};
|
|
426
|
+
}
|
package/core/geom/AABB2.d.ts
CHANGED
package/core/geom/Vector3.js
CHANGED
|
@@ -1003,6 +1003,11 @@ Vector3.typeName = "Vector3";
|
|
|
1003
1003
|
Vector3._dot = v3_dot;
|
|
1004
1004
|
|
|
1005
1005
|
|
|
1006
|
+
/**
|
|
1007
|
+
*
|
|
1008
|
+
* @param {{x:number, y:number, z:number}} v
|
|
1009
|
+
* @return {number}
|
|
1010
|
+
*/
|
|
1006
1011
|
function v3Length(v) {
|
|
1007
1012
|
return v3_length(v.x, v.y, v.z);
|
|
1008
1013
|
}
|
|
@@ -1016,7 +1021,6 @@ function v3Length(v) {
|
|
|
1016
1021
|
* @param {number} fraction
|
|
1017
1022
|
*/
|
|
1018
1023
|
function v3Lerp(result, a, b, fraction) {
|
|
1019
|
-
|
|
1020
1024
|
v3_lerp(result, a.x, a.y, a.z, b.x, b.y, b.z, fraction);
|
|
1021
1025
|
}
|
|
1022
1026
|
|
|
@@ -213,6 +213,28 @@ export class ActionProcessor {
|
|
|
213
213
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
/**
|
|
217
|
+
*
|
|
218
|
+
* @param {M} descriptor
|
|
219
|
+
* @returns {number[]}
|
|
220
|
+
* @private
|
|
221
|
+
*/
|
|
222
|
+
__find_mark_indices_by_description(descriptor) {
|
|
223
|
+
const result = [];
|
|
224
|
+
|
|
225
|
+
const marks = this.marks;
|
|
226
|
+
const mark_count = marks.length;
|
|
227
|
+
for (let i = 0; i < mark_count; i++) {
|
|
228
|
+
const mark = marks[i];
|
|
229
|
+
|
|
230
|
+
if (mark.description === descriptor) {
|
|
231
|
+
result.push(i);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
|
|
216
238
|
/**
|
|
217
239
|
* Navigate to a specific mark back or forward
|
|
218
240
|
* @param {M} mark_descriptor specific mark description
|
|
@@ -221,7 +243,7 @@ export class ActionProcessor {
|
|
|
221
243
|
*/
|
|
222
244
|
navigateTo(mark_descriptor) {
|
|
223
245
|
// find all matching marks
|
|
224
|
-
const matching_marks = this.
|
|
246
|
+
const matching_marks = this.__find_mark_indices_by_description(mark_descriptor);
|
|
225
247
|
|
|
226
248
|
if (matching_marks.length === 0) {
|
|
227
249
|
// not found
|
|
@@ -232,9 +254,15 @@ export class ActionProcessor {
|
|
|
232
254
|
throw new Error(`Attempting to navigate to a non-unique mark descriptor '${mark_descriptor}'. Mark descriptor must be unique, instead ${matching_marks.length} matches were found`);
|
|
233
255
|
}
|
|
234
256
|
|
|
235
|
-
const
|
|
257
|
+
const match_index = matching_marks[0];
|
|
236
258
|
|
|
237
|
-
|
|
259
|
+
let target;
|
|
260
|
+
if (match_index + 1 >= this.marks.length) {
|
|
261
|
+
// mark is already at the end, go to the end of the history
|
|
262
|
+
target = this.history.length;
|
|
263
|
+
} else {
|
|
264
|
+
target = this.marks[match_index + 1].index;
|
|
265
|
+
}
|
|
238
266
|
|
|
239
267
|
let temp_cursor = this.cursor;
|
|
240
268
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inverseLerp } from "../../../core/math/inverseLerp.js";
|
|
2
|
+
import { lerp } from "../../../core/math/lerp.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
*
|
|
@@ -32,12 +33,18 @@ function evaluate(t, keyframe0, keyframe1) {
|
|
|
32
33
|
export class AnimationCurve {
|
|
33
34
|
constructor() {
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
+
* @readonly
|
|
36
37
|
* @type {Keyframe[]}
|
|
37
38
|
*/
|
|
38
39
|
this.keys = [];
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
*
|
|
45
|
+
* @param {number} t
|
|
46
|
+
* @return {number}
|
|
47
|
+
*/
|
|
41
48
|
evaluate(t) {
|
|
42
49
|
const keys = this.keys;
|
|
43
50
|
const key_count = keys.length;
|
|
@@ -69,4 +76,36 @@ export class AnimationCurve {
|
|
|
69
76
|
return keys[key_count - 1].value;
|
|
70
77
|
}
|
|
71
78
|
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
*
|
|
82
|
+
* @param {number} index
|
|
83
|
+
* @param {number} weight
|
|
84
|
+
*/
|
|
85
|
+
smoothTangents(index, weight) {
|
|
86
|
+
const keys = this.keys;
|
|
87
|
+
|
|
88
|
+
const key_main = keys[index];
|
|
89
|
+
|
|
90
|
+
const has_previous = index > 0;
|
|
91
|
+
if (has_previous) {
|
|
92
|
+
const key_previous = keys[index - 1];
|
|
93
|
+
|
|
94
|
+
key_main.inTangent = key_main.value - key_previous.value;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const has_next = index < keys.length - 1;
|
|
98
|
+
if (has_next) {
|
|
99
|
+
const key_next = keys[index + 1];
|
|
100
|
+
|
|
101
|
+
key_main.outTangent = key_next.value - key_main.value;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (has_previous && has_next) {
|
|
105
|
+
const average = lerp(key_main.inTangent, key_main.outTangent, 0.5);
|
|
106
|
+
key_main.inTangent = average;
|
|
107
|
+
key_main.outTangent = average;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
}
|
|
72
111
|
}
|
|
@@ -24,4 +24,23 @@ export class Keyframe {
|
|
|
24
24
|
*/
|
|
25
25
|
this.outTangent = 0;
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
* @param {number} time
|
|
31
|
+
* @param {number} value
|
|
32
|
+
* @param {number} inTangent
|
|
33
|
+
* @param {number} outTangent
|
|
34
|
+
* @return {Keyframe}
|
|
35
|
+
*/
|
|
36
|
+
static from(time, value, inTangent = 0, outTangent = 0) {
|
|
37
|
+
const r = new Keyframe();
|
|
38
|
+
|
|
39
|
+
r.time = time;
|
|
40
|
+
r.value = value;
|
|
41
|
+
r.inTangent = inTangent;
|
|
42
|
+
r.outTangent = outTangent;
|
|
43
|
+
|
|
44
|
+
return r;
|
|
45
|
+
}
|
|
27
46
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sample entire animation curve into a flat array with discrete equal intervals
|
|
6
|
+
* @param {number[]|Float32Array} out
|
|
7
|
+
* @param {number} out_offset
|
|
8
|
+
* @param {number} sample_count
|
|
9
|
+
* @param {AnimationCurve} curve
|
|
10
|
+
*/
|
|
11
|
+
export function animation_curve_to_float_array(out, curve, out_offset = 0, sample_count = out.length) {
|
|
12
|
+
const keyframes = curve.keys;
|
|
13
|
+
const keyframe_count = keyframes.length;
|
|
14
|
+
|
|
15
|
+
if (keyframe_count === 0) {
|
|
16
|
+
// not data
|
|
17
|
+
out.fill(0, out_offset, out_offset + sample_count);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const key_first = keyframes[0];
|
|
22
|
+
const key_last = keyframes[keyframe_count - 1];
|
|
23
|
+
|
|
24
|
+
const time_start = key_first.time;
|
|
25
|
+
const time_end = key_last.time;
|
|
26
|
+
const time_span = time_end - time_start;
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i < sample_count; i++) {
|
|
29
|
+
const f = i / (sample_count - 1);
|
|
30
|
+
|
|
31
|
+
const t = time_start + (f * time_span);
|
|
32
|
+
|
|
33
|
+
out[i + out_offset] = curve.evaluate(t);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//
|
|
2
|
+
|
|
3
|
+
import { assert } from "../../../../core/assert.js";
|
|
4
|
+
import { lerp } from "../../../../core/math/lerp.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param {number[]|Float32Array} output
|
|
9
|
+
* @param {number} output_offset
|
|
10
|
+
* @param {number[]|Float32Array} input
|
|
11
|
+
* @param {number} input_offset
|
|
12
|
+
* @param {number} input_size
|
|
13
|
+
*/
|
|
14
|
+
export function downsample_float_array(output, output_offset, input, input_offset, input_size) {
|
|
15
|
+
assert.isNonNegativeInteger(output_offset, 'output_offset');
|
|
16
|
+
assert.isNonNegativeInteger(input_offset, 'input_offset');
|
|
17
|
+
assert.isNonNegativeInteger(input_size, 'input_size');
|
|
18
|
+
|
|
19
|
+
const target_samples = input_size / 2;
|
|
20
|
+
|
|
21
|
+
if (!Number.isInteger(target_samples)) {
|
|
22
|
+
throw new Error(`input_size(=${input_size}) is not divisible by 2`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (target_samples === 1) {
|
|
26
|
+
// special case, take average
|
|
27
|
+
output[output_offset] = (
|
|
28
|
+
input[input_offset] + input[input_offset + 1]
|
|
29
|
+
) / 2;
|
|
30
|
+
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let i = 0;
|
|
35
|
+
for (; i < target_samples; i++) {
|
|
36
|
+
|
|
37
|
+
const f = i / (target_samples - 1);
|
|
38
|
+
|
|
39
|
+
const input_sample_offset = input_offset + i * 2;
|
|
40
|
+
|
|
41
|
+
const s0 = input[input_sample_offset];
|
|
42
|
+
const s1 = input[input_sample_offset + 1];
|
|
43
|
+
|
|
44
|
+
const sample_value = lerp(s0, s1, f);
|
|
45
|
+
|
|
46
|
+
// TODO implement decision scheme that would pick between min/max/average
|
|
47
|
+
|
|
48
|
+
output[output_offset + i] = sample_value;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
//
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { max2 } from "../../../../core/math/max2.js";
|
|
5
|
+
import { lerp } from "../../../../core/math/lerp.js";
|
|
6
|
+
import { min3 } from "../../../../core/math/min3.js";
|
|
7
|
+
import { max3 } from "../../../../core/math/max3.js";
|
|
8
|
+
import { downsample_float_array } from "./downsample_float_array.js";
|
|
9
|
+
import { array_copy } from "../../../../core/collection/array/copyArray.js";
|
|
10
|
+
import { isPowerOfTwo } from "../../../../core/math/isPowerOrTwo.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param {number[]} input
|
|
15
|
+
* @param {number} input_offset
|
|
16
|
+
* @param {number} input_size
|
|
17
|
+
* @param {number} f
|
|
18
|
+
* @return {number}
|
|
19
|
+
*/
|
|
20
|
+
function array_sampler_linear(input, input_offset, input_size, f) {
|
|
21
|
+
const t = f * (input_size - 1);
|
|
22
|
+
|
|
23
|
+
const fraction = t % 1;
|
|
24
|
+
|
|
25
|
+
if (fraction === 0) {
|
|
26
|
+
return input[input_offset + t];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const i0 = Math.floor(t);
|
|
30
|
+
const i1 = i0 + 1;
|
|
31
|
+
|
|
32
|
+
const s0 = input[input_offset + i0];
|
|
33
|
+
const s1 = input[input_offset + i1];
|
|
34
|
+
|
|
35
|
+
return lerp(s0, s1, fraction);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @param {number[]|Float32Array} input0
|
|
41
|
+
* @param {number} input0_offset
|
|
42
|
+
* @param {number} input0_size
|
|
43
|
+
* @param {number[]|Float32Array} input1
|
|
44
|
+
* @param {number} input1_offset
|
|
45
|
+
* @param {number} input1_size
|
|
46
|
+
*/
|
|
47
|
+
function compute_float_array_curve_linear_sampling_relative_error(
|
|
48
|
+
input0, input0_offset, input0_size,
|
|
49
|
+
input1, input1_offset, input1_size
|
|
50
|
+
) {
|
|
51
|
+
|
|
52
|
+
const largest_size = max2(input0_size, input1_size);
|
|
53
|
+
|
|
54
|
+
let max_error = 0;
|
|
55
|
+
|
|
56
|
+
let min = Number.POSITIVE_INFINITY;
|
|
57
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
58
|
+
|
|
59
|
+
for (let i = 0; i < largest_size; i++) {
|
|
60
|
+
const f = i / (largest_size - 1);
|
|
61
|
+
|
|
62
|
+
const s0 = array_sampler_linear(input0, input0_offset, input0_size, f);
|
|
63
|
+
const s1 = array_sampler_linear(input1, input1_offset, input1_size, f);
|
|
64
|
+
|
|
65
|
+
const error = Math.abs(s0 - s1);
|
|
66
|
+
|
|
67
|
+
if (error > max_error) {
|
|
68
|
+
max_error = error;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
min = min3(min, s0, s1);
|
|
72
|
+
max = max3(max, s0, s1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const span = max - min;
|
|
76
|
+
|
|
77
|
+
if (span === 0) {
|
|
78
|
+
// avoid division by 0
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return max_error / span;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
*
|
|
87
|
+
* @param {number[]} output
|
|
88
|
+
* @param {number} output_offset
|
|
89
|
+
* @param {number[]} input
|
|
90
|
+
* @param {number} input_offset
|
|
91
|
+
* @param {number} input_size
|
|
92
|
+
* @param {number} [error]
|
|
93
|
+
* @return {number}
|
|
94
|
+
*/
|
|
95
|
+
export function downsample_float_array_curve_by_error(output, output_offset, input, input_offset, input_size, error = 0.03) {
|
|
96
|
+
if (!isPowerOfTwo(input_size)) {
|
|
97
|
+
throw new Error(`Input size must be a power of two, instead was '${input_size}'`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const buffer_0 = new Float32Array(input_size >>> 1); // first step needs half the size of the original buffer
|
|
101
|
+
const buffer_1 = new Float32Array(input_size >>> 2); // next step needs quarter
|
|
102
|
+
|
|
103
|
+
let current = input;
|
|
104
|
+
let current_offset = input_offset;
|
|
105
|
+
let current_size = input_size;
|
|
106
|
+
|
|
107
|
+
let next = buffer_0;
|
|
108
|
+
let next_offset = 0;
|
|
109
|
+
let next_size = buffer_0.length;
|
|
110
|
+
|
|
111
|
+
let current_error = 0;
|
|
112
|
+
|
|
113
|
+
while (current_error <= error && current_size > 1) {
|
|
114
|
+
downsample_float_array(next, next_offset, current, current_offset, current_size);
|
|
115
|
+
|
|
116
|
+
current_error = compute_float_array_curve_linear_sampling_relative_error(
|
|
117
|
+
input, input_offset, input_size,
|
|
118
|
+
next, next_offset, next_size
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (current_error > error) {
|
|
122
|
+
// reached best possible reduction
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
current = next;
|
|
127
|
+
current_offset = next_offset;
|
|
128
|
+
current_size = next_size;
|
|
129
|
+
|
|
130
|
+
// perform swap
|
|
131
|
+
if (next === buffer_0) {
|
|
132
|
+
next = buffer_1;
|
|
133
|
+
} else {
|
|
134
|
+
next = buffer_0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
next_offset = 0;
|
|
138
|
+
// next will be half the current size
|
|
139
|
+
next_size = current_size >>> 1;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
// copy current buffer to output
|
|
144
|
+
array_copy(current, current_offset, output, output_offset, current_size);
|
|
145
|
+
|
|
146
|
+
return current_size;
|
|
147
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { downsample_float_array_curve_by_error } from "./downsample_float_array_curve_by_error.js";
|
|
2
|
+
|
|
3
|
+
test('1 constant to 1', () => {
|
|
4
|
+
const output = [];
|
|
5
|
+
|
|
6
|
+
const sample_count = downsample_float_array_curve_by_error(output, 0, [1], 0, 1, 0.1);
|
|
7
|
+
|
|
8
|
+
expect(sample_count).toBe(1);
|
|
9
|
+
expect(output[0]).toBe(1);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('4 constants to 1', () => {
|
|
13
|
+
const output = [];
|
|
14
|
+
|
|
15
|
+
const sample_count = downsample_float_array_curve_by_error(output, 0, [1, 1, 1, 1], 0, 4, 0.1);
|
|
16
|
+
|
|
17
|
+
expect(sample_count).toBe(1);
|
|
18
|
+
expect(output[0]).toBe(1);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('2 constants to 1 with error = 0', () => {
|
|
22
|
+
const output = [];
|
|
23
|
+
|
|
24
|
+
const sample_count = downsample_float_array_curve_by_error(output, 0, [1, 1], 0, 2, 0);
|
|
25
|
+
|
|
26
|
+
expect(sample_count).toBe(1);
|
|
27
|
+
expect(output[0]).toBe(1);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('2 different constants to 2', () => {
|
|
31
|
+
const output = [];
|
|
32
|
+
|
|
33
|
+
const sample_count = downsample_float_array_curve_by_error(output, 0, [1, 2], 0, 2, 0.1);
|
|
34
|
+
|
|
35
|
+
expect(sample_count).toBe(2);
|
|
36
|
+
expect(output).toEqual([1, 2]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('4 to 2, linear scale compression', () => {
|
|
40
|
+
const output = [];
|
|
41
|
+
|
|
42
|
+
const sample_count = downsample_float_array_curve_by_error(output, 0, [1, 2, 3, 4], 0, 4, 0.1);
|
|
43
|
+
|
|
44
|
+
expect(sample_count).toBe(2);
|
|
45
|
+
expect(output[0]).toBeCloseTo(1);
|
|
46
|
+
expect(output[1]).toBeCloseTo(4);
|
|
47
|
+
});
|