#pragma once namespace att { enum class kAttribute : uint8_t { Width, MinWidth, MaxWidth, Height, MinHeight, MaxHeight, Divisions, InnerRadius, OuterRadius, Grow, Shrink, FlexDir, FlexWrap, Padding, Margin, Color }; struct AttributeBase { kAttribute id; }; typedef std::map> AttrubutesMap; template struct Attribute : public AttributeBase { static const kAttribute static_id = T; V value; Attribute() : value{0} { id = static_id; } Attribute(V v) : value(v) { id = static_id; } }; struct typemap { const char* name; kAttribute value; }; static constexpr typemap map[] = { { "width", kAttribute::Width }, { "min-width", kAttribute::MinWidth }, { "max-width", kAttribute::MaxWidth }, { "height", kAttribute::Height }, { "min-height", kAttribute::MinHeight }, { "max-height", kAttribute::MaxHeight }, { "divisions", kAttribute::Divisions }, { "inner-radius", kAttribute::InnerRadius }, { "outer-radius", kAttribute::OuterRadius }, { "grow", kAttribute::Grow }, { "shrink", kAttribute::Shrink }, { "dir", kAttribute::FlexDir }, { "wrap", kAttribute::FlexWrap }, { "pad", kAttribute::Padding }, { "margin", kAttribute::Margin }, { "color", kAttribute::Color }, }; constexpr int map_size = sizeof(map) / sizeof(typemap) - 1; constexpr bool same(const char* a, const char* b) { return (*a && *b) ? (*a == *b && same(a + 1, b + 1)) : !(*a || *b); } constexpr const kAttribute value(char const *name, int i = 0) { return ((i >= map_size) || same(map[i].name, name)) ? map[i].value : value(name, i + 1); } constexpr const char* string(const kAttribute value, int i = 0) { return (map[i].value == value) ? map[i].name : string(value, i + 1); } //constexpr const char* string(kAttribute a) { return names[(int)a]; } // template // constexpr const char* string(const Attribute a) { return names[(int)a.id]; } #define DECLARE_ATTRIBUTE(N,T) \ struct N : public Attribute \ { using Attribute::Attribute; }; DECLARE_ATTRIBUTE(Width, float); DECLARE_ATTRIBUTE(MinWidth, float); DECLARE_ATTRIBUTE(MaxWidth, float); DECLARE_ATTRIBUTE(Height, float); DECLARE_ATTRIBUTE(MinHeight, float); DECLARE_ATTRIBUTE(MaxHeight, float); DECLARE_ATTRIBUTE(Divisions, int); DECLARE_ATTRIBUTE(InnerRadius, float); DECLARE_ATTRIBUTE(OuterRadius, float); DECLARE_ATTRIBUTE(Grow, float); DECLARE_ATTRIBUTE(Shrink, float); DECLARE_ATTRIBUTE(FlexDir, int); DECLARE_ATTRIBUTE(FlexWrap, int); DECLARE_ATTRIBUTE(Padding, glm::vec4); DECLARE_ATTRIBUTE(Margin, glm::vec4); DECLARE_ATTRIBUTE(Color, glm::vec4); #undef DECLARE_ATTRIBUTE } class Shape { protected: GLuint buffers[2]{ 0 }; GLuint arrays[2]{ 0 }; GLuint count[2]{ 0 }; GLvoid* ioff[2]{ 0 }; struct vertex_t { glm::vec4 pos; glm::vec2 uvs; }; public: YGNodeRef y_node; glm::vec4 color; att::AttrubutesMap attribs; bool create_buffers(GLvoid* idx, GLvoid* vertices, int isize, int vsize); void draw_fill() const; void draw_stroke() const; virtual bool create_attrib() { return true; }; ~Shape() { glDeleteBuffers(2, buffers); glDeleteVertexArrays(2, arrays); } protected: glm::vec2 quad_mid_point(glm::vec2 a, glm::vec2 b, glm::vec2 c, glm::vec2 d) { float den = (a.x-c.x)*(b.y-d.y) - (a.y-c.y)*(b.x-d.x); float numx = (a.x*c.y-a.y*c.x)*(b.x-d.x) - (a.x-c.x)*(b.x*d.y-b.y*d.x); float numy = (a.x*c.y-a.y*c.x)*(b.y-d.y) - (a.y-c.y)*(b.x*d.y-b.y*d.x); return glm::vec2(numx, numy) / den; } void adjust_quad_uvs(vertex_t& va, vertex_t& vb, vertex_t& vc, vertex_t& vd) { static float d[4]; static vertex_t* v[4]; v[0] = &va; v[1] = &vb; v[2] = &vc; v[3] = &vd; auto mid = quad_mid_point(va.pos, vb.pos, vc.pos, vd.pos); for (int i = 0; i < 4; i++) d[i] = glm::distance(glm::vec2(v[i]->pos), mid); for (int i = 0; i < 4; i++) { float q = (d[i] + d[(i + 2) % 4]) / d[(i + 2) % 4]; v[i]->uvs = glm::vec2(v[i]->uvs) * q; v[i]->pos.w = q; } } }; class Plane : public Shape { void create_impl(float w, float h, int div, GLushort* idx, vertex_t* vertices); public: template bool create(float w, float h) { static GLushort idx[div * div * 6 + 8]; static vertex_t vertices[(div+1)*(div+1)]; create_impl(w, h, div, idx, vertices); return create_buffers(idx, vertices, sizeof(idx), sizeof(vertices)); } bool create(att::Divisions divisions, att::Width w, att::Height h) { const int div = divisions.value; auto idx = std::make_unique(div * div * 6 + 8); auto vertices = std::make_unique((div + 1)*(div + 1)); create_impl(w.value, h.value, div, idx.get(), vertices.get()); return create_buffers(idx.get(), vertices.get(), sizeof(idx), sizeof(vertices)); } template T get_attribute(T def_val) { auto ret = attribs.find(def_val.id); if (ret == attribs.end()) return def_val; return *reinterpret_cast(ret->second.get()); } bool create_attrib() override { const auto w = get_attribute(att::Width(0)); const auto h = get_attribute(att::Height(0)); const auto d = get_attribute(att::Divisions(1)); return create(d, w, h); } }; class RectShape : public Shape { bool create(float w, float h); }; class Circle : public Shape { public: enum class kUVMapping: uint8_t { Planar, Tube }; template bool create(float radius) { static GLushort idx[div*3 + div*2]; static vertex_t vertices[div+1]; create_impl(radius, div, idx, vertices); return create_buffers(idx, vertices, sizeof(idx), sizeof(vertices)); } template bool create(float radius, kUVMapping map) { static GLushort idx[(div+1)*3 + (div+1)*4]; static vertex_t vertices[(div+1)*2]; create_impl(radius, 0.f, (div+1), idx, vertices, map); return create_buffers(idx, vertices, sizeof(idx), sizeof(vertices)); } template bool create(float radius_out, float radius_in, kUVMapping map) { static GLushort idx[(div+1)*6 + (div+1)*4]; static vertex_t vertices[(div+1) * 2]; create_impl(radius_out, radius_in, (div+1), idx, vertices, map); return create_buffers(idx, vertices, sizeof(idx), sizeof(vertices)); } private: void create_impl(float radius, int div, GLushort* idx, vertex_t* vertices); void create_impl(float radius_out, float radius_in, int div, GLushort* idx, vertex_t* vertices, kUVMapping map); }; class Rounded : public Shape { void create_impl(float w, float h, float r, int div, GLushort* idx, GLushort* idx_tmp, vertex_t* vertices); public: template bool create(float w, float h, float r) { static GLushort idx[(10 + div * 4) * 3 + (4 + div * 4) * 2]; static GLushort idx_tmp[div+1]; static vertex_t vertices[12 + (div-1) * 4]; create_impl(w, h, r, div, idx, idx_tmp, vertices); return create_buffers(idx, vertices, sizeof(idx), sizeof(vertices)); } }; class Slice9 : public Shape { void create_impl(float w, float h, float r, float tr, GLushort* idx, vertex_t* vertices); public: bool create(float w, float h, float r, float tr) { static GLushort idx[3 * 3 * 6 + 4 * 2]; static vertex_t vertices[4 * 4]; create_impl(w, h, r, tr, idx, vertices); return create_buffers(idx, vertices, sizeof(idx), sizeof(vertices)); } };