示例#1
0
class OPENDENTAL_OT_add_bone_roots(bpy.types.Operator):
    """Set the axis and direction of the roots for crowns from view"""
    bl_idname = "opendental.add_bone_roots"
    bl_label = "Add bone roots"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(self, context):
        if context.mode != 'OBJECT':
            return False
        else:
            return True

    def set_axis(self, context, event):

        if not self.target:
            return

        empty_name = self.target.name + 'root_empty'
        if empty_name in context.scene.objects:
            ob = context.scene.objects[empty_name]
            ob.empty_draw_type = 'SINGLE_ARROW'
            ob.empty_draw_size = 10
        else:
            ob = bpy.data.objects.new(empty_name, None)
            ob.empty_draw_type = 'SINGLE_ARROW'
            ob.empty_draw_size = 10
            context.scene.objects.link(ob)

        coord = (event.mouse_region_x, event.mouse_region_y)
        v3d = context.space_data
        rv3d = v3d.region_3d
        view_vector = view3d_utils.region_2d_to_vector_3d(
            context.region, rv3d, coord)
        ray_origin = view3d_utils.region_2d_to_origin_3d(
            context.region, rv3d, coord)
        ray_target = ray_origin + (view_vector * 1000)
        if bversion() < '002.077.000':
            res, obj, loc, no, mx = context.scene.ray_cast(
                ray_origin, ray_target)
        else:
            res, loc, no, ind, obj, mx = context.scene.ray_cast(
                ray_origin, view_vector)

        if res:
            if obj != self.target:
                return

            ob.location = loc
        else:
            return

        if ob.rotation_mode != 'QUATERNION':
            ob.rotation_mode = 'QUATERNION'

        vrot = rv3d.view_rotation
        ob.rotation_quaternion = vrot

    def advance_next_prep(self, context):
        if self.target == None:
            self.target = self.units[0]

        ind = self.units.index(self.target)
        prev = int(math.fmod(ind + 1, len(self.units)))
        self.target = self.units[prev]
        self.message = "Set axis for %s" % self.target.name
        self.target_box.raw_text = self.message
        self.target_box.format_and_wrap_text()
        self.target_box.fit_box_width_to_text_lines()

        for obj in context.scene.objects:
            obj.select = False

        self.target.select = True
        context.space_data.region_3d.view_location = self.target.location

    def select_prev_unit(self, context):
        if self.target == None:
            self.target = self.units[0]

        ind = self.units.index(self.target)
        prev = int(math.fmod(ind - 1, len(self.units)))
        self.target = self.units[prev]
        self.message = "Set axis for %s" % self.target.name
        self.target_box.raw_text = self.message
        self.target_box.format_and_wrap_text()
        self.target_box.fit_box_width_to_text_lines()

        for obj in context.scene.objects:
            obj.select = False

        self.target.select = True
        context.space_data.region_3d.view_location = self.target.location

    def update_selection(self, context):
        if not len(context.selected_objects):
            self.message = "Right Click to Select"
            self.target = None
            return

        if context.selected_objects[0] not in self.units:
            self.message = "Selected Object must be tooth"
            self.target = None
            return

        self.target = context.selected_objects[0]
        self.message = "Set axis for %s" % self.target.name
        self.target_box.raw_text = self.message
        self.target_box.format_and_wrap_text()
        self.target_box.fit_box_width_to_text_lines()

    def empties_to_bones(self, context):
        bpy.ops.object.select_all(action='DESELECT')

        arm_ob = bpy.data.objects['Roots']
        arm_ob.select = True
        context.scene.objects.active = arm_ob
        bpy.ops.object.mode_set(mode='EDIT')

        for ob in self.units:
            e = context.scene.objects.get(ob.name + 'root_empty')
            b = arm_ob.data.edit_bones.get(ob.name + 'root')

            if e != None and b != None:
                b.transform(
                    e.matrix_world)  #this gets the local x,y,z in order
                Z = e.matrix_world.to_quaternion() * Vector((0, 0, 1))
                b.tail.xyz = e.location
                b.head.xyz = e.location - 16 * Z
                b.head_radius = 1.5
                b.tail_radius = 2.5

                context.scene.objects.unlink(e)
                e.user_clear()
                bpy.data.objects.remove(e)
            else:
                print('missing bone or empty')

        bpy.ops.object.mode_set(mode='OBJECT')

    def modal_main(self, context, event):
        # general navigation
        nmode = self.modal_nav(event)
        if nmode != '':
            return nmode  #stop here and tell parent modal to 'PASS_THROUGH'

        if event.type in {'RIGHTMOUSE'} and event.value == 'PRESS':
            self.update_selection(context)
            return 'pass'

        elif event.type == 'RIGHTMOUSE' and event.value == 'RELEASE':
            self.update_selection(context)
            if len(context.selected_objects):
                context.space_data.region_3d.view_location = context.selected_objects[
                    0].location
            return 'main'

        elif event.type in {'LEFTMOUSE'} and event.value == 'PRESS':
            self.set_axis(context, event)
            self.advance_next_prep(context)
            return 'main'

        elif event.type in {'DOWN_ARROW'} and event.value == 'PRESS':
            self.select_prev_unit(context)
            return 'main'

        elif event.type in {'UP_ARROW'} and event.value == 'PRESS':
            self.advance_next_prep(context)
            return 'main'

        elif event.type in {'ESC'}:
            #keep track of and delete new objects? reset old transforms?
            return 'cancel'

        elif event.type in {'RET'} and event.value == 'PRESS':
            self.empties_to_bones(context)
            return 'finish'

        return 'main'

    def modal_nav(self, event):
        events_nav = {
            'MIDDLEMOUSE', 'WHEELINMOUSE', 'WHEELOUTMOUSE', 'WHEELUPMOUSE',
            'WHEELDOWNMOUSE'
        }  #TODO, better navigation, another tutorial
        handle_nav = False
        handle_nav |= event.type in events_nav

        if handle_nav:
            return 'nav'
        return ''

    def modal(self, context, event):
        context.area.tag_redraw()

        FSM = {}
        FSM['main'] = self.modal_main
        FSM['pass'] = self.modal_main
        FSM['nav'] = self.modal_nav

        nmode = FSM[self.mode](context, event)

        if nmode == 'nav':
            return {'PASS_THROUGH'}

        if nmode in {'finish', 'cancel'}:
            #clean up callbacks
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'} if nmode == 'finish' else {'CANCELLED'}
        if nmode == 'pass':
            self.mode = 'main'
            return {'PASS_THROUGH'}

        if nmode: self.mode = nmode

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        settings = get_settings()
        dbg = settings.debug

        if context.space_data.region_3d.is_perspective:
            #context.space_data.region_3d.is_perspective = False
            bpy.ops.view3d.view_persportho()

        if context.space_data.type != 'VIEW_3D':
            self.report({'WARNING'}, "Active space must be a View3d")
            return {'CANCELLED'}

        #gather all the teeth in the scene TODO, keep better track
        self.units = []

        for i in TOOTH_NUMBERS:
            ob = context.scene.objects.get(str(i))
            if ob != None and not ob.hide:
                self.units.append(ob)

        if not len(self.units):
            self.report({
                'ERROR'
            }, "There are no teeth in the scene!, Teeth must be named 2 digits eg 11 or 46"
                        )
            return {'CANCELLED'}

        self.target = self.units[0]
        self.message = "Set axis for %s" % self.target.name

        #check for an armature
        bpy.ops.object.select_all(action='DESELECT')
        if context.mode != 'OBJECT':
            bpy.ops.object.mode_set(mode='OBJECT')

        if context.scene.objects.get('Roots'):
            root_arm = context.scene.objects.get('Roots')
            root_arm.select = True
            root_arm.hide = False
            context.scene.objects.active = root_arm
            bpy.ops.object.mode_set(mode='EDIT')

            for ob in self.units:
                if ob.name + 'root' not in root_arm.data.bones:
                    bpy.ops.armature.bone_primitive_add(name=ob.name + 'root')

        else:
            root_data = bpy.data.armatures.new('Roots')
            root_arm = bpy.data.objects.new('Roots', root_data)
            context.scene.objects.link(root_arm)

            root_arm.select = True
            context.scene.objects.active = root_arm
            bpy.ops.object.mode_set(mode='EDIT')

            for ob in self.units:
                bpy.ops.armature.bone_primitive_add(name=ob.name + 'root')

        bpy.ops.object.mode_set(mode='OBJECT')
        root_arm.select = False
        self.units[0].select = True

        help_txt = "Right click to select a tooth \n Align View with root, mes and distal\n Up Arrow and Dn Arrow to select different units \n Left click in middle of prep to set axis \n Enter to finish \n ESC to cancel"
        self.help_box = TextBox(context, 500, 500, 300, 200, 10, 20, help_txt)
        self.help_box.fit_box_width_to_text_lines()
        self.help_box.fit_box_height_to_text_lines()
        self.help_box.snap_to_corner(context, corner=[1, 1])

        aspect, mid = menu_utils.view3d_get_size_and_mid(context)
        self.target_box = TextBox(context, mid[0], aspect[1] - 20, 300, 200,
                                  10, 20, self.message)
        self.target_box.format_and_wrap_text()
        self.target_box.fit_box_width_to_text_lines()
        self.target_box.fit_box_height_to_text_lines()

        self.mode = 'main'
        context.window_manager.modal_handler_add(self)
        self._handle = bpy.types.SpaceView3D.draw_handler_add(
            insertion_axis_draw_callback, (self, context), 'WINDOW',
            'POST_PIXEL')
        return {'RUNNING_MODAL'}
