# Mesh reverts back to the state before the script was run

I created a script (operator) that converts a selection of quads into a diamond pattern.

It works fine but when I try to Inset the newly created selection and change any of the options (like switching to individual face insets) the mesh seems to revert back to the original topology before my script was even run (see image below).

Video of what happens (I decided to select the faces manually but same result): https://www.youtube.com/watch?v=UHUNfZG1Qnw&feature=youtu.be

Image Example:

I noticed that if I change the selection somehow after running my operator I stop having the Inset issue, so I'm assuming my script is missing some command to fully apply the changes it makes to the mesh or something.

After running the script I call the operator from the F3 menu. Steps to reproduce:

1- Select grid of quad faces.

2- Use F3 to call the Operator after registering it.

3- Press I to inset and move the mouse to give the inset any amount.

4- Without leaving the tool, activate Individual on the inset options or viceversa (changing other options has the same effect).

5- Mesh reverts back.

Here's my operator:

import bpy
import bmesh

def main(context):

#context = bpy.context
ob = context.edit_object
me = ob.data
bm = bmesh.from_edit_mesh(me)

# Store the faces, the edges and the edge border of our current selection.
faces = set(f for f in bm.faces if f.select)
originalEdges = set(e for e in bm.edges if e.select)
borderEdges = set(e for e in originalEdges
if (e.is_boundary or not all(f.select for f in e.link_faces)))

# Poke the faces and select all of them minus the outer border of triangles.
ret = bmesh.ops.poke(bm, faces=list(faces))
for f in ret['faces']:
f.select = not bool(set(f.edges).intersection(borderEdges))

# Dissolve the original edges (minus the border) so we end up with a diamond pattern.
deleteEdges = originalEdges.difference(borderEdges)
diamondEdges = set(e for e in bm.edges if e.select).difference(deleteEdges)
bmesh.ops.dissolve_edges(bm, edges=list(deleteEdges))

bpy.ops.mesh.select_all(action='DESELECT')

# Select the Edges that form the Diamond Pattern
for e in diamondEdges:
e.select = True

# Easy way to convert our newly selected edges into faces
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')

bmesh.update_edit_mesh(me)

class VitalyPoke(bpy.types.Operator):

"""
Description:
Uses the Poke operator and some caveman code to convert a selection of quads into diamond-looking faces
"""

bl_idname = "arc.vitaly_poke"
bl_label = "Vitaly Poke (ARC)"

@classmethod
def poll(cls, context):
return context.active_object is not None

def execute(self, context):
main(context)
return {'FINISHED'}

def register():
bpy.utils.register_class(VitalyPoke)

def unregister():
bpy.utils.unregister_class(VitalyPoke)

if __name__ == "__main__":
register()

• comment here in regard to below. I can't emulate this, when I run your operator it pokes all the selected faces, but I lose the selection. If I select them and inset don't see behaviour above. btw the convenience context var context = bpy.context can be removed if used as above – batFINGER May 16 '19 at 13:25
• disregard losing selection, tested on some weird grid with doubles. Still do not get this issue, how exactly are you invoking the operator. I used F3 menu. – batFINGER May 16 '19 at 13:30
• I also use F3. I copied the code here back to blender just to make sure its the same and run the script before calling the operator but the issue persists. Steps to reproduce: 1-Select grid of quads. 2-Use F3 to call the Operator. 3-Press I to inset and move the mouse to give it a little offset. 4-Without leaving the tool, activate Individual on the inset options (or viceversa) 5-Mesh reverts back. I guess I'll just record a video or something. – Armored Wolf May 16 '19 at 13:48

Man that's a doozy

I have no idea, thought it could be a select flush hassle, as another way to get it to "work as expected" is to toggle into another select mode and back.

Couldn't get any wranglation of code to do it though, even toggling edit mode with code as commented... strange one

I have put together this as not an answer but as a means to express an alternative poke n select code. edges is the boundary of the selection, either is a selected boundary edge (one link face) or only has one of two linked faces selected.

The bmesh poke operator returns the faces created. Any faces that don't include an edge in edges are selected.

Result of using method below

def main(context):
ob = context.edit_object
me = ob.data
bm = bmesh.from_edit_mesh(me)
faces = [f for f in bm.faces if f.select]
edges = set(e for e in bm.edges if e.select
and (e.is_boundary or not all(f.select for f in e.link_faces)))
ret = bmesh.ops.poke(bm, faces=faces)
for f in ret['faces']:
f.select = not bool(set(f.edges).intersection(edges))
bmesh.update_edit_mesh(me)

• Yeah I knew my code was weird before I even finished it so thank you for posting your own; poke returning faces is useful. Thanks for taking so much of your time to explore the issue. I will update the post if I come across anything myself. – Armored Wolf May 16 '19 at 17:46
• Thanks and Ditto re coming across anything. It's a good question deserves more upvotes.. Does toggling the select modes eg faces to verrts to faces manually work for you as expected (like manually toggling edit mode)? – batFINGER May 16 '19 at 17:50
• As far as I can tell it does exactly what it should be doing. In fact, that's how I kinda came up with the code, by doing step by step manually first to make sense of what needed to happen inside the script. That's why the code is as weird as it is. I used to do the same using Mel in Maya, I would do the whole operation manually and then just copy paste the commands the console spat out into a script (didn't have to worry about hidden commands or anything). Obviously Blender is a little different and takes more research. – Armored Wolf May 16 '19 at 18:08

Without studying your code I think a refresh is in order.

bmesh.update_edit_mesh(me)


try putting this statement on the next line

me.update(calc_edges=True)

• think means me.update() although not sure its needed with a live edit mesh. – batFINGER May 16 '19 at 13:14
• i stop having the issue if I Tab out of edit mode and Tab back in manually. I tried doing the toggle with code: bpy.ops.object.editmode_toggle() bpy.ops.object.editmode_toggle() But it only seems to work if I press tab myself. – Armored Wolf May 16 '19 at 13:16
• me.update(calc_edges=True) didn't work either, hmmmm. Could this be a bug? – Armored Wolf May 16 '19 at 13:20

Ok, I found 2 ways of making it work, not very useful though so it's not really an answer.

Number 1 (Watch the GIF for demonstration):

After running my operator and applying an Inset (or anything else, like extrude), if I press "I" again to change the Inset settings to Individual Faces then everything is fine. Example hotkeys when Inset is active:

However, if I try to change the settings through the popup menu on the bottom left, that's when my mesh reverts to a previous state.

So, using the local hotkeys to change the tool settings works as expected, but using the tool popup does NOT. The obvious problem is that not all tools have their own local hotkeys so this isn't really a solution.

Number 2:

I noticed that if I call the operator directly from the text editor after registering it (just like in the operator template sample) then it all works well. It also works fine if I don't wrap it inside an operator at all. The problem only seems to appear when I try to call the operator from the F3 search menu. Binding it to a hotkey has the same issue as using the F3 menu.

So, using the text editor to call the operator works as expected, using the F3 menu does NOT.

Also, I simplified the code in the original question, not that it matters since the result is the same.