4
$\begingroup$

i have a math Problem here with my 'Edge Angle' Python Function. The 'Global' setting works fine (in 2D), but the 'Local' Function is beyond my math capabilities. The goal is an mesh with perpendicular edges (right mesh), but i have no idea how to do the axis transfomation

goal and result

    import bpy
    import bmesh
    import math
    from math import sqrt, pi, degrees
    import mathutils
    from mathutils import Vector

    def setupVertEdit():
        set3DViewModeEdit()
        initMeshEdit()
        return

    def initMeshEdit():
        mod.gObj = bpy.context.active_object
        mod.gObjData = mod.gObj.data
        mod.gBMesh=bmesh.from_edit_mesh(mod.gObjData)
        mod.gMat_world = mod.gObj.matrix_world
        mod.ctx = bpy.context.scene
        return

    def get3DViewMode():
        return bpy.ops.mesh.select_mode()

    def set3DViewModeEdit():
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_mode(type="VERT")
        return

    def getBMeshSelectionMode(vMode=None):
        sM = bpy.context.scene.tool_settings.mesh_select_mode
        if vMode == 'vert':
            return sM[0]
        if vMode == 'edge':
            return sM[1]
        if vMode == 'face':
            return sM[2]
        # print(bpy.context.scene.tool_settings.mesh_selectmodode.__len__())
        return bpy.context.scene.tool_settings.mesh_select_mode

    def updateData():
        bmesh.update_edit_mesh(mod.gObjData)
        return

    def getSelectedVerts():
        return [v for v in mod.gBMesh.verts if v.select]

    def countSelectedVerts():
        return (len(getSelectedVerts()))

    def getActiveVert(optSel=0):
        elem = mod.gBMesh.select_history.active
        if elem is not None and isinstance(elem, bmesh.types.BMVert):
            return elem
        elif optSel == 'sel' and countSelectedVerts() > 0:
            elem = mod.gBMesh.verts[0]
            gBMesh.select_history.add(elem)
            return elem
        return None

    def CreateEdge(vOrientation,
                    vDir_xy,
                    vAxisAngleXY,
                    vAxisAngleZ,
                    vLenght):

        # get Verts count and location
        verts_sel = getSelectedVerts()
        verts_sel_count = countSelectedVerts()

        if verts_sel_count == 0 or mod.ActiveVert == None:
            return

        fnDebugPrint('vAxisAngleXY ', vAxisAngleXY)

        # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
        if vOrientation=='global':
            if verts_sel_count < 1:
                self.report({'WARNING'}, ': no selection')
                return

            if vDir_xy == 'cw':
                vAxisAngleXY = 450 - vAxisAngleXY

                newVertCo = fnGetAeqatorialCoordinates(vLenght, vAxisAngleXY, vAxisAngleZ)
                # newVertCo = fnGetAeqatorialCoordinates(vLenght, vAxisAngleXY, vAxisAngleZ)
            else:
                vAxisAngleXY = 90 - (-1 * vAxisAngleXY)
                newVertCo = fnGetAeqatorialCoordinates(vLenght,vAxisAngleXY, vAxisAngleZ)

            vActiveVertLocalCoor = mod.ActiveVert.co
            vActiveVertGlobalCoor = mod.gMat_world * vActiveVertLocalCoor

            vertNew = mod.gBMesh.verts.new(mod.ActiveVert.co + newVertCo)
            vertsPair = {mod.ActiveVert,vertNew}
            mod.gBMesh.edges.new(vertsPair)

        # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
        if vOrientation=='local':
            if verts_sel_count < 2:
                return False

            if verts_sel[0].index == mod.ActiveVert.index:
                vDestinationVertLocal = verts_sel[0]
                vReferenceVertLocal = verts_sel[1]
            else:
                vDestinationVertLocal = verts_sel[1]
                vReferenceVertLocal = verts_sel[0]

            vDestinationVertGlobalCoor = vDestinationVertLocal.co
            vReferenceVertGlobalCoor = vReferenceVertLocal.co

            diffPolarAngle = fnGetAngleAlpha(vReferenceVertGlobalCoor,vDestinationVertGlobalCoor)

            vAxisAngleZ += 90 - diffPolarAngle
            vAxisAngleXY += 90

            if vDir_xy == 'cw':
                newVertCo = fnGetAeqatorialCoordinates(vLenght, 180 - vAxisAngleXY, vAxisAngleZ)
            else:
                newVertCo = fnGetAeqatorialCoordinates(vLenght, vAxisAngleXY, vAxisAngleZ)

            vertNewCoor = vDestinationVertLocal.co + newVertCo

            vertNew = mod.gBMesh.verts.new(vertNewCoor)
            vertsPair = {vDestinationVertLocal,vertNew}
            mod.gBMesh.edges.new(vertsPair)

        updateData()

    # Print vVariableName and vValue
    def fnDebugPrint(vVariableName, vValue):
        print(vVariableName + ' = ' + str(vValue))

    def fnGetAngleAlpha(vertRef, vertDest):
        v1 = vertRef - vertDest
        v2 = Vector((0, 0, 1))

        a1 = v1.angle(v2)
        if a1 > pi * 0.5:
            a1 = pi - a1
        return degrees(a1)

    # spherical coordinates to Cartesian coordinates
    def fnGetAeqatorialCoordinates(vRadius, vAzimuth_xy, vPolarAngle_z):
        x = vRadius * cos(vPolarAngle_z) * cos(vAzimuth_xy)
        y = vRadius * cos(vPolarAngle_z) * sin(vAzimuth_xy)
        z = vRadius * sin(vPolarAngle_z)
        return Vector([x, y, z])

    def fnGetCartesianCoordinates(vec):
        # result Values: radius, polarangle, azimutangle
        x = vec.x
        y = vec.y
        z = vec.z
        radius = sqrt(x ** 2 + y ** 2 + z ** 2)
        if radius == 0:
            return CartCoorReturn(0, 0, 0)
        if x == 0 and y == 0:
            if z > 0:
                polarangle = 90
            else:
                polarangle = -90
        else:
            polarangle = asin(z / radius)
        if x == 0 and y == 0:
            return CartCoorReturn(radius, polarangle, 0)
        if x > 0:
            if y >= 0:
                return CartCoorReturn(radius, polarangle, atan(y / x))
            if y < 0:
                return CartCoorReturn(radius, polarangle, 360 - abs(atan(y / x)))
        if x == 0:
            if y > 0:
                return CartCoorReturn(radius, polarangle, y * 180 / 2)
            if y <= 0:
                return CartCoorReturn(radius, polarangle, y * 180 / 2 + 360)
        if x < 0:
            if y >= 0:
                return CartCoorReturn(radius, polarangle, atan(y / x) + 180)
            if y < 0:
                return CartCoorReturn(radius, polarangle, 180 + atan(y / x))


    def sin(v):
        return math.sin(math.radians(v))


    def cos(v):
        return math.cos(math.radians(v))


    def tan(v):
        return math.tan(math.radians(v))


    def asin(v):
        return math.degrees(math.asin(v))


    def acos(v):
        return math.degrees(math.acos(v))


    def atan(v):
        return math.degrees(math.atan(v))


    # Registration
    #   All panels and operators must be registered with Blender; otherwise
    #   they do not show up. The simplest way to register everything in the
    #   file is with a call to bpy.utils.registermododule(__name__).
    #

    def register():
        bpy.utils.register_module(__name__)
        if __name__ != "__main__":
            bpy.types.VIEW3D_MT_edit_mesh_vertices.append(_menu_func)
        print('register done')


    def unregister():
        bpy.utils.unregister_module(__name__)
        bpy.types.VIEW3D_MT_edit_mesh_vertices.remove(menu_func)
        print('unregister done')

    #
    #    Menu in tools region
    #
    class ToolsPanel(bpy.types.Panel):
        bl_label = 'Adjust Mesh'
        bl_space_type = "VIEW_3D"
        bl_region_type = "TOOLS"
        bl_category = 'Tools'
        bl_context = "mesh_edit"
        bl_options = {'DEFAULT_CLOSED'}

        def draw(self, context):
            slayout = self.layout
            row = slayout.row(align=True)
            row.operator("mesh.angle_edge")


    #
    #    The  button classes
    #

    # Button to open Angle edge -Dialog
    class OBJECT_edge_Button(bpy.types.Operator):
        bl_idname = "mesh.angle_edge"
        bl_label = "Edge Angle"

        def execute(self, context):
            bpy.ops.SetAngleEdgeDialogOperator('INVOKE_DEFAULT')
            return {'FINISHED'}

    class SetAngleEdgeDialogOperator(bpy.types.Operator):
        bl_idname = "mesh.angle_edge"
        bl_label = 'Edge Angle Dialog'
        bl_space_type = 'VIEW_3D'
        bl_region_type = 'WINDOW'
        bl_context = "data"
        bl_options = {'REGISTER', 'UNDO'}   #Wichtig! ohne diese Einträge kein F6-Menü


        # Local / Global Toggle Button
        enum_items_Ori = (('local',
                           'Local',
                           'create edge, local angle'),
                          ('global',
                           'Global',
                           'create edge, global angle'))
        bpy.types.Scene.Toggle_Local_Global = bpy.props.EnumProperty(items=enum_items_Ori, default="local")

        # cw / ccw Toggle Button
        enum_items_Dir = (('ccw',
                           'ccw',
                           'create edge, counterclockwise'),
                          ('cw',
                           'cw',
                           'create edge, clockwise'))
        bpy.types.Scene.Toggle_cw_ccw = bpy.props.EnumProperty(items=enum_items_Dir, default='cw')

        floatAngleXY = bpy.props.FloatProperty(name="cw/ccw Angle",
                                                description="Angle xy",
                                                default=0,
                                                min=-360,
                                                max=360,
                                                precision=2)

        floatAngleZ = bpy.props.FloatProperty(name="up/down Angle",
                                                description="Angle z",
                                                default=0,
                                                min=-360,
                                                max=360,
                                                precision=2)

        floatLenght = bpy.props.FloatProperty(name="Lenght",
                                                description="Lenght of new edge",
                                                default=1,
                                                min=-9999,
                                                max=9999,
                                                soft_min=-100,
                                                soft_max=100,
                                                precision=3)

        # Execution
        def execute(self, context):
            initMeshEdit()

            if not CreateEdge(mod.ctx.Toggle_Local_Global,
                        mod.ctx.Toggle_cw_ccw,
                        self.floatAngleXY,
                        self.floatAngleZ,
                        self.floatLenght):
                self.report({'WARNING'}, ": " + mod.err_msg)
            else:
                self.report({'INFO'}, ": created Angle edge")
            return {'FINISHED'}

        def invoke(self, context, event):
            wm = context.window_manager
            return wm.invoke_props_dialog(self)

        def draw(self, context):
            slayout = self.layout
            initMeshEdit()
            if getBMeshSelectionMode('vert'):
                mod.ActiveVert = getActiveVert()
                verts_sel_count = countSelectedVerts()

                # if mod.ActiveVert is not None:
                if verts_sel_count == 0 or mod.ActiveVert == None:

                    # err_msg = 'Base [Local]: Select 2 verts, 1 vert as active (shift MB) :: Base [Global]: Select 1 vert, 1 vert as active'

                    slayout.label('Base [Local]: Select 2 verts, 1 vert as active (shift MB)')
                    slayout.label('Base [Global]: Select 1 vert, 1 vert as active')

                    self.report({'WARNING'}, ': no selection: '+mod.err_msg)
                else:
                    # Check Edges Data Enviroment
                    check_msg = 'Mesh: '+mod.gObj.name
                    slayout.label(check_msg)

                    # new row in Dialog
                    row = slayout.row()

                    # Local / Global Toggle Button
                    row.prop(context.scene, 'Toggle_Local_Global', expand=True)

                    # cw / ccw Toggle Button
                    row.prop(context.scene, 'Toggle_cw_ccw', expand=True)

                    # new row in Dialog
                    row = slayout.row(align=True)

                    # cw / ccw Angle
                    row.prop(self,
                             'floatAngleXY',
                             toggle=True,
                             icon='BLENDER')

                    # z Angle
                    row.prop(self,
                             'floatAngleZ',
                             toggle=True,
                             icon='BLENDER')

                    # new row in Dialog
                    row = slayout.row()
                    row.prop(self,
                             'floatLenght',
                             toggle=True,
                             icon='BLENDER')


    class clsmododul:
        pass

    # Global Variable
    mod = clsmododul()
    mod.gObj = None
    mod.gObjData = None
    mod.gBMesh = None
    mod.gMat_world = None
    mod.ctx = None
    mod.ActiveVert = None
    mod.ActiveEdge = None
    mod.addon_Label = 'Set Verts to Axis'
    mod.fDiv = 1
    mod.fAbs = 0
    mod.err_msg = 'Base [Local]: Select 2 verts, 1 vert as active (shift MB) :: Base [Global]: Select 1 vert, 1 vert as active'

    if __name__ == "__main__":
        register()
