#include #include #include #include #include #include #define PI 3.14159265358979323846 #ifdef _WIN32 #define BL_MOTION_EXPORT __declspec(dllexport) #else #define BL_MOTION_EXPORT __attribute__((visibility("default"))) #endif // 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 particles; std::vector 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 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 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> 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> 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"