Hi all,
I love the idea of panel surfaces, and therefore have used them in the past. For my current task, I am using PanelSurface.ByQuads to create a number of quads on a surface, and I want points in each of those quads at a random position. So, I first need these quads as actual surfaces. Unfortunately, I can’t get them directly (right?).
My workaround in the past was (thanks again Jakob ):
Use PanelSurface.GetPolygon to get the polygons of the quads, and then use the Surface.ByPatch or Curve.Patch node to create those surfaces.
Unfortunately, those created surfaces can be trimmed, even though the polygons are super simple. So when I now want to create random points in the UV space of 0-1, points can end up outside of the quad:
Ah the classic ‘random point on a trimmed surface’ issue but scaled to panelization…
Do you want one random point on the surface? Or another number?
There are a few concepts you can put in play here.
If you always have quads or other polygons you can get the perimeter curves, remove two curves forming opposite sides (so 1 and 3 in a square, 3 and 6 on a hex, etc.), build two poly curves using PolyCurve.ByGroupedCurves, and then use a Surface.ByLoft to generate an untrimmed surface which will have normalized U parameters along each edge and normalized V parameters between each edge (U and V might be backwards there). The drawback is it is a lot of new geometry to generate which isn’t ideal from a speed perspective, and the resulting forms risk having a different surface form so you might need to pull the point onto the original surface.
You can get the closest point on the surface to the point and use that. Simple and fast, but the drawback is that random distribution risks being less random here as the trim warps around the surface - you could have a sizable percentage (I can think of regular polygons where 30% parameters are outside the trim) and high frequencies of points on an edge which might not be what you want.
You can adjust your parameter spacing to be less likely to occur outside the bounds. Instead of using a range of 0..1 use 0.25..0.75 or 0.0625..0.9375 or some other ‘centered’ range which omits edge cases. This runs the risk of things looking too centered in some cases as the range of separation for your randomized points gets tighter.
You can get an isocurve of a U parameter of the surface, intersect that with the surface, and get a point at a random parameter of the intersected curve. This is complete for always being in the surface and always getting a good random, but handling the edge cases is difficult and time consuming. Offhand three to deal with: What if the intersection returns a point? What if the intersection returns a list of curves instead of one? What if the resulting curves are non-uniform?
You can use imperative code block, recursive custom nodes, or Python to perform the random sampling repeatedly if you don’t get a point on the surface. This risks being the slowest (in fact you can get stuck in an infinite loop) and requires maintaining custom code (which may or may not be of use elsewhere) in each environment you have, but if you write the code well and keep a good maintenance plan it is likely the best path forward and what I have done in the past - I don’t recall if we used this or tree branching for the design script example on imperative code but you might want to start with that session of the Dynamo Office Hour if you go this route.
I want just one random point on each surface.
I already thought about your proposed approaches 2 and 3, but decided not to go down this road for the exact reasons you mention yourself in those points, the points wouldn’t be completely random any longer.
To be honest, 1, and 5 sound more complicated (and probably less performant?) than my current approach?
But anyway, reading your post, it seems at least I didn’t miss anything obvious, which would have benn easier.
So, thanks nevertheless!
Five can actually be more performant than it at first appears for one point as the likelihood of selecting a point not on the surface is pretty slim to start with (looks to be about 17% of the original surface was outside the shape limits) and the likelihood of rolling back to back ones on a regular dice are pretty slim (something like 2%) and even rarer for 3 or 4 consecutive rolls (0.5% and 0.007%). The act of pulling a point by parameter and testing for geometry intersection is exceedingly fast. Meanwhile surface constructors are just fast and the fact that you are passing functions three times (slowest way to accomplish a task but often necessary) means you’d likely benefit from a ‘try up to 1 times and then force it with a closest point to if still null’.
Let me see if I can get a sample built up for you to play with.