Search code examples
c++vectorfloating-pointoperator-overloading

Error: No operator '+=' matches float and Vector2 in fluid simulation property gradient calculation


I'm trying to follow Sebastian Lague's Fluid simulation video in c++ and OpenGL. I'm at the point where we calculate a propertyGradient value (timestamp is 14:15). I'll try to provide all the code that is relevant further down below.

float CalculatePropertyGradient(Vector2 samplePoint, const std::vector<Vector2>& positions, const std::vector<float>& particleProperties, float smoothingRadius, float mass)
{
    float propertyGradient = 0.0f;
    DensityCalculator calculator(positions, smoothingRadius); 
    for (int i = 0; i < positions.size(); i++)
    {
        float dst = (positions[i] - samplePoint).magnitude();
        Vector2 dir = (positions[i] - samplePoint) / dst;
        float slope = SmoothingKernelDerivative(dst, smoothingRadius);
        float density = calculator.CalculateDensity(positions[i]); 
        Vector2 scaledDir = dir * particleProperties[i];
        propertyGradient += -(scaledDir) * slope *  mass / density;
    }
    return propertyGradient;
}

But I'm getting a syntax error in the line:

propertyGradient += -(scaledDir) * slope * mass / density; under += operator that my Vector2 class doesn't support this operation involving float and vector2

My vector2 class:

class Vector2 {
public:
    float X;
    float Y;

    Vector2(float x, float y) : X(x), Y(y) {}

    float magnitude() const {
        return sqrt(X * X + Y * Y);
    }

    Vector2 operator-(const Vector2& other) const {
        return Vector2(X - other.X, Y - other.Y);
    }

    Vector2 operator/(const float& scalar) const {
        return Vector2(X / scalar, Y / scalar);
    }

    Vector2 operator*(const float& right) const {
        return Vector2(X * right, Y * right);
    }

    Vector2 operator-(const float& scalar) const {
        return Vector2(X - scalar, Y - scalar);
    }

    Vector2 operator*(const Vector2& other) const {
        return Vector2(X * other.X, Y * other.Y);
    }
    Vector2 Zero() {
        return Vector2(0.0f, 0.0f);
    }
    
    Vector2 operator-() const {
        return Vector2(-X, -Y);
    }
    Vector2& operator+=(const float& scalar) {
        X += scalar;
        Y += scalar;
        return *this;
    }
};

My density class:

class DensityCalculator {
private:
    std::vector<float> densities;
    std::vector<Vector2> positions;
    float smoothingRadius;

public:
    DensityCalculator(const std::vector<Vector2>& positions, float smoothingRadius)
        : positions(positions), smoothingRadius(smoothingRadius) {}

    void PreCalculateDensities() {
        densities.clear();
        for (const auto& position : positions) {
            densities.push_back(CalculateDensity(position));
        }
    }

    float CalculateDensity(Vector2 position) const {
        float density = 0;
        const float mass = 1;

        for (const auto& p : positions) {
            float dst = (p - position).magnitude();
            if (dst <= smoothingRadius) {
                float influence = SmoothingKernel(smoothingRadius, dst);
                density += mass * influence;
            }
        }
        return density;

    }

    float GetDensity(int index) const {
        return densities[index];
    }
};

SmoothingKernel and SmoothingKernelDerivative functions:

float SmoothingKernel(float radius, float dst)
{
    if (dst < radius)
    {
        float volume = M_PI * pow(radius, 8) / 4;
        float v = std::max(radius * radius - dst * dst, 0.0f);
        return ((v * v * v) / volume);
    }
    else
    {
        return 0;
    }
}
static float SmoothingKernelDerivative(float dst, float radius)
{
    if (dst >= radius)
        return 0;
    float f = radius * radius - dst * dst;
    float scale = -24 / (M_PI * pow(radius, 8));
    return scale * dst * f * f;
}

Here's the function call in my main function:

for (int i = 0; i < rows; ++i)
{
    for (int j = 0; j < cols; ++j) {
        float x = distX(mt);
        float y = distY(mt);
        balls.emplace_back(x, y, 0.0, 0.0, radius);

        // Calculate the value of ExampleFunction at the particle's location and store it in particleProperties
        float propertyValue = ExampleFunction(Vector2(x, y));
        particleProperties.push_back(propertyValue);
    }
}

Where ExampleFunction is just

static float ExampleFunction(Vector2(pos))
{
    return cos(pos.Y - 3 + sin(pos.X));
}

I've tried declaring the operator += of vector2 as acting on a const object as suggested by another post but i get an error that X and Y must be a modifiable value.


Solution

  • The problem is that currently the overloaded operator+=(const float&) that you've provided takes the right hand operand which is a float instead of Vector2.

    Now, the expression scaledDir * mass is of type Vector2 which means that you're trying to use operator+= with right hand operand of type Vector2. But since you haven't provided any such opertor+= that takes right hand operand of type Vector2, you get the mentioned error.


    To solve this, just provide an overload that takes an operand of type Vector2. One way of doing this is as shown below:

    //----------------------------vvvvvvv---------->changed float to Vector2
        Vector2& operator+=(const Vector2& vec) {
            X += vec.X;
            Y += vec.Y;
            return *this;
        }
    

    Working demo