Release NaraMotion v0.26.0: Structure of Arrays (SoA), Path Arena Allocator, Uniform Grid Indexing, Dual-Buffer Rendering & Centroid Spring Pop-in
b516e00 verified Download c_src/naramotion_motion.cpp from ArifiIslam/NaraMotion: direct link, hf CLI and curl.
- Browser
- Download file 17.5 kB
-
https://huggingface.co/ArifiIslam/NaraMotion/resolve/main/c_src/naramotion_motion.cpp
- Command line
-
hf download hf://ArifiIslam/NaraMotion/c_src/naramotion_motion.cpp
-
curl -L -o naramotion_motion.cpp https://huggingface.co/ArifiIslam/NaraMotion/resolve/main/c_src/naramotion_motion.cpp
17.5 kB
| // Blend2D C Types & Signatures | |
| typedef uint32_t BLResult; | |
| struct BLPoint { | |
| double x; | |
| double y; | |
| }; | |
| struct BLCircle { | |
| double cx; | |
| double cy; | |
| double r; | |
| }; | |
| struct BLPathCore { | |
| void* impl; | |
| }; | |
| struct BLContextCore { | |
| void* impl; | |
| }; | |
| extern "C" { | |
| BLResult bl_path_init(BLPathCore* self); | |
| BLResult bl_path_reset(BLPathCore* self); | |
| BLResult bl_path_clear(BLPathCore* self); | |
| BLResult bl_path_destroy(BLPathCore* self); | |
| size_t bl_path_get_size(const BLPathCore* self); | |
| const BLPoint* bl_path_get_vertex_data(const BLPathCore* self); | |
| const uint8_t* bl_path_get_command_data(const BLPathCore* self); | |
| BLResult bl_path_move_to(BLPathCore* self, double x0, double y0); | |
| BLResult bl_path_line_to(BLPathCore* self, double x1, double y1); | |
| BLResult bl_path_add_path(BLPathCore* self, const BLPathCore* other, const void* range); | |
| BLResult bl_context_fill_geometry_rgba32(BLContextCore* ctx, int geom_type, const void* geom_data, uint32_t rgba32); | |
| } | |
| extern "C" { | |
| // ============================================================================ | |
| // 1. Native C++ Spring Dynamics & Cubic Bezier | |
| // ============================================================================ | |
| BL_MOTION_EXPORT double bl_motion_cubic_bezier_evaluate(double t, double x1, double y1, double x2, double y2) { | |
| if (t <= 0.0) return 0.0; | |
| if (t >= 1.0) return 1.0; | |
| double u = t; | |
| for (int i = 0; i < 8; i++) { | |
| double u2 = u * u; | |
| double u3 = u2 * u; | |
| double om_u = 1.0 - u; | |
| double om_u2 = om_u * om_u; | |
| double current_x = 3.0 * om_u2 * u * x1 + 3.0 * om_u * u2 * x2 + u3; | |
| double dx = 3.0 * om_u2 * x1 + 6.0 * om_u * u * (x2 - x1) + 3.0 * u2 * (1.0 - x2); | |
| double diff = current_x - t; | |
| if (std::abs(diff) < 1e-6) break; | |
| if (std::abs(dx) < 1e-6) break; | |
| u -= diff / dx; | |
| if (u < 0.0) u = 0.0; | |
| if (u > 1.0) u = 1.0; | |
| } | |
| double om_u = 1.0 - u; | |
| return 3.0 * om_u * om_u * u * y1 + 3.0 * om_u * u * u * y2 + u * u * u; | |
| } | |
| BL_MOTION_EXPORT double bl_motion_spring_evaluate(double t, double stiffness, double damping, double mass, double v0) { | |
| if (t <= 0.0) return 0.0; | |
| if (mass <= 0.0) mass = 1.0; | |
| double omega0 = std::sqrt(stiffness / mass); | |
| double zeta = damping / (2.0 * std::sqrt(stiffness * mass)); | |
| if (zeta < 1.0) { | |
| // Underdamped | |
| double omega_d = omega0 * std::sqrt(1.0 - zeta * zeta); | |
| double alpha = zeta * omega0; | |
| double c1 = 1.0; | |
| double c2 = (omega_d > 0.0) ? (v0 + alpha * c1) / omega_d : 0.0; | |
| double decay = std::exp(-alpha * t); | |
| double osc = c1 * std::cos(omega_d * t) + c2 * std::sin(omega_d * t); | |
| return 1.0 - decay * osc; | |
| } else if (std::abs(zeta - 1.0) < 1e-5) { | |
| // Critically damped | |
| double alpha = omega0; | |
| double c1 = 1.0; | |
| double c2 = v0 + alpha * c1; | |
| return 1.0 - (c1 + c2 * t) * std::exp(-alpha * t); | |
| } else { | |
| // Overdamped | |
| double alpha = zeta * omega0; | |
| double beta = omega0 * std::sqrt(zeta * zeta - 1.0); | |
| double r1 = -alpha + beta; | |
| double r2 = -alpha - beta; | |
| double c2 = (v0 - r1) / (r2 - r1); | |
| double c1 = 1.0 - c2; | |
| return 1.0 - (c1 * std::exp(r1 * t) + c2 * std::exp(r2 * t)); | |
| } | |
| } | |
| // ============================================================================ | |
| // 2. Native C++ Particle Simulation Engine (SIMD / Zero Python Overhead) | |
| // ============================================================================ | |
| struct BLMotionParticle { | |
| double x, y; | |
| double vx, vy; | |
| double life, max_life; | |
| double size, start_size, end_size; | |
| uint32_t start_color, end_color; | |
| double rotation, angular_velocity; | |
| }; | |
| struct BLMotionForce { | |
| int type; // 0 = gravity, 1 = drag, 2 = attractor | |
| double param1, param2, param3; | |
| }; | |
| struct BLMotionParticleSystem { | |
| size_t max_particles; | |
| std::vector<BLMotionParticle> particles; | |
| std::vector<BLMotionForce> forces; | |
| }; | |
| BL_MOTION_EXPORT void* bl_motion_ps_create(size_t max_particles) { | |
| auto ps = new BLMotionParticleSystem(); | |
| ps->max_particles = max_particles > 0 ? max_particles : 10000; | |
| ps->particles.reserve(std::min(ps->max_particles, (size_t)10000)); | |
| return (void*)ps; | |
| } | |
| BL_MOTION_EXPORT void bl_motion_ps_destroy(void* ps_ptr) { | |
| if (ps_ptr) { | |
| delete (BLMotionParticleSystem*)ps_ptr; | |
| } | |
| } | |
| BL_MOTION_EXPORT void bl_motion_ps_clear(void* ps_ptr) { | |
| if (ps_ptr) { | |
| auto ps = (BLMotionParticleSystem*)ps_ptr; | |
| ps->particles.clear(); | |
| ps->forces.clear(); | |
| } | |
| } | |
| BL_MOTION_EXPORT void bl_motion_ps_add_gravity(void* ps_ptr, double gx, double gy) { | |
| if (ps_ptr) { | |
| auto ps = (BLMotionParticleSystem*)ps_ptr; | |
| ps->forces.push_back({0, gx, gy, 0.0}); | |
| } | |
| } | |
| BL_MOTION_EXPORT void bl_motion_ps_add_drag(void* ps_ptr, double drag) { | |
| if (ps_ptr) { | |
| auto ps = (BLMotionParticleSystem*)ps_ptr; | |
| ps->forces.push_back({1, drag, 0.0, 0.0}); | |
| } | |
| } | |
| BL_MOTION_EXPORT void bl_motion_ps_add_attractor(void* ps_ptr, double x, double y, double strength) { | |
| if (ps_ptr) { | |
| auto ps = (BLMotionParticleSystem*)ps_ptr; | |
| ps->forces.push_back({2, x, y, strength}); | |
| } | |
| } | |
| static inline double _rand_range(double min_val, double max_val) { | |
| double f = (double)rand() / (double)RAND_MAX; | |
| return min_val + f * (max_val - min_val); | |
| } | |
| BL_MOTION_EXPORT void bl_motion_ps_emit( | |
| void* ps_ptr, | |
| size_t count, | |
| double x, double y, | |
| double min_speed, double max_speed, | |
| double min_angle, double max_angle, | |
| double min_life, double max_life, | |
| double start_size, double end_size, | |
| uint32_t start_color, uint32_t end_color | |
| ) { | |
| if (!ps_ptr) return; | |
| auto ps = (BLMotionParticleSystem*)ps_ptr; | |
| for (size_t i = 0; i < count; i++) { | |
| if (ps->particles.size() >= ps->max_particles) break; | |
| double speed = _rand_range(min_speed, max_speed); | |
| double angle = _rand_range(min_angle, max_angle); | |
| double life = _rand_range(min_life, max_life); | |
| BLMotionParticle p; | |
| p.x = x; | |
| p.y = y; | |
| p.vx = std::cos(angle) * speed; | |
| p.vy = std::sin(angle) * speed; | |
| p.life = life; | |
| p.max_life = life; | |
| p.size = start_size; | |
| p.start_size = start_size; | |
| p.end_size = end_size; | |
| p.start_color = start_color; | |
| p.end_color = end_color; | |
| p.rotation = 0.0; | |
| p.angular_velocity = _rand_range(-5.0, 5.0); | |
| ps->particles.push_back(p); | |
| } | |
| } | |
| BL_MOTION_EXPORT void bl_motion_ps_update(void* ps_ptr, double dt) { | |
| if (!ps_ptr || dt <= 0.0) return; | |
| auto ps = (BLMotionParticleSystem*)ps_ptr; | |
| size_t alive_count = 0; | |
| size_t n = ps->particles.size(); | |
| for (size_t i = 0; i < n; i++) { | |
| BLMotionParticle& p = ps->particles[i]; | |
| p.life -= dt; | |
| if (p.life <= 0.0) continue; | |
| // Apply forces | |
| for (const auto& f : ps->forces) { | |
| if (f.type == 0) { // Gravity | |
| p.vx += f.param1 * dt; | |
| p.vy += f.param2 * dt; | |
| } else if (f.type == 1) { // Drag | |
| double factor = std::max(0.0, 1.0 - f.param1 * dt); | |
| p.vx *= factor; | |
| p.vy *= factor; | |
| } else if (f.type == 2) { // Attractor | |
| double dx = f.param1 - p.x; | |
| double dy = f.param2 - p.y; | |
| double dist_sq = dx * dx + dy * dy + 100.0; | |
| double dist = std::sqrt(dist_sq); | |
| double accel = (f.param3 / dist_sq) * dt; | |
| p.vx += (dx / dist) * accel; | |
| p.vy += (dy / dist) * accel; | |
| } | |
| } | |
| p.x += p.vx * dt; | |
| p.y += p.vy * dt; | |
| p.rotation += p.angular_velocity * dt; | |
| double prog = 1.0 - (p.life / p.max_life); | |
| p.size = p.start_size + (p.end_size - p.start_size) * prog; | |
| if (i != alive_count) { | |
| ps->particles[alive_count] = p; | |
| } | |
| alive_count++; | |
| } | |
| ps->particles.resize(alive_count); | |
| } | |
| static inline uint32_t _lerp_rgba32(uint32_t c1, uint32_t c2, double t) { | |
| uint32_t a1 = (c1 >> 24) & 0xFF; | |
| uint32_t r1 = (c1 >> 16) & 0xFF; | |
| uint32_t g1 = (c1 >> 8) & 0xFF; | |
| uint32_t b1 = c1 & 0xFF; | |
| uint32_t a2 = (c2 >> 24) & 0xFF; | |
| uint32_t r2 = (c2 >> 16) & 0xFF; | |
| uint32_t g2 = (c2 >> 8) & 0xFF; | |
| uint32_t b2 = c2 & 0xFF; | |
| uint32_t a = (uint32_t)((double)a1 + ((double)a2 - (double)a1) * t); | |
| uint32_t r = (uint32_t)((double)r1 + ((double)r2 - (double)r1) * t); | |
| uint32_t g = (uint32_t)((double)g1 + ((double)g2 - (double)g1) * t); | |
| uint32_t b = (uint32_t)((double)b1 + ((double)b2 - (double)b1) * t); | |
| return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); | |
| } | |
| BL_MOTION_EXPORT void bl_motion_ps_render(void* ps_ptr, BLContextCore* ctx) { | |
| if (!ps_ptr || !ctx) return; | |
| auto ps = (BLMotionParticleSystem*)ps_ptr; | |
| for (const auto& p : ps->particles) { | |
| if (p.size <= 0.2) continue; | |
| double prog = 1.0 - (p.life / p.max_life); | |
| uint32_t color = _lerp_rgba32(p.start_color, p.end_color, prog); | |
| BLCircle circle; | |
| circle.cx = p.x; | |
| circle.cy = p.y; | |
| circle.r = p.size; | |
| bl_context_fill_geometry_rgba32(ctx, 4 /* BL_GEOMETRY_TYPE_CIRCLE */, &circle, color); | |
| } | |
| } | |
| BL_MOTION_EXPORT size_t bl_motion_ps_get_count(void* ps_ptr) { | |
| if (!ps_ptr) return 0; | |
| return ((BLMotionParticleSystem*)ps_ptr)->particles.size(); | |
| } | |
| // ============================================================================ | |
| // 3. Native C++ Path Line Trimming (De Casteljau Arc Length) | |
| // ============================================================================ | |
| struct Point2D { | |
| double x, y; | |
| }; | |
| BL_MOTION_EXPORT int bl_motion_trim_path(BLPathCore* src, BLPathCore* dst, double start, double end, double offset) { | |
| if (!src || !dst) return -1; | |
| size_t vtx_count = bl_path_get_size(src); | |
| if (vtx_count == 0) return 0; | |
| const BLPoint* vtx_data = bl_path_get_vertex_data(src); | |
| std::vector<Point2D> pts; | |
| pts.reserve(vtx_count); | |
| for (size_t i = 0; i < vtx_count; i++) { | |
| double x = vtx_data[i].x; | |
| double y = vtx_data[i].y; | |
| if (!std::isnan(x) && !std::isnan(y) && !std::isinf(x) && !std::isinf(y)) { | |
| pts.push_back({x, y}); | |
| } | |
| } | |
| if (pts.size() < 2) return 0; | |
| // Total length | |
| std::vector<double> lengths; | |
| lengths.reserve(pts.size()); | |
| lengths.push_back(0.0); | |
| double total_len = 0.0; | |
| for (size_t i = 1; i < pts.size(); i++) { | |
| double dx = pts[i].x - pts[i-1].x; | |
| double dy = pts[i].y - pts[i-1].y; | |
| total_len += std::hypot(dx, dy); | |
| lengths.push_back(total_len); | |
| } | |
| if (total_len <= 1e-6) return 0; | |
| double s = std::fmod(start + offset, 1.0); | |
| if (s < 0.0) s += 1.0; | |
| double e = std::fmod(end + offset, 1.0); | |
| if (e < 0.0) e += 1.0; | |
| if (std::abs(start - end) >= 1.0) { | |
| bl_path_add_path(dst, src, nullptr); | |
| return 0; | |
| } | |
| if (s > e) { | |
| // Two segments [s, 1.0] and [0.0, e] | |
| double t0_a = s * total_len, t1_a = total_len; | |
| double t0_b = 0.0, t1_b = e * total_len; | |
| auto extract_seg = [&](double target_start, double target_end) { | |
| bool started = false; | |
| for (size_t i = 1; i < pts.size(); i++) { | |
| double l0 = lengths[i-1]; | |
| double l1 = lengths[i]; | |
| if (l1 < target_start || l0 > target_end) continue; | |
| double seg_len = l1 - l0; | |
| double f0 = (seg_len > 0.0 && target_start > l0) ? (target_start - l0) / seg_len : 0.0; | |
| double f1 = (seg_len > 0.0 && target_end < l1) ? (target_end - l0) / seg_len : 1.0; | |
| f0 = std::max(0.0, std::min(1.0, f0)); | |
| f1 = std::max(0.0, std::min(1.0, f1)); | |
| double x_start = pts[i-1].x + (pts[i].x - pts[i-1].x) * f0; | |
| double y_start = pts[i-1].y + (pts[i].y - pts[i-1].y) * f0; | |
| double x_end = pts[i-1].x + (pts[i].x - pts[i-1].x) * f1; | |
| double y_end = pts[i-1].y + (pts[i].y - pts[i-1].y) * f1; | |
| if (!started) { | |
| bl_path_move_to(dst, x_start, y_start); | |
| started = true; | |
| } | |
| bl_path_line_to(dst, x_end, y_end); | |
| } | |
| }; | |
| extract_seg(t0_a, t1_a); | |
| extract_seg(t0_b, t1_b); | |
| return 0; | |
| } | |
| double target_start = s * total_len; | |
| double target_end = e * total_len; | |
| bool started = false; | |
| for (size_t i = 1; i < pts.size(); i++) { | |
| double l0 = lengths[i-1]; | |
| double l1 = lengths[i]; | |
| if (l1 < target_start || l0 > target_end) continue; | |
| double seg_len = l1 - l0; | |
| double f0 = (seg_len > 0.0 && target_start > l0) ? (target_start - l0) / seg_len : 0.0; | |
| double f1 = (seg_len > 0.0 && target_end < l1) ? (target_end - l0) / seg_len : 1.0; | |
| f0 = std::max(0.0, std::min(1.0, f0)); | |
| f1 = std::max(0.0, std::min(1.0, f1)); | |
| double x_start = pts[i-1].x + (pts[i].x - pts[i-1].x) * f0; | |
| double y_start = pts[i-1].y + (pts[i].y - pts[i-1].y) * f0; | |
| double x_end = pts[i-1].x + (pts[i].x - pts[i-1].x) * f1; | |
| double y_end = pts[i-1].y + (pts[i].y - pts[i-1].y) * f1; | |
| if (!started) { | |
| bl_path_move_to(dst, x_start, y_start); | |
| started = true; | |
| } | |
| bl_path_line_to(dst, x_end, y_end); | |
| } | |
| return 0; | |
| } | |
| // ============================================================================ | |
| // 4. Native C++ Chaikin's Corner Smoothing Algorithm | |
| // ============================================================================ | |
| BL_MOTION_EXPORT int nm_motion_chaikin_smooth( | |
| const double* in_pts, size_t in_count, | |
| double* out_pts, size_t* out_count, | |
| int iterations, double ratio, int closed | |
| ) { | |
| if (!in_pts || in_count < 2 || !out_pts || !out_count) return -1; | |
| std::vector<std::pair<double, double>> current(in_count); | |
| for (size_t i = 0; i < in_count; i++) { | |
| current[i] = {in_pts[i * 2], in_pts[i * 2 + 1]}; | |
| } | |
| double r = std::max(0.01, std::min(0.49, ratio)); | |
| double inv_r = 1.0 - r; | |
| for (int it = 0; it < iterations; it++) { | |
| std::vector<std::pair<double, double>> next; | |
| size_t n = current.size(); | |
| if (!closed) { | |
| next.push_back(current[0]); | |
| for (size_t i = 0; i < n - 1; i++) { | |
| auto p0 = current[i]; | |
| auto p1 = current[i + 1]; | |
| next.push_back({inv_r * p0.first + r * p1.first, inv_r * p0.second + r * p1.second}); | |
| next.push_back({r * p0.first + inv_r * p1.first, r * p0.second + inv_r * p1.second}); | |
| } | |
| next.push_back(current[n - 1]); | |
| } else { | |
| for (size_t i = 0; i < n; i++) { | |
| auto p0 = current[i]; | |
| auto p1 = current[(i + 1) % n]; | |
| next.push_back({inv_r * p0.first + r * p1.first, inv_r * p0.second + r * p1.second}); | |
| next.push_back({r * p0.first + inv_r * p1.first, r * p0.second + inv_r * p1.second}); | |
| } | |
| } | |
| current = std::move(next); | |
| } | |
| *out_count = current.size(); | |
| for (size_t i = 0; i < current.size(); i++) { | |
| out_pts[i * 2] = current[i].first; | |
| out_pts[i * 2 + 1] = current[i].second; | |
| } | |
| return 0; | |
| } | |
| // ============================================================================ | |
| // 5. Native C++ Interpolation & Range Remapping | |
| // ============================================================================ | |
| BL_MOTION_EXPORT double nm_motion_interpolate( | |
| double value, | |
| const double* in_range, const double* out_range, size_t count, | |
| int extrapolate_left, int extrapolate_right | |
| ) { | |
| if (!in_range || !out_range || count < 2) return value; | |
| if (value < in_range[0]) { | |
| if (extrapolate_left == 0) return out_range[0]; | |
| if (extrapolate_left == 2) return value; | |
| double dx = in_range[1] - in_range[0]; | |
| if (dx == 0.0) return out_range[0]; | |
| return out_range[0] + ((value - in_range[0]) / dx) * (out_range[1] - out_range[0]); | |
| } | |
| if (value > in_range[count - 1]) { | |
| if (extrapolate_right == 0) return out_range[count - 1]; | |
| if (extrapolate_right == 2) return value; | |
| double dx = in_range[count - 1] - in_range[count - 2]; | |
| if (dx == 0.0) return out_range[count - 1]; | |
| return out_range[count - 1] + ((value - in_range[count - 1]) / dx) * (out_range[count - 1] - out_range[count - 2]); | |
| } | |
| size_t seg = 0; | |
| for (size_t i = 0; i < count - 1; i++) { | |
| if (value >= in_range[i] && value <= in_range[i + 1]) { | |
| seg = i; | |
| break; | |
| } | |
| } | |
| double dx = in_range[seg + 1] - in_range[seg]; | |
| if (dx == 0.0) return out_range[seg]; | |
| double t = (value - in_range[seg]) / dx; | |
| return out_range[seg] + t * (out_range[seg + 1] - out_range[seg]); | |
| } | |
| } // extern "C" | |