$\endgroup$
3
  • $\begingroup$ Might be useful blender.stackexchange.com/questions/26852/… $\endgroup$
    – batFINGER
    Sep 20, 2016 at 15:41
  • 2
    $\begingroup$ It would be easier for people to understand your question if you give more context around what you're trying to do and also trim your code to the minimum snippet that doesn't work, if possible. I clicked on this question with some confidence I could help solve a math/python problem, but it's going to take a lot of effort just to understand what the issue is. $\endgroup$ Mar 23, 2018 at 23:40
  • 1
    $\begingroup$ I'm voting to close this question as off-topic because it is a general mathematics or geometry problem $\endgroup$ Jul 8, 2018 at 18:25

2 Answers 2

0
$\begingroup$

Sorry, i am fiddled around with 'Local' and 'Global' Operators, after a week i realize that verts are always global. This resulted a simple Function 'fnget_xy_angle', now this works for me. The only thing what has failed is the z-alingment. But for z-Axis Orientation a 2-vert Selection is not sufficient.

import bpy
import bmesh
import math
import mathutils

from math import pi, radians, sin, cos, atan2
from mathutils import Vector


bpy.types.Scene.floatLenghtA = bpy.props.FloatProperty(name='Lenght',
                                      description="Lenght of new edge",
                                      default=1,
                                      min=-9999,
                                      max=9999,
                                      soft_min=-100,
                                      soft_max=100,
                                      precision=3)


def init_mesh_edit():
    # global gObj, gObjData, gBMesh, gMat_world
    mod.gObj = bpy.context.active_object
    mod.gObjData = mod.gObj.data
    mod.gBMesh = bmesh.from_edit_mesh(mod.gObjData)
    mod.gMat_world = mod.gObj.matrix_world
    mod.ctx = bpy.context.scene
    return


def get_bmesh_selection_mode(v_mode=None):
    selection_mode = bpy.context.scene.tool_settings.mesh_select_mode
    if v_mode == 'vert':
        return selection_mode[0]
    if v_mode == 'edge':
        return selection_mode[1]
    if v_mode == 'face':
        return selection_mode[2]
    return bpy.context.scene.tool_settings.mesh_select_mode


def update_mesh_data():
    bmesh.update_edit_mesh(mod.gObjData)
    return


def count_verts():
    return len(mod.gObjData.vertices)


def get_selected_verts():
    return [v for v in mod.gBMesh.verts if v.select]


def count_selected_verts():
    return len(get_selected_verts())


def get_active_vert(option_selected=0):
    elem = mod.gBMesh.select_history.active
    if elem is not None and isinstance(elem, bmesh.types.BMVert):
        return elem
    elif option_selected == 'sel' and count_selected_verts() > 0:
        elem = mod.gBMesh.verts[0]
        mod.gBMesh.select_history.add(elem)
        return elem
    return None


