274 lines
6.9 KiB
C++
274 lines
6.9 KiB
C++
|
#include"timeline.h"
|
||
|
|
||
|
#include<wx/menu.h>
|
||
|
#include<wx/dcclient.h>
|
||
|
#include<wx/settings.h>
|
||
|
|
||
|
#include<algorithm>
|
||
|
|
||
|
#include<float.h>
|
||
|
|
||
|
#include"frame.h"
|
||
|
|
||
|
static wxBitmap bmpKf;
|
||
|
static wxBitmap bmpKfExtrap;
|
||
|
|
||
|
template<typename T>
|
||
|
static T mod(T a, T b) {
|
||
|
return (a % b + b) % b;
|
||
|
}
|
||
|
|
||
|
#define ZERO_TIME_BASE 128
|
||
|
|
||
|
bool Timeline::MouseOverKF(wxPoint p, size_t &kfsIdxRet, size_t &kfIdxRet) {
|
||
|
auto f = (Frame*) GetParent();
|
||
|
|
||
|
int kfsIdx = p.y / bmpKf.GetHeight() - 1;
|
||
|
|
||
|
if(kfsIdx < 0 || kfsIdx >= f->graph->backendNG->keyframesList.count) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
float t = (p.x + camX - ZERO_TIME_BASE) / (float) scale;
|
||
|
float threshold = bmpKf.GetWidth() / (float) scale / 2;
|
||
|
|
||
|
size_t idx = CHi_GetClosestKeyframe(f->graph->backendNG, kfsIdx, t);
|
||
|
|
||
|
if(fabs(f->graph->backendNG->keyframesList.keyframes[kfsIdx]->times[idx] - t) > threshold) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
kfsIdxRet = kfsIdx;
|
||
|
kfIdxRet = idx;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
Timeline::Timeline(struct Frame *parent) : wxPanel(parent, wxID_ANY) {
|
||
|
bmpKf = wxBitmap{"keyframe.bmp", wxBITMAP_TYPE_ANY};
|
||
|
bmpKfExtrap = wxBitmap{"keyframe_extrap.bmp", wxBITMAP_TYPE_ANY};
|
||
|
|
||
|
Bind(wxEVT_PAINT, &Timeline::Paint, this);
|
||
|
|
||
|
Bind(wxEVT_MIDDLE_DOWN, [=](wxMouseEvent &ev){
|
||
|
captureMode = Timeline::CaptureMode::CAM;
|
||
|
CaptureMouse();
|
||
|
mouseX = ev.GetX();
|
||
|
});
|
||
|
Bind(wxEVT_MIDDLE_UP, [=](wxMouseEvent &ev){
|
||
|
if(HasCapture() && captureMode == Timeline::CaptureMode::CAM) {
|
||
|
ReleaseMouse();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
Bind(wxEVT_LEFT_DOWN, [=](wxMouseEvent &ev){
|
||
|
auto f = (Frame*) GetParent();
|
||
|
|
||
|
size_t kfsIdx, kfIdx;
|
||
|
if(MouseOverKF(ev.GetPosition(), kfsIdx, kfIdx)) {
|
||
|
captureMode = Timeline::CaptureMode::KF;
|
||
|
CaptureMouse();
|
||
|
mouseX = ev.GetX();
|
||
|
|
||
|
this->captureKfsIdx = kfsIdx;
|
||
|
this->captureKfIdx = kfIdx;
|
||
|
} else {
|
||
|
float t = (ev.GetX() + camX - ZERO_TIME_BASE) / (float) scale;
|
||
|
|
||
|
// Snap to closest keyframe, in all keyframes
|
||
|
if(ev.ControlDown()) {
|
||
|
float minDist = FLT_MAX;
|
||
|
float newT = t;
|
||
|
|
||
|
for(size_t kfsIdx = 0; kfsIdx < f->graph->backendNG->keyframesList.count; kfsIdx++) {
|
||
|
size_t kfIdx = CHi_GetClosestKeyframe(f->graph->backendNG, kfsIdx, t);
|
||
|
|
||
|
float dist = fabs(f->graph->backendNG->keyframesList.keyframes[kfsIdx]->times[kfIdx] - t);
|
||
|
|
||
|
if(dist < minDist) {
|
||
|
minDist = dist;
|
||
|
newT = f->graph->backendNG->keyframesList.keyframes[kfsIdx]->times[kfIdx];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
t = newT;
|
||
|
}
|
||
|
|
||
|
CHi_Time_Set(f->graph->backendNG, t < 0 ? 0 : t);
|
||
|
Refresh();
|
||
|
|
||
|
f->graph->Dirtify(f->graph->gnodes[0]);
|
||
|
}
|
||
|
});
|
||
|
Bind(wxEVT_LEFT_UP, [=](wxMouseEvent &ev){
|
||
|
if(HasCapture() && captureMode == Timeline::CaptureMode::KF) {
|
||
|
ReleaseMouse();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
Bind(wxEVT_MOTION, [=](wxMouseEvent &ev){
|
||
|
auto f = (Frame*) GetParent();
|
||
|
|
||
|
if(HasCapture()) {
|
||
|
if(captureMode == Timeline::CaptureMode::CAM) {
|
||
|
camX += mouseX - ev.GetX();
|
||
|
if(camX < 0) {
|
||
|
camX = 0;
|
||
|
}
|
||
|
|
||
|
Refresh();
|
||
|
} else if(captureMode == Timeline::CaptureMode::KF) {
|
||
|
int64_t diff = ev.GetX() - mouseX;
|
||
|
|
||
|
float timeDiff = (float) diff / this->scale;
|
||
|
|
||
|
captureKfIdx = CHi_MoveKeyframeBy(f->graph->backendNG, f->graph->backendNG->keyframesList.keyframes[captureKfsIdx], captureKfIdx, timeDiff);
|
||
|
|
||
|
Refresh();
|
||
|
|
||
|
f->graph->Dirtify(f->graph->gnodes[0]);
|
||
|
}
|
||
|
|
||
|
mouseX = ev.GetX();
|
||
|
} else {
|
||
|
// This is really baad..
|
||
|
|
||
|
size_t kfsIdx, kfIdx;
|
||
|
if(GetToolTipText() == "" && MouseOverKF(ScreenToClient(wxGetMousePosition()), kfsIdx, kfIdx)) {
|
||
|
CHiKeyframes *kfs = f->graph->backendNG->keyframesList.keyframes[kfsIdx];
|
||
|
|
||
|
CHiPubNode *node = kfs->node;
|
||
|
|
||
|
auto it = std::find_if(f->graph->gnodes.begin(), f->graph->gnodes.end(), [=](GrNode *g){
|
||
|
return g->logical == node;
|
||
|
});
|
||
|
|
||
|
assert(it != f->graph->gnodes.end());
|
||
|
|
||
|
auto &sinks = (*it)->sinks;
|
||
|
|
||
|
for(size_t i = 0; i < node->sinkCount; i++) {
|
||
|
if(node->sinks[i].type == CUTIHI_VAL_KEYED && node->sinks[i].data.keyed == kfs) {
|
||
|
|
||
|
CHiValueRaw *val = &node->sinks[i].data.keyed->values[kfIdx];
|
||
|
|
||
|
switch(sinks[i].type) {
|
||
|
case GrNode::Port::Type::TEXT:
|
||
|
case GrNode::Port::Type::FILE_OPEN:
|
||
|
case GrNode::Port::Type::FILE_SAVE:
|
||
|
SetToolTip(wxString{val->text});
|
||
|
break;
|
||
|
case GrNode::Port::Type::VEC1:
|
||
|
SetToolTip(wxString::Format("%g", val->vec4[0]));
|
||
|
break;
|
||
|
case GrNode::Port::Type::VEC2:
|
||
|
SetToolTip(wxString::Format("(%g, %g)", val->vec4[0], val->vec4[1]));
|
||
|
break;
|
||
|
case GrNode::Port::Type::VEC3:
|
||
|
SetToolTip(wxString::Format("(%g, %g, %g)", val->vec4[0], val->vec4[1], val->vec4[2]));
|
||
|
break;
|
||
|
case GrNode::Port::Type::VEC4:
|
||
|
case GrNode::Port::Type::COLOR:
|
||
|
SetToolTip(wxString::Format("(%g, %g, %g, %g)", val->vec4[0], val->vec4[1], val->vec4[2], val->vec4[3]));
|
||
|
break;
|
||
|
default:
|
||
|
SetToolTip(" ");
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
SetToolTip(nullptr);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
Bind(wxEVT_MOUSEWHEEL, [=](wxMouseEvent &ev){
|
||
|
int delta = ev.GetWheelRotation() / ev.GetWheelDelta();
|
||
|
|
||
|
while(delta > 0) {
|
||
|
scale *= 2;
|
||
|
if(scale > 800) {
|
||
|
scale = 800;
|
||
|
}
|
||
|
delta--;
|
||
|
}
|
||
|
|
||
|
while(delta < 0) {
|
||
|
scale /= 2;
|
||
|
if(scale < 25) {
|
||
|
scale = 25;
|
||
|
}
|
||
|
delta++;
|
||
|
}
|
||
|
|
||
|
Refresh();
|
||
|
});
|
||
|
|
||
|
Bind(wxEVT_CONTEXT_MENU, [=](wxContextMenuEvent &ev){
|
||
|
wxPoint position = ScreenToClient(wxGetMousePosition());
|
||
|
|
||
|
size_t kfsIdx, kfIdx;
|
||
|
if(MouseOverKF(ScreenToClient(wxGetMousePosition()), kfsIdx, kfIdx)) {
|
||
|
wxMenu menu;
|
||
|
|
||
|
int idDel = menu.Append(wxID_ANY, "Delete")->GetId();
|
||
|
|
||
|
menu.Bind(wxEVT_MENU, [=](wxCommandEvent &ev){
|
||
|
if(ev.GetId() == idDel) {
|
||
|
auto f = (Frame*) GetParent();
|
||
|
auto kfs = f->graph->backendNG->keyframesList.keyframes[kfsIdx];
|
||
|
|
||
|
CHi_DeleteKeyframe(f->graph->backendNG, kfs, kfIdx);
|
||
|
|
||
|
Refresh();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
PopupMenu(&menu);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
SetMinSize(wxSize{0, 64});
|
||
|
//SetSize(wxSize{GetSize().x, std::max(GetMinSize().y, GetSize().y)});
|
||
|
Fit();
|
||
|
}
|
||
|
|
||
|
void Timeline::Paint(wxPaintEvent &ev) {
|
||
|
auto frame = (Frame*) GetParent();
|
||
|
|
||
|
wxPaintDC dc{this};
|
||
|
|
||
|
dc.SetPen(wxPen{wxColour{160, 60, 60}});
|
||
|
|
||
|
{
|
||
|
int x = CHi_Time_Get(frame->graph->backendNG) * scale - camX + ZERO_TIME_BASE;
|
||
|
dc.DrawLine(x, 0, x, GetSize().y);
|
||
|
}
|
||
|
|
||
|
dc.SetPen(wxPen{wxSystemSettings::GetColour(wxSYS_COLOUR_INACTIVECAPTIONTEXT)});
|
||
|
|
||
|
float t = std::ceil((float) camX / scale);
|
||
|
for(int64_t x = ZERO_TIME_BASE + mod<int64_t>(-camX, scale); x < GetSize().x; x += scale) {
|
||
|
dc.DrawLine(x, 0, x, 10);
|
||
|
dc.DrawText(wxString::Format("%gs", t), x + 4, 0);
|
||
|
|
||
|
t++;
|
||
|
}
|
||
|
|
||
|
auto kfsList = &frame->graph->backendNG->keyframesList;
|
||
|
|
||
|
for(size_t kfsIdx = 0; kfsIdx < kfsList->count; kfsIdx++) {
|
||
|
|
||
|
CHiKeyframes *kfs = kfsList->keyframes[kfsIdx];
|
||
|
|
||
|
for(size_t kfIdx = 0; kfIdx < kfs->count; kfIdx++) {
|
||
|
wxBitmap &bmp = kfIdx == kfs->count - 1 && kfs->extrapolationMode != CUTIHI_EXTRAPOLATION_NONE ? bmpKfExtrap : bmpKf;
|
||
|
|
||
|
dc.DrawBitmap(bmp, ZERO_TIME_BASE - camX + scale * kfs->times[kfIdx] - bmpKf.GetWidth() / 2, bmpKf.GetHeight() * (kfsIdx + 1));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|