[NUI] Deceleration of scrolling (#1759)
authorneostom432 <31119276+neostom432@users.noreply.github.com>
Fri, 26 Jun 2020 06:55:40 +0000 (15:55 +0900)
committerGitHub <noreply@github.com>
Fri, 26 Jun 2020 06:55:40 +0000 (15:55 +0900)
Previously, scrolling uses same alphafunction so scrolling is always
getting slow as same rate even if the velocity of panning is different.

Now, we use deceleration fomular and generating alphafunction using
velocity and deceleration rate.

deceleration 0.99 is fast deceleration and 0.998 is normal.

src/Tizen.NUI.Components/Controls/ScrollableBase.cs
src/Tizen.NUI.Wearable/src/public/WearableList.cs
src/Tizen.NUI/src/public/AlphaFunction.cs

index 9470275..c2dbd3e 100755 (executable)
@@ -18,6 +18,7 @@ using Tizen.NUI.BaseComponents;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics;
+using System.Runtime.InteropServices;
 
 namespace Tizen.NUI.Components
 {
@@ -192,33 +193,6 @@ namespace Tizen.NUI.Components
         }
 
         /// <summary>
-        /// [Draft] Configurable speed threshold that register the gestures as a flick.
-        /// If the flick speed less than the threshold then will not be considered a flick.
-        /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public float FlickThreshold { get; set; } = 0.2f;
-
-        /// <summary>
-        /// [Draft] Configurable duration modifer for the flick animation.
-        /// Determines the speed of the scroll, large value results in a longer flick animation. Range (0.1 - 1.0)
-        /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public float FlickAnimationSpeed { get; set; } = 0.4f;
-
-        /// <summary>
-        /// [Draft] Configurable modifer for the distance to be scrolled when flicked detected.
-        /// It a ratio of the ScrollableBase's length. (not child's length).
-        /// First value is the ratio of the distance to scroll with the weakest flick.
-        /// Second value is the ratio of the distance to scroll with the strongest flick.
-        /// Second > First.
-        /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public Vector2 FlickDistanceMultiplierRange { get; set; } = new Vector2(0.6f, 1.8f);
-
-        /// <summary>
         /// [Draft] Scrolling direction mode.
         /// Default is Vertical scrolling.
         /// </summary>
@@ -267,12 +241,10 @@ namespace Tizen.NUI.Components
                     if (mScrollEnabled)
                     {
                         mPanGestureDetector.Detected += OnPanGestureDetected;
-                        mTapGestureDetector.Detected += OnTapGestureDetected;
                     }
                     else
                     {
                         mPanGestureDetector.Detected -= OnPanGestureDetected;
-                        mTapGestureDetector.Detected -= OnTapGestureDetected;
                     }
                 }
             }
@@ -410,15 +382,74 @@ namespace Tizen.NUI.Components
             }
         }
 
+        /// <summary>
+        /// Container which has content of ScrollableBase.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public View ContentContainer { get; private set; }
+
+        /// <summary>
+        /// Set the layout on this View. Replaces any existing Layout.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public new LayoutItem Layout
+        {
+            get
+            {
+                return ContentContainer.Layout;
+            }
+            set
+            {
+                ContentContainer.Layout = value;
+                if (ContentContainer.Layout != null)
+                {
+                    ContentContainer.Layout.SetPositionByLayout = false;
+                }
+            }
+        }
+
+        /// <summary>
+        /// List of children of Container.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public new List<View> Children
+        {
+            get
+            {
+                return ContentContainer.Children;
+            }
+        }
+
+        /// <summary>
+        /// Deceleration rate of scrolling by finger.
+        /// Rate should be 0 < rate < 1.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float DecelerationRate
+        {
+            get
+            {
+                return decelerationRate;
+            }
+            set
+            {
+                decelerationRate = value;
+                logValueOfDeceleration = (float)Math.Log(value);
+            }
+        }
+
+        /// <summary>
+        /// Threashold not to go infinit at the end of scrolling animation.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float DecelerationThreshold { get; set; } = 0.1f;
+
         private bool hideScrollbar = true;
-        private Animation scrollAnimation;
         private float maxScrollDistance;
         private float childTargetPosition = 0.0f;
         private PanGestureDetector mPanGestureDetector;
-        private TapGestureDetector mTapGestureDetector;
         private View mInterruptTouchingChild;
         private ScrollbarBase scrollBar;
-        private float multiplier = 1.0f;
         private bool scrolling = false;
         private float ratioOfScreenWidthToCompleteScroll = 0.5f;
         private float totalDisplacementForPan = 0.0f;
@@ -428,9 +459,27 @@ namespace Tizen.NUI.Components
         private bool flickWhenAnimating = false;
         private PropertyNotification propertyNotification;
 
+        private float noticeAnimationEndBeforePosition = 0.0f;
+        private bool readyToNotice = false;
+        // Let's consider more whether this needs to be set as protected.
+        public float NoticeAnimationEndBeforePosition { get => noticeAnimationEndBeforePosition; set => noticeAnimationEndBeforePosition = value; }
+
+
         // Let's consider more whether this needs to be set as protected.
         private float finalTargetPosition;
 
+        private Animation scrollAnimation;
+        // Declare user alpha function delegate
+        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+        private delegate float UserAlphaFunctionDelegate(float progress);
+        private UserAlphaFunctionDelegate customScrollAlphaFunction;
+        private float velocityOfLastPan = 0.0f;
+        private float panAnimationDuration = 0.0f;
+        private float panAnimationDelta = 0.0f;
+        private float logValueOfDeceleration = 0.0f;
+        private float decelerationRate = 0.0f;
+
+
         /// <summary>
         /// [Draft] Constructor
         /// </summary>
@@ -438,16 +487,14 @@ namespace Tizen.NUI.Components
         [EditorBrowsable(EditorBrowsableState.Never)]
         public ScrollableBase() : base()
         {
+            DecelerationRate = 0.998f;
+
             base.Layout = new ScrollableBaseCustomLayout();
             mPanGestureDetector = new PanGestureDetector();
             mPanGestureDetector.Attach(this);
             mPanGestureDetector.AddDirection(PanGestureDetector.DirectionVertical);
             mPanGestureDetector.Detected += OnPanGestureDetected;
 
-            mTapGestureDetector = new TapGestureDetector();
-            mTapGestureDetector.Attach(this);
-            mTapGestureDetector.Detected += OnTapGestureDetected;
-
             ClippingMode = ClippingModeType.ClipChildren;
 
             //Default Scrolling child
@@ -455,7 +502,7 @@ namespace Tizen.NUI.Components
             {
                 WidthSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent,
                 HeightSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent,
-                Layout = new AbsoluteLayout(){SetPositionByLayout = false},
+                Layout = new AbsoluteLayout() { SetPositionByLayout = false },
             };
             ContentContainer.Relayout += OnScrollingChildRelayout;
             propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(1.0f));
@@ -469,48 +516,18 @@ namespace Tizen.NUI.Components
                 BackgroundColor = Color.Transparent,
             };
             mInterruptTouchingChild.TouchEvent += OnIterruptTouchingChildTouched;
-
             Scrollbar = new Scrollbar();
         }
 
-        /// <summary>
-        /// Container which has content of ScrollableBase.
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public View ContentContainer { get; private set; }
-
-        /// <summary>
-        /// Set the layout on this View. Replaces any existing Layout.
-        /// </summary>
-        public new LayoutItem Layout
+        private bool OnIterruptTouchingChildTouched(object source, View.TouchEventArgs args)
         {
-            get
+            if (args.Touch.GetState(0) == PointStateType.Down)
             {
-                return ContentContainer.Layout;
-            }
-            set
-            {
-                ContentContainer.Layout = value;
-                if(ContentContainer.Layout != null)
+                if (scrolling && !SnapToPage)
                 {
-                    ContentContainer.Layout.SetPositionByLayout = false;
+                    StopScroll();
                 }
             }
-        }
-
-        /// <summary>
-        /// List of children of Container.
-        /// </summary>
-        public new List<View> Children
-        {
-            get
-            {
-                return ContentContainer.Children;
-            }
-        }
-
-        private bool OnIterruptTouchingChildTouched(object source, View.TouchEventArgs args)
-        {
             return true;
         }
 
