trmnl_sdl/Widgets/WidgetRect.cpp

225 lines
4.6 KiB
C++

#include "WidgetRect.h"
#include <vector>
#include "../sdl_helpers.h"
using std::vector;
WidgetRect::WidgetRect(int x, int y, int width, int height,
int radius, int stroke_size, SDL_Color color)
: Widget(x, y, width, height),
m_color(color),
m_stroke_size(stroke_size),
m_radius(radius)
{
}
WidgetRect::WidgetRect(int x, int y, int width, int height,
int radius, int stroke_size)
: Widget(x, y, width, height),
m_color{.r = 0, .g = 0, .b = 0, .a = SDL_ALPHA_OPAQUE},
m_stroke_size(stroke_size),
m_radius(radius)
{
}
void WidgetRect::set_color(SDL_Color color)
{
m_color = color;
}
void WidgetRect::set_stroke_size(int stroke_size)
{
m_stroke_size = stroke_size
}
void WidgetRect::set_radius(int radius)
{
m_radius = radius;
}
void WidgetRect::draw()
{
if (nullptr == m_surface)
{
return;
}
// Clear surface
SDL_FillRect(m_surface, nullptr, SDL_MapRGBA(m_surface->format, 255, 255, 255, SDL_ALPHA_TRANSPARENT));
if (m_radius > 0)
{
// Draw rounded corners here
draw_circle_corner(m_rect.w - m_radius, m_radius - 1, 1);
draw_circle_corner(m_radius - 1, m_radius - 1, 2);
draw_circle_corner(m_radius - 1, m_rect.h - m_radius, 3);
draw_circle_corner(m_rect.w - m_radius, m_rect.h - m_radius, 4);
// Draw X rect
if (m_radius * 2 < m_rect.w)
{
SDL_Rect rect;
rect.x = 0;
rect.y = m_radius;
rect.w = m_rect.w;
rect.h = m_rect.h - 2 * m_radius;
SDL_FillRect(m_surface, &rect, SDL_MapRGBA(m_surface->format, m_color.r, m_color.g, m_color.b, m_color.a));
if (m_stroke_size > 0)
{
rect.x = m_stroke_size;
rect.y = m_radius;
rect.w = m_rect.w - 2 * m_stroke_size;
rect.h = m_rect.h - 2 * m_radius;
SDL_FillRect(m_surface, &rect, SDL_MapRGBA(m_surface->format, 255, 255, 255, SDL_ALPHA_TRANSPARENT));
}
}
// Draw Y rect
if (m_radius * 2 < m_rect.h)
{
SDL_Rect rect;
rect.x = m_radius;
rect.y = 0;
rect.w = m_rect.w - 2 * m_radius;
rect.h = m_rect.h;
SDL_FillRect(m_surface, &rect, SDL_MapRGBA(m_surface->format, m_color.r, m_color.g, m_color.b, m_color.a));
if (m_stroke_size > 0)
{
rect.x = m_radius;
rect.y = m_stroke_size;
rect.w = m_rect.w - 2 * m_radius;
rect.h = m_rect.h - 2 * m_stroke_size;
SDL_FillRect(m_surface, &rect, SDL_MapRGBA(m_surface->format, 255, 255, 255, SDL_ALPHA_TRANSPARENT));
}
}
}
else
{
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = m_rect.w;
rect.h = m_rect.h;
SDL_FillRect(m_surface, &rect, SDL_MapRGBA(m_surface->format, m_color.r, m_color.g, m_color.b, m_color.a));
if (m_stroke_size > 0)
{
rect.x = m_stroke_size;
rect.y = m_stroke_size;
rect.w = m_rect.w - 2 * m_stroke_size;
rect.h = m_rect.h - 2 * m_stroke_size;
SDL_FillRect(m_surface, &rect, SDL_MapRGBA(m_surface->format, 255, 255, 255, SDL_ALPHA_TRANSPARENT));
}
}
}
std::unique_ptr<Widget> WidgetRect::builder(const nlohmann::json& j)
{
int x = 0;
int y = 0;
int width = 0;
int height = 0;
int radius = 0;
int stroke_size = -1;
SDL_Color color = {.r = 0, .g = 0, .b = 0, .a = SDL_ALPHA_OPAQUE};
json_extract(j, "x", x);
json_extract(j, "y", y);
json_extract(j, "width", width);
json_extract(j, "height", height);
json_extract(j, "radius", radius);
json_extract(j, "stroke", stroke_size);
json_extract(j, "color", color);
return std::make_unique<WidgetRect>(x, y, width, height, radius, stroke_size, color);
}
void WidgetRect::draw_circle_corner(int x, int y, int quadrant)
{
if (0 == m_radius)
{
return;
}
int x_add;
int y_add;
switch (quadrant)
{
case 1:
x_add = 1;
y_add = -1;
break;
case 2:
x_add = -1;
y_add = -1;
break;
case 3:
x_add = -1;
y_add = 1;
break;
case 4:
x_add = 1;
y_add = 1;
break;
default:
// TODO: Message printing
return;
break;
}
// Create renderer
SDL_Renderer* renderer = SDL_CreateSoftwareRenderer(m_surface);
if (nullptr == renderer)
{
// TODO: Message printing
return;
}
SDL_SetRenderDrawColor(renderer, m_color.r, m_color.g, m_color.b, m_color.a);
vector<SDL_Point> points;
int max_dist = m_radius * m_radius;
int min_dist = 0;
if (m_stroke_size > 0)
{
min_dist = (m_radius - m_stroke_size) * (m_radius - m_stroke_size);
}
for (int i_x = 0; i_x < m_radius; ++i_x)
for (int i_y = 0; i_y < m_radius; ++i_y)
{
bool should_fill = false;
// a2 + b2 = c2
int dist = i_x * i_x + i_y * i_y;
// <= c2 (max) && > c2 (min)
if ((dist <= max_dist) && (dist >= min_dist))
{
should_fill = true;
}
if (should_fill)
{
SDL_Point point =
{
.x = x + x_add * i_x,
.y = y + y_add * i_y
};
points.push_back(point);
}
}
SDL_RenderDrawPoints(renderer, points.data(), points.size());
SDL_DestroyRenderer(renderer);
}