10
$\begingroup$

enter image description here

On the left is a standard instance on points, on the right a sketch of what I want to achieve, where the instances scale based on their distance to the closest other instance.

How can I achieve something like this with Geometry Nodes? Is it possible?

I've been looking into the Geometry Proximity node but that seems to depend on some other object, which is probably not what I need to solve the problem.

Thanks :)

$\endgroup$
2
  • $\begingroup$ Personally, I like this question very much, but it also raises a number of other questions for me: Is the number of objects fixed, or does it result from the scaling of the objects? Do the objects always bump into a certain number of their neighbors, or does this result from chance? My first approach would be to use a Voronoi texture as a base, but that probably wouldn't achieve the desired result. Can you be a little more specific about the requirement? $\endgroup$
    – quellenform
    Mar 22, 2022 at 19:36
  • $\begingroup$ maybe you could fake it by going by layers, placing big ones first and so on, deleting faces on each iteration... tried something a bit like that here but with vertex weights $\endgroup$
    – alambre
    Mar 24, 2022 at 19:07

1 Answer 1

6
$\begingroup$

Note: A real "Circle Packing" cannot be realized with Geometry Nodes alone, because essential nodes like e.g. Loops are missing.

Therefore, there is no way around an additional tool if you really want to solve it well.

I offer two solutions here, a simple one (which only partially leads to a useful result), and one that does exactly what you are looking for with the help of an external Python library.

Solution 1 (the simple variant):

Here, only by distorting a grid and the resulting area of a face, a possible value for a radius is derived. This is visually close to the solution and may be sufficient in some cases, but it is not really a nice solution and certainly not a "real" circle packing.

Circle Packing - Fake

Solution 2 (with the help of Python):

If you want to implement real Circle Packing, you probably won't be happy with Geometry Nodes alone. Basically, you would need to be able to measure the distance of each circle center to any other point (within a certain radius?), and probably repeat this process several times to get a reliable result.

As I said, this is actually what is meant by true "Circle Packing".

Presumably, though, it would be possible to program the algorithm in Python, and that would generate for you the circle centers and the radii of the circles needed for Geometry Nodes.

Search for "Circle Packing Delaunay Triangulation".

...and that's what I did, and I found a solution.

Thanks to a sufficiently stable but very lean implementation in Python, which I personally liked a lot and which was published by mhtchan under the MIT license on Github, I was now able to integrate this into Blender.

You can find the project here: packcircles

This Python library is simply passed an array with Radii, and it returns you the coordinates of the circles, which you then actually just continue to use in Blender with your geometry nodes.

The way to get there is as follows:

  1. To be able to use additional Python libraries in Blender, you have to get them into your system (Windows). The easiest way might be to install the library with pip.

    First open a terminal (cmd.exe) and enter the following (replace the example path used here with the one of your Blender installation!!):

    cd "C:\Program Files\Blender Foundation\Blender 3.1\3.1\python\bin"
    python -m ensurepip
    python -m pip install packcircles
    

    Another method to install packages directly from Blender is this one: Using 3rd party Python modules

    After installation the library is installed in the following path and usable in Blender: C:\Program Files\Blender Foundation\Blender 3.1\3.1\python\lib\site-packages\

  2. Next Start Blender create a new script and make sure the library is loaded and available:

    import packcircles
    
  3. If everything worked out, you only need to create the following script in the editor.

    Replace the upper variables with the values you want and execute the script:

    from bpy import context, data, ops
    import packcircles as pc
    import numpy
    
    # Define your own values here
    min = 0.5
    max = 1.0
    count = 50
    objectName = 'CirclePack'
    
    # Generate some random radii in the specified range
    radii = numpy.random.uniform(min, max, size=(1, count))
    
    # Run the CirclePacking (contains the coordinates and the radius)
    points = list(pc.pack(radii[0]))
    
    # Check if an object already exists and otherwise create a new one
    if not context.scene.objects.get(objectName):
        # Create a curve (starting with a single point, because we set the points later)
        ops.curve.simple(
            align = 'WORLD',
            location = (0, 0, 0),
            rotation = (0, 0, 0),
            Simple_Type = 'Point',
            use_cyclic_u = False
        )
        # Rename the object so that a easy repetition of this process is possible
        for obj in context.selected_objects:
            obj.name = objectName
            obj.data.name = objectName
    
    # Enter edit mode
    ops.object.mode_set(mode = 'EDIT')
    
    # Set curve parameters
    ops.curve.select_all(action = 'SELECT')
    ops.curve.delete(type = 'VERT')
    ops.curve.simple(
        Simple_Type = 'Line',
        shape = '3D',
        outputType = 'POLY',
        use_cyclic_u = False
    )
    
    # Subdivide the curve so that the number of points equals the number of circles
    ops.curve.subdivide(number_cuts = count - 2)
    
    # Set curve as active object
    curve = context.active_object
    
    # Iterate through the circlepacking coordinates
    for i, coordinates in enumerate(points):
        x, y, r = coordinates
        curve.data.splines[0].points[i].co = (x, y, 0, 1)
        curve.data.splines[0].points[i].radius = r
    
    # Leave edit mode   
    ops.object.mode_set(mode = 'OBJECT')
    
    

    Here an array with Radii is passed to the CirclePacking at the beginning, and you get back the coordinates for it.

    To make it easy to process all this in Geometry Nodes, a curve is created with the name you specified, whose points correspond to the positions of the individual circles. In order to be able to position the respective circle in the Geometry Nodes in the appropriate size, a value for the radius is also given to the individual points.

    If you do not rename the created object, you can repeat the process as often as you like.

    All existing points will be removed from the curve and new points will be created randomly.

  4. If the script ran without errors, a new curve will be created. It looks a bit strange, so it needs Geometry Nodes now.

    Circle Packing - Curve

    Add a modifier of type "Geometry Nodes" to the curve and do what ever you want to do with it or use the nodes I attached here in the blend file.

    Circle Packing - Circles

    Essentially, I don't do much more in this example either. Only circles are instantiated at the points of the curve. Depending on how you additionally influence the radii, different results arise here. Of course, you can also instantiate and scale all unit circles first, and then convert the circles into meshes using the Curve to Mesh node.

    Circle Packing - Python

    And here is the blend file:

$\endgroup$
4
  • $\begingroup$ These python scripts are on Git hub, so someone proficient in python could adapt them. Maybe it could be made into a bountied python question? $\endgroup$ Apr 23, 2022 at 13:25
  • $\begingroup$ @common_goldfish UPDATED! ...is that the answer you were looking for? $\endgroup$
    – quellenform
    Apr 23, 2022 at 21:25
  • $\begingroup$ For me it is impressive. Thank you for putting it into Blender. $\endgroup$ Apr 23, 2022 at 23:48
  • $\begingroup$ Amazing! Thanks a lot :) $\endgroup$
    – torels
    May 3, 2022 at 10:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .