Free-form & flat quads with Rhino.Python
Fossile-like structures made of flat quad panels. Panels can be assembled along their folded sides. The script works with free from surfaces (trimmed or not_see conditional minimum distance between any point evaluated within surface domain and the base surface_as per Python Primer example)
CODE:
import rhinoscriptsyntax as rs
import random #import random module
def SurfaceQuad():
idSurface=rs.GetObject("Select Surface",8,True,False)
if not idSurface:return
intCountU = rs.GetInteger("Number of panels in U direction",20,10)
if not intCountU:return
intCountV = rs.GetInteger("Number of panels in V direction",7,5)
if not intCountV:return
uDomain=rs.SurfaceDomain(idSurface,0)
vDomain=rs.SurfaceDomain(idSurface,1)
uStep=uDomain[1] - uDomain[0]
uStep=uStep / intCountU
vStep=vDomain[1] - vDomain[0]
vStep=vStep / intCountV
#Tolerance in between panels
dblTolU = uStep/100
dblTolV = dblTolU*intCountU/intCountV
rs.EnableRedraw(False)
for v in rs.frange(vDomain[0],vDomain[1]-vStep-vDomain[0],vStep):
for u in rs.frange (uDomain[0],uDomain[1]-uStep-uDomain[0],uStep):
#Evaluate panel coordinates on idSurface
ptA=rs.EvaluateSurface(idSurface,u+dblTolU,v+dblTolV)
ptB=rs.EvaluateSurface(idSurface,u+uStep-dblTolU,v+dblTolV)
ptC=rs.EvaluateSurface(idSurface,u+uStep-dblTolU,v+vStep-dblTolV)
ptD=rs.EvaluateSurface(idSurface,u+dblTolU,v+vStep-dblTolV)
#evaluate normal vectors at A,B,C,D on the surface
vecNormA=rs.SurfaceNormal(idSurface,[u,v])
vecNormB=rs.SurfaceNormal(idSurface,[u+uStep,v])
vecNormC=rs.SurfaceNormal(idSurface,[u+uStep,v+vStep])
vecNormD=rs.SurfaceNormal(idSurface,[u,v+vStep])
#Side fixings projecting height
#SurfaceNormal vectors are unitized by default_so we scale them according to the length of each panel
dblPanelLength=rs.Distance(ptA,ptB)/10
vecNormA=rs.VectorScale(vecNormA,dblPanelLength)
vecNormB=rs.VectorScale(vecNormB,dblPanelLength)
vecNormC=rs.VectorScale(vecNormC,dblPanelLength)
vecNormD=rs.VectorScale(vecNormD,dblPanelLength)
#Condition below prevent from adding a panel when surface is trimmed
if rs.Distance(ptA,rs.BrepClosestPoint(idSurface,ptA)[0])< 0.1:
strInfiniPlane=rs.PlaneFromPoints(ptA,ptB,ptD)
if vecNormC:
transPtC=rs.PointAdd(ptC,vecNormC)
arrLine=[ptC,transPtC]
interSecPtC=rs.LinePlaneIntersection(arrLine,strInfiniPlane)
#Typical panel function call:
PanelA(ptA,ptB,interSecPtC,ptD,vecNormA,vecNormB,vecNormC,vecNormD)
def PanelA(a,b,c,d,vecA,vecB,vecC,vecD):
crv1=rs.AddLine(a,b)
crv2=rs.AddLine(b,c)
crv3=rs.AddLine(c,d)
crv4=rs.AddLine(d,a)
#Radius for filletted edges of panels
dblRadius=rs.Distance(a,c)
dblRadius=dblRadius/20
#Filleted curves & curve endpoints
crvFillet1=rs.AddFilletCurve(crv4,crv1,dblRadius)
crvFilletPts1=rs.CurveFilletPoints(crv4,crv1,dblRadius)
crvFillet2=rs.AddFilletCurve(crv1,crv2,dblRadius)
crvFilletPts2=rs.CurveFilletPoints(crv1,crv2,dblRadius)
crvFillet3=rs.AddFilletCurve(crv2,crv3,dblRadius)
crvFilletPts3=rs.CurveFilletPoints(crv2,crv3,dblRadius)
crvFillet4=rs.AddFilletCurve(crv3,crv4,dblRadius)
crvFilletPts4=rs.CurveFilletPoints(crv3,crv4,dblRadius)
#Panel nodes distribution and joining curve
PtPane1=crvFilletPts1[1]
PtPane2=crvFilletPts2[0]
PtPane3=crvFilletPts2[1]
PtPane4=crvFilletPts3[0]
PtPane5=crvFilletPts3[1]
PtPane6=crvFilletPts4[0]
PtPane7=crvFilletPts4[1]
PtPane8=crvFilletPts1[0]
newCrv1=rs.AddCurve([PtPane1,PtPane2])
newCrv2=rs.AddCurve([PtPane3,PtPane4])
newCrv3=rs.AddCurve([PtPane5,PtPane6])
newCrv4=rs.AddCurve([PtPane7,PtPane8])
crvFace=rs.JoinCurves([crvFillet1,newCrv2,crvFillet2,newCrv3,crvFillet3,newCrv4,crvFillet4,newCrv1])
#Side Fixing Plates
NewPtPane1=rs.PointAdd(PtPane1,rs.VectorDivide((rs.VectorAdd(vecA,vecB)),2))
NewPtPane2=rs.PointAdd(PtPane2,rs.VectorDivide((rs.VectorAdd(vecA,vecB)),2))
NewPtPane3=rs.PointAdd(PtPane3,rs.VectorDivide((rs.VectorAdd(vecB,vecC)),2))
NewPtPane4=rs.PointAdd(PtPane4,rs.VectorDivide((rs.VectorAdd(vecB,vecC)),2))
NewPtPane5=rs.PointAdd(PtPane5,rs.VectorDivide((rs.VectorAdd(vecC,vecD)),2))
NewPtPane6=rs.PointAdd(PtPane6,rs.VectorDivide((rs.VectorAdd(vecC,vecD)),2))
NewPtPane7=rs.PointAdd(PtPane7,rs.VectorDivide((rs.VectorAdd(vecD,vecA)),2))
NewPtPane8=rs.PointAdd(PtPane8,rs.VectorDivide((rs.VectorAdd(vecD,vecA)),2))
Offnewcrv1=rs.AddCurve([NewPtPane1,NewPtPane2])
Offnewcrv2=rs.AddCurve([NewPtPane3,NewPtPane4])
Offnewcrv3=rs.AddCurve([NewPtPane5,NewPtPane6])
Offnewcrv4=rs.AddCurve([NewPtPane7,NewPtPane8])
arrDivOffCrv1=rs.DivideCurve(Offnewcrv1,5,False)
ShortOffEdge1=rs.AddCurve((arrDivOffCrv1[2],arrDivOffCrv1[3]))
arrDivOffCrv2=rs.DivideCurve(Offnewcrv2,5,False)
ShortOffEdge2=rs.AddCurve((arrDivOffCrv2[2],arrDivOffCrv2[3]))
arrDivOffCrv3=rs.DivideCurve(Offnewcrv3,5,False)
ShortOffEdge3=rs.AddCurve((arrDivOffCrv3[2],arrDivOffCrv3[3]))
arrDivOffCrv4=rs.DivideCurve(Offnewcrv4,5,False)
ShortOffEdge4=rs.AddCurve((arrDivOffCrv4[2],arrDivOffCrv4[3]))
arrSideFix1=[ShortOffEdge1,newCrv1]
if arrSideFix1 is not None:
SideFix1=rs.AddLoftSrf(arrSideFix1)
arrSideFix2=[ShortOffEdge2,newCrv2]
if arrSideFix2 is not None:
SideFix2=rs.AddLoftSrf(arrSideFix2)
arrSideFix3=[ShortOffEdge3,newCrv3]
if arrSideFix3 is not None:
SideFix3=rs.AddLoftSrf(arrSideFix3)
arrSideFix4=[ShortOffEdge4,newCrv4]
if arrSideFix4 is not None:
SideFix4=rs.AddLoftSrf(arrSideFix4)
#Panel base plate outline (with filletted corners)
arrFace=rs.JoinCurves([crvFillet1,newCrv2,crvFillet2,newCrv3,crvFillet3,newCrv4,crvFillet4,newCrv1])
#Petal pattern
dblSwitch=random.random()#random number to generate random patterns
strDiagonal=rs.AddCurve([a,c])
arrPetalPtBase=rs.CurveMidPoint(strDiagonal)
#Pattern threshold
CrvPetalA=rs.AddCurve([arrPetalPtBase,PtPane1,PtPane8,arrPetalPtBase],3)
CrvPetalB=rs.AddCurve([arrPetalPtBase,PtPane2,PtPane3,arrPetalPtBase],3)
CrvPetalC=rs.AddCurve([arrPetalPtBase,PtPane4,PtPane5,arrPetalPtBase],3)
CrvPetalD=rs.AddCurve([arrPetalPtBase,PtPane6,PtPane7,arrPetalPtBase],3)
if dblSwitch > 0.7:
rs.SelectObjects([arrFace,CrvPetalA,CrvPetalB,CrvPetalC,CrvPetalD])
rs.Command ("_PlanarSrf")
rs.Command ("_SelNone")
else:
rs.SelectObjects([arrFace,CrvPetalA])
rs.Command ("_PlanarSrf")
rs.Command ("_SelNone")
#Brackets
arrBracketBaseLinePt4=rs.DivideCurve(newCrv4,3)
arrBracketBaseLinePt2=rs.DivideCurve(newCrv2,3)
arrBracketBase1=rs.DivideCurve(rs.AddCurve([arrBracketBaseLinePt4[2],arrBracketBaseLinePt2[1]]),7)
FoldBracket1=rs.AddSrfPt([arrBracketBase1[3],arrBracketBase1[4],arrDivOffCrv1[2],arrDivOffCrv1[3]])
arrBracketBaseLinePt1=rs.DivideCurve(newCrv1,3)
arrBracketBaseLinePt3=rs.DivideCurve(newCrv3,3)
arrBracketBase2=rs.DivideCurve(rs.AddCurve([arrBracketBaseLinePt1[2],arrBracketBaseLinePt3[1]]),7)
FoldBracket2=rs.AddSrfPt([arrBracketBase2[3],arrBracketBase2[4],arrDivOffCrv2[2],arrDivOffCrv2[3]])
arrBracketBase3=rs.DivideCurve(rs.AddCurve([arrBracketBaseLinePt2[2],arrBracketBaseLinePt4[1]]),7)
FoldBracket3=rs.AddSrfPt([arrBracketBase3[3],arrBracketBase3[4],arrDivOffCrv3[2],arrDivOffCrv3[3]])
arrBracketBase4=rs.DivideCurve(rs.AddCurve([arrBracketBaseLinePt3[2],arrBracketBaseLinePt1[1]]),7)
FoldBracket4=rs.AddSrfPt([arrBracketBase4[3],arrBracketBase4[4],arrDivOffCrv4[2],arrDivOffCrv4[3]])
#Clean up
rs.DeleteObjects([Offnewcrv1,Offnewcrv2,Offnewcrv3,Offnewcrv4,ShortOffEdge1,ShortOffEdge2,ShortOffEdge3,ShortOffEdge4])
rs.DeleteObjects([crv1,crv2,crv3,crv4,newCrv1,newCrv2,newCrv3,newCrv4,crvFillet1,crvFillet2,crvFillet3,crvFillet4,strDiagonal])
SurfaceQuad()







leave a comment