MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ stroke_slider()

MAYAFLUX_API Form< float > MayaFlux::Portal::Forma::Geometry::stroke_slider ( std::span< const glm::vec2 >  path,
FormaTarget  handle_buf,
float  half_thickness = 0.02F,
glm::vec3  track_color = glm::vec3(0.3F),
glm::vec3  fill_color = glm::vec3(0.2F, 0.6F, 1.0F),
glm::vec3  handle_color = glm::vec3(0.95F),
float  handle_size = 10.0F,
const std::function< glm::vec2()> &  live_offset = {} 
)

Geometry function for a value scrubber along an arbitrary polyline.

Value in [0, 1] maps to arc-length position along path. The full path is rendered as a LINE_LIST in track_color; the prefix from the path start to the handle position is rendered in fill_color. The handle itself is a PointVertex submitted to handle_buf each sync.

Parameters
pathOrdered polyline vertices in NDC. Copied into closure.
handle_bufPOINT_LIST FormaBuffer for the handle point. Must be registered and have setup_rendering called.
half_thicknessHit region half-thickness in NDC units.
track_colorColor of the full path.
fill_colorColor of the prefix segment up to the handle.
handle_colorColor of the handle point.
handle_sizeHandle point size in pixels.
live_offsetOptional. Current translation of the drawn path.

Definition at line 185 of file FormFactory.cpp.

194{
195 std::vector<glm::vec2> pts(path.begin(), path.end());
196
197 std::vector<float> seg_lengths;
198 seg_lengths.reserve(pts.size() > 0 ? pts.size() - 1 : 0);
199 float total_len = 0.0F;
200 for (size_t i = 0; i + 1 < pts.size(); ++i) {
201 float l = glm::length(pts[i + 1] - pts[i]);
202 seg_lengths.push_back(l);
203 total_len += l;
204 }
205
206 Kinesis::AABB2D aabb { .min = pts.empty() ? glm::vec2(0.F) : pts[0],
207 .max = pts.empty() ? glm::vec2(0.F) : pts[0] };
208 for (const auto& p : pts) {
209 aabb.min = glm::min(aabb.min, p);
210 aabb.max = glm::max(aabb.max, p);
211 }
212 aabb = aabb.expanded(half_thickness);
213
214 auto project = Kinesis::path_fraction(path, live_offset);
215 const size_t cap = pts.size() * 4 * sizeof(Kakshya::LineVertex);
216
217 GeometryFn<float> fn = [pts = std::move(pts),
218 seg_lengths = std::move(seg_lengths),
219 total_len,
220 aabb,
221 handle_buf = std::move(handle_buf),
222 half_thickness,
223 track_color,
224 fill_color,
225 handle_color,
226 handle_size,
227 live_offset](float v, std::vector<uint8_t>& out, Element& el) {
228 if (pts.size() < 2) {
229 out.clear();
230 return;
231 }
232
233 const glm::vec2 shift = live_offset ? live_offset() : glm::vec2(0.F);
234
235 const float target = std::clamp(v, 0.0F, 1.0F) * total_len;
236
237 // Arc-length interpolation is translation-invariant. It is computed
238 // in pts' own (unshifted) frame, and the shift is applied once to
239 // the result.
240 glm::vec2 handle_pos = pts.front();
241 float accumulated = 0.0F;
242 size_t split_seg = 0;
243 float split_t = 0.0F;
244 for (size_t i = 0; i < seg_lengths.size(); ++i) {
245 if (accumulated + seg_lengths[i] >= target || i + 1 == seg_lengths.size()) {
246 split_t = seg_lengths[i] > 0.0F
247 ? (target - accumulated) / seg_lengths[i]
248 : 0.0F;
249 split_t = std::clamp(split_t, 0.0F, 1.0F);
250 handle_pos = glm::mix(pts[i], pts[i + 1], split_t);
251 split_seg = i;
252 break;
253 }
254 accumulated += seg_lengths[i];
255 }
256 handle_pos += shift;
257
258 std::vector<glm::vec2> live_pts;
259 live_pts.reserve(pts.size());
260 for (const auto& p : pts)
261 live_pts.push_back(p + shift);
262
263 auto verts = Kinesis::polyline(live_pts, track_color);
264
265 auto fill = Kinesis::polyline(
266 std::span<const glm::vec2>(live_pts).subspan(0, split_seg + 1),
267 fill_color);
268 verts.insert(verts.end(), fill.begin(), fill.end());
269
270 if (split_t > 0.0F) {
271 verts.push_back({ .position = { live_pts[split_seg].x, live_pts[split_seg].y, 0.0F }, .color = fill_color });
272 verts.push_back({ .position = { handle_pos.x, handle_pos.y, 0.0F }, .color = fill_color });
273 }
274
275 write_verts(out, verts);
276
277 el.bounds_hint = aabb.translated(shift);
278 el.contains = Kinesis::stroke_bounds(live_pts, half_thickness);
279
280 if (handle_buf) {
281 Kakshya::PointVertex hv {
282 .position = { handle_pos.x, handle_pos.y, 0.0F },
283 .color = handle_color,
284 .size = handle_size,
285 };
286 std::vector<uint8_t> hbytes(sizeof(hv));
287 std::memcpy(hbytes.data(), &hv, sizeof(hv));
288 handle_buf.submit(hbytes);
289 }
290 };
291
292 return { std::move(fn),
293 Graphics::PrimitiveTopology::LINE_LIST,
294 cap,
295 drag_with<float>(std::move(project)) };
296}
glm::ivec3 max
std::vector< float > * out
AABB3D aabb(std::span< T > pts) noexcept
Axis-aligned bounding box of a PositionCarrying span.
void write_verts(std::vector< uint8_t > &out, const std::vector< V > &verts)
Write a vertex array into a GeometryFn output buffer.
Definition Geometry.hpp:35
Vertex type for line primitives (LINE_LIST / LINE_STRIP topology)
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:63
bool contains(const glm::vec3 &p) const noexcept
Definition Bounds.hpp:192
AABB3D translated(const glm::vec3 &offset) const noexcept
Definition Bounds.hpp:210
AABB3D expanded(float margin) const noexcept
Definition Bounds.hpp:215

References MayaFlux::Kinesis::AABB3D::contains(), MayaFlux::Kinesis::AABB3D::expanded(), MayaFlux::Portal::Graphics::LINE_LIST, MayaFlux::Kinesis::AABB3D::max, MayaFlux::Kinesis::AABB3D::min, out, MayaFlux::Kinesis::path_fraction(), MayaFlux::Kinesis::polyline(), MayaFlux::Kakshya::PointVertex::position, MayaFlux::Kinesis::stroke_bounds(), MayaFlux::Kinesis::AABB3D::translated(), and write_verts().

+ Here is the call graph for this function: