|
@@ -1,6 +1,8 @@
|
|
import * as React from "react";
|
|
import * as React from "react";
|
|
import { Animation } from 'babylonjs/Animations/animation';
|
|
import { Animation } from 'babylonjs/Animations/animation';
|
|
-import { Vector2 } from 'babylonjs/Maths/math.vector';
|
|
|
|
|
|
+import { Vector2, Vector3, Quaternion } from 'babylonjs/Maths/math.vector';
|
|
|
|
+import { Color3, Color4 } from 'babylonjs/Maths/math.color';
|
|
|
|
+import { Size } from 'babylonjs/Maths/math.size';
|
|
import { EasingFunction } from 'babylonjs/Animations/easing';
|
|
import { EasingFunction } from 'babylonjs/Animations/easing';
|
|
import { IAnimationKey } from 'babylonjs/Animations/animationKey';
|
|
import { IAnimationKey } from 'babylonjs/Animations/animationKey';
|
|
import { IKeyframeSvgPoint } from './keyframeSvgPoint';
|
|
import { IKeyframeSvgPoint } from './keyframeSvgPoint';
|
|
@@ -13,6 +15,7 @@ import { Scene } from "babylonjs/scene";
|
|
import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
|
|
import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
|
|
import { TargetedAnimation } from "babylonjs/Animations/animationGroup";
|
|
import { TargetedAnimation } from "babylonjs/Animations/animationGroup";
|
|
import { EditorControls } from './editorControls';
|
|
import { EditorControls } from './editorControls';
|
|
|
|
+import { SelectedCoordinate } from './animationListTree';
|
|
|
|
|
|
require("./curveEditor.scss");
|
|
require("./curveEditor.scss");
|
|
|
|
|
|
@@ -28,10 +31,17 @@ interface ICanvasAxis {
|
|
label: number;
|
|
label: number;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+interface ICurveData {
|
|
|
|
+ pathData: string;
|
|
|
|
+ pathLength: number;
|
|
|
|
+ domCurve: React.RefObject<SVGPathElement>;
|
|
|
|
+ color: string;
|
|
|
|
+ id: string;
|
|
|
|
+}
|
|
|
|
+
|
|
export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
|
|
export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
|
|
isOpen: boolean,
|
|
isOpen: boolean,
|
|
selected: Animation | null,
|
|
selected: Animation | null,
|
|
- currentPathData: string | undefined,
|
|
|
|
svgKeyframes: IKeyframeSvgPoint[] | undefined,
|
|
svgKeyframes: IKeyframeSvgPoint[] | undefined,
|
|
currentFrame: number,
|
|
currentFrame: number,
|
|
currentValue: number,
|
|
currentValue: number,
|
|
@@ -45,9 +55,9 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
playheadOffset: number,
|
|
playheadOffset: number,
|
|
notification: string,
|
|
notification: string,
|
|
currentPoint: SVGPoint | undefined,
|
|
currentPoint: SVGPoint | undefined,
|
|
- lastFrame: number,
|
|
|
|
playheadPos: number,
|
|
playheadPos: number,
|
|
isPlaying: boolean,
|
|
isPlaying: boolean,
|
|
|
|
+ selectedPathData: ICurveData[] | undefined
|
|
}> {
|
|
}> {
|
|
|
|
|
|
// Height scale *Review this functionaliy
|
|
// Height scale *Review this functionaliy
|
|
@@ -56,7 +66,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
readonly _entityName: string;
|
|
readonly _entityName: string;
|
|
readonly _canvasLength: number = 20;
|
|
readonly _canvasLength: number = 20;
|
|
private _svgKeyframes: IKeyframeSvgPoint[] = [];
|
|
private _svgKeyframes: IKeyframeSvgPoint[] = [];
|
|
- private _frames: Vector2[] = [];
|
|
|
|
private _isPlaying: boolean = false;
|
|
private _isPlaying: boolean = false;
|
|
private _graphCanvas: React.RefObject<HTMLDivElement>;
|
|
private _graphCanvas: React.RefObject<HTMLDivElement>;
|
|
private _selectedCurve: React.RefObject<SVGPathElement>;
|
|
private _selectedCurve: React.RefObject<SVGPathElement>;
|
|
@@ -78,7 +87,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
if (this.props.entity instanceof TargetedAnimation) {
|
|
if (this.props.entity instanceof TargetedAnimation) {
|
|
this._isTargetedAnimation = true;
|
|
this._isTargetedAnimation = true;
|
|
initialSelection = this.props.entity.animation;
|
|
initialSelection = this.props.entity.animation;
|
|
- initialLerpMode = initialSelection !== undefined ? this.analizeAnimation(initialSelection) : false;
|
|
|
|
|
|
+ initialLerpMode = initialSelection !== undefined ? this.analizeAnimationForLerp(initialSelection) : false;
|
|
initialPathData = initialSelection !== undefined ? this.getPathData(initialSelection) : "";
|
|
initialPathData = initialSelection !== undefined ? this.getPathData(initialSelection) : "";
|
|
} else {
|
|
} else {
|
|
this._isTargetedAnimation = false;
|
|
this._isTargetedAnimation = false;
|
|
@@ -87,7 +96,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
initialSelection = hasAnimations !== false ? hasAnimations && hasAnimations[0] : null;
|
|
initialSelection = hasAnimations !== false ? hasAnimations && hasAnimations[0] : null;
|
|
|
|
|
|
|
|
|
|
- initialLerpMode = initialSelection !== undefined ? this.analizeAnimation(this.props.entity.animations && initialSelection) : false;
|
|
|
|
|
|
+ initialLerpMode = initialSelection !== undefined ? this.analizeAnimationForLerp(this.props.entity.animations && initialSelection) : false;
|
|
initialPathData = initialSelection && this.getPathData(initialSelection);
|
|
initialPathData = initialSelection && this.getPathData(initialSelection);
|
|
initialPathData = initialPathData === null || initialPathData === undefined ? "" : initialPathData;
|
|
initialPathData = initialPathData === null || initialPathData === undefined ? "" : initialPathData;
|
|
}
|
|
}
|
|
@@ -97,7 +106,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
this.state = {
|
|
this.state = {
|
|
selected: initialSelection,
|
|
selected: initialSelection,
|
|
isOpen: true,
|
|
isOpen: true,
|
|
- currentPathData: initialPathData,
|
|
|
|
svgKeyframes: this._svgKeyframes,
|
|
svgKeyframes: this._svgKeyframes,
|
|
currentFrame: 0,
|
|
currentFrame: 0,
|
|
currentValue: 1,
|
|
currentValue: 1,
|
|
@@ -109,11 +117,11 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
frameAxisLength: (new Array(this._canvasLength)).fill(0).map((s, i) => { return { value: i * 10, label: i * 10 } }),
|
|
frameAxisLength: (new Array(this._canvasLength)).fill(0).map((s, i) => { return { value: i * 10, label: i * 10 } }),
|
|
valueAxisLength: (new Array(10)).fill(0).map((s, i) => { return { value: i * 10, label: valueInd[i] } }),
|
|
valueAxisLength: (new Array(10)).fill(0).map((s, i) => { return { value: i * 10, label: valueInd[i] } }),
|
|
notification: "",
|
|
notification: "",
|
|
- lastFrame: 0,
|
|
|
|
currentPoint: undefined,
|
|
currentPoint: undefined,
|
|
scale: 1,
|
|
scale: 1,
|
|
playheadPos: 0,
|
|
playheadPos: 0,
|
|
isPlaying: this.isAnimationPlaying(),
|
|
isPlaying: this.isAnimationPlaying(),
|
|
|
|
+ selectedPathData: []
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -149,7 +157,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
}
|
|
}
|
|
|
|
|
|
setAxesLength() {
|
|
setAxesLength() {
|
|
-
|
|
|
|
|
|
+
|
|
let length = Math.round(this._canvasLength * this.state.scale);// Check Undefined, or NaN
|
|
let length = Math.round(this._canvasLength * this.state.scale);// Check Undefined, or NaN
|
|
let highestFrame = 100;
|
|
let highestFrame = 100;
|
|
if (this.state.selected !== null) {
|
|
if (this.state.selected !== null) {
|
|
@@ -221,10 +229,108 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
this.setState({ svgKeyframes: updatedKeyframes });
|
|
this.setState({ svgKeyframes: updatedKeyframes });
|
|
}
|
|
}
|
|
|
|
|
|
- renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number) {
|
|
|
|
|
|
+ updateValuePerCoordinate(dataType: number, value: number | Vector2 | Vector3 | Color3 | Color4 | Size | Quaternion, newValue: number, coordinate?: number) {
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if (dataType === Animation.ANIMATIONTYPE_FLOAT) {
|
|
|
|
+ value = newValue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dataType === Animation.ANIMATIONTYPE_VECTOR2) {
|
|
|
|
+ switch (coordinate) {
|
|
|
|
+ case SelectedCoordinate.x:
|
|
|
|
+ (value as Vector2).x = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.y:
|
|
|
|
+ (value as Vector2).y = newValue
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dataType === Animation.ANIMATIONTYPE_VECTOR3) {
|
|
|
|
+ switch (coordinate) {
|
|
|
|
+ case SelectedCoordinate.x:
|
|
|
|
+ (value as Vector3).x = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.y:
|
|
|
|
+ (value as Vector3).y = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.z:
|
|
|
|
+ (value as Vector3).z = newValue
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dataType === Animation.ANIMATIONTYPE_QUATERNION) {
|
|
|
|
+ switch (coordinate) {
|
|
|
|
+ case SelectedCoordinate.x:
|
|
|
|
+ (value as Quaternion).x = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.y:
|
|
|
|
+ (value as Quaternion).y = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.z:
|
|
|
|
+ (value as Quaternion).z = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.w:
|
|
|
|
+ (value as Quaternion).w = newValue
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dataType === Animation.ANIMATIONTYPE_COLOR3) {
|
|
|
|
+ switch (coordinate) {
|
|
|
|
+ case SelectedCoordinate.r:
|
|
|
|
+ (value as Color3).r = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.g:
|
|
|
|
+ (value as Color3).g = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.b:
|
|
|
|
+ (value as Color3).b = newValue
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dataType === Animation.ANIMATIONTYPE_COLOR4) {
|
|
|
|
+ switch (coordinate) {
|
|
|
|
+ case SelectedCoordinate.r:
|
|
|
|
+ (value as Color4).r = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.g:
|
|
|
|
+ (value as Color4).g = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.b:
|
|
|
|
+ (value as Color4).b = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.a:
|
|
|
|
+ (value as Color4).a = newValue
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dataType === Animation.ANIMATIONTYPE_SIZE) {
|
|
|
|
+ switch (coordinate) {
|
|
|
|
+ case SelectedCoordinate.width:
|
|
|
|
+ (value as Size).width = newValue
|
|
|
|
+ break;
|
|
|
|
+ case SelectedCoordinate.g:
|
|
|
|
+ (value as Size).height = newValue
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ return value;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, id: string) {
|
|
|
|
|
|
let animation = this.state.selected as Animation;
|
|
let animation = this.state.selected as Animation;
|
|
// Bug: After play/stop we get an extra keyframe at 0
|
|
// Bug: After play/stop we get an extra keyframe at 0
|
|
|
|
+ let index = parseInt(id.split('_')[3]);
|
|
|
|
+
|
|
|
|
+ let coordinate = parseInt(id.split('_')[2]);
|
|
|
|
|
|
let keys = [...animation.getKeys()];
|
|
let keys = [...animation.getKeys()];
|
|
|
|
|
|
@@ -238,24 +344,30 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
}
|
|
}
|
|
|
|
|
|
keys[index].frame = newFrame; // This value comes as percentage/frame/time
|
|
keys[index].frame = newFrame; // This value comes as percentage/frame/time
|
|
- keys[index].value = ((this._heightScale - updatedSvgKeyFrame.keyframePoint.y) / this._heightScale) * 2; // this value comes inverted svg from 0 = 100 to 100 = 0
|
|
|
|
|
|
|
|
|
|
+ // Calculate value for Vector3...
|
|
|
|
+
|
|
|
|
+ let updatedValue = ((this._heightScale - updatedSvgKeyFrame.keyframePoint.y) / this._heightScale) * 2; // this value comes inverted svg from 0 = 100 to 100 = 0
|
|
|
|
+
|
|
|
|
+ keys[index].value = this.updateValuePerCoordinate(animation.dataType, keys[index].value, updatedValue, coordinate);
|
|
|
|
|
|
if (updatedSvgKeyFrame.isLeftActive) {
|
|
if (updatedSvgKeyFrame.isLeftActive) {
|
|
|
|
|
|
if (updatedSvgKeyFrame.leftControlPoint !== null) {
|
|
if (updatedSvgKeyFrame.leftControlPoint !== null) {
|
|
// Rotate
|
|
// Rotate
|
|
- let updatedValue = ((this._heightScale - updatedSvgKeyFrame.leftControlPoint.y) / this._heightScale) * 2;
|
|
|
|
|
|
+ let newValue = ((this._heightScale - updatedSvgKeyFrame.leftControlPoint.y) / this._heightScale) * 2;
|
|
|
|
|
|
let keyframeValue = ((this._heightScale - updatedSvgKeyFrame.keyframePoint.y) / this._heightScale) * 2;
|
|
let keyframeValue = ((this._heightScale - updatedSvgKeyFrame.keyframePoint.y) / this._heightScale) * 2;
|
|
|
|
|
|
- keys[index].inTangent = keyframeValue - updatedValue;
|
|
|
|
|
|
+ let updatedValue = keyframeValue - newValue;
|
|
|
|
+
|
|
|
|
+ keys[index].inTangent = this.updateValuePerCoordinate(animation.dataType, keys[index].inTangent, updatedValue, coordinate);
|
|
|
|
|
|
if (!this.state.isBrokenMode) {
|
|
if (!this.state.isBrokenMode) {
|
|
// Right control point if exists
|
|
// Right control point if exists
|
|
if (updatedSvgKeyFrame.rightControlPoint !== null) {
|
|
if (updatedSvgKeyFrame.rightControlPoint !== null) {
|
|
// Sets opposite value
|
|
// Sets opposite value
|
|
- keys[index].outTangent = keys[index].inTangent * -1
|
|
|
|
|
|
+ keys[index].outTangent = keys[index].inTangent * -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -265,11 +377,13 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
|
|
|
|
if (updatedSvgKeyFrame.rightControlPoint !== null) {
|
|
if (updatedSvgKeyFrame.rightControlPoint !== null) {
|
|
// Rotate
|
|
// Rotate
|
|
- let updatedValue = ((this._heightScale - updatedSvgKeyFrame.rightControlPoint.y) / this._heightScale) * 2;
|
|
|
|
|
|
+ let newValue = ((this._heightScale - updatedSvgKeyFrame.rightControlPoint.y) / this._heightScale) * 2;
|
|
|
|
|
|
let keyframeValue = ((this._heightScale - updatedSvgKeyFrame.keyframePoint.y) / this._heightScale) * 2;
|
|
let keyframeValue = ((this._heightScale - updatedSvgKeyFrame.keyframePoint.y) / this._heightScale) * 2;
|
|
|
|
|
|
- keys[index].outTangent = keyframeValue - updatedValue;
|
|
|
|
|
|
+ let updatedValue = keyframeValue - newValue;
|
|
|
|
+
|
|
|
|
+ keys[index].outTangent = this.updateValuePerCoordinate(animation.dataType, keys[index].outTangent, updatedValue, coordinate);
|
|
|
|
|
|
if (!this.state.isBrokenMode) {
|
|
if (!this.state.isBrokenMode) {
|
|
if (updatedSvgKeyFrame.leftControlPoint !== null) { // Sets opposite value
|
|
if (updatedSvgKeyFrame.leftControlPoint !== null) { // Sets opposite value
|
|
@@ -282,7 +396,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
|
|
|
|
animation.setKeys(keys);
|
|
animation.setKeys(keys);
|
|
|
|
|
|
- this.selectAnimation(animation);
|
|
|
|
|
|
+ this.selectAnimation(animation, coordinate);
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -425,60 +539,14 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- updateKeyframe(keyframe: Vector2, index: number) {
|
|
|
|
-
|
|
|
|
- let anim = this.state.selected as Animation;
|
|
|
|
- var keys: IAnimationKey[] = [];
|
|
|
|
-
|
|
|
|
- var svgKeyframes = this.state.svgKeyframes?.map((k, i) => {
|
|
|
|
- if (i === index) {
|
|
|
|
- k.keyframePoint.x = keyframe.x;
|
|
|
|
- k.keyframePoint.y = keyframe.y;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var height = 100;
|
|
|
|
- var middle = (height / 2);
|
|
|
|
-
|
|
|
|
- var keyValue;
|
|
|
|
-
|
|
|
|
- if (k.keyframePoint.y < middle) {
|
|
|
|
- keyValue = 1 + ((100 / k.keyframePoint.y) * .1)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (k.keyframePoint.y > middle) {
|
|
|
|
- keyValue = 1 - ((100 / k.keyframePoint.y) * .1)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- keys.push({ frame: k.keyframePoint.x, value: keyValue })
|
|
|
|
- return k;
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- anim.setKeys(keys);
|
|
|
|
-
|
|
|
|
- this.setState({ svgKeyframes: svgKeyframes });
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Curve Rendering Functions
|
|
* Curve Rendering Functions
|
|
* This section handles how to render curves.
|
|
* This section handles how to render curves.
|
|
*/
|
|
*/
|
|
- getAnimationProperties(animation: Animation) {
|
|
|
|
- let easingType, easingMode;
|
|
|
|
- let easingFunction: EasingFunction = animation.getEasingFunction() as EasingFunction;
|
|
|
|
- if (easingFunction === undefined) {
|
|
|
|
- easingType = undefined
|
|
|
|
- easingMode = undefined;
|
|
|
|
- } else {
|
|
|
|
- easingType = easingFunction.constructor.name;
|
|
|
|
- easingMode = easingFunction.getEasingMode();
|
|
|
|
- }
|
|
|
|
- return { easingType, easingMode }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
linearInterpolation(keyframes: IAnimationKey[], data: string, middle: number): string {
|
|
linearInterpolation(keyframes: IAnimationKey[], data: string, middle: number): string {
|
|
keyframes.forEach((key, i) => {
|
|
keyframes.forEach((key, i) => {
|
|
|
|
|
|
|
|
+ // identify type of value and split...
|
|
var point = new Vector2(0, 0);
|
|
var point = new Vector2(0, 0);
|
|
point.x = key.frame;
|
|
point.x = key.frame;
|
|
point.y = this._heightScale - (key.value * middle);
|
|
point.y = this._heightScale - (key.value * middle);
|
|
@@ -492,82 +560,145 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
}
|
|
}
|
|
|
|
|
|
setKeyframePointLinear(point: Vector2, index: number) {
|
|
setKeyframePointLinear(point: Vector2, index: number) {
|
|
|
|
+ // here set the ID to a unique id
|
|
let svgKeyframe = { keyframePoint: point, rightControlPoint: null, leftControlPoint: null, id: index.toString(), selected: false, isLeftActive: false, isRightActive: false }
|
|
let svgKeyframe = { keyframePoint: point, rightControlPoint: null, leftControlPoint: null, id: index.toString(), selected: false, isLeftActive: false, isRightActive: false }
|
|
this._svgKeyframes.push(svgKeyframe);
|
|
this._svgKeyframes.push(svgKeyframe);
|
|
}
|
|
}
|
|
|
|
|
|
- getPathData(animation: Animation | null) {
|
|
|
|
-
|
|
|
|
- if (animation === null){
|
|
|
|
- return "";
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Check if Tangent mode is active and broken mode is active. (Only one tangent moves)
|
|
|
|
- let keyframes = animation.getKeys();
|
|
|
|
-
|
|
|
|
- if (keyframes === undefined) {
|
|
|
|
- return "";
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ flatTangents(keyframes: IAnimationKey[], dataType: number) {
|
|
// Checks if Flat Tangent is active (tangents are set to zero)
|
|
// Checks if Flat Tangent is active (tangents are set to zero)
|
|
|
|
+ let flattened;
|
|
if (this.state && this.state.isFlatTangentMode) {
|
|
if (this.state && this.state.isFlatTangentMode) {
|
|
- keyframes = animation.getKeys().map(kf => {
|
|
|
|
|
|
+
|
|
|
|
+ flattened = keyframes.map(kf => {
|
|
|
|
|
|
if (kf.inTangent !== undefined) {
|
|
if (kf.inTangent !== undefined) {
|
|
- kf.inTangent = 0;
|
|
|
|
|
|
+ kf.inTangent = this.returnZero(dataType);
|
|
}
|
|
}
|
|
|
|
|
|
if (kf.outTangent !== undefined) {
|
|
if (kf.outTangent !== undefined) {
|
|
- kf.outTangent = 0;
|
|
|
|
|
|
+ kf.outTangent = this.returnZero(dataType);
|
|
}
|
|
}
|
|
-
|
|
|
|
return kf;
|
|
return kf;
|
|
});
|
|
});
|
|
} else {
|
|
} else {
|
|
- keyframes = animation.getKeys();
|
|
|
|
|
|
+ flattened = keyframes;
|
|
}
|
|
}
|
|
|
|
+ return flattened;
|
|
|
|
+ }
|
|
|
|
|
|
- const startKey = keyframes[0];
|
|
|
|
|
|
+ returnZero(dataType: number) {
|
|
|
|
+ let type;
|
|
|
|
+ switch (dataType) {
|
|
|
|
+ case Animation.ANIMATIONTYPE_FLOAT:
|
|
|
|
+ type = 0;
|
|
|
|
+ break;
|
|
|
|
+ case Animation.ANIMATIONTYPE_VECTOR3:
|
|
|
|
+ type = Vector3.Zero();
|
|
|
|
+ break;
|
|
|
|
+ case Animation.ANIMATIONTYPE_VECTOR2:
|
|
|
|
+ type = Vector2.Zero();
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return type;
|
|
|
|
+ }
|
|
|
|
|
|
- let middle = this._heightScale / 2;
|
|
|
|
|
|
+ getValueAsArray(valueType: number, value: number | Vector2 | Vector3 | Color3 | Color4 | Size | Quaternion) {
|
|
|
|
+ let valueAsArray: number[] = [];
|
|
|
|
+ switch (valueType) {
|
|
|
|
+ case Animation.ANIMATIONTYPE_FLOAT:
|
|
|
|
+ valueAsArray = [value as number];
|
|
|
|
+ break;
|
|
|
|
+ case Animation.ANIMATIONTYPE_VECTOR3:
|
|
|
|
+ valueAsArray = (value as Vector3).asArray();
|
|
|
|
+ break;
|
|
|
|
+ case Animation.ANIMATIONTYPE_VECTOR2:
|
|
|
|
+ valueAsArray = (value as Vector2).asArray();
|
|
|
|
+ break;
|
|
|
|
+ case Animation.ANIMATIONTYPE_QUATERNION:
|
|
|
|
+ valueAsArray = (value as Quaternion).asArray();
|
|
|
|
+ break;
|
|
|
|
+ case Animation.ANIMATIONTYPE_COLOR3:
|
|
|
|
+ valueAsArray = (value as Color3).asArray();
|
|
|
|
+ break;
|
|
|
|
+ case Animation.ANIMATIONTYPE_COLOR4:
|
|
|
|
+ valueAsArray = (value as Color4).asArray();
|
|
|
|
+ break;
|
|
|
|
+ case Animation.ANIMATIONTYPE_SIZE:
|
|
|
|
+ valueAsArray = [(value as Size).width, (value as Size).height];
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return valueAsArray;
|
|
|
|
+ }
|
|
|
|
|
|
- // START OF LINE/CURVE
|
|
|
|
- let data: string | undefined = `M${startKey.frame}, ${this._heightScale - (startKey.value * middle)}`;
|
|
|
|
|
|
+ getPathData(animation: Animation | null) {
|
|
|
|
+
|
|
|
|
+ if (animation === null) {
|
|
|
|
+ return undefined;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var keyframes = animation.getKeys();
|
|
|
|
+
|
|
|
|
+ if (keyframes === undefined) {
|
|
|
|
+ return undefined;
|
|
|
|
|
|
- if (this.state && this.state.lerpMode) {
|
|
|
|
- data = this.linearInterpolation(keyframes, data, middle);
|
|
|
|
} else {
|
|
} else {
|
|
- if (this.getAnimationData(animation).usesTangents) {
|
|
|
|
- data = this.curvePathWithTangents(keyframes, data, middle, animation.dataType);
|
|
|
|
- } else {
|
|
|
|
- const { easingMode, easingType } = this.getAnimationProperties(animation);
|
|
|
|
- if (easingType !== undefined && easingMode !== undefined) {
|
|
|
|
- let easingFunction = animation.getEasingFunction();
|
|
|
|
- data = this.curvePath(keyframes, data, middle, easingFunction as EasingFunction)
|
|
|
|
|
|
+
|
|
|
|
+ const { easingMode, easingType, usesTangents, valueType, highestFrame, name, targetProperty } = this.getAnimationData(animation);
|
|
|
|
+
|
|
|
|
+ keyframes = this.flatTangents(keyframes, valueType);
|
|
|
|
+ const startKey = keyframes[0];
|
|
|
|
+ let middle = this._heightScale / 2;
|
|
|
|
+ let collection: ICurveData[] = [];
|
|
|
|
+ const colors = ['red', 'green', 'blue', 'white', '#7a4ece'];
|
|
|
|
+ const startValue = this.getValueAsArray(valueType, startKey.value);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ for (var d = 0; d < startValue.length; d++) {
|
|
|
|
+
|
|
|
|
+ const id = `${name}_${targetProperty}_${d}`;
|
|
|
|
+
|
|
|
|
+ const curveColor = valueType === Animation.ANIMATIONTYPE_FLOAT ? colors[4] : colors[d];
|
|
|
|
+ // START OF LINE/CURVE
|
|
|
|
+ let data: string | undefined = `M${startKey.frame}, ${this._heightScale - (startValue[d] * middle)}`; //
|
|
|
|
+
|
|
|
|
+ if (this.state && this.state.lerpMode) {
|
|
|
|
+ data = this.linearInterpolation(keyframes, data, middle);
|
|
} else {
|
|
} else {
|
|
- if (this.state !== undefined) {
|
|
|
|
- let emptyTangents = keyframes.map((kf, i) => {
|
|
|
|
- if (i === 0) {
|
|
|
|
- kf.outTangent = 0;
|
|
|
|
- } else if (i === keyframes.length - 1) {
|
|
|
|
- kf.inTangent = 0;
|
|
|
|
|
|
+ if (usesTangents) {
|
|
|
|
+ data = this.curvePathWithTangents(keyframes, data, middle, valueType, d, id);
|
|
|
|
+ } else {
|
|
|
|
+ if (easingType !== undefined && easingMode !== undefined) {
|
|
|
|
+ let easingFunction = animation.getEasingFunction();
|
|
|
|
+ data = this.curvePath(keyframes, data, middle, easingFunction as EasingFunction)
|
|
|
|
+ } else {
|
|
|
|
+ if (this.state !== undefined) {
|
|
|
|
+ let emptyTangents = keyframes.map((kf, i) => {
|
|
|
|
+ if (i === 0) {
|
|
|
|
+ kf.outTangent = 0;
|
|
|
|
+ } else if (i === keyframes.length - 1) {
|
|
|
|
+ kf.inTangent = 0;
|
|
|
|
+ } else {
|
|
|
|
+ kf.inTangent = 0;
|
|
|
|
+ kf.outTangent = 0;
|
|
|
|
+ }
|
|
|
|
+ return kf;
|
|
|
|
+ });
|
|
|
|
+ data = this.curvePathWithTangents(emptyTangents, data, middle, valueType, d, id);
|
|
} else {
|
|
} else {
|
|
- kf.inTangent = 0;
|
|
|
|
- kf.outTangent = 0;
|
|
|
|
|
|
+ data = this.linearInterpolation(keyframes, data, middle);
|
|
}
|
|
}
|
|
- return kf;
|
|
|
|
- });
|
|
|
|
- data = this.curvePathWithTangents(emptyTangents, data, middle, animation.dataType);
|
|
|
|
- } else {
|
|
|
|
- data = this.linearInterpolation(keyframes, data, middle);
|
|
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ collection.push({ pathData: data, pathLength: highestFrame, domCurve: React.createRef(), color: curveColor, id: id })
|
|
|
|
+
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
|
|
- return data;
|
|
|
|
-
|
|
|
|
|
|
+ return collection;
|
|
|
|
+
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
getAnimationData(animation: Animation) {
|
|
getAnimationData(animation: Animation) {
|
|
@@ -580,86 +711,43 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
let targetPropertyPath = animation.targetPropertyPath;
|
|
let targetPropertyPath = animation.targetPropertyPath;
|
|
let framesPerSecond = animation.framePerSecond;
|
|
let framesPerSecond = animation.framePerSecond;
|
|
let highestFrame = animation.getHighestFrame();
|
|
let highestFrame = animation.getHighestFrame();
|
|
- let serialized = animation.serialize();
|
|
|
|
|
|
+ //let serialized = animation.serialize();
|
|
let usesTangents = animation.getKeys().find(kf => kf.hasOwnProperty('inTangent') || kf.hasOwnProperty('outTangent')) !== undefined ? true : false;
|
|
let usesTangents = animation.getKeys().find(kf => kf.hasOwnProperty('inTangent') || kf.hasOwnProperty('outTangent')) !== undefined ? true : false;
|
|
|
|
+ let valueType = animation.dataType;
|
|
|
|
+ // easing properties
|
|
|
|
+ let easingType, easingMode;
|
|
|
|
+ let easingFunction: EasingFunction = animation.getEasingFunction() as EasingFunction;
|
|
|
|
+ if (easingFunction === undefined) {
|
|
|
|
+ easingType = undefined
|
|
|
|
+ easingMode = undefined;
|
|
|
|
+ } else {
|
|
|
|
+ easingType = easingFunction.constructor.name;
|
|
|
|
+ easingMode = easingFunction.getEasingMode();
|
|
|
|
+ }
|
|
|
|
|
|
- return { loopMode, name, blendingSpeed, targetPropertyPath, targetProperty, framesPerSecond, highestFrame, serialized, usesTangents }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- drawAllFrames(initialKey: IAnimationKey, endKey: IAnimationKey, easingFunction: EasingFunction) {
|
|
|
|
-
|
|
|
|
- let i = initialKey.frame;
|
|
|
|
-
|
|
|
|
- for (i; i < endKey.frame; i++) {
|
|
|
|
-
|
|
|
|
- (i * 100 / endKey.frame)
|
|
|
|
-
|
|
|
|
- let dy = easingFunction.easeInCore(i);
|
|
|
|
- let value = this._heightScale - (dy * (this._heightScale / 2));
|
|
|
|
- this._frames.push(new Vector2(i, value));
|
|
|
|
|
|
+ return { loopMode, name, blendingSpeed, targetPropertyPath, targetProperty, framesPerSecond, highestFrame, usesTangents, easingType, easingMode, valueType }
|
|
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- curvePathFlat(keyframes: IAnimationKey[], data: string, middle: number, dataType: number) {
|
|
|
|
|
|
+ curvePathWithTangents(keyframes: IAnimationKey[], data: string, middle: number, type: number, coordinate: number, animationName: string) {
|
|
|
|
|
|
keyframes.forEach((key, i) => {
|
|
keyframes.forEach((key, i) => {
|
|
|
|
|
|
- if (dataType === Animation.ANIMATIONTYPE_FLOAT) {
|
|
|
|
-
|
|
|
|
- var pointA = new Vector2(0, 0);
|
|
|
|
- if (i === 0) {
|
|
|
|
- pointA.set(key.frame, this._heightScale - (key.value * middle));
|
|
|
|
- this.setKeyframePoint([pointA], i, keyframes.length);
|
|
|
|
- } else {
|
|
|
|
- pointA.set(keyframes[i - 1].frame, this._heightScale - (keyframes[i - 1].value * middle));
|
|
|
|
-
|
|
|
|
- let defaultWeight = 10;
|
|
|
|
-
|
|
|
|
- let nextKeyframe = keyframes[i + 1];
|
|
|
|
- let prevKeyframe = keyframes[i - 1];
|
|
|
|
- if (nextKeyframe !== undefined) {
|
|
|
|
- let distance = keyframes[i + 1].frame - key.frame;
|
|
|
|
- defaultWeight = distance * .33;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (prevKeyframe !== undefined) {
|
|
|
|
- let distance = key.frame - keyframes[i - 1].frame;
|
|
|
|
- defaultWeight = distance * .33;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- let tangentA = new Vector2(pointA.x + defaultWeight, pointA.y);
|
|
|
|
-
|
|
|
|
- let pointB = new Vector2(key.frame, this._heightScale - (key.value * middle));
|
|
|
|
-
|
|
|
|
- let tangentB = new Vector2(pointB.x - defaultWeight, pointB.y);
|
|
|
|
-
|
|
|
|
- this.setKeyframePoint([pointA, tangentA, tangentB, pointB], i, keyframes.length);
|
|
|
|
-
|
|
|
|
- data += ` C${tangentA.x} ${tangentA.y} ${tangentB.x} ${tangentB.y} ${pointB.x} ${pointB.y} `
|
|
|
|
|
|
+ // Create a unique id for curve
|
|
|
|
+ const curveId = animationName + "_" + i
|
|
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- return data;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- curvePathWithTangents(keyframes: IAnimationKey[], data: string, middle: number, type: number) {
|
|
|
|
-
|
|
|
|
- keyframes.forEach((key, i) => {
|
|
|
|
|
|
+ // identify type of value and split...
|
|
|
|
+ const keyframe_valueAsArray = this.getValueAsArray(type, key.value)[coordinate];
|
|
|
|
|
|
let svgKeyframe;
|
|
let svgKeyframe;
|
|
let outTangent;
|
|
let outTangent;
|
|
let inTangent;
|
|
let inTangent;
|
|
let defaultWeight = 5;
|
|
let defaultWeight = 5;
|
|
|
|
|
|
- var inT = key.inTangent === undefined ? null : key.inTangent;
|
|
|
|
- var outT = key.outTangent === undefined ? null : key.outTangent;
|
|
|
|
|
|
+ var inT = key.inTangent === undefined ? null : this.getValueAsArray(type, key.inTangent)[coordinate];
|
|
|
|
+ var outT = key.outTangent === undefined ? null : this.getValueAsArray(type, key.outTangent)[coordinate];
|
|
|
|
|
|
- let y = this._heightScale - (key.value * middle);
|
|
|
|
|
|
+ let y = this._heightScale - (keyframe_valueAsArray * middle);
|
|
|
|
|
|
let nextKeyframe = keyframes[i + 1];
|
|
let nextKeyframe = keyframes[i + 1];
|
|
let prevKeyframe = keyframes[i - 1];
|
|
let prevKeyframe = keyframes[i - 1];
|
|
@@ -688,14 +776,14 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
}
|
|
}
|
|
|
|
|
|
if (i === 0) {
|
|
if (i === 0) {
|
|
- svgKeyframe = { keyframePoint: new Vector2(key.frame, this._heightScale - (key.value * middle)), rightControlPoint: outTangent, leftControlPoint: null, id: i.toString(), selected: false, isLeftActive: false, isRightActive: false }
|
|
|
|
|
|
+ svgKeyframe = { keyframePoint: new Vector2(key.frame, this._heightScale - (keyframe_valueAsArray * middle)), rightControlPoint: outTangent, leftControlPoint: null, id: curveId, selected: false, isLeftActive: false, isRightActive: false }
|
|
if (outTangent !== null) {
|
|
if (outTangent !== null) {
|
|
data += ` C${outTangent.x} ${outTangent.y} `;
|
|
data += ` C${outTangent.x} ${outTangent.y} `;
|
|
}
|
|
}
|
|
|
|
|
|
} else {
|
|
} else {
|
|
|
|
|
|
- svgKeyframe = { keyframePoint: new Vector2(key.frame, this._heightScale - (key.value * middle)), rightControlPoint: outTangent, leftControlPoint: inTangent, id: i.toString(), selected: false, isLeftActive: false, isRightActive: false }
|
|
|
|
|
|
+ svgKeyframe = { keyframePoint: new Vector2(key.frame, this._heightScale - (keyframe_valueAsArray * middle)), rightControlPoint: outTangent, leftControlPoint: inTangent, id: curveId, selected: false, isLeftActive: false, isRightActive: false }
|
|
|
|
|
|
if (outTangent !== null && inTangent !== null) {
|
|
if (outTangent !== null && inTangent !== null) {
|
|
data += ` ${inTangent.x} ${inTangent.y} ${svgKeyframe.keyframePoint.x} ${svgKeyframe.keyframePoint.y} C${outTangent.x} ${outTangent.y} `
|
|
data += ` ${inTangent.x} ${inTangent.y} ${svgKeyframe.keyframePoint.x} ${svgKeyframe.keyframePoint.y} C${outTangent.x} ${outTangent.y} `
|
|
@@ -706,7 +794,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
}
|
|
}
|
|
|
|
|
|
if (this.state) {
|
|
if (this.state) {
|
|
- let prev = this.state.svgKeyframes?.find(kf => kf.id === i.toString());
|
|
|
|
|
|
+ let prev = this.state.svgKeyframes?.find(kf => kf.id === curveId);
|
|
if (prev) {
|
|
if (prev) {
|
|
svgKeyframe.isLeftActive = prev?.isLeftActive;
|
|
svgKeyframe.isLeftActive = prev?.isLeftActive;
|
|
svgKeyframe.isRightActive = prev?.isRightActive;
|
|
svgKeyframe.isRightActive = prev?.isRightActive;
|
|
@@ -730,6 +818,8 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
|
|
|
|
keyframes.forEach((key, i) => {
|
|
keyframes.forEach((key, i) => {
|
|
|
|
|
|
|
|
+ // identify type of value and split...
|
|
|
|
+
|
|
// Gets previous initial point of curve segment
|
|
// Gets previous initial point of curve segment
|
|
var pointA = new Vector2(0, 0);
|
|
var pointA = new Vector2(0, 0);
|
|
if (i === 0) {
|
|
if (i === 0) {
|
|
@@ -847,25 +937,50 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
* Core functions
|
|
* Core functions
|
|
* This section handles main Curve Editor Functions.
|
|
* This section handles main Curve Editor Functions.
|
|
*/
|
|
*/
|
|
- selectAnimation(animation: Animation, axis?: string) {
|
|
|
|
|
|
+ selectAnimation(animation: Animation, coordinate?: SelectedCoordinate) {
|
|
|
|
|
|
- if (!axis){
|
|
|
|
- this.playStopAnimation();
|
|
|
|
-
|
|
|
|
- this._svgKeyframes = [];
|
|
|
|
|
|
+ this._svgKeyframes = [];
|
|
|
|
+ let updatedPath;
|
|
|
|
+ let filteredSvgKeys;
|
|
|
|
|
|
- const pathData = this.getPathData(animation);
|
|
|
|
|
|
+ if (coordinate === undefined) {
|
|
|
|
+ this.playStopAnimation();
|
|
|
|
|
|
- let lastFrame = animation.getHighestFrame();
|
|
|
|
|
|
+ updatedPath = this.getPathData(animation);
|
|
|
|
|
|
- if (pathData === "") {
|
|
|
|
|
|
+ if (updatedPath === undefined) {
|
|
console.log("no keyframes in this animation");
|
|
console.log("no keyframes in this animation");
|
|
}
|
|
}
|
|
|
|
|
|
- this.setState({ selected: animation, currentPathData: pathData, svgKeyframes: this._svgKeyframes, lastFrame: lastFrame });
|
|
|
|
} else {
|
|
} else {
|
|
- console.log(axis); // This will handle animations that are not Float type
|
|
|
|
|
|
+ let curves = this.getPathData(animation);
|
|
|
|
+ if (curves === undefined) {
|
|
|
|
+ console.log("no keyframes in this animation");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ updatedPath = [];
|
|
|
|
+
|
|
|
|
+ filteredSvgKeys = this._svgKeyframes?.filter(curve => {
|
|
|
|
+ let id = parseInt(curve.id.split('_')[2]);
|
|
|
|
+ if (id === coordinate) {
|
|
|
|
+ return true
|
|
|
|
+ } else {
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ curves?.map(curve => {
|
|
|
|
+ let id = parseInt(curve.id.split('_')[2]);
|
|
|
|
+ if (id === coordinate) {
|
|
|
|
+ updatedPath.push(curve);
|
|
|
|
+ }
|
|
|
|
+ })
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // check for empty svgKeyframes, lastframe, selected
|
|
|
|
+ this.setState({ selected: animation, svgKeyframes: coordinate !== undefined ? filteredSvgKeys : this._svgKeyframes, selectedPathData: updatedPath });
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
isAnimationPlaying() {
|
|
isAnimationPlaying() {
|
|
@@ -877,34 +992,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
return this.props.scene.getAllAnimatablesByTarget(target).length > 0;
|
|
return this.props.scene.getAllAnimatablesByTarget(target).length > 0;
|
|
}
|
|
}
|
|
|
|
|
|
- playPause(direction: number) {
|
|
|
|
- if (this.state.selected) {
|
|
|
|
- let target = this.props.entity;
|
|
|
|
- if (this.props.entity instanceof TargetedAnimation) {
|
|
|
|
- target = this.props.entity.target;
|
|
|
|
- }
|
|
|
|
- if (this.state.isPlaying) {
|
|
|
|
- this.props.scene.stopAnimation(target);
|
|
|
|
- this.setState({ isPlaying: false })
|
|
|
|
- this._isPlaying = false;
|
|
|
|
- this.forceUpdate();
|
|
|
|
- } else {
|
|
|
|
- let keys = this.state.selected.getKeys();
|
|
|
|
- let firstFrame = keys[0].frame;
|
|
|
|
- let LastFrame = keys[keys.length - 1].frame;
|
|
|
|
- if (direction === 1){
|
|
|
|
- this.props.scene.beginAnimation(target, firstFrame, LastFrame, true);
|
|
|
|
- }
|
|
|
|
- if (direction === -1){
|
|
|
|
- this.props.scene.beginAnimation(target, LastFrame, firstFrame, true);
|
|
|
|
- }
|
|
|
|
- this._isPlaying = true;
|
|
|
|
- this.setState({ isPlaying: true });
|
|
|
|
- this.forceUpdate();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
playStopAnimation() {
|
|
playStopAnimation() {
|
|
let target = this.props.entity;
|
|
let target = this.props.entity;
|
|
if (this.props.entity instanceof TargetedAnimation) {
|
|
if (this.props.entity instanceof TargetedAnimation) {
|
|
@@ -920,12 +1007,10 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- analizeAnimation(animation: Animation | null) {
|
|
|
|
|
|
+ analizeAnimationForLerp(animation: Animation | null) {
|
|
if (animation !== null) {
|
|
if (animation !== null) {
|
|
- const { easingMode, easingType } = this.getAnimationProperties(animation);
|
|
|
|
- let hasDefinedTangents = this.getAnimationData(animation).usesTangents;
|
|
|
|
-
|
|
|
|
- if (easingType === undefined && easingMode === undefined && !hasDefinedTangents) {
|
|
|
|
|
|
+ const { easingMode, easingType, usesTangents } = this.getAnimationData(animation);
|
|
|
|
+ if (easingType === undefined && easingMode === undefined && !usesTangents) {
|
|
return true;
|
|
return true;
|
|
} else {
|
|
} else {
|
|
return false;
|
|
return false;
|
|
@@ -975,6 +1060,34 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ playPause(direction: number) {
|
|
|
|
+ if (this.state.selected) {
|
|
|
|
+ let target = this.props.entity;
|
|
|
|
+ if (this.props.entity instanceof TargetedAnimation) {
|
|
|
|
+ target = this.props.entity.target;
|
|
|
|
+ }
|
|
|
|
+ if (this.state.isPlaying) {
|
|
|
|
+ this.props.scene.stopAnimation(target);
|
|
|
|
+ this.setState({ isPlaying: false })
|
|
|
|
+ this._isPlaying = false;
|
|
|
|
+ this.forceUpdate();
|
|
|
|
+ } else {
|
|
|
|
+ let keys = this.state.selected.getKeys();
|
|
|
|
+ let firstFrame = keys[0].frame;
|
|
|
|
+ let LastFrame = keys[keys.length - 1].frame;
|
|
|
|
+ if (direction === 1) {
|
|
|
|
+ this.props.scene.beginAnimation(target, firstFrame, LastFrame, true);
|
|
|
|
+ }
|
|
|
|
+ if (direction === -1) {
|
|
|
|
+ this.props.scene.beginAnimation(target, LastFrame, firstFrame, true);
|
|
|
|
+ }
|
|
|
|
+ this._isPlaying = true;
|
|
|
|
+ this.setState({ isPlaying: true });
|
|
|
|
+ this.forceUpdate();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
render() {
|
|
render() {
|
|
return (
|
|
return (
|
|
<div id="animation-curve-editor">
|
|
<div id="animation-curve-editor">
|
|
@@ -996,16 +1109,16 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
lerpMode={this.state.lerpMode}
|
|
lerpMode={this.state.lerpMode}
|
|
setLerpMode={() => this.setLerpMode()}
|
|
setLerpMode={() => this.setLerpMode()}
|
|
flatTangent={() => this.setFlatTangent()} />
|
|
flatTangent={() => this.setFlatTangent()} />
|
|
-
|
|
|
|
|
|
+
|
|
<div className="content">
|
|
<div className="content">
|
|
<div className="row">
|
|
<div className="row">
|
|
- <EditorControls selectAnimation={(animation: Animation, axis?: string) => this.selectAnimation(animation, axis)}
|
|
|
|
- isTargetedAnimation={this._isTargetedAnimation}
|
|
|
|
- entity={this.props.entity}
|
|
|
|
- selected={this.state.selected}
|
|
|
|
- setNotificationMessage={(message: string) => { this.setState({notification: message})}}
|
|
|
|
|
|
+ <EditorControls selectAnimation={(animation: Animation, axis?: SelectedCoordinate) => this.selectAnimation(animation, axis)}
|
|
|
|
+ isTargetedAnimation={this._isTargetedAnimation}
|
|
|
|
+ entity={this.props.entity}
|
|
|
|
+ selected={this.state.selected}
|
|
|
|
+ setNotificationMessage={(message: string) => { this.setState({ notification: message }) }}
|
|
/>
|
|
/>
|
|
-
|
|
|
|
|
|
+
|
|
<div ref={this._graphCanvas} className="graph-chart" onWheel={(e) => this.zoom(e)} >
|
|
<div ref={this._graphCanvas} className="graph-chart" onWheel={(e) => this.zoom(e)} >
|
|
|
|
|
|
<Playhead frame={this.state.currentFrame} offset={this.state.playheadOffset} />
|
|
<Playhead frame={this.state.currentFrame} offset={this.state.playheadOffset} />
|
|
@@ -1015,7 +1128,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
viewBoxScale={this.state.frameAxisLength.length} scale={this.state.scale}
|
|
viewBoxScale={this.state.frameAxisLength.length} scale={this.state.scale}
|
|
keyframeSvgPoints={this.state.svgKeyframes}
|
|
keyframeSvgPoints={this.state.svgKeyframes}
|
|
selectedControlPoint={(type: string, id: string) => this.selectedControlPoint(type, id)}
|
|
selectedControlPoint={(type: string, id: string) => this.selectedControlPoint(type, id)}
|
|
- updatePosition={(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number) => this.renderPoints(updatedSvgKeyFrame, index)}>
|
|
|
|
|
|
+ updatePosition={(updatedSvgKeyFrame: IKeyframeSvgPoint, id: string) => this.renderPoints(updatedSvgKeyFrame, id)}>
|
|
|
|
|
|
{/* Frame Labels */}
|
|
{/* Frame Labels */}
|
|
{ /* Vertical Grid */}
|
|
{ /* Vertical Grid */}
|
|
@@ -1034,14 +1147,13 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
|
|
|
|
|
|
})}
|
|
})}
|
|
|
|
|
|
- { /* Single Curve -Modify this for multiple selection and view */}
|
|
|
|
- <path ref={this._selectedCurve} pathLength={this.state.lastFrame} id="curve" d={this.state.currentPathData} style={{ stroke: 'red', fill: 'none', strokeWidth: '0.5' }}></path>
|
|
|
|
|
|
+ { /* Multiple Curves */}
|
|
|
|
+ {
|
|
|
|
+ this.state.selectedPathData?.map((curve, i) =>
|
|
|
|
+ <path key={i} ref={curve.domCurve} pathLength={curve.pathLength} id="curve" d={curve.pathData} style={{ stroke: curve.color, fill: 'none', strokeWidth: '0.5' }}></path>
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
|
|
- {this._frames && this._frames.map(frame =>
|
|
|
|
- <svg x={frame.x} y={frame.y} style={{ overflow: 'visible' }}>
|
|
|
|
- <circle cx="0" cy="0" r="2" stroke="black" strokeWidth="1" fill="white" />
|
|
|
|
- </svg>
|
|
|
|
- )}
|
|
|
|
|
|
|
|
</SvgDraggableArea>
|
|
</SvgDraggableArea>
|
|
|
|
|