From 545508987903be8c2f361bbee8b3beae683c73b5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 26 Oct 2023 23:23:48 -0400
Subject: a variety of fixes to the animation timeline to make it make some
 sense. lots still broken.

---
 src/client/views/animationtimeline/Timeline.tsx | 49 ++++++++++++++-----------
 1 file changed, 27 insertions(+), 22 deletions(-)

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

diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 7ca13756a..3675238fd 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -1,28 +1,28 @@
+import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 import { faBackward, faForward, faGripLines, faPauseCircle, faPlayCircle } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction, trace } from 'mobx';
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
-import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from '../../../Utils';
+import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
+import { DocumentType } from '../../documents/DocumentTypes';
+import clamp from '../../util/clamp';
 import { FieldViewProps } from '../nodes/FieldView';
-import { KeyframeFunc } from './Keyframe';
+import { RegionHelpers } from './Region';
 import './Timeline.scss';
 import { TimelineOverview } from './TimelineOverview';
 import { Track } from './Track';
-import clamp from '../../util/clamp';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 
 /**
- * Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are 
+ * Timeline class controls most of timeline functions besides individual region and track mechanism. Main functions are 
  * zooming, panning, currentBarX (scrubber movement). Most of the UI stuff is also handled here. You shouldn't really make 
  * any logical changes here. Most work is needed on UI. 
  * 
  * The hierarchy works this way: 
  * 
- *              Timeline.tsx --> Track.tsx --> Keyframe.tsx 
+ *              Timeline.tsx --> Track.tsx --> Region.tsx
                       |                              |
                       |                   TimelineMenu.tsx (timeline's custom contextmenu)
                       |
@@ -58,7 +58,6 @@ export class Timeline extends React.Component<FieldViewProps> {
     //boolean vars and instance vars
     @observable private _currentBarX: number = 0;
     @observable private _windSpeed: number = 1;
-    @observable private _isPlaying: boolean = false; //scrubber playing
     @observable private _totalLength: number = 0;
     @observable private _visibleLength: number = 0;
     @observable private _visibleStart: number = 0;
@@ -69,6 +68,8 @@ export class Timeline extends React.Component<FieldViewProps> {
     @observable private _playButton = faPlayCircle;
     @observable private _titleHeight = 0;
 
+    @observable public IsPlaying: boolean = false; //scrubber playing
+
     /**
      * collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit.
      */
@@ -144,14 +145,17 @@ export class Timeline extends React.Component<FieldViewProps> {
     @action
     play = () => {
         const playTimeline = () => {
-            if (this._isPlaying) {
+            if (this.IsPlaying) {
                 this.changeCurrentBarX(this._currentBarX >= this._totalLength ? 0 : this._currentBarX + this._windSpeed);
                 setTimeout(playTimeline, 15);
             }
         };
-        this._isPlaying = !this._isPlaying;
-        this._playButton = this._isPlaying ? faPauseCircle : faPlayCircle;
-        this._isPlaying && playTimeline();
+        Array.from(this.mapOfTracks.values())
+            .filter(key => key)
+            .forEach(key => key!.saveKeyframe());
+        this.IsPlaying = !this.IsPlaying;
+        this._playButton = this.IsPlaying ? faPauseCircle : faPlayCircle;
+        this.IsPlaying && playTimeline();
     };
 
     /**
@@ -221,7 +225,7 @@ export class Timeline extends React.Component<FieldViewProps> {
         if (this._visibleStart + this._visibleLength + 20 >= this._totalLength) {
             this._visibleStart -= e.movementX;
             this._totalLength -= e.movementX;
-            this._time -= KeyframeFunc.convertPixelTime(e.movementX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
+            this._time -= RegionHelpers.convertPixelTime(e.movementX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
             this.props.Document.AnimationLength = this._time;
         }
         return false;
@@ -278,11 +282,11 @@ export class Timeline extends React.Component<FieldViewProps> {
         e.preventDefault();
         e.stopPropagation();
         const offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left;
-        const prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, 'mili', 'time', this._tickSpacing, this._tickIncrement);
-        const prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
+        const prevTime = RegionHelpers.convertPixelTime(this._visibleStart + offset, 'mili', 'time', this._tickSpacing, this._tickIncrement);
+        const prevCurrent = RegionHelpers.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
         this.zoom(e.deltaY < 0);
-        const currPixel = KeyframeFunc.convertPixelTime(prevTime, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
-        const currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+        const currPixel = RegionHelpers.convertPixelTime(prevTime, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+        const currCurrent = RegionHelpers.convertPixelTime(prevCurrent, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
         this._infoContainer.current!.scrollLeft = currPixel - offset;
         this._visibleStart = currPixel - offset > 0 ? currPixel - offset : 0;
         this._visibleStart += this._visibleLength + this._visibleStart > this._totalLength ? this._totalLength - (this._visibleStart + this._visibleLength) : 0;
@@ -478,7 +482,7 @@ export class Timeline extends React.Component<FieldViewProps> {
 
     // @computed
     getCurrentTime = () => {
-        const current = KeyframeFunc.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
+        const current = RegionHelpers.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
         return this.toReadTime(current > this._time ? this._time : current);
     };
 
@@ -505,13 +509,13 @@ export class Timeline extends React.Component<FieldViewProps> {
     @action
     toAuthoring = () => {
         this._time = Math.ceil((this.findLongestTime() ?? 1) / 100000) * 100000;
-        this._totalLength = KeyframeFunc.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+        this._totalLength = RegionHelpers.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
     };
 
     @action
     toPlay = () => {
         this._time = this.findLongestTime();
-        this._totalLength = KeyframeFunc.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+        this._totalLength = RegionHelpers.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
     };
 
     /**
@@ -535,6 +539,7 @@ export class Timeline extends React.Component<FieldViewProps> {
                                 {this.children.map(doc => (
                                     <Track
                                         ref={ref => this.mapOfTracks.push(ref)}
+                                        timeline={this}
                                         animatedDoc={doc}
                                         currentBarX={this._currentBarX}
                                         changeCurrentBarX={this.changeCurrentBarX}
-- 
cgit v1.2.3-70-g09d2


From b2233984df663be6554c935fe9e40f4778237e67 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 27 Oct 2023 10:23:21 -0400
Subject: removed async nonsense from tracks and regions.  added some comments.

---
 src/client/views/animationtimeline/Region.tsx   |  4 +--
 src/client/views/animationtimeline/Timeline.tsx |  9 ++++-
 src/client/views/animationtimeline/Track.tsx    | 47 +++++++++++++------------
 3 files changed, 34 insertions(+), 26 deletions(-)

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

diff --git a/src/client/views/animationtimeline/Region.tsx b/src/client/views/animationtimeline/Region.tsx
index df00924c6..53c5c4718 100644
--- a/src/client/views/animationtimeline/Region.tsx
+++ b/src/client/views/animationtimeline/Region.tsx
@@ -313,7 +313,7 @@ export class Region extends React.Component<IProps> {
     };
 
     @action
-    createKeyframe = async (clientX: number) => {
+    createKeyframe = (clientX: number) => {
         this._mouseToggled = true;
         const bar = this._bar.current!;
         const offset = RegionHelpers.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement);
@@ -327,7 +327,7 @@ export class Region extends React.Component<IProps> {
     };
 
     @action
-    moveKeyframe = async (e: React.MouseEvent, kf: Doc) => {
+    moveKeyframe = (e: React.MouseEvent, kf: Doc) => {
         e.preventDefault();
         e.stopPropagation();
         this.props.changeCurrentBarX(RegionHelpers.convertPixelTime(NumCast(kf.time!), 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement));
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 3675238fd..71517d4fe 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -22,13 +22,20 @@ import { Track } from './Track';
  * 
  * The hierarchy works this way: 
  * 
- *              Timeline.tsx --> Track.tsx --> Region.tsx
+ *              Timeline.tsx --> Track.tsx --> Region.tsx 
                       |                              |
                       |                   TimelineMenu.tsx (timeline's custom contextmenu)
                       |
                       |
                 TimelineOverview.tsx (youtube like dragging thing is play mode, complex dragging thing in editing mode)
 
+                
+                Timeline (Track[])
+                      Track(Region[],animatedDoc)    ->   Region1(K[])    Region2 ... 
+                                                          F1 K1 K2...FL    K1 K2 K...
+                                                           K(x,y,_width,opacity)
+                      ...
+                      Track
 
     Most style changes are in SCSS file. 
     If you have any questions, email me or text me. 
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 2fd062a88..b90ff5eaf 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -44,18 +44,19 @@ export class Track extends React.Component<IProps> {
         return NumCast(RegionHelpers.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement));
     }
 
-    async componentDidMount() {
-        const regions = await DocListCastAsync(this.props.animatedDoc.regions);
-        if (!regions) this.props.animatedDoc.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;
-        runInAction(() => (this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT)); //for responsiveness
-        this._timelineVisibleReaction = this.timelineVisibleReaction();
-        this._currentBarXReaction = this.currentBarXReaction();
-        if (DocListCast(this.props.animatedDoc.regions).length === 0) this.createRegion(this.time);
-        this.props.animatedDoc.hidden = false;
-        this.props.animatedDoc.opacity = 1;
-        // this.autoCreateKeyframe();
+    componentDidMount() {
+        DocListCastAsync(this.props.animatedDoc.regions).then(regions => {
+            if (!regions) this.props.animatedDoc.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;
+            runInAction(() => (this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT)); //for responsiveness
+            this._timelineVisibleReaction = this.timelineVisibleReaction();
+            this._currentBarXReaction = this.currentBarXReaction();
+            if (DocListCast(this.props.animatedDoc.regions).length === 0) this.createRegion(this.time);
+            this.props.animatedDoc.hidden = false;
+            this.props.animatedDoc.opacity = 1;
+            // this.autoCreateKeyframe();
+        });
     }
 
     /**
@@ -86,7 +87,7 @@ export class Track extends React.Component<IProps> {
      *
      */
     @action
-    saveKeyframe = async () => {
+    saveKeyframe = () => {
         if (this.props.timeline.IsPlaying || !this.saveStateRegion || !this.saveStateKf) {
             this.saveStateKf = undefined;
             this.saveStateRegion = undefined;
@@ -223,23 +224,23 @@ export class Track extends React.Component<IProps> {
      * when scrubber position changes. Need to edit the logic
      */
     @action
-    timeChange = async () => {
+    timeChange = () => {
         if (this.saveStateKf !== undefined || this._newKeyframe) {
-            await this.saveKeyframe();
+            this.saveKeyframe();
         }
-        const regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on
+        const regiondata = this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on
         if (regiondata) {
-            const leftkf: Doc | undefined = await RegionHelpers.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists
-            const rightkf: Doc | undefined = await RegionHelpers.calcMinRight(regiondata, this.time); //right keyframe, if it exists
-            const currentkf: Doc | undefined = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe
+            const leftkf: Doc | undefined = RegionHelpers.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists
+            const rightkf: Doc | undefined = RegionHelpers.calcMinRight(regiondata, this.time); //right keyframe, if it exists
+            const currentkf: Doc | undefined = this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe
             if (currentkf) {
-                await this.applyKeys(currentkf);
+                this.applyKeys(currentkf);
                 runInAction(() => {
                     this.saveStateKf = currentkf;
                     this.saveStateRegion = regiondata;
                 });
             } else if (leftkf && rightkf) {
-                await this.interpolate(leftkf, rightkf);
+                this.interpolate(leftkf, rightkf);
             }
         }
     };
@@ -249,7 +250,7 @@ export class Track extends React.Component<IProps> {
      * need to change the logic here
      */
     @action
-    private applyKeys = async (kf: Doc) => {
+    private applyKeys = (kf: Doc) => {
         this.primitiveWhitelist.forEach(key => {
             if (!kf[key]) {
                 this.props.animatedDoc[key] = undefined;
@@ -277,7 +278,7 @@ export class Track extends React.Component<IProps> {
      * basic linear interpolation function
      */
     @action
-    interpolate = async (left: Doc, right: Doc) => {
+    interpolate = (left: Doc, right: Doc) => {
         this.primitiveWhitelist.forEach(key => {
             if (typeof left[key] === 'number' && typeof right[key] === 'number') {
                 //if it is number, interpolate
-- 
cgit v1.2.3-70-g09d2


From a866baea5fdbf30650cbfbf4aa383019ef61ec3d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 27 Oct 2023 13:39:04 -0400
Subject: added collection itself to timeline

---
 src/client/views/animationtimeline/Timeline.tsx | 14 +++-----------
 src/client/views/animationtimeline/Track.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx         |  2 +-
 3 files changed, 5 insertions(+), 13 deletions(-)

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

diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 71517d4fe..4be3b05ab 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -543,7 +543,7 @@ export class Timeline extends React.Component<FieldViewProps> {
                                 <div key="timeline_scrubberhead" className="scrubberhead" onPointerDown={this.onScrubberDown}></div>
                             </div>
                             <div key="timeline_trackbox" className="trackbox" ref={this._trackbox} style={{ width: `${this._totalLength}px` }}>
-                                {this.children.map(doc => (
+                                {[...this.children, this.props.Document].map(doc => (
                                     <Track
                                         ref={ref => this.mapOfTracks.push(ref)}
                                         timeline={this}
@@ -562,16 +562,8 @@ export class Timeline extends React.Component<FieldViewProps> {
                         </div>
                         <div className="currentTime">Current: {this.getCurrentTime()}</div>
                         <div key="timeline_title" className="title-container" ref={this._titleContainer}>
-                            {this.children.map(doc => (
-                                <div
-                                    style={{ height: `${this._titleHeight}px` }}
-                                    className="datapane"
-                                    onPointerOver={() => {
-                                        Doc.BrushDoc(doc);
-                                    }}
-                                    onPointerOut={() => {
-                                        Doc.UnBrushDoc(doc);
-                                    }}>
+                            {[...this.children, this.props.Document].map(doc => (
+                                <div style={{ height: `${this._titleHeight}px` }} className="datapane" onPointerOver={() => Doc.BrushDoc(doc)} onPointerOut={() => Doc.UnBrushDoc(doc)}>
                                     <p>{StrCast(doc.title)}</p>
                                 </div>
                             ))}
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index b90ff5eaf..dfab849a4 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -34,7 +34,7 @@ export class Track extends React.Component<IProps> {
     @observable private _newKeyframe: boolean = false;
     private readonly MAX_TITLE_HEIGHT = 75;
     @observable private _trackHeight = 0;
-    private primitiveWhitelist = ['x', 'y', '_width', '_height', '_rotation', 'opacity', '_layout_scrollTop'];
+    private primitiveWhitelist = ['x', 'y', '_freeform_panX', '_freeform_panY', '_width', '_height', '_rotation', 'opacity', '_layout_scrollTop'];
     private objectWhitelist = ['data'];
 
     @computed private get regions() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4a43ffe3e..43be4b724 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1312,7 +1312,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents,
                 }}>
                 <>
-                    {DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[Animation], this.rootDoc)}
+                    {this._componentView instanceof KeyValueBox ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[Animation], this.rootDoc)}
                     {borderPath?.jsx}
                 </>
             </div>
-- 
cgit v1.2.3-70-g09d2

