commit e93a412ba65d893bcebd2c8fd443546fce03cbbe Author: Nefrace Date: Mon Nov 4 17:09:01 2024 +0300 first commit diff --git a/examples/callbacks/main.odin b/examples/callbacks/main.odin new file mode 100644 index 0000000..b6bc0ab --- /dev/null +++ b/examples/callbacks/main.odin @@ -0,0 +1,62 @@ +package vectors + +import nt "../../" +import "core:math/ease" +import "core:math/rand" +import rl "vendor:raylib" + +vec2 :: [2]f32 +easings := [?]ease.Ease{.Sine_In_Out, .Bounce_Out, .Cubic_In, .Elastic_Out, .Linear} + +pos_target :: struct { + value: ^vec2, + target: vec2, +} + +tweens: nt.Tween_Map(vec2) + +main :: proc() { + rl.InitWindow(800, 600, "VECTORS") + rl.SetTargetFPS(60) + + + tweens := nt.init(vec2) + defer nt.destroy_tweens(&tweens) + + positions: [5]vec2 + for &pos, i in positions { + pos.y = f32(i * 100 + 50) + pos.x = 100 + + target := new(pos_target) + target.value = &pos + target.target = pos + + nt.animate( + &tweens, + &pos, + vec2{rand.float32_range(100, 700), rand.float32_range(100, 500)}, + 1 + f32(i) / 5, + easings[i], + &pos, + proc(data: rawptr) { + p := transmute(^vec2)data + nt.animate(&tweens, p, vec2{100, 100}, 1) + }, + ) + } + + for !rl.WindowShouldClose() { + delta := rl.GetFrameTime() + + nt.process(&tweens, delta) + + rl.BeginDrawing() + rl.ClearBackground(rl.BLACK) + for x in positions { + rl.DrawRectangleV(x, {20, 20}, rl.WHITE) + } + rl.EndDrawing() + } +} + diff --git a/examples/float/main.odin b/examples/float/main.odin new file mode 100644 index 0000000..3fa6c50 --- /dev/null +++ b/examples/float/main.odin @@ -0,0 +1,36 @@ +package floats + +import nt "../../" +import "core:math/ease" +import rl "vendor:raylib" + +main :: proc() { + rl.InitWindow(800, 600, "FLOATS") + rl.SetTargetFPS(60) + + vec2 :: [2]f32 + + easings := [?]ease.Ease{.Sine_In_Out, .Bounce_Out, .Cubic_In, .Elastic_Out, .Linear} // Just a list of various easings + tweens := nt.init(f32) // Make a map of tweens. Can be used to animage single f32 values + + positions: [5]vec2 + for &pos, i in positions { + pos.y = f32(i * 100 + 50) + pos.x = 100 + + nt.animate(&tweens, &pos.x, 700, 5, easings[i]) // Animating all the positions with different easings + } + + for !rl.WindowShouldClose() { + delta := rl.GetFrameTime() + + nt.process(&tweens, delta) // Needs to be called every frame with delta time as argument + + rl.BeginDrawing() + rl.ClearBackground(rl.BLACK) + for x in positions { + rl.DrawRectangleV(x, {20, 20}, rl.WHITE) + } + rl.EndDrawing() + } +} diff --git a/examples/vector/main.odin b/examples/vector/main.odin new file mode 100644 index 0000000..284be05 --- /dev/null +++ b/examples/vector/main.odin @@ -0,0 +1,43 @@ +package vectors + +import nt "../../" +import "core:math/ease" +import "core:math/rand" +import rl "vendor:raylib" + +main :: proc() { + rl.InitWindow(800, 600, "VECTORS") + rl.SetTargetFPS(60) + + vec2 :: [2]f32 + + easings := [?]ease.Ease{.Sine_In_Out, .Bounce_Out, .Cubic_In, .Elastic_Out, .Linear} + tweens := nt.init([2]f32) + + positions: [5]vec2 + for &pos, i in positions { + pos.y = f32(i * 100 + 50) + pos.x = 100 + + nt.animate( + &tweens, + &pos, + vec2{rand.float32_range(100, 700), rand.float32_range(100, 500)}, + 5, + easings[i], + ) + } + + for !rl.WindowShouldClose() { + delta := rl.GetFrameTime() + + nt.process(&tweens, delta) + + rl.BeginDrawing() + rl.ClearBackground(rl.BLACK) + for x in positions { + rl.DrawRectangleV(x, {20, 20}, rl.WHITE) + } + rl.EndDrawing() + } +} diff --git a/ntween.odin b/ntween.odin new file mode 100644 index 0000000..411ee39 --- /dev/null +++ b/ntween.odin @@ -0,0 +1,100 @@ +package ntween + +import "base:intrinsics" +import "core:math" +import "core:math/ease" +import "core:math/linalg" + + +Tween :: struct($T: typeid) where intrinsics.type_is_float(T) || intrinsics.type_is_array(T) { + value: ^T, + from: T, + to: T, + time: f32, + duration: f32, + ease_type: ease.Ease, + active: bool, + finished: proc(data: rawptr), + data: rawptr, +} + +Tween_Map :: struct($T: typeid) { + values: map[^T]Tween(T), +} + +init :: proc( + $T: typeid, + capacity := 8, +) -> Tween_Map(T) where intrinsics.type_is_float(T) || + intrinsics.type_is_array(T) { + return {values = make_map_cap(map[^T]Tween(T), capacity)} +} + +clear_tweens :: proc(tweens: ^Tween_Map($T)) { + clear(&tweens.values) +} + +destroy_tweens :: proc(tweens: ^Tween_Map($T)) { + delete_map(tweens.values) +} + + +animate :: proc( + tweens: ^Tween_Map($T), + value: ^T, + to: T, + duration: f32, + ease: ease.Ease = ease.Ease.Quartic_In_Out, + data: rawptr = nil, + callback: proc(data: rawptr) = nil, +) -> ^Tween(T) { + tween: ^Tween(T) + if t, ok := &tweens.values[value]; ok { + tween = t + } else { + tweens.values[value] = {} + tween = &tweens.values[value] + } + + tween^ = { + value = value, + from = value^, + to = to, + duration = duration, + ease_type = ease, + active = true, + data = data, + finished = callback, + } + + return tween +} + +cancel :: proc(t: ^Tween($T)) { + t.active = false +} + +process :: proc(tweens: ^Tween_Map($T), delta: f32) { + for i, &tween in tweens.values { + tween.time += delta + p := clamp(tween.time / tween.duration, 0, 1) + + val := ease.ease(tween.ease_type, p) + if tween.value != nil { + tween.value^ = math.lerp(tween.from, tween.to, val) + } else { + tween.active = false + } + + if tween.time >= tween.duration { + tween.active = false + if tween.finished != nil { + tween.finished(tween.data) + } + } + if !tween.active { + delete_key(&tweens.values, tween.value) + } + } +} +