v4k-git-backup/engine/split/v4k_editor_timeline.h

173 lines
6.3 KiB
C
Raw Normal View History

2024-03-01 09:12:47 +00:00
#define TIMELINE_ICON ICON_MDI_CHART_TIMELINE
#define TIMELINE_TITLE "Timeline " TIMELINE_ICON
EDITOR_BIND(timeline, "held(CTRL)&down(3)", { ui_show(TIMELINE_TITLE, ui_visible(TIMELINE_TITLE) ^ true); });
int ui_tween(const char *label, tween_t *t) {
if( ui_filter && ui_filter[0] ) if( !strstr(label, ui_filter) ) return 0;
int expand_keys = label[0] == '!'; label += expand_keys;
const char *id = label;
if( strchr(id, '@') ) *strchr((char*)(id = (const char*)va("%s", label)), '@') = '\0';
enum { LABEL_SPACING = 250 };
enum { ROUNDING = 0 };
enum { THICKNESS = 1 };
enum { PIXELS_PER_SECOND = 60 };
enum { KEY_WIDTH = 5, KEY_HEIGHT = 5 };
enum { TIMELINE_HEIGHT = 25 };
enum { MARKER1_HEIGHT = 5, MARKER10_HEIGHT = 20, MARKER5_HEIGHT = (MARKER1_HEIGHT + MARKER10_HEIGHT) / 2 };
unsigned base_color = WHITE;
unsigned time_color = YELLOW;
unsigned duration_color = ORANGE;
unsigned key_color = GREEN;
int changed = 0;
#if 0
// two rows with height:30 composed of three widgets
nk_layout_row_template_begin(ui_ctx, 30);
nk_layout_row_template_push_variable(ui_ctx, t->duration * PIXELS_PER_SECOND); // min 80px. can grow
nk_layout_row_template_end(ui_ctx);
#endif
char *sid = va("%s.%d", id, 0);
uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
for(int i = 0; sid[i]; ++i) hash = (hash ^ sid[i]) * mult;
ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;
ui_label(label);
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
struct nk_rect bounds; nk_layout_peek(&bounds, ui_ctx);
bounds.y -= 30;
struct nk_rect baseline = bounds; baseline.y += 30/2;
baseline.x += LABEL_SPACING;
baseline.w -= LABEL_SPACING;
// tween duration
{
struct nk_rect pos = baseline;
pos.w = pos.x + t->duration * PIXELS_PER_SECOND;
pos.y -= TIMELINE_HEIGHT/2;
pos.h = TIMELINE_HEIGHT;
nk_stroke_rect(canvas, pos, ROUNDING, THICKNESS*2, AS_NKCOLOR(duration_color));
}
// tween ranges
for(int i = 0, end = array_count(t->keyframes) - 1; i < end; ++i) {
tween_keyframe_t *k = t->keyframes + i;
tween_keyframe_t *next = k + 1;
struct nk_rect pos = baseline;
pos.x += k->t * PIXELS_PER_SECOND;
pos.w = (next->t - k->t) * PIXELS_PER_SECOND;
pos.y -= TIMELINE_HEIGHT/2;
pos.h = TIMELINE_HEIGHT;
char *sid = va("%s.%d", id, i);
uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
for(int i = 0; sid[i]; ++i) hash = (hash ^ sid[i]) * mult;
ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;
struct nk_color c = nk_hsva_f(ui_hue, 0.75f, 0.8f, ui_alpha);
nk_fill_rect(canvas, pos, ROUNDING, k->ease == EASE_ZERO ? AS_NKCOLOR(0) : c); // AS_NKCOLOR(track_color));
}
// horizontal line
nk_stroke_line(canvas, baseline.x, baseline.y, baseline.x+baseline.w,baseline.y, THICKNESS, AS_NKCOLOR(base_color));
// unit, 5-unit and 10-unit markers
for( float i = 0, j = 0; i < baseline.w; i += PIXELS_PER_SECOND/10, ++j ) {
int len = !((int)j%10) ? MARKER10_HEIGHT : !((int)j%5) ? MARKER5_HEIGHT : MARKER1_HEIGHT;
nk_stroke_line(canvas, baseline.x+i, baseline.y-len, baseline.x+i, baseline.y+len, THICKNESS, AS_NKCOLOR(base_color));
}
// time marker
float px = t->time * PIXELS_PER_SECOND;
nk_stroke_line(canvas, baseline.x+px, bounds.y, baseline.x+px, bounds.y+bounds.h, THICKNESS*2, AS_NKCOLOR(time_color));
nk_draw_symbol(canvas, NK_SYMBOL_TRIANGLE_DOWN, ((struct nk_rect){ baseline.x+px-4,bounds.y-4-8,8,8}), /*bg*/AS_NKCOLOR(0), /*fg*/AS_NKCOLOR(time_color), 0.f/*border_width*/, ui_ctx->style.font);
// key markers
for each_array_ptr(t->keyframes, tween_keyframe_t, k) {
struct nk_rect pos = baseline;
pos.x += k->t * PIXELS_PER_SECOND;
vec2 romboid[] = {
{pos.x-KEY_WIDTH,pos.y}, {pos.x,pos.y-KEY_HEIGHT},
{pos.x+KEY_WIDTH,pos.y}, {pos.x,pos.y+KEY_HEIGHT}
};
nk_fill_polygon(canvas, (float*)romboid, countof(romboid), AS_NKCOLOR(key_color));
}
// keys ui
if( expand_keys )
for(int i = 0, end = array_count(t->keyframes); i < end; ++i) {
tween_keyframe_t *k = t->keyframes + i;
if( ui_collapse(va("Key %d", i), va("%s.%d", id, i))) {
changed |= ui_float("Time", &k->t);
changed |= ui_float3("Value", &k->v.x);
changed |= ui_list("Ease", ease_enums(), EASE_NUM, &k->ease );
ui_collapse_end();
}
}
return changed;
}
tween_t* rand_tween() {
tween_t demo = tween();
int num_keys = randi(2,8);
double t = 0;
for( int i = 0; i < num_keys; ++i) {
tween_setkey(&demo, t, scale3(vec3(randf(),randf(),randf()),randi(-5,5)), randi(0,EASE_NUM) );
t += randi(1,5) / ((float)(1 << randi(0,2)));
}
tween_t *p = CALLOC(1, sizeof(tween_t));
memcpy(p, &demo, sizeof(tween_t));
return p;
}
int editor_timeline(int window_mode) {
static array(tween_t*) tweens = 0;
do_once {
array_push(tweens, rand_tween());
}
if( editor.t == 0 )
for each_array(tweens, tween_t*,t) {
tween_reset(t);
}
else
for each_array(tweens, tween_t*,t) {
tween_update(t, editor.dt);
}
static void *selected = NULL;
if( editor_begin(TIMELINE_TITLE, window_mode) ) {
int choice = ui_toolbar(ICON_MDI_PLUS ";" ICON_MDI_MINUS );
if( choice == 1 ) array_push(tweens, rand_tween());
if( choice == 2 && selected ) {
int target = -1;
for( int i = 0, end = array_count(tweens); i < end; ++i ) if( tweens[i] == selected ) { target = i; break; }
if( target >= 0 ) { array_erase_slow(tweens, target); selected = NULL; }
}
for each_array(tweens, tween_t*,t) {
ui_tween(va("%s%p@%05.2fs Value: %s", t == selected ? "!":"", t, t->time, ftoa3(t->result)), t);
if(ui_label_icon_clicked_L.x) selected = (t != selected) ? t : NULL;
}
editor_end(window_mode);
}
return 0;
}
AUTORUN {
array_push(editor.subeditors, editor_timeline);
}