def set_active_vert(vert):
    if type(vert) == int:
        mod.gBMesh.select_history.add(mod.gBMesh.verts[vert])
    else:
        mod.gBMesh.select_history.add(vert)

    update_mesh_data()
    return


def select_vert(vert):
    if type(vert) == int:
        mod.gBMesh.verts[vert].select = True
    else:
        vert.select = True


def get_vert_coord(vert):
    if type(vert) == int:
        return mod.gBMesh.verts[vert].co
    else:
        return vert.co


def fn_in_q(vtext):
    return chr(34) + str(vtext) + chr(34)


# create Edge
def create_edge(edge_orientation,
                direction_xy,
                axis_angle_xy,
                axis_angle_z,
                edge_lenght):
    verts_sel = get_selected_verts()
    verts_sel_count = count_selected_verts()

    if mod.ActiveVert is None:
        return False

    axis_angle_xy = radians(axis_angle_xy)
    axis_angle_z = radians(axis_angle_z)

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    if edge_orientation == 'global':

        if verts_sel_count < 1:
            return False

        axis_angle_xy = -1 * axis_angle_xy if direction_xy == 'cw' else axis_angle_xy
        new_vert_co = fnget_aequatorial_coordinates(edge_lenght, axis_angle_xy, axis_angle_z)
        vert_new = mod.gBMesh.verts.new(mod.ActiveVert.co + new_vert_co)
        verts_pair = {mod.ActiveVert, vert_new}
        mod.gBMesh.edges.new(verts_pair)

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    if edge_orientation == 'local':

        if verts_sel_count < 2:
            return False

        vlast = count_selected_verts() - 1  # 0 - n internal, 1 - n + 1 return value

        if verts_sel[0].index == mod.ActiveVert.index:
            vdestination_vert = verts_sel[vlast - 1]
            vreference_vert = verts_sel[vlast]
        else:
            vdestination_vert = verts_sel[vlast]
            vreference_vert = verts_sel[vlast - 1]

        xy_offset_angle = fnget_xy_angle(vdestination_vert, vreference_vert)

        if direction_xy == 'cw':
            axis_angle_xy = xy_offset_angle - axis_angle_xy
        else:
            axis_angle_xy += xy_offset_angle

        new_vert_co = fnget_aequatorial_coordinates(edge_lenght, axis_angle_xy, axis_angle_z)

        vert_new = mod.gBMesh.verts.new(vdestination_vert.co + new_vert_co)

        verts_pair = {vdestination_vert, vert_new}
        mod.gBMesh.edges.new(verts_pair)

    select_vert(vert_new)
    set_active_vert(vert_new)

    update_mesh_data()

    return {'FINISHED'}

