Alejandro Toledo пре 5 година
родитељ
комит
0718cb7535

+ 9 - 40
inspector/src/components/actionTabs/tabs/propertyGrids/animations/anchorSvgPoint.tsx

@@ -3,57 +3,26 @@ import * as React from "react";
 import { Vector2 } from 'babylonjs/Maths/math.vector';
 
 interface IAnchorSvgPointProps {
-   point: Vector2;
+   control: Vector2;
    anchor: Vector2;
+   active: boolean;
+   type: string;
+   index: string;
 }
 
 
-export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps, { position: Vector2, active: boolean, offset: Vector2 } >{ 
+export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps>{ 
     constructor(props: IAnchorSvgPointProps) {
         super(props);
-
-        this.state = { offset: new Vector2(this.props.anchor.x,this.props.anchor.y), position: new Vector2(this.props.anchor.x,this.props.anchor.y), active: false }
     }
-
-    handlePointerDown(e: React.PointerEvent<SVGSVGElement>) {
-        const el = e.target as SVGSVGElement;
-        const bbox = el.getBoundingClientRect();
-        const x = e.clientX - bbox.left;
-        const y = e.clientY - bbox.top;
-        el.setPointerCapture(e.pointerId);
-        this.setState({ active: true, position: new Vector2(x,y), offset: new Vector2(x, y) });
-      };
-
-    handlePointerMove(e: React.PointerEvent<SVGSVGElement>) {
-        const el = e.target as SVGSVGElement;
-        const bbox = el.getBoundingClientRect();
-        const x = e.clientX - bbox.left;
-        const y = e.clientY - bbox.top;
-        if (this.state.active) {
-
-            
-            this.setState({ 
-                active: true, 
-                position: new Vector2(this.state.position.x - (this.state.offset.x - x), this.state.position.y - (this.state.offset.y - y))
-            })
-
-        }
-      };
-
-    handlePointerUp(e: React.PointerEvent<SVGSVGElement>) {
-        this.setState({ active: false})
-      };
-
+    
     render() {
         return (
         <>
-            <svg x={this.props.anchor.x} y={this.props.anchor.y} style={{overflow:'visible'}}       
-            onPointerDown={(e) => this.handlePointerDown(e)}
-            onPointerUp={(e) => this.handlePointerUp(e)}
-            onPointerMove={(e) => this.handlePointerMove(e)}>
-                <circle cx="0" cy="0"  r="0.75" stroke="none" strokeWidth="0" fill={this.state.active ? "blue" : "black"}   />
+            <svg x={this.props.control.x} y={this.props.control.y} style={{overflow:'visible'}}>
+                <circle type={this.props.type} data-id={this.props.index} className="draggable control-point" cx="0" cy="0"  r="2" stroke="none" strokeWidth="0" fill={this.props.active ? "blue" : "black"}   />
             </svg>
-            <line x1={this.props.point.x} y1={this.props.point.y} x2={this.props.anchor.x} y2={this.props.anchor.y} stroke="green" strokeWidth="0.75" />
+            <line x1={this.props.anchor.x} y1={this.props.anchor.y} x2={this.props.control.x} y2={this.props.control.y} stroke="green" strokeWidth="0.75" />
         </>
         )
     }

+ 105 - 99
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -5,8 +5,10 @@ import { Animation } from 'babylonjs/Animations/animation';
 import { Vector2 } from 'babylonjs/Maths/math.vector';
 import { EasingFunction } from 'babylonjs/Animations/easing';
 import { IAnimationKey } from 'babylonjs/Animations/animationKey';
-import { AnchorSvgPoint } from './anchorSvgPoint';
-import { KeyframeSvgPoint } from './keyframeSvgPoint';
+import { IKeyframeSvgPoint } from './keyframeSvgPoint';
+import { SvgDraggableArea } from './svgDraggableArea';
+import { Scene } from "babylonjs/scene";
+import { Animatable } from 'babylonjs/Animations/animatable';
 
 require("./curveEditor.scss");
 
@@ -15,18 +17,19 @@ interface IAnimationCurveEditorComponentProps {
     title: string;
     animations: Animation[];
     entityName: string;
+    scene: Scene;
     entity: Animatable;
 }
 
-export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, { animations: Animation[], animationName: string, selectedProperty:string, isOpen: boolean, selected: Animation, currentPathData: string | undefined, anchorPoints: { point: Vector2, anchor: Vector2 }[] | undefined, keyframes: Vector2[] | undefined }> {
+export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, { animations: Animation[], animationName: string, selectedProperty:string, isOpen: boolean, selected: Animation, currentPathData: string | undefined, svgKeyframes: IKeyframeSvgPoint[] | undefined }> {
 
-    private _anchorPoints: { point: Vector2, anchor: Vector2 }[] = [];
+    readonly _heightScale: number = 100;
     private _newAnimations: Animation[] = [];
-    private _keyframes: Vector2[] = [];
+    private _svgKeyframes: IKeyframeSvgPoint[] = [];
     constructor(props: IAnimationCurveEditorComponentProps) {
         super(props);
-        this.state = { animations: this._newAnimations,selected: this.props.animations[0], isOpen: true, currentPathData: this.getPathData(this.props.animations[0]), anchorPoints: this._anchorPoints, keyframes: this._keyframes, selectedProperty: 'position.x', animationName: "" }
-        
+        this.state = { animations: this._newAnimations,selected: this.props.animations[0], isOpen: true, currentPathData: this.getPathData(this.props.animations[0]), svgKeyframes: this._svgKeyframes, selectedProperty: 'position.x', animationName: "" }
+
     }
 
     handlePropertyChange(event: React.ChangeEvent<HTMLSelectElement>){
@@ -35,7 +38,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
     }
 
     handleNameChange(event: React.ChangeEvent<HTMLInputElement>){
-        //let value = (event.target as HTMLInputElement).value;
         event.preventDefault();
         this.setState({animationName: event.target.value});
     }
@@ -46,34 +48,21 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
             let animation = new Animation(this.state.animationName, this.state.selectedProperty, 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
 
             var keys = []; 
-// //At the animation key 0, the value of scaling is "1"
-  keys.push({
-    frame: 0,
-    value: 1
-  });
-//   //At the animation key 20, the value of scaling is "0.2"
-//   keys.push({
-//     frame: 20,
-//     value: 0.2
-//   });
-//   //At the animation key 100, the value of scaling is "1"
-//   keys.push({
-//     frame: 100,
-//     value: 1
-//   });
-
-animation.setKeys(keys);
-
-var bezierEase = new BABYLON.CircleEase();
-//var bezierEase = new BABYLON.BezierCurveEase(0.55,0,1,0.45) as unknown;
-bezierEase.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);
-animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
+              keys.push({
+                frame: 0,
+                value: 1
+              });
+
+
+            animation.setKeys(keys);
+
+            var bezierEase = new BABYLON.CircleEase();
+            bezierEase.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);
+            animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
 
             this._newAnimations.push(animation);
             this.setState({animations: this._newAnimations, animationName: ""});
         }
-
-    
     }
 
     addKeyFrame(event: React.MouseEvent<SVGSVGElement>){
@@ -125,10 +114,10 @@ animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
         let anim = this.state.selected as Animation;
         var keys: IAnimationKey[] = [];
 
-        var keyframes = this.state.keyframes?.map((k, i) => {
+        var svgKeyframes = this.state.svgKeyframes?.map((k, i) => {
             if (i === index){
-                k.x = keyframe.x;
-                k.y = keyframe.y;
+                k.keyframePoint.x = keyframe.x;
+                k.keyframePoint.y = keyframe.y;
             }
 
             var height = 100;
@@ -136,23 +125,21 @@ animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
 
             var keyValue;
 
-            if (k.y < middle){
-                keyValue = 1 + ((100/k.y) * .1)
+            if (k.keyframePoint.y < middle){
+                keyValue = 1 + ((100/k.keyframePoint.y) * .1)
             }
 
-            if (k.y > middle){
-                keyValue = 1 - ((100/k.y) * .1)
+            if (k.keyframePoint.y > middle){
+                keyValue = 1 - ((100/k.keyframePoint.y) * .1)
             }
 
 
-            keys.push({frame: k.x, value: keyValue})
+            keys.push({frame: k.keyframePoint.x, value: keyValue})
             return k;
         });
         anim.setKeys(keys);
 
-        this.setState({ keyframes: keyframes})
-
-
+        this.setState({ svgKeyframes: svgKeyframes})
 
     }
 
@@ -174,57 +161,57 @@ animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
         const { easingMode, easingType } = this.getAnimationProperties(animation);
 
         const keyframes = animation.getKeys();
+
+
         if (keyframes === undefined) {
             return "";
         }
 
         const startKey = keyframes[0];
 
-        // This will change when Graph width becomes dinamic
-        const heightScale = 100;
-
-        // This assumes the startkey is always 0... we will change this
-        let middle = (startKey.value / 2) * heightScale;
+        // This assumes the startkey is always 0... beed to change this
+        let middle = this._heightScale/2;
 
         // START OF LINE/CURVE
-        let data: string | undefined = `M0, ${middle}`;
+        let data: string | undefined = `M${startKey.frame}, ${this._heightScale - (startKey.value * middle)}`;
 
         if (easingType === undefined && easingMode === undefined){
-            data = this.linearInterpolation(keyframes, data, heightScale, middle);
+            data = this.linearInterpolation(keyframes, data, middle);
         } else {
             let easingFunction = animation.getEasingFunction();
-            data = this.curvePath(keyframes, data, heightScale, middle, easingFunction as EasingFunction)
+            data = this.curvePath(keyframes, data, middle, easingFunction as EasingFunction)
         }
 
         return data;
 
     }
 
-    curvePath(keyframes: BABYLON.IAnimationKey[], data: string, heightScale: number, middle: number, easingFunction: EasingFunction) {
+    curvePath(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, easingFunction: EasingFunction) {
 
         // This will get 1/4 and 3/4 of points in eased curve
         const u = .25;
         const v = .75;
 
         keyframes.forEach((key, i) => {
-            if (i !== 0) {
 
-                // Gets previous initial point of curve segment
-                var pointA =  new Vector2(0, 0);
-                if (i === 0) {
-                    pointA.x = 0
-                    pointA.y = middle;
-                } else {
-                    pointA.x = keyframes[i - 1].frame;
-                    pointA.y = heightScale - (keyframes[i - 1].value * middle)
-                }
+            // Gets previous initial point of curve segment
+            var pointA =  new Vector2(0, 0);
+            if (i === 0) {
+                pointA.x = key.frame;
+                pointA.y = this._heightScale - (key.value * middle);
+
+                this.setKeyframePoint([pointA], i, keyframes.length);
 
+            } else {
+                pointA.x = keyframes[i - 1].frame;
+                pointA.y = this._heightScale - (keyframes[i - 1].value * middle)
+            
                 // Gets the end point of this curve segment
-                var pointB = new Vector2(key.frame, heightScale - (key.value * middle));
+                var pointB = new Vector2(key.frame, this._heightScale - (key.value * middle));
 
                 // Get easing value of percentage to get the bezier control points below
-                let du = easingFunction.ease(u);
-                let dv = easingFunction.ease(v);
+                let du = easingFunction.ease(u); // What happens to this function when we edit a curve? 
+                let dv = easingFunction.ease(v); // What happens to this function when we edit a curve? 
 
                 // Intermediate points in curve
                 let intermediatePoint25 = new Vector2(((pointB.x - pointA.x) * u) + pointA.x,  ((pointB.y - pointA.y) * du) + middle);
@@ -236,13 +223,10 @@ animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
                 if (controlPoints === undefined){
                     console.log("error getting bezier control points");
                 } else {
-                    this.setAnchorPoint(controlPoints[0], controlPoints[1]);
-                    this.setAnchorPoint(controlPoints[3], controlPoints[2]);
     
-                    this.setKeyframePoint(pointA);
-                    this.setKeyframePoint(pointB);
+                    this.setKeyframePoint(controlPoints, i, keyframes.length);
     
-                    data += ` C${controlPoints[1].x}, ${controlPoints[1].y} ${controlPoints[2].x}, ${controlPoints[2].y} ${pointB.x}, ${pointB.y}`
+                    data += ` C${controlPoints[1].x}, ${controlPoints[1].y} ${controlPoints[2].x}, ${controlPoints[2].y} ${controlPoints[3].x}, ${controlPoints[3].y}`
 
                 }
             }
@@ -253,35 +237,68 @@ animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
 
     }
 
+    renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number){
+
+
+        let animation = this.state.selected as Animation;
+
+        let keys = [...animation.getKeys()];
+
+        let newFrame = 0;
+        if (updatedSvgKeyFrame.keyframePoint.x !== 0){
+            if (updatedSvgKeyFrame.keyframePoint.x > 0 && updatedSvgKeyFrame.keyframePoint.x < 1){
+                newFrame = 1;
+            }else {
+                newFrame = Math.round(updatedSvgKeyFrame.keyframePoint.x);
+            }
+        }
+
+        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
+
+        animation.setKeys(keys);
+
+        this.selectAnimation(animation);
+
+    }
+
     
-    linearInterpolation(keyframes: IAnimationKey[], data: string, heightScale: number, middle: number): string {
+    linearInterpolation(keyframes: IAnimationKey[], data: string, middle: number): string {
         keyframes.forEach((key, i) => {
             if (i !== 0) {
-                data += ` L${key.frame} ${heightScale - (key.value * middle)}`
+                data += ` L${key.frame} ${this._heightScale - (key.value * middle)}`
             }
 
         });
         return data;
     }
 
-    
-    setAnchorPoint(point: Vector2, anchor: Vector2) {
-        this._anchorPoints.push({ point, anchor });
-    }
+    setKeyframePoint(controlPoints: Vector2[], index: number, keyframesCount: number) {
+
+        let svgKeyframe;
+        if (index === 0){
+            svgKeyframe = { keyframePoint: controlPoints[0], rightControlPoint: null, leftControlPoint: null, id: index.toString() }
+        }else if (index === 1) {
+            this._svgKeyframes[index-1].rightControlPoint = controlPoints[1];
+            svgKeyframe = { keyframePoint: controlPoints[3], leftControlPoint: controlPoints[2], rightControlPoint: null, id: index.toString() }
+        } else if (index === keyframesCount - 1){
+            this._svgKeyframes[index-1].rightControlPoint = controlPoints[1];
+            svgKeyframe = { keyframePoint: controlPoints[3], rightControlPoint: null, leftControlPoint: controlPoints[2], id: index.toString() }
+        } else {
+            svgKeyframe = { keyframePoint: controlPoints[0], rightControlPoint: controlPoints[1], leftControlPoint: this._svgKeyframes[index-1].rightControlPoint, id: index.toString() }
+        }
 
-    setKeyframePoint(point: Vector2) {
-        this._keyframes.push(point);
+        this._svgKeyframes.push(svgKeyframe);
     }
 
     selectAnimation(animation: Animation) {
-        this._anchorPoints = [];
-        this._keyframes = [];
+        this._svgKeyframes = [];
 
         const pathData = this.getPathData(animation);
         if (pathData === "") {
             console.log("no keyframes in this animation");
         }
-        this.setState({ selected: animation, currentPathData: pathData, anchorPoints: this._anchorPoints, keyframes: this._keyframes });
+        this.setState({ selected: animation, currentPathData: pathData, svgKeyframes: this._svgKeyframes });
 
     }
 
@@ -338,7 +355,7 @@ animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
                 <div className="content">
                   
                     <div className="animation-list">
-
+                
                     <div>
                         <select value={this.state.selectedProperty} onChange={(e) => this.handlePropertyChange(e)}>
                             <option value="position.x">Position X</option>
@@ -369,16 +386,10 @@ animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
 
                         </ul>
                     </div>
-                    <div className="sample-chart" style={{width:500, height:500}}>
-
-                        <svg className="linear" viewBox="0 0 100 100" preserveAspectRatio="none">
-                        
-                           <KeyframeSvgPoint key={0} point={new Vector2(50,50)} onUpdate={(keyframe: Vector2, index: number) => {}} index={0} />
-
-                        </svg>
-                    </div>
                     <div className="graph-chart">
-                        <svg className="linear" viewBox="0 0 100 100" preserveAspectRatio="none" onMouseDown={(e) => this.addKeyFrame(e)}>
+
+                           { this.state.svgKeyframes && <SvgDraggableArea keyframeSvgPoints={this.state.svgKeyframes} updatePosition={(updatedSvgKeyFrame: IKeyframeSvgPoint,index: number) => this.renderPoints(updatedSvgKeyFrame, index)}>
+                       
                              {/* Frame Labels  */}
                             <text x="10" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>10</text>
                             <text x="20" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>20</text>
@@ -426,15 +437,10 @@ animation.setEasingFunction((bezierEase as unknown) as EasingFunction);
                             { /* Single Curve -Modify this for multiple selection and view  */}
                             <path id="curve" d={this.state.currentPathData} style={{ stroke: 'red', fill: 'none', strokeWidth: '0.5' }}></path>
 
-                            {this.state.anchorPoints?.map((anchorPoint, i) =>
-                                <AnchorSvgPoint key={i} point={anchorPoint.point} anchor={anchorPoint.anchor} />
-                            )}
-
-                            {this.state.keyframes?.map((keyframe, i) =>
-                                <KeyframeSvgPoint key={i} point={keyframe} onUpdate={(keyframe: Vector2, index: number) => this.updateKeyframe(keyframe, index)} index={i} />
-                            )}
+                        
+                        </SvgDraggableArea>
 
-                        </svg>
+                        } 
 
                         Animation name: {this.state.selected.name}
 

+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationPropertyGridComponent.tsx

@@ -204,7 +204,7 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                                     onOpen={(window: Window) => { window.console.log("Window opened!!") }}
                                     onClose={(window: Window) => this.onCloseAnimationCurveEditor(window)}>
 
-                                    <AnimationCurveEditorComponent title="Animations Curve Editor" entity={animatableAsAny} entityName={animatableAsAny.id} close={(event) => this.onCloseAnimationCurveEditor(event.view)} animations={animations}/>
+                                    <AnimationCurveEditorComponent title="Animations Curve Editor" scene={this.props.scene} entity={animatableAsAny} entityName={animatableAsAny.id} close={(event) => this.onCloseAnimationCurveEditor(event.view)} animations={animations}/>
                                 </PopupComponent>
                             }
                         </LineContainerComponent>

+ 21 - 111
inspector/src/components/actionTabs/tabs/propertyGrids/animations/keyframeSvgPoint.tsx

@@ -1,126 +1,36 @@
 import * as React from "react";
 import { Vector2 } from 'babylonjs/Maths/math.vector';
+import { AnchorSvgPoint } from './anchorSvgPoint';
+
+export interface IKeyframeSvgPoint {
+    keyframePoint: Vector2;
+    rightControlPoint: Vector2 | null;
+    leftControlPoint: Vector2 | null;
+    id: string;
+}
 
 interface IKeyframeSvgPointProps {
-    point: Vector2;
-    index: number;
-    onUpdate: (keyframe: Vector2, index: number) => void
+    keyframePoint: Vector2;
+    leftControlPoint: Vector2 | null;
+    rightControlPoint: Vector2 | null;
+    id: string;
 }
 
-export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps,{ active: boolean, position: Vector2}>{ 
-    
-    private _active = false;
-    private _offset: Vector2;
-    private _draggable: React.RefObject<SVGSVGElement>;
-    private _local: Vector2;
-    
+export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps>{ 
+ 
     constructor(props: IKeyframeSvgPointProps) {
         super(props);
-        this._draggable = React.createRef();  
-        this.state = { position: this.props.point, active: this._active }
-        this._local = new Vector2(0,0);
-    }
-
-    dragStart(e: React.TouchEvent<SVGSVGElement>) : void;
-    dragStart(e: React.MouseEvent<SVGSVGElement, MouseEvent>) : void;
-    dragStart(e: any): void {
-        e.preventDefault();    
-          if (e.currentTarget === this._draggable.current) {
-            this._active = true;
-          }
-          
-    }
-
-    drag(e: React.TouchEvent<SVGSVGElement>) : void;
-    drag(e: React.MouseEvent<SVGSVGElement, MouseEvent>) : void;
-    drag(e: any): void {
-        if (this._active) {
-        
-          e.preventDefault();
-
-          var coord = this.getMousePosition(e);//.subtract(this._offset)
-            this._local = this.getMousePosition(e);
-            //console.log(this._local.x, this._local.y)
-          //this.setTranslate(coord.x, coord.y, this._draggable);
-          //this._local = coord;
-          this.setState({ position: coord });
-
-        }
     }
 
-    dragEnd(e: React.TouchEvent<SVGSVGElement> | React.MouseEvent<SVGSVGElement, MouseEvent>) {
-        this._active = false;  
-    }
-
-    setTranslate(xPos: number, yPos: number, el: React.RefObject<SVGSVGElement>) {
-        if (el.current){
-            el.current.style.transform = "translate(" + xPos + "px, " + yPos + "px)";
-        }   
-    }
-
-    getMousePosition(e: React.TouchEvent<SVGSVGElement>) : Vector2;
-    getMousePosition(e: React.MouseEvent<SVGSVGElement, MouseEvent>) : Vector2;
-    getMousePosition(e: any): Vector2 {
-
-        if (e.touches) { e = e.touches[0]; }
-
-        var svg = this._draggable.current as SVGSVGElement;
-
-        // var CTM = svg.getScreenCTM();
-        // if (CTM){
-        //     return new Vector2((e.clientX - CTM.e) / CTM.a,(e.clientY - CTM.f) / CTM.d)
-        // } else {
-        //     return new Vector2(e.clientX, e.clientY);
-        // }
-
-        var pt = svg.createSVGPoint();
-
-        pt.x = e.clientX;
-        pt.y = e.clientY;
-
-        var inverse = svg.getScreenCTM()?.inverse();
-
-        var cursorpt =  pt.matrixTransform(inverse);
-        
-
-        return new Vector2(cursorpt.x, cursorpt.y);
-
-        // var pt = svg.createSVGPoint();
-
-        // pt.x = e.clientX;
-        // pt.y = e.clientY;
-
-        // var inverse = svg.getScreenCTM()?.inverse();
-
-        // var cursorpt =  pt.matrixTransform(inverse);
-
-     
-       //onMouseLeave = {(e) => this.dragEnd(e)}
-        
-        //return new Vector2(cursorpt.x, cursorpt.y);
-      }
-
-
-
-
-
-
     render() {
         return (
-        <>
-            <svg className="draggable" ref={this._draggable} x={this.state.position.x} y={this.state.position.y} style={{overflow:'visible'}}
-                       onTouchMove = {(e) => this.drag(e)} 
-                       onTouchStart = {(e) => this.dragStart(e)}
-                       onTouchEnd  ={(e) => this.dragEnd(e)}
-                       onMouseMove={(e) => this.drag(e)} 
-                       onMouseDown ={(e) => this.dragStart(e)}
-                       onMouseUp  ={(e) => this.dragEnd(e)}
-                       >
-                <circle cx="0" cy="0"  r="2" stroke="none" strokeWidth="0" fill="red" />
-    
-
-            </svg>
-        </>
+            <>
+                <svg className="draggable" x={this.props.keyframePoint.x} y={this.props.keyframePoint.y} style={{overflow:'visible'}}>
+                    <circle data-id={this.props.id} className="draggable" cx="0" cy="0"  r="2" stroke="none" strokeWidth="0" fill="red" />
+                </svg>
+               { this.props.leftControlPoint && <AnchorSvgPoint type="left" index={this.props.id} control={this.props.leftControlPoint} anchor={this.props.keyframePoint} active={false}/>} 
+               { this.props.rightControlPoint &&  <AnchorSvgPoint type="right" index={this.props.id} control={this.props.rightControlPoint} anchor={this.props.keyframePoint} active={false}/>}
+            </>
         )
     }
 } 

+ 85 - 62
inspector/src/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea.tsx

@@ -1,79 +1,99 @@
 import * as React from "react";
 import { Vector2 } from 'babylonjs/Maths/math.vector';
+import { KeyframeSvgPoint, IKeyframeSvgPoint } from './keyframeSvgPoint';
 
 interface ISvgDraggableAreaProps {
-    points: Vector2[];
+    keyframeSvgPoints: IKeyframeSvgPoint[];
+    updatePosition: (updatedKeyframe: IKeyframeSvgPoint, index: number) => void
 }
 
-export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps,{ points: Vector2[]}>{ 
+export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps>{ 
 
-    private _active: Vector2;
+    private _active: boolean;
+    private _isCurrentPointControl: string;
+    private _currentPointIndex: number;
+    private _draggableArea: React.RefObject<SVGSVGElement>;
 
     constructor(props: ISvgDraggableAreaProps) {
         super(props);
-        this.state = { points: this.props.points }
+        this._currentPointIndex = -1;
+        this._isCurrentPointControl = "";
+        this._draggableArea = React.createRef();     
     }
 
    
-    dragStart(e: React.TouchEvent<SVGSVGElement>, point: Vector2) : void;
-    dragStart(e: React.MouseEvent<SVGSVGElement, MouseEvent>, point: Vector2) : void;
-    dragStart(e: any, point: Vector2): void {
-        e.preventDefault();    
-            this._active = ;
-          
+    dragStart(e: React.TouchEvent<SVGSVGElement>) : void;
+    dragStart(e: React.MouseEvent<SVGSVGElement, MouseEvent>) : void;
+    dragStart(e: any): void {
+        e.preventDefault();  
+        if (e.target.classList.contains("draggable")){
+            this._active = true;
+            this._currentPointIndex = parseInt(e.target.getAttribute('data-id'));
+
+            if (e.target.classList.contains("control-point")){
+                this._isCurrentPointControl = e.target.getAttribute("type");
+            }
+        } 
     }
 
-    drag(e: React.TouchEvent<SVGSVGElement>, point: Vector2) : void;
-    drag(e: React.MouseEvent<SVGSVGElement, MouseEvent>, point: Vector2) : void;
-    drag(e: any, point: Vector2): void {
+    drag(e: React.TouchEvent<SVGSVGElement>) : void;
+    drag(e: React.MouseEvent<SVGSVGElement, MouseEvent>) : void;
+    drag(e: any): void {
         if (this._active) {
         
           e.preventDefault();
 
-          var coord = this.getMousePosition(e);//.subtract(this._offset)
-            this._local = this.getMousePosition(e);
-            //console.log(this._local.x, this._local.y)
-          //this.setTranslate(coord.x, coord.y, this._draggable);
-          //this._local = coord;
-          this.setState({ position: coord });
+          var coord = this.getMousePosition(e);
 
-        }
-    }
-
-    dragEnd(e: React.TouchEvent<SVGSVGElement> | React.MouseEvent<SVGSVGElement, MouseEvent>, point: Vector2) {
-        this._active = false;  
-    }
+          if (coord !== undefined){
+         
+            var newPoints = [...this.props.keyframeSvgPoints];
 
+            if (this._isCurrentPointControl === "left"){
+                newPoints[this._currentPointIndex].leftControlPoint = coord;
+            } else if (this._isCurrentPointControl === "right"){
+                newPoints[this._currentPointIndex].rightControlPoint = coord;
+            } else {
+                newPoints[this._currentPointIndex].keyframePoint = coord;
+            }
 
+            this.props.updatePosition(newPoints[this._currentPointIndex], this._currentPointIndex);
 
-    getMousePosition(e: React.TouchEvent<SVGSVGElement>, point: Vector2) : Vector2;
-    getMousePosition(e: React.MouseEvent<SVGSVGElement, MouseEvent>, point: Vector2) : Vector2;
-    getMousePosition(e: any, point: Vector2): Vector2 {
-
-        if (e.touches) { e = e.touches[0]; }
-
-        var svg = this._draggable.current as SVGSVGElement;
+          }     
 
-        // var CTM = svg.getScreenCTM();
-        // if (CTM){
-        //     return new Vector2((e.clientX - CTM.e) / CTM.a,(e.clientY - CTM.f) / CTM.d)
-        // } else {
-        //     return new Vector2(e.clientX, e.clientY);
-        // }
+        }
+    }
 
-        var pt = svg.createSVGPoint();
+    dragEnd(e: React.TouchEvent<SVGSVGElement>): void;
+    dragEnd(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
+    dragEnd(e: any): void  {
+        e.preventDefault(); 
+        this._active = false;
+        this._currentPointIndex = -1;
+        this._isCurrentPointControl = "";
+    }
 
-        pt.x = e.clientX;
-        pt.y = e.clientY;
 
-        var inverse = svg.getScreenCTM()?.inverse();
 
-        var cursorpt =  pt.matrixTransform(inverse);
-        
+    getMousePosition(e: React.TouchEvent<SVGSVGElement>) : Vector2 | undefined;
+    getMousePosition(e: React.MouseEvent<SVGSVGElement, MouseEvent>) : Vector2 | undefined;
+    getMousePosition(e: any): Vector2 | undefined {
 
-        return new Vector2(cursorpt.x, cursorpt.y);
+        if (e.touches) { e = e.touches[0]; }
 
-     
+        if (this._draggableArea.current){
+            var svg = this._draggableArea.current as SVGSVGElement;
+            var CTM = svg.getScreenCTM();
+            if (CTM){
+                return new Vector2((e.clientX - CTM.e) / CTM.a, (e.clientY - CTM.f) / CTM.d);
+            } else {
+                return undefined;
+            }
+
+        } else {
+            return undefined;
+        }
+       
       }
 
 
@@ -81,28 +101,31 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps,{ p
     render() {
         return (
         <>
-            <svg className="linear" viewBox="0 0 100 100" preserveAspectRatio="none">
+        
+            <svg className="linear" style={{border: '1px solid black'}} ref={this._draggableArea} 
+            
+            onMouseMove={(e) => this.drag(e)}  
+            onTouchMove = {(e) => this.drag(e)} 
+            onTouchStart = {(e) => this.dragStart(e)}
+            onTouchEnd  ={(e) => this.dragEnd(e)}
+            
+            onMouseDown ={(e) => this.dragStart(e)}
+            onMouseUp  ={(e) => this.dragEnd(e)}
+            onMouseLeave = {(e) => this.dragEnd(e)}
+            // Add way to add new keyframe
+            
+            viewBox="0 0 100 100" preserveAspectRatio="none">
 
-            { this.state.points.map((point, i) => {
-            <svg key={i} className="draggable" x={point.x} y={point.y} style={{overflow:'visible'}}
-            onTouchMove = {(e, point) => this.drag(e, point)} 
-            onTouchStart = {(e,point) => this.dragStart(e, point)}
-            onTouchEnd  ={(e, point) => this.dragEnd(e, point)}
-            onMouseMove={(e, point) => this.drag(e, point)} 
-            onMouseDown ={(e, point) => this.dragStart(e, point)}
-            onMouseUp  ={(e, point) => this.dragEnd(e, point)}
-            onMouseLeave = {(e, point) => this.dragEnd(e, point)}
-            >
-            <circle cx="0" cy="0"  r="2" stroke="none" strokeWidth="0" fill="red" />
+                {this.props.children}
 
 
-            </svg>
+                { this.props.keyframeSvgPoints.map((keyframe, i) => 
 
+                    <KeyframeSvgPoint key={i} id={i.toString()} keyframePoint={keyframe.keyframePoint} leftControlPoint={keyframe.leftControlPoint} rightControlPoint={keyframe.rightControlPoint}/>
+                    
+                )}
 
 
-            })}
-                        
-      
         </svg>
         </>)
     }