diff --git a/examples/button.c b/examples/button.c new file mode 100644 index 0000000..daeb146 --- /dev/null +++ b/examples/button.c @@ -0,0 +1,30 @@ +#include +#include +#include + +static PtkRect *r; + +static void on_press(int button, int action, int mods) { + (void)button; (void)action; (void)mods; + PTK_DEBUG("pressed!"); + r->color.r = r->color.r + (1.0f / 255.0f); + if (r->color.r == 256.0f) { + r->color.r = 0.0f; + } +} + +int main(void) { + if (!ptk_init(800, 600, "button", (PtkVersion){ .major = 0, .minor = 1, .patch = 0 })) { + return EXIT_FAILURE; + } + + r = (PtkRect *)ptk_rect( + (PtkPos){ .x = 100.0f, .y = 100.0f }, + (PtkSize){ .w = 100.0f, .h = 50.0f }, + (PtkColor){ .r = 1.0f, .g = 0.0f, .b = 0.0f } + ); + + return ptk_run( + ptk_button((PtkHandle)r, on_press) + ); +} diff --git a/include/ptk.h b/include/ptk.h index 36fe213..8661353 100644 --- a/include/ptk.h +++ b/include/ptk.h @@ -23,6 +23,7 @@ typedef enum { PTK_COMPONENT_TYPE_TRIANGLE = 1, PTK_COMPONENT_TYPE_RECT = 2, PTK_COMPONENT_TYPE_ELLIPSE = 3, + PTK_COMPONENT_TYPE_BUTTON = 4, } PtkComponentType; typedef struct PtkComponent { @@ -55,12 +56,21 @@ typedef struct PtkEllipse { PtkColor color; } PtkEllipse; +typedef void (*MouseButtonCallback)(int button, int action, int mods); + +typedef struct PtkButton { + PtkComponentType type; + PtkHandle hitbox; + MouseButtonCallback on_press; +} PtkButton; + PtkHandle ptk_box(size_t child_count, PtkHandle *children); PtkHandle ptk_triangle(PtkPos vertices[3], PtkColor color); PtkHandle ptk_rect(PtkPos top_left, PtkSize size, PtkColor color); PtkHandle ptk_square(PtkPos top_left, float size, PtkColor color); PtkHandle ptk_ellipse(PtkPos center, PtkSize radii, PtkColor color); PtkHandle ptk_circle(PtkPos center, float radius, PtkColor color); +PtkHandle ptk_button(PtkHandle hitbox, MouseButtonCallback on_press); #define PTK_BOX(...) ptk_box(sizeof((PtkHandle []){ __VA_ARGS__ }) / sizeof(PtkHandle), (PtkHandle []) { __VA_ARGS__ }) diff --git a/include/ptk_vec.h b/include/ptk_vec.h index d63684d..210f020 100644 --- a/include/ptk_vec.h +++ b/include/ptk_vec.h @@ -62,4 +62,4 @@ typedef union { typedef PtkVec4 PtkColorA; -#endif // _PTK_PTK_VEC_H +#endif // PTK_PTK_VEC_H_ diff --git a/src/ptk.c b/src/ptk.c index b9c64cc..692f304 100644 --- a/src/ptk.c +++ b/src/ptk.c @@ -21,11 +21,27 @@ static GLFWwindow *g_window = NULL; -void framebuffer_resize_callback(GLFWwindow *window, int width, int height) { +static void framebuffer_resize_callback(GLFWwindow *window, int width, int height) { (void)window; (void)width; (void)height; g_framebuffer_resized = true; } +static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) { + (void)window; (void)scancode; (void)action; (void)mods; + if (key == GLFW_KEY_SPACE) { + for (size_t i = 0; i < g_vertices.size; ++i) { + g_vertices.data[i].pos.x += 1.0f; + } + vk_transfer_vertex_data(); + } +} + +static void mouse_button_callback(GLFWwindow *window, int button, int action, int mods) { + double x, y; + glfwGetCursorPos(window, &x, &y); + vk_handle_mouse_button_input((PtkPos){ .x = x, .y = y }, button, action, mods); +} + PTK_OPTION_DEFINE(PtkLogLevel); PTK_OPTION(PtkLogLevel) get_log_level(void) { @@ -101,6 +117,8 @@ bool ptk_init(size_t width, size_t height, const char *title, PtkVersion applica } glfwSetFramebufferSizeCallback(g_window, framebuffer_resize_callback); + glfwSetKeyCallback(g_window, key_callback); + glfwSetMouseButtonCallback(g_window, mouse_button_callback); vk_components_init(); @@ -158,8 +176,20 @@ PtkHandle ptk_circle(PtkPos center, float radius, PtkColor color) { return ptk_ellipse(center, (PtkSize){ .w = radius, .h = radius }, color); } +PtkHandle ptk_button(PtkHandle hitbox, MouseButtonCallback on_press) { + PtkButton *ret = malloc(sizeof(PtkButton)); + ret->type = PTK_COMPONENT_TYPE_BUTTON; + ret->hitbox = hitbox; + ret->on_press = on_press; + + return (PtkHandle)ret; +} + void init_components(PtkHandle root) { switch (root->type) { + case PTK_COMPONENT_TYPE_BOX: { + vk_box((PtkBox *)root); + } break; case PTK_COMPONENT_TYPE_TRIANGLE: { vk_triangle((PtkTriangle *)root); } break; @@ -169,11 +199,8 @@ void init_components(PtkHandle root) { case PTK_COMPONENT_TYPE_ELLIPSE: { vk_ellipse((PtkEllipse *)root); } break; - case PTK_COMPONENT_TYPE_BOX: { - PtkBox *box = (PtkBox *)root; - for (size_t i = 0; i < box->child_count; ++i) { - init_components(box->children[i]); - } + case PTK_COMPONENT_TYPE_BUTTON: { + vk_button((PtkButton *)root); } break; } } diff --git a/src/ptk_vk/components.c b/src/ptk_vk/components.c index 519a564..b442e50 100644 --- a/src/ptk_vk/components.c +++ b/src/ptk_vk/components.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -72,9 +73,7 @@ void vk_rect(PtkRect *rect) { _vk_rect(rect, PTK_COMPONENT_TYPE_RECT); } -void vk_ellipse(PtkEllipse *ellipse) { - PTK_LIST_ADD(PtkHandle, m_components, ellipse); - +void _vk_ellipse(PtkEllipse *ellipse, PtkComponentType type) { PtkPos top_left = { .x = ellipse->center.x - ellipse->radii.w, .y = ellipse->center.y - ellipse->radii.h, @@ -85,7 +84,130 @@ void vk_ellipse(PtkEllipse *ellipse) { .h = ellipse->radii.h * 2.0f, }; - _vk_rect((PtkRect *)ptk_rect(top_left, size, ellipse->color), PTK_COMPONENT_TYPE_ELLIPSE); + _vk_rect((PtkRect *)ptk_rect(top_left, size, ellipse->color), type); +} + +void vk_ellipse(PtkEllipse *ellipse) { + PTK_LIST_ADD(PtkHandle, m_components, ellipse); + + _vk_ellipse(ellipse, PTK_COMPONENT_TYPE_ELLIPSE); +} + +void _vk_box(PtkBox *box, PtkComponentType type) { + for (size_t i = 0; i < box->child_count; ++i) { + PtkHandle child = box->children[i]; + switch (child->type) { + case PTK_COMPONENT_TYPE_BOX: { + _vk_box((PtkBox *)child, type); + } break; + case PTK_COMPONENT_TYPE_TRIANGLE: { + _vk_triangle((PtkTriangle *)child, type, 0); + } break; + case PTK_COMPONENT_TYPE_RECT: { + _vk_rect((PtkRect *)child, type); + } break; + case PTK_COMPONENT_TYPE_ELLIPSE: { + _vk_ellipse((PtkEllipse *)child, type); + } break; + default: + return; + } + } +} + +void vk_box(PtkBox *box) { + PTK_LIST_ADD(PtkHandle, m_components, box); + + _vk_box(box, PTK_COMPONENT_TYPE_BOX); +} + +void vk_button(PtkButton *button) { + PTK_LIST_ADD(PtkHandle, m_components, button); + + switch (button->hitbox->type) { + case PTK_COMPONENT_TYPE_BOX: { + _vk_box((PtkBox *)button->hitbox, PTK_COMPONENT_TYPE_BUTTON); + } break; + case PTK_COMPONENT_TYPE_TRIANGLE: { + _vk_triangle((PtkTriangle *)button->hitbox, PTK_COMPONENT_TYPE_BUTTON, 0); + } break; + case PTK_COMPONENT_TYPE_RECT: { + _vk_rect((PtkRect *)button->hitbox, PTK_COMPONENT_TYPE_BUTTON); + } break; + case PTK_COMPONENT_TYPE_ELLIPSE: { + _vk_ellipse((PtkEllipse *)button->hitbox, PTK_COMPONENT_TYPE_BUTTON); + } break; + default: + return; + } +} + +bool triangle_intersects(PtkTriangle t, PtkPos p) { + PtkPos p0 = t.vertices[0]; + PtkPos p1 = t.vertices[1]; + PtkPos p2 = t.vertices[2]; + float a = 1.0f/2.0f * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y); + int sign = a < 0.0f ? -1 : 1; + float s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y) * sign; + float r = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y) * sign; + + return s > 0 && r > 0 && (s + r) < 2 * a * sign; +} + +bool rect_intersects(PtkRect r, PtkPos p) { + bool intersects_x = r.top_left.x <= p.x + && r.top_left.x + r.top_left.w >= p.x; + + bool intersects_y = r.top_left.y <= p.y + && r.top_left.y + r.top_left.h >= p.y; + + return intersects_x && intersects_y; +} + +bool ellipse_intersects(PtkEllipse e, PtkPos p) { + float x = p.x - e.center.x; + float rx = e.radii.x; + float y = p.y - e.center.y; + float ry = e.radii.y; + return ((x * x) / (rx * rx)) + ((y * y) / (ry * ry)) <= 1.0f; +} + +bool intersects(PtkHandle component, PtkPos point) { + switch (component->type) { + case PTK_COMPONENT_TYPE_BOX: { + PtkBox *box = (PtkBox *)component; + for (size_t i = 0; i < box->child_count; ++i) { + if (intersects(box->children[i], point)) { + return true; + } + } + + return false; + } + case PTK_COMPONENT_TYPE_TRIANGLE: + return triangle_intersects(*(PtkTriangle *)component, point); + case PTK_COMPONENT_TYPE_RECT: + return rect_intersects(*(PtkRect *)component, point); + case PTK_COMPONENT_TYPE_ELLIPSE: + return ellipse_intersects(*(PtkEllipse *)component, point); + case PTK_COMPONENT_TYPE_BUTTON: + return intersects(((PtkButton *)component)->hitbox, point); + } +} + +void vk_handle_mouse_button_input(PtkPos cursor_pos, int button, int action, int mods) { + for (size_t i = 0; i < m_components.size; ++i) { + PtkHandle current = m_components.data[i]; + if (current->type != PTK_COMPONENT_TYPE_BUTTON) { + continue; + } + + PtkButton *b = (PtkButton *)current; + + if (intersects((PtkHandle)b, cursor_pos)) { + b->on_press(button, action, mods); + } + } } void vk_components_cleanup(void) { diff --git a/src/ptk_vk/components.h b/src/ptk_vk/components.h index 2f0430d..a377434 100644 --- a/src/ptk_vk/components.h +++ b/src/ptk_vk/components.h @@ -17,9 +17,13 @@ extern PTK_LIST(Vertex) g_vertices; void vk_components_init(void); +void vk_box(PtkBox *box); void vk_triangle(PtkTriangle *triangle); void vk_rect(PtkRect *rect); void vk_ellipse(PtkEllipse *ellipse); +void vk_button(PtkButton *button); + +void vk_handle_mouse_button_input(PtkPos cursor_pos, int button, int action, int mode); void vk_components_cleanup(void);