12 glm::vec3 track_color,
13 glm::vec3 handle_color)
15 return [bounds, handle_w, track_color, handle_color](
16 float v, std::vector<uint8_t>& out,
Element& el) {
17 float x = bounds.
min.x + v * (bounds.
width() - handle_w);
18 float yt = bounds.
min.y + bounds.
height() * 0.35F;
19 float yb = bounds.
min.y + bounds.
height() * 0.65F;
26 verts.insert(verts.end(), herts.begin(), herts.end());
30 el.bounds_hint = handle;
38 glm::vec3 track_color,
39 glm::vec3 handle_color)
41 return [bounds, handle_h, track_color, handle_color](
42 float v, std::vector<uint8_t>& out,
Element& el) {
43 const float y = bounds.
min.y + v * (bounds.
height() - handle_h);
44 const float xl = bounds.
min.x + bounds.
width() * 0.35F;
45 const float xr = bounds.
min.x + bounds.
width() * 0.65F;
52 verts.insert(verts.end(), herts.begin(), herts.end());
56 el.bounds_hint = handle;
68 return [center, radius, angle_start, angle_end, color](
69 float v, std::vector<uint8_t>& out,
Element& el) {
70 float angle = angle_start + v * (angle_end - angle_start);
71 glm::vec2 tip = center + radius * glm::vec2(std::cos(angle), std::sin(angle));
74 std::vector<V> verts = {
75 { .
position = { center.x, center.y, 0 }, .color = color },
76 { .position = { tip.x, tip.y, 0 }, .color = color },
91 return [color, size, hit_radius](glm::vec2 pos, std::vector<uint8_t>& out,
Element& el) {
107 return [bounds, color, size](
108 glm::vec2 v, std::vector<uint8_t>& out,
Element& el) {
109 float x = bounds.
min.x + v.x * bounds.
width();
110 float y = bounds.
min.y + v.y * bounds.
height();
113 std::vector<V> verts = {
114 { .
position = { x, y, 0 }, .color = color, .size = size },
119 el.bounds_hint = bounds;
121 std::array<glm::vec2, 4> {
123 glm::vec2(bounds.
max.x, bounds.
min.y),
125 glm::vec2(bounds.
min.x, bounds.
max.y) } });
130 std::span<const glm::vec2> path,
131 std::shared_ptr<Buffers::FormaBuffer> handle_buf,
132 float half_thickness,
133 glm::vec3 track_color,
134 glm::vec3 fill_color,
135 glm::vec3 handle_color,
138 std::vector<glm::vec2> pts(path.begin(), path.end());
140 std::vector<float> seg_lengths;
141 seg_lengths.reserve(pts.size() > 0 ? pts.size() - 1 : 0);
142 float total_len = 0.0F;
143 for (
size_t i = 0; i + 1 < pts.size(); ++i) {
144 float l = glm::length(pts[i + 1] - pts[i]);
145 seg_lengths.push_back(l);
150 .max = pts.empty() ? glm::vec2(0.F) : pts[0] };
151 for (
const auto& p : pts) {
152 aabb.
min = glm::min(aabb.
min, p);
153 aabb.
max = glm::max(aabb.
max, p);
155 aabb = aabb.
expanded(half_thickness);
157 return [pts = std::move(pts),
158 seg_lengths = std::move(seg_lengths),
161 handle_buf = std::move(handle_buf),
166 handle_size](
float v, std::vector<uint8_t>& out,
Element& el) {
167 if (pts.size() < 2) {
172 const float target = std::clamp(v, 0.0F, 1.0F) * total_len;
174 glm::vec2 handle_pos = pts.front();
175 float accumulated = 0.0F;
176 size_t split_seg = 0;
177 float split_t = 0.0F;
178 for (
size_t i = 0; i < seg_lengths.size(); ++i) {
179 if (accumulated + seg_lengths[i] >= target || i + 1 == seg_lengths.size()) {
180 split_t = seg_lengths[i] > 0.0F
181 ? (target - accumulated) / seg_lengths[i]
183 split_t = std::clamp(split_t, 0.0F, 1.0F);
184 handle_pos = glm::mix(pts[i], pts[i + 1], split_t);
188 accumulated += seg_lengths[i];
194 std::span<const glm::vec2>(pts).subspan(0, split_seg + 1),
196 verts.insert(verts.end(), fill.begin(), fill.end());
198 if (split_t > 0.0F) {
199 verts.push_back({ .position = { pts[split_seg].x, pts[split_seg].y, 0.0F }, .color = fill_color });
200 verts.push_back({ .position = { handle_pos.x, handle_pos.y, 0.0F }, .color = fill_color });
205 el.bounds_hint = aabb;
210 .
position = { handle_pos.x, handle_pos.y, 0.0F },
211 .color = handle_color,
214 std::vector<uint8_t> hbytes(
sizeof(hv));
215 std::memcpy(hbytes.data(), &hv,
sizeof(hv));
216 handle_buf->submit(hbytes);
226 return [region, color_off, color_on](
227 bool v, std::vector<uint8_t>& out,
Element& el) {
229 el.bounds_hint = region;
237 glm::vec3 fill_color,
238 glm::vec3 track_color)
240 return [bounds, horizontal, fill_color, track_color](
241 float v, std::vector<uint8_t>& out,
Element& el) {
242 const float t = std::clamp(v, 0.F, 1.F);
246 const float split = bounds.
min.x + t * bounds.
width();
247 fill = { .min = bounds.
min, .max = { split, bounds.
max.y } };
248 remainder = { .min = { split, bounds.
min.y }, .max = bounds.
max };
250 const float split = bounds.
min.y + t * bounds.
height();
251 fill = { .min = bounds.
min, .max = { bounds.
max.x, split } };
252 remainder = { .min = { bounds.
min.x, split }, .max = bounds.
max };
257 verts.insert(verts.end(), rest.begin(), rest.end());
261 el.bounds_hint = bounds;
272 return [arm_len, color, thickness, hit_radius](
273 glm::vec2 pos, std::vector<uint8_t>& out,
Element& el) {
275 const std::array<V, 4> verts { {
276 { .
position = { pos.x - arm_len, pos.y, 0.F }, .color = color, .thickness = thickness },
277 { .position = { pos.x + arm_len, pos.y, 0.F }, .color = color, .thickness = thickness },
278 { .position = { pos.x, pos.y - arm_len, 0.F }, .color = color, .thickness = thickness },
279 { .position = { pos.x, pos.y + arm_len, 0.F }, .color = color, .thickness = thickness },
292 return [bounds, color, thickness](
293 const std::vector<float>& v, std::vector<uint8_t>& out,
Element& el) {
296 el.bounds_hint = bounds;
298 std::array<glm::vec2, 4> {
300 glm::vec2(bounds.
max.x, bounds.
min.y),
302 glm::vec2(bounds.
min.x, bounds.
max.y) } });
306 const auto n = v.size();
307 const float x_step = bounds.
width() /
static_cast<float>(n - 1);
309 std::vector<Kakshya::LineVertex> verts;
310 verts.reserve((n - 1) * 2);
312 for (
size_t i = 0; i + 1 < n; ++i) {
313 const float xa = bounds.
min.x +
static_cast<float>(i) * x_step;
314 const float xb = bounds.
min.x +
static_cast<float>(i + 1) * x_step;
315 const float ya = bounds.
min.y + std::clamp(v[i], 0.F, 1.F) * bounds.
height();
316 const float yb = bounds.
min.y + std::clamp(v[i + 1], 0.F, 1.F) * bounds.
height();
318 verts.push_back({ .position = { xa, ya, 0.F }, .color = color, .thickness = thickness });
319 verts.push_back({ .position = { xb, yb, 0.F }, .color = color, .thickness = thickness });
324 el.bounds_hint = bounds;
326 std::array<glm::vec2, 4> {
328 glm::vec2(bounds.
max.x, bounds.
min.y),
330 glm::vec2(bounds.
min.x, bounds.
max.y) } });
337 std::shared_ptr<
MappedState<std::vector<float>>> state,
341 std::optional<size_t> prev_index;
343 auto ds = std::make_shared<DragState>();
346 [state, bounds, ds](uint32_t, glm::vec2 ndc) {
347 auto& v = state->value;
351 const float t = (ndc.x - bounds.
min.x) / bounds.
width();
352 const float a = (ndc.y - bounds.
min.y) / bounds.
height();
353 const size_t n = v.size();
354 const size_t idx =
static_cast<size_t>(
355 std::clamp(t, 0.F, 1.F) *
static_cast<float>(n - 1));
356 const float amp = std::clamp(
a, 0.F, 1.F);
358 if (ds->prev_index && *ds->prev_index != idx) {
359 const size_t lo = std::min(*ds->prev_index, idx);
360 const size_t hi = std::max(*ds->prev_index, idx);
361 const float v0 = v[*ds->prev_index];
362 const auto span =
static_cast<float>(hi - lo);
363 for (
size_t i = lo; i <= hi; ++i) {
364 const float f = span > 0.F
365 ?
static_cast<float>(i - lo) / span
367 v[i] = glm::mix(v0, amp, f);
373 ds->prev_index = idx;
378 ds->prev_index = std::nullopt;
Illustrative geometry functions for common Mapped use cases.
void on_release(uint32_t id, IO::MouseButtons btn, PressFn fn)
Called when a mouse button is released over an element.
void on_drag(uint32_t id, IO::MouseButtons btn, MoveFn fn)
Called on each mouse-move event while btn is held, tracking the element where the drag began even whe...
Event wiring between a Layer and a window surface.
std::vector< MeshVertex > to_mesh_vertices(std::span< const Vertex > vertices, glm::vec2 weight_range)
Batch-project raw Vertex vector to MeshVertex.
std::function< bool(glm::vec2)> polygon_bounds(std::span< const glm::vec2 > vertices)
Containment test for a convex or concave polygon.
std::vector< Kakshya::LineVertex > polyline(std::span< const glm::vec2 > pts, glm::vec3 color, float thickness)
Polyline as a LINE_LIST (open path, 2 * (pts.size() - 1) vertices).
std::function< bool(glm::vec2)> stroke_bounds(std::span< const glm::vec2 > points, float half_thickness)
Containment test for a polyline with a uniform half-thickness.
std::function< bool(glm::vec2)> circular_bounds(glm::vec2 center, float radius) noexcept
Containment test for a circle.
std::array< Kakshya::Vertex, 4 > filled_rect(Kinesis::AABB2D region, glm::vec3 color)
Generate a filled TRIANGLE_STRIP quad from an AABB2D.
Vertex type for line primitives (LINE_LIST / LINE_STRIP topology)
Vertex type for point primitives (POINT_LIST topology)
static AABB2D from_ndc(glm::vec2 center, glm::vec2 half) noexcept
float height() const noexcept
bool contains(glm::vec2 p) const noexcept
float width() const noexcept
Axis-aligned bounding rectangle in a 2D coordinate space.
bool contains(const glm::vec3 &p) const noexcept
AABB3D expanded(float margin) const noexcept