def fnget_xy_angle(vertmajor, vertminor):
    vect_diff = vertmajor.co - vertminor.co
    v_angle = atan2(vect_diff.y, vect_diff.x)
    return v_angle + (2 * pi) if (v_angle < 0) else v_angle


def fnget_aequatorial_coordinates(v_radius, v_azimut_xy, v_polarangle_z):
    x = v_radius * cos(v_polarangle_z) * cos(v_azimut_xy)
    y = v_radius * cos(v_polarangle_z) * sin(v_azimut_xy)
    z = v_radius * sin(v_polarangle_z)
    return Vector([x, y, z])


def fn_err_msg(slayout=None):
    err_msg = 'Base ' + fn_in_q(
        'Local') + ': ' + 'Select 2 verts, 1 vert as active (shift MB) :: Base ' + fn_in_q(
        'Global') + ': ' + 'Select 1 vert, 1 vert as active'

    if slayout is not None:
        slayout.label('Base ' + fn_in_q('Local') + ': ' + 'Select 2 verts, 1 vert as active (shift MB)')
        slayout.label('Base ' + fn_in_q('Global') + ': ' + 'Select 1 vert, 1 vert as active')
    return err_msg


def register():
    bpy.utils.register_module(__name__)
    print('register done')


def unregister():
    bpy.utils.unregister_module(__name__)
    print('unregister done')


class ToolsPanel(bpy.types.Panel):
    bl_label = 'Adjust Mesh'
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_category = 'Tools'
    bl_context = "mesh_edit"
    bl_options = {'DEFAULT_CLOSED'}

    def draw(self, context):
        slayout = self.layout

        row = slayout.row(align=True)
        row.operator("mesh.angle_edge")

        row = slayout.row(align=True)
        row.operator("mesh.set_edge_n90")
        row.operator("mesh.set_edge_n45")
        row.operator("mesh.set_edge_0")
        row.operator("mesh.set_edge_p45")
        row.operator("mesh.set_edge_p90")

        row = slayout.row(align=True)
        row.prop(context.scene, 'floatLenghtA')


#
#    The  button classes
#

# Button to direct create Edge Angle
class obj_set_edge_button_n90(bpy.types.Operator):
    bl_idname = "mesh.set_edge_n90"
    bl_label = "<90"
    bl_options = {'UNDO'}  # Undo, Push an undo event (needed for operator redo)

    def execute(self, context):
        init_mesh_edit()
        mod.ActiveVert = get_active_vert()
        mod.SELF = self
        try:
            v_lenght = bpy.context.scene['floatLenghtA']
        except (RuntimeError, TypeError, NameError):
            v_lenght = 1.0
        v_result = create_edge('local',
                              'ccw',
                              90,
                              0,
                               v_lenght)
        if v_result:
            self.report({mod.info}, ": 90 degree ccw Edge Angle created")
        else:
            self.report({mod.warning}, mod.errmsg)
        return {'FINISHED'}


