godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.12k stars 69 forks source link

Add an alternative mode to calculate center of mass of a 2D polygon #10410

Open EGA-SUPREMO opened 1 month ago

EGA-SUPREMO commented 1 month ago

Describe the project you are working on

A worms Armageddon clone, my maps feature polygons that need accurate center of mass to work properly

Describe the problem or limitation you are having in your project

when working with polygons, by default it places the center of mass to 0, 0. however since I clip polygons with Geometry2D.clip_polygons() I get polygons with a offset position of over 700 in the X and Y coordinates.

Example here's the value of a typical polygon PackedVector2Array in my game [[(706, 135), (719, 135), (731, 132), (731, 160), (734, 237), (735, 237), (736, 247), (737, 247), (744, 255), (744, 283), (748, 356), (745,...]

I can't use a center of mass of 0, 0. I have to write a function to get a better center of mass. It's unlikely that I will be the only one with this problem, and if Godot would handle it out the box would save time and I could be rest assured that will be free of bugs and efficient!

Describe the feature / enhancement and how it helps to overcome the problem or limitation

The default can remain 0, 0, however I propose another mode for RigidBody2D.center_of_mass_mode, can be called RigidBody2D.CENTER_OF_MASS_MODE_CENTROID or something im bad at naming and english

That mode would put the center of mass on the centroid / geometric center of the polygon, which assuming uniform mass density, it coincides with the real center of mass.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Add RigidBody2D.CENTER_OF_MASS_MODE_CENTROID and internally it uses:

func calculate_area(mesh_vertices: PackedVector2Array) -> float:
    var result := 0.0
    var num_vertices := mesh_vertices.size()

    for q in range(num_vertices):
        var p = (q - 1 + num_vertices) % num_vertices
        result += mesh_vertices[q].cross(mesh_vertices[p])

    return result * 0.5

func calculate_centroid(mesh_vertices: PackedVector2Array) -> Vector2:
    var centroid = Vector2()
    var area = calculate_area(mesh_vertices)
    var num_vertices = mesh_vertices.size()
    var factor = 0.0

    for q in range(num_vertices):
        var p = (q - 1 + num_vertices) % num_vertices
        factor = mesh_vertices[q].cross(mesh_vertices[p])
        centroid += (mesh_vertices[q] + mesh_vertices[p]) * factor

    centroid /= (6.0 * area)
    return centroid.abs()

If this enhancement will not be used often, can it be worked around with a few lines of script?

Can be worked around with a Physics library, but out of the box is more convenient

Is there a reason why this should be core and not an add-on in the asset library?

To save time

EGA-SUPREMO commented 1 month ago

BTW if implemented can be calculate_area() be a public function? I use that to calculate the mass

Jesper-Johansson commented 1 month ago

Yes please, using Polygon2D with physics is a pain. Even if you just have asymmetrical polygons the center of mass becomes wrong.