Skip to content

Commit c9dc3b0

Browse files
committed
Add follow eyes
1 parent 92efd09 commit c9dc3b0

File tree

9 files changed

+216
-6
lines changed

9 files changed

+216
-6
lines changed

src/context.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <cassert>
22
#include <context.hpp>
3+
#include <maths/maths.hpp>
34
#include <scene.hpp>
45

56
namespace misc {
@@ -35,6 +36,13 @@ std::vector<sf::Event> context_t::poll() {
3536
return ret;
3637
}
3738

39+
sf::Vector2f context_t::mouse_pos() const noexcept {
40+
static auto const offset = sf::Vector2f(f32(width) * 0.5f, f32(height) * 0.5f);
41+
auto ret = maths::cast<sf::Vector2f>(sf::Mouse::getPosition(*this));
42+
ret -= offset;
43+
return {ret.x, -ret.y};
44+
}
45+
3846
drawer_t::drawer_t(context_t* ctx, sf::Color clear_colour) : ctx(ctx) { ctx->clear(clear_colour); }
3947
drawer_t::~drawer_t() { ctx->display(); }
4048
} // namespace misc

src/context.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class context_t : public sf::RenderWindow {
2323

2424
drawer_t drawer(sf::Color clear_colour = sf::Color::Black);
2525

26+
sf::Vector2f mouse_pos() const noexcept;
27+
2628
private:
2729
bool m_closed = false;
2830
};

src/gfx/follow_eye.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include <context.hpp>
2+
#include <gfx/follow_eye.hpp>
3+
#include <maths/maths.hpp>
4+
5+
namespace misc {
6+
namespace {
7+
struct circle_params_t {
8+
sf::Vector2f position{};
9+
sf::Color fill = sf::Color::White;
10+
sf::Color outline = sf::Color::Transparent;
11+
f32 radius = 25.0f;
12+
u32 points = 64;
13+
};
14+
15+
void draw_circle(drawer_t& drawer, circle_params_t const& params) {
16+
sf::CircleShape circle;
17+
circle.setFillColor(params.fill);
18+
circle.setOutlineColor(params.outline);
19+
circle.setPointCount(params.points);
20+
circle.setRadius(params.radius);
21+
circle.setOrigin(params.radius, params.radius);
22+
circle.setPosition(params.position.x, -params.position.y);
23+
drawer.draw(circle);
24+
}
25+
} // namespace
26+
27+
sf::Vector2f follow_eye_t::update(sf::Vector2f target) noexcept { return (dir = maths::norm(target - pos)); }
28+
void follow_eye_t::draw(drawer_t& drawer, bool blink) {
29+
circle_params_t params;
30+
params.radius = 40.0f * scale;
31+
params.outline = sf::Color(0x221100ff);
32+
params.position = pos;
33+
draw_circle(drawer, params);
34+
if (!blink) {
35+
auto const radius = params.radius * 0.25f;
36+
auto const offset = dir * (params.radius - radius) * 0.80f; // 20% padding
37+
params.radius *= 0.25f;
38+
params.outline = sf::Color::Transparent;
39+
params.fill = sf::Color::Black;
40+
params.position += offset;
41+
draw_circle(drawer, params);
42+
}
43+
}
44+
} // namespace misc

src/gfx/follow_eye.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
#include <SFML/System/Vector2.hpp>
3+
#include <types.hpp>
4+
5+
namespace misc {
6+
struct drawer_t;
7+
8+
struct follow_eye_t {
9+
sf::Vector2f dir = {0.0f, 1.0f};
10+
sf::Vector2f pos = {0.0f, 0.0f};
11+
f32 scale = 1.0f;
12+
13+
sf::Vector2f update(sf::Vector2f target) noexcept;
14+
void draw(drawer_t& drawer, bool blink);
15+
};
16+
} // namespace misc

src/main.cpp

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <set>
44
#include <context.hpp>
55
#include <delta_time.hpp>
6+
#include <gfx/follow_eye.hpp>
7+
#include <maths/maths.hpp>
68
#include <world_clock/gui.hpp>
79
#include <world_clock/io.hpp>
810

