|
1 | | -// Easing procedures and flux easing used for animations. |
| 1 | +// Easing procedures used for animations. |
2 | 2 | package ease |
3 | 3 |
|
4 | | -import "core:math" |
| 4 | +@require import "core:math" |
5 | 5 | import "base:intrinsics" |
6 | | -import "core:time" |
7 | 6 |
|
8 | 7 | @(private) PI_2 :: math.PI / 2 |
9 | 8 |
|
@@ -174,7 +173,7 @@ exponential_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_f |
174 | 173 | if p == 0.0 || p == 1.0 { |
175 | 174 | return p |
176 | 175 | } |
177 | | - |
| 176 | + |
178 | 177 | if p < 0.5 { |
179 | 178 | return 0.5 * math.pow(2, (20 * p) - 10) |
180 | 179 | } else { |
@@ -307,224 +306,51 @@ Ease :: enum { |
307 | 306 | } |
308 | 307 |
|
309 | 308 | @(require_results) |
310 | | -ease :: proc "contextless" (type: Ease, p: $T) -> T |
311 | | - where intrinsics.type_is_float(T) { |
| 309 | +ease :: proc "contextless" (type: Ease, p: $T) -> T where intrinsics.type_is_float(T) { |
312 | 310 | switch type { |
313 | | - case .Linear: return p |
| 311 | + case .Linear: return p |
314 | 312 |
|
315 | | - case .Quadratic_In: return quadratic_in(p) |
316 | | - case .Quadratic_Out: return quadratic_out(p) |
317 | | - case .Quadratic_In_Out: return quadratic_in_out(p) |
| 313 | + case .Quadratic_In: return quadratic_in(p) |
| 314 | + case .Quadratic_Out: return quadratic_out(p) |
| 315 | + case .Quadratic_In_Out: return quadratic_in_out(p) |
318 | 316 |
|
319 | | - case .Cubic_In: return cubic_in(p) |
320 | | - case .Cubic_Out: return cubic_out(p) |
321 | | - case .Cubic_In_Out: return cubic_in_out(p) |
| 317 | + case .Cubic_In: return cubic_in(p) |
| 318 | + case .Cubic_Out: return cubic_out(p) |
| 319 | + case .Cubic_In_Out: return cubic_in_out(p) |
322 | 320 |
|
323 | | - case .Quartic_In: return quartic_in(p) |
324 | | - case .Quartic_Out: return quartic_out(p) |
325 | | - case .Quartic_In_Out: return quartic_in_out(p) |
| 321 | + case .Quartic_In: return quartic_in(p) |
| 322 | + case .Quartic_Out: return quartic_out(p) |
| 323 | + case .Quartic_In_Out: return quartic_in_out(p) |
326 | 324 |
|
327 | | - case .Quintic_In: return quintic_in(p) |
328 | | - case .Quintic_Out: return quintic_out(p) |
329 | | - case .Quintic_In_Out: return quintic_in_out(p) |
| 325 | + case .Quintic_In: return quintic_in(p) |
| 326 | + case .Quintic_Out: return quintic_out(p) |
| 327 | + case .Quintic_In_Out: return quintic_in_out(p) |
330 | 328 |
|
331 | | - case .Sine_In: return sine_in(p) |
332 | | - case .Sine_Out: return sine_out(p) |
333 | | - case .Sine_In_Out: return sine_in_out(p) |
| 329 | + case .Sine_In: return sine_in(p) |
| 330 | + case .Sine_Out: return sine_out(p) |
| 331 | + case .Sine_In_Out: return sine_in_out(p) |
334 | 332 |
|
335 | | - case .Circular_In: return circular_in(p) |
336 | | - case .Circular_Out: return circular_out(p) |
337 | | - case .Circular_In_Out: return circular_in_out(p) |
| 333 | + case .Circular_In: return circular_in(p) |
| 334 | + case .Circular_Out: return circular_out(p) |
| 335 | + case .Circular_In_Out: return circular_in_out(p) |
338 | 336 |
|
339 | | - case .Exponential_In: return exponential_in(p) |
340 | | - case .Exponential_Out: return exponential_out(p) |
| 337 | + case .Exponential_In: return exponential_in(p) |
| 338 | + case .Exponential_Out: return exponential_out(p) |
341 | 339 | case .Exponential_In_Out: return exponential_in_out(p) |
342 | 340 |
|
343 | | - case .Elastic_In: return elastic_in(p) |
344 | | - case .Elastic_Out: return elastic_out(p) |
345 | | - case .Elastic_In_Out: return elastic_in_out(p) |
| 341 | + case .Elastic_In: return elastic_in(p) |
| 342 | + case .Elastic_Out: return elastic_out(p) |
| 343 | + case .Elastic_In_Out: return elastic_in_out(p) |
346 | 344 |
|
347 | | - case .Back_In: return back_in(p) |
348 | | - case .Back_Out: return back_out(p) |
349 | | - case .Back_In_Out: return back_in_out(p) |
| 345 | + case .Back_In: return back_in(p) |
| 346 | + case .Back_Out: return back_out(p) |
| 347 | + case .Back_In_Out: return back_in_out(p) |
350 | 348 |
|
351 | | - case .Bounce_In: return bounce_in(p) |
352 | | - case .Bounce_Out: return bounce_out(p) |
353 | | - case .Bounce_In_Out: return bounce_in_out(p) |
| 349 | + case .Bounce_In: return bounce_in(p) |
| 350 | + case .Bounce_Out: return bounce_out(p) |
| 351 | + case .Bounce_In_Out: return bounce_in_out(p) |
354 | 352 | } |
355 | 353 |
|
356 | 354 | // in case type was invalid |
357 | 355 | return 0 |
358 | 356 | } |
359 | | -Flux_Map :: struct($T: typeid) { |
360 | | - values: map[^T]Flux_Tween(T), |
361 | | - keys_to_be_deleted: [dynamic]^T, |
362 | | -} |
363 | | - |
364 | | -Flux_Tween :: struct($T: typeid) { |
365 | | - value: ^T, |
366 | | - start: T, |
367 | | - diff: T, |
368 | | - goal: T, |
369 | | - |
370 | | - delay: f64, // in seconds |
371 | | - duration: time.Duration, |
372 | | - |
373 | | - progress: f64, |
374 | | - rate: f64, |
375 | | - type: Ease, |
376 | | - |
377 | | - inited: bool, |
378 | | - |
379 | | - // callbacks, data can be set, will be pushed to callback |
380 | | - data: rawptr, // by default gets set to value input |
381 | | - on_start: proc(flux: ^Flux_Map(T), data: rawptr), |
382 | | - on_update: proc(flux: ^Flux_Map(T), data: rawptr), |
383 | | - on_complete: proc(flux: ^Flux_Map(T), data: rawptr), |
384 | | -} |
385 | | - |
386 | | -// init flux map to a float type and a wanted cap |
387 | | -@(require_results) |
388 | | -flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) { |
389 | | - return { |
390 | | - values = make(map[^T]Flux_Tween(T), value_capacity), |
391 | | - keys_to_be_deleted = make([dynamic]^T, 0, value_capacity), |
392 | | - } |
393 | | -} |
394 | | - |
395 | | -// delete map content |
396 | | -flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) { |
397 | | - delete(flux.values) |
398 | | - delete(flux.keys_to_be_deleted) |
399 | | -} |
400 | | - |
401 | | -// clear map content, stops all animations |
402 | | -flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) { |
403 | | - clear(&flux.values) |
404 | | -} |
405 | | - |
406 | | -// append / overwrite existing tween value to parameters |
407 | | -// rest is initialized in flux_tween_init, inside update |
408 | | -// return value can be used to set callbacks |
409 | | -@(require_results) |
410 | | -flux_to :: proc( |
411 | | - flux: ^Flux_Map($T), |
412 | | - value: ^T, |
413 | | - goal: T, |
414 | | - type: Ease = .Quadratic_Out, |
415 | | - duration: time.Duration = time.Second, |
416 | | - delay: f64 = 0, |
417 | | -) -> (tween: ^Flux_Tween(T)) where intrinsics.type_is_float(T) { |
418 | | - if res, ok := &flux.values[value]; ok { |
419 | | - tween = res |
420 | | - } else { |
421 | | - flux.values[value] = {} |
422 | | - tween = &flux.values[value] |
423 | | - } |
424 | | - |
425 | | - tween^ = { |
426 | | - value = value, |
427 | | - goal = goal, |
428 | | - duration = duration, |
429 | | - delay = delay, |
430 | | - type = type, |
431 | | - data = value, |
432 | | - } |
433 | | - |
434 | | - return |
435 | | -} |
436 | | - |
437 | | -// init internal properties |
438 | | -flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where intrinsics.type_is_float(T) { |
439 | | - tween.inited = true |
440 | | - tween.start = tween.value^ |
441 | | - tween.diff = tween.goal - tween.value^ |
442 | | - s := time.duration_seconds(duration) |
443 | | - tween.rate = duration > 0 ? 1.0 / s : 0 |
444 | | - tween.progress = duration > 0 ? 0 : 1 |
445 | | -} |
446 | | - |
447 | | -// update all tweens, wait for their delay if one exists |
448 | | -// calls callbacks in all stages, when they're filled |
449 | | -// deletes tween from the map after completion |
450 | | -flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) { |
451 | | - clear(&flux.keys_to_be_deleted) |
452 | | - |
453 | | - for key, &tween in flux.values { |
454 | | - delay_remainder := f64(0) |
455 | | - |
456 | | - // Update delay if necessary. |
457 | | - if tween.delay > 0 { |
458 | | - tween.delay -= dt |
459 | | - |
460 | | - if tween.delay < 0 { |
461 | | - // We finished the delay, but in doing so consumed part of this frame's `dt` budget. |
462 | | - // Keep track of it so we can apply it to this tween without affecting others. |
463 | | - delay_remainder = tween.delay |
464 | | - // We're done with this delay. |
465 | | - tween.delay = 0 |
466 | | - } |
467 | | - } |
468 | | - |
469 | | - // We either had no delay, or the delay has been consumed. |
470 | | - if tween.delay <= 0 { |
471 | | - if !tween.inited { |
472 | | - flux_tween_init(&tween, tween.duration) |
473 | | - |
474 | | - if tween.on_start != nil { |
475 | | - tween.on_start(flux, tween.data) |
476 | | - } |
477 | | - } |
478 | | - |
479 | | - // If part of the `dt` budget was consumed this frame, then `delay_remainder` will be |
480 | | - // that remainder, a negative value. Adding it to `dt` applies what's left of the `dt` |
481 | | - // to the tween so it advances properly, instead of too much or little. |
482 | | - tween.progress += tween.rate * (dt + delay_remainder) |
483 | | - x := tween.progress >= 1 ? 1 : ease(tween.type, tween.progress) |
484 | | - tween.value^ = tween.start + tween.diff * T(x) |
485 | | - |
486 | | - if tween.on_update != nil { |
487 | | - tween.on_update(flux, tween.data) |
488 | | - } |
489 | | - |
490 | | - if tween.progress >= 1 { |
491 | | - // append keys to array that will be deleted after the loop |
492 | | - append(&flux.keys_to_be_deleted, key) |
493 | | - |
494 | | - if tween.on_complete != nil { |
495 | | - tween.on_complete(flux, tween.data) |
496 | | - } |
497 | | - } |
498 | | - } |
499 | | - } |
500 | | - |
501 | | - // loop through keys that should be deleted from the map |
502 | | - if len(flux.keys_to_be_deleted) != 0 { |
503 | | - for key in flux.keys_to_be_deleted { |
504 | | - delete_key(&flux.values, key) |
505 | | - } |
506 | | - } |
507 | | -} |
508 | | - |
509 | | -// stop a specific key inside the map |
510 | | -// returns true when it successfully removed the key |
511 | | -@(require_results) |
512 | | -flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) { |
513 | | - if key in flux.values { |
514 | | - delete_key(&flux.values, key) |
515 | | - return true |
516 | | - } |
517 | | - |
518 | | - return false |
519 | | -} |
520 | | - |
521 | | -// returns the amount of time left for the tween animation, if the key exists in the map |
522 | | -// returns 0 if the tween doesnt exist on the map |
523 | | -@(require_results) |
524 | | -flux_tween_time_left :: proc(flux: Flux_Map($T), key: ^T) -> f64 { |
525 | | - if tween, ok := flux.values[key]; ok { |
526 | | - return ((1 - tween.progress) * tween.rate) + tween.delay |
527 | | - } else { |
528 | | - return 0 |
529 | | - } |
530 | | -} |
0 commit comments