11 glm::vec3 track_color,
12 glm::vec3 handle_color)
15 float v, std::vector<uint8_t>& out,
Element& el) {
16 float x = bounds.
min.x + v * (bounds.
width() - handle_w);
17 float yt = bounds.
min.y + bounds.
height() * 0.35F;
18 float yb = bounds.
min.y + bounds.
height() * 0.65F;
25 verts.insert(verts.end(), herts.begin(), herts.end());
29 el.bounds_hint = handle;
33 return { std::move(fn),
42 glm::vec3 track_color,
43 glm::vec3 handle_color)
46 float v, std::vector<uint8_t>& out,
Element& el) {
47 const float y = bounds.
min.y + v * (bounds.
height() - handle_h);
48 const float xl = bounds.
min.x + bounds.
width() * 0.35F;
49 const float xr = bounds.
min.x + bounds.
width() * 0.65F;
56 verts.insert(verts.end(), herts.begin(), herts.end());
60 el.bounds_hint = handle;
64 return { std::move(fn),
76 const glm::vec2 center = region.
center();
80 float v, std::vector<uint8_t>& out,
Element& el) {
81 const float angle = angle_start + v * (angle_end - angle_start);
82 const glm::vec2 tip = center +
radius * glm::vec2(std::cos(angle), std::sin(angle));
85 const std::vector<V> verts = {
86 { .
position = { center.x, center.y, 0 }, .color = color },
87 { .position = { tip.x, tip.y, 0 }, .color = color },
96 return { std::move(fn),
117 return { std::move(fn),
129 glm::vec2 v, std::vector<uint8_t>& out,
Element& el) {
130 float x = bounds.
min.x + v.x * bounds.
width();
131 float y = bounds.
min.y + v.y * bounds.
height();
134 std::vector<V> verts = {
135 { .
position = { x, y, 0 }, .color = color, .size = size },
140 el.bounds_hint = bounds;
142 std::array<glm::vec2, 4> {
144 glm::vec2(bounds.
max.x, bounds.
min.y),
146 glm::vec2(bounds.
min.x, bounds.
max.y) } });
149 return { std::move(fn),
156 std::span<const glm::vec2> path,
157 std::shared_ptr<Buffers::FormaBuffer> handle_buf,
158 float half_thickness,
159 glm::vec3 track_color,
160 glm::vec3 fill_color,
161 glm::vec3 handle_color,
164 std::vector<glm::vec2> pts(path.begin(), path.end());
166 std::vector<float> seg_lengths;
167 seg_lengths.reserve(pts.size() > 0 ? pts.size() - 1 : 0);
168 float total_len = 0.0F;
169 for (
size_t i = 0; i + 1 < pts.size(); ++i) {
170 float l = glm::length(pts[i + 1] - pts[i]);
171 seg_lengths.push_back(l);
176 .max = pts.empty() ? glm::vec2(0.F) : pts[0] };
177 for (
const auto& p : pts) {
178 aabb.
min = glm::min(aabb.
min, p);
179 aabb.
max = glm::max(aabb.
max, p);
181 aabb = aabb.
expanded(half_thickness);
187 seg_lengths = std::move(seg_lengths),
190 handle_buf = std::move(handle_buf),
195 handle_size](
float v, std::vector<uint8_t>& out,
Element& el) {
196 if (pts.size() < 2) {
201 const float target = std::clamp(v, 0.0F, 1.0F) * total_len;
203 glm::vec2 handle_pos = pts.front();
204 float accumulated = 0.0F;
205 size_t split_seg = 0;
206 float split_t = 0.0F;
207 for (
size_t i = 0; i < seg_lengths.size(); ++i) {
208 if (accumulated + seg_lengths[i] >= target || i + 1 == seg_lengths.size()) {
209 split_t = seg_lengths[i] > 0.0F
210 ? (target - accumulated) / seg_lengths[i]
212 split_t = std::clamp(split_t, 0.0F, 1.0F);
213 handle_pos = glm::mix(pts[i], pts[i + 1], split_t);
217 accumulated += seg_lengths[i];
223 std::span<const glm::vec2>(pts).subspan(0, split_seg + 1),
225 verts.insert(verts.end(), fill.begin(), fill.end());
227 if (split_t > 0.0F) {
228 verts.push_back({ .position = { pts[split_seg].x, pts[split_seg].y, 0.0F }, .color = fill_color });
229 verts.push_back({ .position = { handle_pos.x, handle_pos.y, 0.0F }, .color = fill_color });
234 el.bounds_hint = aabb;
239 .
position = { handle_pos.x, handle_pos.y, 0.0F },
240 .color = handle_color,
243 std::vector<uint8_t> hbytes(
sizeof(hv));
244 std::memcpy(hbytes.data(), &hv,
sizeof(hv));
245 handle_buf->submit(hbytes);
249 return { std::move(fn),
252 drag_with<float>(std::move(project)) };
261 bool v, std::vector<uint8_t>& out,
Element& el) {
263 el.bounds_hint = region;
267 return { std::move(fn),
276 glm::vec3 fill_color,
277 glm::vec3 track_color)
280 float v, std::vector<uint8_t>& out,
Element& el) {
281 const float t = std::clamp(v, 0.F, 1.F);
285 const float split = bounds.
min.x + t * bounds.
width();
286 fill = { .min = bounds.
min, .max = { split, bounds.
max.y } };
287 remainder = { .min = { split, bounds.
min.y }, .max = bounds.
max };
289 const float split = bounds.
min.y + t * bounds.
height();
290 fill = { .min = bounds.
min, .max = { bounds.
max.x, split } };
291 remainder = { .min = { bounds.
min.x, split }, .max = bounds.
max };
296 verts.insert(verts.end(), rest.begin(), rest.end());
300 el.bounds_hint = bounds;
304 return { std::move(fn),
316 glm::vec2 pos, std::vector<uint8_t>& out,
Element& el) {
318 const std::array<V, 4> verts { {
319 { .
position = { pos.x - arm_len, pos.y, 0.F }, .color = color, .thickness = thickness },
320 { .position = { pos.x + arm_len, pos.y, 0.F }, .color = color, .thickness = thickness },
321 { .position = { pos.x, pos.y - arm_len, 0.F }, .color = color, .thickness = thickness },
322 { .position = { pos.x, pos.y + arm_len, 0.F }, .color = color, .thickness = thickness },
329 return { std::move(fn),
341 const std::vector<float>& v, std::vector<uint8_t>& out,
Element& el) {
344 el.bounds_hint = bounds;
346 std::array<glm::vec2, 4> {
348 glm::vec2(bounds.
max.x, bounds.
min.y),
350 glm::vec2(bounds.
min.x, bounds.
max.y) } });
354 const auto n = v.size();
355 const float x_step = bounds.
width() /
static_cast<float>(n - 1);
357 std::vector<Kakshya::LineVertex> verts;
358 verts.reserve((n - 1) * 2);
360 for (
size_t i = 0; i + 1 < n; ++i) {
361 const float xa = bounds.
min.x +
static_cast<float>(i) * x_step;
362 const float xb = bounds.
min.x +
static_cast<float>(i + 1) * x_step;
363 const float ya = bounds.
min.y + std::clamp(v[i], 0.F, 1.F) * bounds.
height();
364 const float yb = bounds.
min.y + std::clamp(v[i + 1], 0.F, 1.F) * bounds.
height();
366 verts.push_back({ .position = { xa, ya, 0.F }, .color = color, .thickness = thickness });
367 verts.push_back({ .position = { xb, yb, 0.F }, .color = color, .thickness = thickness });
372 el.bounds_hint = bounds;
374 std::array<glm::vec2, 4> {
376 glm::vec2(bounds.
max.x, bounds.
min.y),
378 glm::vec2(bounds.
min.x, bounds.
max.y) } });
390 std::shared_ptr<
MappedState<std::vector<float>>> state,
394 std::optional<size_t> prev_index;
396 auto ds = std::make_shared<DragState>();
399 [state, bounds, ds](uint32_t, glm::vec2 ndc) {
400 auto& v = state->value;
404 const float t = (ndc.x - bounds.
min.x) / bounds.
width();
405 const float a = (ndc.y - bounds.
min.y) / bounds.
height();
406 const size_t n = v.size();
407 const size_t idx =
static_cast<size_t>(
408 std::clamp(t, 0.F, 1.F) *
static_cast<float>(n - 1));
409 const float amp = std::clamp(
a, 0.F, 1.F);
411 if (ds->prev_index && *ds->prev_index != idx) {
412 const size_t lo = std::min(*ds->prev_index, idx);
413 const size_t hi = std::max(*ds->prev_index, idx);
414 const float v0 = v[*ds->prev_index];
415 const auto span =
static_cast<float>(
hi -
lo);
416 for (
size_t i =
lo; i <=
hi; ++i) {
417 const float f = span > 0.F
418 ?
static_cast<float>(i -
lo) / span
420 v[i] = glm::mix(v0, amp, f);
426 ds->prev_index = idx;
431 ds->prev_index = std::nullopt;
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< float(glm::vec2)> path_fraction(std::span< const glm::vec2 > points)
Normalized arc-length position of the closest point on a polyline.
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::function< float(glm::vec2)> angle_fraction(glm::vec2 center, float angle_start, float angle_end) noexcept
Fraction along an angular sweep about center.
std::function< glm::vec2(glm::vec2)> unit_square(AABB2D bounds) noexcept
Unit-square coordinates of a point within bounds.
std::function< float(glm::vec2)> axis_fraction(AABB2D bounds, float handle_extent=0.F, bool horizontal=true) noexcept
Fraction along one axis of bounds, inverse to fader placement.
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 indexed triangle mesh primitives (TRIANGLE_LIST 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
glm::vec2 center() 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