Geometría
Geometry
openNURBS kernel:
objects in rhino:
import rhinoscriptsyntax as rs
def displayobjectattributes(object_id):
source = "By Layer", "By Object", "By Parent"
data = []
data.append( "Object attributes for :"+str(object_id) )
data.append( "Description: " + rs.ObjectDescription(object_id))
data.append( "Layer: " + rs.ObjectLayer(object_id))
#data.append( "LineType: " + rs.ObjectLineType(object_id))
#data.append( "LineTypeSource: " + rs.ObjectLineTypeSource(object_id))
data.append( "MaterialSource: " + str(rs.ObjectMaterialSource(object_id)))
name = rs.ObjectName(object_id)
if not name: data.append("<Unnamed object>")
else: data.append("Name: " + name)
groups = rs.ObjectGroups(object_id)
if groups:
for i,group in enumerate(groups):
data.append( "Group(%d): %s" % i+1, group )
else:
data.append("<Ungrouped object>")
s = ""
for line in data: s += line + "\n"
rs.EditBox(s, "Object attributes", "RhinoPython") # shows data
if __name__=="__main__":
id = rs.GetObject()
displayobjectattributes(id)
points and pointclouds:
points: list of coordinate -values in x, y, z or u, v-. Three coordinates in 3d world space, 2 coordinates (x, y or u, v) over surface or one dimensional space referred to as 'parameters' (t or p).
# Conversion from r1 to r3 space
import rhinoscriptsyntax as rs
def main():
curve_id = rs.GetObject("Select a curve to sample", 4, True, True)
if not curve_id: return
rs.EnableRedraw(False)
t = 0
while t<=1.0:
addpointat_r1_parameter(curve_id,t)
t+=0.002
rs.EnableRedraw(True)
def addpointat_r1_parameter(curve_id, parameter):
domain = rs.CurveDomain(curve_id)
r1_param = domain[0] + parameter*(domain[1]-domain[0]) # translate the unitized r1 coordinate into actual domain coordinates
r3point = rs.EvaluateCurve(curve_id, r1_param) # evaluate the curve at the specified parameter takes an r1 coord and returns an r3 coordinate
if r3point:
point_id = rs.AddPoint(r3point)
rs.ObjectColor(point_id, parametercolor(parameter)) # set the custom colour
def parametercolor(parameter):
'''change color gradually 0 to one, blue to red'''
red = 255 * parameter
if red<0: red=0
if red>255: red=255
return (red,0,255-red)
if __name__=="__main__":
main()
curve structure: nurb curve (degree 3) standard, cluster control points and weighted control points.
import rhinoscriptsyntax as rs
def main():
'''if the projection distance exceed 1 unit a line and a point will be added'''
surface_id = rs.GetObject("Select a surface to sample", 8, True)
if not surface_id: return
curve_id = rs.GetObject("Select a curve to measure", 4, True, True)
if not curve_id: return
points = rs.DivideCurve(curve_id, 500) # r3 coordinate on the curve
rs.EnableRedraw(False)
for point in points:
evaluatedeviation(surface_id, 1.0, point)
rs.EnableRedraw(True)
def evaluatedeviation( surface_id, threshold, sample ):
r2point = rs.SurfaceClosestPoint(surface_id, sample) # return an array of 2 doubles representing the r2 point on the surface which is closest to the sample point.
if not r2point: return
r3point = rs.EvaluateSurface(surface_id, r2point[0], r2point[1]) # translates r2 parameter into r3 world coordinates
if not r3point: return # compute distance and add geometry if necessary.
deviation = rs.Distance(r3point, sample)
if deviation<=threshold: return
rs.AddPoint(sample)
rs.AddLine(sample, r3point)
if __name__=="__main__":
main()
lines and polylines:
definition: polylines are essentially the same as point-lists, treat as a series. (degree 1).
def PolylineLength(arrVertices):
# Compute the length of a polyline point-array
PolylineLength = 0.0
for i in range(0,len(arrVertices)-1):
PolylineLength = PolylineLength + rs.Distance(arrVertices[i], arrVertices[i+1])
def SubDividePolyline(arrV):
'''Subdivide a polyline by adding extra vertices halfway between all existing vertices'''
arrSubD = []
for i in range(0, len(arrV)-1):
# copy the original vertex location
arrSubD.append(arrV[i])
# compute the average of the curren t vertex and the next one
arrSubD.append(
[arrV[i][0] + arrV[i+1][0]] / 2.0,
[arrV[i][1] + arrV[i+1][1]] / 2.0,
[arrV[i][2] + arrV[i+1][2]] / 2.0)
# copy the last vertex (this is skipped by the loop)
arrSubD.append(arrV[len(arrV)])
return arrSubD
def getr2pathonsurface(surface_id, segments, prompt1, prompt2):
# promt the user for the A point
start_point = rs.GetPointOnSurface(surface_id, prompt1)
if not start_point: return
# prompt the user for the B point
end_point = rs.GetPointOnSurface(surface_id, prompt2)
if not end_point: return
# project A and B onto the surface to get the respective r2 coordinates
if rs.Distance(start_point, end_point)==0.0: return
uva = rs.SurfaceClosestPoint(surface_id, start_point)
uvb = rs.SurfaceClosestPoint(surface_id, end_point)
path = []
for i in range(segments):
t = i / segments
u = uva[0] + t*(uvb[0] - uva[0])
v = uva[1] + t*(uvb[1] - uva[1])
pt = rs.EvaluateSurface(surface_id, u, v) # takes u and v and spits out a 3d world coordinate, r2 -> r3
path.append(pt)
return path
def projectpolyline(vertices, surface_id):
polyline = []
for vertex in vertices:
pt = rs.BrepClosestPoint(surface_id, vertex)
if pt: polyline.append(pt[0])
return polyline
def smoothpolyline(vertices):
smooth = []
smooth.append(vertices[0])
for i in range(1, len(vertices)-1):
prev = vertices[i-1]
this = vertices[i]
next = vertices[i+1]
pt = (prev+this+next) / 3.0
smooth.append(pt)
smooth.append(vertices[len(vertices)-1])
return smooth
def geodesicfit(vertices, surface_id, tolerance):
length = polylinelength(vertices)
while True:
vertices = smoothpolyline(vertices)
vertices = projectpolyline(vertices, surface_id)
newlength = polylinelength(vertices)
if abs(newlength-length)<tolerance: return vertices
length = newlength
def geodesiccurve():
# get the surface to be used in the geodesic routine
surface_id = rs.GetObject("Select surface for geodesic curve solution", 8, True, True)
if not surface_id: return
# store polyline vertices
vertices = getr2pathonsurface(surface_id, 10, "Start of geodes curve", "End of geodes curve")
if not vertices: return
tolerance = rs.UnitAbsoluteTolerance() / 10
length = 1e300
newlength = 0.0
while True:
print("Solving geodesic fit for %d samples" % len(vertices))
vertices = geodesicfit(vertices, surface_id, tolerance)
newlength = polylinelength(vertices)
if abs(newlength-length)<tolerance: break
if len(vertices)>1000: break # safety-switch, we don't want to our curve to become too dense
vertices = subdividepolyline(vertices)
length = newlength
rs.AddPolyline(vertices)
print "Geodesic curve added with length: ", newlength
planes:
definition: array of one point and three vectors. The axis vectors must be unitized, all axis perpendicular to each other. The x and y axis are ordered anti-clockwise.
ptOrigin = rs.GetPoint("Plane origin")
ptX = rs.GetPoint("Plane X-axis", ptOrigin)
ptY = rs.GetPoint("Plane Y-axis", ptOrigin)
dX = rs.Distance(ptOrigin, ptX)
dY = rs.Distance(ptOrigin, ptY)
arrPlane = rs.PlaneFromPoints(ptOrigin, ptX, ptY)
rs.AddPlaneSurface(arrPlane, 1.0, 1.0)
rs.AddPlaneSurface(arrPlane, dX, dY)
idSurface = rs.GetObject("Surface to frame", 8, True, True)
intCount = rs.GetInteger("Number of iterations per direction", 20, 2)
# determine the domain of the surface in u and v direction and we derive the required stepsize from those limits.
uDomain = rs.SurfaceDomain(idSurface, 0)
vDomain = rs.SurfaceDomain(idSurface, 1)
uStep = (uDomain[1] - uDomain[0]) / intCount
vStep = (vDomain[1] - vDomain[0]) / intCount
rs.EnableRedraw(False)
# main structure of the 2 simensional iteration. iterate through all columns and inside every column iterate through all rows
for u in range(uDomain[0],uDomain[1], uStep):
for v in range(vdomain[0],vDomain[1],vStep):
pt = rs.EvaluateSurface(idSurface, [u, v])
if rs.Distance(pt, rs.BrepClosestPoint(idSurface, pt)[0]) < 0.1: # prevent the script from adding planes in cut-away areas
srfFrame = rs.SurfaceFrame(idSurface, [u, v]) # returns a unitized frame
rs.AddPlaneSurface(srfFrame, 1.0, 1.0) # populate the surface with so-called surface frames
rs.EnableRedraw(True)
circles, ellipses and arcs:
circles: mathematical primitives stored parametrically. Example ON_Circle (plane, radius).
def DistributeCirclesOnSphere():
# collect variables and set thresholds
sphere_radius = rs.GetReal("Radius of sphere", 10.0, 0.01)
if not sphere_radius: return
circle_radius = rs.GetReal("Radius of circles", 0.05*sphere_radius, 0.001, 0.5*sphere_radius)
if not circle_radius: return
# number of circles from pole to pole.
vertical_count = int( (math.pi*sphere_radius)/(2*circle_radius) )
rs.EnableRedraw(False)
# denote angles in spherical space
phi = -0.5*math.pi
phi_step = math.pi/vertical_count
while phi<0.5*math.pi:
horizontal_count = int( (2*math.pi*math.cos(phi)*sphere_radius)/(2*circle_radius) ) # calculate how many circles we can fit also calculate the length of the path around the sphere.
if horizontal_count==0: horizontal_count=1 # in the poles only 1 circle
theta = 0
theta_step = 2*math.pi/horizontal_count
while theta<2*math.pi-1e-8: # same as before but with different stepsize and a different numeric range
# store variables
circle_center = (sphere_radius*math.cos(theta)*math.cos(phi),
sphere_radius*math.sin(theta)*math.cos(phi), sphere_radius*math.sin(phi)) # standard way of translating the spherical coordinates into Cartesja coordinates x, y, z.
circle_normal = rs.PointSubtract(circle_center, (0,0,0)) # normal of a sphere (inverted vector from that point to the center of the sphere)
circle_plane = rs.PlaneFromNormal(circle_center, circle_normal)
rs.AddCircle(circle_plane, circle_radius)
theta += theta_step
phi += phi_step
rs.EnableRedraw(True)
elipses: essentially works the same as circles. Supply 2 radii instead of just one.
def FlatWorm():
curve_object = rs.GetObject("Pick a backbone curve", 4, True, False)
if not curve_object: return
samples = rs.GetInteger("Number of cross sections", 100, 5)
if not samples: return
bend_radius = rs.GetReal("Bend plane radius", 0.5, 0.001)
if not bend_radius: return
perp_radius = rs.GetReal("Ribbon plane radius", 2.0, 0.001)
if not perp_radius: return
crvdomain = rs.CurveDomain(curve_object)
crosssections = [] # we need to store all our ellipse IDs for fed AddLoftSrf()
t_step = (crvdomain[1]-crvdomain[0])/samples
t = crvdomain[0]
for t in rs.frange(crvdomain[0], crvdomain[1], t_step):
crvcurvature = rs.CurveCurvature(curve_object, t) # fail on linear segments because the curvature is infinite
crosssectionplane = None
if not crvcurvature:
crvPoint = rs.EvaluateCurve(curve_object, t)
crvTangent = rs.CurveTangent(curve_object, t)
crvPerp = (0,0,1)
crvNormal = rs.VectorCrossProduct(crvTangent, crvPerp)
crosssectionplane = rs.PlaneFromFrame(crvPoint, crvPerp, crvNormal)
else:
crvPoint = crvcurvature[0]
crvTangent = crvcurvature[1]
crvPerp = rs.VectorUnitize(crvcurvature[4])
crvNormal = rs.VectorCrossProduct(crvTangent, crvPerp)
crosssectionplane = rs.PlaneFromFrame(crvPoint, crvPerp, crvNormal)
if crosssectionplane: # add the ellipse to the file and append the new ellipse curve ID csec to the list crosssections
csec = rs.AddEllipse(crosssectionplane, bend_radius, perp_radius)
crosssections.append(csec)
t += t_step
if not crosssections: return # create a lofted surface through all ellipses and delete the curves afterwards
rs.AddLoftSrf(crosssections)
rs.DeleteObjects(crosssections)
arcs:
recursion
process of self-repetition (like loop) recursive functions call themselves and this also execute the same code over and over again, hierarchical process.propagation factor
range of twigs at the end of every branch -its call factor because is random-
def AddArcDir(ptStart, ptEnd, vecDir): # behaves like AddArc3Pt() method
vecBase = rs.PointSubtract(ptEnd, ptStart)
if rs.VectorLength(vecBase)==0.0: return
if rs.IsVectorParallelTo(vecBase, vecDir): return
vecBase = rs.VectorUnitize(vecBase)
vecDir = rs.VectorUnitize(vecDir)
# create the bisector vecotr and unitized
vecBisector = rs.VectorAdd(vecDir, vecBase)
vecBisector = rs.VectorUnitize(vecBisector)
dotProd = rs.VectorDotProduct(vecBisector, vecDir)
midLength = (0.5*rs.Distance(ptStart, ptEnd))/dotProd
vecBisector = rs.VectorScale(vecBisector, midLength) # resize the unitized bisector to math his length
return rs.AddArc3Pt(ptStart, rs.PointAdd(ptStart, vecBisector), ptEnd)
def RandomPointInCone( origin, direction, minDistance, maxDistance, maxAngle):
vecTwig = rs.VectorUnitize(direction)
vecTwig = rs.VectorScale(vecTwig, minDistance + random.random()*(maxDistance-minDistance))
MutationPlane = rs.PlaneFromNormal((0,0,0), vecTwig)
vecTwig = rs.VectorRotate(vecTwig, random.random()*maxAngle, MutationPlane[1])
vecTwig = rs.VectorRotate(vecTwig, random.random()*360, direction)
return rs.PointAdd(origin, vecTwig)
def RecursiveGrowth( ptStart, vecDir, props, generation):
minTwigCount, maxTwigCount, maxGenerations, maxTwigLength, lengthMutation, maxTwigAngle, angleMutation = props # break out tuple into individual variable
if generation>maxGenerations: return
#Copy and mutate the growth-properties
newProps = props
maxTwigLength *= lengthMutation
maxTwigAngle *= angleMutation
if maxTwigAngle>90: maxTwigAngle=90
#Determine the number of twigs (could be less than zero)
newprops = minTwigCount, maxTwigCount, maxGenerations, maxTwigLength, lengthMutation, maxTwigAngle, angleMutation
maxN = int( minTwigCount+random.random()*(maxTwigCount-minTwigCount) )
for n in range(1,maxN):
ptGrow = RandomPointInCone(ptStart, vecDir, 0.25*maxTwigLength, maxTwigLength, maxTwigAngle)
newTwig = AddArcDir(ptStart, ptGrow, vecDir)
if newTwig:
vecGrow = rs.CurveTangent(newTwig, rs.CurveDomain(newTwig)[1])
RecursiveGrowth(ptGrow, vecGrow, newProps, generation+1) # function calling itself with differnt arguments. This new function instance behaves differently.
nurbs-curves:
introduction: 60's Pierre Bezier, Paul de Casteljau, Carl de Boor. Aerodinamics in card production. Mathematically accurate, freely adjustable geometry -> Splines. Nurbs means non uniform rational basic/basis spline.
control point curves:
def blendcorners():
# promp the user for a polyline
polyline_id = rs.GetObject("Polyline to blend", 4, True, True)
if not polyline_id: return
vertices = rs.PolylineVertices(polyline_id)
if not vertices: return
radius = rs.GetReal("Blend radius", 1.0, 0.0)
if radius is None: return
# will not be used until line 25 but obtaining them here make the script much more efficient.
between = lambda a,b: (a+b)/2.0
newverts = []
for i in range(len(vertices)-1):
a = vertices[i]
b = vertices[i+1]
segmentlength = rs.Distance(a, b)
vec_segment = rs.PointSubtract(b, a)
vec_segment = rs.VectorUnitize(vec_segment)
if radius<(0.5*segmentlength):
vec_segment = rs.VectorScale(vec_segment, radius)
else:
vec_segment = rs.VectorScale(vec_segment, 0.5*segment_length)
w1 = rs.VectorAdd(a, vec_segment)
w2 = rs.VectorSubtract(b, vec_segment)
newverts.append(a)
newverts.append(between(a,w1))
newverts.append(w1)
newverts.append(between(w1,w2))
newverts.append(w2)
newverts.append(between(w2,b))
newverts.append(vertices[len(vertices)-1])
rs.AddCurve(newverts, 5)
rs.DeleteObject(polyline_id)
interpolate curves: Binary-searching (split, select, split. select...).
def BSearchCurve(idCrv, Length, Tolerance):
Lcrv = rs.CurveLength(idCrv)
if Lcrv<Length: return
tmin = rs.CurveDomain(idCrv)[0]
tmax = rs.CurveDomain(idCrv)[1]
t0 = tmin
t1 = tmax
while True:
t = 0.5*(t1+t0) # t in the middle
Ltmp = rs.CurveLength(idCrv, 0, [tmin, t])
if abs(Ltmp-Length)<Tolerance: break
if Ltmp<Length: t0=t # adjust the subdomain based on the result of the length comparison. if shorten then restrict ourself to the lower hald of the old subdomain.
else: t1 = t
return t
def equidistanceoffset():
srf_id = rs.GetObject("Pick surface to offset", 8, True, True)
if not srf_id: return
offset = rs.GetReal("Offset distance", 1.0, 0.0)
if not offset: return
udomain = rs.SurfaceDomain(srf_id, 0)
ustep = (udomain[1]-udomain[0])/200
rs.EnableRedraw(False)
offsetvertices = []
u = udomain[0]
while u<=(udomain[1]+0.5*ustep):
isocurves = rs.ExtractIsoCurve(srf_id, (u,0), 1)
if isocurves:
t = BSearchCurve(isocurves[0], offset, 0.001)
if t is not None:
offsetvertices.append(rs.EvaluateCurve(isocurves[0], t))
rs.DeleteObjects(isocurves)
u+=ustep
if offsetvertices: rs.AddInterpCrvOnSrf(srf_id, offsetvertices)
rs.EnableRedraw(True)
geometric curve properties:
def addcurvaturegraphsection(idCrv, t0, t1, samples, scale):
if (t1-t0)<=0.0: return # check null span inside kinks
tstep = (t1-t0)/samples # step size for the loop
points = []
objects = [] # hold the IDs of the perpendicular lines and the connecting curve
for t in rs.frange(t0,t1+(0.5*tstep),tstep):
if t>=t1:t = t1-1e-10 # check t does not go beyond t1 since that might give us the curvature data of the next segment
cData = rs.CurveCurvature(idCrv, t)
if not cData:
points.append(rs.EvaluateCurve(idCrv, t))
else:
c = rs.VectorScale(cData[4], scale)
a = cData[0]
b = rs.VectorSubtract(a, c)
objects.append(rs.AddLine(a,b))
points.append(b)
objects.append(rs.AddInterpCurve(points))
return objects
def addcurvaturegraph( idCrv, spansamples, scale):
''' iteration over the knot-vector of a curve object'''
allGeometry = []
knots = rs.CurveKnots(idCrv)
p=5
for i in range(len(knots)-1):
tmpGeometry = addcurvaturegraphsection(idCrv, knots[i], knots[i+1], spansamples, scale)
if tmpGeometry: allGeometry.append(tmpGeometry)
rs.AddObjectsToGroup(allGeometry, rs.AddGroup())
return allGeometry
def createcurvaturegraph():
curve_ids = rs.GetObjects("Curves for curvature graph", 4, False, True, True)
if not curve_ids: return
samples = 10
scale = 1.0
preview = [] # list contain arrays of IDs one for each curve in idCurves
while True:
rs.EnableRedraw(False)
for p in preview: rs.DeleteObjects(p)
preview = []
for id in curve_ids:
cg = addcurvaturegraph(id, samples, scale)
preview.append(cg)
rs.EnableRedraw(True)
result = rs.GetString("Curvature settings", "Accept", ("Samples", "Scale", "Accept"))
if not result:
for p in preview: rs.DeleteObjects(p)
break
result = result.upper()
if result=="ACCEPT": break
elif result=="SAMPLES":
numsamples = rs.GetInteger("Number of samples per knot-span", samples, 3, 100)
if numsamples: samples = numsamples
elif result=="SCALE":
sc = rs.GetReal("Scale of the graph", scale, 0.01, 1000.0)
if sc: scale = sc
meshes:
polygon meshes: meshes are defined locally, they can store more information such as color, texture-coordinates and normals, vertices and faces.
topology: mathematical study of the properties that are preserved through deformations, twisting and stretching of objects.
Última actualización
¿Te fue útil?