Эх сурвалжийг харах

:arrow_up: Upgrade effects module

Animations Package
:boom: Ported many of the Curves/Interpolators used by the Flutter framework for animations to JavaFX
:sparkles: Animations: Added new method to TimelineBuilder
:recycle: AnimationFactory: allow to build animations with a specific Interpolator or a default one
:fire: Removed BezierEasing implementation class in favor of the implementation offered by Flutter, Cubic.java, which is much simpler and more performant

Beans Package
:sparkles: Introduced Offset bean needed by some Flutter Curves

Ripple Package
:recycle: Improved efficiency of MFXRippleGenerators when disabled through the visible property. This was needed especially because in CSS you cannot set the disable state but only the visibility

Signed-off-by: Alessadro Parisi <alessandro.parisi406@gmail.com>
Alessadro Parisi 2 жил өмнө
parent
commit
5c98098f54
27 өөрчлөгдсөн 1344 нэмэгдсэн , 169 устгасан
  1. 19 0
      modules/effects/CHANGELOG.md
  2. 45 31
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/AnimationFactory.java
  3. 9 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/Animations.java
  4. 0 133
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/BezierEasing.java
  5. 33 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/BounceInCurve.java
  6. 37 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/BounceInOutCurve.java
  7. 33 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/BounceOutCurve.java
  8. 101 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/Cubic.java
  9. 35 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/DecelerateCurve.java
  10. 47 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/ElasticInCurve.java
  11. 52 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/ElasticInOutCurve.java
  12. 46 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/ElasticOutCurve.java
  13. 39 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/FlippedCurve.java
  14. 60 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/Interval.java
  15. 33 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/Linear.java
  16. 76 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/M3Motion.java
  17. 91 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/Motion.java
  18. 43 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/SawTooth.java
  19. 85 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/ThreePointCubic.java
  20. 41 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/Threshold.java
  21. 58 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/base/Curve.java
  22. 88 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/beans/Offset.java
  23. 63 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/beans/base/OffsetBase.java
  24. 2 2
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/enums/ElevationLevel.java
  25. 8 3
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/ripple/MFXRippleGenerator.java
  26. 197 0
      modules/effects/src/main/java/io/github/palexdev/mfxeffects/utils/NumberUtils.java
  27. 3 0
      modules/effects/src/main/java/module-info.java

+ 19 - 0
modules/effects/CHANGELOG.md

@@ -16,6 +16,25 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 [//]: ##[Unreleased]
 
+## [Unreleased] - 08-03-2023
+
+## Added
+
+- Ported many of the Curves/Interpolators used by the Flutter framework for animations to JavaFX
+- Animations: Added new method to TimelineBuilder
+- Introduced Offset bean needed by some Flutter Curves
+
+## Changed
+
+- AnimationFactory: allow to build animations with a specific Interpolator or a default one
+- Improved efficiency of MFXRippleGenerators when disabled through the visible property. This was needed especially
+  because in CSS you cannot set the disable state but only the visibility
+
+## Removed
+
+- Removed BezierEasing implementation class in favor of the implementation offered by Flutter, Cubic.java, which is much
+  simpler and more performant
+
 ## [11.0.4] - 09-02-2023
 
 ## Changed

+ 45 - 31
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/AnimationFactory.java

@@ -33,12 +33,12 @@ import javafx.util.Duration;
 public enum AnimationFactory {
 	FADE_IN {
 		@Override
-		public Timeline build(Node node, double durationMillis) {
+		public Timeline build(Node node, double durationMillis, Interpolator i) {
 			AnimationFactory.resetNode(node);
-			KeyValue keyValue1 = new KeyValue(node.opacityProperty(), 0, INTERPOLATOR_V1);
+			KeyValue keyValue1 = new KeyValue(node.opacityProperty(), 0, i);
 			KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
 
-			KeyValue keyValue2 = new KeyValue(node.opacityProperty(), 1.0, INTERPOLATOR_V1);
+			KeyValue keyValue2 = new KeyValue(node.opacityProperty(), 1.0, i);
 			KeyFrame keyFrame2 = new KeyFrame(Duration.millis(durationMillis), keyValue2);
 
 			return new Timeline(keyFrame1, keyFrame2);
@@ -46,12 +46,12 @@ public enum AnimationFactory {
 	},
 	FADE_OUT {
 		@Override
-		public Timeline build(Node node, double durationMillis) {
+		public Timeline build(Node node, double durationMillis, Interpolator i) {
 			AnimationFactory.resetNode(node);
-			KeyValue keyValue1 = new KeyValue(node.opacityProperty(), 1.0, INTERPOLATOR_V1);
+			KeyValue keyValue1 = new KeyValue(node.opacityProperty(), 1.0, i);
 			KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
 
-			KeyValue keyValue2 = new KeyValue(node.opacityProperty(), 0, INTERPOLATOR_V1);
+			KeyValue keyValue2 = new KeyValue(node.opacityProperty(), 0, i);
 			KeyFrame keyFrame2 = new KeyFrame(Duration.millis(durationMillis), keyValue2);
 
 			return new Timeline(keyFrame1, keyFrame2);
@@ -59,12 +59,12 @@ public enum AnimationFactory {
 	},
 	SLIDE_IN_BOTTOM {
 		@Override
-		public Timeline build(Node node, double durationMillis) {
+		public Timeline build(Node node, double durationMillis, Interpolator i) {
 			AnimationFactory.resetNode(node);
-			KeyValue keyValue1 = new KeyValue(node.translateYProperty(), -node.getBoundsInParent().getHeight() * 2, INTERPOLATOR_V1);
+			KeyValue keyValue1 = new KeyValue(node.translateYProperty(), -node.getBoundsInParent().getHeight() * 2, i);
 			KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
 
-			KeyValue keyValue2 = new KeyValue(node.translateYProperty(), 0, INTERPOLATOR_V1);
+			KeyValue keyValue2 = new KeyValue(node.translateYProperty(), 0, i);
 			KeyFrame keyFrame2 = new KeyFrame(Duration.millis(durationMillis), keyValue2);
 
 			return new Timeline(keyFrame1, keyFrame2);
@@ -72,12 +72,12 @@ public enum AnimationFactory {
 	},
 	SLIDE_OUT_BOTTOM {
 		@Override
-		public Timeline build(Node node, double durationMillis) {
+		public Timeline build(Node node, double durationMillis, Interpolator i) {
 			AnimationFactory.resetNode(node);
-			KeyValue keyValue1 = new KeyValue(node.translateYProperty(), 0, INTERPOLATOR_V1);
+			KeyValue keyValue1 = new KeyValue(node.translateYProperty(), 0, i);
 			KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
 
-			KeyValue keyValue2 = new KeyValue(node.translateYProperty(), node.getBoundsInParent().getHeight() * 2, INTERPOLATOR_V1);
+			KeyValue keyValue2 = new KeyValue(node.translateYProperty(), node.getBoundsInParent().getHeight() * 2, i);
 			KeyFrame keyFrame2 = new KeyFrame(Duration.millis(durationMillis), keyValue2);
 
 			return new Timeline(keyFrame1, keyFrame2);
@@ -85,12 +85,12 @@ public enum AnimationFactory {
 	},
 	SLIDE_IN_LEFT {
 		@Override
-		public Timeline build(Node node, double durationMillis) {
+		public Timeline build(Node node, double durationMillis, Interpolator i) {
 			AnimationFactory.resetNode(node);
-			KeyValue keyValue1 = new KeyValue(node.translateXProperty(), -node.getBoundsInParent().getWidth() * 2, INTERPOLATOR_V1);
+			KeyValue keyValue1 = new KeyValue(node.translateXProperty(), -node.getBoundsInParent().getWidth() * 2, i);
 			KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
 
-			KeyValue keyValue2 = new KeyValue(node.translateXProperty(), 0, INTERPOLATOR_V1);
+			KeyValue keyValue2 = new KeyValue(node.translateXProperty(), 0, i);
 			KeyFrame keyFrame2 = new KeyFrame(Duration.millis(durationMillis), keyValue2);
 
 			return new Timeline(keyFrame1, keyFrame2);
@@ -98,12 +98,12 @@ public enum AnimationFactory {
 	},
 	SLIDE_OUT_LEFT {
 		@Override
-		public Timeline build(Node node, double durationMillis) {
+		public Timeline build(Node node, double durationMillis, Interpolator i) {
 			AnimationFactory.resetNode(node);
-			KeyValue keyValue1 = new KeyValue(node.translateXProperty(), 0, INTERPOLATOR_V1);
+			KeyValue keyValue1 = new KeyValue(node.translateXProperty(), 0, i);
 			KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
 
-			KeyValue keyValue2 = new KeyValue(node.translateXProperty(), -node.getBoundsInParent().getWidth() * 2, INTERPOLATOR_V1);
+			KeyValue keyValue2 = new KeyValue(node.translateXProperty(), -node.getBoundsInParent().getWidth() * 2, i);
 			KeyFrame keyFrame2 = new KeyFrame(Duration.millis(durationMillis), keyValue2);
 
 			return new Timeline(keyFrame1, keyFrame2);
@@ -111,12 +111,12 @@ public enum AnimationFactory {
 	},
 	SLIDE_IN_RIGHT {
 		@Override
-		public Timeline build(Node node, double durationMillis) {
+		public Timeline build(Node node, double durationMillis, Interpolator i) {
 			AnimationFactory.resetNode(node);
-			KeyValue keyValue1 = new KeyValue(node.translateXProperty(), node.getBoundsInParent().getWidth() * 2, INTERPOLATOR_V1);
+			KeyValue keyValue1 = new KeyValue(node.translateXProperty(), node.getBoundsInParent().getWidth() * 2, i);
 			KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
 
-			KeyValue keyValue2 = new KeyValue(node.translateXProperty(), 0, INTERPOLATOR_V1);
+			KeyValue keyValue2 = new KeyValue(node.translateXProperty(), 0, i);
 			KeyFrame keyFrame2 = new KeyFrame(Duration.millis(durationMillis), keyValue2);
 
 			return new Timeline(keyFrame1, keyFrame2);
@@ -124,12 +124,12 @@ public enum AnimationFactory {
 	},
 	SLIDE_OUT_RIGHT {
 		@Override
-		public Timeline build(Node node, double durationMillis) {
+		public Timeline build(Node node, double durationMillis, Interpolator i) {
 			AnimationFactory.resetNode(node);
-			KeyValue keyValue1 = new KeyValue(node.translateXProperty(), 0, INTERPOLATOR_V1);
+			KeyValue keyValue1 = new KeyValue(node.translateXProperty(), 0, i);
 			KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
 
-			KeyValue keyValue2 = new KeyValue(node.translateXProperty(), node.getBoundsInParent().getWidth() * 2, INTERPOLATOR_V1);
+			KeyValue keyValue2 = new KeyValue(node.translateXProperty(), node.getBoundsInParent().getWidth() * 2, i);
 			KeyFrame keyFrame2 = new KeyFrame(Duration.millis(durationMillis), keyValue2);
 
 			return new Timeline(keyFrame1, keyFrame2);
@@ -137,12 +137,12 @@ public enum AnimationFactory {
 	},
 	SLIDE_IN_TOP {
 		@Override
-		public Timeline build(Node node, double durationMillis) {
+		public Timeline build(Node node, double durationMillis, Interpolator i) {
 			AnimationFactory.resetNode(node);
-			KeyValue keyValue1 = new KeyValue(node.translateYProperty(), node.getBoundsInParent().getHeight() * 2, INTERPOLATOR_V1);
+			KeyValue keyValue1 = new KeyValue(node.translateYProperty(), node.getBoundsInParent().getHeight() * 2, i);
 			KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
 
-			KeyValue keyValue2 = new KeyValue(node.translateYProperty(), 0, INTERPOLATOR_V1);
+			KeyValue keyValue2 = new KeyValue(node.translateYProperty(), 0, i);
 			KeyFrame keyFrame2 = new KeyFrame(Duration.millis(durationMillis), keyValue2);
 
 			return new Timeline(keyFrame1, keyFrame2);
@@ -150,12 +150,12 @@ public enum AnimationFactory {
 	},
 	SLIDE_OUT_TOP {
 		@Override
-		public Timeline build(Node node, double durationMillis) {
+		public Timeline build(Node node, double durationMillis, Interpolator i) {
 			AnimationFactory.resetNode(node);
-			KeyValue keyValue1 = new KeyValue(node.translateYProperty(), 0, INTERPOLATOR_V1);
+			KeyValue keyValue1 = new KeyValue(node.translateYProperty(), 0, i);
 			KeyFrame keyFrame1 = new KeyFrame(Duration.ZERO, keyValue1);
 
-			KeyValue keyValue2 = new KeyValue(node.translateYProperty(), -node.getBoundsInParent().getHeight() * 2, INTERPOLATOR_V1);
+			KeyValue keyValue2 = new KeyValue(node.translateYProperty(), -node.getBoundsInParent().getHeight() * 2, i);
 			KeyFrame keyFrame2 = new KeyFrame(Duration.millis(durationMillis), keyValue2);
 
 			return new Timeline(keyFrame1, keyFrame2);
@@ -172,5 +172,19 @@ public enum AnimationFactory {
 		}
 	}
 
-	public abstract Timeline build(Node node, double durationMillis);
+	/**
+	 * Calls {@link #build(Node, double, Interpolator)} with {@link #INTERPOLATOR_V1} as the default interpolator.
+	 */
+	public Timeline build(Node node, double durationMillis) {
+		return build(node, durationMillis, INTERPOLATOR_V1);
+	}
+
+	/**
+	 * Each enum constant will produce a {@link Timeline} with the given parameters.
+	 *
+	 * @param node           the {@link Node} on which perform the animation
+	 * @param durationMillis the duration of the animation in milliseconds
+	 * @param i              the {@link Interpolator} used by the animations
+	 */
+	public abstract Timeline build(Node node, double durationMillis, Interpolator i);
 }

+ 9 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/Animations.java

@@ -533,6 +533,15 @@ public class Animations {
 			return this;
 		}
 
+		/**
+		 * Adds the specified KeyFrame to the timeline only if the given condition is true.
+		 */
+		public TimelineBuilder addConditional(Supplier<Boolean> condition, KeyFrame keyFrame) {
+			if (condition.get())
+				timeline.getKeyFrames().add(keyFrame);
+			return this;
+		}
+
 		/**
 		 * Builds a KeyFrame to hide the given Window by fading it out.
 		 *

+ 0 - 133
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/BezierEasing.java

@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2022 Parisi Alessandro - alessandro.parisi406@gmail.com
- * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
- *
- * MaterialFX is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 3 of the License,
- * or (at your option) any later version.
- *
- * MaterialFX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package io.github.palexdev.mfxeffects.animations;
-
-import javafx.animation.Interpolator;
-
-/**
- * Cubic Bezier implementation from <a href="https://github.com/gre/bezier-easing">https://github.com/gre/bezier-easing</a>
- * <p>
- * Make sure to check this tool out! <a href="https://cubic-bezier.com/#0,0,.58,1">https://cubic-bezier.com</a>
- */
-public class BezierEasing {
-	private static final int NEWTON_ITERATIONS = 4;
-	private static final double NEWTON_MIN_SLOPE = 0.001;
-	private static final double SUBDIVISION_PRECISION = 0.0000001;
-	private static final int SUBDIVISION_MAX_ITERATIONS = 10;
-
-	private static final int kSplineTableSize = 11;
-	private static final double kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
-
-	public static final Interpolator EASE = toInterpolator(.25, 1, .25, 1);
-	public static final Interpolator LINEAR = toInterpolator(0, 0, 1, 1);
-	public static final Interpolator EASE_IN = toInterpolator(.42, 0, 1, 1);
-	public static final Interpolator EASE_OUT = toInterpolator(0, 0, .58, 1);
-	public static final Interpolator EASE_BOTH = toInterpolator(.42, 0, .58, 1);
-
-	private BezierEasing() {
-	}
-
-	private static double a(double a1, double a2) {
-		return 1.0 - 3.0 * a2 + 3.0 * a1;
-	}
-
-	private static double b(double a1, double a2) {
-		return 3.0 * a2 - 6.0 * a1;
-	}
-
-	private static double c(double a1) {
-		return 3.0 * a1;
-	}
-
-	private static double calcBezier(double t, double a1, double a2) {
-		return ((a(a1, a2) * t + b(a1, a2)) * t + c(a1)) * t;
-	}
-
-	// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
-	private static double getSlope(double aT, double aA1, double aA2) {
-		return 3.0 * a(aA1, aA2) * aT * aT + 2.0 * b(aA1, aA2) * aT + c(aA1);
-	}
-
-	private static double binarySubdivide(double aX, double aA, double aB, double mX1, double mX2) {
-		double currentX, currentT, i = 0;
-		do {
-			currentT = aA + (aB - aA) / 2.0;
-			currentX = calcBezier(currentT, mX1, mX2) - aX;
-			if (currentX > 0.0) {
-				aB = currentT;
-			} else {
-				aA = currentT;
-			}
-		} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
-		return currentT;
-	}
-
-	private static double newtonRaphsonIterate(double aX, double aGuessT, double mX1, double mX2) {
-		for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
-			var currentSlope = getSlope(aGuessT, mX1, mX2);
-			if (currentSlope == 0.0) {
-				return aGuessT;
-			}
-			var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
-			aGuessT -= currentX / currentSlope;
-		}
-		return aGuessT;
-	}
-
-	public static double bezier(double frac, double x1, double y1, double x2, double y2) {
-		double[] values = new double[kSplineTableSize];
-		for (int i = 0; i < values.length; ++i) {
-			values[i] = calcBezier(i * kSampleStepSize, x1, x2);
-		}
-
-		double intervalStart = 0.0;
-		int currentSample = 1;
-		double lastSample = kSplineTableSize - 1;
-
-		for (; currentSample != lastSample && values[currentSample] <= frac; ++currentSample) {
-			intervalStart += kSampleStepSize;
-		}
-		--currentSample;
-
-		// Interpolate to provide an initial guess for t
-		double dist = (frac - values[currentSample]) / (values[currentSample + 1] - values[currentSample]);
-		double guessForT = intervalStart + dist * kSampleStepSize;
-
-		double initialSlope = getSlope(guessForT, x1, x2);
-		double fracToT;
-		if (initialSlope >= NEWTON_MIN_SLOPE) {
-			fracToT = newtonRaphsonIterate(frac, guessForT, x1, x2);
-		} else if (initialSlope == 0.0) {
-			fracToT = guessForT;
-		} else {
-			fracToT = binarySubdivide(frac, intervalStart, intervalStart + kSampleStepSize, x1, x2);
-		}
-
-		return calcBezier(fracToT, y1, y2);
-	}
-
-	public static Interpolator toInterpolator(double x1, double y1, double x2, double y2) {
-		return new Interpolator() {
-			@Override
-			protected double curve(double t) {
-				return bezier(t, x1, y1, x2, y2);
-			}
-		};
-	}
-}

+ 33 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/BounceInCurve.java

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * An oscillating curve that grows in magnitude.
+ * <p></p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in.mp4>Bounce In</a>
+ */
+public class BounceInCurve extends Curve {
+	@Override
+	public double curve(double t) {
+		return 1.0 - Motion.bounce(1.0 - t);
+	}
+}

+ 37 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/BounceInOutCurve.java

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * An oscillating curve that first grows and then shrink in magnitude.
+ * <p></p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in_out.mp4>Bounce In Out</a>
+ */
+public class BounceInOutCurve extends Curve {
+	@Override
+	public double curve(double t) {
+		if (t < 0.5) {
+			return (1.0 - Motion.bounce(1.0 - t * 2.0)) * 0.5;
+		} else {
+			return Motion.bounce(t * 2.0 - 1.0) * 0.5 + 0.5;
+		}
+	}
+}

+ 33 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/BounceOutCurve.java

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * An oscillating curve that shrinks in magnitude.
+ * <p></p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_out.mp4>Bounce Out</a>
+ */
+public class BounceOutCurve extends Curve {
+	@Override
+	public double curve(double t) {
+		return Motion.bounce(t);
+	}
+}

+ 101 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/Cubic.java

@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * A cubic polynomial mapping of the unit interval.
+ * <p>
+ * Implements third-order Bézier curves.
+ * <p></p>
+ * Video examples of various curves based on this:
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_linear_to_slow_ease_in.mp4>Fast Linear To Slow Ease In</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease.mp4>Ease</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in.mp4>Ease In</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_to_linear.mp4>Ease In To Linear</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_sine.mp4>Ease In Sine</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quad.mp4>Ease In Quad</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_cubic.mp4>Ease In Cubic</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quart.mp4>Ease In Quart</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quint.mp4>Ease In Quint</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_expo.mp4>Ease In Expo</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_circ.mp4>Ease In Circ</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_back.mp4>Ease In Back</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out.mp4>Ease Out</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_linear_to_ease_out.mp4>Linear To Ease Out</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_sine.mp4>Ease Out Sine</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quad.mp4>Ease Out Quad</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_cubic.mp4>Ease Out Cubic</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quart.mp4>Ease Out Quart</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quint.mp4>Ease Out Quint</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_expo.mp4>Ease Out Expo</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_circ.mp4>Ease Out Circ</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_back.mp4>Ease Out Back</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out.mp4>Ease In Out</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_sine.mp4>Ease In Out Sine</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quad.mp4>Ease In Out Quad</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_cubic.mp4>Ease In Out Cubic</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quart.mp4>Ease In Out Quart</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quint.mp4>Ease In Out Quint</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_expo.mp4>Ease In Out Expo</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_circ.mp4>Ease In Out Circ</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_back.mp4>Ease In Out Back</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_out_slow_in.mp4>Fast Out Slow In</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_slow_middle.mp4>Slow Middle</a>
+ */
+public class Cubic extends Curve {
+	private final double x1;
+	private final double y1;
+	private final double x2;
+	private final double y2;
+
+	private static final double CUBIC_ERROR_BOUND = 0.001;
+
+	public Cubic(double x1, double y1, double x2, double y2) {
+		this.x1 = x1;
+		this.y1 = y1;
+		this.x2 = x2;
+		this.y2 = y2;
+	}
+
+	double elevateCubic(double a, double b, double m) {
+		return 3 * a * (1 - m) * (1 - m) * m +
+				3 * b * (1 - m) * m * m +
+				m * m * m;
+	}
+
+	@Override
+	public double curve(double t) {
+		double start = 0.0;
+		double end = 1.0;
+		while (true) {
+			final double midpoint = (start + end) / 2;
+			final double estimate = elevateCubic(x1, x2, midpoint);
+			if (Math.abs(t - estimate) < CUBIC_ERROR_BOUND) {
+				return elevateCubic(y1, y2, midpoint);
+			}
+			if (estimate < t) {
+				start = midpoint;
+			} else {
+				end = midpoint;
+			}
+		}
+	}
+}

+ 35 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/DecelerateCurve.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * A curve where the rate of change starts out quickly and then decelerates; an
+ * upside-down {@code `f(t) = t²`} parabola.
+ * <p></p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_decelerate.mp4>Decelerate</a>
+ */
+public class DecelerateCurve extends Curve {
+	@Override
+	public double curve(double t) {
+		t = 1.0 - t;
+		return 1.0 - t * t;
+	}
+}

+ 47 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/ElasticInCurve.java

@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * An oscillating curve that grows in {@code magnitude} while overshooting its bounds.
+ * <p></p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_in.mp4>Elastic In Example</a>
+ * <p>
+ * The above example is built with a {@code magnitude} of {@code 0.4}.
+ */
+public class ElasticInCurve extends Curve {
+	private final double period;
+
+	public ElasticInCurve() {
+		this(0.4);
+	}
+
+	public ElasticInCurve(double period) {
+		this.period = period;
+	}
+
+	@Override
+	public double curve(double t) {
+		final double s = period / 4.0;
+		t = t - 1.0;
+		return -Math.pow(2.0, 10.0 * t) * Math.sin((t - s) * (Math.PI * 2.0) / period);
+	}
+}

+ 52 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/ElasticInOutCurve.java

@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * An oscillating curve that grows and then shrinks in {@code magnitude} while overshooting its bounds.
+ * <p></p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_in_out.mp4>Elastic In Out Example</a>
+ * <p>
+ * The above example is built with a {@code magnitude} of {@code 0.4}.
+ */
+public class ElasticInOutCurve extends Curve {
+	private final double period;
+
+	public ElasticInOutCurve() {
+		this(0.4);
+	}
+
+	public ElasticInOutCurve(double period) {
+		this.period = period;
+	}
+
+	@Override
+	public double curve(double t) {
+		final double s = period / 4.0;
+		t = 2.0 * t - 1.0;
+		double sin = Math.sin((t - s) * (Math.PI * 2.0) / period);
+		if (t < 0.0) {
+			return -0.5 * Math.pow(2.0, 10.0 * t) * sin;
+		} else {
+			return Math.pow(2.0, -10.0 * t) * sin * 0.5 + 1.0;
+		}
+	}
+}

+ 46 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/ElasticOutCurve.java

@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * An oscillating curve that shrinks in {@code magnitude} while overshooting its bounds.
+ * <p></p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_out.mp4>Elastic Out Example</a>
+ * <p>
+ * The above example is built with a {@code magnitude} of {@code 0.4}.
+ */
+public class ElasticOutCurve extends Curve {
+	private final double period;
+
+	public ElasticOutCurve() {
+		this(0.4);
+	}
+
+	public ElasticOutCurve(double period) {
+		this.period = period;
+	}
+
+	@Override
+	public double curve(double t) {
+		final double s = period / 4.0;
+		return Math.pow(2.0, -10 * t) * Math.sin((t - s) * (Math.PI * 2.0) / period) + 1.0;
+	}
+}

+ 39 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/FlippedCurve.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * This {@link Curve} is capable of flipping the motion of other curves:
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in.mp4>BounceIn</a>
+ * <p> - <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_flipped.mp4>Flipped BounceIn</a>
+ */
+public class FlippedCurve extends Curve {
+	private final Curve curve;
+
+	public FlippedCurve(Curve curve) {
+		this.curve = curve;
+	}
+
+	@Override
+	public double curve(double t) {
+		return 1.0 - curve.curve(1.0 - t);
+	}
+}

+ 60 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/Interval.java

@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+import io.github.palexdev.mfxeffects.utils.NumberUtils;
+
+/**
+ * A curve that is 0.0 until {@code begin}, then curved (according to given {@code Curve}) from
+ * 0.0 at {@code begin} to 1.0 at {@code end}, then remains 1.0 past {@code end}.
+ * <p>
+ * An {@code Interval} can be used to delay an animation. For example, a six-second
+ * animation that uses an {@code Interval} with its {@code begin} set to 0.5 and its {@code end}
+ * set to 1.0 will essentially become a three-second animation that starts
+ * three seconds later.
+ * <p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_interval.mp4>Interval</a>
+ */
+public class Interval extends Curve {
+	private final double begin;
+	private final double end;
+	private final Curve curve;
+
+	public Interval(double begin, double end) {
+		this.begin = begin;
+		this.end = end;
+		this.curve = Motion.LINEAR;
+	}
+
+	@Override
+	public double curve(double t) {
+		assert (begin >= 0.0);
+		assert (begin <= 1.0);
+		assert (end >= 0.0);
+		assert (end <= 1.0);
+		assert (end >= begin);
+		t = NumberUtils.clamp((t - begin) / (end - begin), 0.0, 1.0);
+		if (t == 0.0 || t == 1.0) {
+			return t;
+		}
+		return curve.curve(t);
+	}
+
+}

+ 33 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/Linear.java

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * The identity map over the unit interval.
+ * <p></p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_linear.mp4>Linear</a>
+ */
+public class Linear extends Curve {
+	@Override
+	public double curve(double t) {
+		return t;
+	}
+}

+ 76 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/M3Motion.java

@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+import javafx.animation.Interpolator;
+import javafx.util.Duration;
+
+import static javafx.util.Duration.millis;
+
+/**
+ * This class contains all the 'tokens' needed by Material 3 components regarding animations/motion, such as
+ * {@link Curve}s and {@link Duration}s.
+ *
+ * @see <a href=https://m3.material.io/styles/motion/overview>Material 3 Guidelines</a>
+ */
+public class M3Motion {
+	//================================================================================
+	// Curves
+	//================================================================================
+	public static final Interpolator LINEAR = Motion.LINEAR;
+	public static final Interpolator STANDARD = new Cubic(0.2, 0.0, 0.0, 1.0);
+	public static final Interpolator STANDARD_ACCELERATE = new Cubic(0.3, 0, 1.0, 1.0);
+	public static final Interpolator STANDARD_DECELERATE = new Cubic(0, 0, 0, 1.0);
+	public static final Interpolator EMPHASIZED = Motion.EASE_IN_OUT_CUBIC_EMPHASIZED;
+	public static final Interpolator EMPHASIZED_ACCELERATE = new Cubic(0.3, 0.0, 0.8, 0.15);
+	public static final Interpolator EMPHASIZED_DECELERATE = new Cubic(0.05, 0.7, 0.1, 1.0);
+	public static final Interpolator LEGACY = new Cubic(0.4, 0, 0.2, 1.0);
+	public static final Interpolator LEGACY_ACCELERATE = new Cubic(0.4, 0, 1.0, 1.0);
+	public static final Interpolator LEGACY_DECELERATE = new Cubic(0, 0, 0.2, 1.0);
+
+	//================================================================================
+	// Durations
+	//================================================================================
+	public static final Duration SHORT1 = millis(50);
+	public static final Duration SHORT2 = millis(100);
+	public static final Duration SHORT3 = millis(150);
+	public static final Duration SHORT4 = millis(200);
+
+	public static final Duration MEDIUM1 = millis(250);
+	public static final Duration MEDIUM2 = millis(300);
+	public static final Duration MEDIUM3 = millis(350);
+	public static final Duration MEDIUM4 = millis(400);
+
+	public static final Duration LONG1 = millis(450);
+	public static final Duration LONG2 = millis(500);
+	public static final Duration LONG3 = millis(550);
+	public static final Duration LONG4 = millis(600);
+
+	public static final Duration EXTRA_LONG1 = millis(700);
+	public static final Duration EXTRA_LONG2 = millis(800);
+	public static final Duration EXTRA_LONG3 = millis(900);
+	public static final Duration EXTRA_LONG4 = millis(1000);
+
+	//================================================================================
+	// Constructors
+	//================================================================================
+	private M3Motion() {
+	}
+}

+ 91 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/Motion.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+import io.github.palexdev.mfxeffects.beans.Offset;
+import javafx.animation.Interpolator;
+
+/**
+ * This class holds a collection of {@link Curve}s (JavaFX's {@link Interpolator}s) to produce any kind of animation
+ * you could ever want. These have been ported from the {@code Flutter} framework, in particular from the
+ * <a href=https://github.com/flutter/flutter/tree/master/packages/flutter/lib/src/animation>animations</a> package.
+ */
+public class Motion {
+	public static final Curve LINEAR = new Linear();
+	public static final Curve DECELERATE = new DecelerateCurve();
+	public static final Curve FAST_LINEAR_TO_SLOW_EASE_IN = new Cubic(0.18, 1.0, 0.04, 1.0);
+	public static final Curve EASE = new Cubic(0.25, 0.1, 0.25, 1.0);
+	public static final Curve EASE_IN = new Cubic(0.42, 0.0, 1.0, 1.0);
+	public static final Curve EASE_IN_TO_LINEAR = new Cubic(0.67, 0.03, 0.65, 0.09);
+	public static final Curve EASE_IN_SINE = new Cubic(0.47, 0.0, 0.745, 0.715);
+	public static final Curve EASE_IN_QUAD = new Cubic(0.55, 0.085, 0.68, 0.53);
+	public static final Curve EASE_IN_CUBIC = new Cubic(0.55, 0.055, 0.675, 0.19);
+	public static final Curve EASE_IN_QUART = new Cubic(0.895, 0.03, 0.685, 0.22);
+	public static final Curve EASE_IN_QUINT = new Cubic(0.755, 0.05, 0.855, 0.06);
+	public static final Curve EASE_IN_EXPO = new Cubic(0.95, 0.05, 0.795, 0.035);
+	public static final Curve EASE_IN_CIRC = new Cubic(0.6, 0.04, 0.98, 0.335);
+	public static final Curve EASE_IN_BACK = new Cubic(0.6, -0.28, 0.735, 0.045);
+	public static final Curve EASE_OUT = new Cubic(0.0, 0.0, 0.58, 1.0);
+	public static final Curve LINEAR_TO_EASE_OUT = new Cubic(0.35, 0.91, 0.33, 0.97);
+	public static final Curve EASE_OUT_SINE = new Cubic(0.39, 0.575, 0.565, 1.0);
+	public static final Curve EASE_OUT_QUAD = new Cubic(0.25, 0.46, 0.45, 0.94);
+	public static final Curve EASE_OUT_CUBIC = new Cubic(0.215, 0.61, 0.355, 1.0);
+	public static final Curve EASE_OUT_QUART = new Cubic(0.165, 0.84, 0.44, 1.0);
+	public static final Curve EASE_OUT_QUINT = new Cubic(0.23, 1.0, 0.32, 1.0);
+	public static final Curve EASE_OUT_EXPO = new Cubic(0.19, 1.0, 0.22, 1.0);
+	public static final Curve EASE_OUT_CIRC = new Cubic(0.075, 0.82, 0.165, 1.0);
+	public static final Curve EASE_OUT_BACk = new Cubic(0.175, 0.885, 0.32, 1.275);
+	public static final Curve EASE_IN_OUT = new Cubic(0.42, 0.0, 0.58, 1.0);
+	public static final Curve EASE_IN_OUT_SINE = new Cubic(0.445, 0.05, 0.55, 0.95);
+	public static final Curve EASE_IN_OUT_QUAD = new Cubic(0.455, 0.03, 0.515, 0.955);
+	public static final Curve EASE_IN_OUT_CUBIC = new Cubic(0.645, 0.045, 0.355, 1.0);
+	public static final Curve EASE_IN_OUT_CUBIC_EMPHASIZED = new ThreePointCubic(
+			new Offset(0.05, 0), new Offset(0.133333, 0.06),
+			new Offset(0.166666, 0.4),
+			new Offset(0.208333, 0.82), new Offset(0.25, 1)
+	);
+	public static final Curve EASE_IN_OUT_QUART = new Cubic(0.77, 0.0, 0.175, 1.0);
+	public static final Curve EASE_IN_OUT_QUINT = new Cubic(0.86, 0.0, 0.07, 1.0);
+	public static final Curve EASE_IN_OUT_EXPO = new Cubic(1.0, 0.0, 0.0, 1.0);
+	public static final Curve EASE_IN_OUT_CIRC = new Cubic(0.785, 0.135, 0.15, 0.86);
+	public static final Curve EASE_IN_OUT_BACK = new Cubic(0.68, -0.55, 0.265, 1.55);
+	public static final Curve FADE_OUT_SLOW_IN = new Cubic(0.4, 0.0, 0.2, 1.0);
+	public static final Curve SLOW_MIDDLE = new Cubic(0.15, 0.85, 0.85, 0.15);
+	public static final Curve BOUNCE_IN = new BounceInCurve();
+	public static final Curve BOUNCE_OUT = new BounceOutCurve();
+	public static final Curve BOUND_IN_OUT = new BounceInOutCurve();
+	public static final Curve ELASTIC_IN = new ElasticInCurve();
+	public static final Curve ELASTIC_OUT = new ElasticOutCurve();
+	public static final Curve ELASTIC_IN_OUT = new ElasticInOutCurve();
+
+	static double bounce(double t) {
+		if (t < 1.0 / 2.75) {
+			return 7.5625 * t * t;
+		} else if (t < 2 / 2.75) {
+			t -= 1.5 / 2.75;
+			return 7.5625 * t * t + 0.75;
+		} else if (t < 2.5 / 2.75) {
+			t -= 2.25 / 2.75;
+			return 7.5625 * t * t + 0.9375;
+		}
+		t -= 2.625 / 2.75;
+		return 7.5625 * t * t + 0.984375;
+	}
+}

+ 43 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/SawTooth.java

@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * A sawtooth curve that repeats a given number of times over the unit interval.
+ * <p>
+ * The curve rises linearly from 0.0 to 1.0 and then falls discontinuously back
+ * to 0.0 each iteration.
+ * <p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_sawtooth.mp4>SawTooth</a>
+ */
+public class SawTooth extends Curve {
+	private final int count;
+
+	public SawTooth(int count) {
+		this.count = count;
+	}
+
+	@Override
+	public double curve(double t) {
+		t *= count;
+		return t - (int) t;
+	}
+}

+ 85 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/ThreePointCubic.java

@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+import io.github.palexdev.mfxeffects.beans.Offset;
+
+/**
+ * A cubic polynomial composed of two curves that share a common center point.
+ * <p>
+ * The curve runs through three points: (0,0), the {@code midpoint}, and (1,1).
+ * <p>
+ * The {@link Motion} class contains a curve defined with this class:
+ * {@link Motion#EASE_IN_OUT_CUBIC_EMPHASIZED}.
+ * <p>
+ * The {@code ThreePointCubic} class implements third-order Bézier curves, where two
+ * curves share an interior {@code midpoint} that the curve passes through. If the
+ * control points surrounding the middle point ({@code b1}, and {@code a2}) are not
+ * co-linear with the middle point, then the curve's derivative will have a
+ * discontinuity (a cusp) at the shared middle point.
+ * <p></p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_cubic_emphasized.mp4>ThreePointCubic Example</a>
+ * Note: the above example is built with the given {@link Offset}s in order:
+ * <pre>
+ * {@code
+ *   Offset(0.05, 0), Offset(0.133333, 0.06), // a1 and b1
+ *   Offset(0.166666, 0.4), // midpoint
+ *   Offset(0.208333, 0.82), Offset(0.25, 1) // a2 and b2
+ * }
+ * </pre>
+ */
+public class ThreePointCubic extends Curve {
+	private final Offset a1;
+	private final Offset b1;
+	private final Offset midpoint;
+	private final Offset a2;
+	private final Offset b2;
+
+	public ThreePointCubic(Offset a1, Offset b1, Offset midpoint, Offset a2, Offset b2) {
+		this.a1 = a1;
+		this.b1 = b1;
+		this.midpoint = midpoint;
+		this.a2 = a2;
+		this.b2 = b2;
+	}
+
+	@Override
+	public double curve(double t) {
+		boolean firstCurve = t < midpoint.getDx();
+		double scaleX = firstCurve ? midpoint.getDx() : 1.0 - midpoint.getDx();
+		double scaleY = firstCurve ? midpoint.getDy() : 1.0 - midpoint.getDy();
+		double scaledT = (t - (firstCurve ? 0.0 : midpoint.getDx())) / scaleX;
+		if (firstCurve) {
+			return new Cubic(
+					a1.getDx() / scaleX,
+					a1.getDy() / scaleY,
+					b1.getDx() / scaleX,
+					b1.getDy() / scaleY
+			).curve(scaledT) * scaleY;
+		} else {
+			return new Cubic(
+					(a2.getDx() - midpoint.getDx()) / scaleX,
+					(a2.getDy() - midpoint.getDy()) / scaleY,
+					(b2.getDx() - midpoint.getDx()) / scaleX,
+					(b2.getDy() - midpoint.getDy()) / scaleY
+			).curve(scaledT) * scaleY + midpoint.getDy();
+		}
+	}
+}

+ 41 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/Threshold.java

@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion;
+
+import io.github.palexdev.mfxeffects.animations.motion.base.Curve;
+
+/**
+ * A curve that is 0.0 until it hits the threshold, then it jumps to 1.0.
+ * <p>
+ * <a href=https://flutter.github.io/assets-for-api-docs/assets/animation/curve_threshold.mp4>Threshold</a>
+ */
+public class Threshold extends Curve {
+	private final double threshold;
+
+	public Threshold(double threshold) {
+		this.threshold = threshold;
+	}
+
+	@Override
+	public double curve(double t) {
+		assert (threshold >= 0.0);
+		assert (threshold <= 1.0);
+		return t < threshold ? 0.0 : 1.0;
+	}
+}

+ 58 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/animations/motion/base/Curve.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.animations.motion.base;
+
+import io.github.palexdev.mfxeffects.animations.motion.*;
+import javafx.animation.Interpolator;
+
+/**
+ * Equivalent of JavaFX's {@link Interpolator} (in fact the class extends it), in {@code Flutter} they
+ * are called {@code Curves}.
+ * <p></p>
+ * Aside from just a "rename", the protected method {@link Interpolator#curve(double)} has been made public
+ * since some curves need to access other curves' algorithm; also adds a method to quickly create a {@link FlippedCurve}
+ * from any implementation.
+ * <p></p>
+ *
+ * @see Motion for a series of pre-defines curves
+ * @see BounceInCurve
+ * @see BounceOutCurve
+ * @see BounceInOutCurve
+ * @see Cubic
+ * @see DecelerateCurve
+ * @see ElasticInCurve
+ * @see ElasticOutCurve
+ * @see ElasticInOutCurve
+ * @see FlippedCurve
+ * @see Interval
+ * @see Linear
+ * @see SawTooth
+ * @see ThreePointCubic
+ * @see Threshold
+ */
+public abstract class Curve extends Interpolator {
+	public abstract double curve(double t);
+
+	/**
+	 * Flips this curve's motion. This is done by creating a new {@link FlippedCurve}.
+	 */
+	public Curve flipped() {
+		return new FlippedCurve(this);
+	}
+}

+ 88 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/beans/Offset.java

@@ -0,0 +1,88 @@
+package io.github.palexdev.mfxeffects.beans;
+
+import io.github.palexdev.mfxeffects.beans.base.OffsetBase;
+
+public class Offset extends OffsetBase {
+	public static final Offset ZERO = new Offset(0, 0);
+	public static final Offset INFINITE = new Offset(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+
+	public Offset(double dx, double dy) {
+		super(dx, dy);
+	}
+
+	public static Offset fromDirection(double direction) {
+		return fromDirection(direction, 1.0);
+	}
+
+	public static Offset fromDirection(double direction, double distance) {
+		return new Offset(distance * Math.cos(direction), distance * Math.sin(direction));
+	}
+
+	public static Offset lerp(Offset a, Offset b, double t) {
+		if (b == null) {
+			if (a == null) {
+				return null;
+			} else {
+				return a.mul(1.0 - t);
+			}
+		} else {
+			if (a == null) {
+				return b.mul(t);
+			} else {
+				return new Offset(lerpDouble(a.dx, b.dx, t), lerpDouble(a.dy, b.dy, t));
+			}
+		}
+	}
+
+	private static double lerpDouble(double a, double b, double t) {
+		return a * (1.0 - t) + b * t;
+	}
+
+	public double getDistance() {
+		return Math.sqrt(dx * dx + dy * dy);
+	}
+
+	public double getDistanceSquared() {
+		return dx * dx + dy * dy;
+	}
+
+	public double getDirection() {
+		return Math.atan2(dy, dx);
+	}
+
+	public Offset scale(double scaleX, double scaleY) {
+		return new Offset(dx * scaleX, dy * scaleY);
+	}
+
+	public Offset translate(double translateX, double translateY) {
+		return new Offset(dx + translateX, dy + translateY);
+	}
+
+	public Offset inverse() {
+		return new Offset(-dx, -dy);
+	}
+
+	public Offset minus(Offset other) {
+		return new Offset(dx - other.dx, dy - other.dy);
+	}
+
+	public Offset plus(Offset other) {
+		return new Offset(dx + other.dx, dy + other.dy);
+	}
+
+	public Offset mul(double operand) {
+		return new Offset(dx * operand, dy * operand);
+	}
+
+	public Offset div(double operand) {
+		return new Offset(dx / operand, dy / operand);
+	}
+
+	public Offset divTruncate(double operand) {
+		return new Offset((int) dx / operand, (int) dy / operand);
+	}
+
+	public Offset mod(double operand) {
+		return new Offset(dx % operand, dy % operand);
+	}
+}

+ 63 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/beans/base/OffsetBase.java

@@ -0,0 +1,63 @@
+package io.github.palexdev.mfxeffects.beans.base;
+
+import java.util.Objects;
+
+public abstract class OffsetBase {
+	protected final double dx;
+	protected final double dy;
+
+	public OffsetBase(double dx, double dy) {
+		this.dx = dx;
+		this.dy = dy;
+	}
+
+	public boolean lesser(OffsetBase other) {
+		return dx < other.dx && dy < other.dy;
+	}
+
+	public boolean lesserEq(OffsetBase other) {
+		return dx <= other.dx && dy <= other.dy;
+	}
+
+	public boolean greater(OffsetBase other) {
+		return dx > other.dx && dy > other.dy;
+	}
+
+	public boolean greaterEq(OffsetBase other) {
+		return dx >= other.dx && dy >= other.dy;
+	}
+
+	public boolean isInfinite() {
+		return Double.isInfinite(dx) || Double.isInfinite(dy);
+	}
+
+	public boolean isFinite() {
+		return !isInfinite();
+	}
+
+	public double getDx() {
+		return dx;
+	}
+
+	public double getDy() {
+		return dy;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (this == o) return true;
+		if (o == null || getClass() != o.getClass()) return false;
+		OffsetBase that = (OffsetBase) o;
+		return Double.compare(that.dx, dx) == 0 && Double.compare(that.dy, dy) == 0;
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(dx, dy);
+	}
+
+	@Override
+	public String toString() {
+		return String.format("%s(%f, %f)", getClass().getSimpleName(), dx, dy);
+	}
+}

+ 2 - 2
modules/effects/src/main/java/io/github/palexdev/mfxeffects/enums/ElevationLevel.java

@@ -20,7 +20,7 @@ package io.github.palexdev.mfxeffects.enums;
 
 import io.github.palexdev.mfxeffects.animations.Animations.KeyFrames;
 import io.github.palexdev.mfxeffects.animations.Animations.TimelineBuilder;
-import io.github.palexdev.mfxeffects.animations.BezierEasing;
+import io.github.palexdev.mfxeffects.animations.motion.Motion;
 import javafx.animation.Interpolator;
 import javafx.scene.effect.BlurType;
 import javafx.scene.effect.DropShadow;
@@ -99,7 +99,7 @@ public enum ElevationLevel {
 	}
 
 	public void animateTo(DropShadow current, ElevationLevel next) {
-		Interpolator i = BezierEasing.EASE;
+		Interpolator i = Motion.EASE;
 		TimelineBuilder.build()
 				.add(KeyFrames.of(1, current.offsetXProperty(), next.getOffsetX(), i))
 				.add(KeyFrames.of(1, current.offsetYProperty(), next.getOffsetY(), i))

+ 8 - 3
modules/effects/src/main/java/io/github/palexdev/mfxeffects/ripple/MFXRippleGenerator.java

@@ -83,7 +83,7 @@ import static javafx.util.Duration.ZERO;
  * for the generator accordingly, more info here {@link #autoClipProperty()} and here {@link #buildClip()}
  * <p></p>
  * Last but not least, know that there are 3 main ways to stop the generator if you don't want it on added controls:
- * <p> 1) You can disable the generator just like any other node with {@link #disableProperty()} (best way)
+ * <p> 1) You can disable the generator just like any other node with {@link #disableProperty()}
  * <p> 2) You can set the visibility to hidden, {@link #visibleProperty()}
  * <p> 3) You can set the ripples opacity to 0, {@link #rippleOpacityProperty()}
  */
@@ -172,7 +172,7 @@ public class MFXRippleGenerator extends Region implements RippleGenerator {
 	 */
 	@Override
 	public void generate(MouseEvent me) {
-		if (isDisabled()) return;
+		if (isDisabled() || !isVisible() || getRippleOpacity() <= 0) return;
 		if (isCheckBounds() && !isWithinBounds(me)) return;
 		if (getClip() != null) setClip(null);
 		setClip(buildClip());
@@ -319,7 +319,12 @@ public class MFXRippleGenerator extends Region implements RippleGenerator {
 			this,
 			"backgroundColor",
 			Color.LIGHTGRAY
-	);
+	) {
+		@Override
+		public StyleOrigin getStyleOrigin() {
+			return StyleOrigin.USER_AGENT;
+		}
+	};
 
 	private final StyleableDoubleProperty backgroundOpacity = new SimpleStyleableDoubleProperty(
 			StyleableProperties.BACKGROUND_OPACITY,

+ 197 - 0
modules/effects/src/main/java/io/github/palexdev/mfxeffects/utils/NumberUtils.java

@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 Parisi Alessandro - alessandro.parisi406@gmail.com
+ * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
+ *
+ * MaterialFX is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * MaterialFX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package io.github.palexdev.mfxeffects.utils;
+
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Utils class for working with numbers.
+ */
+public class NumberUtils {
+
+	private NumberUtils() {
+	}
+
+	/**
+	 * Limits the given value to the given min-max range by returning the nearest bound
+	 * if it exceeds or val if it's in range.
+	 */
+	public static double clamp(double val, double min, double max) {
+		return Math.max(min, Math.min(max, val));
+	}
+
+	/**
+	 * Limits the given value to the given min-max range by returning the nearest bound
+	 * if it exceeds or val if it's in range.
+	 */
+	public static float clamp(float val, float min, float max) {
+		return Math.max(min, Math.min(max, val));
+	}
+
+	/**
+	 * Limits the given value to the given min-max range by returning the nearest bound
+	 * if it exceeds or val if it's in range.
+	 */
+	public static int clamp(int val, int min, int max) {
+		return Math.max(min, Math.min(max, val));
+	}
+
+	/**
+	 * Limits the given value to the given min-max range by returning the nearest bound
+	 * if it exceeds or val if it's in range.
+	 */
+	public static long clamp(long val, long min, long max) {
+		return Math.max(min, Math.min(max, val));
+	}
+
+	/**
+	 * Given a certain value, finds the closest value in the given numbers list.
+	 */
+	public static double closestValueTo(double val, List<Double> list) {
+		if (list.isEmpty()) {
+			return 0.0;
+		}
+
+		double res = list.get(0);
+		for (int i = 1; i < list.size(); i++) {
+			if (Math.abs(val - res) >
+					Math.abs(val - list.get(i))) {
+				res = list.get(i);
+			}
+		}
+
+		return res;
+	}
+
+	/**
+	 * Given a certain value, finds the closest value in the given numbers list.
+	 */
+	public static float closestValueTo(float val, List<Float> list) {
+		if (list.isEmpty()) {
+			return 0;
+		}
+
+		float res = list.get(0);
+		for (int i = 1; i < list.size(); i++) {
+			if (Math.abs(val - res) >
+					Math.abs(val - list.get(i))) {
+				res = list.get(i);
+			}
+		}
+
+		return res;
+	}
+
+	/**
+	 * Given a certain value, finds the closest value in the given numbers list.
+	 */
+	public static int closestValueTo(int val, List<Integer> list) {
+		if (list.isEmpty()) {
+			return 0;
+		}
+
+		int res = list.get(0);
+		for (int i = 1; i < list.size(); i++) {
+			if (Math.abs(val - res) >
+					Math.abs(val - list.get(i))) {
+				res = list.get(i);
+			}
+		}
+
+		return res;
+	}
+
+	/**
+	 * Given a certain value, finds the closest value in the given numbers list.
+	 */
+	public static long closestValueTo(long val, List<Long> list) {
+		if (list.isEmpty()) {
+			return 0;
+		}
+
+		long res = list.get(0);
+		for (int i = 1; i < list.size(); i++) {
+			if (Math.abs(val - res) >
+					Math.abs(val - list.get(i))) {
+				res = list.get(i);
+			}
+		}
+
+		return res;
+	}
+
+	/**
+	 * Formats the given double value to have the given number of decimal places.
+	 */
+	public static double formatTo(double value, int decimalPrecision) {
+		int calcScale = (int) Math.pow(10, decimalPrecision);
+		return (double) Math.round(value * calcScale) / calcScale;
+	}
+
+	/**
+	 * Returns the given value as a string the specified number of decimal places.
+	 */
+	public static String formatToString(double value, int decimalPrecision) {
+		return String.format("%." + decimalPrecision + "f", value);
+	}
+
+	/**
+	 * Returns a random double between the specified min-max range.
+	 * <p></p>
+	 * Uses {@link ThreadLocalRandom#nextDouble(double, double)}.
+	 */
+	public static double getRandomDoubleBetween(double min, double max) {
+		return ThreadLocalRandom.current().nextDouble(min, max);
+	}
+
+	/**
+	 * Returns a random float value between 0 and 1.
+	 * <p></p>
+	 * Uses {@link ThreadLocalRandom#nextFloat()}
+	 */
+	public static float getRandomFloat() {
+		return ThreadLocalRandom.current().nextFloat();
+	}
+
+	/**
+	 * Returns a random int value between the specified min-max range.
+	 * <p></p>
+	 * Uses {@link ThreadLocalRandom#nextInt(int, int)}.
+	 */
+	public static int getRandomIntBetween(int min, int max) {
+		return ThreadLocalRandom.current().nextInt(min, max);
+	}
+
+	/**
+	 * Returns a random long value between the specified min-max range.
+	 * <p></p>
+	 * Uses {@link ThreadLocalRandom#nextLong(long, long)}.
+	 */
+	public static long getRandomLongBetween(long min, long max) {
+		return ThreadLocalRandom.current().nextLong(min, max);
+	}
+
+	/**
+	 * Checks if the given number is even or odd, just a convenience method for aesthetic.
+	 */
+	public static boolean isEven(int number) {
+		return (number % 2 == 0);
+	}
+}

+ 3 - 0
modules/effects/src/main/java/module-info.java

@@ -3,12 +3,15 @@ module mfx.effects {
 
 	// Animations
 	exports io.github.palexdev.mfxeffects.animations;
+	exports io.github.palexdev.mfxeffects.animations.motion;
+	exports io.github.palexdev.mfxeffects.animations.motion.base;
 
 	// Base
 	exports io.github.palexdev.mfxeffects;
 
 	// Beans
 	exports io.github.palexdev.mfxeffects.beans;
+	exports io.github.palexdev.mfxeffects.beans.base;
 
 	// Builders
 	exports io.github.palexdev.mfxeffects.builders;