├── .gitignore ├── 3d-rotation-spring ├── Assets │ ├── Cube.unity │ ├── Cube.unity.meta │ ├── New Material.mat │ ├── New Material.mat.meta │ ├── QuaternionSpring.cs │ └── QuaternionSpring.cs.meta └── ProjectSettings │ ├── AudioManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── NavMeshAreas.asset │ ├── NetworkManager.asset │ ├── Physics2DSettings.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── TagManager.asset │ └── TimeManager.asset ├── LICENSE ├── README.md ├── angular-spring ├── Cursor.as ├── Main.as └── Main.fla ├── animation-spring ├── Cursor.as ├── Main.as └── Main.fla ├── box-springs ├── springs.as3proj └── src │ └── Main.as ├── button-spring ├── Cursor.as ├── Main.as └── Main.fla ├── img ├── 3d-rotation-spring.gif ├── angular-spring.gif ├── animation-spring.gif ├── box-springs.gif └── button-spring.gif └── matlab ├── springImplicitEuler.m ├── springSemiImplicitEuler.m └── springTest.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders 2 | [Oo]bj/ 3 | [Bb]in/ 4 | [Ll]ibrary/ 5 | [Tt]emp/ 6 | [Ee]xportedObj/ 7 | 8 | # Binaries 9 | *.swf 10 | *.csproj 11 | *.unityproj 12 | *.sln 13 | *.suo 14 | *.tmp 15 | *.user 16 | *.svd 17 | -------------------------------------------------------------------------------- /3d-rotation-spring/Assets/Cube.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/Assets/Cube.unity -------------------------------------------------------------------------------- /3d-rotation-spring/Assets/Cube.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 052468ae757728f4e894c2b58f7a9fe3 3 | timeCreated: 1428744878 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /3d-rotation-spring/Assets/New Material.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/Assets/New Material.mat -------------------------------------------------------------------------------- /3d-rotation-spring/Assets/New Material.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c802231082a35d4478ebf92a7967ccb8 3 | timeCreated: 1428744391 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /3d-rotation-spring/Assets/QuaternionSpring.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class QuaternionSpring : MonoBehaviour { 5 | 6 | float x, v, xt; 7 | Vector3 dir; 8 | 9 | // Use this for initialization 10 | void Start () { 11 | x = 0.0f; 12 | v = 0.0f; 13 | xt = 0.0f; 14 | } 15 | 16 | float mx, my; 17 | 18 | // Update is called once per frame 19 | void Update () { 20 | 21 | if (Input.GetMouseButtonDown(0)) { 22 | Vector3 m = Input.mousePosition; 23 | mx = m.x; 24 | my = m.y; 25 | } 26 | 27 | if (Input.GetMouseButton(0)) { 28 | Vector3 m = Input.mousePosition; 29 | float dx = m.x - mx; 30 | float dy = m.y - my; 31 | float r = Mathf.Sqrt (dx * dx + dy * dy); 32 | if (r > 1.0f) { 33 | x = r; 34 | dir.Set (dx, 0.0f, dy); 35 | } 36 | } else { 37 | Spring (ref x, ref v, xt, 0.23f, 8.0f * Mathf.PI, Time.deltaTime); 38 | } 39 | transform.rotation = Quaternion.AngleAxis (x, Vector3.Cross (Vector3.up, dir)); 40 | } 41 | 42 | void Spring 43 | ( 44 | ref float x, ref float v, float xt, 45 | float zeta, float omega, float h 46 | ) 47 | { 48 | float f = 1.0f + 2.0f * h * zeta * omega; 49 | float oo = omega * omega; 50 | float hoo = h * oo; 51 | float hhoo = h * hoo; 52 | float detInv = 1.0f / (f + hhoo); 53 | float detX = f * x + h * v + hhoo * xt; 54 | float detV = v + hoo * (xt - x); 55 | x = detX * detInv; 56 | v = detV * detInv; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /3d-rotation-spring/Assets/QuaternionSpring.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a71ec4bc4387ab14ea825610832eef73 3 | timeCreated: 1428741091 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 5.0.0f4 2 | m_StandardAssetsVersion: 0 3 | -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /3d-rotation-spring/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/3d-rotation-spring/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Allen Chou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Numeric Springing 2 | by **Ming-Lun "Allen" Chou** / [AllenChou.net](http://AllenChou.net) / [@TheAllenChou](http://twitter.com/TheAllenChou) / [Patreon](https://www.patreon.com/TheAllenChou) 3 | 4 | Here are the source files used to test algorithms and generate example animations for my numeric springing blog posts: 5 | * [Precise Control over Numeric Springing](http://allenchou.net/2015/04/game-math-precise-control-over-numeric-springing/) 6 | * [Numeric Springing Examples](http://allenchou.net/2015/04/game-math-numeric-springing-examples/) 7 | * [More on Numeric Springing](http://allenchou.net/2015/04/game-math-more-on-numeric-springing/) 8 | 9 | ![box-springs](/img/box-springs.gif) ![button-spring](/img/button-spring.gif) 10 | ![angular-spring](/img/angular-spring.gif) ![animation-spring](/img/animation-spring.gif) ![3d-rotation-spring](/img/3d-rotation-spring.gif) 11 | -------------------------------------------------------------------------------- /angular-spring/Cursor.as: -------------------------------------------------------------------------------- 1 | package 2 | { 3 | import flash.display.MovieClip; 4 | import flash.events.MouseEvent; 5 | import flash.ui.Mouse; 6 | 7 | public class Cursor extends MovieClip 8 | { 9 | public function Cursor() 10 | { 11 | Mouse.hide(); 12 | stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove); 13 | stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown); 14 | stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp); 15 | } 16 | 17 | private function mouseMove(e:MouseEvent):void 18 | { 19 | x = parent.mouseX; 20 | y = parent.mouseY; 21 | 22 | const d0:Number = Math.abs(stage.mouseX); 23 | const d1:Number = Math.abs(stage.stageWidth - stage.mouseX); 24 | const d2:Number = Math.abs(stage.mouseY); 25 | const d3:Number = Math.abs(stage.stageHeight - stage.mouseY); 26 | const dd:Number = Math.min(Math.min(d0, d1), Math.min(d2, d3)); 27 | 28 | if (dd < 30.0) 29 | { 30 | alpha = Math.max(0.0, (dd - 10.0) / 20.0); 31 | } 32 | else 33 | { 34 | alpha = 1.0; 35 | } 36 | } 37 | 38 | private function mouseDown(e:MouseEvent):void 39 | { 40 | gotoAndStop(2); 41 | } 42 | 43 | private function mouseUp(e:MouseEvent):void 44 | { 45 | gotoAndStop(1); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /angular-spring/Main.as: -------------------------------------------------------------------------------- 1 | package 2 | { 3 | import flash.display.DisplayObject; 4 | import flash.display.MovieClip; 5 | import flash.display.Sprite; 6 | import flash.events.Event; 7 | import flash.events.MouseEvent; 8 | import flash.ui.Mouse; 9 | import flash.utils.getTimer; 10 | 11 | public class Main extends Sprite 12 | { 13 | private var m_data:Data = new Data(); 14 | 15 | public function Main() 16 | { 17 | stage.addEventListener(Event.ENTER_FRAME, update); 18 | } 19 | 20 | private var m_prevTime:int = 0; 21 | private function update(e:Event):void 22 | { 23 | const curTime:int = getTimer(); 24 | const dt:Number = (curTime - m_prevTime) / 1000.0; 25 | m_prevTime = curTime; 26 | 27 | m_data.xt = 90.0 + 180.0 * Math.atan2(stage.mouseY - m_obj.y, stage.mouseX - m_obj.x) / Math.PI; 28 | if (m_cursor.alpha == 0.0) 29 | m_data.xt = 0.0; 30 | 31 | spring(m_data, 0.23, 8.0 * Math.PI, dt); 32 | m_obj.rotation = m_data.x; 33 | } 34 | } 35 | } 36 | 37 | class Data 38 | { 39 | public var x:Number; 40 | public var v:Number; 41 | public var xt:Number; 42 | public function Data(x:Number = 0.0, v:Number = 0.0, xt:Number = 0.0) 43 | { 44 | this.x = x; 45 | this.v = v; 46 | this.xt = xt; 47 | } 48 | } 49 | 50 | function spring(data:Data, zeta:Number, omega:Number, h:Number):void 51 | { 52 | const f:Number = 1.0 + 2.0 * h * zeta * omega; 53 | const hh:Number = h * h; 54 | const oo:Number = omega * omega; 55 | const hoo:Number = h * oo; 56 | const hhoo:Number = h * hoo; 57 | const detInv:Number = 1.0 / (f + hhoo); 58 | const detX:Number = f * data.x + h * data.v + hhoo * data.xt; 59 | const detV:Number = data.v + hoo * (data.xt - data.x); 60 | data.x = detX * detInv; 61 | data.v = detV * detInv; 62 | } -------------------------------------------------------------------------------- /angular-spring/Main.fla: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/angular-spring/Main.fla -------------------------------------------------------------------------------- /animation-spring/Cursor.as: -------------------------------------------------------------------------------- 1 | package 2 | { 3 | import flash.display.MovieClip; 4 | import flash.events.MouseEvent; 5 | import flash.ui.Mouse; 6 | 7 | public class Cursor extends MovieClip 8 | { 9 | public function Cursor() 10 | { 11 | Mouse.hide(); 12 | stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove); 13 | stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown); 14 | stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp); 15 | } 16 | 17 | private function mouseMove(e:MouseEvent):void 18 | { 19 | x = parent.mouseX; 20 | y = parent.mouseY; 21 | 22 | const d0:Number = Math.abs(stage.mouseX); 23 | const d1:Number = Math.abs(stage.stageWidth - stage.mouseX); 24 | const d2:Number = Math.abs(stage.mouseY); 25 | const d3:Number = Math.abs(stage.stageHeight - stage.mouseY); 26 | const dd:Number = Math.min(Math.min(d0, d1), Math.min(d2, d3)); 27 | 28 | if (dd < 30.0) 29 | { 30 | alpha = Math.max(0.0, (dd - 10.0) / 20.0); 31 | } 32 | else 33 | { 34 | alpha = 1.0; 35 | } 36 | } 37 | 38 | private function mouseDown(e:MouseEvent):void 39 | { 40 | gotoAndStop(2); 41 | } 42 | 43 | private function mouseUp(e:MouseEvent):void 44 | { 45 | gotoAndStop(1); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /animation-spring/Main.as: -------------------------------------------------------------------------------- 1 | package 2 | { 3 | import flash.display.DisplayObject; 4 | import flash.display.MovieClip; 5 | import flash.display.Sprite; 6 | import flash.events.Event; 7 | import flash.events.MouseEvent; 8 | import flash.ui.Mouse; 9 | import flash.utils.getTimer; 10 | 11 | public class Main extends Sprite 12 | { 13 | private var m_data:Data = new Data(); 14 | 15 | public function Main() 16 | { 17 | stage.addEventListener(Event.ENTER_FRAME, update); 18 | } 19 | 20 | private var m_prevTime:int = 0; 21 | private function update(e:Event):void 22 | { 23 | const curTime:int = getTimer(); 24 | const dt:Number = (curTime - m_prevTime) / 1000.0; 25 | m_prevTime = curTime; 26 | 27 | m_data.xt = Math.min(1.0, Math.max(0.0, (stage.mouseX - m_obj.x) / (stage.stageWidth - m_obj.x - 50.0))); 28 | 29 | spring(m_data, 0.15, 8.0 * Math.PI, dt); 30 | m_obj.gotoAndStop(int(m_data.x * (m_obj.totalFrames - 30)) + 15); 31 | } 32 | } 33 | } 34 | 35 | class Data 36 | { 37 | public var x:Number; 38 | public var v:Number; 39 | public var xt:Number; 40 | public function Data(x:Number = 0.0, v:Number = 0.0, xt:Number = 0.0) 41 | { 42 | this.x = x; 43 | this.v = v; 44 | this.xt = xt; 45 | } 46 | } 47 | 48 | function spring(data:Data, zeta:Number, omega:Number, h:Number):void 49 | { 50 | const f:Number = 1.0 + 2.0 * h * zeta * omega; 51 | const hh:Number = h * h; 52 | const oo:Number = omega * omega; 53 | const hoo:Number = h * oo; 54 | const hhoo:Number = h * hoo; 55 | const detInv:Number = 1.0 / (f + hhoo); 56 | const detX:Number = f * data.x + h * data.v + hhoo * data.xt; 57 | const detV:Number = data.v + hoo * (data.xt - data.x); 58 | data.x = detX * detInv; 59 | data.v = detV * detInv; 60 | } -------------------------------------------------------------------------------- /animation-spring/Main.fla: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/animation-spring/Main.fla -------------------------------------------------------------------------------- /box-springs/springs.as3proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | -------------------------------------------------------------------------------- /box-springs/src/Main.as: -------------------------------------------------------------------------------- 1 | package 2 | { 3 | import flash.display.Shape; 4 | import flash.display.Sprite; 5 | import flash.events.Event; 6 | import flash.utils.getTimer; 7 | 8 | [SWF(width=160, height=160, frameRate=60)] 9 | public class Main extends Sprite 10 | { 11 | private var m_shape0:Shape; 12 | private var m_shape1:Shape; 13 | private var m_shape2:Shape; 14 | 15 | public function Main() 16 | { 17 | var container0:Sprite = new Sprite(); 18 | container0.x = 0.3 * stage.stageWidth; 19 | container0.y = 0.3 * stage.stageHeight; 20 | addChild(container0); 21 | 22 | var container1:Sprite = new Sprite(); 23 | container1.x = 0.7 * stage.stageWidth; 24 | container1.y = 0.3 * stage.stageHeight; 25 | addChild(container1); 26 | 27 | var container2:Sprite = new Sprite(); 28 | container2.x = 0.3 * stage.stageWidth; 29 | container2.y = 0.7 * stage.stageHeight; 30 | addChild(container2); 31 | 32 | m_shape0 = new Shape(); 33 | m_shape0.graphics.beginFill(0x8080C0); 34 | m_shape0.graphics.drawRoundRect(-20.0, -20.0, 40.0, 40.0, 10.0, 10.0); 35 | container0.addChild(m_shape0); 36 | 37 | m_shape1 = new Shape(); 38 | m_shape1.graphics.beginFill(0x8080C0); 39 | m_shape1.graphics.drawRoundRect(-20.0, -20.0, 40.0, 40.0, 10.0, 10.0); 40 | container1.addChild(m_shape1); 41 | 42 | m_shape2 = new Shape(); 43 | m_shape2.graphics.beginFill(0x8080C0); 44 | m_shape2.graphics.drawRoundRect(-20.0, -20.0, 40.0, 40.0, 10.0, 10.0); 45 | container2.addChild(m_shape2); 46 | 47 | stage.addEventListener(Event.ENTER_FRAME, update); 48 | } 49 | 50 | private var m_data:Data = new Data(); 51 | private var m_timer:Number = 0.0; 52 | private var m_oldTime:int = 0; 53 | private var m_i:int = 0; 54 | private function update(e:Event):void 55 | { 56 | const curTime:int = getTimer(); 57 | const dt:Number = (curTime - m_oldTime) / 1000.0; 58 | 59 | m_timer += dt; 60 | if (m_timer > 1.0) 61 | { 62 | m_timer = 0.0; 63 | 64 | m_data.x = -1.0; 65 | m_data.v = 0.0; 66 | 67 | m_i ^= 1; 68 | } 69 | 70 | spring(m_data, 0.23, 8.0 * Math.PI, 0.016); 71 | 72 | m_shape0.scaleX = 1.0 - m_data.x; 73 | m_shape0.scaleY = m_data.x + 1.0; 74 | m_shape1.rotation = 90.0 * m_data.x; 75 | 76 | if (m_i) 77 | { 78 | m_shape2.x = 0.4 * stage.stageWidth * (1.0 + m_data.x); 79 | } 80 | else 81 | { 82 | m_shape2.x = 0.4 * stage.stageWidth * -m_data.x; 83 | } 84 | 85 | m_oldTime = curTime; 86 | } 87 | } 88 | } 89 | 90 | class Data 91 | { 92 | public var x:Number; 93 | public var v:Number; 94 | public function Data(x:Number = -1.0, v:Number = 0.0) 95 | { 96 | this.x = x; 97 | this.v = v; 98 | } 99 | } 100 | 101 | function spring(data:Data, zeta:Number, omega:Number, dt:Number):void 102 | { 103 | data.v = (1.0 - 2.0 * dt * zeta * omega) * data.v - dt * omega * omega * data.x; 104 | data.x = data.x + data.v * dt; 105 | } -------------------------------------------------------------------------------- /button-spring/Cursor.as: -------------------------------------------------------------------------------- 1 | package 2 | { 3 | import flash.display.MovieClip; 4 | import flash.events.MouseEvent; 5 | import flash.ui.Mouse; 6 | 7 | public class Cursor extends MovieClip 8 | { 9 | public function Cursor() 10 | { 11 | stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove); 12 | stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown); 13 | stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp); 14 | } 15 | 16 | private function mouseMove(e:MouseEvent):void 17 | { 18 | x = parent.mouseX; 19 | y = parent.mouseY; 20 | 21 | const d0:Number = Math.abs(stage.mouseX); 22 | const d1:Number = Math.abs(stage.stageWidth - stage.mouseX); 23 | const d2:Number = Math.abs(stage.mouseY); 24 | const d3:Number = Math.abs(stage.stageHeight - stage.mouseY); 25 | const dd:Number = Math.min(Math.min(d0, d1), Math.min(d2, d3)); 26 | 27 | if (dd < 30.0) 28 | { 29 | alpha = Math.max(0.0, (dd - 10.0) / 20.0); 30 | } 31 | else 32 | { 33 | alpha = 1.0; 34 | } 35 | 36 | e.updateAfterEvent(); 37 | } 38 | 39 | private function mouseDown(e:MouseEvent):void 40 | { 41 | gotoAndStop(2); 42 | } 43 | 44 | private function mouseUp(e:MouseEvent):void 45 | { 46 | gotoAndStop(1); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /button-spring/Main.as: -------------------------------------------------------------------------------- 1 | package 2 | { 3 | import flash.display.DisplayObject; 4 | import flash.display.MovieClip; 5 | import flash.display.Sprite; 6 | import flash.events.Event; 7 | import flash.events.MouseEvent; 8 | import flash.ui.Mouse; 9 | import flash.utils.getTimer; 10 | 11 | public class Main extends Sprite 12 | { 13 | private var m_data:Data = new Data(); 14 | 15 | public function Main() 16 | { 17 | Mouse.hide(); 18 | 19 | m_data.x = m_data.xt = m_obj.y = m_btn2.y; 20 | 21 | m_btn0.addEventListener(MouseEvent.CLICK, onClick); 22 | m_btn1.addEventListener(MouseEvent.CLICK, onClick); 23 | m_btn2.addEventListener(MouseEvent.CLICK, onClick); 24 | stage.addEventListener(Event.ENTER_FRAME, update); 25 | } 26 | 27 | private var m_prevTime:int = 0; 28 | private function update(e:Event):void 29 | { 30 | const curTime:int = getTimer(); 31 | const dt:Number = (curTime - m_prevTime) / 1000.0; 32 | m_prevTime = curTime; 33 | 34 | spring(m_data, 0.23, 8.0 * Math.PI, dt); 35 | m_obj.y = m_data.x; 36 | } 37 | 38 | private function onClick(e:Event):void 39 | { 40 | m_btn0.gotoAndStop(1); 41 | m_btn1.gotoAndStop(1); 42 | m_btn2.gotoAndStop(1); 43 | MovieClip(e.target).gotoAndStop(2); 44 | 45 | m_data.xt = DisplayObject(e.target).y; 46 | } 47 | } 48 | } 49 | 50 | class Data 51 | { 52 | public var x:Number; 53 | public var v:Number; 54 | public var xt:Number; 55 | public function Data(x:Number = 0.0, v:Number = 0.0, xt:Number = 0.0) 56 | { 57 | this.x = x; 58 | this.v = v; 59 | this.xt = xt; 60 | } 61 | } 62 | 63 | function spring(data:Data, zeta:Number, omega:Number, h:Number):void 64 | { 65 | const f:Number = 1.0 + 2.0 * h * zeta * omega; 66 | const hh:Number = h * h; 67 | const oo:Number = omega * omega; 68 | const hoo:Number = h * oo; 69 | const hhoo:Number = h * hoo; 70 | const detInv:Number = 1.0 / (f + hhoo); 71 | const detX:Number = f * data.x + h * data.v + hhoo * data.xt; 72 | const detV:Number = data.v + hoo * (data.xt - data.x); 73 | data.x = detX * detInv; 74 | data.v = detV * detInv; 75 | } -------------------------------------------------------------------------------- /button-spring/Main.fla: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/button-spring/Main.fla -------------------------------------------------------------------------------- /img/3d-rotation-spring.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/img/3d-rotation-spring.gif -------------------------------------------------------------------------------- /img/angular-spring.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/img/angular-spring.gif -------------------------------------------------------------------------------- /img/animation-spring.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/img/animation-spring.gif -------------------------------------------------------------------------------- /img/box-springs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/img/box-springs.gif -------------------------------------------------------------------------------- /img/button-spring.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAllenChou/numeric-springing/e0307fc8ba1083495625f5edaafa5974c60446dd/img/button-spring.gif -------------------------------------------------------------------------------- /matlab/springImplicitEuler.m: -------------------------------------------------------------------------------- 1 | function [ x2, v2 ] = springImplicitEuler( x1, v1, xt, zeta, omega, dt ) 2 | 3 | f = 1.0 + 2.0 * dt * zeta * omega; 4 | omega2 = omega * omega; 5 | detInv = 1.0 / (f + dt * dt * omega2); 6 | detX = x1 * f + dt * v1 + dt * dt * omega2 * xt; 7 | detV = v1 + dt * omega2 * xt - dt * omega2 * x1; 8 | 9 | x2 = detX * detInv; 10 | v2 = detV * detInv; 11 | 12 | end 13 | 14 | -------------------------------------------------------------------------------- /matlab/springSemiImplicitEuler.m: -------------------------------------------------------------------------------- 1 | function [ x2, v2 ] = springSemiImplicitEuler( x1, v1, xt, zeta, omega, dt ) 2 | 3 | v2 = v1 - 2.0 * dt * zeta * omega * v1 - dt * omega * omega * (x1 - xt); 4 | x2 = x1 + dt * v2; 5 | 6 | end 7 | 8 | -------------------------------------------------------------------------------- /matlab/springTest.m: -------------------------------------------------------------------------------- 1 | function [ ] = springTest( f, zeta, omega, duration ) 2 | 3 | dt = 1 / 60; 4 | tv = 0:dt:duration; 5 | 6 | t = zeros(1, length(tv)); 7 | y = zeros(1, length(tv)); 8 | x = 1; 9 | v = 0; 10 | xt = 0; 11 | 12 | for i = 1:length(tv) 13 | t(i) = i * dt; 14 | y(i) = x; 15 | [x, v] = f(x, v, xt, zeta, omega, dt); 16 | end 17 | 18 | set(figure(), 'Position', [200, 200, 400, 250]); 19 | plot(t, y); 20 | xlabel('time'); 21 | ylabel('value'); 22 | axis([0, duration, -1, 1]); 23 | 24 | end 25 | 26 | --------------------------------------------------------------------------------