def fractal(depth, x1, y1, z1, x2, y2, z2, length, anglerec, angle, lvariation, aran, lran, anglerech, angleh, branches, verticality, gchance, depthstart, radtolen, radchng, mngon, polygon, branch_cluster): #test if depth>0 if depth: #defining random angle variation and length variation arn = random.uniform(-angle / 100 * aran, angle / 100 * aran) lrn = random.uniform(-length / 100 * lran, length / 100 * lran) if hrandom == True: #defining horizontal rotation angles ahor = random.sample(range(0, 360), branches) #removing numbers within tolerance ahr = rs.CullDuplicateNumbers(ahor, angleh) #in a 360 fashion if ahr[0] + 360 - angleh < ahr[-1]: del ahr[0] else: #generating evenly distributed angles ahr = range(0, 360 + 1, 360 // branches)[:-1] #previous branch vector vecst = rg.Point3d(x1, y1, z1) vecend = rg.Point3d(x2, y2, z2) movevec = ghc.Vector2Pt(vecst, vecend, True)[0] #returns vector and it's length #perpendicular vector rotplane3 = ghc.PlaneNormal( vecend, movevec) #creates plane perpendicular to vector plns = ghc.DeconstructPlane(rotplane3) #origin, x, y, z rotplane = ghc.ConstructPlane( plns[2], plns[1], plns[3] ) #constructing new plane switching x and y planes to make perpendicular #generating perpendicular vector vecperp = ghc.Rotate(movevec, radians(90), rotplane)[0] #generating vector amplitudes leny = (length + lrn) * sin( radians((anglerec + arn) * (1 - (verticality**depth)))) lenz = (length + lrn) * cos(radians(anglerec + arn)) ampy = ghc.Amplitude(vecperp, leny) ampz = ghc.Amplitude(movevec, lenz) #changing branch length dependant on branch depth length = length * lvariation #building points endpoint1 = ghc.Move( vecend, ampz)[0] #returns moved object and transformation data endpoint = ghc.Move( endpoint1, ampy)[0] #returns moved object and transformation data #rotating point in a cone fashion rotpoint = ghc.Rotate3D( endpoint, radians(anglerech), vecend, movevec)[0] #returns rotated geometry and transformation data #building line between points linegeo = rg.Line(vecend, rotpoint) #defining recursion depth key = depthstart + 1 - depth #building geometry pln = ghc.PlaneNormal(rotpoint, linegeo) #returns a plane perp to a vector radius = length * (radchng**(key)) / radtolen #reduce details with each branch, but not less than 3 splits = 3 if mngon - key + 1 <= 3 else mngon - key + 1 polygonend = ghc.Polygon(pln, radius, splits, 0)[0] #returns a polygon and its perimeter #aligning curves for loft creation crvst = ghc.EndPoints(polygon)[0] pntcld = ghc.Discontinuity(polygonend, 1) #returns points and point parameters #finind seam point closest_point = ghc.ClosestPoint( crvst, pntcld[0] ) #returns closest point, closest point index, distance between closest points seampnt = pntcld[1][closest_point[1]] polygonend = ghc.Seam(polygonend, seampnt) lcurves = [polygon, polygonend] #building geometry geo = ghc.ExtrudePoint( polygon, rotpoint ) if depth == 1 and splits == 3 else rg.Brep.CreateFromLoft( lcurves, rg.Point3d.Unset, rg.Point3d.Unset, rg.LoftType.Normal, False)[0] #if last branch than make a pyramid #make solid geocapped = ghc.CapHoles(geo) #building a dict of geo with depth as key, and geo as values pgons.update( {branch_cluster: [geocapped]}) if branch_cluster not in pgons.keys( ) else pgons[branch_cluster].append(geocapped) branchesout.append(geocapped) #setting coords for next branch x1 = x2 y1 = y2 z1 = z2 #getting xyz from rotated point x2 = rg.Point3d(rotpoint)[0] y2 = rg.Point3d(rotpoint)[1] z2 = rg.Point3d(rotpoint)[2] #setting base polygon for next branch polygon = polygonend #filling dict with branch clusters cluster.append(cluster[-1] + 1) branch_cluster = cluster[-1] #calling function with different angle parameter for branch splitting, calling as many branches as spread within tolerance if depth != 1: for aa in ahr: if ( (random.randint(40, 99) / 100)**depth ) < gchance or depth == depthstart + 1: #or added to prevent blank trees fractal(depth - 1, x1, y1, z1, x2, y2, z2, length, angle, angle, lvariation, aran, lran, aa, angleh, branches, verticality, gchance, depthstart, radtolen, radchng, mngon, polygon, branch_cluster) #leaf logic if depth <= leavesdepth and leavesperbranch > 0 and maxleaves > 0: #vector for leaf growth spread leafpntvec = ghc.Vector2Pt(vecend, rotpoint, True)[0] #setting leaf growth position on last barnch, leafpnt list lastbranchlength = ghc.Length(linegeo) leaves_list = [lastbranchlength] [ leaves_list.append(lengthparam) for lengthparam in random.sample( range(0, int(lastbranchlength)), leavesperbranch - 1) ] if leavesperbranch > 1 else None for leafpnt in leaves_list: leafamp = ghc.Amplitude(leafpntvec, leafpnt) leafpoint = ghc.Move(vecend, leafamp)[0] #plane for leaf generation linetocenter = ghc.Line(stpntbase, leafpoint) planetocenter = ghc.PlaneNormal(leafpoint, linetocenter) #create an imaginary circle with leaflen radius and populate it with points for random leaf generation leafgenerationcircle = ghc.CircleCNR(leafpoint, linetocenter, leaflen) circlesurf = ghc.BoundarySurfaces(leafgenerationcircle) leafcnt = random.randint(0, maxleaves) if leafcnt > 0: leafendpnts = ghc.PopulateGeometry(circlesurf, leafcnt, random.randint(1, 500)) def leafgenerator(point): #random z move zmove = rg.Vector3d(0, 0, 1) moveamp = random.uniform(-leaflen / 3, leaflen / 5) ampzmove = ghc.Amplitude(zmove, moveamp) llendpnt = ghc.Move(point, ampzmove)[0] #setting a leaf centerline vector leafvector = ghc.Vector2Pt(leafpoint, llendpnt, True)[0] #defining leaf center point as avarage of st and end pnts midpnt = ghc.Average([leafpoint, llendpnt]) #generating perpendicular vector vecperpleaf = ghc.Rotate(leafvector, radians(90), planetocenter)[0] leafperpamp = ghc.Amplitude( vecperpleaf, random.uniform((leafwidth / 2) / 5 * 4, (leafwidth / 2) / 5 * 6)) #moving mid point to both sides midpnt1 = ghc.Move(midpnt, leafperpamp)[0] midpnt2 = ghc.Move(midpnt, -leafperpamp)[0] #leaf geo leafgeo = rg.NurbsSurface.CreateFromCorners( leafpoint, midpnt1, llendpnt, midpnt2) leaves.append(leafgeo) #iterate over random number of generated points if list, else generate for one point [leafgenerator(pp) for pp in leafendpnts] if isinstance( leafendpnts, list) else leafgenerator(leafendpnts)
def main(geometryMesh, numOfInitialPts, initialPtsSpread, flowPathsType): # create "initialPts" bb = geometryMesh.GetBoundingBox(False) upperBBsurface = bb.ToBrep().Faces[5].DuplicateSurface() initialPts = [] bbUpperFacePts = [] # remains empty for initialPtsSpread == 0 if initialPtsSpread == 0: # calculate Udivisions, Vdivisions according to approximatelly numOfInitialPts upperBBsurface_edge1 = bb.GetEdges()[4] upperBBsurface_edge2 = bb.GetEdges()[5] U_V_directions_ratio = upperBBsurface_edge1.Length / upperBBsurface_edge2.Length if U_V_directions_ratio < 1: # if upperBBsurface_edge1 < upperBBsurface_edge2 U_V_directions_ratio = upperBBsurface_edge2.Length / upperBBsurface_edge1.Length initial_Udivisions = round( math.sqrt(U_V_directions_ratio * numOfInitialPts), 0) Vdivisions = round(numOfInitialPts / initial_Udivisions, 0) Udivisions = round(numOfInitialPts / Vdivisions, 0) # final_Udivisions # divide the upper face of the geometryMesh bounding box with points according to Udivisions, Vdivisions domainUV = Rhino.Geometry.Interval(0, 1) upperBBsurface.SetDomain(0, domainUV) upperBBsurface.SetDomain(1, domainUV) uDomain = upperBBsurface.Domain(0) vDomain = upperBBsurface.Domain(1) uStep = uDomain.T1 / (Udivisions - 1) vStep = vDomain.T1 / (Vdivisions - 1) for i in xrange(Udivisions): u = i * uStep for k in xrange(Vdivisions): v = k * vStep bbUpperFacePt = upperBBsurface.PointAt(u, v) ray = Rhino.Geometry.Ray3d(bbUpperFacePt, Rhino.Geometry.Vector3d(0, 0, -1)) rayIntersectParam = Rhino.Geometry.Intersect.Intersection.MeshRay( geometryMesh, ray) if rayIntersectParam > 0: initialPt = ray.PointAt(rayIntersectParam) initialPts.append(initialPt) # end of create "initialPts" elif initialPtsSpread == 1: seed = 0 bbUpperFacePts = ghc.PopulateGeometry(upperBBsurface, numOfInitialPts, seed) for bbUpperFacePt in bbUpperFacePts: ray = Rhino.Geometry.Ray3d(bbUpperFacePt, Rhino.Geometry.Vector3d(0, 0, -1)) rayIntersectParam = Rhino.Geometry.Intersect.Intersection.MeshRay( geometryMesh, ray) if rayIntersectParam > 0: initialPt = ray.PointAt(rayIntersectParam) initialPts.append(initialPt) # calculate the flow lines flowPaths = [] for initialPt in initialPts: flowPath = calculateFlowPaths(geometryMesh, initialPt, flowPathsType) if flowPath != None: flowPaths.append(flowPath) del initialPts del bbUpperFacePts gc.collect() return flowPaths
def createThreeDeeTrees(forestSrfs, treeType, randomHeightRangeStart, randomHeightRangeEnd, deciduousOrConiferous_index, maxNumOfTrees, randomSeed, unitConversionFactor): projectionDirection = Rhino.Geometry.Vector3d(0,0,1) # it can be direction = Rhino.Geometry.Vector3d(0,0,-1) as well, does not matter tol = Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance # find the largest area areas = [] maximalArea = -1000000000000 # dummy small value for i,srf in enumerate(forestSrfs): area = Rhino.Geometry.AreaMassProperties.Compute(srf).Area if area > maximalArea: maximalArea = area areas.append(area) threeDeeTreesDataTree = Grasshopper.DataTree[object]() threeDeeTreesOriginDataTree = Grasshopper.DataTree[object]() treeHeightDataTree = Grasshopper.DataTree[object]() for i,srf in enumerate(forestSrfs): groundTerrain = srf srf_brepFace = srf.Faces[0] middle_u = (srf_brepFace.Domain(0).T0 + srf_brepFace.Domain(0).T1) / 2 middle_v = (srf_brepFace.Domain(1).T0 + srf_brepFace.Domain(1).T1) / 2 normal = srf_brepFace.NormalAt(middle_u, middle_v) if normal.Z >= 0: pass elif normal.Z < 0: srf.Flip() # get groundBrep_singleBrepFace if (groundTerrain != None): groundBrep_singleBrepFace = groundTerrain.Faces[0].DuplicateFace(False) # always use the top face (the actual terrain) in case inputted groundTerrain_ has been created as a polysurface accurate = False bb_volume, bb_centroid, bb_length, bb_depth, bb_height, bb_bottomLeftCorner, bb_bottomRightCorner, bb_topRightCorner, bb_topLeftCorner = gismo_preparation.boundingBox_properties([groundTerrain], accurate) elif (groundTerrain == None): groundBrep_singleBrepFace = None bb_height = 10 # dummy value bb_height = 3000 # dummy large value (until "Ladybug Terrain Generator" starts support "origin_" input to be on the terrain) numberOfTrees = int( maxNumOfTrees * (areas[i]/maximalArea) ) if (numberOfTrees < 1): numberOfTrees = 1 treeBottom_ptL = ghc.PopulateGeometry(srf, numberOfTrees, randomSeed) try: # if "ghc.PopulateGeometry" returns a single point, it will not be returned in a list! treeBottom_ptL.extend([]) # test if "treeBottom_ptL" is a list except: treeBottom_ptL = [treeBottom_ptL] path = Grasshopper.Kernel.Data.GH_Path(i) threeDeeTreesL = [] threeDeeTreesOriginL = [] heightL = [] for index, treeBottom_pt in enumerate(treeBottom_ptL): # 1) try creating 3d trees height = round(random.uniform(randomHeightRangeStart, randomHeightRangeEnd),2) # in Rhino document units if (deciduousOrConiferous_index == 0): deciduousOrConiferous = "deciduous" # by default, if it can not be identified if a tree is deciduous or coniferous always use the deciduous elif (deciduousOrConiferous_index == 1): deciduousOrConiferous = "coniferous" elif (deciduousOrConiferous_index == 2): randomIndex = random.randint(0,1) deciduousOrConiferous = ["deciduous", "coniferous"][randomIndex] numOfTreeHorizontalSegments = 6 # this value is fixed, and should not be changed # heights and radii trunkRadius = height/random.uniform(44, 48) # lower values (than 44,48) can result in "bottomCrown_brep" not being able to be created crownRadius = height/random.uniform(2, 5) trunkHeight = 0.2*height crownHeight = height - trunkHeight unprojectedTrunkBottom_crv = Rhino.Geometry.Circle(treeBottom_pt, trunkRadius).ToNurbsCurve() # project the shapes (points) to the groundTerrain_ if (groundTerrain == None): projectedTreeBottom_pt = treeBottom_pt projectedTrunkBottom_crv = unprojectedTrunkBottom_crv extrusionVector = Rhino.Geometry.Vector3d(0, 0, trunkHeight) trunkBrep = Rhino.Geometry.Surface.CreateExtrusion(projectedTrunkBottom_crv, extrusionVector).ToBrep() trunkTop_pt = Rhino.Geometry.Point3d(projectedTreeBottom_pt.X, projectedTreeBottom_pt.Y, projectedTreeBottom_pt.Z + trunkHeight) trunkTop_crv = Rhino.Geometry.Circle(trunkTop_pt, trunkRadius).ToNurbsCurve() elif (groundTerrain != None): projectedTreeBottom_pts = Rhino.Geometry.Intersect.Intersection.ProjectPointsToBreps([groundTerrain], [treeBottom_pt], projectionDirection, tol) if len(projectedTreeBottom_pts) == 0: # the shapeL[0] point is located outside of terrainGround_ input boundaries continue else: projectedTreeBottom_pt = projectedTreeBottom_pts[0] projectedTrunkBottom_crv1 = Rhino.Geometry.Curve.ProjectToBrep(unprojectedTrunkBottom_crv, groundTerrain, projectionDirection, tol)[0] if (not projectedTrunkBottom_crv1.IsClosed): # the projected shape is not a closed curve anymore (it was before the projection) line = Rhino.Geometry.Line(projectedTrunkBottom_crv1.PointAtStart, projectedTrunkBottom_crv1.PointAtEnd).ToNurbsCurve() joinedPolyCrv = Rhino.Geometry.Curve.JoinCurves([projectedTrunkBottom_crv1,line], tol)[0] projectedTrunkBottom_crv = joinedPolyCrv.ToNurbsCurve() else: # the projected curve is still a closed curve (and it was before the projection) projectedTrunkBottom_crv = projectedTrunkBottom_crv1 # a) trunk trunkTop_pt = Rhino.Geometry.Point3d(projectedTreeBottom_pt.X, projectedTreeBottom_pt.Y, projectedTreeBottom_pt.Z + trunkHeight) trunkTop_crv = Rhino.Geometry.Circle(trunkTop_pt, trunkRadius).ToNurbsCurve() extrudePathCurve = Rhino.Geometry.Line(trunkTop_pt, Rhino.Geometry.Point3d(trunkTop_pt.X, trunkTop_pt.Y, trunkTop_pt.Z - bb_height)).ToNurbsCurve() extrusionVector = Rhino.Geometry.Vector3d(0, 0, -bb_height) global extrudedShapeBrep extrudedShapeBrep = Rhino.Geometry.Surface.CreateExtrusion(trunkTop_crv, extrusionVector).ToBrep() splittedBreps = Rhino.Geometry.Brep.Split(extrudedShapeBrep, groundTerrain, tol) if (len(splittedBreps) == 0): # treeBottom_pt (origin pt projected on groundBrep_singleBrepFace) lies somewhere close to the edge of the ground Terrain continue else: trunkBrep1 = splittedBreps[0] trunkBrep2 = splittedBreps[1] if trunkBrep1.Vertices[0].Location.Z > trunkBrep2.Vertices[0].Location.Z: trunkBrep = trunkBrep1 else: trunkBrep = trunkBrep2 shrinkSuccess = trunkBrep.Faces.ShrinkFaces() del splittedBreps; # b) crown # crown curves/polylines crownSection_crvs = [] if deciduousOrConiferous == "deciduous": crownRadii = [0.15*crownRadius, crownRadius, crownRadius, crownRadius, 0.15*crownRadius] # [crownBottom_crv radius, crownMiddle1_crv, crownMiddle2_crv, crownTop_crv] crownPartitionHeights = [0*crownHeight, 0.275*crownHeight, 0.5*crownHeight, 0.725*crownHeight, 1.0*crownHeight] elif deciduousOrConiferous == "coniferous": crownRadii = [0.2*crownRadius, crownRadius, 0.1*crownRadius] # [crownBottom_crv radius, crownMiddle1_crv, crownMiddle2_crv, crownTop_crv] crownPartitionHeights = [0*crownHeight, 0.05*crownHeight, 1.0*crownHeight] for i in xrange(len(crownRadii)): crown_pt = Rhino.Geometry.Point3d(trunkTop_pt.X, trunkTop_pt.Y, trunkTop_pt.Z + crownPartitionHeights[i]) circleCrv = Rhino.Geometry.Circle(crown_pt, crownRadii[i]).ToNurbsCurve() if (treeType == 0): crownSection_crvs.append(circleCrv) elif (treeType == 1): includeEnds = True circleDivision_tL = list(Rhino.Geometry.Curve.DivideByCount(circleCrv, numOfTreeHorizontalSegments, includeEnds)) circleDivision_tL = circleDivision_tL + [circleDivision_tL[0]] # closing the polyline circleDivision_ptsL = [circleCrv.PointAt(t) for t in circleDivision_tL] crown_polyline = Rhino.Geometry.Polyline(circleDivision_ptsL).ToNurbsCurve() crownSection_crvs.append(crown_polyline) elif (treeType == 2): centroid = Rhino.Geometry.AreaMassProperties.Compute(circleCrv).Centroid includeEnds = True t_L = circleCrv.DivideByCount(12, includeEnds) randomCirclePts = [] for t in t_L: pt = circleCrv.PointAt(t) vector = pt - centroid vectorScaleFactor = random.uniform(-0.2, 0.2) randomPt = pt + vector*vectorScaleFactor randomCirclePts.append(randomPt) degree = 3; knotstyle = 3; knotstyle2 = System.Enum.ToObject(Rhino.Geometry.CurveKnotStyle, 3); start_tangent = end_tangent = Rhino.Geometry.Vector3d.Unset randomCirclePts2 = randomCirclePts + [randomCirclePts[0]] # close the crv randomCrv = Rhino.Geometry.Curve.CreateInterpolatedCurve(randomCirclePts2, degree, knotstyle2, start_tangent, end_tangent) crownSection_crvs.append(randomCrv) # crow brep bottomCrown_brep = Rhino.Geometry.Brep.CreatePlanarBreps([crownSection_crvs[0],trunkTop_crv])[0] topCrown_brep = Rhino.Geometry.Brep.CreatePlanarBreps(crownSection_crvs[-1])[0] if (treeType == 0): loftType2 = Rhino.Geometry.LoftType.Normal elif (treeType == 1): loftType2 = Rhino.Geometry.LoftType.Straight elif (treeType == 2): loftType2 = Rhino.Geometry.LoftType.Normal closed = False crownSideBrep = Rhino.Geometry.Brep.CreateFromLoft(crownSection_crvs, Rhino.Geometry.Point3d.Unset, Rhino.Geometry.Point3d.Unset, loftType2, closed)[0] treeJoinedBrep = Rhino.Geometry.Brep.JoinBreps([bottomCrown_brep, crownSideBrep, topCrown_brep, trunkBrep], tol)[0] treeJoinedBrep.Flip() # for some reason the "treeJoinedBrep" has always normals pointed downwards threeDeeTreesL.append(treeJoinedBrep) threeDeeTreesOriginL.append(projectedTreeBottom_pt) heightL.append(height) threeDeeTreesDataTree.AddRange(threeDeeTreesL, path) threeDeeTreesOriginDataTree.AddRange(threeDeeTreesOriginL, path) treeHeightDataTree.AddRange(heightL, path) # baking if bakeIt_: layerName = "treeType=" + str(treeType) + "_randomHeightRange=" + str(randomHeightRangeStart) + "-" + str(randomHeightRangeEnd) + "_deciduousOrConiferous=" + str(deciduousOrConiferous_index) + "_maxNumOfTrees=" + str(maxNumOfTrees) + "_randomSeed" + str(randomSeed) layParentName = "GISMO"; laySubName = "OSM"; layerCategoryName = "3D_FOREST" newLayerCategory = False laySubName_color = System.Drawing.Color.FromArgb(100,191,70) # OSM green layerColor = System.Drawing.Color.FromArgb(11,156,50) # tree green layerIndex, layerName_dummy = gismo_preparation.createLayer(layParentName, laySubName, layerCategoryName, newLayerCategory, layerName, laySubName_color, layerColor) geometryIds = gismo_preparation.bakeGeometry(threeDeeTreesLL, layerIndex) # grouping groupIndex = gismo_preparation.groupGeometry("3D_OSM_FOREST" + "_" + layerName, geometryIds) del geometryIds; del threeDeeTreesLL # hide "threeDeeTreesOrigin" output ghenv.Component.Params.Output[2].Hidden = True # deleting del forestSrfs; # delete local variables gc.collect() validThreeDeeRoadsCreation = True printMsg = "ok" return threeDeeTreesDataTree, threeDeeTreesOriginDataTree, treeHeightDataTree