[imgui] 8. WallClock 구현

목표

  • 아날로그 시계

WallClock
WallClock

구현코드

github 링크

메인함수

class WindowClass
{
private:
    static constexpr auto PI = std::numbers::pi_v<float>;
    static constexpr auto circleRadius = 250.0f;
    static constexpr auto offset = PI / 2.0f;
    static constexpr auto innerRadius = 5.0f;

    // 시계의 시,분,초침 길이
    static constexpr auto hrsClockHandLength = 0.75F;
    static constexpr auto minsClockHandLength = 0.85F;
    static constexpr auto secsClockHandLength = 0.95F;

    // 시계의 한칸을 나타내는 선 길이
    static constexpr auto hrsStrokesLength = 0.90f;
    static constexpr auto minsStrokesLength = 0.95f;

public:
    WindowClass() : secs(0), mins(0), hrs(0), center({}){};
    void Draw(std::string_view label);

private:
    void DrawCircle(const float radius);      // 시계 테두리 그리는함수
    void DrawClockHand(const float radius,    // 바늘 그리는 함수
                       const float theta,
                       const ImColor color);
    void DrawAllHourStrokes();                // 시단위 한칸 표시
    void DrawAllMinuteStrokes();              // 분단위 한칸 표시
    void DrawDigitalClock();                  // 디버깅용 디지털시계

    void GetTime();                           // 현재시간 멤버에 업데이트
    std::tuple<float, float, float> GetTheta(); // 각도얻어오기

public:
    std::int32_t secs;
    std::int32_t mins;
    std::int32_t hrs;

private:
    ImVec2 center;
};
void WindowClass::Draw(std::string_view label)
{
    constexpr static auto window_flags =
        ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
        ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar;
    constexpr static auto window_size = ImVec2(1280.0F, 720.0F);
    constexpr static auto window_pos = ImVec2(0.0F, 0.0F);

    ImGui::SetNextWindowSize(window_size);
    ImGui::SetNextWindowPos(window_pos);

    ImGui::Begin(label.data(), nullptr, window_flags);

    // 시계의 center 위치 업데이트
    const auto cursor_pos = ImGui::GetCursorScreenPos();
    center = ImVec2(cursor_pos.x + circleRadius, cursor_pos.y + circleRadius);

    DrawCircle(circleRadius);

    GetTime();
    const auto [hour_theta, minute_theta, second_theta] = GetTheta();

    DrawClockHand(circleRadius * hrsClockHandLength,
                  hour_theta,
                  ImColor(1.0F, 0.5F, 0.5F, 1.0F));
    DrawClockHand(circleRadius * minsClockHandLength,
                  minute_theta,
                  ImColor(0.5F, 1.0F, 0.5F, 1.0F));
    DrawClockHand(circleRadius * secsClockHandLength,
                  second_theta,
                  ImColor(0.5F, 0.5F, 1.0F, 1.0F));

    DrawAllHourStrokes();
    DrawAllMinuteStrokes();

    DrawCircle(innerRadius);
    
    DrawDigitalClock();

    ImGui::End();
}

DrawCircle

void WindowClass::DrawCircle(const float radius)
{
    // DrawList에 원 그리기 추가.
    // 3번째 인자: 색상 지정. 현재 ImGui의 텍스트 컬러와 동일한색 지정
    // 4번째 인자: 선분 몇개로 원을 그릴건지 지정 0은 무한대, 1~3까지는 삼각형 고정
    ImGui::GetWindowDrawList()->AddCircle(center,
                                          radius,
                                          ImGui::GetColorU32(ImGuiCol_Text),
                                          100,
                                          2.0f);
}

GetTime

void WindowClass::GetTime()
{
    // 현재시간 가져오기. time_point 타입
    const auto timestamp_now = std::chrono::system_clock::now();
    // time_t 로 변경
    const auto time_now = std::chrono::system_clock::to_time_t(timestamp_now);
    // time_t 타입을 tm 구조체에 집어넣음
    const auto time_struct = std::localtime(&time_now);

    // tm 구조체에서 시간 분 초만 꺼내온다.
    secs = time_struct->tm_sec;
    mins = time_struct->tm_min;
    hrs = time_struct->tm_hour;
}

GetTheta

시, 분, 초침들의 각도를 계산하는 함수

std::tuple<float, float, float> WindowClass::GetTheta()
{
    // 초침은 그대로 가지고있어도 되지만,
    // 분침은 초침이 움직인만큼 추가로 더 움직여야함
    // 10분 0초의 분침 != 10분 30초의 분침
    const auto seconds_frac = static_cast<float>(secs);
    const auto minutes_frac = static_cast<float>(mins) + seconds_frac / 60.0f;
    const auto hours_frac = static_cast<float>(hrs) + minutes_frac / 60.0f;

    // 2PI가 360이니까 12로 나눠서 시침의 theta를 구한다.
    // 9시 방향에서 시작하기 때문에 offset 90도를 더해서 12시부터 시작되게 한다.
    const auto hour_theta = (hours_frac * ((2.0f * PI) / 12.0f)) + offset;
    const auto minute_theta = (minutes_frac * ((2.0f * PI) / 60.0f)) + offset;
    const auto second_theta = (seconds_frac * ((2.0f * PI) / 60.0f)) + offset;

    return std::make_tuple(hour_theta, minute_theta, second_theta);
}

DrawClockHand

시, 분, 초 침을 그린다.
(시작지점 center부터 end_point 까지 잇는 선분)

void WindowClass::DrawClockHand(const float radius,
                                const float theta,
                                const ImColor color)
{
    const auto c = std::cos(theta);   // r이 1일때 x 좌표
    const auto s = std::sin(theta);   // r이 1일때 y 좌표

    // 정규화된 x, y 좌표에 r을 곱한 마이너스 값을 찾는다
    // 마이너스값인 이유는 y축이 
    const auto end_point = ImVec2(center.x - radius * c, center.y - radius * s);

    ImGui::GetWindowDrawList()->AddLine(center, end_point, color, 3.0f);
}

DrawAllHourStrokes

12개의 시 눈금을 그린다. 위에서 했던 시침 그리는것과 같음

void WindowClass::DrawAllHourStrokes()
{
    for (std::uint32_t hr = 0; hr < 12; ++hr)
    {
        const auto theta = (hr * ((2.0f * PI) / 12.0f)) + offset;
        const auto c = std::cos(theta);
        const auto s = std::sin(theta);

        const auto start_point =
            ImVec2(center.x + (circleRadius * hrsStrokesLength) * c,
                   center.y + (circleRadius * hrsStrokesLength) * s);

        const auto end_point =
            ImVec2(center.x + circleRadius * c,
                   center.y + circleRadius * s);

        ImGui::GetWindowDrawList()->AddLine(start_point,
                                            end_point,
                                            ImGui::GetColorU32(ImGuiCol_Text),
                                            3.0f);
    }
}

Comments

ESC
Type to search...