Refine symmetry script

Look at the last post for script. For using it, select center vertices of mesh with symmetrical topogogy (it can be easily done with alt-click on quad mesh), then edit name of mesh in the script and run it.

First part of the script:

import bpy, itertools
from math import sqrt
mesh='Cube'
eg=bpy.data.meshes[mesh].edges
ek=bpy.data.meshes[mesh].edge_keys
ekset=[set(eki) for eki in ek]
vrt=bpy.data.meshes[mesh].vertices
def edges_intersect(x,y):
    for z in bpy.data.meshes[mesh].edge_keys[x.index]:
        if z in bpy.data.meshes[mesh].edge_keys[y.index]:
            return True
    return False
def has_edge(x,y):
  if {x,y} in ekset:
      return True
  return False
def avg(iter):
    if len(iter)==0:
        return 0
    return sum(*iter)/len(iter)
class TwoEdgeEx(Exception):
    pass
a={(ed,ed) for ed in eg if ed.select==True}
ac=set(sum(a,()))
b={(ed.vertices[0],)*2 for ed in ac}|{(ed.vertices[1],)*2 for ed in ac}
bc=set(sum(b,()))
memory=len(bc)-1
while len(bc)>memory:
#for i in range(9):
    memory=len(bc)
    vc=set(range(len(vrt)))-bc
    c=set(eg)-ac
    d=set()
#    print(i)
    for ed,pa in a:
        for ints in c:
            if edges_intersect(ed,ints) or edges_intersect(pa,ints):
                d.update(((ed,pa,),))
    e=set()
    for ed,pa in d:
        t,u,v,w=vrt[ed.vertices[0]],vrt[ed.vertices[1]],vrt[pa.vertices[0]],vrt[pa.vertices[1]]
        if t.co[0]+u.co[0]>v.co[0]+w.co[0]: #if r x+ and s x-
            t,u,v,w=v,w,t,u #r should be x- and s x+
        p,q=sum(t.co[1:3])-sum(u.co[1:3]),sum(v.co[1:3])-sum(w.co[1:3])
        if p>=0 and q>=0:
            pass
        elif p>=0 and q<0:
            v,w=w,v
        elif p<0 and q>=0:
            t,u=u,t
        else:
            t,u,v,w=u,t,w,v #t,v and u,w are now vert pairs
#        print(t.co,u.co,v.co,w.co)
        tx,ux,vx,wx=set(),set(),set(),set()
        for vrtx in vc:
#            print("vrtx",vrtx,t.index,u.index,v.index,w.index)
            if has_edge(vrtx,t.index) and vrt[vrtx].co[0]<0:
                tx.update({vrtx})
            if has_edge(vrtx,u.index) and vrt[vrtx].co[0]<0:
                ux.update({vrtx})
            if has_edge(vrtx,v.index) and vrt[vrtx].co[0]>0:
                vx.update({vrtx})
            if has_edge(vrtx,w.index) and vrt[vrtx].co[0]>0:
                wx.update({vrtx})
        r,s=list(itertools.product(tx,ux)),list(itertools.product(vx,wx))
        n,o=[l[0] for l in r if l[0]==l[1]],[l[0] for l in s if l[0]==l[1]]
        if len(n)==1 and len(o)==1:
            e.update({(n[0],o[0],)})
        else:
            for te,ue in r:
                for ve,we in s:
#                    print("te,ue",vrt[te].co,vrt[ue].co)
#                    print("ve,we",vrt[ve].co,vrt[we].co)
                    if {te,ue} in ekset and {ve,we} in ekset:
#                        print("finally")
                        e.update({(te,ve,)})
                        e.update({(ue,we,)})
#                    else:
#                        print("drat")
#    print(e)
#    print(b)
    f=tuple(zip(*e))
#    print(f)
    g=dict()
    for m in e:
        for l in b:
#            print(m,l)
            for j in set(itertools.product(m,l)):
