import { Vector3 } from '@zephyr3d/base';

/**
 * Base class for spring bone constraints.
 *
 * @remarks
 * Constraints are applied after physics simulation to enforce
 * additional rules between spring bones.
 *
 * @public
 */ class SpringBoneConstraint {
}
/**
 * Distance constraint between two spring bones.
 *
 * @remarks
 * Maintains a distance range between two spring bones.
 * Useful for preventing penetration and excessive stretching between
 * adjacent chains (e.g., skirt panels).
 *
 * @public
 */ class DistanceConstraint extends SpringBoneConstraint {
    /** First spring bone */ _bone1;
    /** Second spring bone */ _bone2;
    /** Minimum distance (prevents penetration) */ _minDistance;
    /** Maximum distance (prevents excessive stretching) */ _maxDistance;
    /** Constraint stiffness (0-1, how strongly to enforce) */ _stiffness;
    /**
   * Create a distance constraint.
   *
   * @param bone1 - First spring bone
   * @param bone2 - Second spring bone
   * @param minDistance - Minimum distance (default: 0.01)
   * @param maxDistance - Maximum distance (default: 0.2)
   * @param stiffness - Constraint stiffness (0-1, default: 0.5)
   */ constructor(bone1, bone2, minDistance = 0.01, maxDistance = 0.2, stiffness = 0.5){
        super();
        this._bone1 = bone1;
        this._bone2 = bone2;
        this._minDistance = Math.max(0, minDistance);
        this._maxDistance = Math.max(this._minDistance, maxDistance);
        this._stiffness = Math.max(0, Math.min(1, stiffness));
    }
    /**
   * Get/set minimum distance.
   */ get minDistance() {
        return this._minDistance;
    }
    set minDistance(value) {
        this._minDistance = Math.max(0, value);
        this._maxDistance = Math.max(this._minDistance, this._maxDistance);
    }
    /**
   * Get/set maximum distance.
   */ get maxDistance() {
        return this._maxDistance;
    }
    set maxDistance(value) {
        this._maxDistance = Math.max(this._minDistance, value);
    }
    /**
   * Get/set stiffness.
   */ get stiffness() {
        return this._stiffness;
    }
    set stiffness(value) {
        this._stiffness = Math.max(0, Math.min(1, value));
    }
    /**
   * Apply the distance constraint.
   */ apply() {
        const pos1 = this._bone1.currentPosition;
        const pos2 = this._bone2.currentPosition;
        // Calculate current distance
        const direction = Vector3.sub(pos2, pos1, new Vector3());
        const distance = direction.magnitude;
        if (distance < 0.000001) {
            return; // Bones are at same position
        }
        const directionNorm = Vector3.scale(direction, 1 / distance, new Vector3());
        let correction = 0;
        if (distance < this._minDistance) {
            // Too close, push apart
            correction = (this._minDistance - distance) * 0.5 * this._stiffness;
        } else if (distance > this._maxDistance) {
            // Too far, pull together
            correction = (this._maxDistance - distance) * 0.5 * this._stiffness;
        } else {
            // Within range, no correction needed
            return;
        }
        // Apply correction to both bones (equal and opposite)
        const offset = Vector3.scale(directionNorm, correction, new Vector3());
        pos1.addBy(Vector3.scale(offset, -1, new Vector3()));
        pos2.addBy(offset);
    }
}
/**
 * Circular constraint for skirt-like structures.
 *
 * @remarks
 * Maintains distance constraints between adjacent bones in a circular arrangement.
 * Automatically creates distance constraints between neighboring chains.
 *
 * @public
 */ class CircularConstraint extends SpringBoneConstraint {
    /** Array of spring bone chains arranged in a circle */ _chains;
    /** Distance constraints between adjacent chains */ _constraints;
    /** Minimum distance between adjacent bones */ _minDistance;
    /** Maximum distance between adjacent bones */ _maxDistance;
    /** Constraint stiffness */ _stiffness;
    /**
   * Create a circular constraint.
   *
   * @param chains - Array of spring bone chains arranged in a circle
   * @param minDistance - Minimum distance between adjacent bones (default: 0.05)
   * @param maxDistance - Maximum distance between adjacent bones (default: 0.15)
   * @param stiffness - Constraint stiffness (0-1, default: 0.5)
   */ constructor(chains, minDistance = 0.05, maxDistance = 0.15, stiffness = 0.5){
        super();
        this._chains = chains;
        this._minDistance = minDistance;
        this._maxDistance = maxDistance;
        this._stiffness = stiffness;
        this._constraints = [];
        this._buildConstraints();
    }
    /**
   * Build distance constraints between adjacent chains.
   */ _buildConstraints() {
        this._constraints = [];
        if (this._chains.length < 2) {
            return; // Need at least 2 chains
        }
        // For each level (depth in the chain)
        const maxDepth = Math.min(...this._chains.map((chain)=>chain.length));
        for(let depth = 0; depth < maxDepth; depth++){
            // Connect adjacent chains
            for(let i = 0; i < this._chains.length; i++){
                const nextIndex = (i + 1) % this._chains.length; // Wrap around
                const bone1 = this._chains[i][depth];
                const bone2 = this._chains[nextIndex][depth];
                if (bone1 && bone2) {
                    const constraint = new DistanceConstraint(bone1, bone2, this._minDistance, this._maxDistance, this._stiffness);
                    this._constraints.push(constraint);
                }
            }
        }
    }
    /**
   * Rebuild constraints (call after modifying chains).
   */ rebuild() {
        this._buildConstraints();
    }
    /**
   * Apply all distance constraints.
   */ apply() {
        for (const constraint of this._constraints){
            constraint.apply();
        }
    }
    /**
   * Get all distance constraints.
   */ get constraints() {
        return this._constraints;
    }
}

export { CircularConstraint, DistanceConstraint, SpringBoneConstraint };
//# sourceMappingURL=spring_bone_constraint.js.map
