I wonder if there is a way to create loops or arcs around cornered shapes. The corners here have been extended to create a balloon shape so that the curve should be harmonious, is there a way to place an arc between the end points of the extended curves? Similar to this exemple from silhuette studio called
“smart loops”
If I only want the loops to be on the outside of the shape here, I used region difference to get rid of the internal loops, however, the curve is not connected anymore, how can I remove the inner loops while being able to reconnect it into one curve?
You can differentiate between convex and concave corners of a closed polyline by calculating the two-dimensional vector cross product of adjacent edges as vectors of a given corner.
The cross product returns a scalar value that if negative means that the corner is concave, and if positive, it is convex.
I failed to do this in Grasshopper with vanilla components because of too much tree shenanigans. My brain is currently fried by COVID-19.
However, I succeeded with a GHPython script, which loops through the polyline vertices, which is much simpler than doing this with trees, where everything happens at once, so to speak:
prev_pt = rg.Point3d.Unset # prev point to connect to
crv = rg.PolyCurve()
for i in range(P.Count-1):
curr_vtx = P[i]
vec1 = P[(i-1)%(P.Count-1)] - curr_vtx
vec1.Unitize()
vec2 = P[(i+1)%(P.Count-1)] - curr_vtx
vec2.Unitize()
cross = rg.Vector3d.CrossProduct(vec2, vec1)
if cross.Z > 0.0: # convex vertex/corner
start_pt = curr_vtx - vec1 * O
end_pt = curr_vtx - vec2 * O
if prev_pt != rg.Point3d.Unset:
ln = rg.Line(prev_pt, start_pt)
crv.Append(ln)
arc = rg.Arc(start_pt, -vec1, end_pt)
crv.Append(arc)
prev_pt = end_pt
else: # concave vertex/corner
if prev_pt == rg.Point3d.Unset:
prev_pt = curr_vtx
continue
ln = rg.Line(prev_pt, curr_vtx)
crv.Append(ln)
prev_pt = curr_vtx
crv.Append(rg.Line(crv.PointAtEnd, crv.PointAtStart)) # close the polycurve
# Output
C = crv
I don’t have Rhino 7 installed any more, but you can try the following to make it work.
You can simply create a GHPython component in Grasshopper in Rhino 7.
Rename inputs x and y to P and O. Then set the type hint of P to “Polyline” and that of O (i.e. offset) to “float” by right-clicking on the relevant input and selecting “Type Hint > …”.
Then rename output a to C, double-click the GHPython component to open the script editor, and copy and paste the following code inside:
import Rhino.Geometry as rg
import Grasshopper as gh
if P is not None:
if O is not None and O > 0.0:
prev_pt = rg.Point3d.Unset # prev point to connect to
crv = rg.PolyCurve()
for i in range(P.Count-1):
curr_vtx = P[i]
vec1 = P[(i-1)%(P.Count-1)] - curr_vtx
vec1.Unitize()
vec2 = P[(i+1)%(P.Count-1)] - curr_vtx
vec2.Unitize()
cross = rg.Vector3d.CrossProduct(vec2, vec1)
if cross.Z > 0.0: # convex vertex/corner
start_pt = curr_vtx - vec1 * O
end_pt = curr_vtx - vec2 * O
if prev_pt != rg.Point3d.Unset:
ln = rg.Line(prev_pt, start_pt)
crv.Append(ln)
arc = rg.Arc(start_pt, -vec1, end_pt)
crv.Append(arc)
prev_pt = end_pt
else: # concave vertex/corner
if prev_pt == rg.Point3d.Unset:
prev_pt = curr_vtx
continue
ln = rg.Line(prev_pt, curr_vtx)
crv.Append(ln)
prev_pt = curr_vtx
crv.Append(rg.Line(crv.PointAtEnd, crv.PointAtStart)) # close the polycurve
# Output
C = crv
else:
# Output
C = P
else:
ghenv.Component.AddRuntimeMessage(
gh.Kernel.GH_RuntimeMessageLevel.Warning,
"Parameter P failed to collect data"
)