From ab4297e59e142f70b420831a73c3f3e4506eee15 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 28 Apr 2020 23:00:15 -0400
Subject: more animation timeline cleanup

---
 src/client/views/animationtimeline/Track.tsx | 239 +++++++++++----------------
 1 file changed, 98 insertions(+), 141 deletions(-)

(limited to 'src/client/views/animationtimeline/Track.tsx')

diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 0e3c209dc..9e2ec8886 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -1,15 +1,15 @@
-import * as React from "react";
+import { action, computed, intercept, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun, toJS, isObservableArray, IObservableArray, trace, observe, intercept } from "mobx";
-import "./Track.scss";
-import { Doc, DocListCastAsync, DocListCast, Field } from "../../../new_fields/Doc";
-import { listSpec } from "../../../new_fields/Schema";
-import { FieldValue, Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types";
-import { List } from "../../../new_fields/List";
-import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe";
-import { Transform } from "../../util/Transform";
+import * as React from "react";
+import { Doc, DocListCast, Opt } from "../../../new_fields/Doc";
 import { Copy } from "../../../new_fields/FieldSymbols";
+import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
+import { listSpec } from "../../../new_fields/Schema";
+import { Cast, NumCast } from "../../../new_fields/Types";
+import { Transform } from "../../util/Transform";
+import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe";
+import "./Track.scss";
 
 interface IProps {
     node: Doc;
@@ -43,48 +43,41 @@ export class Track extends React.Component<IProps> {
         "data"
     ];
 
-    @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List<Doc>; }
+    @computed private get regions() { return DocListCast(this.props.node.regions); }
     @computed private get time() { return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); }
 
     ////////// life cycle functions///////////////
     componentWillMount() {
-        runInAction(() => {
-            if (!this.props.node.regions) this.props.node.regions = new List<Doc>(); //if there is no region, then create new doc to store stuff
-            //these two lines are exactly same from timeline.tsx 
-            let relativeHeight = window.innerHeight / 20;
-            this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness
-        });
+        if (!this.props.node.regions) this.props.node.regions = new List<Doc>(); //if there is no region, then create new doc to store stuff
+        //these two lines are exactly same from timeline.tsx 
+        const relativeHeight = window.innerHeight / 20;
+        this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness
     }
 
     componentDidMount() {
-        runInAction(async () => {
-            this._timelineVisibleReaction = this.timelineVisibleReaction();
-            this._currentBarXReaction = this.currentBarXReaction();
-            if (this.regions.length === 0) this.createRegion(this.time);
-            this.props.node.hidden = false;
-            this.props.node.opacity = 1;
-            // this.autoCreateKeyframe();
-        });
+        this._timelineVisibleReaction = this.timelineVisibleReaction();
+        this._currentBarXReaction = this.currentBarXReaction();
+        if (this.regions.length === 0) this.createRegion(this.time);
+        this.props.node.hidden = false;
+        this.props.node.opacity = 1;
+        // this.autoCreateKeyframe();
     }
 
     /**
      * mainly for disposing reactions
      */
     componentWillUnmount() {
-        runInAction(() => {
-            //disposing reactions 
-            if (this._currentBarXReaction) this._currentBarXReaction();
-            if (this._timelineVisibleReaction) this._timelineVisibleReaction();
-            if (this._autoKfReaction) this._autoKfReaction();
-        });
+        this._currentBarXReaction?.();
+        this._timelineVisibleReaction?.();
+        this._autoKfReaction?.();
     }
     ////////////////////////////////
 
 
     getLastRegionTime = () => {
         let lastTime: number = 0;
-        let lastRegion: (Doc | undefined);
-        DocListCast(this.regions).forEach(region => {
+        let lastRegion: Opt<Doc>;
+        this.regions.forEach(region => {
             const time = NumCast(region.position);
             if (lastTime <= time) {
                 lastTime = time;
@@ -100,41 +93,34 @@ export class Track extends React.Component<IProps> {
      */
     @action
     saveKeyframe = async () => {
-        console.log("saving keyframe");
-        let keyframes: List<Doc> = (Cast(this.saveStateRegion!.keyframes, listSpec(Doc)) as List<Doc>);
-        let kfIndex: number = keyframes.indexOf(this.saveStateKf!);
+        let keyframes = Cast(this.saveStateRegion?.keyframes, listSpec(Doc)) as List<Doc>;
+        let kfIndex = keyframes.indexOf(this.saveStateKf!);
         let kf = keyframes[kfIndex] as Doc; //index in the keyframe
         if (this._newKeyframe) {
-            console.log("new keyframe registering");
-            let kfList = DocListCast(this.saveStateRegion!.keyframes);
-            kfList.forEach(kf => {
-                kf.key = this.makeCopy();
-                if (kfList.indexOf(kf) === 0 || kfList.indexOf(kf) === 3) {
-                    (kf.key as Doc).opacity = 0.1;
-                } else {
-                    (kf.key as Doc).opacity = 1;
-                }
+            DocListCast(this.saveStateRegion?.keyframes).forEach((kf, index) => {
+                this.copyDocDataToKeyFrame(kf);
+                kf.opacity = (index === 0 || index === 3) ? 0.1 : 1;
             });
             this._newKeyframe = false;
         }
         if (!kf) return;
         if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades
-            kf.key = this.makeCopy();
-            let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists
-            let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists 
-            if (leftkf!.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades
-                let edge: (Doc | undefined) = await KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf!);
-                edge!.key = this.makeCopy();
-                leftkf!.key = this.makeCopy();
-                (Cast(edge!.key, Doc)! as Doc).opacity = 0.1;
-                (Cast(leftkf!.key, Doc)! as Doc).opacity = 1;
+            this.copyDocDataToKeyFrame(kf);
+            let leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists
+            let rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists 
+            if (leftkf?.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades
+                let edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf);
+                edge && this.copyDocDataToKeyFrame(edge);
+                leftkf && this.copyDocDataToKeyFrame(leftkf);
+                edge && (edge!.opacity = 0.1);
+                leftkf && (leftkf!.opacity = 1);
             }
-            if (rightkf!.type === KeyframeFunc.KeyframeType.fade) {
-                let edge: (Doc | undefined) = await KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf!);
-                edge!.key = this.makeCopy();
-                rightkf!.key = this.makeCopy();
-                (Cast(edge!.key, Doc)! as Doc).opacity = 0.1;
-                (Cast(rightkf!.key, Doc)! as Doc).opacity = 1;
+            if (rightkf?.type === KeyframeFunc.KeyframeType.fade) {
+                let edge = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf);
+                edge && this.copyDocDataToKeyFrame(edge);
+                rightkf && this.copyDocDataToKeyFrame(rightkf);
+                edge && (edge.opacity = 0.1);
+                rightkf && (rightkf.opacity = 1);
             }
         }
         keyframes[kfIndex] = kf;
@@ -148,24 +134,22 @@ export class Track extends React.Component<IProps> {
      */
     @action
     autoCreateKeyframe = () => {
-        const { node } = this.props;
-        const objects = this.objectWhitelist.map(key => node[key]);
+        const objects = this.objectWhitelist.map(key => this.props.node[key]);
         intercept(this.props.node, change => {
             console.log(change);
             return change;
         });
         return reaction(() => {
-            return [...this.primitiveWhitelist.map(key => node[key]), ...objects];
+            return [...this.primitiveWhitelist.map(key => this.props.node[key]), ...objects];
         }, (changed, reaction) => {
             //check for region 
-            this.findRegion(this.time).then((region) => {
-                if (region !== undefined) { //if region at scrub time exist
-                    let r = region as any as RegionData; //for some region is returning undefined... which is not the case
-                    if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot 
-                        this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default);
-                    }
+            const region = this.findRegion(this.time);
+            if (region !== undefined) { //if region at scrub time exist
+                let r = region as RegionData; //for some region is returning undefined... which is not the case
+                if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot 
+                    this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default);
                 }
-            });
+            }
         }, { fireImmediately: false });
     }
 
@@ -188,20 +172,19 @@ export class Track extends React.Component<IProps> {
     @action
     currentBarXReaction = () => {
         return reaction(() => this.props.currentBarX, () => {
-            this.findRegion(this.time).then((regiondata: (Doc | undefined)) => {
-                if (regiondata) {
-                    this.props.node.hidden = false;
-                    // if (!this._autoKfReaction) {
-                    //     // console.log("creating another reaction"); 
-                    //     // this._autoKfReaction = this.autoCreateKeyframe(); 
-                    // }
-                    this.timeChange();
-                } else {
-                    this.props.node.hidden = true;
-                    this.props.node.opacity = 0;
-                    //if (this._autoKfReaction) this._autoKfReaction(); 
-                }
-            });
+            const regiondata = this.findRegion(this.time);
+            if (regiondata) {
+                this.props.node.hidden = false;
+                // if (!this._autoKfReaction) {
+                //     // console.log("creating another reaction"); 
+                //     // this._autoKfReaction = this.autoCreateKeyframe(); 
+                // }
+                this.timeChange();
+            } else {
+                this.props.node.hidden = true;
+                this.props.node.opacity = 0;
+                //if (this._autoKfReaction) this._autoKfReaction(); 
+            }
         });
     }
 
@@ -214,13 +197,11 @@ export class Track extends React.Component<IProps> {
             return this.props.timelineVisible;
         }, isVisible => {
             if (isVisible) {
-                DocListCast(this.regions).forEach(region => {
-                    if (!BoolCast((Cast(region, Doc) as Doc).hasData)) {
-                        for (let i = 0; i < 4; i++) {
-                            DocListCast(((Cast(region, Doc) as Doc).keyframes as List<Doc>))[i].key = this.makeCopy();
-                            if (i === 0 || i === 3) { //manually inputing fades
-                                (DocListCast(((Cast(region, Doc) as Doc).keyframes as List<Doc>))[i].key! as Doc).opacity = 0.1;
-                            }
+                this.regions.filter(region => !region.hasData).forEach(region => {
+                    for (let i = 0; i < 4; i++) {
+                        this.copyDocDataToKeyFrame(DocListCast(region.keyframes)[i]);
+                        if (i === 0 || i === 3) { //manually inputing fades
+                            DocListCast(region.keyframes)[i].opacity = 0.1;
                         }
                     }
                 });
@@ -266,12 +247,11 @@ export class Track extends React.Component<IProps> {
      */
     @action
     private applyKeys = async (kf: Doc) => {
-        let kfNode = await Cast(kf.key, Doc) as Doc;
         this.primitiveWhitelist.forEach(key => {
-            if (!kfNode[key]) {
+            if (!kf[key]) {
                 this.props.node[key] = undefined;
             } else {
-                let stored = kfNode[key];
+                let stored = kf[key];
                 this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
             }
         });
@@ -297,16 +277,14 @@ export class Track extends React.Component<IProps> {
      */
     @action
     interpolate = async (left: Doc, right: Doc) => {
-        let leftNode = await (left.key) as Doc;
-        let rightNode = await (right.key) as Doc;
         this.primitiveWhitelist.forEach(key => {
-            if (leftNode[key] && rightNode[key] && typeof (leftNode[key]) === "number" && typeof (rightNode[key]) === "number") { //if it is number, interpolate
-                let dif = NumCast(rightNode[key]) - NumCast(leftNode[key]);
+            if (left[key] && right[key] && typeof (left[key]) === "number" && typeof (right[key]) === "number") { //if it is number, interpolate
+                let dif = NumCast(right[key]) - NumCast(left[key]);
                 let deltaLeft = this.time - NumCast(left.time);
                 let ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time));
-                this.props.node[key] = NumCast(leftNode[key]) + (dif * ratio);
+                this.props.node[key] = NumCast(left[key]) + (dif * ratio);
             } else { // case data 
-                let stored = leftNode[key];
+                let stored = left[key];
                 this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
             }
         });
@@ -316,17 +294,8 @@ export class Track extends React.Component<IProps> {
      * finds region that corresponds to specific time (is there a region at this time?)
      * linear O(n) (maybe possible to optimize this with other Data structures?)
      */
-    @action
-    findRegion = async (time: number) => {
-        let foundRegion: (Doc | undefined) = undefined;
-        let regions = await DocListCastAsync(this.regions);
-        regions!.forEach(region => {
-            region = region as RegionData;
-            if (time >= NumCast(region.position) && time <= (NumCast(region.position) + NumCast(region.duration))) {
-                foundRegion = region;
-            }
-        });
-        return foundRegion;
+    findRegion = (time: number) => {
+        return this.regions?.find(rd => (time >= NumCast(rd.position) && time <= (NumCast(rd.position) + NumCast(rd.duration))));
     }
 
 
@@ -345,8 +314,8 @@ export class Track extends React.Component<IProps> {
      * creates a region (KEYFRAME.TSX stuff). 
      */
     @action
-    createRegion = async (time: number) => {
-        if (await this.findRegion(time) === undefined) {  //check if there is a region where double clicking (prevents phantom regions)
+    createRegion = (time: number) => {
+        if (this.findRegion(time) === undefined) {  //check if there is a region where double clicking (prevents phantom regions)
             let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data
 
             regiondata.position = time; //set position
@@ -356,7 +325,7 @@ export class Track extends React.Component<IProps> {
                 regiondata.duration = rightRegion.position - regiondata.position;
             }
             if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) {
-                this.regions.push(regiondata);
+                Cast(this.props.node.regions, listSpec(Doc))?.push(regiondata);
                 this._newKeyframe = true;
                 this.saveStateRegion = regiondata;
                 return regiondata;
@@ -366,48 +335,36 @@ export class Track extends React.Component<IProps> {
 
     @action
     makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time 
-        let doclist = DocListCast(regiondata.keyframes)!;
-        let existingkf: (Doc | undefined) = undefined;
-        doclist.forEach(TK => {
-            if (TK.time === time) existingkf = TK;
-        });
+        const trackKeyFrames = DocListCast(regiondata.keyframes)!;
+        const existingkf = trackKeyFrames.find(TK => TK.time === time);
         if (existingkf) return existingkf;
         //else creates a new doc. 
-        let TK: Doc = new Doc();
-        TK.time = time;
-        TK.key = this.makeCopy();
-        TK.type = type;
+        const newKeyFrame: Doc = new Doc();
+        newKeyFrame.time = time;
+        newKeyFrame.type = type;
+        this.copyDocDataToKeyFrame(newKeyFrame);
         //assuming there are already keyframes (for keeping keyframes in order, sorted by time)
-        if (doclist.length === 0) regiondata.keyframes!.push(TK);
-        doclist.forEach(kf => {
-            let index = doclist.indexOf(kf);
-            let kfTime = NumCast(kf.time);
-            if ((kfTime < time && index === doclist.length - 1) || (kfTime < time && time < NumCast(doclist[index + 1].time))) {
-                regiondata.keyframes!.splice(index + 1, 0, TK);
-                return;
+        if (trackKeyFrames.length === 0) regiondata.keyframes!.push(newKeyFrame);
+        trackKeyFrames.map(kf => NumCast(kf.time)).forEach((kfTime, index) => {
+            if ((kfTime < time && index === trackKeyFrames.length - 1) || (kfTime < time && time < NumCast(trackKeyFrames[index + 1].time))) {
+                regiondata.keyframes!.splice(index + 1, 0, newKeyFrame);
             }
         });
-        return TK;
+        return newKeyFrame;
     }
 
     @action
-    makeCopy = () => {
-        let doc = new Doc();
-        this.primitiveWhitelist.forEach(key => {
-            let originalVal = this.props.node[key];
-            if (key === "data") {
-                console.log(originalVal);
-            }
-            doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : this.props.node[key];
+    copyDocDataToKeyFrame = (doc: Doc) => {
+        this.primitiveWhitelist.map(key => {
+            const originalVal = this.props.node[key];
+            doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : originalVal;
         });
-        return doc;
     }
 
     /**
      * UI sstuff here. Not really much to change
      */
     render() {
-        trace();
         return (
             <div className="track-container">
                 <div className="track">
@@ -415,7 +372,7 @@ export class Track extends React.Component<IProps> {
                         onDoubleClick={this.onInnerDoubleClick}
                         onPointerOver={() => Doc.BrushDoc(this.props.node)}
                         onPointerOut={() => Doc.UnBrushDoc(this.props.node)} >
-                        {DocListCast(this.regions).map((region, i) => {
+                        {this.regions?.map((region, i) => {
                             return <Keyframe key={`${i}`} {...this.props} RegionData={region} makeKeyData={this.makeKeyData} />;
                         })}
                     </div>
-- 
cgit v1.2.3-70-g09d2