# Button to direct create Edge Angle
class obj_set_edge_button_n45(bpy.types.Operator):
    bl_idname = "mesh.set_edge_n45"
    bl_label = "<45"
    bl_options = {'UNDO'}  # Undo, Push an undo event (needed for operator redo)

    def execute(self, context):
        init_mesh_edit()
        mod.ActiveVert = get_active_vert()
        mod.SELF = self
        try:
            v_lenght = bpy.context.scene['floatLenghtA']
        except (RuntimeError, TypeError, NameError):
            v_lenght = 1.0
        v_result = create_edge('local',
                               'ccw',
                               45,
                               0,
                               v_lenght)
        if v_result:
            self.report({mod.info}, ": 45 degree ccw Edge Angle created")
        else:
            self.report({mod.warning}, mod.errmsg)
        return {'FINISHED'}


# Button to direct create Edge Angle
class obj_set_edge_button_0(bpy.types.Operator):
    bl_idname = "mesh.set_edge_0"
    bl_label = "0"
    bl_options = {'UNDO'}  # Undo, Push an undo event (needed for operator redo)

    def execute(self, context):
        init_mesh_edit()
        mod.ActiveVert = get_active_vert()
        mod.SELF = self
        try:
            v_lenght = bpy.context.scene['floatLenghtA']
        except (RuntimeError, TypeError, NameError):
            v_lenght = 1.0
        v_result = create_edge('local',
                               'ccw',
                               0,
                               0,
                               v_lenght)
        if v_result:
            self.report({mod.info}, ": straight Edge Angle created")
        else:
            self.report({mod.warning}, mod.errmsg)
        return {'FINISHED'}


# Button to direct create Edge Angle
class obj_set_edge_button_p45(bpy.types.Operator):
    bl_idname = "mesh.set_edge_p45"
    bl_label = "45>"
    bl_options = {'UNDO'}  # Undo, Push an undo event (needed for operator redo)

    def execute(self, context):
        init_mesh_edit()
        mod.ActiveVert = get_active_vert()
        mod.SELF = self
        try:
            v_lenght = bpy.context.scene['floatLenghtA']
        except (RuntimeError, TypeError, NameError):
            v_lenght = 1.0
        v_result = create_edge('local',
                               'cw',
                               45,
                               0,
                               v_lenght)
        if v_result:
            self.report({mod.info}, ": 45 degree cw Edge Angle created")
        else:
            self.report({mod.warning}, mod.errmsg)
        return {'FINISHED'}


# Button to direct create Edge Angle
class obj_set_edge_button_p90(bpy.types.Operator):
    bl_idname = "mesh.set_edge_p90"
    bl_label = "90>"
    bl_options = {'UNDO'}  # Undo, Push an undo event (needed for operator redo)

    def execute(self, context):
        init_mesh_edit()
        mod.ActiveVert = get_active_vert()
        mod.SELF = self
        try:
            v_lenght = bpy.context.scene['floatLenghtA']
        except (RuntimeError, TypeError, NameError):
            v_lenght = 1.0
        v_result = create_edge('local',
                               'cw',
                               90,
                               0,
                               v_lenght)
        if v_result:
            self.report({mod.info}, ": 90 degree cw Edge Angle created")
        else:
            self.report({mod.warning}, mod.errmsg)
        return {'FINISHED'}


# Button to open Angle edge -Dialog
class obj_make_edge_Button(bpy.types.Operator):
    bl_idname = "mesh.angle_edge"
    bl_label = "Angle Edge"

    def execute(self, context):
        bpy.ops.SetAngleEdgeDialogOperator('INVOKE_DEFAULT')
        return {'FINISHED'}


