/*********************************************************************** * 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 #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; using finalcut::FColor; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 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 = default; // 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}; std::wstring data{std::wstring(256, L'\0')}; 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"); std::size_t h{0}; for (std::size_t j{0}; j < 8; j++) { for (std::size_t i{0}; i < 8; i++) { data[h] = L' '; h++; } for (std::size_t i{0}; i < 8; i++) { data[h] = L'+'; h++; } } for (std::size_t j{0}; j < 8; j++) { for (std::size_t i{0}; i < 8; i++) { data[h] = L'x'; h++; } for (std::size_t i{0}; i < 8; i++) { data[h] = L' '; h++; } } } //---------------------------------------------------------------------- void RotoZoomer::draw() { if ( benchmark && start == time_point() ) start = system_clock::now(); finalcut::FDialog::draw(); auto cx = double(80.0 / 2.0 + (80.0 / 2.0 * std::sin(double(path) / 50.0))); auto cy = double(23.0 + (23.0 * std::cos(double(path) / 50.0))); auto r = double(128.0 + 96.0 * std::cos(double(path) / 10.0)); auto a = double(path) / 50.0; rotozoomer (cx, cy, r, a); } //---------------------------------------------------------------------- void RotoZoomer::rotozoomer (double cx, double cy, double r, double a) { const auto Cols = int(getClientWidth()); const auto Lines = int(getClientHeight()); auto Ax = int(4096.0 * (cx + r * std::cos(a))); auto Ay = int(4096.0 * (cy + r * std::sin(a))); auto Bx = int(4096.0 * (cx + r * std::cos(a + 2.02358))); auto By = int(4096.0 * (cy + r * std::sin(a + 2.02358))); auto Cx = int(4096.0 * (cx + r * std::cos(a - 1.11701))); auto 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 (auto y = 0; y < Lines; y++) { Cx = Ax; Cy = Ay; print() << FPoint{2, 3 + y}; for (auto x = 0; x < Cols; x++) { auto ch = data[((Cy >> 14) & 0xf) + ((Cx >> 10) & 0xf0)]; if ( ch == '+' ) print() << finalcut::FColorPair{FColor::Black, FColor::Red}; else if ( ch == 'x' ) print() << finalcut::FColorPair{FColor::Black, FColor::Cyan}; else print() << finalcut::FColorPair{FColor::Black, FColor::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 << "s"; 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(); forceTerminalUpdate(); } end = system_clock::now(); generateReport(); flush(); close(); } } //---------------------------------------------------------------------- void RotoZoomer::onTimer (finalcut::FTimerEvent*) { if ( path >= 314 ) // More than 360 degrees path = 0; else path++; redraw(); forceTerminalUpdate(); } //---------------------------------------------------------------------- void RotoZoomer::onKeyPress (finalcut::FKeyEvent* ev) { if ( ! ev ) return; if ( ev->key() == finalcut::FKey('q') ) { close(); ev->accept(); } else finalcut::FDialog::onKeyPress(ev); } //---------------------------------------------------------------------- void RotoZoomer::onClose (finalcut::FCloseEvent* ev) { if ( benchmark ) ev->accept(); else 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; // Disable terminal data requests auto& start_options = finalcut::FStartOptions::getFStartOptions(); start_options.terminal_data_request = false; } { // 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}); roto.setShadow(); // Instead of the transparent window shadow // 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; }