@@ -538,11 +555,11 @@ namespace Tizen.NUI.Components
         [EditorBrowsable(EditorBrowsableState.Never)]
         public override void Remove(View view)
         {
-            if(SnapToPage && CurrentPage == Children.IndexOf(view) &&  CurrentPage == Children.Count -1)
+            if (SnapToPage && CurrentPage == Children.IndexOf(view) && CurrentPage == Children.Count - 1)
             {
                 // Target View is current page and also last child.
                 // CurrentPage should be changed to previous page.
-                CurrentPage = Math.Max(0, CurrentPage-1);
+                CurrentPage = Math.Max(0, CurrentPage - 1);
                 ScrollToIndex(CurrentPage);
             }
 
@@ -623,16 +640,13 @@ namespace Tizen.NUI.Components
 
         private void OnScrollAnimationEnded()
         {
+            scrolling = false;
+            base.Remove(mInterruptTouchingChild);
+
             ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
             ScrollAnimationEnded?.Invoke(this, eventArgs);
         }
 
-        private bool readyToNotice = false;
-
-        private float noticeAnimationEndBeforePosition = 0.0f;
-        // Let's consider more whether this needs to be set as protected.
-        public float NoticeAnimationEndBeforePosition { get => noticeAnimationEndBeforePosition; set => noticeAnimationEndBeforePosition = value; }
-
         private void OnScroll()
         {
             ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
@@ -711,7 +725,7 @@ namespace Tizen.NUI.Components
             }
 
             scrollAnimation.Duration = duration;
-            scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSine);
+            scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSquare);
             scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", axisPosition);
             scrolling = true;
             OnScrollAnimationStarted();
@@ -777,12 +791,9 @@ namespace Tizen.NUI.Components
             {
                 // Calculate scroll animaton duration
                 float scrollDistance = Math.Abs(displacement);
-                int duration = (int)((320 * FlickAnimationSpeed) + (scrollDistance * FlickAnimationSpeed));
-                Debug.WriteLineIf(LayoutDebugScrollableBase, "Scroll Animation Duration:" + duration + " Distance:" + scrollDistance);
-
                 readyToNotice = true;
 
-                AnimateChildTo(duration, BoundScrollPosition(AdjustTargetPositionOfScrollAnimation(BoundScrollPosition(childTargetPosition))));
+                AnimateChildTo(ScrollDuration, BoundScrollPosition(AdjustTargetPositionOfScrollAnimation(BoundScrollPosition(childTargetPosition))));
             }
             else
             {
@@ -824,48 +835,10 @@ namespace Tizen.NUI.Components
                     mPanGestureDetector.Dispose();
                     mPanGestureDetector = null;
                 }
-
-                if (mTapGestureDetector != null)
-                {
-                    mTapGestureDetector.Detected -= OnTapGestureDetected;
-                    mTapGestureDetector.Dispose();
-                    mTapGestureDetector = null;
-                }
             }
             base.Dispose(type);
         }
 
