generative modeling for Rhino
I'd like to find points on a curve at which the tangent line is parallel to a given direction. I call this operation 'tangent matching'. I'll briefly explain it, show a fun application of it, and then ask how to do it in Grasshopper.
Let d be non-zero vector in the xy plane, and let c be a closed, planar, g1 continuous spline, with non-zero tangent vector everywhere. Then we have that there exists at least two points c(t) and c(s) on c such that c'(t) and c'(s) is parallel to d. These are the points we wish to find by tangent matching.
In rhino proper you can construct these points using the line tool:
Create a curve c and a line l in the chosen direction. Use the
! _Line _Perpendicular _2Curves
command to create lines perpendicular to both c and l. By some geometry, the endpoints on the curve are our two required points. The figure shows an example of this construction on an ellipse.
For general splines, these points need not exist. Indeed, there may be any number of them, depending on how the the spline twists. A further direction is this: given a plane p, find those points c(t) on c for which c'(t) is parallel to p.
A direct application of tangent matching lets us make high quality developable surfaces between planar guide rails, such as this ribbon:
Which unrolls into the following shape:
To do this in Grasshopper, I'd like to implement a network for tangent matching. Does any approach come to mind? I've attempted to duplicate the 'line perpendicular to two curve' operation in terms of Grasshopper components with no luck so far. Insights on this or other approaches would be welcome.
If it is not possible with default components, might there be any components available in the community? Finally, if there is no way to do this yet, would anyone be interested in mentoring my attempt to code this up?
Thanks a lot!
Well, it seems like you're talking to Martin so that's a good start!
Also there were ZipShape discussions which may or may not be helpful...
And Christoph's work with D2P
This is a pretty cool problem. I have a solution that I think works fairly well - see if it is accurate enough for your needs.
Here's how it works. If you rotate your first drawing such that the line is along the X axis, you can understand the curve as a graph, and the points you're looking for as its local maxima and minima. You can find these local extremes by locating the points where the first derivative of the graph is equal to 0.
The definition achieves this by dividing the curve into many points, locating the tangent at each of those points, and then essentially creating an oriented graph along the axis of the line. I'm not actually graphing the true derivative, but the graph of the dot product of the tangent and the vector perpendicular to the line, which will also equal 0 (intersect the line) wherever the derivative is 0. A curve-line intersection finds all these points. The last step is to map back from those intersections to the original curve. I'm relying on the fact that the parameterization of the curve (after rebuilding it with N points) and its dot-product graph (also constructed from N points) are consistent. This is the one assumption I'm not 100% sure will always hold true, although it seems to work fairly well testing it out. If it fails you could also try constructing perpendicular lines at the intersection points and find where they intersect the original curve, and throw out the points where the tangent isn't parallel.
Hope this is helpful to you!
Thanks for the responses! Taz, I love the ZipShape technology. Isn't it one of those mehtods that is just impossible to unsee? Martin's blog is definitely one of the reasons I decided to check out Grasshopper for the developable surface problem. Thanks for connecting me to those discussions on it. Those images are fantastic!
Andrew, I took inspiration from your approach and my ran with it! This works for my curve set:
Here's what's going on. We can see the curve C as a graph above some plane P, and we'd like to find the local maxima, ie, it's critical points. We segment C into smooth, convex segments and use the Extremes component to locate the global maxima of each segment. This can include the endpoints of segments as artifacts, which we filter out. Now we have only genuine critical points of C with respect to P. We also check the endpoints that we filtered out earlier. If a tangent at one these points is perpendicular to P's normal vector, the point is in fact critical, and is included in the final point stream.
The pro of this is that it uses Extremes as the 'workhorse', and this component is likely to use an efficient and numerically robust zero/maxima finding algorithm, such as bisection, or Brent's method.
The con is that it requires curves to be segmented at their inflection points, and for each segment to turn less than one full rotation around any line whatsoever that is parallel to P. Note that segmenting curves in these ways are interesting and useful problems in their own rights. Without this kind of segmentation, my approach will in general fail to extract some critical points on splines with inflection points, and on curves such as helixes or spirals. However, arc-segmented polylines naturally have these properties, and these are enough for my purposes for now.
In a different direction, I think I will try the method of bisection to extend your curve subdividision / dot product graphing approach. Essentially we'd sample the curve, use your dot product graph construction to find regions that we know will contain a zero crossing, subsample on those regions, and repeat the process until we've hit floating point precision for the result. This probably takes around 20 to 50 iterations. But it would give the highest quality results possible, and likely be far more stable than any dubious, inflection-point-splitting, wild-turn-avoiding, subtle-bug-inducing approach that I'll come up with otherwise, hah.
Andrew, thanks for the code and inspiration!