@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
94 lines (82 loc) • 3.91 kB
JavaScript
import {position_canvas_to_curve} from "./position_canvas_to_curve.js";
import {Keyframe} from "../Keyframe.js";
import {assert} from "../../../../core/assert.js";
import {animation_curve_subdivide} from "../animation_curve_subdivide.js";
import {animation_curve_nearest_point} from "../animation_curve_nearest_point.js";
import Vector2 from "../../../../core/geom/Vector2.js";
import {position_curve_to_canvas} from "./position_curve_to_canvas.js";
/**
* Check if new keyframe is in valid bounds
* @param {CanvasView} graph
* @param {AABB2} frame
* @param {Vector2} margin
* @param {AnimationCurve} curve
* @param {AABB2} validEditableBounds
* @param {Vector2} newCanvasPosition
* @param {Keyframe} newKeyframeContainer
* @returns {boolean}
*/
export function isInjectedKeyframeInBounds(graph, frame, margin, curve, validEditableBounds, newCanvasPosition, newKeyframeContainer) {
const curvePosition = position_canvas_to_curve(graph.size, frame, margin, newCanvasPosition.x, newCanvasPosition.y);
const curveTimePos = curvePosition.x;
//New keyframe is within the curve range
const isWithinCurveTimeBounds = curveTimePos > curve.keys[0].time && curveTimePos < curve.keys[curve.length - 1].time
if (isWithinCurveTimeBounds) {
const timeSplicePosition = detectClosestCurveTimePoint(curve, graph, frame, margin, curvePosition, newCanvasPosition);
if (timeSplicePosition !== Infinity) {
acquireInjectedKeyframePosition(curve, timeSplicePosition, newKeyframeContainer);
return true;
}
} else if (validEditableBounds.containsPoint(curvePosition.x, curvePosition.y)) {
//New keyframe is within the bounds limit
newKeyframeContainer.time = curvePosition.x;
newKeyframeContainer.value = curvePosition.y;
return true;
}
return false;
}
/**
*
* @param {AnimationCurve} curve
* @param {CanvasView} graph
* @param {AABB2} frame
* @param {Vector2} margin
* @param {Vector2} curvePosition
* @param {Vector2} mousePosition
* @returns {number}
*/
export function detectClosestCurveTimePoint(curve, graph, frame, margin, curvePosition, mousePosition) {
const distTolerance = 10;
const nearestTimePoint = animation_curve_nearest_point(curve, curvePosition.x, curvePosition.y)
const foundCurvePoint = new Vector2(nearestTimePoint, curve.evaluate(nearestTimePoint));
const canvasPositionOfFoundCurvePoint = position_curve_to_canvas(graph.size, frame, margin, foundCurvePoint.x, foundCurvePoint.y)
let closestPoint = Infinity;
if (mousePosition.distanceTo(canvasPositionOfFoundCurvePoint) <= distTolerance) {
closestPoint = nearestTimePoint;
}
return closestPoint;
}
/**
*
* @param {AnimationCurve} curve
* @param {number} timeSplicePosition
* @param {Keyframe} newKeyframeContainer
*/
function acquireInjectedKeyframePosition(curve, timeSplicePosition, newKeyframeContainer) {
const prevKeyframeRef = new Keyframe();
const nextKeyframeRef = new Keyframe();
curve.keys.forEach((kf) => {
if (kf.time <= timeSplicePosition && kf.time >= prevKeyframeRef.time) {
prevKeyframeRef.copy(kf);
}
if (kf.time >= timeSplicePosition && nextKeyframeRef.time === 0) {
nextKeyframeRef.copy(kf);
}
});
assert.notEqual(nextKeyframeRef.time, 0, "Invalid next keyframe value");
assert.notEqual(prevKeyframeRef.time, nextKeyframeRef.time, "Invalid keyframes value")
const segmentTimeDiff = nextKeyframeRef.time - prevKeyframeRef.time;
const segmentTimeTravel = timeSplicePosition - prevKeyframeRef.time;
const segmentTimeSplitRatio = segmentTimeTravel / segmentTimeDiff;
animation_curve_subdivide(newKeyframeContainer, prevKeyframeRef, nextKeyframeRef, segmentTimeSplitRatio);
}