3 #include <wx/bmpbuttn.h>
8 if (inputPath.
empty()) {
9 throw Exception(
"sequence for empty path");
16 return { std::make_pair(0, absolutePath) };
19 Path fileMask = deducedFile->getMask();
20 std::map<int, Path> fileMap;
25 for (
Path& file : files) {
28 if (deducedMask && deducedMask->getMask() == fileMask) {
31 fileMap[index.
value()] = dir / file;
35 if (fileMap.empty()) {
43 : wxPanel(parent, wxID_ANY)
44 , callbacks(callbacks) {
48 this->SetMinSize(wxSize(300, 30));
49 this->Connect(wxEVT_PAINT, wxPaintEventHandler(TimeLinePanel::onPaint));
50 this->Connect(wxEVT_MOTION, wxMouseEventHandler(TimeLinePanel::onMouseMotion));
51 this->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(TimeLinePanel::onLeftClick));
52 this->Connect(wxEVT_KEY_UP, wxKeyEventHandler(TimeLinePanel::onKeyUp));
75 currentFrame = newFrame;
80 auto iter = fileMap.find(currentFrame);
82 if (iter != fileMap.begin()) {
84 currentFrame = iter->first;
94 auto iter = fileMap.find(currentFrame);
97 if (iter != fileMap.end()) {
98 currentFrame = iter->first;
103 int TimeLinePanel::positionToFrame(
const wxPoint
position)
const {
104 if (fileMap.empty()) {
107 wxSize size = this->GetSize();
108 const int firstFrame = fileMap.begin()->first;
109 const int lastFrame = fileMap.rbegin()->first;
110 const int frame = firstFrame + int(roundf(
float(
position.x) * (lastFrame - firstFrame) / size.x));
111 const auto upperIter = fileMap.upper_bound(frame);
112 if (upperIter == fileMap.begin()) {
113 return upperIter->first;
115 auto lowerIter = upperIter;
118 if (upperIter == fileMap.end()) {
119 return lowerIter->first;
122 const int lowerDist = frame - lowerIter->first;
123 const int upperDist = upperIter->first - frame;
124 return (upperDist < lowerDist) ? upperIter->first : lowerIter->first;
129 void TimeLinePanel::reload() {
134 void TimeLinePanel::onPaint(wxPaintEvent&
UNUSED(evt)) {
135 if (fileMap.empty()) {
141 const wxSize size = dc.GetSize();
143 Rgba backgroundColor =
Rgba(this->GetParent()->GetBackgroundColour());
144 wxPen pen = *wxBLACK_PEN;
147 wxColour fillColor(backgroundColor.
darken(0.3f));
148 brush.SetColour(fillColor);
149 pen.SetColour(fillColor);
153 dc.DrawRectangle(wxPoint(0, 0), size);
154 dc.SetTextForeground(wxColour(255, 255, 255));
155 wxFont font = dc.GetFont();
159 const int fileCnt = fileMap.size();
168 step = int(fileCnt / 60) * 5;
169 }
else if (fileCnt > 30) {
173 const int firstFrame = fileMap.begin()->first;
174 const int lastFrame = fileMap.rbegin()->first;
177 const bool isLightTheme = backgroundColor.
intensity() > 0.5f;
179 dc.SetTextForeground(wxColour(30, 30, 30));
182 for (
auto frameAndPath : fileMap) {
183 const int frame = frameAndPath.first;
184 bool keyframe = (i % step == 0);
185 bool doFull = keyframe;
186 if (frame == currentFrame) {
187 pen.SetColour(wxColour(255, 80, 0));
189 }
else if (frame == mouseFrame) {
190 pen.SetColour(wxColour(128, 128, 128));
194 pen.SetColour(wxColour(30, 30, 30));
196 pen.SetColour(wxColour(backgroundColor));
200 const int x = (frame - firstFrame) * size.x / (lastFrame - firstFrame);
202 dc.DrawLine(wxPoint(x, 0), wxPoint(x, size.y));
204 dc.DrawLine(wxPoint(x, 0), wxPoint(x, 5));
205 dc.DrawLine(wxPoint(x, size.y - 5), wxPoint(x, size.y));
209 const std::string text = std::to_string(frame);
210 const wxSize extent = dc.GetTextExtent(text);
211 if (x + extent.x + 3 < size.x) {
212 dc.DrawText(text, wxPoint(x + 3, size.y - 20));
219 void TimeLinePanel::onMouseMotion(wxMouseEvent& evt) {
220 mouseFrame = positionToFrame(evt.GetPosition());
224 void TimeLinePanel::onLeftClick(wxMouseEvent& evt) {
225 currentFrame = positionToFrame(evt.GetPosition());
229 void TimeLinePanel::onKeyUp(wxKeyEvent& evt) {
230 switch (evt.GetKeyCode()) {
243 static wxBitmapButton* createButton(wxWindow* parent,
const wxBitmap& bitmap) {
245 wxBitmapButton* button =
new wxBitmapButton(parent, wxID_ANY, bitmap);
250 static wxBitmapButton* createButton(wxWindow* parent,
char** data) {
251 wxBitmap bitmap(data);
252 return createButton(parent, bitmap);
256 : wxPanel(parent, wxID_ANY) {
257 wxBoxSizer* sizer =
new wxBoxSizer(wxHORIZONTAL);
259 wxImage nextImage = wxBitmap(nextData).ConvertToImage();
260 nextImage = nextImage.Mirror();
261 wxBitmapButton* prevButton = createButton(
this, wxBitmap(nextImage, wxBITMAP_SCREEN_DEPTH));
262 sizer->Add(prevButton, 1, wxALL);
264 wxBitmapButton* pauseButton = createButton(
this, pauseData);
265 sizer->Add(pauseButton, 1, wxALL);
267 wxBitmapButton* stopButton = createButton(
this, stopData);
268 sizer->Add(stopButton, 1, wxALL);
270 wxBitmapButton* playButton = createButton(
this, playData);
271 sizer->Add(playButton, 1, wxALL);
273 wxBitmapButton* nextButton = createButton(
this, nextData);
274 sizer->Add(nextButton, 1, wxALL);
278 sizer->Add(timeline, 40, wxALL | wxEXPAND);
280 this->SetSizer(sizer);
283 prevButton->Bind(wxEVT_BUTTON, [
this](wxCommandEvent&
UNUSED(evt)) { timeline->
setPrevious(); });
284 nextButton->Bind(wxEVT_BUTTON, [
this](wxCommandEvent&
UNUSED(evt)) { timeline->
setNext(); });
285 playButton->Bind(wxEVT_BUTTON, [
this](wxCommandEvent&
UNUSED(evt)) { timeline->
startSequence(); });
286 stopButton->Bind(wxEVT_BUTTON, [callbacks](wxCommandEvent&
UNUSED(evt)) { callbacks->
stop(); });
287 pauseButton->Bind(wxEVT_BUTTON, [callbacks](wxCommandEvent&
UNUSED(evt)) { callbacks->
pause(); });
#define SPH_ASSERT(x,...)
@ NO_THROW
Function cannot throw exceptions.
@ MAIN_THREAD
Function can only be executed from main thread.
#define CHECK_FUNCTION(flags)
uint32_t Size
Integral type used to index arrays (by default).
#define NAMESPACE_SPH_END
const wxSize buttonSize(250, -1)
NAMESPACE_SPH_BEGIN std::map< int, Path > getSequenceFiles(const Path &inputPath)
Generic dynamically allocated resizable storage.
virtual void pause() const =0
virtual void startSequence(const Path &firstFile) const =0
virtual void frameChanged(const Path &newFile) const =0
virtual void stop() const =0
Wrapper of type value of which may or may not be present.
INLINE Type & value()
Returns the reference to the stored value.
INLINE Type valueOr(const TOther &other) const
Returns the stored value if the object has been initialized, otherwise returns provided parameter.
Helper file generating file names for output files.
static Optional< Size > getDumpIdx(const Path &path)
Extracts the dump index from given path generated by OutputFile.
static Optional< OutputFile > getMaskFromPath(const Path &path, const Size firstDumpIdx=0)
Attemps to get the OutputFile from one of the path generated from it.
bool hasWildcard() const
Returns true if the file mask contains (at least one) wildcard.
Object representing a path on a filesystem.
std::string native() const
Returns the native version of the path.
bool empty() const
Checks if the path is empty.
Path parentPath() const
Returns the parent directory. If the path is empty or root, return empty path.
float intensity() const
Returns the average intensity of the color.
Rgba darken(const float amount) const
Returns a color darker by given factor.
void setFrame(const Size newFrame)
TimeLinePanel(wxWindow *parent, const Path &inputFile, SharedPtr< ITimeLineCallbacks > callbacks)
void update(const Path &inputFile)
TimeLine(wxWindow *parent, const Path &inputFile, SharedPtr< ITimeLineCallbacks > callbacks)
Array< Path > getFilesInDirectory(const Path &directory)
Alternatitve to iterateDirectory, returning all files in directory in an array.
Path getAbsolutePath(const Path &relativePath)
Returns the absolute path to the file.
Vector position(const Float a, const Float e, const Float u)
Computes the position on the elliptic trajectory.