All Downloads are FREE. Search and download functionalities are using the official Maven repository.

doodle.interact.easing.Easing.scala Maven / Gradle / Ivy

/*
 * Copyright 2015 Creative Scala
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package doodle
package interact
package easing

import fs2.Pure
import fs2.Stream

/** An easing function is a function from [0,1] to the real numbers (but usually
  * numbers in [0,1]) that is used to construct animation that move in a
  * pleasing way.
  *
  * All easing functions must return 0.0 for input 0.0 and 1.0 for input 1.0.
  */
trait Easing extends Function1[Double, Double] {

  /** Construct an easing function that has the first half of its output from
    * this easing function, and the second half from that easing function.
    *
    * For the input [0, 0.5) the resulting function uses this easing function,
    * and for [0.5, 1] uses that easing function. The input to the two easing
    * functions is linearly scaled so that they both receive a value in the
    * range [0, 1]. Their output is scaled in half so the first function
    * generates values in [0, 0.5] and the second in [0.5, 1.0].
    */
  def followedBy(that: Easing): Easing =
    Easing { t =>
      if t < 0.5 then this(t * 2.0) / 2.0
      else (that((t - 0.5) * 2.0) / 2.0) + 0.5
    }

  /** Reflect this easing around x = 0.5 and y = 0.5. Constructs an "out" easing
    * from an "in" easing and vice versa.
    */
  def reflect: Easing =
    Easing { t =>
      1.0 - this(1.0 - t)
    }

  /** Convert to a Stream that produces a total of steps values, starting at 0.0
    * and finishing at 1.0
    */
  def toStream(steps: Int): Stream[Pure, Double] =
    Stream
      .range(0L, steps.toLong)
      .map(step =>
        if step == 0 then 0.0
        else if step == steps then 1.0
        else this(step.toDouble / steps.toDouble)
      )
}
object Easing {

  /** Construct an easing function from a Double => Double function
    */
  def apply(f: Double => Double): Easing =
    new Easing {
      def apply(in: Double): Double = f(in)
    }

  /** The identity easing function simply returns its input
    */
  val identity: Easing = Easing(t => t)

  /** Linear easing, is equivalent to the identity
    */
  val linear: Easing = identity

  /** The quadratic easing f(t) = t^2
    */
  val quadratic: Easing = Easing(t => t * t)

  /** The cubic easing f(t) = t^3
    */
  val cubic: Easing = Easing(t => t * t * t)

  /** The sin easing f(t) = sin(t * (Math.PI / 2))
    */
  val sin: Easing = Easing(t => Math.sin(t * 0.5 * Math.PI))

  /** The easing function that moves in a circular path
    */
  val circle: Easing = Easing(t => 1 - Math.sqrt(1 - t * t))

  /** Easing function that overshoots its destination and then smoothly comes
    * back
    */
  val back: Easing =
    Easing(t => t * t * ((1.70158 + 1) * t - 1.70158))

  /** The elastic easing function. Has a little bounce. Might not be correct.
    */
  val elastic: Easing = {
    val p = 0.3
    val s = Math.asin(1) * (0.3 / (2 * Math.PI))

    Easing { t =>
      Math.pow(2, 10 * t - 1) * Math.sin((s - t - 1) / (p / (2 * Math.PI)))
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy