My attempt at this problem is quite similar to Omar Ahmad's answer in that I calculate the spline parameter of the intersections – i.e. their positions along the spline's length in the range of zero to one – and use this value for the placement.
(Visible hollow copy only for demonstration purposes. Origial filled box used for calculations is hidden)
As a first step, I modify the Raycast loop of the node tree for the intersections to return the parameter and a boolean that gives information about the angle of normal and segment, and through that, whether the intersection is an entry into or an exit from the volume. In my setup, False
means entry and True
means exit.
The additional parameter spline length comes from a Get Spline Length node of the spline.
With an expression node list(zip(x, y))
I convert the two lists into one list of pairs, one for each intersection. For example
[(False, 0.2),
(True, 0.5),
(False, 0.6),
(True, 0.9)]
To handle the spline's ends, I check for the first and the last point on the spline whether they are in the volume (BVH Tree > Is Inside Volume) and add a expression that will later interpret these points as additional entries or exits.
If there is always at least one intersection, one could invert the first and last boolean of the list, but checking Is Inside Volume also works when there is no intersection.
This will end up as something like this:
[(True, 0),
(False, 0.2),
(True, 0.5),
(False, 0.6),
(True, 0.9)
(False, 1)]
Similarly to the two slices vectors[:-1]
and vectors[1:]
in the tree to find the intersections, I create two slices tuples[:-1]
and tuples[1:]
and feed them into another loop.
The Create Splines loop checks whether each segment is inside of the volume based on the supplied boolean values, adjust the parameters of start and end by a given value to “prevent” (in some cases) the objects to appear directly at the wall or even clip into it, and check whether the adjusted segment is even long enough to hold any object.
While Omar Ahmad at this point continues to work with the parameters, I return a trimmed copy of the original spline (iff the spline is inside the volume and the segment is long enough).
The list of splines is fed into a final loop sample splines, along with a float number to set the average number of items to place per unit. From the loop's Evaluate Spline node we can extract location, tangents for the rotation etc.
Using the segments' length and the ceiling function gives a rather regular distribution of objects, but it complicates things if you want a fixed number of objects. Maybe algorithms commonly used to assign seats to politicians after elections like the Sainte-Laguë method can be used, considering the lengths of the segments. Or you simply tweak the average until you're happy with the total. :P
The number of placed objects depends on the length of the segments:
The distance of the first objects from each wall, or the average number of objects per unit can be adjusted, though the distance from a wall is calculated along the spline and might be misleading (a.k.a. wrong) for shallow angles:
As mentioned above, the node tree should work for no intersections as well. For splines completely enclosed in the volume (with objects) or splines completely outside (no objects):
Unfortunately, it won't work for cyclic splines.
N.B.: By default, the resolution of the Evaluate Spline nodes, which can be set in the advanced node settings in the properties panel of the node, is quite low. This resulted in an uneven distribution for me. Increasing the resolution solved this problem.
My .blend file for Animation Nodes v2.1: