// ----------------------------------------------------------------------------------------------
bool PositionBasedFluids::solveDensityConstraint(
	const unsigned int particleIndex,
	const unsigned int numberOfParticles,
	const Vector3r x[],	
	const Real mass[],
	const Vector3r boundaryX[],
	const Real boundaryPsi[],
	const unsigned int numNeighbors,
	const unsigned int neighbors[],
	const Real density0,
	const bool boundaryHandling,
	const Real lambda[],
	Vector3r &corr)
{
	// Compute position correction
	corr.setZero();
	for (unsigned int j = 0; j < numNeighbors; j++)
	{
		const unsigned int neighborIndex = neighbors[j];
		if (neighborIndex < numberOfParticles)		// Test if fluid particle
		{
			const Vector3r gradC_j = -mass[neighborIndex] / density0 * CubicKernel::gradW(x[particleIndex] - x[neighborIndex]);
			corr -= (lambda[particleIndex] + lambda[neighborIndex]) * gradC_j;
		}
		else if (boundaryHandling)
		{
			// Boundary: Akinci2012
			const Vector3r gradC_j = -boundaryPsi[neighborIndex - numberOfParticles] / density0 * CubicKernel::gradW(x[particleIndex] - boundaryX[neighborIndex - numberOfParticles]);
			corr -= (lambda[particleIndex]) * gradC_j;
		}
	}

	return true;
}
bool  PositionBasedElasticRod::ProjectEdgeConstraints(
    const Vector3r& pA, const float wA,
    const Vector3r& pB, const float wB,
    const Vector3r& pG, const float wG,
    const float edgeKs, const float edgeRestLength, const float ghostEdgeRestLength,
    Vector3r& corrA, Vector3r&  corrB, Vector3r&  corrC)
{
    corrA.setZero();
    corrB.setZero();
    corrC.setZero();

    //Edge distance constraint
    Vector3r dir = pA - pB;
    float len = dir.norm();

    float wSum = wA + wB;
    if (len > EPSILON && wSum > EPSILON)
    {
        Vector3r dP = (1.0f / wSum) * (len - edgeRestLength) * (dir / len) * edgeKs;

        corrA -= dP * wA;
        corrB += dP * wB;
        corrC = Vector3r(0, 0, 0);
    }

    //Bisector constraint
    Vector3r pm = 0.5f * (pA + pB);
    Vector3r p0p2 = pA - pG;
    Vector3r p2p1 = pG - pB;
    Vector3r p1p0 = pB - pA;
    Vector3r p2pm = pG - pm;
    float lambda;

    wSum = wA * p0p2.squaredNorm() + wB * p2p1.squaredNorm() + wG * p1p0.squaredNorm();
    if (wSum > EPSILON)
    {
        lambda = p2pm.dot(p1p0) / wSum * edgeKs;

        corrA -= p0p2 * lambda * wA;
        corrB -= p2p1 * lambda * wB;
        corrC -= p1p0 * lambda * wG;
    }

    ////Ghost-Edge constraint
    wSum = 0.25f * wA + 0.25f * wB + 1.0f * wG;

    if (wSum > EPSILON)
    {
        //need to use updated positions
        pm = 0.5f * (pA + corrA + pB + corrB);
        p2pm = pG + corrC - pm;

        float p2pm_mag = p2pm.norm();
        p2pm *= 1.0f / p2pm_mag;

        lambda = (p2pm_mag - ghostEdgeRestLength) / wSum * edgeKs;

        corrA += 0.5f * wA * lambda * p2pm;
        corrB += 0.5f * wB * lambda * p2pm;
        corrC -= 1.0f * wG * lambda * p2pm;
    }


    return true;
}