-        private float CalculateDisplacementFromVelocity(float axisVelocity)
-        {
-            // Map: flick speed of range (2.0 - 6.0) to flick multiplier of range (0.7 - 1.6)
-            float speedMinimum = FlickThreshold;
-            float speedMaximum = FlickThreshold + 6.0f;
-            float multiplierMinimum = FlickDistanceMultiplierRange.X;
-            float multiplierMaximum = FlickDistanceMultiplierRange.Y;
-
-            float flickDisplacement = 0.0f;
-
-            float speed = Math.Min(4.0f, Math.Abs(axisVelocity));
-
-            Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollableBase Candidate Flick speed:" + speed);
-
-            if (speed > FlickThreshold)
-            {
-                // Flick length is the length of the ScrollableBase.
-                float flickLength = (ScrollingDirection == Direction.Horizontal) ? CurrentSize.Width : CurrentSize.Height;
-
-                // Calculate multiplier by mapping speed between the multiplier minimum and maximum.
-                multiplier = ((speed - speedMinimum) / ((speedMaximum - speedMinimum) * (multiplierMaximum - multiplierMinimum))) + multiplierMinimum;
-
-                // flick displacement is the product of the flick length and multiplier
-                flickDisplacement = ((flickLength * multiplier) * speed) / axisVelocity;  // *speed and /velocity to perserve sign.
-
-                Debug.WriteLineIf(LayoutDebugScrollableBase, "Calculated FlickDisplacement[" + flickDisplacement + "] from speed[" + speed + "] multiplier:"
-                                                        + multiplier);
-            }
-            return flickDisplacement;
-        }
-
         private float CalculateMaximumScrollDistance()
         {
             float scrollingChildLength = 0;
@@ -915,38 +888,11 @@ namespace Tizen.NUI.Components
             AnimateChildTo(ScrollDuration, destinationX);
         }
 
-        private void Flick(float flickDisplacement)
-        {
-            if (SnapToPage && Children.Count > 0)
-            {
-                if ((flickWhenAnimating && scrolling == true) || (scrolling == false))
-                {
-                    if (flickDisplacement < 0)
-                    {
-                        CurrentPage = Math.Min(Math.Max(Children.Count - 1, 0), CurrentPage + 1);
-                        Debug.WriteLineIf(LayoutDebugScrollableBase, "Snap - to page:" + CurrentPage);
-                    }
-                    else
-                    {
-                        CurrentPage = Math.Max(0, CurrentPage - 1);
-                        Debug.WriteLineIf(LayoutDebugScrollableBase, "Snap + to page:" + CurrentPage);
-                    }
-
-                    float destinationX = -(Children[CurrentPage].Position.X + Children[CurrentPage].CurrentSize.Width / 2.0f - CurrentSize.Width / 2.0f); // set to middle of current page
-                    Debug.WriteLineIf(LayoutDebugScrollableBase, "Snapping to :" + destinationX);
-                    AnimateChildTo(ScrollDuration, destinationX);
-                }
-            }
-            else
-            {
-                ScrollBy(flickDisplacement, true); // Animate flickDisplacement.
-            }
-        }
-
         private void OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
         {
             if (e.PanGesture.State == Gesture.StateType.Started)
             {
+                readyToNotice = false;
                 base.Add(mInterruptTouchingChild);
                 Debug.WriteLineIf(LayoutDebugScrollableBase, "Gesture Start");
                 if (scrolling && !SnapToPage)
@@ -972,51 +918,146 @@ namespace Tizen.NUI.Components
             }
             else if (e.PanGesture.State == Gesture.StateType.Finished)
             {
-                float axisVelocity = (ScrollingDirection == Direction.Horizontal) ? e.PanGesture.Velocity.X : e.PanGesture.Velocity.Y;
-                float flickDisplacement = CalculateDisplacementFromVelocity(axisVelocity);
-
-                Debug.WriteLineIf(LayoutDebugScrollableBase, "FlickDisplacement:" + flickDisplacement + "TotalDisplacementForPan:" + totalDisplacementForPan);
                 OnScrollDragEnded();
+                StopScroll(); // Will replace previous animation so will stop existing one.
+
+                if (scrollAnimation == null)
+                {
+                    scrollAnimation = new Animation();
+                    scrollAnimation.Finished += ScrollAnimationFinished;
+                }
 
-                if (flickDisplacement > 0 | flickDisplacement < 0)// Flick detected
+                if (SnapToPage)
                 {
-                    Flick(flickDisplacement);
+                    PageSnap();
                 }
                 else
                 {
-                    // End of panning gesture but was not a flick
-                    if (SnapToPage && Children.Count > 0)
-                    {
-                        PageSnap();
-                    }
-                    else
-                    {
-                        ScrollBy(0, true);
-                    }
+                    Decelerating((ScrollingDirection == Direction.Horizontal) ? e.PanGesture.Velocity.X : e.PanGesture.Velocity.Y);
                 }
+
                 totalDisplacementForPan = 0;
+                scrolling = true;
+                readyToNotice = true;
+                OnScrollAnimationStarted();
+            }
+        }
 
