Skip to content

Commit

Permalink
add material motion
Browse files Browse the repository at this point in the history
  • Loading branch information
T-Dynamos committed May 11, 2024
1 parent cfe5aac commit fc49b0e
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 1 deletion.
117 changes: 117 additions & 0 deletions kivymd/motion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""
"""

import math

float_epsilon = 8.3446500e-7

class CubicBezier:
p0 = 0
p1 = 0
p2 = 0
p3 = 0

def __init__(self, *args):
self.p0, self.p1, self.p2, self.p3 = args

def evaluate_cubic(self, p1, p2, t):
a = 1.0 / 3.0 + (p1 - p2)
b = p2 - 2.0 * p1
c = p1
return 3.0 * ((a * t + b) * t + c) * t

def clamp_range(self, r):
if r < 0.0:
if -float_epsilon <= r < 0.0:
return 0.0
else:
return math.nan
elif r > 1.0:
if 1.0 <= r <= 1.0 + float_epsilon:
return 1.0
else:
return math.nan
else:
return r

def close_to(self, x, y):
return abs(x - y) < float_epsilon

def find_first_cubic_root(self, p0, p1, p2, p3):
a = 3.0 * (p0 - 2.0 * p1 + p2)
b = 3.0 * (p1 - p0)
c = p0
d = -p0 + 3.0 * (p1 - p2) + p3

if self.close_to(d, 0.0):
if self.close_to(a, 0.0):
if self.close_to(b, 0.0):
return math.nan
return self.clamp_range(-c / b)
else:
q = math.sqrt(b * b - 4.0 * a * c)
a2 = 2.0 * a

root = self.clamp_range((q - b) / a2)
if not math.isnan(root):
return root

return self.clamp_range((-b - q) / a2)

a /= d
b /= d
c /= d
o3 = (3.0 * b - a * a) / 9.0
q2 = (2.0 * a * a * a - 9.0 * a * b + 27.0 * c) / 54.0
discriminant = q2 * q2 + o3 * o3 * o3
a3 = a / 3.0

if discriminant < 0.0:
mp33 = -(o3 * o3 * o3)
r = math.sqrt(mp33)
t = -q2 / r
cos_phi = max(-1.0, min(t, 1.0))
phi = math.acos(cos_phi)
t1 = 2.0 * math.cbrt(r)
root = self.clamp_range(t1 * math.cos(phi / 3.0) - a3)
if not math.isnan(root):
return root
root = self.clamp_range(t1 * math.cos((phi + 2.0 * math.pi) / 3.0) - a3)
if not math.isnan(root):
return root
return self.clamp_range(t1 * math.cos((phi + 4.0 * math.pi) / 3.0) - a3)

elif self.close_to(discriminant, 0.0):
u1 = -math.cbrt(q2)
root = self.clamp_range(2.0 * u1 - a3)
if not math.isnan(root):
return root
return self.clamp_range(-u1 - a3)

sd = math.sqrt(discriminant)
u1 = math.cbrt(-q2 + sd)
v1 = math.cbrt(q2 + sd)
return self.clamp_range(u1 - v1 - a3)

def transform(self, value: float):
return self.evaluate_cubic(
self.p1, self.p3, self.find_first_cubic_root(
-value,
self.p0 - value,
self.p2 - value,
1.0 - value,
)
)


class MaterialMotion:

easing_standard = CubicBezier(0.4, 0.0, 0.2, 1.0)
easing_decelerated = CubicBezier(0.0, 0.0, 0.2, 1.0)
easing_accelerated = CubicBezier(0.4, 0.0, 1.0, 1.0)
easing_linear = CubicBezier(0.0, 0.0, 1.0, 1.0)

# TODO: add `easing_emphasized` here
# it's defination is
# path(M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1)
27 changes: 26 additions & 1 deletion kivymd/uix/transition/transition.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@

from kivymd.uix.hero import MDHeroFrom, MDHeroTo
from kivymd.uix.screenmanager import MDScreenManager
from kivymd.motion import MaterialMotion


class MDTransitionBase(TransitionBase):
Expand Down Expand Up @@ -351,6 +352,22 @@ class MDSharedAxisTransition(MDTransitionBase):
defaults to 0.15 (= 150ms).
"""

switch_animation = OptionProperty(
"easing_decelerated",
options=[
"easing_standard",
"easing_decelerated",
"easing_accelerated",
"easing_linear",
],
)
"""
Custom material design animation transition.
:attr:`switch_animation` is a :class:`~kivy.properties.OptionProperty` and
defaults to `"easing_decelerated"`.
"""

slide_distance = NumericProperty(dp(15))
"""
Distance to which it slides left, right, bottom or up depending on axis.
Expand Down Expand Up @@ -385,6 +402,10 @@ def start(self, manager):
# Save hash of the objects
self.ih = hash(self.screen_in)
self.oh = hash(self.screen_out)

# Init pos
self.screen_in.pos = manager.pos
self.screen_out.pos = manager.pos

if self.transition_axis == "z":
if self.ih not in self._s_map.keys():
Expand Down Expand Up @@ -418,7 +439,9 @@ def start(self, manager):

def on_progress(self, progress):
# This code could be simplyfied with setattr, but it's slow
progress = AnimationTransition.out_cubic(progress)
progress = getattr(MaterialMotion, self.switch_animation).transform(
progress
)
progress_i = progress - 1
progress_d = progress * 2
# first half
Expand Down Expand Up @@ -464,6 +487,8 @@ def on_progress(self, progress):
def on_complete(self):
self.screen_in.pos = self.manager.pos
self.screen_out.pos = self.manager.pos
self.screen_out.opacity = 1
self.screen_in.opacity = 1
if self.oh in self._s_map.keys():
self._s_map[self.oh].xyz = (1, 1, 1)
if self.ih in self._s_map.keys():
Expand Down

0 comments on commit fc49b0e

Please sign in to comment.