/***********************************************************************
* rotozoomer.cpp - Rotozoomer effect demo *
* *
* This file is part of the FINAL CUT widget toolkit *
* *
* Copyright 2020 Markus Gans *
* *
* FINAL CUT is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation; either version 3 of *
* the License, or (at your option) any later version. *
* *
* FINAL CUT is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this program. If not, see *
* . *
***********************************************************************/
#include
#include
#include
#include
namespace fc = finalcut::fc;
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::system_clock;
using std::chrono::time_point;
using finalcut::FPoint;
using finalcut::FSize;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This rotozoomer demo is based on the code of Robert Dörfler
// http://bloerp.de/code/tempel/roto.c
// http://bloerp.de/code/tempel/tempel-des-codes.html
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//----------------------------------------------------------------------
// class RotoZoomer
//----------------------------------------------------------------------
class RotoZoomer final : public finalcut::FDialog
{
public:
// Constructor
explicit RotoZoomer (finalcut::FWidget* = nullptr, bool = false, int = 314);
// Destructor
~RotoZoomer() override;
// Accessors
finalcut::FString getReport() const;
// Event handlers
void onShow (finalcut::FShowEvent*) override;
void onTimer (finalcut::FTimerEvent*) override;
void onKeyPress (finalcut::FKeyEvent*) override;
void onClose (finalcut::FCloseEvent*) override;
private:
// Methods
void draw() override;
void rotozoomer (double, double, double, double);
void generateReport();
void adjustSize() override;
// Data member
bool benchmark{false};
int loops{0};
int path{0};
wchar_t data[256]{};
finalcut::FString report{};
time_point start{};
time_point end{};
};
//----------------------------------------------------------------------
RotoZoomer::RotoZoomer (finalcut::FWidget* parent, bool b, int l)
: finalcut::FDialog{parent}
, benchmark{b}
, loops{l}
{
FDialog::setText ("Rotozoomer effect");
int h{0};
for (int j{0}; j < 8; j++)
{
for (int i{0}; i < 8; i++)
{
data[h++] = L' ';
}
for (int i{0}; i < 8; i++)
{
data[h++] = L'+';
}
}
for (int j{0}; j < 8; j++)
{
for (int i{0}; i < 8; i++)
{
data[h++] = L'x';
}
for (int i{0}; i < 8; i++)
{
data[h++] = L' ';
}
}
}
//----------------------------------------------------------------------
RotoZoomer::~RotoZoomer()
{ }
//----------------------------------------------------------------------
void RotoZoomer::draw()
{
if ( benchmark && start == time_point() )
start = system_clock::now();
finalcut::FDialog::draw();
double cx = double(80.0 / 2.0 + (80.0 / 2.0 * std::sin(double(path) / 50.0)));
double cy = double(23.0 + (23.0 * std::cos(double(path) / 50.0)));
double r = double(128.0 + 96.0 * std::cos(double(path) / 10.0));
double a = double(path) / 50.0;
rotozoomer (cx, cy, r, a);
}
//----------------------------------------------------------------------
void RotoZoomer::rotozoomer (double cx, double cy, double r, double a)
{
const int Cols = int(getClientWidth());
const int Lines = int(getClientHeight());
int Ax = int(4096.0 * (cx + r * std::cos(a)));
int Ay = int(4096.0 * (cy + r * std::sin(a)));
int Bx = int(4096.0 * (cx + r * std::cos(a + 2.02358)));
int By = int(4096.0 * (cy + r * std::sin(a + 2.02358)));
int Cx = int(4096.0 * (cx + r * std::cos(a - 1.11701)));
int Cy = int(4096.0 * (cy + r * std::sin(a - 1.11701)));
int dxdx = (Bx - Ax) / 80;
int dydx = (By - Ay) / 80;
int dxdy = (Cx - Ax) / 23;
int dydy = (Cy - Ay) / 23;
for (int y = 0; y < Lines; y++)
{
Cx = Ax;
Cy = Ay;
print() << FPoint{2, 3 + y};
for (int x = 0; x < Cols; x++)
{
wchar_t ch = data[((Cy >> 14) & 0xf) + ((Cx >> 10) & 0xf0)];
if ( ch == '+' )
print() << finalcut::FColorPair{fc::Black, fc::Red};
else if ( ch == 'x' )
print() << finalcut::FColorPair{fc::Black, fc::Cyan};
else
print() << finalcut::FColorPair{fc::Black, fc::White};
print() << ch;
Cx += dxdx;
Cy += dydx;
}
Ax += dxdy;
Ay += dydy;
}
}
//----------------------------------------------------------------------
void RotoZoomer::generateReport()
{
finalcut::FString term_type = finalcut::FTerm::getTermType();
finalcut::FString dimension_str{};
finalcut::FString time_str{};
finalcut::FString fps_str{};
finalcut::FStringStream rep;
dimension_str << getDesktopWidth()
<< "x" << getDesktopHeight();
int elapsed_ms = int(duration_cast(end - start).count());
time_str << double(elapsed_ms) / 1000 << "ms";
fps_str << double(loops) * 1000.0 / double(elapsed_ms);
rep << finalcut::FString{55, '-'} << "\n"
<< "Terminal Size Time Loops Frame rate\n"
<< finalcut::FString{55, '-'} << "\n"
<< std::left << std::setw(20) << term_type
<< std::setw(8) << dimension_str
<< std::setw(10) << time_str
<< std::setw(7) << loops
<< std::setw(7) << fps_str.left(7) << "fps\n";
report << rep.str();
}
//----------------------------------------------------------------------
inline finalcut::FString RotoZoomer::getReport() const
{
return report;
}
//----------------------------------------------------------------------
void RotoZoomer::onShow (finalcut::FShowEvent*)
{
if ( ! benchmark )
addTimer(33); // Starts the timer every 33 milliseconds
else
{
for (path = 1; path < loops; path++)
{
redraw();
updateTerminal();
}
end = system_clock::now();
generateReport();
flush();
quit();
}
}
//----------------------------------------------------------------------
void RotoZoomer::onTimer (finalcut::FTimerEvent*)
{
if ( path >= 314 ) // More than 360 degrees
path = 0;
else
path++;
redraw();
updateTerminal();
flush();
}
//----------------------------------------------------------------------
void RotoZoomer::onKeyPress (finalcut::FKeyEvent* ev)
{
if ( ! ev )
return;
if ( ev->key() == 'q' )
{
close();
ev->accept();
}
else
finalcut::FDialog::onKeyPress(ev);
}
//----------------------------------------------------------------------
void RotoZoomer::onClose (finalcut::FCloseEvent* ev)
{
if ( ! benchmark )
finalcut::FApplication::closeConfirmationDialog (this, ev);
}
//----------------------------------------------------------------------
void RotoZoomer::adjustSize()
{
if ( ! benchmark )
{
std::size_t h = getDesktopHeight();
std::size_t w = getDesktopWidth();
if ( h > 1 )
h--;
if ( w > 8 )
w -= 8;
setGeometry(FPoint{5, 1}, FSize{w, h}, false);
}
finalcut::FDialog::adjustSize();
}
//----------------------------------------------------------------------
// main part
//----------------------------------------------------------------------
int main (int argc, char* argv[])
{
bool benchmark{false};
finalcut::FString report{};
int quit_code{0};
if ( argv[1] && ( strcmp(argv[1], "--help") == 0
|| strcmp(argv[1], "-h") == 0 ) )
{
std::cout << "RotoZoomer options:\n"
<< " -b, --benchmark "
<< "Starting a benchmark run\n\n";
}
else if ( argv[1] && ( strcmp(argv[1], "--benchmark") == 0
|| strcmp(argv[1], "-b") == 0 ) )
{
benchmark = true;
}
{ // Create the application object in this scope
finalcut::FApplication app{argc, argv};
app.setNonBlockingRead();
// Create a simple dialog box
constexpr int iterations = 314;
RotoZoomer roto{&app, benchmark, iterations};
if ( benchmark )
roto.setGeometry (FPoint{1, 1}, FSize{80, 24});
else
roto.setGeometry (FPoint{5, 1}, FSize{72, 23});
roto.setShadow();
// Set the RotoZoomer object as main widget
finalcut::FWidget::setMainWidget(&roto);
// Show and start the application
roto.show();
quit_code = app.exec();
if ( benchmark )
report = roto.getReport();
} // Hide and destroy the application object
if ( benchmark )
{
std::cout << "Benchmark:\n" << report;
}
return quit_code;
}