-                base.Remove(mInterruptTouchingChild);
+        private float CustomScrollAlphaFunction(float progress)
+        {
+            if (panAnimationDelta == 0)
+            {
+                return 1.0f;
+            }
+            else
+            {
+                // Parameter "progress" is normalized value. We need to multiply target duration to calculate distance.
+                // Can get real distance using equation of deceleration (check Decelerating function)
+                // After get real distance, normalize it
+                float realDuration = progress * panAnimationDuration;
+                float realDistance = velocityOfLastPan * ((float)Math.Pow(decelerationRate, realDuration) - 1) / logValueOfDeceleration;
+                float result = Math.Min(realDistance / Math.Abs(panAnimationDelta), 1.0f);
+                return result;
             }
         }
 
-        private new void OnTapGestureDetected(object source, TapGestureDetector.DetectedEventArgs e)
+        private void Decelerating(float velocity)
         {
-            if (e.TapGesture.Type == Gesture.GestureType.Tap)
+            // Decelerating using deceleration equation ===========
+            //
+            // V   : velocity (pixel per milisecond)
+            // V0  : initial velocity
+            // d   : deceleration rate,
+            // t   : time
+            // X   : final position after decelerating
+            // log : natural logarithm
+            //
+            // V(t) = V0 * d pow t;
+            // X(t) = V0 * (d pow t - 1) / log d;  <-- Integrate the velocity function
+            // X(∞) = V0 * d / (1 - d); <-- Result using inifit T can be final position because T is tending to infinity.
+            //
+            // Because of final T is tending to inifity, we should use threshold value to finish.
+            // Final T = log(-threshold * log d / |V0| ) / log d; 
+
+            velocityOfLastPan = Math.Abs(velocity);
+
+            float currentScrollPosition = -(ScrollingDirection == Direction.Horizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y);
+            panAnimationDelta = (velocityOfLastPan * decelerationRate) / (1 - decelerationRate);
+            panAnimationDelta = velocity > 0 ? -panAnimationDelta : panAnimationDelta;
+
+            float destination = -(panAnimationDelta + currentScrollPosition);
+            float adjustDestination = AdjustTargetPositionOfScrollAnimation(destination);
+            float maxPosition = ScrollAvailableArea != null ? ScrollAvailableArea.Y : maxScrollDistance;
+            float minPosition = ScrollAvailableArea != null ? ScrollAvailableArea.X : 0;
+
+            if (destination < -maxPosition || destination > minPosition)
             {
-                // Stop scrolling if tap detected (press then relase).
-                // Unless in Pages mode, do not want a page change to stop part way.
-                if (scrolling && !SnapToPage)
+                panAnimationDelta = velocity > 0 ? (currentScrollPosition - minPosition) : (maxPosition - currentScrollPosition);
+                destination = velocity > 0 ? minPosition : -maxPosition;
+
+                if (panAnimationDelta == 0)
                 {
-                    StopScroll();
+                    panAnimationDuration = 0.0f;
+                }
+                else
+                {
+                    panAnimationDuration = (float)Math.Log((panAnimationDelta * logValueOfDeceleration / velocityOfLastPan + 1), decelerationRate);
+                }
+
+                Debug.WriteLineIf(LayoutDebugScrollableBase, "\n" +
+                    "OverRange======================= \n" +
+                    "[decelerationRate] " + decelerationRate + "\n" +
+                    "[logValueOfDeceleration] " + logValueOfDeceleration + "\n" +
+                    "[Velocity] " + velocityOfLastPan + "\n" +
+                    "[CurrentPosition] " + currentScrollPosition + "\n" +
+                    "[CandidateDelta] " + panAnimationDelta + "\n" +
+                    "[Destination] " + destination + "\n" +
+                    "[Duration] " + panAnimationDuration + "\n" +
+                    "================================ \n"
+                );
+            }
+            else
+            {
+                panAnimationDuration = (float)Math.Log(-DecelerationThreshold * logValueOfDeceleration / velocityOfLastPan) / logValueOfDeceleration;
+
+                if (adjustDestination != destination)
+                {
+                    destination = adjustDestination;
+                    panAnimationDelta = destination + currentScrollPosition;
+                    velocityOfLastPan = Math.Abs(panAnimationDelta * logValueOfDeceleration / ((float)Math.Pow(decelerationRate, panAnimationDuration) - 1));
+                    panAnimationDuration = (float)Math.Log(-DecelerationThreshold * logValueOfDeceleration / velocityOfLastPan) / logValueOfDeceleration;
                 }
+
+                Debug.WriteLineIf(LayoutDebugScrollableBase, "\n" +
+                    "================================ \n" +
+                    "[decelerationRate] " + decelerationRate + "\n" +
+                    "[logValueOfDeceleration] " + logValueOfDeceleration + "\n" +
+                    "[Velocity] " + velocityOfLastPan + "\n" +
+                    "[CurrentPosition] " + currentScrollPosition + "\n" +
+                    "[CandidateDelta] " + panAnimationDelta + "\n" +
+                    "[Destination] " + destination + "\n" +
+                    "[Duration] " + panAnimationDuration + "\n" +
+                    "================================ \n"
+                );
             }
+
+            finalTargetPosition = destination;
+
+            customScrollAlphaFunction = new UserAlphaFunctionDelegate(CustomScrollAlphaFunction);
+            scrollAnimation.DefaultAlphaFunction = new AlphaFunction(customScrollAlphaFunction);
+            GC.KeepAlive(customScrollAlphaFunction);
+            scrollAnimation.Duration = (int)panAnimationDuration;
+            scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", destination);
+            scrollAnimation.Play();
+        }
+
+        protected void OnTapGestureDetected(object source, TapGestureDetector.DetectedEventArgs e)
+        {
+
         }
 
         private void ScrollAnimationFinished(object sender, EventArgs e)
         {
-            scrolling = false;
-            CheckPreReachedTargetPosition();
             OnScrollAnimationEnded();
         }
 
index dd07bdf..e9d07ee 100644 (file)
@@ -54,6 +54,8 @@ namespace Tizen.NUI.Wearable
             SetFocus(0, false);
 
             Scrollbar = new CircularScrollbar();
+            DecelerationThreshold = 60.0f;
+            DecelerationRate = 0.991f;
         }
 
         protected override void SetScrollbar()
index 5d3f786..a668051 100755 (executable)
@@ -35,7 +35,7 @@ namespace Tizen.NUI
         /// </summary>
         /// <param name="func">User defined fuction. It must be a method formatted as float alphafunction(float progress)</param>
         /// <since_tizen> 3 </since_tizen>
-        public AlphaFunction(System.Delegate func) : this(Interop.AlphaFunction.new_AlphaFunction__SWIG_2(SWIGTYPE_p_f_float__float.getCPtr(new SWIGTYPE_p_f_float__float(System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(func), true))), true)
+        public AlphaFunction(global::System.Delegate func) : this(Interop.AlphaFunction.new_AlphaFunction__SWIG_2(SWIGTYPE_p_f_float__float.getCPtr(new SWIGTYPE_p_f_float__float(System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(func), true))), true)
         {
             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
         }