MapVertsOnSurface_ver3.py
Dijkstra detection part is the same as dijkstra.py.
If isRing is True, only verts on the shortest pathes between selected 3 verts will be mapped, otherwise all verts mapped.
Now eF(evaluation function) is detachable.
Choose one from distBtwn and dist2plane, or write your own.
# ver.3 implemented Dijkstra # let u, v vectors on the plane. b the origin on it. # let a a point off plane. and n a normal of the plane. import sys import math import bmesh import numpy as np ################################################################################ # Dijkstra part (same as dijkstra.py) # Evalation functions. # Assign one to eF to use it. def distBtwn(env, v0, v1): dX = v0.co[0] - v1.co[0] dY = v0.co[1] - v1.co[1] dZ = v0.co[2] - v1.co[2] return math.sqrt(dX*dX + dY*dY + dZ*dZ) def dist2plane(env, v0, v1): return abs(np.dot(env.co_start-v1.co, env.nml)) class DjEnv: def __init__(self, nml, co_start, bmesh): self.co_start = co_start self.nml = nml self.bm = bmesh class VDijkstra: def __init__(self, env, evalF, vert): self.id = vert.index self.v = vert self.bestParent = None self.cost2start = float('inf') self.costs = {} # init peers self.peers = set() for e in self.v.link_edges: v_other = e.other_vert(self.v) self.peers.add(v_other.index) self.costs[v_other.index] = evalF(env, self.v, v_other) print('vds['+str(self.id)+'].peers = '+str(self.peers)) #def procDijkstra(vds, H, A, start, goal): def procDijkstra(vds, H, A, start, goal, depth=0): print('---------------------------------------------') print('depth: '+str(depth)) if vds[goal].bestParent is not None: print('alreadily reached the goal.') return # d_min_i = None d_min_p = None d_best = float('inf') for i in A: vd = vds[i] w = vd.peers.intersection(H) #print('w = '+str(w)) for p in w: vp = vds[p] d = vd.costs[p] + vp.cost2start if d < d_best: print('found best') d_best = d d_min_i = i d_min_p = p vds[d_min_i].cost2start = d_best vds[d_min_i].bestParent = d_min_p if d_min_i == goal: vds[goal].bestParent = d_min_p return # H.add(d_min_i) A.remove(d_min_i) A = A.union(vds[d_min_i].peers - H) # print('d_min_i = '+str(d_min_i)) print('d_min_p = '+str(d_min_p)) print('H = '+str(H)) print('A = '+str(A)) procDijkstra(vds, H, A, start, goal, depth+1) return def djk(env, eF, start, goal): print('start = '+str(start), 'goal = '+str(goal)) #bpy.ops.object.mode_set(mode="OBJECT") #o = bpy.context.scene.objects.active #b = o.data #bm = bmesh.new() #bm.from_mesh(b) #bm.verts.index_update() # bmv = env.bm.verts nVerts = len(bmv) vds = [] for i in range(nVerts): vds.append(VDijkstra(env, eF, bmv[i])) # print("num verts = "+str(len(vds))) # H = set() A = set() # H.add(vds[start].id) H = H.union(vds[start].peers) s = set() for i in H: s = s.union(vds[i].peers) vds[i].cost2start = distBtwn(None, vds[i].v, vds[start].v) vds[i].bestParent = start print('H = '+str(H)) A = s - H print('A = '+str(A)) # procDijkstra(vds, H, A, start, goal) i = goal while True: if not isinstance(i,int): print('*ERROR* i is invalid.') break elif i == start: print('reached start.') break #bpy.context.object.data.vertices[i].select = True env.bm.verts[i].select = True print(str(i)+' -> '+str(vds[i].bestParent)) i = vds[i].bestParent bpy.ops.object.mode_set(mode="EDIT") ################################################################################ def mapOnPlane(a, env, b0, b1, b2): u = np.array([b1[0]-b0[0], b1[1]-b0[1], b1[2]-b0[2]]) v = np.array([b2[0]-b0[0], b2[1]-b0[1], b2[2]-b0[2]]) n = np.cross(u, v) tX = np.array([n, u, v]) X = np.array([tX[:,0], tX[:,1], tX[:,2]]) print(X) # X = np.array([[n[0], u[0], v[0]], [n[1], u[1], v[1]], [n[2], u[2], v[2]]]) # print(X) Y = np.array([b0[0]-a[0], b0[1]-a[1], b0[2]-a[2]]) # print(u,v,n,X,Y) s = np.linalg.solve(X, Y) return (a[0] + s[0]*n[0], a[1] + s[0]*n[1], a[2] + s[0]*n[2]) # If ringOnly is True, verts on the ring with selected-verts will be mapped. # Only 1st to 3rd verts account selection. # If False, the plane is the one which involves first three verts selected. # eF = evaluation function def pressLine(isRing=True, eF = distBtwn): if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode='EDIT', toggle=False) ao = bpy.context.scene.objects.active bm = bmesh.from_edit_mesh(ao.data) vs = bm.verts # nt = total num of vs. ns = num of selected verts. nt = 0 ns = 0 bases = [] for v in vs: nt += 1 if v.select: ns += 1 bases.append(v.index) print("verts="+str(nt)+" selected="+str(ns)) # if ns != 3: print("Please select 3 vertices.") return # b0 = vs[bases[0]].co b1 = vs[bases[1]].co b2 = vs[bases[2]].co nml = np.cross(b1-b0, b2-b0) nml = nml/np.linalg.norm(nml) print('nml = '+str(nml)) env = DjEnv(nml,b0,bm) if isRing: for i in range(3): #pass djk(env, eF, bases[i], bases[i-1]) # for i in range(nt): #print(i, vs[i].co) if isRing is False or vs[i].select: vs[i].co = mapOnPlane(vs[i].co, env, vs[bases[0]].co, vs[bases[1]].co, vs[bases[2]].co) #print(i, vs[i].co) ao.data.update()