@@ -58,21 +60,47 @@ class clock_ticker_t {
5860
if (m_input->any_held(key_t::Up, key_t::Right, key_t::W, key_t::D)) { delta += 1.0f; }
5961
add(translation_scale * delta * dt.count());
6062
}
63+
auto next_blink = [this]() { return maths::random_range(m_blink.interval.first.count(), m_blink.interval.second.count()); };
64+
if (m_blink.next_blink_in <= sec_t{}) {
65+
m_blink.next_blink_in = sec_t{next_blink()};
66+
m_blink.stop_blink_in = m_blink.duration;
67+
update(flag::blinking);
68+
}
69+
if (test(flag::blinking)) {
70+
m_blink.stop_blink_in -= dt;
71+
if (m_blink.stop_blink_in <= sec_t{}) {
72+
m_blink.next_blink_in = sec_t{next_blink()};
73+
update(flag::none, flag::blinking);
74+
}
75+
} else {
76+
m_blink.next_blink_in -= dt;
77+
}
6178
}
6279

80+
bool blink() const noexcept { return test(flag::blinking); }
81+
82+
struct {
83+
pair_t<sec_t> interval = {sec_t{3.0f}, sec_t{8.0f}};
84+
sec_t duration = sec_t{0.15f};
85+
86+
sec_t next_blink_in{};
87+
sec_t stop_blink_in{};
88+
} m_blink;
89+
6390
private:
6491
using flags_t = u8;
65-
enum flag : flags_t { none = 0, zeroing = 1 << 0 };
92+
enum flag : flags_t { none = 0, zeroing = 1 << 0, blinking = 1 << 1 };
6693

6794
input_t const* m_input;
6895
world_clock_t* m_clock;
6996
world_hour_t m_offset;
70-
flags_t flags = flag::none;
7197

72-
bool test(flags_t f) const noexcept { return (flags & f) == f; }
98+
flags_t m_flags = flag::none;
99+
100+
bool test(flags_t f) const noexcept { return (m_flags & f) == f; }
73101
void update(flags_t set, flags_t unset = 0) noexcept {
74-
flags |= set;
75-
flags &= ~unset;
102+
m_flags |= set;
103+
m_flags &= ~unset;
76104
}
77105

78106
void add(hour_t offset) noexcept {
@@ -96,7 +124,12 @@ int main() {
96124
while (ctx.running()) {
97125
input.update(ctx.poll());
98126
tick(++dt);
99-
if (auto drawer = ctx.drawer()) { world_clock_drawer_t{}(drawer, clock, {}); }
127+
if (auto drawer = ctx.drawer()) {
128+
world_clock_drawer_t::in_t in;
129+
in.blink = tick.blink();
130+
in.mouse_pos = ctx.mouse_pos();
131+
world_clock_drawer_t{}(drawer, clock, in);
132+
}
100133
}
101134
std::cout << "\nCompleted successfully\n";
102135
}

src/maths/maths.hpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#pragma once
2+
#include <cmath>
3+
#include <random>
4+
#include <type_traits>
5+
#include <SFML/System/Vector2.hpp>
6+
#include <types.hpp>
7+
8+
namespace misc::maths {
9+
using rad_t = f32;
10+
11+
template <bool V, typename...>
12+
constexpr bool value_v = V;
13+
14+
constexpr f32 pi = 3.1415;
15+
sf::Vector2f const n_up = {0.0f, 1.0f};
16+
sf::Vector2f const n_down = {0.0f, -1.0f};
17+
sf::Vector2f const n_left = {-1.0f, 0.0f};
18+
sf::Vector2f const n_right = {1.0f, 0.0f};
19+
20+
constexpr rad_t rad(f32 degrees) noexcept;
21+
constexpr f32 deg(rad_t rad) noexcept;
22+
23+
f32 sqr_mag(sf::Vector2f vec) noexcept;
24+
f32 mag(sf::Vector2f vec) noexcept;
25+
f32 mag_or(sf::Vector2f vec, f32 fallback) noexcept;
26+
sf::Vector2f norm(sf::Vector2f vec) noexcept;
27+
sf::Vector2f safe_norm(sf::Vector2f vec) noexcept;
28+
29+
sf::Vector2f dir(rad_t rad) noexcept;
30+
rad_t rad(sf::Vector2f n_dir) noexcept;
31+
32+
template <typename Ret, typename In>
33+
Ret cast(sf::Vector2<In> in) noexcept;
34+
35+
template <typename T>
36+
T random_range(T lo, T hi) noexcept;
37+
38+
// impl
39+
40+
constexpr rad_t rad(f32 degrees) noexcept { return degrees * pi / 180.0f; }
41+
constexpr f32 deg(rad_t radians) noexcept { return 180.0f * radians / pi; }
42+
inline f32 sqr_mag(sf::Vector2f vec) noexcept { return vec.x * vec.x + vec.y * vec.y; }
43+
inline f32 mag(sf::Vector2f vec) noexcept { return std::sqrt(sqr_mag(vec)); }
44+
45+
inline f32 mag_or(sf::Vector2f vec, f32 fallback) noexcept {
46+
f32 const ret = std::sqrt(sqr_mag(vec));
47+
return ret == 0.0f ? fallback : ret;
48+
}
49+
50+
inline sf::Vector2f norm(sf::Vector2f vec) noexcept {
51+
auto const m = mag(vec);
52+
return {vec.x / m, vec.y / m};
53+
}
54+
55+
inline sf::Vector2f safe_norm(sf::Vector2f vec) noexcept {
56+
auto const m = mag_or(vec, 1.0f);
57+
return {vec.x / m, vec.y / m};
58+
}
59+
60+
inline sf::Vector2f dir(rad_t rad) noexcept { return {std::sin(rad), std::cos(rad)}; }
61+
inline rad_t rad(sf::Vector2f n_dir) noexcept {
62+
if (n_dir.y == 0.0f) { return rad(n_dir.x == 1.0f ? -90.0f : 90.0f); }
63+
auto const atan = std::atan(n_dir.x / n_dir.y);
64+
return n_dir.y > 0.0f ? atan : atan + rad(180.0f);
65+
}
66+
67+
template <typename Ret, typename In>
68+
Ret cast(sf::Vector2<In> in) noexcept {
69+
using type = decltype(std::declval<Ret>().x);
70+
return Ret{static_cast<type>(in.x), static_cast<type>(in.y)};
71+
}
72+
73+
template <typename T>
74+
T random_range(T lo, T hi) noexcept {
75+
thread_local std::random_device rd;
76+
thread_local std::default_random_engine eng{rd()};
77+
if constexpr (std::is_integral_v<T>) {
78+
std::uniform_int_distribution<T> dist(lo, hi);
79+
return dist(eng);
80+
} else if constexpr (std::is_floating_point_v<T>) {
81+
std::uniform_real_distribution<T> dist(lo, hi);
82+
return dist(eng);
83+
} else {
84+
static_assert(value_v<false, T>, "Invalid type");
85+
}
86+
}
87+
} // namespace misc::maths

src/types.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
#pragma once
22
#include <cstdint>
3+
#include <utility>
34

45
namespace misc {
56
using s32 = std::uint32_t;
67
using u8 = std::uint8_t;
78
using u32 = std::uint32_t;
89
using f32 = float;
10+
11+
template <typename T, typename U = T>
12+
using pair_t = std::pair<T, U>;
913
} // namespace misc

src/world_clock/gui.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <cmath>
33
#include <SFML/Graphics.hpp>
44
#include <context.hpp>
5+
#include <gfx/follow_eye.hpp>
56
#include <world_clock/gui.hpp>
67

78
namespace misc {
@@ -78,6 +79,18 @@ struct facade_t {
7879
drawer.draw(t);
7980
}
8081

82+
void draw_eyes(sf::Vector2f target, bool blink) const {
83+
follow_eye_t left;
84+
left.pos = {-80.0f, 50.0f};
85+
left.pos *= in.scale;
86+
left.update(target);
87+
left.draw(drawer, blink);
88+
follow_eye_t right;
89+
right.pos = {-left.pos.x, left.pos.y};
90+
right.update(target);
91+
right.draw(drawer, blink);
92+
}
93+
8194
void draw_clock() const {
8295
if (in.face_tex) {
8396
draw<sf::Sprite>();
@@ -105,6 +118,7 @@ struct facade_t {
105118
void world_clock_drawer_t::operator()(drawer_t& drawer, world_clock_t const& clock, in_t const& in) {
106119
facade_t f{drawer, in};
107120
f.draw_clock();
121+
f.draw_eyes(in.mouse_pos, in.blink);
108122
for (auto const& entry : clock) { f.draw_hand(entry); }
109123
}
110124
} // namespace misc

src/world_clock/gui.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct world_clock_drawer_t {
1414
struct in_t {
1515
sf::Vector2f centre{};
1616
sf::Vector2f hand_pivot = {0.5f, 0.9f};
17+
sf::Vector2f mouse_pos{};
1718
sf::Color face_tint = sf::Color(0xccccbbff);
1819
sf::Color marker_tint = sf::Color(0x555555ff);
1920
sf::Texture const* face_tex{};
@@ -22,6 +23,7 @@ struct world_clock_drawer_t {
2223
f32 marker_radius = 10.0f;
2324
f32 face_pad = 0.2f;
2425
f32 scale = 1.0f;
26+
bool blink = false;
2527
};
2628

2729
void operator()(drawer_t& drawer, world_clock_t const& clock, in_t const& in);

0 commit comments

Comments
 (0)