#                print(j)
                if j in ek:
                    if eg[ek.index(j)] in c:
                        if j[0] in f[0]:
                            g[f[0].index(j[0])]=g.get(f[0].index(j[0]),set())|{ek.index(j)}
                        if j[1] in f[0]:
                            g[f[0].index(j[1])]=g.get(f[0].index(j[1]),set())|{ek.index(j)}
    h=set()
    for l,m in e:
        if l in g.keys() and m in g.keys():
            h.update({(eg[tuple(g[l])[0]],eg[tuple(g[m])[0]])})
#    print(h)
    a.update(h)
    b.update(e)
    ac=set(itertools.chain(*a))
    bc=set(itertools.chain(*b))
#print(b)
for ve,pa in b:
    print(vrt[ve],vrt[pa],vrt[ve].co,vrt[pa].co)

This works for me, it detected all vertices of cube except of two.
Now to the symmetry fixer.

import bpy, itertools
from math import sqrt
mesh='chukchee5'
eg=bpy.data.meshes[mesh].edges
ek=bpy.data.meshes[mesh].edge_keys
ekset=[set(eki) for eki in ek]
vrt=bpy.data.meshes[mesh].vertices
def edges_intersect(x,y):
    for z in bpy.data.meshes[mesh].edge_keys[x.index]:
        if z in bpy.data.meshes[mesh].edge_keys[y.index]:
            return True
    return False
def has_edge(x,y):
  if {x,y} in ekset:
      return True
  return False
def avg(iter):
    if len(iter)==0:
        return 0
    return sum(*iter)/len(iter)
class TwoEdgeEx(Exception):
    pass
a={(ed,ed) for ed in eg if ed.select==True}
ac=set(sum(a,()))
b={(ed.vertices[0],)*2 for ed in ac}|{(ed.vertices[1],)*2 for ed in ac}
bc=set(sum(b,()))
memory=len(bc)-1
while len(bc)>memory:
#for i in range(9):
    memory=len(bc)
    vc=set(range(len(vrt)))-bc
    c=set(eg)-ac
    d=set()
#    print(i)
    dv=set()
    for (ed,pa),ints in itertools.product(a,c):
            if edges_intersect(ed,ints) or edges_intersect(pa,ints):
                d.update({(ed,pa,)})
                dv.update({*ed.vertices,*pa.vertices,*ints.vertices})
                print("bloat1")
    dc=dv-bc
    e=set()
    for ed,pa in d:
        print("bloat2")
        t,u,v,w=vrt[ed.vertices[0]],vrt[ed.vertices[1]],vrt[pa.vertices[0]],vrt[pa.vertices[1]]
        if t.co[0]+u.co[0]>v.co[0]+w.co[0]: #if r x+ and s x-
            t,u,v,w=v,w,t,u #r should be x- and s x+
        p,q=sum(t.co[1:3])-sum(u.co[1:3]),sum(v.co[1:3])-sum(w.co[1:3])
        if p>=0 and q>=0:
            pass
        elif p>=0 and q<0:
            v,w=w,v
        elif p<0 and q>=0:
            t,u=u,t
        else:
            t,u,v,w=u,t,w,v #t,v and u,w are now vert pairs
#        print(t.co,u.co,v.co,w.co)
        tx,ux,vx,wx=set(),set(),set(),set()
        for vrtx in dc:
#            print("vrtx",vrtx,t.index,u.index,v.index,w.index)
            if has_edge(vrtx,t.index) and vrt[vrtx].co[0]<0:
                tx.update({vrtx})
            if has_edge(vrtx,u.index) and vrt[vrtx].co[0]<0:
                ux.update({vrtx})
            if has_edge(vrtx,v.index) and vrt[vrtx].co[0]>0:
                vx.update({vrtx})
            if has_edge(vrtx,w.index) and vrt[vrtx].co[0]>0:
                wx.update({vrtx})
            print("bloat3")
        r,s=list(itertools.product(tx,ux)),list(itertools.product(vx,wx))
        n,o=[l[0] for l in r if l[0]==l[1]],[l[0] for l in s if l[0]==l[1]]
        if len(n)==1 and len(o)==1:
            e.update({(n[0],o[0],)})
        else:
            for te,ue in r:
                for ve,we in s:
#                    print("te,ue",vrt[te].co,vrt[ue].co)
#                    print("ve,we",vrt[ve].co,vrt[we].co)
                    if {te,ue} in ekset and {ve,we} in ekset:
                        print("finally4")
                        e.update({(te,ve,)})
                        e.update({(ue,we,)})
#                    else:
#                        print("drat")
#    print(e)
#    print(b)
    f=tuple(zip(*e))
#    print(f)
    g=dict()
    for m in e:
        for l in b:
#            print(m,l)
            for j in set(itertools.product(m,l)):
#                print(j)
                if j in ek:
                    if eg[ek.index(j)] in c:
                        print("bloat5")
                        if j[0] in f[0]:
                            g[f[0].index(j[0])]=g.get(f[0].index(j[0]),set())|{ek.index(j)}
                        if j[1] in f[0]:
                            g[f[0].index(j[1])]=g.get(f[0].index(j[1]),set())|{ek.index(j)}
    h=set()
    for l,m in e:
        if l in g.keys() and m in g.keys():
            print("bloat6")
            h.update({(eg[tuple(g[l])[0]],eg[tuple(g[m])[0]])})
#    print(h)
    a.update(h)
    b.update(e)
    ac=set(itertools.chain(*a))
    bc=set(itertools.chain(*b))
    print(len(b),len(bc))
#print(b)
for ve,pa in b:
#    print(vrt[ve],vrt[pa],vrt[ve].co,vrt[pa].co)
    x=(vrt[ve].co[0]-vrt[pa].co[0])/2
    y=(vrt[ve].co[1]+vrt[pa].co[1])/2
    z=(vrt[ve].co[2]+vrt[pa].co[2])/2
    vrt[ve].co=(x,y,z)
    vrt[pa].co=(0-x,y,z)

On real mesh it prints bloat1 and slowly goes on. How to make it run faster?

Usage: create symmetrical lowpoly sphere, select vertices in x plane, change mesh value and run it.

import bpy, itertools
from math import sqrt
mesh='chukchee5'
eg=bpy.data.meshes[mesh].edges
ek=bpy.data.meshes[mesh].edge_keys
ekset=[set(eki) for eki in ek]
vrt=bpy.data.meshes[mesh].vertices
def edges_intersect(x,y):
    for z in x.vertices:
        if z in y.vertices:
            return True
    return False
def has_edge(x,y):
  if {x,y} in ekset:
      return True
  return False
def avg(iter):
    if len(iter)==0:
        return 0
    return sum(*iter)/len(iter)
class TwoEdgeEx(Exception):
    pass
a={(ed,ed) for ed in eg if ed.select==True}
ac=set(sum(a,()))
b={(ed.vertices[0],)*2 for ed in ac}|{(ed.vertices[1],)*2 for ed in ac}
bc=set(sum(b,()))
memory=len(bc)-1
while len(bc)>memory:
#for i in range(9):
    memory=len(bc)
    vc=set(range(len(vrt)))-bc
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_more(use_face_step=True)
    bpy.ops.object.mode_set(mode='OBJECT')
    print("buck1")
    c={ed for ed in eg if ed.select==True}-ac
    print("buck2")
#    print(c)
    d=set()
#    print(i)
    dv=set()
    for (ed,pa),ints in itertools.product(a,c):
        if edges_intersect(ed,ints) or edges_intersect(pa,ints):
            d.update({(ed,pa,)})
            dv.update({*ed.vertices,*pa.vertices,*ints.vertices})
            print("bloat1")
    print("buck3")
    dc=dv-bc
    e=set()
    for ed,pa in d:
        print("bloat2")
        try:
            t,u,v,w=vrt[ed.vertices[0]],vrt[ed.vertices[1]],vrt[pa.vertices[0]],vrt[pa.vertices[1]]
        except IndexError:
            continue
        if t.co[0]+u.co[0]>v.co[0]+w.co[0]: #if r x+ and s x-
            t,u,v,w=v,w,t,u #r should be x- and s x+
        p,q=sum(t.co[1:3])-sum(u.co[1:3]),sum(v.co[1:3])-sum(w.co[1:3])
        if p>=0 and q>=0:
            pass
        elif p>=0 and q<0:
            v,w=w,v
        elif p<0 and q>=0:
            t,u=u,t
        else:
            t,u,v,w=u,t,w,v #t,v and u,w are now vert pairs