示例#2
0
class OPENDENTAL_OT_fast_label_teeth(bpy.types.Operator):
    """Label teeth by clicking on them"""
    bl_idname = "opendental.fast_label_teeth"
    bl_label = "Fast Label Teeth"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(self, context):
        if context.mode != 'OBJECT':
            return False
        else:
            return True

    def set_axis(self, context, event):

        coord = (event.mouse_region_x, event.mouse_region_y)
        v3d = context.space_data
        rv3d = v3d.region_3d
        view_vector = view3d_utils.region_2d_to_vector_3d(
            context.region, rv3d, coord)
        ray_origin = view3d_utils.region_2d_to_origin_3d(
            context.region, rv3d, coord)
        ray_target = ray_origin + (view_vector * 1000)
        if bversion() < '002.077.000':
            res, obj, loc, no, mx = context.scene.ray_cast(
                ray_origin, ray_target)
        else:
            res, loc, no, ind, obj, mx = context.scene.ray_cast(
                ray_origin, view_vector)

        if res:
            obj.name = str(self.target)
            for ob in bpy.data.objects:
                ob.select = False
            obj.select = True
            obj.show_name = True
            context.scene.objects.active = obj
            bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
            return True
        else:
            return False

    def advance_next_prep(self, context):
        def next_ind(n):
            if math.fmod(n, 10) < 7:
                return n + 1
            elif math.fmod(n, 10) == 7:
                if n == 17: return 21
                elif n == 27: return 31
                elif n == 37: return 41
                elif n == 47: return 11

        self.target = next_ind(self.target)
        self.message = "Click on tooth % i" % self.target
        self.target_box.raw_text = self.message
        self.target_box.format_and_wrap_text()
        self.target_box.fit_box_width_to_text_lines()

    def select_prev_unit(self, context):
        def prev_ind(n):
            if math.fmod(n, 10) > 1:
                return n - 1
            elif math.fmod(n, 10) == 1:
                if n == 11: return 41
                elif n == 21: return 11
                elif n == 31: return 21
                elif n == 41: return 31

        self.target = prev_ind(self.target)

        self.message = "Click on tooth %i" % self.target
        self.target_box.raw_text = self.message
        self.target_box.format_and_wrap_text()
        self.target_box.fit_box_width_to_text_lines()

    def modal_main(self, context, event):
        # general navigation
        nmode = self.modal_nav(event)
        if nmode != '':
            return nmode  #stop here and tell parent modal to 'PASS_THROUGH'

        if event.type in {'RIGHTMOUSE'} and event.value == 'PRESS':
            self.advance_next_prep(context)
            return 'pass'

        elif event.type in {'LEFTMOUSE'} and event.value == 'PRESS':
            res = self.set_axis(context, event)
            if res:
                self.advance_next_prep(context)
            return 'main'

        elif event.type in {'DOWN_ARROW'} and event.value == 'PRESS':
            self.select_prev_unit(context)
            return 'main'

        elif event.type in {'UP_ARROW'} and event.value == 'PRESS':
            self.advance_next_prep(context)
            return 'main'

        elif event.type in {'ESC'}:
            #keep track of and delete new objects? reset old transforms?
            return 'cancel'

        elif event.type in {'RET'} and event.value == 'PRESS':
            #self.empties_to_bones(context)
            return 'finish'

        return 'main'

    def modal_nav(self, event):
        events_nav = {
            'MIDDLEMOUSE', 'WHEELINMOUSE', 'WHEELOUTMOUSE', 'WHEELUPMOUSE',
            'WHEELDOWNMOUSE'
        }  #TODO, better navigation, another tutorial
        handle_nav = False
        handle_nav |= event.type in events_nav

        if handle_nav:
            return 'nav'
        return ''

    def modal(self, context, event):
        context.area.tag_redraw()

        FSM = {}
        FSM['main'] = self.modal_main
        FSM['pass'] = self.modal_main
        FSM['nav'] = self.modal_nav

        nmode = FSM[self.mode](context, event)

        if nmode == 'nav':
            return {'PASS_THROUGH'}

        if nmode in {'finish', 'cancel'}:
            #clean up callbacks
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'} if nmode == 'finish' else {'CANCELLED'}
        if nmode == 'pass':
            self.mode = 'main'
            return {'PASS_THROUGH'}

        if nmode: self.mode = nmode

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        settings = get_settings()
        dbg = settings.debug

        if context.space_data.region_3d.is_perspective:
            #context.space_data.region_3d.is_perspective = False
            bpy.ops.view3d.view_persportho()

        if context.space_data.type != 'VIEW_3D':
            self.report({'WARNING'}, "Active space must be a View3d")
            return {'CANCELLED'}

        #gather all the teeth in the scene TODO, keep better track

        self.target = 11
        self.message = "Set axis for " + str(self.target)

        if context.mode != 'OBJECT':
            bpy.ops.object.mode_set(mode='OBJECT')

        #check for an armature
        bpy.ops.object.select_all(action='DESELECT')

        help_txt = "Left click on the tooth indicated to label it. Right click skip a tooth \n Up or Dn Arrow to change label\n Enter to finish \n ESC to cancel"
        self.help_box = TextBox(context, 500, 500, 300, 200, 10, 20, help_txt)
        self.help_box.fit_box_width_to_text_lines()
        self.help_box.fit_box_height_to_text_lines()
        self.help_box.snap_to_corner(context, corner=[1, 1])

        aspect, mid = menu_utils.view3d_get_size_and_mid(context)
        self.target_box = TextBox(context, mid[0], aspect[1] - 20, 300, 200,
                                  10, 20, self.message)
        self.target_box.format_and_wrap_text()
        self.target_box.fit_box_width_to_text_lines()
        self.target_box.fit_box_height_to_text_lines()

        self.mode = 'main'
        context.window_manager.modal_handler_add(self)
        self._handle = bpy.types.SpaceView3D.draw_handler_add(
            rapid_label_teeth_callback, (self, context), 'WINDOW',
            'POST_PIXEL')
        return {'RUNNING_MODAL'}