@woosh/meep-engine 2.110.8 → 2.110.10
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/build/bundle-worker-terrain.js +1 -1
- package/build/meep.cjs +116 -107
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +116 -107
- package/editor/tools/v2/TransformControls.js +11 -6
- package/package.json +2 -1
- package/src/core/UUID.d.ts +3 -0
- package/src/core/UUID.d.ts.map +1 -1
- package/src/core/UUID.js +9 -7
- package/src/core/collection/array/arraySetDiff.d.ts.map +1 -1
- package/src/core/collection/array/arraySetDiff.js +0 -1
- package/src/core/collection/array/binarySearchHighIndex.d.ts.map +1 -1
- package/src/core/collection/array/binarySearchHighIndex.js +7 -1
- package/src/core/collection/array/computeHashArray.d.ts.map +1 -1
- package/src/core/collection/array/computeHashArray.js +3 -2
- package/src/core/collection/array/fastArrayEquals.d.ts +1 -0
- package/src/core/collection/array/fastArrayEquals.d.ts.map +1 -1
- package/src/core/collection/array/fastArrayEquals.js +1 -0
- package/src/core/collection/array/isArrayEqual.d.ts.map +1 -1
- package/src/core/collection/array/isArrayEqual.js +9 -12
- package/src/core/collection/array/isArrayEqual.spec.d.ts +2 -0
- package/src/core/collection/array/isArrayEqual.spec.d.ts.map +1 -0
- package/src/core/collection/array/isArrayEqual.spec.js +25 -0
- package/src/core/collection/array/typed/is_typed_array_equals.d.ts.map +1 -1
- package/src/core/collection/array/typed/is_typed_array_equals.js +1 -0
- package/src/core/function/makeSequenceLoop.d.ts.map +1 -0
- package/src/core/geom/2d/aabb/AABB2.d.ts.map +1 -1
- package/src/core/geom/2d/aabb/AABB2.js +27 -1
- package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/AbstractShape3D.js +14 -0
- package/src/core/geom/3d/shape/PointShape3D.d.ts +9 -0
- package/src/core/geom/3d/shape/PointShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/PointShape3D.js +14 -0
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/TransformedShape3D.js +26 -8
- package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnionShape3D.js +33 -4
- package/src/core/geom/3d/shape/UnitCubeShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnitCubeShape3D.js +13 -6
- package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnitSphereShape3D.js +10 -0
- package/src/core/geom/3d/tetrahedra/TetrahedralMesh.d.ts +15 -8
- package/src/core/geom/3d/tetrahedra/TetrahedralMesh.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/TetrahedralMesh.js +33 -9
- package/src/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.d.ts +1 -1
- package/src/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +7 -7
- package/src/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_build_from_grid.spec.d.ts +2 -0
- package/src/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_build_from_grid.spec.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_build_from_grid.spec.js +61 -0
- package/src/core/geom/3d/tetrahedra/get_tetrahedron_volume.d.ts +9 -0
- package/src/core/geom/3d/tetrahedra/get_tetrahedron_volume.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/get_tetrahedron_volume.js +18 -0
- package/src/core/geom/3d/tetrahedra/is_tetrahedron_degenerate.d.ts +9 -0
- package/src/core/geom/3d/tetrahedra/is_tetrahedron_degenerate.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/is_tetrahedron_degenerate.js +21 -0
- package/src/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.d.ts +2 -10
- package/src/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/validate_tetrahedral_mesh.js +3 -40
- package/src/core/geom/3d/v3_compute_triangle_normal.js +1 -1
- package/src/core/geom/3d/v3_negate_array.d.ts +9 -0
- package/src/core/geom/3d/v3_negate_array.d.ts.map +1 -0
- package/src/core/geom/3d/v3_negate_array.js +16 -0
- package/src/core/geom/Quaternion.d.ts.map +1 -1
- package/src/core/geom/Quaternion.js +30 -77
- package/src/core/geom/Vector3.d.ts +4 -0
- package/src/core/geom/Vector3.js +4 -4
- package/src/core/geom/mat3/m3_cm_invert.d.ts +7 -0
- package/src/core/geom/mat3/m3_cm_invert.d.ts.map +1 -0
- package/src/core/geom/mat3/m3_cm_invert.js +45 -0
- package/src/core/geom/vec2/v2_matrix3_cm_multiply.d.ts +10 -0
- package/src/core/geom/vec2/v2_matrix3_cm_multiply.d.ts.map +1 -0
- package/src/core/geom/vec2/v2_matrix3_cm_multiply.js +20 -0
- package/src/core/geom/vec2/v2_matrix3_rm_multiply.d.ts +10 -0
- package/src/core/geom/vec2/v2_matrix3_rm_multiply.d.ts.map +1 -0
- package/src/core/geom/vec2/v2_matrix3_rm_multiply.js +21 -0
- package/src/core/geom/vec3/v3_cross_array.d.ts +11 -0
- package/src/core/geom/vec3/v3_cross_array.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_cross_array.js +31 -0
- package/src/core/geom/vec3/v3_displace_in_direction.js +3 -2
- package/src/core/geom/vec3/v3_displace_in_direction_array.d.ts +12 -0
- package/src/core/geom/vec3/v3_displace_in_direction_array.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_displace_in_direction_array.js +22 -0
- package/src/core/geom/vec3/v3_dot_array_array.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_dot_array_array.js +4 -1
- package/src/core/geom/vec3/v3_normalize_array.d.ts +3 -3
- package/src/core/geom/vec3/v3_normalize_array.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_normalize_array.js +3 -3
- package/src/core/math/copysign.d.ts +1 -1
- package/src/core/math/copysign.js +1 -1
- package/src/core/math/epsilonEquals.d.ts.map +1 -1
- package/src/core/math/epsilonEquals.js +3 -2
- package/src/core/math/linalg/README.md +1 -1
- package/src/core/math/random/randomGaussian.spec.js +1 -1
- package/src/core/math/spline/spline_hermite3.d.ts +1 -1
- package/src/core/math/spline/spline_hermite3.js +3 -2
- package/src/core/math/spline/spline_hermite3_to_bezier.d.ts +12 -0
- package/src/core/math/spline/spline_hermite3_to_bezier.d.ts.map +1 -0
- package/src/core/math/spline/spline_hermite3_to_bezier.js +24 -0
- package/src/core/process/delay.d.ts +6 -1
- package/src/core/process/delay.d.ts.map +1 -1
- package/src/core/process/delay.js +6 -1
- package/src/core/process/undo/Mark.d.ts +5 -5
- package/src/core/process/undo/Mark.d.ts.map +1 -1
- package/src/core/process/undo/Mark.js +6 -5
- package/src/core/process/worker/WorkerBuilder.d.ts +13 -2
- package/src/core/process/worker/WorkerBuilder.d.ts.map +1 -1
- package/src/core/process/worker/WorkerBuilder.js +8 -0
- package/src/core/process/worker/WorkerProxy.d.ts +16 -6
- package/src/core/process/worker/WorkerProxy.d.ts.map +1 -1
- package/src/core/process/worker/WorkerProxy.js +24 -5
- package/src/engine/EngineHarness.d.ts +7 -1
- package/src/engine/EngineHarness.d.ts.map +1 -1
- package/src/engine/EngineHarness.js +12 -1
- package/src/engine/animation/clip/AnimationTrack.d.ts.map +1 -1
- package/src/engine/animation/clip/AnimationTrack.js +4 -1
- package/src/engine/animation/clip/bind_property_writer.d.ts +2 -2
- package/src/engine/animation/clip/bind_property_writer.d.ts.map +1 -1
- package/src/engine/animation/clip/bind_property_writer.js +22 -13
- package/src/engine/animation/clip/curve_from_track_data.d.ts.map +1 -1
- package/src/engine/animation/clip/curve_from_track_data.js +9 -1
- package/src/engine/animation/clip/ecd_bind_animation_curve.d.ts.map +1 -1
- package/src/engine/animation/clip/ecd_bind_animation_curve.js +6 -5
- package/src/engine/animation/curve/AnimationCurve.d.ts +32 -0
- package/src/engine/animation/curve/AnimationCurve.d.ts.map +1 -1
- package/src/engine/animation/curve/AnimationCurve.js +94 -19
- package/src/engine/animation/curve/AnimationCurve.spec.js +67 -0
- package/src/engine/animation/curve/Keyframe.d.ts +24 -3
- package/src/engine/animation/curve/Keyframe.d.ts.map +1 -1
- package/src/engine/animation/curve/Keyframe.js +49 -3
- package/src/engine/animation/curve/Keyframe.spec.js +11 -0
- package/src/engine/animation/curve/animation_curve_compute_aabb.d.ts +7 -0
- package/src/engine/animation/curve/animation_curve_compute_aabb.d.ts.map +1 -0
- package/src/engine/animation/curve/{compute_curve_aabb.js → animation_curve_compute_aabb.js} +1 -1
- package/src/engine/animation/curve/animation_curve_optimize.d.ts +8 -0
- package/src/engine/animation/curve/animation_curve_optimize.d.ts.map +1 -0
- package/src/engine/animation/curve/animation_curve_optimize.js +89 -0
- package/src/engine/animation/curve/animation_curve_optimize.spec.d.ts +2 -0
- package/src/engine/animation/curve/animation_curve_optimize.spec.d.ts.map +1 -0
- package/src/engine/animation/curve/animation_curve_optimize.spec.js +50 -0
- package/src/engine/animation/curve/draw/build_curve_editor.d.ts.map +1 -1
- package/src/engine/animation/curve/draw/build_curve_editor.js +17 -11
- package/src/engine/animation/curve/evaluate_two_key_curve.d.ts +9 -0
- package/src/engine/animation/curve/evaluate_two_key_curve.d.ts.map +1 -0
- package/src/engine/animation/curve/evaluate_two_key_curve.js +23 -0
- package/src/engine/animation/curve/prototypeGLTF.js +14 -14
- package/src/engine/animation/curve/view/AnimationCurveView.d.ts +47 -0
- package/src/engine/animation/curve/view/AnimationCurveView.d.ts.map +1 -0
- package/src/engine/animation/curve/view/AnimationCurveView.js +343 -0
- package/src/engine/animation/curve/view/prototype.d.ts +2 -0
- package/src/engine/animation/curve/view/prototype.d.ts.map +1 -0
- package/src/engine/animation/curve/view/prototype.js +86 -0
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.d.ts +6 -1
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.d.ts.map +1 -1
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.js +11 -6
- package/src/engine/ecs/guid/{GUID.d.ts → UUID.d.ts} +27 -15
- package/src/engine/ecs/guid/UUID.d.ts.map +1 -0
- package/src/engine/ecs/guid/{GUID.js → UUID.js} +52 -17
- package/src/engine/ecs/guid/UUID.spec.d.ts +2 -0
- package/src/engine/ecs/guid/UUID.spec.d.ts.map +1 -0
- package/src/engine/ecs/guid/{GUID.spec.js → UUID.spec.js} +25 -12
- package/src/engine/ecs/guid/UUIDSerializationAdapter.d.ts +18 -0
- package/src/engine/ecs/guid/UUIDSerializationAdapter.d.ts.map +1 -0
- package/src/engine/ecs/guid/{GUIDSerializationAdapter.js → UUIDSerializationAdapter.js} +5 -5
- package/src/engine/ecs/guid/UUIDSerializationAdapter.spec.d.ts +2 -0
- package/src/engine/ecs/guid/UUIDSerializationAdapter.spec.d.ts.map +1 -0
- package/src/engine/ecs/guid/{GUIDSerializationAdapter.spec.js → UUIDSerializationAdapter.spec.js} +5 -5
- package/src/engine/ecs/ik/InverseKinematics.js +3 -3
- package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.d.ts.map +1 -1
- package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +1 -140
- package/src/engine/input/ecs/systems/InputControllerSystem.js +1 -1
- package/src/engine/intelligence/blackboard/BlackboardDynamicStorageAdapter.d.ts +1 -1
- package/src/engine/intelligence/blackboard/BlackboardDynamicStorageAdapter.js +4 -4
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts +13 -0
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +1 -0
- package/src/engine/physics/gjk/expanding_polytope_algorithm.js +395 -0
- package/src/engine/physics/gjk/expanding_polytope_algorithm.spec.d.ts +2 -0
- package/src/engine/physics/gjk/expanding_polytope_algorithm.spec.d.ts.map +1 -0
- package/src/engine/physics/gjk/expanding_polytope_algorithm.spec.js +46 -0
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik3d_solve_primitive.d.ts +18 -0
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik3d_solve_primitive.d.ts.map +1 -0
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik3d_solve_primitive.js +277 -0
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik3d_solve_primitive.spec.d.ts +2 -0
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik3d_solve_primitive.spec.d.ts.map +1 -0
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik3d_solve_primitive.spec.js +43 -0
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik_solve.d.ts +12 -0
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik_solve.d.ts.map +1 -0
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik_solve.js +100 -0
- package/src/engine/physics/inverse_kinematics/fabrik/prototype.d.ts +2 -0
- package/src/engine/physics/inverse_kinematics/fabrik/prototype.d.ts.map +1 -0
- package/src/engine/physics/inverse_kinematics/fabrik/prototype.js +112 -0
- package/src/engine/physics/inverse_kinematics/two_joint_ik.d.ts +16 -0
- package/src/engine/physics/inverse_kinematics/two_joint_ik.d.ts.map +1 -0
- package/src/engine/physics/inverse_kinematics/two_joint_ik.js +127 -0
- package/src/view/elements/DropDownSelectionView.js +2 -2
- package/src/view/elements/tiles2d/Tile.d.ts +1 -1
- package/src/view/elements/tiles2d/Tile.d.ts.map +1 -1
- package/src/view/elements/tiles2d/Tile.js +3 -3
- package/src/core/math/makeSequenceLoop.d.ts.map +0 -1
- package/src/engine/animation/curve/compute_curve_aabb.d.ts +0 -7
- package/src/engine/animation/curve/compute_curve_aabb.d.ts.map +0 -1
- package/src/engine/ecs/guid/GUID.d.ts.map +0 -1
- package/src/engine/ecs/guid/GUID.spec.d.ts +0 -2
- package/src/engine/ecs/guid/GUID.spec.d.ts.map +0 -1
- package/src/engine/ecs/guid/GUIDSerializationAdapter.d.ts +0 -18
- package/src/engine/ecs/guid/GUIDSerializationAdapter.d.ts.map +0 -1
- package/src/engine/ecs/guid/GUIDSerializationAdapter.spec.d.ts +0 -2
- package/src/engine/ecs/guid/GUIDSerializationAdapter.spec.d.ts.map +0 -1
- /package/src/core/{math → function}/makeSequenceLoop.d.ts +0 -0
- /package/src/core/{math → function}/makeSequenceLoop.js +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import AABB2 from "../../../core/geom/2d/aabb/AABB2.js";
|
|
3
|
+
import { animation_curve_compute_aabb } from "./animation_curve_compute_aabb.js";
|
|
4
|
+
import { evaluate_two_key_curve } from "./evaluate_two_key_curve.js";
|
|
5
|
+
|
|
6
|
+
const bounds = new AABB2();
|
|
7
|
+
|
|
8
|
+
const NORMALIZED_CHECK_DISTANCE = 0.07;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Compute value difference in the curve if the `middle` key between `previous` and `next` is removed
|
|
12
|
+
* @param {Keyframe} key_middle
|
|
13
|
+
* @param {Keyframe} key_previous
|
|
14
|
+
* @param {Keyframe} key_next
|
|
15
|
+
* @return {number} value delta if the middle frame is removed
|
|
16
|
+
*/
|
|
17
|
+
function compute_keyframe_value_effect(key_middle, key_previous, key_next) {
|
|
18
|
+
// check if this key contributes to the shape
|
|
19
|
+
const v1_actual = evaluate_two_key_curve(key_middle.time, key_previous, key_next);
|
|
20
|
+
const v1_expected = key_middle.value;
|
|
21
|
+
|
|
22
|
+
const v1_error = Math.abs(v1_actual - v1_expected);
|
|
23
|
+
|
|
24
|
+
// sample just before the current frame
|
|
25
|
+
const v0_time = key_middle.time - (key_middle.time - key_previous.time) * NORMALIZED_CHECK_DISTANCE;
|
|
26
|
+
|
|
27
|
+
const v0_actual = evaluate_two_key_curve(v0_time, key_previous, key_next);
|
|
28
|
+
const v0_expected = evaluate_two_key_curve(v0_time, key_previous, key_middle);
|
|
29
|
+
|
|
30
|
+
const v0_error = Math.abs(v0_actual - v0_expected);
|
|
31
|
+
|
|
32
|
+
// sample just after the current frame
|
|
33
|
+
const v2_time = key_middle.time + (key_next.time - key_middle.time) * NORMALIZED_CHECK_DISTANCE;
|
|
34
|
+
|
|
35
|
+
const v2_actual = evaluate_two_key_curve(v2_time, key_previous, key_next);
|
|
36
|
+
const v2_expected = evaluate_two_key_curve(v2_time, key_middle, key_next);
|
|
37
|
+
|
|
38
|
+
const v2_error = Math.abs(v2_actual - v2_expected);
|
|
39
|
+
|
|
40
|
+
return Math.max(v0_error, v1_error, v2_error);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Will remove keyframes that do not affect shape of the curve significantly. The significance degree is determined by the error tolerance
|
|
45
|
+
* Intended to reduce complexity of a curve to improve performance and lower memory usage
|
|
46
|
+
* @param {AnimationCurve} curve
|
|
47
|
+
* @param {number} [error_tolerance] how much of a loss to accept, this is relative to normalized value bounds of the curve
|
|
48
|
+
*/
|
|
49
|
+
export function animation_curve_optimize(curve, error_tolerance = 1e-3) {
|
|
50
|
+
assert.lessThan(error_tolerance, 1, 'error_tolerance must be less than 1');
|
|
51
|
+
|
|
52
|
+
animation_curve_compute_aabb(bounds, curve);
|
|
53
|
+
|
|
54
|
+
const absolute_error_tolerance = bounds.height * error_tolerance;
|
|
55
|
+
|
|
56
|
+
let key_count = curve.length;
|
|
57
|
+
|
|
58
|
+
const keyframes = curve.keys;
|
|
59
|
+
|
|
60
|
+
for (let i = 1; i < key_count; i++) {
|
|
61
|
+
const key_previous = keyframes[i - 1];
|
|
62
|
+
const key_current = keyframes[i];
|
|
63
|
+
|
|
64
|
+
let should_remove = false;
|
|
65
|
+
|
|
66
|
+
if (key_current.equals(key_previous)) {
|
|
67
|
+
// adds no semantic value
|
|
68
|
+
should_remove = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!should_remove && i < key_count - 1) {
|
|
72
|
+
const key_next = keyframes[i + 1];
|
|
73
|
+
|
|
74
|
+
const max_error = compute_keyframe_value_effect(key_current, key_previous, key_next);
|
|
75
|
+
|
|
76
|
+
if (max_error <= absolute_error_tolerance) {
|
|
77
|
+
// does not significantly affect the curve shape
|
|
78
|
+
should_remove = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if (should_remove) {
|
|
84
|
+
curve.remove(key_current);
|
|
85
|
+
i--;
|
|
86
|
+
key_count--;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animation_curve_optimize.spec.d.ts","sourceRoot":"","sources":["../../../../../src/engine/animation/curve/animation_curve_optimize.spec.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { animation_curve_optimize } from "./animation_curve_optimize.js";
|
|
2
|
+
import { AnimationCurve } from "./AnimationCurve.js";
|
|
3
|
+
import { Keyframe } from "./Keyframe.js";
|
|
4
|
+
|
|
5
|
+
test("single key", () => {
|
|
6
|
+
|
|
7
|
+
const curve = AnimationCurve.from([
|
|
8
|
+
Keyframe.from(1, -2, 3, -5)
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
animation_curve_optimize(curve);
|
|
12
|
+
|
|
13
|
+
expect(curve.keys.length).toBe(1);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("repeated key", () => {
|
|
17
|
+
|
|
18
|
+
const curve = AnimationCurve.from([
|
|
19
|
+
Keyframe.from(1, -2, 3, -5),
|
|
20
|
+
Keyframe.from(1, -2, 3, -5),
|
|
21
|
+
Keyframe.from(1, -2, 3, -5)
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
animation_curve_optimize(curve);
|
|
25
|
+
|
|
26
|
+
expect(curve.equals(
|
|
27
|
+
AnimationCurve.from([
|
|
28
|
+
Keyframe.from(1, -2, 3, -5)
|
|
29
|
+
])
|
|
30
|
+
)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("linear curve", () => {
|
|
34
|
+
|
|
35
|
+
const curve = AnimationCurve.from([
|
|
36
|
+
Keyframe.from(0, 0, 1, 1),
|
|
37
|
+
Keyframe.from(1, 1, 1, 1),
|
|
38
|
+
Keyframe.from(2, 2, 1, 1),
|
|
39
|
+
])
|
|
40
|
+
|
|
41
|
+
animation_curve_optimize(curve);
|
|
42
|
+
|
|
43
|
+
expect(curve.equals(
|
|
44
|
+
AnimationCurve.from([
|
|
45
|
+
Keyframe.from(0, 0, 1, 1),
|
|
46
|
+
Keyframe.from(2, 2, 1, 1)
|
|
47
|
+
])
|
|
48
|
+
)).toBe(true);
|
|
49
|
+
|
|
50
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build_curve_editor.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/animation/curve/draw/build_curve_editor.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build_curve_editor.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/animation/curve/draw/build_curve_editor.js"],"names":[],"mappings":"AAiEA;;;;;;GAMG;AACH,kFAiSC"}
|
|
@@ -11,9 +11,10 @@ import { canvas2d_plot_data_line } from "../../../graphics/canvas/canvas2d_plot_
|
|
|
11
11
|
import { MouseEvents } from "../../../input/devices/events/MouseEvents.js";
|
|
12
12
|
import { readPositionFromMouseEvent } from "../../../input/devices/PointerDevice.js";
|
|
13
13
|
import { DraggableAspect } from "../../../ui/DraggableAspect.js";
|
|
14
|
+
import { animation_curve_compute_aabb } from "../animation_curve_compute_aabb.js";
|
|
14
15
|
import { sample_animation_curve_to_float_array } from "../compression/sample_animation_curve_to_float_array.js";
|
|
15
|
-
import { compute_curve_aabb } from "../compute_curve_aabb.js";
|
|
16
16
|
import { Keyframe } from "../Keyframe.js";
|
|
17
|
+
import { build_tangent_editor } from "./build_tangent_editor.js";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
*
|
|
@@ -106,7 +107,7 @@ export function build_curve_editor({
|
|
|
106
107
|
});
|
|
107
108
|
|
|
108
109
|
function handle_curve_update() {
|
|
109
|
-
|
|
110
|
+
animation_curve_compute_aabb(frame, curve);
|
|
110
111
|
|
|
111
112
|
frame_updated.send0();
|
|
112
113
|
|
|
@@ -133,6 +134,10 @@ export function build_curve_editor({
|
|
|
133
134
|
|
|
134
135
|
const keyframe_views = new Map();
|
|
135
136
|
|
|
137
|
+
/**
|
|
138
|
+
*
|
|
139
|
+
* @param {Keyframe} keyframe
|
|
140
|
+
*/
|
|
136
141
|
function add_keyframe(keyframe) {
|
|
137
142
|
const vContainerMarker = new EmptyView({
|
|
138
143
|
css: {
|
|
@@ -202,7 +207,8 @@ export function build_curve_editor({
|
|
|
202
207
|
|
|
203
208
|
const previous = new Vector2();
|
|
204
209
|
const draggable = new DraggableAspect({
|
|
205
|
-
el: marker_el,
|
|
210
|
+
el: marker_el,
|
|
211
|
+
drag(position) {
|
|
206
212
|
const delta = new Vector2();
|
|
207
213
|
delta.subVectors(position, previous);
|
|
208
214
|
|
|
@@ -300,14 +306,14 @@ export function build_curve_editor({
|
|
|
300
306
|
marker.bindSignal(frame_updated, updatePosition);
|
|
301
307
|
marker.bindSignal(active_keyframe.onChanged, updateActiveState);
|
|
302
308
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
const vTangentEditor = build_tangent_editor({
|
|
310
|
+
keyframe: keyframe,
|
|
311
|
+
size: graph.size,
|
|
312
|
+
ctx: graph.context2d,
|
|
313
|
+
frame,
|
|
314
|
+
margin
|
|
315
|
+
});
|
|
316
|
+
vContainerMarker.addChild(vTangentEditor);
|
|
311
317
|
|
|
312
318
|
vContainerMarker.addChild(marker);
|
|
313
319
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {number} time
|
|
4
|
+
* @param {Keyframe} keyframe0
|
|
5
|
+
* @param {Keyframe} keyframe1
|
|
6
|
+
* @return {number}
|
|
7
|
+
*/
|
|
8
|
+
export function evaluate_two_key_curve(time: number, keyframe0: Keyframe, keyframe1: Keyframe): number;
|
|
9
|
+
//# sourceMappingURL=evaluate_two_key_curve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate_two_key_curve.d.ts","sourceRoot":"","sources":["../../../../../src/engine/animation/curve/evaluate_two_key_curve.js"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,6CALW,MAAM,aACN,QAAQ,aACR,QAAQ,GACP,MAAM,CAcjB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { inverseLerp } from "../../../core/math/inverseLerp.js";
|
|
2
|
+
import { spline_hermite3 } from "../../../core/math/spline/spline_hermite3.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {number} time
|
|
7
|
+
* @param {Keyframe} keyframe0
|
|
8
|
+
* @param {Keyframe} keyframe1
|
|
9
|
+
* @return {number}
|
|
10
|
+
*/
|
|
11
|
+
export function evaluate_two_key_curve(time, keyframe0, keyframe1) {
|
|
12
|
+
// convert to 0..1 region
|
|
13
|
+
const normalized_time = inverseLerp(keyframe0.time, keyframe1.time, time);
|
|
14
|
+
|
|
15
|
+
const time_distance = keyframe1.time - keyframe0.time;
|
|
16
|
+
|
|
17
|
+
return spline_hermite3(
|
|
18
|
+
normalized_time,
|
|
19
|
+
keyframe0.value, keyframe1.value,
|
|
20
|
+
keyframe0.outTangent * time_distance,
|
|
21
|
+
keyframe1.inTangent * time_distance
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -38,17 +38,17 @@ async function main(engine) {
|
|
|
38
38
|
yaw: 3.123185307179593,
|
|
39
39
|
});
|
|
40
40
|
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
make_sample('data/models/samples/InterpolationTest.glb',[
|
|
42
|
+
'Step Scale',
|
|
43
|
+
'Linear Scale',
|
|
44
|
+
'CubicSpline Scale',
|
|
45
|
+
'Step Rotation',
|
|
46
|
+
'CubicSpline Rotation',
|
|
47
|
+
'Linear Rotation',
|
|
48
|
+
'Step Translation',
|
|
49
|
+
'CubicSpline Translation',
|
|
50
|
+
'Linear Translation'
|
|
51
|
+
],engine.entityManager.dataset);
|
|
52
52
|
|
|
53
53
|
// make_sample('data/models/samples/BoxAnimated.glb',[
|
|
54
54
|
// 'animation_0'
|
|
@@ -61,9 +61,9 @@ async function main(engine) {
|
|
|
61
61
|
// make_sample('moicon/Separated_Cardboard-Box-800x600x400_Roughness0/V1 Cardboard B.gltf', [
|
|
62
62
|
// 'All Animations'
|
|
63
63
|
// ], engine.entityManager.dataset);
|
|
64
|
-
make_sample('moicon/v26/v26.gltf', [
|
|
65
|
-
|
|
66
|
-
], engine.entityManager.dataset);
|
|
64
|
+
// make_sample('moicon/v26/v26.gltf', [
|
|
65
|
+
// 'All Animations'
|
|
66
|
+
// ], engine.entityManager.dataset);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/**
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export class AnimationCurveView extends EmptyView {
|
|
2
|
+
constructor();
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {AnimationCurve} v
|
|
6
|
+
*/
|
|
7
|
+
set curve(arg: AnimationCurve);
|
|
8
|
+
auto_set_frame(): void;
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @param {Float32Array|number[]} out
|
|
12
|
+
* @param {number} x
|
|
13
|
+
* @param {number} y
|
|
14
|
+
*/
|
|
15
|
+
point_curve_to_canvas(out: Float32Array | number[], x: number, y: number): void;
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {Float32Array|number[]} out
|
|
19
|
+
* @param {number} x
|
|
20
|
+
* @param {number} y
|
|
21
|
+
*/
|
|
22
|
+
point_canvas_to_curve(out: Float32Array | number[], x: number, y: number): void;
|
|
23
|
+
update(): void;
|
|
24
|
+
draw(): void;
|
|
25
|
+
draw_grid(): void;
|
|
26
|
+
draw_key_knots(): void;
|
|
27
|
+
draw_key_knot(key: any): void;
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
* @param {Keyframe} keyframe
|
|
31
|
+
*/
|
|
32
|
+
draw_key_tangents(keyframe: Keyframe): void;
|
|
33
|
+
/**
|
|
34
|
+
*
|
|
35
|
+
* @param {CanvasRenderingContext2D} ctx
|
|
36
|
+
* @param {Keyframe} keyframe
|
|
37
|
+
* @param {number} angle
|
|
38
|
+
* @param {number} handle_length in pixels
|
|
39
|
+
*/
|
|
40
|
+
draw_tangent(ctx: CanvasRenderingContext2D, keyframe: Keyframe, angle: number, handle_length: number): void;
|
|
41
|
+
draw_tangents(): void;
|
|
42
|
+
draw_curve(): void;
|
|
43
|
+
#private;
|
|
44
|
+
}
|
|
45
|
+
import EmptyView from "../../../../view/elements/EmptyView.js";
|
|
46
|
+
import { AnimationCurve } from "../AnimationCurve.js";
|
|
47
|
+
//# sourceMappingURL=AnimationCurveView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnimationCurveView.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/animation/curve/view/AnimationCurveView.js"],"names":[],"mappings":"AAeA;IAoCI,cAWC;IAtBD;;;OAGG;IACH,+BAIC;IAgBD,uBA6BC;IAoBD;;;;;OAKG;IACH,2BAJW,YAAY,GAAC,MAAM,EAAE,KACrB,MAAM,KACN,MAAM,QAWhB;IAED;;;;;OAKG;IACH,2BAJW,YAAY,GAAC,MAAM,EAAE,KACrB,MAAM,KACN,MAAM,QAQhB;IAED,eASC;IAED,aAsBC;IAED,kBAwBC;IAED,uBAQC;IAED,8BAiBC;IAGD;;;OAGG;IACH,4BAFW,QAAQ,QAelB;IAED;;;;;;OAMG;IACH,kBALW,wBAAwB,YACxB,QAAQ,SACR,MAAM,iBACN,MAAM,QA+BhB;IAED,sBASC;IAED,mBAqCC;;CAEJ;sBA/UqB,wCAAwC;+BAG/B,sBAAsB"}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { assert } from "../../../../core/assert.js";
|
|
2
|
+
import AABB2 from "../../../../core/geom/2d/aabb/AABB2.js";
|
|
3
|
+
import { m3_cm_compose_transform } from "../../../../core/geom/mat3/m3_cm_compose_transform.js";
|
|
4
|
+
import { m3_cm_invert } from "../../../../core/geom/mat3/m3_cm_invert.js";
|
|
5
|
+
import { v2_length } from "../../../../core/geom/vec2/v2_length.js";
|
|
6
|
+
import { v2_matrix3_cm_multiply } from "../../../../core/geom/vec2/v2_matrix3_cm_multiply.js";
|
|
7
|
+
import { CanvasView } from "../../../../view/elements/CanvasView.js";
|
|
8
|
+
import EmptyView from "../../../../view/elements/EmptyView.js";
|
|
9
|
+
import { canvas2d_draw_grid } from "../../../graphics/canvas/canvas2d_draw_grid.js";
|
|
10
|
+
import { animation_curve_compute_aabb } from "../animation_curve_compute_aabb.js";
|
|
11
|
+
import { AnimationCurve } from "../AnimationCurve.js";
|
|
12
|
+
|
|
13
|
+
const scratch_v2_a = new Float32Array(2);
|
|
14
|
+
const scratch_v2_b = new Float32Array(2);
|
|
15
|
+
|
|
16
|
+
export class AnimationCurveView extends EmptyView {
|
|
17
|
+
|
|
18
|
+
#curve = AnimationCurve.constant();
|
|
19
|
+
#canvas = new CanvasView();
|
|
20
|
+
#frame = new AABB2();
|
|
21
|
+
#curve_bounds = new AABB2();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 3x3 transform matrix
|
|
25
|
+
* Transform from curve space to canvas space
|
|
26
|
+
* @type {Float32Array}
|
|
27
|
+
*/
|
|
28
|
+
#transform = new Float32Array(9);
|
|
29
|
+
/**
|
|
30
|
+
* 3x3 inverse of transform matrix
|
|
31
|
+
* @type {Float32Array}
|
|
32
|
+
*/
|
|
33
|
+
#transform_inverse = new Float32Array(9);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @type {number}
|
|
38
|
+
*/
|
|
39
|
+
#margin = 36
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
* @param {AnimationCurve} v
|
|
44
|
+
*/
|
|
45
|
+
set curve(v) {
|
|
46
|
+
this.#curve = v;
|
|
47
|
+
|
|
48
|
+
this.update();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
constructor() {
|
|
53
|
+
super();
|
|
54
|
+
|
|
55
|
+
this.addChild(this.#canvas);
|
|
56
|
+
|
|
57
|
+
this.size.onChanged.add((x, y) => {
|
|
58
|
+
|
|
59
|
+
this.#canvas.size.set(x, y);
|
|
60
|
+
this.update();
|
|
61
|
+
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
auto_set_frame() {
|
|
66
|
+
animation_curve_compute_aabb(this.#curve_bounds, this.#curve);
|
|
67
|
+
|
|
68
|
+
this.#frame.copy(this.#curve_bounds);
|
|
69
|
+
|
|
70
|
+
if (this.#frame.height === 0) {
|
|
71
|
+
this.#frame.growHeight(0.5);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (this.#frame.width === 0) {
|
|
75
|
+
this.#frame.growWidth(0.5);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const margin_top = this.#margin;
|
|
79
|
+
const margin_bottom = this.#margin;
|
|
80
|
+
const margin_left = this.#margin;
|
|
81
|
+
const margin_right = this.#margin;
|
|
82
|
+
|
|
83
|
+
const normalized_margin_left = (margin_left / this.size.x) * this.#frame.width;
|
|
84
|
+
const normalized_margin_right = (margin_right / this.size.x) * this.#frame.width;
|
|
85
|
+
const normalized_margin_top = (margin_top / this.size.y) * this.#frame.height;
|
|
86
|
+
const normalized_margin_bottom = (margin_bottom / this.size.y) * this.#frame.height;
|
|
87
|
+
|
|
88
|
+
this.#frame.x0 -= normalized_margin_left;
|
|
89
|
+
this.#frame.x1 += normalized_margin_right;
|
|
90
|
+
this.#frame.y0 -= normalized_margin_top;
|
|
91
|
+
this.#frame.y1 += normalized_margin_bottom;
|
|
92
|
+
|
|
93
|
+
this.#update_frame();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#update_frame() {
|
|
97
|
+
|
|
98
|
+
const frame = this.#frame;
|
|
99
|
+
const size = this.size;
|
|
100
|
+
|
|
101
|
+
const scale_x = size.x / frame.width;
|
|
102
|
+
const scale_y = -size.y / frame.height;
|
|
103
|
+
|
|
104
|
+
m3_cm_compose_transform(
|
|
105
|
+
this.#transform,
|
|
106
|
+
-frame.x0, -frame.y1,
|
|
107
|
+
scale_x, scale_y,
|
|
108
|
+
frame.x0, frame.y1, 0
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
m3_cm_invert(this.#transform_inverse, this.#transform);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
*
|
|
116
|
+
* @param {Float32Array|number[]} out
|
|
117
|
+
* @param {number} x
|
|
118
|
+
* @param {number} y
|
|
119
|
+
*/
|
|
120
|
+
point_curve_to_canvas(out, x, y) {
|
|
121
|
+
|
|
122
|
+
assert.notNaN(x, 'x');
|
|
123
|
+
assert.notNaN(y, 'y');
|
|
124
|
+
|
|
125
|
+
v2_matrix3_cm_multiply(out, 0,
|
|
126
|
+
x, y, this.#transform
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
*
|
|
133
|
+
* @param {Float32Array|number[]} out
|
|
134
|
+
* @param {number} x
|
|
135
|
+
* @param {number} y
|
|
136
|
+
*/
|
|
137
|
+
point_canvas_to_curve(out, x, y) {
|
|
138
|
+
|
|
139
|
+
v2_matrix3_cm_multiply(out, 0,
|
|
140
|
+
x, y, this.#transform_inverse
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
update() {
|
|
146
|
+
if (this.size.x <= 0 || this.size.y <= 0) {
|
|
147
|
+
// size too small
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.auto_set_frame();
|
|
152
|
+
|
|
153
|
+
this.draw();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
draw() {
|
|
157
|
+
const ctx = this.#canvas.context2d;
|
|
158
|
+
|
|
159
|
+
ctx.fillStyle = '#222222';
|
|
160
|
+
ctx.fillRect(0, 0, this.size.x, this.size.y);
|
|
161
|
+
|
|
162
|
+
this.draw_grid();
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
ctx.fillStyle = 'none';
|
|
166
|
+
ctx.strokeStyle = '#00ff00';
|
|
167
|
+
ctx.lineWidth = 1;
|
|
168
|
+
|
|
169
|
+
this.draw_curve();
|
|
170
|
+
|
|
171
|
+
ctx.fillStyle = 'none';
|
|
172
|
+
ctx.strokeStyle = '#0080ff';
|
|
173
|
+
ctx.lineWidth = 1;
|
|
174
|
+
|
|
175
|
+
this.draw_tangents();
|
|
176
|
+
|
|
177
|
+
this.draw_key_knots();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
draw_grid() {
|
|
181
|
+
const ctx = this.#canvas.context2d;
|
|
182
|
+
|
|
183
|
+
const width = this.size.x;
|
|
184
|
+
const height = this.size.y;
|
|
185
|
+
|
|
186
|
+
canvas2d_draw_grid({
|
|
187
|
+
ctx,
|
|
188
|
+
width,
|
|
189
|
+
height,
|
|
190
|
+
color: '#262626',
|
|
191
|
+
spacing: 32,
|
|
192
|
+
offset_x: 0
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
canvas2d_draw_grid({
|
|
196
|
+
ctx,
|
|
197
|
+
width,
|
|
198
|
+
height,
|
|
199
|
+
color: '#303030',
|
|
200
|
+
spacing: 32,
|
|
201
|
+
offset_x: 16,
|
|
202
|
+
offset_y: 16
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
draw_key_knots() {
|
|
207
|
+
const keys = this.#curve.keys;
|
|
208
|
+
const key_count = keys.length;
|
|
209
|
+
|
|
210
|
+
for (let i = 0; i < key_count; i++) {
|
|
211
|
+
this.draw_key_knot(keys[i]);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
draw_key_knot(key) {
|
|
217
|
+
|
|
218
|
+
const ctx = this.#canvas.context2d;
|
|
219
|
+
|
|
220
|
+
this.point_curve_to_canvas(scratch_v2_a, key.time, key.value);
|
|
221
|
+
|
|
222
|
+
const stroke_width = 1;
|
|
223
|
+
const radius = 2;
|
|
224
|
+
|
|
225
|
+
ctx.beginPath();
|
|
226
|
+
ctx.arc(scratch_v2_a[0], scratch_v2_a[1], radius + stroke_width * 0.5, 0, 2 * Math.PI, false);
|
|
227
|
+
ctx.fillStyle = 'green';
|
|
228
|
+
ctx.fill();
|
|
229
|
+
ctx.lineWidth = stroke_width;
|
|
230
|
+
ctx.strokeStyle = 'rgba(0,133,0,0.2)';
|
|
231
|
+
ctx.stroke();
|
|
232
|
+
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
*
|
|
238
|
+
* @param {Keyframe} keyframe
|
|
239
|
+
*/
|
|
240
|
+
draw_key_tangents(keyframe) {
|
|
241
|
+
const handle_length = 36;
|
|
242
|
+
|
|
243
|
+
const ctx = this.#canvas.context2d;
|
|
244
|
+
|
|
245
|
+
// incoming
|
|
246
|
+
const in_angle = -Math.atan2(keyframe.inTangent, -1);
|
|
247
|
+
this.draw_tangent(ctx, keyframe, in_angle, handle_length);
|
|
248
|
+
|
|
249
|
+
// outgoing
|
|
250
|
+
const ut_angle = Math.atan2(keyframe.outTangent, 1);
|
|
251
|
+
|
|
252
|
+
this.draw_tangent(ctx, keyframe, ut_angle, handle_length);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
*
|
|
257
|
+
* @param {CanvasRenderingContext2D} ctx
|
|
258
|
+
* @param {Keyframe} keyframe
|
|
259
|
+
* @param {number} angle
|
|
260
|
+
* @param {number} handle_length in pixels
|
|
261
|
+
*/
|
|
262
|
+
draw_tangent(ctx, keyframe, angle, handle_length) {
|
|
263
|
+
ctx.beginPath();
|
|
264
|
+
|
|
265
|
+
this.point_curve_to_canvas(scratch_v2_a, keyframe.time, keyframe.value);
|
|
266
|
+
|
|
267
|
+
ctx.moveTo(scratch_v2_a[0], scratch_v2_a[1]);
|
|
268
|
+
|
|
269
|
+
this.point_curve_to_canvas(scratch_v2_b,
|
|
270
|
+
keyframe.time + Math.cos(angle),
|
|
271
|
+
keyframe.value + Math.sin(angle)
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
// a - b
|
|
275
|
+
scratch_v2_b[0] -= scratch_v2_a[0];
|
|
276
|
+
scratch_v2_b[1] -= scratch_v2_a[1];
|
|
277
|
+
|
|
278
|
+
// normalize to desired length
|
|
279
|
+
const norm = handle_length / v2_length(scratch_v2_b[0], scratch_v2_b[1]);
|
|
280
|
+
|
|
281
|
+
scratch_v2_b[0] *= norm;
|
|
282
|
+
scratch_v2_b[1] *= norm;
|
|
283
|
+
|
|
284
|
+
// restore offset
|
|
285
|
+
scratch_v2_b[0] += scratch_v2_a[0];
|
|
286
|
+
scratch_v2_b[1] += scratch_v2_a[1];
|
|
287
|
+
|
|
288
|
+
ctx.lineTo(scratch_v2_b[0], scratch_v2_b[1]);
|
|
289
|
+
|
|
290
|
+
ctx.stroke();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
draw_tangents() {
|
|
294
|
+
const curve = this.#curve;
|
|
295
|
+
const keys = curve.keys;
|
|
296
|
+
|
|
297
|
+
for (let i = 0; i < keys.length; i++) {
|
|
298
|
+
const keyframe = keys[i];
|
|
299
|
+
|
|
300
|
+
this.draw_key_tangents(keyframe);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
draw_curve() {
|
|
305
|
+
|
|
306
|
+
const ctx = this.#canvas.context2d;
|
|
307
|
+
|
|
308
|
+
ctx.beginPath();
|
|
309
|
+
|
|
310
|
+
const curve = this.#curve;
|
|
311
|
+
|
|
312
|
+
const keys = curve.keys;
|
|
313
|
+
|
|
314
|
+
if (keys.length < 2) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const duration = curve.duration;
|
|
319
|
+
const first_key = curve.keys[0];
|
|
320
|
+
const time_start = first_key.time;
|
|
321
|
+
|
|
322
|
+
const segments = Math.ceil(this.size.x / 4);
|
|
323
|
+
|
|
324
|
+
this.point_curve_to_canvas(scratch_v2_a, first_key.time, first_key.value);
|
|
325
|
+
|
|
326
|
+
ctx.moveTo(scratch_v2_a[0], scratch_v2_a[1]);
|
|
327
|
+
|
|
328
|
+
for (let i = 1; i < segments; i++) {
|
|
329
|
+
const t = i / (segments - 1);
|
|
330
|
+
|
|
331
|
+
const time = time_start + duration * t;
|
|
332
|
+
|
|
333
|
+
const value = curve.evaluate(time);
|
|
334
|
+
|
|
335
|
+
this.point_curve_to_canvas(scratch_v2_a, time, value);
|
|
336
|
+
|
|
337
|
+
ctx.lineTo(scratch_v2_a[0], scratch_v2_a[1]);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
ctx.stroke();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
}
|