#        print(t.co,u.co,v.co,w.co)
        tx,ux,vx,wx=set(),set(),set(),set()
        for vrtx in dc:
#            print("vrtx",vrtx,t.index,u.index,v.index,w.index)
            if has_edge(vrtx,t.index) and vrt[vrtx].co[0]<0:
                tx.update({vrtx})
            if has_edge(vrtx,u.index) and vrt[vrtx].co[0]<0:
                ux.update({vrtx})
            if has_edge(vrtx,v.index) and vrt[vrtx].co[0]>0:
                vx.update({vrtx})
            if has_edge(vrtx,w.index) and vrt[vrtx].co[0]>0:
                wx.update({vrtx})
            print("bloat3")
        r,s=list(itertools.product(tx,ux)),list(itertools.product(vx,wx))
        n,o=[l[0] for l in r if l[0]==l[1]],[l[0] for l in s if l[0]==l[1]]
        if len(n)==1 and len(o)==1:
            e.update({(n[0],o[0],)})
        else:
            for te,ue in r:
                for ve,we in s:
#                    print("te,ue",vrt[te].co,vrt[ue].co)
#                    print("ve,we",vrt[ve].co,vrt[we].co)
                    if {te,ue} in ekset and {ve,we} in ekset:
                        print("finally4")
                        e.update({(te,ve,)})
                        e.update({(ue,we,)})
#                    else:
#                        print("drat")
#    print(e)
#    print(b)
    f=tuple(zip(*e))
#    print(f)
    g=dict()
    for m in e:
        for l in b:
#            print(m,l)
            for j in set(itertools.product(m,l)):
#                print(j)
                if j in ek:
                    if eg[ek.index(j)] in c:
                        print("bloat5")
                        if j[0] in f[0]:
                            g[f[0].index(j[0])]=g.get(f[0].index(j[0]),set())|{ek.index(j)}
                        if j[1] in f[0]:
                            g[f[0].index(j[1])]=g.get(f[0].index(j[1]),set())|{ek.index(j)}
    h=set()
    for l,m in e:
        if l in g.keys() and m in g.keys():
            print("bloat6")
            h.update({(eg[tuple(g[l])[0]],eg[tuple(g[m])[0]])})
#    print(h)
    a.update(h)
    b.update(e)
    ac=set(itertools.chain(*a))
    bc=set(itertools.chain(*b))
    print(len(b),len(bc))
#print(b)
for ve,pa in b:
#    print(vrt[ve],vrt[pa],vrt[ve].co,vrt[pa].co)
    x=(vrt[ve].co[0]-vrt[pa].co[0])/2
    y=(vrt[ve].co[1]+vrt[pa].co[1])/2
    z=(vrt[ve].co[2]+vrt[pa].co[2])/2
    vrt[ve].co=(x,y,z)
    vrt[pa].co=(0-x,y,z)

For some mysterious reasons, it works only with spherical part of mesh. Someone can use it to detact the seams. Anyway, i’ve symmetrized the rest of the vertices manually.

Renamed variables. Tried on triangular mesh, it’s not working.

import bpy, itertools
mesh='Cube'
obj = bpy.context.active_object
obj_edges=bpy.data.meshes[mesh].edges
obj_edgesset=set(obj_edges)
obj_edge_key=bpy.data.meshes[mesh].edge_keys
obj_edge_keyset=[set(eki) for eki in obj_edge_key]
obj_vertices=bpy.data.meshes[mesh].vertices
obj_vertices_ind=set((ve.index for ve in obj_vertices))
edges_intersect_database=dict()
edges_connect_database=dict()

def has_edge(x,y):
  if {x,y} in obj_edge_keyset:
      return True
  return False

def get_edge(x):
    for w in (x,x[::-1]):
        if w in obj_edge_key:
            return obj_edges[obj_edge_key.index(w)]
    return None

def get_edge_pair(x,y):
    for w in (x,x[::-1]):
        if w in obj_edge_key:
            for z in (y,y[::-1]):
                if z in obj_edge_key:
                    return {(obj_edges[obj_edge_key.index(w)],obj_edges[obj_edge_key.index(z)],)}
    return None