class SetAngleEdgeDialogOperator(bpy.types.Operator):
    bl_idname = "mesh.angle_edge"
    bl_label = 'Angle Edge Dialog'
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'WINDOW'
    bl_context = "data"
    bl_options = {'REGISTER', 'UNDO'}  # Wichtig! ohne diese Einträge kein F6-Menü

    # Local / Global Toggle Button
    enum_items = (('local',
                   'Local',
                   'create edge, local angle'),
                  ('global',
                   'Global',
                   'create edge, global angle'))
    bpy.types.Scene.Toggle_Local_Global = bpy.props.EnumProperty(items=enum_items, default="local")

    # cw / ccw Toggle Button
    enum_items = (('ccw',
                   'ccw',
                   'create edge, counterclockwise'),
                  ('cw',
                   'cw',
                   'create edge, clockwise'))
    bpy.types.Scene.Toggle_cw_ccw = bpy.props.EnumProperty(items=enum_items, default='cw')

    floatAngleXY = bpy.props.FloatProperty(name="cw/ccw Angle",
                                           description="Angle xy",
                                           default=0,
                                           min=-360,
                                           max=360,
                                           precision=2)

    floatAngleZ = bpy.props.FloatProperty(name="up/down Angle",
                                          description="Angle z",
                                          default=0,
                                          min=-360,
                                          max=360,
                                          precision=2)

    floatLenght = bpy.props.FloatProperty(name="Lenght",
                                          description="Lenght of new edge",
                                          default=1,
                                          min=-9999,
                                          max=9999,
                                          soft_min=-100,
                                          soft_max=100,
                                          precision=3)

    # Execution
    def execute(self, context):
        init_mesh_edit()
        mod.SELF = self

        bool_result = create_edge(mod.ctx.Toggle_Local_Global,
                              mod.ctx.Toggle_cw_ccw,
                              self.floatAngleXY,
                              self.floatAngleZ,
                              self.floatLenght)
        if bool_result:
            self.report({mod.info}, ": Edge Angle created")
        else:
            err_msg = fn_err_msg()
            self.report({mod.warning}, ': wrong selection: ' + err_msg)
        return {'FINISHED'}

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_props_dialog(self)

    def draw(self, context):
        slayout = self.layout
        init_mesh_edit()
        if get_bmesh_selection_mode('vert'):
            mod.ActiveVert = get_active_vert()
            verts_sel_count = count_selected_verts()

            if verts_sel_count == 0 or mod.ActiveVert is None:
                err_msg = fn_err_msg(slayout)
                self.report({mod.warning}, ': no selection: ' + err_msg)
            else:
                # Check Edges Data Enviroment
                check_msg = 'Mesh: ' + mod.gObj.name
                slayout.label(check_msg)

                # new row in Dialog
                row = slayout.row()

                # Local / Global Toggle Button
                row.prop(context.scene, 'Toggle_Local_Global', expand=True)

                # cw / ccw Toggle Button
                row.prop(context.scene, 'Toggle_cw_ccw', expand=True)

                # new row in Dialog
                row = slayout.row(align=True)

                # cw / ccw Angle
                row.prop(self,
                         'floatAngleXY',
                         toggle=True)

                # z Angle
                row.prop(self,
                         'floatAngleZ',
                         toggle=True)

                # new row in Dialog
                row = slayout.row()
                row.prop(self,
                         'floatLenght',
                         toggle=True)


class ClassModulGlobalVar:
    pass

mod = ClassModulGlobalVar()
mod.SELF = None
mod.gObj = None
mod.gObjData = None
mod.gBMesh = None
mod.gMat_world = None
mod.ctx = None
mod.ActiveVert = None
mod.ActiveEdge = None
mod.fDiv = 1
mod.fAbs = 0
mod.errmsg = '\n* Need two selected Verts \n* 1 vert must be active [shift click]'
mod.info = 'INFO'
mod.warning = 'WARNING'

if __name__ == "__main__":
    register()
$\endgroup$
1
  • 1
    $\begingroup$ I would say vert coordinates are always local. This appears overly complicated. Lets call verts o (centre) n, e, s, and w. If select edges 'no' and 'eo` (the 2 edges define a plane) and the cross product gives us a normal. Having the plane and the normal, the edges can be projected onto the plane, then rotated, using the normal as axis, to make each angle 90 degrees, using one of the two selected edges as an arbitrary starting point. $\endgroup$
    – batFINGER
    Nov 13, 2017 at 12:42
0
$\begingroup$

I may something interesting, but also perhaps not. I tl;dr-ed your code (definitely shouldn't, sorry), but I did found (web browser search) that you didn't use the world matrix backwards.

My trick is this: if "[local_coord_vector][matrix_world]" gives global coordinates; "[matrix_world].inverted()[global_coord_vector]" gives local coordinates. this might be slightly different if there is a parenting busyness with your object as child, but still.

Also, the multiplication order IS important, because [vector][matrix]!=[matrix][vector]

Again, this is a quick tip from a cheap trick, written in 5 minutes. But it will help. probably. And sorry if it doesn't. Anyways, have a nice day!

$\endgroup$
1
  • $\begingroup$ sh.. you are right. Of course i need the the matrix_world. I saw it when i try to use my Code in practical application. A vert is a Part of an Object, so i had to add the Object coordinates. Fortunately the weather is quite bad ... $\endgroup$
    – UncleTeo
    Oct 3, 2016 at 6:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .