import { solveFK } from './rig/rigMath.js';
import { DEFAULT_POSE, DEFAULT_RIG } from './rig/rigTypes.js';

// ── Constants ───────────────────────────────────────────────────
const DEFAULT_PHYSICS = {
    gravity: { x: 0, y: 980 },
    damping: 0.99,
    iterations: 4,
    timestep: 1 / 60,
};
// ── Verlet Engine ───────────────────────────────────────────────
function createParticle(x, y, mass = 1, pinned = false) {
    return { pos: { x, y }, prev: { x, y }, acc: { x: 0, y: 0 }, mass, pinned };
}
function stepParticles(particles, config) {
    const dt2 = config.timestep * config.timestep;
    for (const p of particles) {
        if (p.pinned)
            continue;
        const vx = (p.pos.x - p.prev.x) * config.damping;
        const vy = (p.pos.y - p.prev.y) * config.damping;
        p.prev.x = p.pos.x;
        p.prev.y = p.pos.y;
        p.pos.x += vx + (config.gravity.x + p.acc.x) * dt2;
        p.pos.y += vy + (config.gravity.y + p.acc.y) * dt2;
        p.acc.x = 0;
        p.acc.y = 0;
    }
}
function solveConstraints(particles, constraints, iterations) {
    for (let i = 0; i < iterations; i++) {
        for (const c of constraints) {
            const a = particles[c.a];
            const b = particles[c.b];
            const dx = b.pos.x - a.pos.x;
            const dy = b.pos.y - a.pos.y;
            const dist = Math.hypot(dx, dy) || 0.001;
            const diff = (c.restLength - dist) / dist * c.stiffness;
            const ox = dx * diff * 0.5;
            const oy = dy * diff * 0.5;
            if (!a.pinned) {
                a.pos.x -= ox;
                a.pos.y -= oy;
            }
            if (!b.pinned) {
                b.pos.x += ox;
                b.pos.y += oy;
            }
        }
    }
}
// ── Ragdoll ─────────────────────────────────────────────────────
const RAGDOLL_BONES = [
    { name: "pelvis", length: 0 },
    { name: "spineLower", parent: "pelvis", length: 0.1 },
    { name: "spineUpper", parent: "spineLower", length: 0.1 },
    { name: "neck", parent: "spineUpper", length: 0.05 },
    { name: "head", parent: "neck", length: 0.08 },
    { name: "lShoulder", parent: "spineUpper", length: 0.06 },
    { name: "lUpperArm", parent: "lShoulder", length: 0.12 },
    { name: "lForearm", parent: "lUpperArm", length: 0.1 },
    { name: "lHand", parent: "lForearm", length: 0.05 },
    { name: "rShoulder", parent: "spineUpper", length: 0.06 },
    { name: "rUpperArm", parent: "rShoulder", length: 0.12 },
    { name: "rForearm", parent: "rUpperArm", length: 0.1 },
    { name: "rHand", parent: "rForearm", length: 0.05 },
    { name: "lThigh", parent: "pelvis", length: 0.15 },
    { name: "lShin", parent: "lThigh", length: 0.14 },
    { name: "lFoot", parent: "lShin", length: 0.05 },
    { name: "rThigh", parent: "pelvis", length: 0.15 },
    { name: "rShin", parent: "rThigh", length: 0.14 },
    { name: "rFoot", parent: "rShin", length: 0.05 },
];
/** Create a ragdoll simulation from a rig pose. */
function createRagdoll(cfg) {
    const { size } = cfg;
    const particles = [];
    const constraints = [];
    const boneMap = {};
    // Create particles for each bone joint
    const fk = solveFK(DEFAULT_RIG, DEFAULT_POSE, size);
    for (const bone of RAGDOLL_BONES) {
        const solved = fk[bone.name];
        const x = solved ? solved.x : size / 2;
        const y = solved ? solved.y : size / 2;
        boneMap[bone.name] = particles.length;
        particles.push(createParticle(x, y));
    }
    // Create distance constraints along bone chain
    for (const bone of RAGDOLL_BONES) {
        if (!bone.parent)
            continue;
        const ai = boneMap[bone.parent];
        const bi = boneMap[bone.name];
        const restLen = bone.length * size;
        constraints.push({ a: ai, b: bi, restLength: restLen, stiffness: 0.8 });
    }
    return { particles, constraints, boneMap };
}
/** Step the ragdoll simulation forward by one frame. */
function stepRagdoll(sim, config = {}) {
    const cfg = { ...DEFAULT_PHYSICS, ...config };
    stepParticles(sim.particles, cfg);
    solveConstraints(sim.particles, sim.constraints, cfg.iterations);
}
/** Extract a RigPose from the current ragdoll particle positions. */
function ragdollToPose(sim) {
    const pose = {};
    for (const bone of RAGDOLL_BONES) {
        if (!bone.parent)
            continue;
        const ai = sim.boneMap[bone.parent];
        const bi = sim.boneMap[bone.name];
        const a = sim.particles[ai];
        const b = sim.particles[bi];
        const angle = Math.atan2(b.pos.y - a.pos.y, b.pos.x - a.pos.x) * 180 / Math.PI;
        pose[bone.name] = angle;
    }
    return pose;
}
/** Apply a force impulse to a ragdoll particle. */
function applyImpulse(sim, boneName, force) {
    const idx = sim.boneMap[boneName];
    if (idx === undefined)
        return;
    const p = sim.particles[idx];
    p.acc.x += force.x / p.mass;
    p.acc.y += force.y / p.mass;
}
// ── Cloth Simulation ────────────────────────────────────────────
/** Create a cloth simulation grid. */
function createCloth(cfg) {
    const { width, height, cols, rows, pinTop = true, stiffness = 0.9, } = cfg;
    const particles = [];
    const constraints = [];
    const spacingX = width / cols;
    const spacingY = height / rows;
    // Create particle grid
    for (let r = 0; r <= rows; r++) {
        for (let c = 0; c <= cols; c++) {
            const x = c * spacingX;
            const y = r * spacingY;
            const pinned = pinTop && r === 0;
            particles.push(createParticle(x, y, 1, pinned));
        }
    }
    const idx = (r, c) => r * (cols + 1) + c;
    // Structural constraints (horizontal + vertical)
    for (let r = 0; r <= rows; r++) {
        for (let c = 0; c <= cols; c++) {
            if (c < cols) {
                constraints.push({ a: idx(r, c), b: idx(r, c + 1), restLength: spacingX, stiffness });
            }
            if (r < rows) {
                constraints.push({ a: idx(r, c), b: idx(r + 1, c), restLength: spacingY, stiffness });
            }
        }
    }
    // Shear constraints (diagonals for stability)
    const diagLen = Math.hypot(spacingX, spacingY);
    for (let r = 0; r < rows; r++) {
        for (let c = 0; c < cols; c++) {
            constraints.push({ a: idx(r, c), b: idx(r + 1, c + 1), restLength: diagLen, stiffness: stiffness * 0.5 });
            constraints.push({ a: idx(r, c + 1), b: idx(r + 1, c), restLength: diagLen, stiffness: stiffness * 0.5 });
        }
    }
    return { particles, constraints, cols, rows };
}
/** Step the cloth simulation forward by one frame. */
function stepCloth(sim, config = {}) {
    const cfg = { ...DEFAULT_PHYSICS, ...config };
    stepParticles(sim.particles, cfg);
    solveConstraints(sim.particles, sim.constraints, cfg.iterations);
}
/** Get cloth vertex positions as a flat array [x0,y0, x1,y1, ...]. */
function clothVertices(sim) {
    const out = [];
    for (const p of sim.particles) {
        out.push(p.pos.x, p.pos.y);
    }
    return out;
}
/** Pin or unpin a cloth particle by grid position. */
function pinClothParticle(sim, col, row, pinned) {
    const idx = row * (sim.cols + 1) + col;
    if (idx >= 0 && idx < sim.particles.length) {
        sim.particles[idx].pinned = pinned;
    }
}

export { applyImpulse, clothVertices, createCloth, createRagdoll, pinClothParticle, ragdollToPose, stepCloth, stepRagdoll };
//# sourceMappingURL=physics.js.map