def edges_intersect(x,y):
    if (x,y) in edges_intersect_database.keys():
        return edges_intersect_database[(x,y)]
    for z in x.vertices:
        if z in y.vertices:
            edges_intersect_database[(x,y)]=True
            return True
    edges_intersect_database[(x,y)]=False
    return False

def edges_may_be_connected(x,y):
    if (x,y) in edges_connect_database.keys():
        return edges_connect_database[(x,y)]
    for z in x.vertices:
        for w in y.vertices:
            if has_edge(z,w):
                edges_connect_database[(x,y)]=edges_connect_database.get((x,y),set())|{get_edge((z,w))}
    edges_connect_database[(x,y)]=edges_connect_database.get((x,y),None)
    return edges_connect_database[(x,y)]

def avg(iter):
    if len(iter)==0:
        return 0
    return sum(*iter)/len(iter)

def sel_edges():
    return {ed for ed in obj_edges if ed.select==True}
    
def x_minus(x):
    return obj_vertices[x].co[0]<0

def x_plus(x):
    return obj_vertices[x].co[0]>0

class TwoEdgeEx(Exception):
    pass

found_edges=sel_edges()
found_sym_edges=set(zip(found_edges,found_edges))
found_vert_ind={ed.vertices[0] for ed in found_edges}|{ed.vertices[1] for ed in found_edges}
found_sym_vert_ind=dict(zip(found_vert_ind,found_vert_ind))
#while len(found_vert_ind)>memory:
for i in range(10):
    notfound_vert_ind=obj_vertices_ind-found_vert_ind
    notfound_edges=obj_edgesset-found_edges
    inner_edges=set()
    for ed,pa in found_sym_edges:
        for ints in notfound_edges:
            if edges_intersect(ed,ints) or edges_intersect(pa,ints):
                inner_edges.update({(ed,pa,)})
    print("inner_edges",len(inner_edges))
    vert_pair_search=set()
    edge_pair_search=set()
    for ed,pa in inner_edges:
        sq=[(obj_vertices[ed.vertices[0]],obj_vertices[ed.vertices[1]]),(obj_vertices[pa.vertices[0]],obj_vertices[pa.vertices[1]])]
        if sq[0][0].co+sq[0][1].co>sq[1][0].co+sq[1][1].co:
            sq=sq[::-1]
        p,q=sum(sq[0][0].co[1:3])-sum(sq[0][1].co[1:3]),sum(sq[1][0].co[1:3])-sum(sq[1][1].co[1:3])
        if p>=0 and q>=0:
            pass
        elif p>=0 and q<0:
            sq[1]=sq[1][::-1]
        elif p<0 and q>=0:
            sq[0]=sq[0][::-1]
        else:
            sq=[ek[::-1] for ek in sq] #t,v and u,w are now vert pairs
        sqe=[[set(),set()],[set(),set()]]
        for ve in notfound_vert_ind:
            if x_minus(ve):
                if has_edge(ve,sq[0][0].index):
                    sqe[0][0].update({ve})
                if has_edge(ve,sq[0][1].index):
                    sqe[0][1].update({ve})
            elif x_plus(ve):
                if has_edge(ve,sq[1][0].index):
                    sqe[1][0].update({ve})
                if has_edge(ve,sq[1][1].index):
                    sqe[1][1].update({ve})
#the problem is probably here
        for te,ue in itertools.product(*sqe[0]):
            if {te,ue} in obj_edge_keyset:
                for ve,we in itertools.product(*sqe[1]):
                     if {ve,we} in obj_edge_keyset:
                        vert_pair_search.update({(te,ve,),(ue,we,)})
                        print("GEP",get_edge_pair((te,ue),(ve,we)))
                        print(edge_pair_search)
                        edge_pair_search.update(get_edge_pair((te,ue),(ve,we)))

