3
$\begingroup$

I tried to do the same thing as an existing answer but Under Object -> Set Origin. I mean, the Set Origin currently shows 5 elements. How can I append First Menu at the bottom of it so that the Set Origin would have 6 elements?

import bpy

class MyCustomMenu(bpy.types.Menu):
    bl_label = "First Menu"
    bl_idname = "OBJECT_MT_custom_menu"

    def draw(self, context):
        layout = self.layout
        layout.label(text="Hello First Menu!", icon='WORLD_DATA')

def draw_menu(self, context):
    self.layout.menu(MyCustomMenu.bl_idname)

def register():
    bpy.utils.register_class(MyCustomMenu)
    #bpy.types.VIEW3D_MT_curve_add.append(draw_menu)
    bpy.ops.object.origin_set.append(draw_menu) #Does not work.

def unregister():
    bpy.types.VIEW3D_MT_curve_add.remove(draw_menu)
    bpy.utils.unregister_class(MyCustomMenu)    


if __name__ == "__main__":
    register()
$\endgroup$
6
  • 2
    $\begingroup$ It is not a menu you can append to using method shown in linked answer. It is a menu created from the enum items available for type property of origin set operator. layout.operator_menu_enum("object.origin_set", "type") $\endgroup$
    – batFINGER
    Jun 10, 2020 at 8:54
  • $\begingroup$ @batFINGER So, it is not possible to add a menu item under "Set Origin" using add-on? $\endgroup$ Jun 10, 2020 at 9:04
  • 1
    $\begingroup$ Not impossible, would need to overwrite (rewrite) set origin operator, or blenders UI code, both options I would not recommend. (Altering UI code is IMO a very slippery slope to go down) Writing a new operator with same bl_idname is a lesser of two evils. Low level versions of most options for origin type have been used as answers here. Do you want to go down this road? $\endgroup$
    – batFINGER
    Jun 10, 2020 at 9:19
  • $\begingroup$ batFINGER is right, it's the only item of that menu which you can not append to, see the source code: i.stack.imgur.com/pnJrb.png $\endgroup$
    – brockmann
    Jun 10, 2020 at 9:21
  • 1
    $\begingroup$ Just replace VIEW3D_MT_curve_add menu type with VIEW3D_MT_object, done. $\endgroup$
    – brockmann
    Jun 10, 2020 at 10:04

1 Answer 1

7
$\begingroup$

Adding a new op and / or overriding the old.

It is not a menu you can append to using method shown in linked answer. It is a menu created from the enum items available for type property of origin set operator.

layout.operator_menu_enum("object.origin_set", "type")

Not impossible, would need to overwrite (rewrite) set origin operator, or blenders UI code, both options I would not recommend. (Altering UI code is IMO a very slippery slope to go down) Writing a new operator with same bl_idname is a lesser of two evils. Low level versions of most options for origin type have been used as answers here. Do you want to go down this road?

In case that it is not that hacky, please show me an example to add a menu there by creating a new operator with the same name

Ok let's go down that road. Overriding an operator is as simple as registering a new one with the same bl_idname

enter image description here Ran the code first with bl_idname = "object.simple_operator" and the next time with origin set. Simple is prepended atop menu, the latter replaces the enums of original in menu

The example below uses the simple operator template. The options for the type property of the original are used to build the items of the new.

Have only added code to change origin for bottom center (using code from https://blender.stackexchange.com/a/42110/15543 )

NOTE: test code below overrides the origin set operator. Will most likely need to restart blender to get the old one back.

import bpy
from bpy.props import EnumProperty
from mathutils import Vector, Matrix

def origin_to_bottom(ob, matrix=Matrix()):
    me = ob.data
    mw = ob.matrix_world
    local_verts = [matrix @ Vector(v[:]) for v in ob.bound_box]
    o = sum(local_verts, Vector()) / 8
    o.z = min(v.z for v in local_verts)
    o = matrix.inverted() @ o
    me.transform(Matrix.Translation(-o))

    mw.translation = mw @ o
    
def main(context, origin_type):
    ob = context.object
    if origin_type == 'BOTTOM_CENTER':
        origin_to_bottom(ob)
    else:
        print(f"Add code for {origin_type}")
        


class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    #bl_idname = "object.simple_operator"
    bl_idname = "object.origin_set"  # override the old one
    bl_label = "Simple Object Operator"
    
    def items():
        items = []
        rna = bpy.ops.object.origin_set.get_rna_type()
        for i in rna.properties['type'].enum_items:
            items.append((i.identifier, i.name, i.description))
        items.append(('BOTTOM_CENTER', "Bottom Center", "Set origin to Bottom Center"))
        return items
    
    type : EnumProperty(
        items=items(),
        default=rna.properties['type'].default)
    
    @classmethod
    def poll(cls, context):
        return context.active_object is not None

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

def draw_menu(self, context):
    self.layout.operator_menu_enum("object.simple_operator", "type")

def register():
    #bpy.types.VIEW3D_MT_object.prepend(draw_menu)
    bpy.utils.register_class(SimpleOperator)


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


if __name__ == "__main__":
    register()

Prepend another or overwrite the old?

Leaving the choice up to you. Not currently aware of a way to copy the execute method of a built in operator hence will need to rewrite each of the other origin types. Some are covered in the links below.

My suggestion would be to not override, instead make a new operator that uses the bounding box as a coordinate system. The origin (arbitrarily the) front bottom left corner and all other corners scaled such that opposite corner to origin is (1, 1, 1). In this case bottom center would be (0.5, 0.5, 0)

The user could be given the option of choosing from predefined list (that sets the offset) or a custom option with popup to set the offset themselves. eg origin a third along bottom front edge of bbox set offset to (1/ 3, 0, 0)

Related

How to compute the centroid of a mesh with triangular faces without using Blender's built-in functions?

Get center of geometry of an object

https://blender.stackexchange.com/questions/161823/origins-to-the-down-of-the-object-by-default/163258?r=SearchResults&s=6|8.6222#163258

Setting mesh's origin in python (2.8)

$\endgroup$
2
  • $\begingroup$ There is a problem. After running the script, only the newly added "bottom centre" works and existing methods like "Origin to 3D cursor" do not work any more. $\endgroup$ Jun 21, 2020 at 17:45
  • $\begingroup$ Please read the answer fully. "Have only added code to change origin for bottom center" in way of example of how to append to that menu, $\endgroup$
    – batFINGER
    Jun 22, 2020 at 5:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.