harmonica

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2021 License: MIT Imports: 2 Imported by: 28

README

Harmonica

Harmonica Image
Latest Release GoDoc Build Status

A simple, efficient spring animation library for smooth, natural motion.

Harmonica OpenGL Demo

It even works well on the command line.

Harmonica TUI Demo

Usage

Harmonica is framework-agnostic and works well in 2D and 3D contexts. Simply call NewSpring with your settings to initialize and Update on each frame to animate.

import "github.com/charmbracelet/harmonica"

// A thing we want to animate.
sprite := struct{
    x, xVelocity float64
    y, yVelocity float64
}{}

// Where we want to animate it.
const targetX = 50.0
const targetY = 100.0

// Initialize a spring with framerate, angular frequency, and damping values.
spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5)

// Animate!
for {
    sprite.x, sprite.xVelocity = spring.Update(&sprite.x, &sprite.xVelocity, targetX)
    sprite.y, sprite.yVelocity = spring.Update(&sprite.y, &sprite.yVelocity, targetY)
    time.Sleep(time.Second/60)
}

For details, see the examples and the docs.

Settings

NewSpring takes three values:

  • Time Delta: the time step to operate on. Game engines typically provide a way to determine the time delta, however if that's not available you can simply set the framerate with the included FPS(int) utility function. Make the framerate you set here matches your actual framerate.
  • Angular Velocity: this translates roughly to the speed. Higher values are faster.
  • Damping Ratio: the springiness of the animation, generally between 0 and 1, though it can go higher. Lower values are springier. For details, see below.

Damping Ratios

The damping ratio affects the motion in one of three different ways depending on how it's set.

Under-Damping

A spring is under-damped when its damping ratio is less than 1. An under-damped spring reaches equilibrium the fastest, but overshoots and will continue to oscillate as its amplitude decays over time.

Critical Damping

A spring is critically-damped the damping ratio is exactly 1. A critically damped spring will reach equilibrium as fast as possible without oscillating.

Over-Damping

A spring is over-damped the damping ratio is greater than 1. An over-damped spring will never oscillate, but reaches equilibrium at a slower rate than a critically damped spring.

Acknowledgements

This library is a fairly straightforward port of Ryan Juckett’s excellent damped simple harmonic oscillator originally writen in C++ in 2008 and published in 2012. Ryan’s writeup on the subject is fantastic.

License

MIT


Part of Charm.

The Charm logo

Charm热爱开源 • Charm loves open source

Documentation

Overview

Package harmonica implements a simplified damped harmonic oscillator. This is ported from Ryan Juckett’s simple damped harmonic motion, originally written in C++.

Example usage:

// Run once to initialize.
spring := NewSpring(FPS(60), 6.0, 0.2)

// Update on every frame.
pos := 0.0
velocity := 0.0
targetPos := 100.0
someUpdateLoop(func() {
    pos, velocity = spring.Update(pos, velocity, targetPos)
})

For background on the algorithm see: https://www.ryanjuckett.com/damped-springs/

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FPS

func FPS(n int) float64

FPS returns a time delta for a given number of frames per second. This value can be used as the time delta when initializing a Spring. Note that game engines often provide the time delta as well, which you should use instead of this function, if possible.

Example:

spring := NewSpring(FPS(60), 5.0, 0.2)

Types

type Spring

type Spring struct {
	// contains filtered or unexported fields
}

Spring contains a cached set of motion parameters that can be used to efficiently update multiple springs using the same time step, angular frequency and damping ratio.

To use a Spring call New with the time delta (that's animation frame length), frequency, and damping parameters, cache the result, then call Update to update position and velocity values for each spring that neeeds updating.

Example:

// First precompute spring coefficients based on your settings:
var x, xVel, y, yVel float64
deltaTime := FPS(60)
s := NewSpring(deltaTime, 5.0, 0.2)

// Then, in your update loop:
x, xVel = s.Update(x, xVel, 10) // update the X position
y, yVel = s.Update(y, yVel, 20) // update the Y position

func NewSpring

func NewSpring(deltaTime, angularFrequency, dampingRatio float64) (s Spring)

NewSpring initializes a new Spring, computing the parameters needed to simulate a damped spring over a given period of time.

The delta time is the time step to advance; essentially the framerate.

The angular frequency is the angular frequency of motion, which affects the speed.

The damping ratio is the damping ratio of motion, which determines the oscillation, or lack thereof. There are three categories of damping ratios:

Damping ratio > 1: over-damped. Damping ratio = 1: critlcally-damped. Damping ratio < 1: under-damped.

An over-damped spring will never oscillate, but reaches equilibrium at a slower rate than a critically damped spring.

A critically damped spring will reach equilibrium as fast as possible without oscillating.

An under-damped spring will reach equilibrium the fastest, but also overshoots it and continues to oscillate as its amplitude decays over time.

func (Spring) Update

func (s Spring) Update(pos, vel float64, equilibriumPos float64) (newPos, newVel float64)

Update updates position and velocity values against a given target value. Call this after calling NewSpring to update values.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL