예제 #1
0
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)
예제 #2
0
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
예제 #3
0
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