[imgui] 8. WallClock 구현
목표
- 아날로그 시계

구현코드
메인함수
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