You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
9.1 KiB
C++
249 lines
9.1 KiB
C++
#include <string>
|
|
#include <iostream>
|
|
|
|
#include <SFML/System.hpp>
|
|
#include <SFML/Window.hpp>
|
|
#include <SFML/Graphics.hpp>
|
|
|
|
#include "ConcentricClock.h"
|
|
#include "MovingSectors.h"
|
|
|
|
namespace CClock {
|
|
|
|
const std::string title = "Concentric Clock";
|
|
const std::string version = "v0.2";
|
|
const sf::Time updateTimestep = sf::seconds(0.0005);
|
|
const sf::Color bgColor = sf::Color::Black;
|
|
const int antialisingLvl = 4; //set antialising for shapes. doesnt affect textures (-> sf::Texture::setSmooth())
|
|
sf::Vector2f resolution{1280, 800};
|
|
bool fullScreen = false;
|
|
|
|
const double clockRadius = 150;
|
|
const double clockRingsize = 30;
|
|
const double clockSpacing = 5;
|
|
const double clockThickness = 6 * clockRingsize + 6 * clockSpacing;
|
|
sf::Vector2f zoomSize{90, (float)clockThickness + 45};
|
|
|
|
// because we want to create the objects in init(), we use the default constructor, so nothing can go wrong here
|
|
sf::RenderWindow window{};
|
|
sf::Font font{};
|
|
sf::View stdView{};
|
|
sf::View zoomedView{};
|
|
|
|
sf::Text titleText{};
|
|
sf::Text infoText{};
|
|
|
|
ConcentricClock clock{};
|
|
|
|
MovingSectors mSectors{};
|
|
|
|
sf::RectangleShape indicator{};
|
|
sf::RectangleShape zoomRect{};
|
|
sf::RectangleShape darkenRect{};
|
|
sf::CircleShape middleFixCirc{};
|
|
|
|
void resizeViews(sf::Vector2f size) {
|
|
//change view size (eg on resize event), to display everything porportionally
|
|
stdView.setSize(size);
|
|
stdView.setCenter(0, 0);
|
|
stdView.setViewport(sf::FloatRect(0, 0, 1, 1));
|
|
|
|
zoomedView.setSize(zoomSize);
|
|
zoomedView.setCenter(0, clockRadius + clockThickness / 2);
|
|
zoomedView.zoom(clockThickness / zoomSize.y + 0.04);
|
|
zoomedView.setViewport(sf::FloatRect(0.5 - (zoomSize.x / size.x) / 2,
|
|
0.5 + (clockRadius - ((zoomSize.y - clockThickness) / 2)) / size.y,
|
|
zoomSize.x / size.x,
|
|
zoomSize.y / size.y));
|
|
//reset darkenRect
|
|
darkenRect.setSize(size);
|
|
darkenRect.setPosition(-size / 2.f);
|
|
}
|
|
|
|
bool toggleFullscreen() {
|
|
sf::ContextSettings settings;
|
|
settings.antialiasingLevel = antialisingLvl;
|
|
|
|
if (!fullScreen) {
|
|
//go fullscreen
|
|
sf::VideoMode fsMode = sf::VideoMode::getFullscreenModes()[0];
|
|
window.create(fsMode, title + " " + version + " [FULLSCREEN]", sf::Style::Fullscreen, settings);
|
|
|
|
if(!window.isOpen()) {
|
|
std::cout << "Unable to enter fullscreen mode. Aborting execution.";
|
|
return false;
|
|
}
|
|
|
|
fullScreen = true;
|
|
resizeViews(sf::Vector2f(fsMode.width, fsMode.height));
|
|
} else {
|
|
//go windowed
|
|
window.create(sf::VideoMode(resolution.x, resolution.y, 32), title + " " + version, sf::Style::None, settings);
|
|
fullScreen = false;
|
|
resizeViews(resolution);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool init() {
|
|
|
|
std::string RES_PATH = "./res/";
|
|
#ifdef _WIN32
|
|
RES_PATH = "../res/";
|
|
#endif
|
|
|
|
//load font
|
|
if (!font.loadFromFile(RES_PATH + "Audiowide-Regular.ttf")) {
|
|
std::cout << "Unable to load font. Aborting execution.";
|
|
return false;
|
|
}
|
|
|
|
//window
|
|
sf::ContextSettings settings;
|
|
settings.antialiasingLevel = antialisingLvl;
|
|
window.create(sf::VideoMode(resolution.x, resolution.y, 32), title + " " + version, sf::Style::None, settings);
|
|
if(!window.isOpen()) {
|
|
std::cout << "Unable to open window object. Aborting execution.";
|
|
return false;
|
|
}
|
|
|
|
//views
|
|
resizeViews(resolution);
|
|
|
|
//texts
|
|
titleText = sf::Text("Concentric Clock", font, 27);
|
|
titleText.setColor(sf::Color::White);
|
|
titleText.setOrigin(titleText.getLocalBounds().width / 2, titleText.getLocalBounds().height / 2);
|
|
titleText.setPosition(0, -22);
|
|
|
|
infoText = sf::Text("github.com/noerw", font, 12);
|
|
infoText.setColor(sf::Color(200,200,200));
|
|
infoText.setOrigin(infoText.getLocalBounds().width / 2, infoText.getLocalBounds().height / 2);
|
|
infoText.setPosition(0, 7);
|
|
|
|
//moving Sectors
|
|
double radii[2] = {clockRadius - 5, clockRadius + clockThickness + 11};
|
|
double rotSpeeds[2] = {40, -20};
|
|
mSectors = MovingSectors{sizeof(radii) / sizeof(*radii), radii, rotSpeeds};
|
|
|
|
//half transparent rectShape to darken the clock where its not in the zoomedView
|
|
darkenRect.setSize(resolution);
|
|
darkenRect.setPosition(-resolution / 2.f);
|
|
darkenRect.setFillColor(bgColor * sf::Color(0,0,0,100));
|
|
|
|
//zoomed indicator
|
|
zoomRect.setSize(zoomSize);
|
|
zoomRect.setOrigin(zoomRect.getLocalBounds().width / 2, zoomRect.getLocalBounds().height / 2);
|
|
zoomRect.setPosition(0, clockRadius + clockThickness / 2);
|
|
zoomRect.setOutlineThickness(4);
|
|
zoomRect.setFillColor(bgColor);
|
|
zoomRect.setOutlineColor(sf::Color::Red * sf::Color(180,180,180,200));
|
|
|
|
indicator.setSize(sf::Vector2f(2, zoomSize.y + 25));
|
|
indicator.setOrigin(indicator.getLocalBounds().width / 2, indicator.getLocalBounds().height / 2);
|
|
indicator.setPosition(0, clockRadius + clockThickness / 2);
|
|
indicator.setFillColor(sf::Color::Red * sf::Color(200,200,200,230));
|
|
|
|
//circleshape to hide artifacts in the middle of the clock
|
|
middleFixCirc = sf::CircleShape(clockRadius-1);
|
|
middleFixCirc.setOrigin(middleFixCirc.getLocalBounds().width / 2, middleFixCirc.getLocalBounds().height / 2);
|
|
middleFixCirc.setPosition(0,0);
|
|
middleFixCirc.setFillColor(bgColor);
|
|
|
|
//the actual clock
|
|
clock = ConcentricClock(clockRadius, clockRingsize, clockSpacing, &font);
|
|
|
|
std::cout << "Initialisation complete!\n";
|
|
return true;
|
|
}
|
|
|
|
void update(double timeStep) {
|
|
// get & handle events
|
|
sf::Event event;
|
|
while (window.pollEvent(event)) {
|
|
//window
|
|
switch (event.type) {
|
|
case sf::Event::Closed:
|
|
window.close();
|
|
break;
|
|
|
|
case sf::Event::Resized:
|
|
//resize the views, so everything is still displayed proportionally
|
|
resizeViews(sf::Vector2f(event.size.width, event.size.height));
|
|
break;
|
|
|
|
case sf::Event::KeyPressed:
|
|
if (event.key.code == sf::Keyboard::Escape) window.close();
|
|
else if (event.key.code == sf::Keyboard::Q) window.close();
|
|
else if (event.key.code == sf::Keyboard::F) toggleFullscreen();
|
|
else if (event.key.code == sf::Keyboard::Return && event.key.alt) toggleFullscreen();
|
|
break;
|
|
|
|
case sf::Event::MouseButtonPressed:
|
|
toggleFullscreen();
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
//update the content
|
|
clock.update(timeStep);
|
|
mSectors.update(timeStep, window); //window needed for mouseposition
|
|
}
|
|
|
|
void draw() {
|
|
window.clear(bgColor);
|
|
|
|
window.setView(stdView);
|
|
window.draw(clock);
|
|
window.draw(middleFixCirc); //to hide strange artifacts in the middle of the clock
|
|
window.draw(mSectors);
|
|
window.draw(darkenRect);
|
|
window.draw(titleText);
|
|
window.draw(infoText);
|
|
window.draw(zoomRect);
|
|
|
|
window.setView(zoomedView);
|
|
window.draw(clock);
|
|
|
|
window.setView(stdView);
|
|
window.draw(indicator);
|
|
|
|
window.display();
|
|
}
|
|
|
|
void mainloop() {
|
|
sf::Clock clock;
|
|
sf::Time previousTime = sf::milliseconds(0); //time from previous loop
|
|
sf::Time lagTime = sf::milliseconds(0); //time how many updates we're behind
|
|
|
|
while(window.isOpen()) {
|
|
//calc elapsed time since last loop
|
|
sf::Time currentTime = clock.getElapsedTime();
|
|
sf::Time elapsedTime = currentTime - previousTime;
|
|
previousTime = currentTime;
|
|
lagTime += elapsedTime; //add elapsed time on time we have to update
|
|
|
|
//update as many times as we are behind
|
|
while (lagTime >= updateTimestep) {
|
|
update(updateTimestep.asSeconds());
|
|
lagTime -= updateTimestep;
|
|
}
|
|
//draw anytime we can
|
|
draw();
|
|
}
|
|
}
|
|
|
|
int run() {
|
|
if(!init()) return 1;
|
|
mainloop();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
int main() {
|
|
return CClock::run();
|
|
} |