#the problem ends here
    vert_align=tuple(zip(*found_sym_vert_ind.items()))
    vert_align_new=tuple(zip(*vert_pair_search))
    print(vert_align)
    extra_vert_x_minus=dict()
    extra_vert_x_plus=dict()
    for va,vb in itertools.product(found_sym_vert_ind.items(),vert_pair_search):
        for ek in itertools.product(va,vb):
            if has_edge(*ek):
                eg=get_edge(ek)
                if eg not in found_edges:
                    if ek[0] in vert_align[0] and x_minus(ek[1]):
                            extra_vert_x_minus[eg]=extra_vert_x_minus.get(eg,set())|{("old",vert_align[0].index(ek[0])),("new",vert_align_new[0].index(ek[1]))}
                    if ek[0] in vert_align[1] and x_plus(ek[1]):
                            extra_vert_x_plus[eg]=extra_vert_x_plus.get(eg,set())|{("old",vert_align[1].index(ek[0])),("new",vert_align_new[1].index(ek[1]))}
    for va,vb in itertools.product(vert_pair_search,vert_pair_search):
        for ek in itertools.product(va,vb):
            if has_edge(*ek):
                eg=get_edge(ek)
                if eg not in found_edges:
                    if ek[0] in vert_align_new[0] and x_minus(ek[1]):
                            extra_vert_x_minus[eg]=extra_vert_x_minus.get(eg,set())|{("new",vert_align_new[0].index(ek[0])),("new",vert_align_new[0].index(ek[1]))}
                    if ek[0] in vert_align_new[1] and x_plus(ek[1]):
                            extra_vert_x_plus[eg]=extra_vert_x_plus.get(eg,set())|{("new",vert_align_new[1].index(ek[0])),("new",vert_align_new[1].index(ek[1]))}
    extra_edge_x_minus={tuple(eg[1]):eg[0] for eg in extra_vert_x_minus.items()}
    extra_edge_x_plus={tuple(eg[1]):eg[0] for eg in extra_vert_x_plus.items()}
#    print("before",len(edge_pair_search))
    for edge in set(extra_edge_x_minus.keys())&set(extra_edge_x_plus.keys()):
        edge_pair_search.update({(extra_edge_x_minus[edge],extra_edge_x_plus[edge],)})
#    print("ma",extra_edge_x_minus)
#    print("pc",extra_edge_x_plus)
#    print(edge_pair_search)
#    print("after",len(edge_pair_search))
    found_sym_edges.update(edge_pair_search)
    found_sym_vert_ind.update({ve[0]:ve[1] for ve in vert_pair_search})
    found_edges=set(itertools.chain(*found_sym_edges))
    found_vert_ind=set(itertools.chain(*found_sym_vert_ind.items()))
#print([ed.index for ed in found_edges])
#print(edges_intersect_database)
print(edges_connect_database)
for ve in found_sym_vert_ind.keys():
    x=(obj_vertices[ve].co[0]-obj_vertices[found_sym_vert_ind[ve]].co[0])/2
    y=(obj_vertices[ve].co[1]+obj_vertices[found_sym_vert_ind[ve]].co[1])/2
    z=(obj_vertices[ve].co[2]+obj_vertices[found_sym_vert_ind[ve]].co[2])/2
    obj_vertices[ve].co=(x,y,z)
    obj_vertices[found_sym_vert_ind[ve]].co=(0-x,y,z)


Have a new idea. Maybe to rewrite algorithm with point and edge, edge and edge, and then edge and point connect detection?

import bpy,itertools
meshname='Roundcube.001'
mesh=bpy.data.meshes[meshname]
edges=mesh.edges
eks=[set(pair) for pair in mesh.edge_keys]
vo=mesh.vertices
verts={vert.index for vert in vo}
sel_edges={edge for edge in edges if edge.select==True}
sel_verts=set(itertools.chain(*(edge.vertices for edge in sel_edges)))
sym_sel_verts=set(zip(sel_verts,sel_verts))
def has_edge(x,y):
    if {x,y} in eks:
        return True
    return False
