def to_point(self, centers, falloff): n = len(centers) if n == 1: sfield = SvScalarFieldPointDistance(centers[0], falloff=falloff) vfield = SvVectorFieldPointDistance(centers[0], falloff=falloff) elif self.point_mode == 'AVG': sfields = [SvScalarFieldPointDistance(center, falloff=falloff) for center in centers] sfield = SvMergedScalarField('AVG', sfields) vfields = [SvVectorFieldPointDistance(center, falloff=falloff) for center in centers] vfield = SvAverageVectorField(vfields) elif self.point_mode == 'MIN': kdt = kdtree.KDTree(len(centers)) for i, v in enumerate(centers): kdt.insert(v, i) kdt.balance() vfield = SvKdtVectorField(kdt=kdt, falloff=falloff) sfield = SvKdtScalarField(kdt=kdt, falloff=falloff) else: # SEP sfield = [SvScalarFieldPointDistance(center, falloff=falloff) for center in centers] vfield = [SvVectorFieldPointDistance(center, falloff=falloff) for center in centers] return vfield, sfield
def match_raycast_vertex(self): self.bm = bmesh.new() self.bm.from_mesh(self.obj_t.data) self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() bmesh.ops.remove_doubles(self.bm, verts=list(self.bm.verts), dist=3) size = len(self.bm.verts) kd = kdtree.KDTree(size) for i, vtx in enumerate(self.bm.verts): v = Vector((vtx.co.x, vtx.co.y, 0.0)) kd.insert(v, i) kd.balance() plane = bpy.data.objects['Plane.001'] self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() for p in plane.data.vertices: wp = plane.matrix_world @ p.co lp = self.obj_t.matrix_world.inverted() @ wp #BL_DEBUG.set_mark(self.obj_t.matrix_world @ location) #BL_DEBUG.set_mark(plane.matrix_world @ p.co) location = Vector((lp.x, lp.y, 0.0)) co, index, dist = kd.find(location) # dist is the distance #print("Found", co, index, dist) #BL_DEBUG.set_mark(self.obj_t.matrix_world @ co) self.bm.verts[index].co[2] = lp.z ## translated coords # tune this. There are more space (2-4m) if dist > 0.4: self.bm.verts[index].co[0] = lp.x ## translated coords self.bm.verts[index].co[1] = lp.y ## translated coords self.update_bm(freeit=True)
def object_list2(objects, active=0): """ Return an approximate shortest path through objects starting at the active index using the nearest neighbor heuristic. """ s = time() # calculate a kd tree to quickly answer nearest neighbor queries kd = kdtree.KDTree(len(objects)) for i, ob in enumerate(objects): kd.insert(ob.location, i) kd.balance() current = objects[active] chain = [current] # we start at the chosen object added = {active} for i in range(1, len(objects)): # we know how many objects to add # when looking for the nearest neighbor we start with two neigbors # (because we include the object itself in the search) and if # the other neigbors is not yet in the chain we add it, otherwise # we expand our search to a maximum of the total number of objects for n in range(2, len(objects)): neighbors = { index for _, index, _ in kd.find_n(current.location, n) } neighbors -= added if neighbors: # strictly speaking we shoudl assert that len(neighbors) == 1 chain.extend(objects[i] for i in neighbors) added |= neighbors break current = chain[-1] print("{n:d} objects {t:.1f}s".format(t=time() - s, n=len(objects))) return chain
def mirror_l_to_r_back(): bpy.ops.object.mode_set(mode = 'OBJECT') obj = bpy.context.active_object mesh = obj.data size = len(mesh.vertices) kd = kdtree.KDTree(size) rside_indices = [i for i,v in enumerate(mesh.vertices) if v.co.x < 0] lside_pos = [[i,(-v.co.x , v.co.y , v.co.z )] for i,v in enumerate(mesh.vertices) if v.co.x < 0] # selectedVtxIndex = set([v.index for v in obj.data.vertices if v.select]) # print(selectedVtxIndex) print(lside_pos) for i, v in enumerate(mesh.vertices): kd.insert(v.co, i) kd.balance() #co_find = (0.0, 0.0, 0.0) bpy.ops.object.mode_set(mode = 'EDIT') bpy.ops.mesh.select_mode(type="VERT") bpy.ops.mesh.select_all(action = 'DESELECT') bpy.ops.object.mode_set(mode = 'OBJECT') for p in lside_pos: co, index, dist = kd.find( p[1] ) print("Close to center:",p[0],':', co, index, dist) obj.data.vertices[p[0]].co = (-co.x,co.y,co.z) # for i in rside_indices: # mesh.vertices[i].select = True #obj.data.vertices[i].co.x = 2.0 bpy.ops.object.mode_set(mode = 'EDIT')
def sv_main(v=[[]], mdist=0.4): in_sockets = [ ['v', 'verts', v], ['s', 'mdist', mdist] ] e = [] if v and v[0]: v = v[0] # make kd tree # documentation mathutils.kdtree.html size = len(v) kd = kdtree.KDTree(size) for i, vtx in enumerate(v): kd.insert(Vector(vtx), i) kd.balance() # makes edges for i, vtx in enumerate(v): num_edges = 0 for (co, index, dist) in kd.find_range(vtx, mdist): if i == index or (num_edges > 2): continue e.append([i, index]) num_edges += 1 # out boilerplate out_sockets = [ ['s', 'Edges', [e]] ] return in_sockets, out_sockets
def by_sphere(self, vertices, edges, faces): radius = self.inputs['Radius'].sv_get(default=[1.0])[0][0] centers = self.inputs['Center'].sv_get()[0] if len(centers) == 1: center = centers[0] out_verts_mask = [((Vector(v) - Vector(center)).length <= radius) for v in vertices] else: # build KDTree tree = kdtree.KDTree(len(centers)) for i, v in enumerate(centers): tree.insert(v, i) tree.balance() out_verts_mask = [] for vertex in vertices: _, _, rho = tree.find(vertex) mask = rho <= radius out_verts_mask.append(mask) out_edges_mask = self.select_edges_by_verts(out_verts_mask, edges) out_faces_mask = self.select_faces_by_verts(out_verts_mask, faces) return out_verts_mask, out_edges_mask, out_faces_mask
def init_guess(): us = np.linspace(u_min, u_max, num=init_samples) vs = np.linspace(v_min, v_max, num=init_samples) us, vs = np.meshgrid(us, vs) us = us.flatten() vs = vs.flatten() points = surface.evaluate_array(us, vs).tolist() kdt = kdtree.KDTree(len(us)) for i, v in enumerate(points): kdt.insert(v, i) kdt.balance() us_out = [] vs_out = [] nearest_out = [] for point_from in points_from: nearest, i, distance = kdt.find(point_from) us_out.append(us[i]) vs_out.append(vs[i]) nearest_out.append(tuple(nearest)) return us_out, vs_out, nearest_out
def process(self): if not any(output.is_linked for output in self.outputs): return vertices_s = self.inputs['Vertices'].sv_get(default=[[]]) masks_s = self.inputs['Mask'].sv_get() radius_s = self.inputs['Radius'].sv_get() out_coeffs = [] meshes = match_long_repeat([vertices_s, masks_s, radius_s]) for vertices, masks, radius in zip(*meshes): fullList(masks, len(vertices)) if isinstance(radius, list) and isinstance(radius[0], (int, float)): radius = radius[0] # build KDTree base = [v for v, mask in zip(vertices, masks) if mask] tree = kdtree.KDTree(len(base)) for i, v in enumerate(base): tree.insert(v, i) tree.balance() coeffs = [] for vertex, mask in zip(vertices, masks): if mask: coef = 1.0 else: _, _, rho = tree.find(vertex) coef = self.falloff(radius, rho) coeffs.append(coef) out_coeffs.append(coeffs) self.outputs['Coeffs'].sv_set(out_coeffs)
def creat_tree(line): # Create KDTree for line tree = kdtree.KDTree(len(line)) [tree.insert(p, i) for i, p in enumerate(line)] tree.balance() return tree
def Terraform(index, startY, startX, distY, distX, endY, endX, level, radius, lenY, lenX, falloff, vertY, vertX, realY, realX, m, terraKey, exclude): index = eval(index) dy = index[0] - startY dx = index[1] - startX localPos = (200 * dy, 200 * dx) worldPos = Vector([realX + localPos[1], realY + localPos[0], 0]) cellDict = core.world["cellDict"] worldKey = (dx + realX, dy + realY) if worldKey not in cellDict: cellDict[worldKey] = {"heightMap": [], "objects": [], "init": False} file = gamepath + "\\dsm\\cache\\landmass\\%s\\exclude.txt" % (terraKey) exclude = eval(exclude) if str(index) in exclude: return False cvi = core.grid["verts"][vertY, vertX][0] origin = Vector(m.getVertex(0, cvi).getXYZ()[0:2]) + Vector([realX, realY]) try: f = np.load(gamepath + "\\dsm\\cache\\landmass\\%s\\%s.npy" % (terraKey, str(index))) except: kd = kdtree.KDTree(41 * 41) f = np.full((41, 41), abs(radius * level)) terraLoop1_ufunc(kd, terra_i, worldPos.x, worldPos.y) kd.balance() near = kd.find_range(Vector([realX, realY, 0]), radius**2) doLenAdjust = lenX != lenY for n in near: co, kd_index, dist = n point = ps[kd_index] if doLenAdjust: if min(lenX, lenY) == lenX: distX = np.linalg.norm( Vector([co.x, 0, 0]) - Vector([origin.x, 0, 0])) dist += distX * (16 * falloff) elif min(lenX, lenY) == lenY: distY = np.linalg.norm( Vector([0, co.y, 0]) - Vector([0, origin.y, 0])) dist += distY * (16 * falloff) e = max(0, dist) new_f = getHeightFactor(level, radius, falloff, e) if abs(new_f) > 0: f[i_list[kd_index]] = e if len(f[f != abs(radius * level)].tolist()) > 0: np.save( gamepath + "\\dsm\\cache\\landmass\\%s\\%s.npy" % (terraKey, str(index)), f) else: exclude = open(file, "a") exclude.write(str(index) + "\n") if len(f[f != abs(radius * level)].tolist()) > 0: cellDict[worldKey]["heightMap"].append( [vertY, vertX, radius, level, falloff, f, (lenX, lenY)]) return True
def execute(self, context): depsgraph = bpy.context.depsgraph ob = bpy.context.active_object obj_eval = depsgraph.objects.get(ob.name, None) # particleObj = context.active_object particleObj = obj_eval if bpy.context.active_object.particle_systems is None: # create new one self.report({'INFO'}, 'No active Particle Hair System found!') return {"CANCELLED"} index = particleObj.particle_systems.active_index psys_active = particleObj.particle_systems[index] if psys_active.settings.type != 'HAIR': # create new one self.report({'INFO'}, 'Active Particle System is not Hair type! Cancelling') return {"CANCELLED"} pointsList_hair = [] context.scene.update() if len(psys_active.particles) == 0: # require more that three strands self.report({'INFO'}, 'Active Particle System has zero strands! Cancelling') return {"CANCELLED"} diagonal = sqrt( pow(particleObj.dimensions[0], 2) + pow(particleObj.dimensions[1], 2) + pow(particleObj.dimensions[2], 2)) # to normalize some values for particle in psys_active.particles: # for strand point pointsList_hair.append([ hair_key.co for hair_key in particle.hair_keys ]) # DONE: exclude duplicates if first strand[0] in list already if len(psys_active.particles ) == 1: #create two fake strands so that barycentric works pointsList_hair.append([ x.xyz + Vector((0.01 * diagonal, 0, 0)) for x in pointsList_hair[0] ]) pointsList_hair.append([ x.xyz + Vector((0, 0.01 * diagonal, 0)) for x in pointsList_hair[0] ]) elif len(psys_active.particles ) == 2: #create one fake strands so that barycentric works pointsList_hair.append([ x.xyz + Vector((0.01 * diagonal, 0, 0)) for x in pointsList_hair[0] ]) pointsList_uniq = [] [ pointsList_uniq.append(x) for x in pointsList_hair if x not in pointsList_uniq ] #removing doubles (can cause zero size tris) #same_point_count cos barycentric transform requires it pointsList = interpol_Catmull_Rom( pointsList_uniq, self.t_in_y, uniform_spacing=True, same_point_count=True) # just gives smoother result on borders searchDistance = 100 * diagonal parentRoots = [strand[0] for strand in pointsList] # first point of roots #create nnew Part Sytem with uniform points pointsChildRoots = self.createUniformParticleSystem( context, self.childCount, self.PlacementJittering, self.Seed) # return child part roots positions kd = kdtree.KDTree(len(parentRoots)) for i, root in enumerate(parentRoots): kd.insert(root, i) kd.balance() sourceSurface_BVHT = BVHTree.FromObject(particleObj, context.depsgraph) childStrandsPoints = [] #will contain strands with child points childStrandRootNormals = [] length_ver_group_index = -1 vertex_group_length_name = psys_active.vertex_group_length if vertex_group_length_name: # calc weight based on root point length_ver_group_index = particleObj.vertex_groups[ vertex_group_length_name].index particleObjMesh = particleObj.to_mesh(context.depsgraph, apply_modifiers=True, calc_undeformed=False) seed(a=self.lenSeed, version=2) embed = self.embed * 0.04 * diagonal cpow = calc_power(self.noiseFalloff) cpowClump = calc_power(self.ClumpingFalloff) noiseFalloff = [pow(i / self.t_in_y, cpow) for i in range(self.t_in_y)] ClumpFalloff = [ pow((i + 1) / self.t_in_y, cpowClump) for i in range(self.t_in_y) ] for i, childRoot in enumerate( pointsChildRoots ): #for each child find it three parents and genereate strands by barycentric transform snappedPoint, normalChildRoot, rootHitIndex, distance = sourceSurface_BVHT.find_nearest( childRoot, searchDistance) childStrandRootNormals.append(normalChildRoot) threeClosestParentRoots = kd.find_n( childRoot, 3) #find three closes parent roots rootTri_co, ParentRootIndices, distances = zip( *threeClosestParentRoots) #split it into 3 arrays sourceTri_BVHT = BVHTree.FromPolygons( rootTri_co, [(0, 1, 2)], all_triangles=True) # [0,1,2] - polygon == vert indices list childRootSnapped, normalChildProjected, index, distance = sourceTri_BVHT.find_nearest( childRoot, searchDistance ) #snap generated child to parent triangle ares \normals are sometimes flipped childRootSnapped2, normalChildProjected2, index2, distance2 = sourceSurface_BVHT.find_nearest( childRootSnapped, searchDistance) #this gives ok normals always lenWeight = 1 if length_ver_group_index != -1: # if vg exist averageWeight = 0 for vertIndex in particleObjMesh.polygons[ rootHitIndex].vertices: #DONE: check if work on mesh with modifiers for group in particleObjMesh.vertices[vertIndex].groups: if group.group == length_ver_group_index: averageWeight += group.weight break lenWeight = averageWeight / len( particleObjMesh.polygons[rootHitIndex].vertices) ranLen = uniform(-self.RandomizeLengthMinus, self.RandomizeLengthPlus) lenWeight *= (1 + ranLen) # diff = childRoot - childRootSnapped # mat_loc = Matrix.Translation(childRootSnapped) # matTriangleSpaceInv = mat_loc #* rotMatrix # matTriangleSpaceInv.invert() rotQuat = normalChildProjected2.rotation_difference( normalChildRoot) translationMatrix = Matrix.Translation(childRoot) rotMatrixRot = rotQuat.to_matrix().to_4x4() mat_sca = Matrix.Scale(lenWeight, 4) transformMatrix = translationMatrix @ rotMatrixRot strandPoints = [] #for childRootSnapped points transform them from parent root triangles to parent next segment triangle t1,t2,t3 # and compensate child snapping to root triangle from before for j, (t1, t2, t3) in enumerate( zip(pointsList[ParentRootIndices[0]], pointsList[ParentRootIndices[1]], pointsList[ParentRootIndices[2]])): pointTransformed = barycentric_transform( childRootSnapped, rootTri_co[0], rootTri_co[1], rootTri_co[2], Vector(t1), Vector(t2), Vector(t3)) childInterpolatedPoint = transformMatrix @ mat_sca @ ( pointTransformed - childRootSnapped ) #rotate child strand to original pos (from before snapt) #do noise noise.seed_set(self.Seed + i) # add seed per strand/ring ? noiseVectorPerStrand = noise.noise_vector( childInterpolatedPoint * self.freq / diagonal, noise_basis='PERLIN_ORIGINAL' ) * noiseFalloff[j] * self.noiseAmplitude * diagonal / 10 # childInterpolatedPoint += noiseVectorPerStrand #do clumping diff = Vector( t1 ) - childInterpolatedPoint # calculate distance to parent strand (first strand from trio) # point += noiseVectorPerStrand * noiseFalloff[j] * self.noiseAmplitude * diagonal / 10 # childClumped = childInterpolatedPoint + ClumpFalloff[j] * self.Clumping * diff + noiseVectorPerStrand * (1-ClumpFalloff[j]) childClumped = childInterpolatedPoint + ClumpFalloff[ j] * self.Clumping * diff + noiseVectorPerStrand * ( 1 - ClumpFalloff[j] * self.Clumping) # childClumped = childInterpolatedPoint + noiseVectorPerStrand strandPoints.append(childClumped) # embeding roots diff = strandPoints[0] - strandPoints[1] diff.normalize() normalWeight = abs(diff.dot(normalChildRoot)) strandPoints[0] += ( diff * normalWeight - normalChildRoot * (1 - normalWeight) ) * embed # do childStrandRootNormal to move it more into mesh surface childStrandsPoints.append(strandPoints) bpy.data.meshes.remove(particleObjMesh) # create the Curve Datablock curveData = bpy.data.curves.new(particleObj.name + '_curve', type='CURVE') splinePointsNp = np.array(childStrandsPoints, dtype=np.float32) if self.hairType != 'BEZIER': splinePointsNpOnes = np.ones( (len(childStrandsPoints), self.t_in_y, 4), dtype=np.float32) # 4 coord x,y,z ,1 splinePointsNpOnes[:, :, :-1] = splinePointsNp splinePointsNp = splinePointsNpOnes for strandPoints in splinePointsNp: # for strand point curveLength = len(strandPoints) polyline = curveData.splines.new(self.hairType) if self.hairType == 'BEZIER': polyline.bezier_points.add(curveLength - 1) elif self.hairType == 'POLY' or self.hairType == 'NURBS': polyline.points.add(curveLength - 1) if self.hairType == 'NURBS': polyline.order_u = 3 # like bezier thing polyline.use_endpoint_u = True if self.hairType == 'BEZIER': # polyline.bezier_points.co = (x, y, z) polyline.bezier_points.foreach_set("co", strandPoints.ravel()) polyline.bezier_points.foreach_set('handle_left_type', 'AUTO') polyline.bezier_points.foreach_set('handle_right_type', 'AUTO') else: polyline.points.foreach_set("co", strandPoints.ravel()) # polyline.points[i].co = (x, y, z, 1) curveData.resolution_u = self.strandResU curveData.dimensions = '3D' # create Object curveOB = bpy.data.objects.new(particleObj.name + '_curve', curveData) curveOB.matrix_world = particleObj.matrix_world scn = context.scene scn.collection.objects.link(curveOB) curveOB.targetObjPointer = particleObj.name # store source surface for snapping oper context.view_layer.objects.active = curveOB curveOB.select_set(True) # curveOB.data.show_normal_face = False if self.generateRibbons: bpy.ops.object.generate_ribbons(strandResU=self.strandResU, strandResV=self.strandResV, strandWidth=self.strandWidth, strandPeak=self.strandPeak, strandUplift=self.strandUplift, alignToSurface=self.alignToSurface) HT_OT_CurvesUVRefresh.uvCurveRefresh(curveOB) context.view_layer.objects.active = particleObj else: curveData.fill_mode = 'FULL' curveData.bevel_depth = 0.004 * diagonal curveData.bevel_resolution = 2 bpy.ops.object.curve_taper(TipRadiusFalloff=self.RadiusFalloff, TipRadius=self.TipRadius, MainRadius=self.Radius) return {"FINISHED"}
def __init__(self, vertices): self.kdt = kdtree.KDTree(len(vertices)) for i, v in enumerate(vertices): self.kdt.insert(v, i) self.kdt.balance() self.__description__ = "Voronoi"
def particle_hair_to_points_with_children(self, context): partsysMod = self.particleObj.particle_systems.active # use last particle_id = self.particleObj.particle_systems.active_index # use last particle_modal_settings = context.active_object.modal_hair.particle_system_modal_settings[ particle_id] # cos using self.particleObj gives random settings... diagonal = self.diagonal particle_count = len(partsysMod.particles) if particle_count == 0: #require more that three strands return self.particle_hair_to_points(context) pointsList_hair = [] coords = [] global PARTICLE_CO #cache strands for drawing strands_len = len(partsysMod.particles[0].hair_keys) - 1 for strand in partsysMod.particles: # for strand point pointsList_hair.append([ hair_key.co for hair_key in strand.hair_keys ]) # DONE: exclude duplicates if first strand[0] in list already if context.active_object.modal_hair.drawStrands: for i, point in enumerate( strand.hair_keys): # create p1,p2, p2,p3, p3,p4 if i == 0 or i == strands_len: coords.append((point.co[0], point.co[1], point.co[2])) else: coords.extend([(point.co[0], point.co[1], point.co[2]), (point.co[0], point.co[1], point.co[2]) ]) else: coords = [(0, 0, 0)] PARTICLE_CO = coords if particle_count == 1: #create two fake strands so that barycentric works pointsList_hair.append([ x.xyz + Vector((0.01 * diagonal, 0, 0)) for x in pointsList_hair[0] ]) pointsList_hair.append([ x.xyz + Vector((0, 0.01 * diagonal, 0)) for x in pointsList_hair[0] ]) elif particle_count == 2: #create one fake strands so that barycentric works pointsList_hair.append([ x.xyz + Vector((0.01 * diagonal, 0, 0)) for x in pointsList_hair[0] ]) #TODO: damm it is slow - 0.3 sec - on 3333 particle mesh (while doing sinterpol is just 0.02 sec) #FIXED: it was here cos particles woudl fail on duplicates.... Fixed by removing duplis in init # pointsList_uniq = [] # [pointsList_uniq.append(x) for x in pointsList_hair if x not in pointsList_uniq] # removing doubles (can cause zero size tris) pointsList_uniq = pointsList_hair # same_point_count cos barycentric transform requires it pointsList = interpol_Catmull_Rom( pointsList_uniq, particle_modal_settings.t_in_y, uniform_spacing=False, same_point_count=True) # just gives smoother result on borders searchDistance = 100 * diagonal parentRoots = [strand[0] for strand in pointsList] # first point of roots # create nnew Part Sytem with uniform points pointsChildRoots = HT_OT_RibbonsFromParticleHairChild.createUniformParticleSystem( context, particle_modal_settings.childCount, particle_modal_settings.PlacementJittering ) # return child part roots positions kd = kdtree.KDTree(len(parentRoots)) for i, root in enumerate(parentRoots): kd.insert(root, i) kd.balance() sourceSurface_BVHT = self.sourceSurface_BVHT childStrandsPoints = [] # will contain strands with child points childStrandRootNormals = [] seed(a=particle_modal_settings.lenSeed, version=2) for i, childRoot in enumerate( pointsChildRoots ): # for each child find it three parents and genereate strands by barycentric transform snappedPoint, normalChildRoot, rootHitIndex, distance = sourceSurface_BVHT.find_nearest( childRoot, searchDistance) childStrandRootNormals.append(normalChildRoot) threeClosestParentRoots = kd.find_n( childRoot, 3) # find three closes parent roots rootTri_co, ParentRootIndices, distances = zip( *threeClosestParentRoots) # split it into 3 arrays sourceTri_BVHT = BVHTree.FromPolygons( rootTri_co, [(0, 1, 2)], all_triangles=True) # [0,1,2] - polygon == vert indices list childRootSnapped, normalChildProjected, index, distance = sourceTri_BVHT.find_nearest( childRoot, searchDistance ) # snap generated child to parent triangle ares \normals are sometimes flipped childRootSnapped2, normalChildProjected2, index2, distance2 = sourceSurface_BVHT.find_nearest( childRootSnapped, searchDistance) # this gives ok normals always lenWeight = 1 + uniform( -particle_modal_settings.RandomizeLengthMinus, particle_modal_settings.RandomizeLengthPlus) rotQuat = normalChildProjected2.rotation_difference( normalChildRoot) translationMatrix = Matrix.Translation(childRoot) rotMatrixRot = rotQuat.to_matrix().to_4x4() mat_sca = Matrix.Scale(lenWeight, 4) transformMatrix = translationMatrix @ rotMatrixRot strandPoints = [] # for childRootSnapped points transform them from parent root triangles to parent next segment triangle t1,t2,t3 # and compensate child snapping to root triangle from before for j, (t1, t2, t3) in enumerate( zip(pointsList[ParentRootIndices[0]], pointsList[ParentRootIndices[1]], pointsList[ParentRootIndices[2]])): pointTransformed = barycentric_transform( childRootSnapped, rootTri_co[0], rootTri_co[1], rootTri_co[2], Vector(t1), Vector(t2), Vector(t3)) childInterpolatedPoint = transformMatrix @ mat_sca @ ( pointTransformed - childRootSnapped ) # rotate child strand to original pos (from before snapt) strandPoints.append(childInterpolatedPoint) childStrandsPoints.append(strandPoints) if particle_modal_settings.use_parent_strands: childStrandsPoints.extend(pointsList) return childStrandsPoints
def create_u_core_vert_groups_vert_lists_2(bm, dimensions, margin, vert_locs, subdivs): """Create vertex group vertex lists for U core sides Args: bm (bmesh): bmesh dimensions (dict{ leg_1_inner: float, leg_2_inner: float, base_height: float, height: float, x_inner: float, thickness: float, thickness_diff: float}): core dimensions margin (float): margin between textured and blank areas vert_locs (dict{ Leg 1 Inner: list[Vector(3)] Leg 2 Inner: list[Vector(3)] Leg 1 Outer: list[Vector(3)] Leg 2 Outer: list[Vector(3)] Leg 1 End: list[Vector(3)] Leg 2 End: list[Vector(3)] End Wall Inner': list[Vector(3)] End Wall Outer': list[Vector(3)]}: vertex locations subdivs (dict{ leg_1: int, leg_2: int, x: int, width: int, height: int}): subdivisions Returns: dict{ Leg 1 Inner: list[BMVert], Leg 1 Outer: list[BMVert], Leg 2 Inner: list[BMVert], Leg 2 Outer: list[BMVert], Leg 1 Top: list[BMVert], Leg 2 Top: list[BMVert], Leg 1 Bottom: list[BMVert], Leg 2 Bottom: list[BMVert], Leg 1 End: list[BMVert], Leg 2 End: list[BMVert], End Wall Inner: list[BMVert], End Wall Outer: list[BMVert], End Wall Top: list[BMVert] End Wall Bottom: list[BMVert]}: Verts to assign to vert groups """ height = dimensions['height'] thickness_diff = dimensions['thickness_diff'] thickness = dimensions['thickness'] leg_1_inner_len = dimensions['leg_1_inner'] + (thickness_diff / 2) leg_2_inner_len = dimensions['leg_2_inner'] + (thickness_diff / 2) leg_1_outer_len = leg_1_inner_len + thickness leg_2_outer_len = leg_2_inner_len + thickness vert_groups = {} # create kdtree size = len(bm.verts) kd = kdtree.KDTree(size) for i, v in enumerate(bm.verts): kd.insert(v.co, i) kd.balance() # leg_sides leg_sides = { 'Leg 1 Inner': (vert_locs['Leg 1 Inner'][::-1], leg_1_inner_len), 'Leg 2 Inner': (vert_locs['Leg 2 Inner'], leg_2_inner_len), 'Leg 1 Outer': (vert_locs['Leg 1 Outer'], leg_1_outer_len), 'Leg 2 Outer': (vert_locs['Leg 2 Outer'][::-1], leg_2_outer_len) } for key, value in leg_sides.items(): vert_groups[key] = select_verts_in_bounds( lbound=(value[0][0]), ubound=(value[0][-1][0], value[0][-1][1] + value[1], value[0][-1][2] + height), buffer=margin / 2, bm=bm) end_wall_sides = { 'End Wall Inner': vert_locs['End Wall Inner'], 'End Wall Outer': vert_locs['End Wall Outer'][::-1] } # end_wall_sides for key, value in end_wall_sides.items(): vert_groups[key] = select_verts_in_bounds( lbound=(value[0]), ubound=(value[-1][0], value[-1][1], value[-1][2] + height), buffer=margin / 2, bm=bm) # leg ends ends = { 'Leg 1 End': vert_locs['Leg 1 End'], 'Leg 2 End': vert_locs['Leg 2 End'] } for key, value in ends.items(): vert_groups[key] = select_verts_in_bounds( lbound=(value[0]), ubound=(value[1][0], value[1][1], value[1][2] + height), buffer=margin / 2, bm=bm) bm_deselect_all(bm) # bottom # leg 1 inner_locs = vert_locs['Leg 1 Inner'][::-1] outer_locs = vert_locs['Leg 1 Outer'] selected_verts = [] i = 0 while i < len(outer_locs) and i < len(inner_locs): v1_co, v1_index, dist = kd.find(inner_locs[i]) v2_co, v2_index, dist = kd.find(outer_locs[i]) bm.verts.ensure_lookup_table() v1 = bm.verts[v1_index] v2 = bm.verts[v2_index] nodes = bm_shortest_path(bm, v1, v2) node = nodes[v2] for e in node.shortest_path: e.select_set(True) bm.select_flush(True) verts = [v for v in bm.verts if v.select] selected_verts.extend(verts) i += 1 vert_groups['Leg 1 Bottom'] = selected_verts bm_deselect_all(bm) # leg 2 inner_locs = vert_locs['Leg 2 Inner'][::-1] outer_locs = vert_locs['Leg 2 Outer'] selected_verts = [] i = 0 while i < len(inner_locs) and i < len(outer_locs): v1_co, v1_index, dist = kd.find(inner_locs[i]) v2_co, v2_index, dist = kd.find(outer_locs[i]) bm.verts.ensure_lookup_table() v1 = bm.verts[v1_index] v2 = bm.verts[v2_index] nodes = bm_shortest_path(bm, v1, v2) node = nodes[v2] for e in node.shortest_path: e.select_set(True) bm.select_flush(True) verts = [v for v in bm.verts if v.select] selected_verts.extend(verts) i += 1 vert_groups['Leg 2 Bottom'] = selected_verts bm_deselect_all(bm) # end wall inner_locs = vert_locs['End Wall Inner'][::-1] outer_locs = vert_locs['End Wall Outer'] selected_verts = [] i = 0 while i < len(inner_locs) and i < len(outer_locs): v1_co, v1_index, dist = kd.find(inner_locs[i]) v2_co, v2_index, dist = kd.find(outer_locs[i]) bm.verts.ensure_lookup_table() v1 = bm.verts[v1_index] v2 = bm.verts[v2_index] nodes = bm_shortest_path(bm, v1, v2) node = nodes[v2] for e in node.shortest_path: e.select_set(True) bm.select_flush(True) verts = [v for v in bm.verts if v.select] selected_verts.extend(verts) i += 1 vert_groups['End Wall Bottom'] = selected_verts bm_deselect_all(bm) # top vert_groups['Leg 1 Top'] = [] vert_groups['Leg 2 Top'] = [] vert_groups['End Wall Top'] = [] bm.verts.ensure_lookup_table() for v in vert_groups['Leg 1 Bottom']: v_co, v_index, dist = kd.find((v.co[0], v.co[1], v.co[2] + height)) vert_groups['Leg 1 Top'].append(bm.verts[v_index]) bm.verts.ensure_lookup_table() for v in vert_groups['Leg 2 Bottom']: v_co, v_index, dist = kd.find((v.co[0], v.co[1], v.co[2] + height)) vert_groups['Leg 2 Top'].append(bm.verts[v_index]) bm.verts.ensure_lookup_table() for v in vert_groups['End Wall Bottom']: v_co, v_index, dist = kd.find((v.co[0], v.co[1], v.co[2] + height)) vert_groups['End Wall Top'].append(bm.verts[v_index]) return vert_groups
def test_kdtree_invalid_size(self): with self.assertRaises(ValueError): kdtree.KDTree(-1)
def get_closest_vertex(self): if hasattr(self.bm.verts, "ensure_lookup_table"): self.bm.verts.ensure_lookup_table() kd = kdtree.KDTree(len(self.bm.verts)) for i, v in enumerate(self.bm.verts): kd.insert(v.co, i) kd.balance() kd_closest_vertex = kd.find_n(self.hit_location, MAX_SEARCH) #print("kd_closest_vertex ", kd_closest_vertex) #print(">Av ", self.active_vertex) neighbors = [] for c in kd_closest_vertex: v = self.bm.verts[c[1]] if (len(self.bm.verts) == 1 or v.is_wire or v.is_boundary or v in self.active_solution[1]) and (v != self.active_vertex): # 0 index, 1 distance, 2 hit distance, 3 score neighbors.append([v, c[2], 0., [1.]]) #print("neighbors ", neighbors) # Sort n = float(len(neighbors)) if(n > 1): max_hit_distance = -1. #min_hit_distance = float('inf') #max_distance = -1. #min_distance = float('inf') for i, v in enumerate(neighbors): c_hit_distance, c_hit_normal = get_closest_ray_hit_test(v[0], self.object, self.snap_obj, self.hit_normal) #print("c_hit_distance", c_hit_distance) if(c_hit_normal != None): v[3].append( (n - i) / n) v[2] = c_hit_distance + SNAP_TOLERANCE hit_conform = 1 if(not v[0].is_wire): conform_angle = v[0].normal.angle(c_hit_normal, pi) hit_conform = (-(conform_angle / pi)**2 + 1) / 2 + 0.5 v[3].append(hit_conform) conform = 1 if(not v[0].is_wire): conform_angle = v[0].normal.angle(self.hit_normal, pi) conform = (-(conform_angle / pi)**2 + 1) / 2 + 0.5 v[3].append(conform) #if(v[1] > max_distance): max_distance = v[1] #if(v[1] < min_distance): min_distance = v[1] if(v[2] > max_hit_distance): max_hit_distance = v[2] #if(v[2] < min_hit_distance): min_hit_distance = v[2] else: v[3][0] = 0 for v in neighbors: if ( (1. - v[2]/max_hit_distance) > 0.5): v[3].append( 1.) else: v[3].append(.5) #v[3].append(v[1] / max_distance) neighbors.sort(key=lambda x: prod(x[3]), reverse=True) if (neighbors != []): #for v in neighbors: #try: # print("--- v %5d "% v[0].index, "| [dist %.3f, conform %.3f, snap dist %.3f]"% (v[3][1], v[3][2], v[3][3])) #except: # pass return (neighbors[0][0], neighbors[0][1]) else: return (None, None)
def add_geometry(self): DEBUG = False self.time_it = BL_TOOLS.TimeIt() BL_DEBUG.clear_marks() self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() # create a kd tree with the vertex positions vertex = [] for v in self.selected_vertex: if v.is_valid: vertex.append(v.index) # remove dupes vertex = list(set(vertex)) kd = kdtree.KDTree(len(vertex)) for v in vertex: ##BL_DEBUG.set_mark(self.bm.verts[v].co,kind="CUBE",scale=0.2) kd.insert(self.bm.verts[v].co, v) kd.balance() def nearest_points(kd, edge, v0, side, Nitems=7): nearest = [] for (co, index, dist) in kd.find_n(v0, Nitems): # calculate normals A = self.obj_t.matrix_world @ edge[0].co B = self.obj_t.matrix_world @ edge[1].co C = self.obj_t.matrix_world @ co normal_vec = Vector(B-A).cross(Vector(C-A)) normal_rl = normal_vec.z if normal_rl < 0.0 and side.upper() == 'RIGHT': nearest.append( (co, index, dist) ) if normal_rl > 0.0 and side.upper() == 'LEFT': nearest.append( (co, index, dist) ) return(nearest) # iterate my vertex, and find the nearest point my vertex side_keys = { 0: 'right', 1: 'left', 'right': 0, 'left': 1} count = 0 B = C = None for plane in self.mesh_vertex: print(plane) nearest = {} # calculate the nearest point for each point, for each side for i in range(len(plane)): if i == 0: #right ARROWS->CUBES kind = 'ARROWS' else: #left AXES->SPHERES kind = 'PLAIN_AXES' #BL_DEBUG.set_mark(self.obj_t.matrix_world @ plane[i][0].co,kind=kind) #BL_DEBUG.set_mark(self.obj_t.matrix_world @ plane[i][1].co,kind=kind) nearest[side_keys[i]] = [ nearest_points(kd, plane[i], self.obj_t.matrix_world @ plane[i][0].co, side_keys[i]), nearest_points(kd, plane[i], self.obj_t.matrix_world @ plane[i][1].co, side_keys[i]) ] A = plane[0][1] D = plane[0][0] # lower point if not C: _,C,_ = nearest['right'][0][0] _,B,_ = nearest['right'][1][0] if B == C: _,B,_ = nearest['right'][1][1] else: C = B _,B,_ = nearest['right'][1][0] if B == C: _,B,_ = nearest['right'][1][1] print(A,B,C,D) # if not B in used_points.keys(): used_points[B] = 1 # if not C in used_points.keys(): used_points[C] = 1 # if B in used_points.keys() and used_points[B] < 2: # used_points[B] += 1 # else: # B = None # if C in used_points.keys() and used_points[C] < 2: # used_points[C] += 1 # else: # C = None # first point: nearest from top, and in nearest of bottom one. a = nearest['right'][0] print("r0-") for i in a: co,idx,dist = i print(co,idx,dist) DEBUG and BL_DEBUG.set_mark(co,kind="ARROWS") a = nearest['right'][1] print("r1-") for i in a: co,idx,dist = i print(co,idx,dist) DEBUG and BL_DEBUG.set_mark(co,kind="ARROWS") a = nearest['left'][0] print("l0-") for i in a: co,idx,dist = i print(co,idx,dist) DEBUG and BL_DEBUG.set_mark(co,kind="PLAIN_AXES") a = nearest['left'][1] print("l1-") for i in a: co,idx,dist = i print(co,idx,dist) DEBUG and BL_DEBUG.set_mark(co,kind="PLAIN_AXES") ## right side # top_vertex_list = [] # for _,idx,_ in nearest['right'][1]: # top_vertex_list.append(idx) # bottom_vertex_list = [] # for _,idx,_ in nearest['right'][0]: # bottom_vertex_list.append(idx) # # default, first item # # bottom # pB = None # get_next = False # _,pA,_ = nearest['right'][0][0] # for data in nearest['right'][0]: # co,idx,dist = data # if get_next: # pA = idx # break # if idx in top_vertex_list and not pB: # pB = pA # get_next = True # if not pB: # _,pB,_ = nearest['right'][1][0] # for data in nearest['right'][1]: # co,idx,dist = data # pB = idx # break if B and C: pBv = self.bm.verts[B] pCv = self.bm.verts[C] DEBUG and BL_DEBUG.set_mark(pBv.co,kind="CUBE") DEBUG and BL_DEBUG.set_mark(pCv.co,kind="CUBE") bmesh.ops.contextual_create(self.bm, geom=[A, pBv, pCv, D]) self.bm.faces.index_update() # ## left side # top_vertex_list = [] # for i in nearest['left'][1]: # co,idx,dist = i # top_vertex_list.append(idx) # bottom_vertex_list = [] # for i in nearest['left'][0]: # co,idx,dist = i # bottom_vertex_list.append(idx) # # default, first item # # bottom # pD = None # get_next = False # _,pC,_ = nearest['left'][0][0] # for data in nearest['left'][0]: # co,idx,dist = data # if get_next: # pC = idx # break # if idx in top_vertex_list and not pD: # pD = pC # get_next = True # if not pD: # _,pD,_ = nearest['left'][1][0] # for data in nearest['left'][1]: # co,idx,dist = data # pD = idx # break # pCv = self.bm.verts[pC] # pDv = self.bm.verts[pD] # DEBUG and BL_DEBUG.set_mark(pCv.co,kind="SPHERE") # DEBUG and BL_DEBUG.set_mark(pDv.co,kind="SPHERE") # if plane[0][1] != pBv and pBv != pAv and pAv != plane[0][0]: # bmesh.ops.contextual_create(self.bm, geom=[plane[0][0], pAv, pBv, plane[0][1]]) # self.bm.faces.index_update() # if plane[1][1] != pCv and pCv != pDv and pDv != plane[1][0]: # bmesh.ops.contextual_create(self.bm, geom=[plane[1][1], plane[1][0], pDv, pCv]) # self.bm.faces.index_update() # do just ones count +=1 if count > 5: break # update the index of the new created verts. WTF ??? self.update_bm() self.time_it.stop() print("[t] add_geometry(): ", self.time_it) #return(("ERROR", "Can't found terrain below the curve")) return(("INFO", "Done"))
def update(self): if 'vec' in self.inputs and 'edg' in self.inputs: print(self.name, 'is starting') if self.inputs['vec'].links and self.inputs['edg'].links: vec = self.inputs['vec'].sv_get() edg = self.inputs['edg'].sv_get() vecplan = self.inputs['vecplan'].sv_get() edgplan = self.inputs['edgplan'].sv_get() loc = self.inputs['loc'].sv_get() norm = self.inputs['norm'].sv_get() thick = self.inputs['thick'].sv_get()[0][0] sinuso60 = 0.8660254037844386 sinuso60_minus = 0.133974596 sinuso30 = 0.5 sinuso45 = 0.7071067811865475 if 'loccont' in self.inputs and self.inputs['loccont'].links and \ 'normcont' in self.inputs and self.inputs['normcont'].links: vecont = self.inputs['vecont'].sv_get() loccont = self.inputs['loccont'].sv_get() normcont = self.inputs['normcont'].sv_get() vec_cont = Vector_generate(vecont) loc_cont = Vector_generate(loccont) norm_cont = Vector_generate(normcont) else: norm_cont = [[Vector((0,0,1)) for i in range(len(norm[0]))]] loc_cont = [[Vector((0,0,10000)) for i in range(len(norm[0]))]] vec_cont = [[Vector((1000,0,1))] for i in range(len(norm[0]))] outeup = [] outelo = [] vupper = [] vlower = [] vec_ = Vector_generate(vec) loc_ = Vector_generate(loc) norm_ = Vector_generate(norm) vecplan_ = Vector_generate(vecplan) #print(self.name, 'veriables: \n', \ # vec_,'\n', # vecplan_,'\n', # loc_,'\n', # loc_cont) for l,n,vecp, edgp in zip(loc_[0],norm_[0],vecplan_,edgplan): newinds1 = edgp.copy() newinds2 = edgp.copy() vupperob = vecp.copy() vlowerob = vecp.copy() deledges1 = [] deledges2 = [] k = 0 lenvep = len(vecp) # KDtree collections closest to join edges to sockets tree = KDT.KDTree(lenvep) for i,v in enumerate(vecp): tree.insert(v,i) tree.balance() # to define bounds x = [i[0] for i in vecp] y = [i[1] for i in vecp] m1x,m2x,m1y,m2y = max(x), min(x), max(y), min(y) # vertical edges iterations # every edge is object - two points, one edge for v in vec_: # sort vertices by Z value # find two vertices - one lower, two upper vlist = [v[0],v[1]] vlist.sort(key=lambda x: x[2], reverse=False) # flip if coplanar to enemy plane # flip plane coplanar fliped = self.get_coplanar(v[0], loc_cont,norm_cont, vec_cont) if fliped: two, one = vlist else: one, two = vlist # coplanar to owner cop = abs(D2P(one,l,n)) # defining bounds inside = one[0]<m1x and one[0]>m2x and one[1]<m1y and one[1]>m2y # if in bounds and coplanar do: #print(self.name,l, cop, inside) if cop < 0.001 and inside: ''' huge calculations. if we can reduce... ''' # find shift for thickness in sockets angle = radians(degrees(atan(n.y/n.x))+90) thick_2 = thick/2 direction = Vector((cos(angle),sin(angle),0))*thick_2 #matr = Euler((0,0,angle),'YZX').to_matrix().to_4x4() #matr.translation = #direction = matr # вектор, индекс, расстояние # запоминаем порядок # находим какие удалить рёбра # делаем выборку левая-правая точка nearv_1, near_1 = tree.find(one)[:2] nearv_2, near_2 = tree.find(two)[:2] # indexes of two nearest points # удалить рёбра что мешают спать заодно en_0, en_1, de1 = self.calc_indexes(edgp, near_1) deledges1.extend(de1) en_2, en_3, de2 = self.calc_indexes(edgp, near_2) deledges2.extend(de2) # old delete # en_0,en_1 = [[t for t in i if t != near_1] for i in edgp if near_1 in i] # en_2,en_3 = [[t for t in i if t != near_2] for i in edgp if near_2 in i] # print(vecp, one, direction, en_0, en_1) # left-right indexes and vectors # с учётом интерполяций по высоте left1, right1, l1, r1, lz1, rz1 = \ self.calc_leftright(vecp, one, direction, en_0, en_1, thick_2) left2, right2, l2, r2, lz2, rz2 = \ self.calc_leftright(vecp, two, direction, en_2, en_3, thick_2) # средняя точка и её смещение по толщине материала three = (one-two)/2 + two if self.rounded: '''рёбра''' if fliped: doflip = -1 else: doflip = 1 # пазы формируем независимо от верх низ outeob1 = [[lenvep+k+8,lenvep+k],[lenvep+k+1,lenvep+k+2], [lenvep+k+2,lenvep+k+3],[lenvep+k+3,lenvep+k+4], [lenvep+k+4,lenvep+k+5],[lenvep+k+5,lenvep+k+6], [lenvep+k+6,lenvep+k+7],[lenvep+k+7,lenvep+k+8], [lenvep+k+9,lenvep+k+1]] outeob2 = [[lenvep+k,lenvep+k+1],[lenvep+k+1,lenvep+k+2], [lenvep+k+2,lenvep+k+3],[lenvep+k+3,lenvep+k+4], [lenvep+k+4,lenvep+k+5],[lenvep+k+5,lenvep+k+6], [lenvep+k+6,lenvep+k+7],[lenvep+k+7,lenvep+k+8], [lenvep+k+8,lenvep+k+9]] # наполнение списков lenvep = length(vecp) newinds1.extend([[l1, lenvep+k], [lenvep+k+9, r1]]) newinds2.extend([[l2, lenvep+k+9], [lenvep+k, r2]]) '''Вектора''' thick_3 = thick/3 thick_6 = thick/6 round1 = Vector((0,0,doflip*thick_3)) round2 = Vector((0,0,doflip*thick_3*sinuso30)) round2_= direction/3 + direction*(2*sinuso60/3) round3 = Vector((0,0,doflip*thick_3*sinuso60_minus)) round3_= direction/3 + direction*(2*sinuso30/3) round4 = direction/3 vupperob.extend([two-direction-Vector((0,0,lz2)), three+round1-direction, three+round2-round2_, three+round3-round3_, three-round4, three+round4, three+round3+round3_, three+round2+round2_, three+round1+direction, two+direction-Vector((0,0,rz2))]) vlowerob.extend([one+direction-Vector((0,0,rz1)), three-round1-direction, three-round2-round2_, three-round3-round3_, three-round4, three+round4, three-round3+round3_, three-round2+round2_, three-round1+direction, one-direction-Vector((0,0,lz1))]) k += 10 else: '''рёбра''' # пазы формируем независимо от верх низ outeob1 = [[lenvep+k,lenvep+k+1],[lenvep+k+1,lenvep+k+2],[lenvep+k+2,lenvep+k+3]] outeob2 = [[lenvep+k,lenvep+k+1],[lenvep+k+1,lenvep+k+2],[lenvep+k+2,lenvep+k+3]] # наполнение списков lenvep = length(vecp) newinds1.extend([[l1, lenvep+k], [lenvep+k+3, r1]]) newinds2.extend([[l2, lenvep+k+3], [lenvep+k, r2]]) '''Вектора''' vupperob.extend([two-direction-Vector((0,0,lz2)), three-direction, three+direction, two+direction-Vector((0,0,rz2))]) vlowerob.extend([one+direction-Vector((0,0,rz1)), three+direction, three-direction, one-direction-Vector((0,0,lz1))]) k += 4 newinds1.extend(outeob1) newinds2.extend(outeob2) del tree for e in deledges1: if e in newinds1: newinds1.remove(e) for e in deledges2: if e in newinds2: newinds2.remove(e) if vupperob or vlowerob: outeup.append(newinds2) outelo.append(newinds1) vupper.append(vupperob) vlower.append(vlowerob) vupper = Vector_degenerate(vupper) vlower = Vector_degenerate(vlower) if 'vupper' in self.outputs and self.outputs['vupper'].links: out = dataCorrect(vupper) SvSetSocketAnyType(self, 'vupper', out) if 'outeup' in self.outputs and self.outputs['outeup'].links: SvSetSocketAnyType(self, 'outeup', outeup) if 'vlower' in self.outputs and self.outputs['vlower'].links: SvSetSocketAnyType(self, 'vlower', vlower) if 'outelo' in self.outputs and self.outputs['outelo'].links: SvSetSocketAnyType(self, 'outelo', outelo) print(self.name, 'is finishing')
def __init__(self, context, cut_object, ui_type='DENSE_POLY'): self.cut_ob = cut_object self.bme = bmesh.new() self.bme.from_mesh(cut_object.data) self.bme.verts.ensure_lookup_table() self.bme.edges.ensure_lookup_table() self.bme.faces.ensure_lookup_table() non_tris = [f for f in self.bme.faces if len(f.verts) > 3] #if len(non_tris): #geom = bmesh.ops.connect_verts_concave(self.bme, non_tris) #self.bme.verts.ensure_lookup_table() #self.bme.edges.ensure_lookup_table() #self.bme.faces.ensure_lookup_table() self.bvh = BVHTree.FromBMesh(self.bme) self.cyclic = False self.start_edge = None self.end_edge = None self.pts = [] self.cut_pts = [] #local points self.normals = [] self.face_map = [] #all the faces that user drawn poly line fall upon self.face_changes = [ ] #the indices where the next point lies on a different face self.face_groups = dict( ) #maps bmesh face index to all the points in user drawn polyline which fall upon it self.new_ed_face_map = dict( ) #maps face index in bmesh to new edges created by bisecting self.ed_map = [] #existing edges in bmesh crossed by cut line self.new_cos = [] #location of crosses self.non_man_eds = [ ed.index for ed in self.bme.edges if not ed.is_manifold ] self.non_man_ed_loops = edge_loops_from_bmedges( self.bme, self.non_man_eds) #print(self.non_man_ed_loops) self.non_man_points = [] self.non_man_bmverts = [] for loop in self.non_man_ed_loops: self.non_man_points += [ self.cut_ob.matrix_world * self.bme.verts[ind].co for ind in loop ] self.non_man_bmverts += [self.bme.verts[ind].index for ind in loop] if len(self.non_man_points): kd = kdtree.KDTree(len(self.non_man_points)) for i, v in enumerate(self.non_man_points): kd.insert(v, i) kd.balance() self.kd = kd else: self.kd = None self.face_chain = set() #all faces crossed by the cut curve if ui_type not in {'SPARSE_POLY', 'DENSE_POLY', 'BEZIER'}: self.ui_type = 'SPARSE_POLY' else: self.ui_type = ui_type self.selected = -1 self.hovered = [None, -1] self.grab_undo_loc = None self.start_edge_undo = None self.end_edge_undo = None self.mouse = (None, None) #keep up with these to show user self.bad_segments = [] self.split = False self.face_seed = None
def add_geometry_edge(self): "try to match near edges" def edge_median(edge): v0,v1 = edge.verts median = v0.co + ((v0.co-v1.co)/2.0) return(median) def nearest_points(kd, edge, v0, side, Nitems=7): nearest = [] for (co, index, dist) in kd.find_n(v0, Nitems): # calculate normals A = self.obj_t.matrix_world @ edge[0].co B = self.obj_t.matrix_world @ edge[1].co C = self.obj_t.matrix_world @ co normal_vec = Vector(B-A).cross(Vector(C-A)) normal_rl = normal_vec.z if normal_rl < 0.0 and side.upper() == 'RIGHT': nearest.append( (co, index, dist) ) if normal_rl > 0.0 and side.upper() == 'LEFT': nearest.append( (co, index, dist) ) return(nearest) def nearest_edge(kd, edge, side, Nitems=7): nearest = [] median = edge_median(edge) for (co, index, dist) in kd.find_n(self.obj_t.matrix_world @ median, Nitems): # calculate normals A = self.obj_t.matrix_world @ edge.verts[0].co B = self.obj_t.matrix_world @ edge.verts[1].co C = self.obj_t.matrix_world @ co normal_vec = Vector(B-A).cross(Vector(C-A)) normal_rl = normal_vec.z if normal_rl < 0.0 and side.upper() == 'RIGHT': nearest.append( (co, index, dist) ) if normal_rl > 0.0 and side.upper() == 'LEFT': nearest.append( (co, index, dist) ) return(nearest) DEBUG = False self.time_it = BL_TOOLS.TimeIt() BL_DEBUG.clear_marks() self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() # create a kd tree with the median edge positions. median_edges = [] for edge in self.inner_edges: if edge.is_valid and edge.is_boundary: median = edge_median(edge) median_edges.append( (median, edge.index) ) kd = kdtree.KDTree(len(median_edges)) for median,idx in median_edges: kd.insert(median, idx) kd.balance() # iterate my vertex, and find the nearest point my vertex side_keys = { 0: 'right', 1: 'left', 'right': 0, 'left': 1} count = 0 for plane in self.mesh_vertex: print("plane", plane) nearest = {} # calculate the nearest point for each point, for each side for i in range(len(plane)): if i == 0: #right ARROWS->CUBES kind = 'ARROWS' else: #left AXES->SPHERES kind = 'PLAIN_AXES' #nearest[side_keys[i]] = [ # nearest_points(kd, plane[i], self.obj_t.matrix_world @ plane[i][0].co, side_keys[i]), # nearest_points(kd, plane[i], self.obj_t.matrix_world @ plane[i][1].co, side_keys[i]) #] # create the working edge edge = [ plane[i][0], plane[i][1] ] ret_geom = bmesh.ops.contextual_create(self.bm, geom= edge) self.bm.edges.index_update() self.update_bm() for f in ret_geom['edges']: if isinstance(f, bmesh.types.BMEdge): edge = f median = edge_median(edge) BL_DEBUG.set_mark(self.obj_t.matrix_world @ median,kind='CUBE') ## mesh edge n_edge = nearest_edge(kd, edge, side_keys[i]) # build the face, rebuild the tree co,idx,_ = n_edge[0] self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() BL_DEBUG.set_mark(self.obj_t.matrix_world @ co,kind='SPHERE') ## found edge #ret_geom = bmesh.ops.contextual_create(self.bm, geom= [edge, self.bm.edges[idx]]) self.bm.edges.index_update() self.update_bm() # remove the edge from the table. edges_tmp = [] for edge in self.inner_edges: if edge.is_valid and edge.index != idx: edges_tmp.append(edge) self.inner_edges = edges_tmp median_edges = [] for edge in self.inner_edges: if edge.is_valid and edge.is_boundary: median = edge_median(edge) median_edges.append( (median, edge.index) ) kd = kdtree.KDTree(len(median_edges)) for median,idx in median_edges: kd.insert(median, idx) kd.balance() # do just ones count +=1 if count >=1: break # update the index of the new created verts. WTF ??? self.update_bm() self.time_it.stop() print("[t] add_geometry_edges(): ", self.time_it) #return(("ERROR", "Can't found terrain below the curve")) return(("INFO", "Done"))
def do_holes(self): "get the plane, generate the info to raycast in the grid, select the matching faces" self.time_it = BL_TOOLS.TimeIt() EXTEND_SELECTION = True TRIANGULATE_FACES = False # True SUBDIVIDE_INNER_FACES = False EXTEND_DELETE_SELECTION = False SUBDIVIDE_INNER_EDGES = True CALCULATE_NEAR_FACES = True MIN_FACE_DIST = 3 BL_DEBUG.clear_marks() # at this point XD point_data has # terrain_up/down, face_index, point, location) self.unselect_all() self.selected_faces = [] # select faces tracked for point in self.raycast_points: terrain_down, index, point, location = point self.bm.faces[index].select = True if self.bm.faces[index] not in self.selected_faces: self.selected_faces.append(self.bm.faces[index]) if EXTEND_SELECTION: ret_geom = bmesh.ops.region_extend(self.bm, geom=self.selected_faces, use_faces=True, use_face_step=False, use_contract=False) for f in ret_geom['geom']: if isinstance(f, bmesh.types.BMFace): f.select = True self.selected_faces.append(f) if SUBDIVIDE_INNER_FACES: # subdivide the inner faces #el_faces = [f for f in self.bm.faces if f.select] face_edges = [] for f in self.selected_faces: for e in f.edges: face_edges.append(e.index) face_edges = list(set(face_edges)) face_edges_geom = [self.bm.edges[i] for i in face_edges] ret = bmesh.ops.subdivide_edges(self.bm, edges=face_edges_geom, cuts=2, use_grid_fill=True) for f in ret['geom']: if isinstance(f, bmesh.types.BMFace): f.select = True self.selected_faces.append(f) if TRIANGULATE_FACES: #triangulate geom = [f for f in self.bm.faces if f.select] ret_trig = bmesh.ops.triangulate(self.bm, faces=geom) # select the new created triangles for f in ret_trig['faces']: f.select = True self.selected_faces.append(f) # 0. at this point, faces are triangulated, and selected ############################################################## # # 1. delete the faces recalculate again the faces # this version only deletes the nearest faces from the # road. Maybe it's a little narrow # ############################################################## self.update_bm() #self.bm.verts.ensure_lookup_table() #self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() # # I have to CHANGE to OBJECT in order to keep working on that # and update the structure. After change, return to edit and # keep working, it works. # # after triangulate, or adding more faces, there's a need of recalculate again the affected # faces to make the hole self.raycast_points = BL_TOOLS.get_raycast(self.plane, self.terrain, vertices = self.plane_points, DEBUG=self.DEBUG, LIMIT=self.LIMIT) # at this point XD point_data has # terrain_up/down, face_index, point, location) BL_DEBUG.clear_marks() faces_to_delete = {} for item in self.raycast_points: terrain_down, index, point, location = item #print(terrain_down,index, point, location) #BL_DEBUG.set_mark( obj_t.matrix_world @ location.xyz, kind="PLAIN_AXES" ) if index not in faces_to_delete.keys(): faces_to_delete[index] = self.bm.faces[index] if EXTEND_DELETE_SELECTION: for face in self.bm.faces: face.select = False for f in faces_to_delete.keys(): self.bm.faces[f].select = True #sel_verts = [v for v in self.bm.verts if v.select] #sel_edges = [e for e in self.bm.edges if e.select] selected_faces_local = [f for f in self.bm.faces if f.select] geom = selected_faces_local ret_geom = bmesh.ops.region_extend(self.bm, geom=selected_faces_local, use_faces=True, use_face_step=False, use_contract=False) for f in ret_geom['geom']: if isinstance(f, bmesh.types.BMFace): faces_to_delete[f.index] = self.bm.faces[f.index] # delete the result faces faces_to_delete = list(faces_to_delete.values()) deleted_geom_edges = [] for f in faces_to_delete: deleted_geom_edges += [e for e in f.edges] bmesh.ops.delete(self.bm, geom=faces_to_delete, context='FACES_KEEP_BOUNDARY') # FACES_ONLY # remove deleted faces from the selected container #temp_faces = [f for f in self.selected_faces if f.is_valid] #self.selected_faces = temp_faces # this creates a subdivision of the inner vertex. if SUBDIVIDE_INNER_EDGES: # subdivide existing edges keep_edges = [] for edge in deleted_geom_edges: if edge.is_valid: keep_edges.append(edge) self.inner_edges = [] print("Inner edges: %d" % (len(keep_edges)*2)) geom_created = bmesh.ops.subdivide_edges(self.bm, edges=keep_edges, cuts=1, use_grid_fill=True) for item in geom_created['geom_split']: if isinstance(item, bmesh.types.BMVert): pass #item.select = True #print(item.index) #self.inner_edges.append(item) if isinstance(item, bmesh.types.BMEdge): #item.select = True #print(item.index) self.inner_edges.append(item) if isinstance(item, bmesh.types.BMFace): #item.select = True print(item.index) item.select = True self.selected_faces.append(item) print("Inner edges Subdivide: %d" % (len(self.inner_edges)*2) ) self.update_bm() # create a KD tree for faces. add the center, and get only faces that are "near" in radious if CALCULATE_NEAR_FACES: valid_faces = [face for face in self.selected_faces if face.is_valid] kd = kdtree.KDTree(len(valid_faces)) for face in valid_faces: kd.insert(face.calc_center_median(), face.index) kd.balance() faces_to_delete = {} verts_to_delete = {} edges_to_delete = {} self.bm.faces.ensure_lookup_table() self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() for item in self.raycast_points: terrain_down, index, point, location = item local_faces = [] local_point = self.plane_to_terrain_point(point.co) for (co, index, dist) in kd.find_range(local_point, 200): #print("face_local", index) local_faces.append(self.bm.faces[index]) for f in local_faces: # check for deleted faces! if f.is_valid and f.index not in faces_to_delete.keys(): #center = BL_FLATTEN.DummyVector(f.calc_center_median()) for fv in f.verts: #for fv in [center]: vpw = self.obj_s.matrix_world @ point.co vtw = self.obj_t.matrix_world @ fv.co dist = (vpw.xy - vtw.xy).length if dist < MIN_FACE_DIST: #print("Face too near: ",f.index,dist) faces_to_delete[f.index] = self.bm.faces[f.index] for e in f.edges: edges_to_delete[e.index] = self.bm.edges[e.index] for v in f.verts: verts_to_delete[v.index] = self.bm.verts[v.index] faces_to_delete = list(faces_to_delete.values()) verts_to_delete = list(verts_to_delete.values()) edges_to_delete = list(edges_to_delete.values()) # FACES_ONLY bmesh.ops.delete(self.bm, geom=verts_to_delete + edges_to_delete + faces_to_delete, context='FACES') self.update_bm() # now, just select the faces that are not select, but are in the edges. # also select all the vertex in the keep edges that are not selected by default. for e in self.inner_edges: if not e.is_valid: continue for v in e.verts: if not v.is_valid: continue v.select = True for f in v.link_faces: f.select = True if f not in self.selected_faces: self.selected_faces.append(f) # select vertex that are in a boundary (internal) self.selected_vertex = [] for e in self.bm.edges: if e.is_valid and e.is_boundary: for v in e.verts: self.selected_vertex.append(v) ############################################################## # # End # update the meshes # ############################################################## self.time_it.stop() self.update_bm() print("[t] do_holes(): ", self.time_it) #return(("ERROR", "Can't found terrain below the curve")) return(("INFO", "Done"))
def align_curve_tilt(context, Curve, align_target_bvht, resetTilt=False, onlySelection=False): if Curve.mode == 'EDIT': # to make transfrom() work in edit mode bpy.ops.object.mode_set(mode="OBJECT") Curve.data.transform(Curve.matrix_world) bpy.ops.object.mode_set(mode="EDIT") else: Curve.data.transform(Curve.matrix_world) if resetTilt: HT_OT_CurvesTiltAlign.resetTiltFunction(Curve.data, onlySelection) curveBevelObj = Curve.data.bevel_object # backup context.active_object.data.bevel_object = None # zero out to prevent changing default one curveResU = Curve.data.resolution_u # backup context.depsgraph.scene.update() if 'Align_curve_helper' in bpy.data.objects.keys(): Curve.data.bevel_object = bpy.data.objects['Align_curve_helper'] else: bpy.ops.object.generate_ribbons( strandResV=2, strandResU=curveResU, strandWidth=0.01, strandPeak=0, skipInitProps=True) # for temp curve for tangent and normals Curve.data.bevel_object.name = 'Align_curve_helper' meFromCurve = Curve.to_mesh(context.depsgraph, True, calc_undeformed=False) meshVertCount = len(meFromCurve.vertices) kdMeshFromCurve = kdtree.KDTree(meshVertCount) for i, v in enumerate(meFromCurve.vertices): kdMeshFromCurve.insert(v.co, i) kdMeshFromCurve.balance() curveData = Curve.data unitsScale = context.scene.unit_settings.scale_length searchDistance = 10 / unitsScale angleFlipFix = [-2 * math.pi, -math.pi, 2 * math.pi, math.pi] for i, polyline in enumerate(curveData.splines): # for strand point pointsNumber = len( polyline.bezier_points) if polyline.type == 'BEZIER' else len( polyline.points) if polyline.type == 'NURBS' or polyline.type == 'POLY': if onlySelection: spline_selection_mask = np.zeros(pointsNumber, dtype=np.bool) polyline.points.foreach_get('select', spline_selection_mask) if not any(spline_selection_mask): continue # if not even one pointis selected points = polyline.points else: if onlySelection: spline_selection_mask = np.zeros(pointsNumber, dtype=np.bool) polyline.bezier_points.foreach_get('select_control_point', spline_selection_mask) if not any(spline_selection_mask): continue # if not even one point is selected points = polyline.bezier_points # helper for tangetnt function (to detect last point on cuve) prevAngle = 0 corrective_angles = np.zeros(pointsNumber, dtype=np.float16) current_tilt = np.zeros(pointsNumber, dtype=np.float16) points.foreach_get('tilt', current_tilt) for j, curvePoint in enumerate(points): # for strand point pointOnMeshLoc, normSnapTarget, face_index, distance, isInside = \ HT_OT_CurvesTiltAlign.find_nearest(curvePoint.co.xyz, align_target_bvht, searchDistance) # get vertex closest to spline point (for normal) from temp spline representation co, index, dist = kdMeshFromCurve.find(curvePoint.co.xyz) curveNormal = meFromCurve.vertices[index].normal vecSurfaceHit = curvePoint.co.xyz - pointOnMeshLoc if isInside: # if curve point is outside snapSurface flip it vecSurfaceHit *= -1 # vecSurfaceHit = normSnapTarget * -1 # less accurate but faster... if index + 3 < meshVertCount: # for last spline just use prvevangle tangent = HT_OT_CurvesTiltAlign.get_tangents( meFromCurve, index, j == pointsNumber - 1) # for bezier we can gets handles for tanget biTangentCurve = tangent.cross(curveNormal) vectSurfHitProjected90 = tangent.cross(vecSurfaceHit) vectSurfHitProjectedToTangent = vectSurfHitProjected90.cross( tangent) # angle = biTangentCurve.angle(vectSurfHitProjectedToTangent) #unsigned angle 0 - 180 angle = angle_signed( biTangentCurve, vectSurfHitProjectedToTangent, tangent) - math.pi / 2 # a,b, vN - signed -180 to 180 if j > 1 and abs( prevAngle - angle ) > math.pi / 2: # try fixing random flips in ribbons surface fix = [ fix_angle for fix_angle in angleFlipFix if abs(prevAngle - fix_angle - angle) < math.pi / 2 ] if len(fix) > 0: angle += fix[0] corrective_angles[j] = angle prevAngle = angle else: corrective_angles[j] = prevAngle # current_tilt = current_tilt + corrective_angles if pointsNumber >= 4: corrective_angles[1] = (corrective_angles[2] + corrective_angles[3]) / 2 corrective_angles[0] = (corrective_angles[1] + corrective_angles[2]) / 2 elif pointsNumber == 3: corrective_angles[0] = (corrective_angles[1] + corrective_angles[2]) / 2 if onlySelection: current_tilt[spline_selection_mask] += corrective_angles[ spline_selection_mask] else: current_tilt += corrective_angles points.foreach_set('tilt', current_tilt.ravel()) # in radians # manually smooth first two points cos they are usually broken bpy.data.meshes.remove(meFromCurve) Curve.data.bevel_object = curveBevelObj # restore old bevel obj was_edit = False if Curve.mode == 'EDIT': # to make transfrom() work in edit mode was_edit = True bpy.ops.object.mode_set(mode="OBJECT") Curve.data.transform(Curve.matrix_world.inverted()) if Curve.rotation_euler != Euler((0.0, 0.0, 0.0), 'XYZ') or min( Curve.scale[:] ) < 0: # fix rot if curve rot is non 0,0,0, or scele x,y,z <0 override = {'selected_editable_objects': [Curve]} if Curve.data.users > 1: Curve.data = Curve.data.copy() bpy.ops.object.transform_apply(override, location=False, rotation=True, scale=True) if was_edit: bpy.ops.object.mode_set(mode="EDIT")
def __init__(self, points): self.kdtree = kdtree.KDTree(len(points)) for i, v in enumerate(points): self.kdtree.insert(v, i) self.kdtree.balance()
def get_closest_vertex(self): if hasattr(self.bm.verts, "ensure_lookup_table"): self.bm.verts.ensure_lookup_table() kd = kdtree.KDTree(len(self.bm.verts)) for i, v in enumerate(self.bm.verts): kd.insert(v.co, i) kd.balance() kd_closest_vertex = kd.find_n(self.hit_location, MAX_SEARCH) #print("kd_closest_vertex ", [v[1] for v in kd_closest_vertex]) #if(self.closest_vertex != None): print("Closest: ", self.closest_vertex.index) neighbors = [] for c in kd_closest_vertex: v = self.bm.verts[c[1]] if (len(self.bm.verts) == 1 or v.is_wire or v.is_boundary or v in self.active_solution[1]) and (v != self.active_vertex): # 0 index, 1 distance, 2 hit distance, 3 score neighbors.append([v, c[2], 0., []]) #print("neighbors ", neighbors) # Sort n = float(len(neighbors)) if (n > 1): max_hit_distance = -1. for i, v in enumerate(neighbors): c_hit_distance, c_hit_normal = get_closest_ray_hit_test( v[0], self.object, self.snap_obj, self.hit_normal) #print("c_hit_distance", c_hit_distance) if (c_hit_normal != None): v[3].append((n - i) / n) v[2] = c_hit_distance + SNAP_TOLERANCE hit_conform = 1 conform_angle = v[0].normal.angle(c_hit_normal, pi) hit_conform = (-(conform_angle / pi)**2 + 1) / 2 + 0.5 v[3].append(hit_conform) conform = 1 conform_angle = v[0].normal.angle(self.hit_normal, pi) conform = (-(conform_angle / pi)**2 + 1) / 2 + 0.5 v[3].append(conform) if (v[0].is_wire): v[3].append(0.8) if (v[2] > max_hit_distance): max_hit_distance = v[2] else: v[3].append(0.) for v in neighbors: if ((1. - v[2] / max_hit_distance) > 0.5): v[3].append(1.) else: v[3].append(.5) ##### Closest on surface # 0 index, 1 distance, 2 hit distance, 3 score plane_distances = [ abs((v[0].co - self.hit_location).dot(self.hit_normal)) for v in neighbors ] max_distance_to_plane = max(plane_distances) for v, d in zip(neighbors, plane_distances): v[3].append(cos(d * pi / 2 / max_distance_to_plane) / 2 + 0.5) ##### neighbors.sort(key=lambda x: prod(x[3]), reverse=True) if (neighbors != []): #for v in neighbors: #try: #print("--- v %5d "% v[0].index, "| [dist %.3f, conform %.3f, conform self %.3f, proj %.3f]"% (v[3][0], v[3][1], v[3][2], v[3][4]), " | ", str(v[3])) #except: # pass return (neighbors[0][0], neighbors[0][1]) else: return (None, None)
def transfer(self, target, operator = None, transfer_type='Shape'): context = bpy.context s = self.get_shape_settings(target) obj = self.id_data.dp_helper ob = self.id_data me = ob.data source = context.selected_objects[:] source.remove(ob) if operator: transfer_type = operator.transfer_type #with obj.bm(1) as bm: if transfer_type == 'Shape': if not me.shape_keys: ob.shape_key_add("Basis") kb = me.shape_keys.key_blocks for sk in s.transferable: key = kb.get(sk.name) if not key: key = ob.shape_key_add(sk.name) me.update() source_ob = source[0].dp_helper #print(operator,transfer_type) with obj.bm(1) as bm: indices_id = bm.verts.layers.int.get("indices_save") if transfer_type == 'ID' and not indices_id: indices_id = bm.verts.layers.int.new('indices_save') with source_ob.bm() as sbm: source_verts = sbm.verts indices_id2 = sbm.verts.layers.int.get("indices_save") if not indices_id2 or not indices_id : operator.report({"ERROR"},"Store indices first in source AND target") return if transfer_type == 'ID': from mathutils import kdtree size = len(source_verts) kd = kdtree.KDTree(size) for i,v in enumerate(source_verts): kd.insert(v.co, i) kd.balance() for i,v in enumerate(bm.verts): if not v.select:continue closest = kd.find_n(v.co,2) #print(closest) v[indices_id] = source_verts[closest[1][1]][indices_id2] elif transfer_type == 'Shape': mapped_source = { v[indices_id2]:v for v in source_verts } for k in s.transferable: source_shape_lay = sbm.verts.layers.shape.get(k.name) target_lay = bm.verts.layers.shape.get(k.name) #source_v = { for v in bm.verts: if mapped_source.get(v[indices_id]): v[target_lay] = mapped_source[v[indices_id]][source_shape_lay] me.update()
def process(self): if 'vecLine' in self.inputs and \ 'vecPlane' in self.inputs and \ 'edgPlane' in self.inputs: print(self.name, 'is starting') if self.inputs['vecLine'].links and \ self.inputs['vecPlane'].links and \ self.inputs['edgPlane'].links: if self.bindCircle: circle = [ (Vector((sin(radians(i)),cos(radians(i)),0))*self.circle_rad)/4 \ for i in range(0,360,30) ] vec = self.inputs['vecLine'].sv_get() vecplan = self.inputs['vecPlane'].sv_get() edgplan = self.inputs['edgPlane'].sv_get() if len(edgplan[0][0]) > 2: edgplan = pols_edges(edgplan) thick = self.inputs['thick'].sv_get()[0][0] threshold_coplanar = 0.005 sinuso60 = 0.8660254037844386 sinuso60_minus = 0.133974596 sinuso30 = 0.5 sinuso45 = 0.7071067811865475 thick_2 = thick / 2 thick_3 = thick / 3 thick_6 = thick / 6 threshold = self.threshold if 'vecContr' in self.inputs and self.inputs['vecContr'].links: vecont = self.inputs['vecContr'].sv_get() #edgcont = self.inputs['edgContr'].sv_get() vec_cont = Vector_generate(vecont) loc_cont = [[i[0]] for i in vec_cont] norm_cont = [[NM(i[0], i[len(i) // 2], i[-1])] for i in vec_cont] # довести до ума else: vec_cont = [] if 'vecTube' in self.inputs and self.inputs['vecTube'].links: vectube = self.inputs['vecTube'].sv_get() vec_tube = Vector_generate(vectube) tube_radius = self.inputs['radTube'].sv_get()[0][0] circle_tube = [ (Vector((sin(radians(i)),cos(radians(i)),0))*tube_radius) \ for i in range(0,360,15) ] else: vec_tube = [] outeup = [] outelo = [] vupper = [] vlower = [] centers = [] vec_ = Vector_generate(vec) vecplan_ = Vector_generate(vecplan) for centersver, vecp, edgp in zip(vecplan, vecplan_, edgplan): tubes_flag_bed_solution_i_know = False newinds1 = [list(e) for e in edgp] newinds2 = newinds1.copy() vupperob = vecp.copy() vlowerob = vecp.copy() deledges1 = [] deledges2 = [] # to define bounds x = [i[0] for i in vecp] y = [i[1] for i in vecp] z = [i[2] for i in vecp] m1x, m2x, m1y, m2y, m1z, m2z = max(x), min(x), max(y), min( y), max(z), min(z) l = Vector( (sum(x) / len(x), sum(y) / len(y), sum(z) / len(z))) n_select = [vecp[0], vecp[len(vecp) // 2], vecp[-1]] # довести до ума n_select.sort(key=lambda x: sum(x[:]), reverse=False) n_ = NM(n_select[0], n_select[1], n_select[2]) n_.normalize() # а виновта ли нормаль? if n_[0] < 0: n = n_ * -1 else: n = n_ cen = [sum(i) for i in zip(*centersver)] centers.append(Vector(cen) / len(centersver)) k = 0 lenvep = len(vecp) # KDtree collections closest to join edges to sockets tree = KDT.KDTree(lenvep) for i, v in enumerate(vecp): tree.insert(v, i) tree.balance() # vertical edges iterations # every edge is object - two points, one edge for v in vec_: if not v: continue # sort vertices by Z value # find two vertices - one lower, two upper vlist = [v[0], v[1]] vlist.sort(key=lambda x: x[2], reverse=False) # flip if coplanar to enemy plane # flip plane coplanar if vec_cont: fliped = self.get_coplanar(v[0], loc_cont, norm_cont, vec_cont) else: fliped = False shortedge = (vlist[1] - vlist[0]).length if fliped: two, one = vlist else: one, two = vlist # coplanar to owner cop = abs(D2P(one, l, n)) # defining bounds inside = one[0]<m1x and one[0]>m2x and one[1]<m1y and one[1]>m2y \ and one[2]<=m1z and one[2]>=m2z # if in bounds and coplanar do: #print(self.name,l, cop, inside) if cop < threshold_coplanar and inside and shortedge > thick * threshold: ''' huge calculations. if we can reduce... ''' # find shift for thickness in sockets diry = two - one diry.normalize() # solution for vertical wafel - cool but not in diagonal case # angle = radians(degrees(atan(n.y/n.x))+90) dirx_ = self.rotation_on_axis(diry, n, radians(90)) dirx = dirx_ * thick_2 # вектор, индекс, расстояние # запоминаем порядок находим какие удалить рёбра # делаем выборку левая-правая точка nearv_1, near_1 = tree.find(one)[:2] nearv_2, near_2 = tree.find(two)[:2] # indexes of two nearest points # удалить рёбра что мешают спать заодно en_0, en_1, de1 = self.calc_indexes(edgp, near_1) deledges1.extend(de1) en_2, en_3, de2 = self.calc_indexes(edgp, near_2) deledges2.extend(de2) # print(vecp, one, dirx, en_0, en_1) # left-right indexes and vectors # с учётом интерполяций по высоте l1, r1, lz1, rz1 = \ self.calc_leftright(vecp, one, dirx, en_0, en_1, thick_2, diry) l2, r2, lz2, rz2 = \ self.calc_leftright(vecp, two, dirx, en_2, en_3, thick_2, diry) # print(left2, right2, l2, r2, lz2, rz2) # средняя точка и её смещение по толщине материала three = (one - two) / 2 + two # rounded section if self.rounded: '''рёбра''' # пазы формируем независимо от верх низ outeob1 = [[lenvep + k + 8, lenvep + k], [lenvep + k + 1, lenvep + k + 2], [lenvep + k + 2, lenvep + k + 3], [lenvep + k + 3, lenvep + k + 4], [lenvep + k + 4, lenvep + k + 5], [lenvep + k + 5, lenvep + k + 6], [lenvep + k + 6, lenvep + k + 7], [lenvep + k + 7, lenvep + k + 8], [lenvep + k + 9, lenvep + k + 1]] outeob2 = [[lenvep + k, lenvep + k + 1], [lenvep + k + 1, lenvep + k + 2], [lenvep + k + 2, lenvep + k + 3], [lenvep + k + 3, lenvep + k + 4], [lenvep + k + 4, lenvep + k + 5], [lenvep + k + 5, lenvep + k + 6], [lenvep + k + 6, lenvep + k + 7], [lenvep + k + 7, lenvep + k + 8], [lenvep + k + 8, lenvep + k + 9]] # наполнение списков lenvep = length(vecp) newinds1.extend([[l1, lenvep + k], [lenvep + k + 9, r1]]) newinds2.extend([[l2, lenvep + k + 9], [lenvep + k, r2]]) '''Вектора''' round1 = diry * thick_3 round2 = diry * thick_3 * sinuso30 round2_ = dirx / 3 + dirx * (2 * sinuso60 / 3) round3 = diry * thick_3 * sinuso60_minus round3_ = dirx / 3 + dirx * (2 * sinuso30 / 3) round4 = dirx / 3 vupperob.extend([ lz2, three + round1 - dirx, three + round2 - round2_, three + round3 - round3_, three - round4, three + round4, three + round3 + round3_, three + round2 + round2_, three + round1 + dirx, rz2 ]) vlowerob.extend([ rz1, three - round1 - dirx, three - round2 - round2_, three - round3 - round3_, three - round4, three + round4, three - round3 + round3_, three - round2 + round2_, three - round1 + dirx, lz1 ]) k += 10 # streight section else: '''рёбра''' # пазы формируем независимо от верх низ outeob1 = [[lenvep + k, lenvep + k + 1], [lenvep + k + 1, lenvep + k + 2], [lenvep + k + 2, lenvep + k + 3]] outeob2 = [[lenvep + k, lenvep + k + 1], [lenvep + k + 1, lenvep + k + 2], [lenvep + k + 2, lenvep + k + 3]] # наполнение списков lenvep = length(vecp) newinds1.extend([[l1, lenvep + k], [lenvep + k + 3, r1]]) newinds2.extend([[l2, lenvep + k + 3], [lenvep + k, r2]]) '''Вектора''' vupperob.extend( [lz2, three - dirx, three + dirx, rz2]) vlowerob.extend( [rz1, three + dirx, three - dirx, lz1]) k += 4 newinds1.extend(outeob1) newinds2.extend(outeob2) # circles to bing panels section if self.bindCircle: CP = self.circl_place if CP == 'Midl': crcl_cntr = IL2P(one, two, Vector( (0, 0, 0)), Vector((0, 0, -1))) elif CP == 'Up' and not fliped: crcl_cntr = two - diry * self.circle_rad * 2 elif CP == 'Down' and not fliped: crcl_cntr = one + diry * self.circle_rad * 2 elif CP == 'Up' and fliped: crcl_cntr = one + diry * self.circle_rad * 2 elif CP == 'Down' and fliped: crcl_cntr = two - diry * self.circle_rad * 2 # forgot howto 'else' in line iteration? outeob1 = [[ lenvep + k + i, lenvep + k + i + 1 ] for i in range(0, 11)] outeob1.append([lenvep + k, lenvep + k + 11]) outeob2 = [[ lenvep + k + i, lenvep + k + i + 1 ] for i in range(12, 23)] outeob2.append( [lenvep + k + 12, lenvep + k + 23]) newinds1.extend(outeob1 + outeob2) newinds2.extend(outeob1 + outeob2) mat_rot_cir = n.rotation_difference( Vector((0, 0, 1))).to_matrix().to_4x4() circle_to_add_1 = [vecir*mat_rot_cir+crcl_cntr+ \ dirx_*self.circle_rad for vecir in circle ] circle_to_add_2 = [vecir*mat_rot_cir+crcl_cntr- \ dirx_*self.circle_rad for vecir in circle ] vupperob.extend(circle_to_add_1 + circle_to_add_2) vlowerob.extend(circle_to_add_1 + circle_to_add_2) k += 24 # TUBE section if vec_tube and not tubes_flag_bed_solution_i_know: for v in vec_tube: tubeverlength = len(v) if tubeverlength == 2: crcl_cntr = IL2P(v[0], v[1], l, n) if crcl_cntr: inside = crcl_cntr[0]<m1x and crcl_cntr[0]>m2x and crcl_cntr[1]<m1y \ and crcl_cntr[1]>m2y and crcl_cntr[2]<=m1z and crcl_cntr[2]>=m2z if inside: outeob = [[ lenvep + k + i, lenvep + k + i + 1 ] for i in range(0, 23)] outeob.append([ lenvep + k, lenvep + k + 23 ]) newinds1.extend(outeob) newinds2.extend(outeob) mat_rot_cir = n.rotation_difference( Vector( (0, 0, 1))).to_matrix( ).to_4x4() circle_to_add = [ vecir * mat_rot_cir + crcl_cntr for vecir in circle_tube ] vupperob.extend(circle_to_add) vlowerob.extend(circle_to_add) k += 24 else: tubeshift = tubeverlength // 2 crcl_cntr = IL2P( v[0], v[tubeshift], l, n) if crcl_cntr: inside = crcl_cntr[0]<m1x and crcl_cntr[0]>m2x and crcl_cntr[1]<m1y \ and crcl_cntr[1]>m2y and crcl_cntr[2]<=m1z and crcl_cntr[2]>=m2z if inside: outeob = [[ lenvep + k + i, lenvep + k + i + 1 ] for i in range(tubeshift - 1) ] outeob.append([ lenvep + k, lenvep + k + tubeshift - 1 ]) newinds1.extend(outeob) newinds2.extend(outeob) for tubevert in range( tubeshift): tubevert_out = IL2P( v[tubevert], v[tubevert + tubeshift], l, n) vupperob.append( tubevert_out) vlowerob.append( tubevert_out) k += tubeshift tubes_flag_bed_solution_i_know = True elif cop < threshold_coplanar and inside and shortedge <= thick * threshold: vupperob.extend([one, two]) vlowerob.extend([one, two]) newinds1.append([lenvep + k, lenvep + k + 1]) newinds2.append([lenvep + k, lenvep + k + 1]) k += 2 del tree for e in deledges1: if e in newinds1: newinds1.remove(e) for e in deledges2: if e in newinds2: newinds2.remove(e) if vupperob or vlowerob: outeup.append(newinds2) outelo.append(newinds1) vupper.append(vupperob) vlower.append(vlowerob) vupper = Vector_degenerate(vupper) vlower = Vector_degenerate(vlower) centers = Vector_degenerate([centers]) if 'vert' in self.outputs: if self.out_up_down == 'Up': out = dataCorrect(vupper) else: out = dataCorrect(vlower) self.outputs['vert'].sv_set(out) if 'edge' in self.outputs and self.outputs['edge'].links: if self.out_up_down == 'Up': self.outputs['edge'].sv_set(outeup) else: self.outputs['edge'].sv_set(outelo) if 'centers' in self.outputs and self.outputs['centers'].links: self.outputs['centers'].sv_set(centers) print(self.name, 'is finishing')
def __init__(self, vertices): self.kdt = kdtree.KDTree(len(vertices)) for i, v in enumerate(vertices): self.kdt.insert(v, i) self.kdt.balance()
def create_corner_vert_groups_vert_lists(bm, height, margin, vert_locs): """Return a dict containing lists of BMVerts to be added to vert groups Args: bm (bmesh): bmesh obj (bpy.types.Object): Object height (float): height margin (float): margin size of untextured bit deform_groups (bm.verts.layers.deform): deform groups - correspond to vertex groups dict { 'Leg 1 Inner': list[Vector(3)], 'Leg 2 Inner': list[Vector(3)], 'Leg 1 Outer': list[Vector(3)], 'Leg 2 Outer': list[Vector(3)], 'Leg 1 End': list[Vector(3)], 'Leg 2 End': list[Vector(3)]} : Locations of bottom verts in each group Returns: dict { 'Leg 1 Inner': list[BMVert], 'Leg 2 Inner': list[BMVert], 'Leg 1 Outer': list[BMVert], 'Leg 2 Outer': list[BMVert], 'Leg 1 End': list[BMVert], 'Leg 2 End': list[BMVert]} : verts in each group """ # sides sides = { 'Leg 1 Inner': vert_locs['Leg 1 Inner'], 'Leg 2 Inner': vert_locs['Leg 2 Inner'], 'Leg 1 Outer': vert_locs['Leg 1 Outer'], 'Leg 2 Outer': vert_locs['Leg 2 Outer'] } vert_groups = {} # create kdtree size = len(bm.verts) kd = kdtree.KDTree(size) for i, v in enumerate(bm.verts): kd.insert(v.co, i) kd.balance() for key, value in sides.items(): vert_group = [] for loc in value: bottom_vert_co, index, dist = kd.find(loc) verts = select_verts_in_bounds(lbound=bottom_vert_co, ubound=(bottom_vert_co[0], bottom_vert_co[1], bottom_vert_co[2] + height), buffer=margin / 2, bm=bm) vert_group.extend(verts) vert_groups[key] = vert_group bm_deselect_all(bm) # ends ends = { 'Leg 1 End': vert_locs['Leg 1 End'], 'Leg 2 End': vert_locs['Leg 2 End'] } for key, value in ends.items(): v1_co, v1_index, dist = kd.find(value[0]) v2_co, v2_index, dist = kd.find(value[1]) bm.verts.ensure_lookup_table() v1 = bm.verts[v1_index] v2 = bm.verts[v2_index] # select shortest path nodes = bm_shortest_path(bm, v1, v2) node = nodes[v2] for e in node.shortest_path: e.select_set(True) bm.select_flush(True) verts = [v for v in bm.verts if v.select] selected_verts = [] for v in verts: selected = select_verts_in_bounds( v.co, (v.co[0], v.co[1], v.co[2] + height), margin / 2, bm) selected_verts.extend(selected) vert_groups[key] = selected_verts bm_deselect_all(bm) # bottom # leg 1 inner_locs = vert_locs['Leg 1 Inner'][::-1] outer_locs = vert_locs['Leg 1 Outer'] selected_verts = [] i = 0 while i < len(outer_locs) and i < len(inner_locs): v1_co, v1_index, dist = kd.find(inner_locs[i]) v2_co, v2_index, dist = kd.find(outer_locs[i]) bm.verts.ensure_lookup_table() v1 = bm.verts[v1_index] v2 = bm.verts[v2_index] nodes = bm_shortest_path(bm, v1, v2) node = nodes[v2] for e in node.shortest_path: e.select_set(True) bm.select_flush(True) verts = [v for v in bm.verts if v.select] selected_verts.extend(verts) i += 1 vert_groups['Leg 1 Bottom'] = selected_verts bm_deselect_all(bm) # leg 2 inner_locs = vert_locs['Leg 2 Inner'][::-1] outer_locs = vert_locs['Leg 2 Outer'] selected_verts = [] i = 0 while i < len(inner_locs) and i < len(outer_locs): v1_co, v1_index, dist = kd.find(inner_locs[i]) v2_co, v2_index, dist = kd.find(outer_locs[i]) bm.verts.ensure_lookup_table() v1 = bm.verts[v1_index] v2 = bm.verts[v2_index] nodes = bm_shortest_path(bm, v1, v2) node = nodes[v2] for e in node.shortest_path: e.select_set(True) bm.select_flush(True) verts = [v for v in bm.verts if v.select] selected_verts.extend(verts) i += 1 vert_groups['Leg 2 Bottom'] = selected_verts # assign_verts_to_group(selected_verts, obj, deform_groups, 'Leg 2 Bottom') # top # leg 1 inner_locs = vert_locs['Leg 1 Inner'][::-1] outer_locs = vert_locs['Leg 1 Outer'] leg_1_top_verts = [] i = 0 while i < len(inner_locs) and i < len(outer_locs): v1 = select_verts_in_bounds( (inner_locs[i][0], inner_locs[i][1], inner_locs[i][2] + height), (inner_locs[i][0], inner_locs[i][1], inner_locs[i][2] + height), margin / 2, bm) v2 = select_verts_in_bounds( (outer_locs[i][0], outer_locs[i][1], outer_locs[i][2] + height), (outer_locs[i][0], outer_locs[i][1], outer_locs[i][2] + height), margin / 2, bm) nodes = bm_shortest_path(bm, v1[0], v2[0]) node = nodes[v2[0]] for e in node.shortest_path: e.select_set(True) bm.select_flush(True) verts = [v for v in bm.verts if v.select] leg_1_top_verts.extend(verts) i += 1 vert_groups['Leg 1 Top'] = leg_1_top_verts bm_deselect_all(bm) # leg 2 inner_locs = vert_locs['Leg 2 Inner'][::-1] outer_locs = vert_locs['Leg 2 Outer'] leg_2_top_verts = [] i = 0 while i < len(inner_locs) and i < len(outer_locs): v1 = select_verts_in_bounds( (inner_locs[i][0], inner_locs[i][1], inner_locs[i][2] + height), (inner_locs[i][0], inner_locs[i][1], inner_locs[i][2] + height), margin / 2, bm) v2 = select_verts_in_bounds( (outer_locs[i][0], outer_locs[i][1], outer_locs[i][2] + height), (outer_locs[i][0], outer_locs[i][1], outer_locs[i][2] + height), margin / 2, bm) nodes = bm_shortest_path(bm, v1[0], v2[0]) node = nodes[v2[0]] for e in node.shortest_path: e.select_set(True) bm.select_flush(True) verts = [v for v in bm.verts if v.select] leg_2_top_verts.extend(verts) i += 1 vert_groups['Leg 2 Top'] = leg_2_top_verts bm_deselect_all(bm) return vert_groups
def execute(self, context): sourceObj = context.active_object TargetObjs = [ obj for obj in context.selected_objects if obj != sourceObj and obj.type == 'MESH' ] if not TargetObjs: self.report({ 'ERROR' }, 'You seed to select two mesh objects (source then target that will receive vert order)! Cancelling' ) return {'CANCELLED'} bm = bmesh.new() # load mesh bm.from_mesh(sourceObj.data) src_obj_kd_verts = kdtree.KDTree(len(bm.verts)) for i, v in enumerate(bm.verts): src_obj_kd_verts.insert(v.co, i) src_obj_kd_verts.balance() src_obj_kd_edges = kdtree.KDTree(len(bm.edges)) for i, edge in enumerate(bm.edges): src_obj_kd_edges.insert((edge.verts[0].co + edge.verts[1].co) / 2, i) src_obj_kd_edges.balance() src_obj_kd_faces = kdtree.KDTree(len(bm.faces)) for i, f in enumerate(bm.faces): src_obj_kd_faces.insert(f.calc_center_median(), i) src_obj_kd_faces.balance() bm.free() processedVertsIdDict = {} processedEdgesIdDict = {} processedFacesIdDict = {} for target in TargetObjs: copiedCount = 0 processedVertsIdDict.clear() bm = bmesh.new() # load mesh bm.from_mesh(target.data) for vert in bm.verts: co, index, dist = src_obj_kd_verts.find(vert.co) if dist < self.delta: # delta copiedCount += 1 vert.index = index processedVertsIdDict[vert] = index for edge in bm.edges: co, index, dist = src_obj_kd_edges.find( (edge.verts[0].co + edge.verts[1].co) / 2) if dist < self.delta: #delta copiedCount += 1 edge.index = index processedEdgesIdDict[edge] = index for face in bm.faces: co, index, dist = src_obj_kd_faces.find( face.calc_center_median()) if dist < self.delta: #delta copiedCount += 1 face.index = index processedFacesIdDict[face] = index VOT_OT_PasteVertID.sortOtherVerts(processedVertsIdDict, processedEdgesIdDict, processedFacesIdDict, bm) bm.verts.sort() bm.edges.sort() bm.faces.sort() bm.to_mesh(target.data) bm.free() self.report({'INFO'}, 'Pasted ' + str(copiedCount) + ' vert id\'s ') return {"FINISHED"}