while len(sel_verts)<len(verts):
    not_sel_verts=verts-sel_verts
    not_sel_verts_left=set()
    not_sel_verts_right=set()
    for vert in not_sel_verts:
        if vo[vert].co[0]<0:
            not_sel_verts_left.add(vert)
        else:
            not_sel_verts_right.add(vert)
    sym_inner_verts=set()
    for ve,pa in sym_sel_verts:
        for nv in not_sel_verts_left:
            if has_edge(ve,nv):
                for np in not_sel_verts_right:
                    if has_edge(pa,np):
                        sym_inner_verts.add((ve,pa,))
    new_sym_verts=set()
    for ve,pa in sym_inner_verts:
        for v2, p2 in sym_inner_verts:
            for v3 in not_sel_verts_left:
                if has_edge(ve,v3):
                    for p3 in not_sel_verts_right:
                        if has_edge(pa,p3):
                            if has_edge(v2,v3) and has_edge(p2,p3):
                                new_sym_verts.update({(v3,p3,)})
                            elif has_edge(ve,v2) and has_edge(pa,p2):
                                for v4 in not_sel_verts_left:
                                    if has_edge(v2,v4) and has_edge(v3,v4):
                                        for p4 in not_sel_verts_right:
                                            if has_edge(p2,p4) and has_edge(p3,p4):
                                                new_sym_verts.update({(v3,p3,),(v4,p4,)})
    sym_sel_verts.update(new_sym_verts)
    sel_verts.update(sum(new_sym_verts,()))
for ve,pa in sym_sel_verts:
    x=(vo[ve].co[0]-vo[pa].co[0])/2
    y=(vo[ve].co[1]+vo[pa].co[1])/2
    z=(vo[ve].co[2]+vo[pa].co[2])/2
    vo[ve].co=(x,y,z)
    vo[pa].co=(0-x,y,z)

Simplified the code. Now it completely covers the quad mesh but ruins triangle mesh.

Finally works with both quad and triangular mesh:

import bpy,itertools
meshname='Roundcube'
mesh=bpy.data.meshes[meshname]
edges=mesh.edges
eks=[set(pair) for pair in mesh.edge_keys]
vo=mesh.vertices
verts={vert.index for vert in vo}
sel_edges={edge for edge in edges if edge.select==True}
sel_verts=set(itertools.chain(*(edge.vertices for edge in sel_edges)))
sym_sel_verts=set(zip(sel_verts,sel_verts))
def has_edge(x,y):
    if {x,y} in eks:
        return True
    return False
while len(sel_verts)<len(verts):
    not_sel_verts=verts-sel_verts
    not_sel_verts_left=set()
    not_sel_verts_right=set()
    for vert in not_sel_verts:
        if vo[vert].co[0]<0:
            not_sel_verts_left.add(vert)
        else:
            not_sel_verts_right.add(vert)
    sym_inner_verts=set()
    for ve,pa in sym_sel_verts:
        for nv in not_sel_verts_left:
            if has_edge(ve,nv):
                for np in not_sel_verts_right:
                    if has_edge(pa,np):
                        sym_inner_verts.add((ve,pa,))
    new_sym_verts=set()
    for ve,pa in sym_inner_verts:
        for v2, p2 in sym_inner_verts:
            if ve!=v2 and pa!=p2:
                for v3 in not_sel_verts_left:
                    if has_edge(ve,v3):
                        for p3 in not_sel_verts_right:
                            if has_edge(pa,p3):
                                if has_edge(v2,v3) and has_edge(p2,p3):
                                    new_sym_verts.update({(v3,p3,)})
                                elif has_edge(ve,v2) and has_edge(pa,p2):
                                    for v4 in not_sel_verts_left:
                                        if has_edge(v2,v4) and has_edge(v3,v4) and not has_edge(ve,v4):
                                            for p4 in not_sel_verts_right:
                                                if has_edge(p2,p4) and has_edge(p3,p4) and not has_edge(pa,p4):
                                                    new_sym_verts.update({(v3,p3,),(v4,p4,)})
    sym_sel_verts.update(new_sym_verts)
    sel_verts.update(sum(new_sym_verts,()))
for ve,pa in sym_sel_verts:
    x=(vo[ve].co[0]-vo[pa].co[0])/2
    y=(vo[ve].co[1]+vo[pa].co[1])/2
    z=(vo[ve].co[2]+vo[pa].co[2])/2
    vo[ve].co=(x,y,z)
    vo[pa].co=(0-x,y,z)