Esempio n. 1
0
    def execute(self, context):
        #add a textbox to display information.  attach it to this
        #add a persisetent callback on scene update
        #which monitors the status of the ODC

        #clear previous handlers
        clear_help_handlers()
        global guide_help_app_handle
        guide_help_app_handle = bpy.app.handlers.scene_update_pre.append(
            guide_help_parser)

        global help_display_box
        if help_display_box != None:
            del help_display_box
        help_text = 'Open Dental Guide Help Wizard \n'
        help_text += 'Next Step: ' + 'TBA'

        help_display_box = TextBox(context, 500, 500, 300, 100, 10, 20,
                                   help_text)
        help_display_box.snap_to_corner(context, corner=[0, 1])

        global guide_help_draw_handle
        guide_help_draw_handle = bpy.types.SpaceView3D.draw_handler_add(
            odc_help_draw, (self, context), 'WINDOW', 'POST_PIXEL')
        return {'FINISHED'}
Esempio n. 2
0
    def execute(self, context):
        #add a textbox to display information.  attach it to this
        #add a persisetent callback on scene update
        #which monitors the status of the ODC

        #clear previous handlers
        clear_help_handlers()
        global implant_help_app_handle
        implant_help_app_handle = bpy.app.handlers.scene_update_pre.append(
            implant_help_parser)

        global help_display_box
        if help_display_box != None:
            del help_display_box
        help_text = 'Open Dental Implant Help Wizard \n'
        selections = odcutils.implant_selection(
            bpy.context)  #weird, how do I specify better arguments?
        sel_names = [item.name for item in selections]
        help_text += 'Selected Implants: ' + ', '.join(sel_names) + '\n'
        help_text += 'Next Step: ' + 'TBA'

        help_display_box = TextBox(context, 500, 500, 300, 100, 10, 20,
                                   help_text)
        help_display_box.snap_to_corner(context, corner=[0, 1])

        global implant_help_draw_handle
        implant_help_draw_handle = bpy.types.SpaceView3D.draw_handler_add(
            odc_help_draw, (self, context), 'WINDOW', 'POST_PIXEL')
        return {'FINISHED'}
Esempio n. 3
0
class OPENDENTAL_OT_place_bracket(bpy.types.Operator):
    """Place Bracket on surface of selected object"""
    bl_idname = "opendental.place_ortho_bracket"
    bl_label = "Ortho Bracket Place"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        if context.mode == "OBJECT" and context.object != None:
            return True
        else:
            return False

    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_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 == 'G' and event.value == 'PRESS' and self.bracket_slicer:
            self.bracket_slicer.prepare_slice()
            return 'grab'

        if event.type == 'T' and event.value == 'PRESS' and self.bracket_slicer:
            self.bracket_slicer.prepare_slice()
            return 'torque'

        if event.type == 'R' and event.value == 'PRESS' and self.bracket_slicer:
            self.bracket_slicer.prepare_slice()
            return 'rotate'

        if event.type == 'S' and event.value == 'PRESS' and self.bracket_slicer:
            self.bracket_slicer.prepare_slice()
            return 'tip'

        if event.type == 'MOUSEMOVE':
            return 'main'

        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            x, y = event.mouse_region_x, event.mouse_region_y
            self.bracket_manager.place_bracket(context, x, y)
            return 'main'

        if event.type == 'RET' and event.value == 'PRESS':
            if self.bracket_slicer:
                self.bracket_slicer.cache_slice_to_grease(context)

            return 'finish'

        elif event.type == 'ESC' and event.value == 'PRESS':
            del_obj = self.bracket_manager.bracket_obj
            context.scene.objects.unlink(del_obj)
            bpy.data.objects.remove(del_obj)
            return 'cancel'

        return 'main'

    def modal_torque(self, context, event):
        # no navigation in grab mode

        if event.type in {'LEFTMOUSE', 'RET', 'ENTER'
                          } and event.value == 'PRESS':
            #confirm location
            self.bracket_slicer.slice_confirm()
            return 'main'

        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.bracket_slicer.slice_cancel()
            return 'main'

        #elif event.type == 'MOUSEMOVE':
        #update the b_pt location
        #    self.bracket_slicer.slice_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
        #    return 'torque'

        elif event.type in {
                'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 'UP_ARROW', 'DOWN_ARROW'
        }:
            self.bracket_manager.torque_event(event.type, event.shift)
            self.bracket_slicer.slice()
            return 'torque'

    def modal_rotate(self, context, event):
        # no navigation in grab mode

        if event.type in {'LEFTMOUSE', 'RET', 'ENTER'
                          } and event.value == 'PRESS':
            #confirm location
            self.bracket_slicer.slice_confirm()
            return 'main'

        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.bracket_slicer.slice_cancel()
            return 'main'

        #commented out, no longer want to move the mouse
        #elif event.type == 'MOUSEMOVE':
        #update the b_pt location
        #    self.bracket_slicer.slice_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
        #    return 'rotate'

        elif event.type in {
                'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 'LEFT_ARROW', 'RIGHT_ARROW'
        }:
            self.bracket_manager.rotate_event(event.type, event.shift)
            self.bracket_slicer.slice()
            return 'rotate'

        else:
            return 'rotate'

    def modal_tip(self, context, event):
        # no navigation in grab mode

        if event.type in {'LEFTMOUSE', 'RET', 'ENTER'
                          } and event.value == 'PRESS':
            #confirm location
            self.bracket_slicer.slice_confirm()
            return 'main'

        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.bracket_slicer.slice_cancel()
            return 'main'

        #commented out, no longer want to move the mouse
        #elif event.type == 'MOUSEMOVE':
        #update the b_pt location
        #    self.bracket_slicer.slice_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
        #    return 'rotate'

        elif event.type in {
                'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 'LEFT_ARROW', 'RIGHT_ARROW'
        }:
            self.bracket_manager.spin_event(event.type, event.shift)
            self.bracket_slicer.slice()
            return 'tip'

        else:
            return 'tip'

    def modal_start(self, context, event):

        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.bracket_slicer.slice_confirm()
            return 'main'

        elif event.type == 'MOUSEMOVE':
            x, y = event.mouse_region_x, event.mouse_region_y
            self.bracket_manager.place_bracket(context, x, y, normal=True)
            self.bracket_slicer.slice_mouse_move(context, event.mouse_region_x,
                                                 event.mouse_region_y)
            return 'start'

        elif event.type in {
                'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 'UP_ARROW', 'DOWN_ARROW'
        }:
            self.bracket_manager.spin_event(event.type, event.shift)
            self.bracket_slicer.slice()
            return 'start'

        elif event.type == "RIGTMOUSE" and event.value == 'PRESS':
            del_obj = self.bracket_manager.bracket_obj
            context.scene.objects.unlink(del_obj)
            bpy.data.objects.remove(del_obj)
            return 'cancel'

        else:
            return 'start'

    def modal_grab(self, context, event):
        # no navigation in grab mode
        #uses the slicer to manage the grab

        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.bracket_slicer.slice_confirm()
            return 'main'

        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.bracket_slicer.slice_cancel()
            return 'main'

        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.bracket_slicer.slice_mouse_move(context, event.mouse_region_x,
                                                 event.mouse_region_y)
            return 'grab'

        elif event.type in {
                'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 'UP_ARROW', 'DOWN_ARROW'
        }:
            self.bracket_manager.spin_event(event.type, event.shift)
            self.bracket_slicer.slice()
            return 'grab'

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

        FSM = {}
        FSM['start'] = self.modal_start
        FSM['main'] = self.modal_main
        FSM['rotate'] = self.modal_rotate
        FSM['grab'] = self.modal_grab
        FSM['torque'] = self.modal_torque
        FSM['tip'] = self.modal_tip
        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: self.mode = nmode

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):

        settings = get_settings()
        libpath = settings.ortho_lib
        assets = obj_list_from_lib(libpath)

        if settings.bracket in assets:
            current_obs = [ob.name for ob in bpy.data.objects]
            obj_from_lib(settings.ortho_lib, settings.bracket)
            for ob in bpy.data.objects:
                if ob.name not in current_obs:
                    Bracket = ob
                    Bracket.hide = False

            context.scene.objects.link(Bracket)
        else:
            Bracket = None

        if context.object and context.object.type == 'MESH':
            self.bracket_manager = BracketDataManager(
                context,
                snap_type='OBJECT',
                snap_object=context.object,
                name='Bracket',
                bracket=Bracket)
            self.bracket_slicer = BracektSlicer(context, self.bracket_manager)
        else:
            self.bracket_manager = BracketDataManager(context,
                                                      snap_type='SCENE',
                                                      snap_object=None,
                                                      name='Bracket',
                                                      bracket=Bracket)
            self.bracket_slicer = None

        help_txt = "DRAW MARGIN OUTLINE\n\nLeft Click on model to place bracket.\n G to grab  \n S to show slice \n ENTER to confirm \n ESC to cancel"
        self.help_box = TextBox(context, 500, 500, 300, 200, 10, 20, help_txt)
        self.help_box.snap_to_corner(context, corner=[1, 1])
        self.mode = 'start'
        self._handle = bpy.types.SpaceView3D.draw_handler_add(
            bracket_placement_draw_callback, (self, context), 'WINDOW',
            'POST_PIXEL')
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}
Esempio n. 4
0
class D3SPLINT_OT_plane_cut_mesh_rough(bpy.types.Operator):
    """Click and draw a line to cut mesh"""
    bl_idname = "d3splint.splint_plane_cut"
    bl_label = "Plane Cut Mesh"
    bl_options = {'REGISTER', 'UNDO'}
    
    cut_method = bpy.props.EnumProperty(name = 'Mode', items = (('SURFACE','SURFACE','SURFACE'), ('SOLID', 'SOLID','SOLID')), default = 'SURFACE')
    @classmethod
    def poll(cls,context):
        if context.object == None: return False
        if context.object.type != 'MESH': return False
        
        return True
    
    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_extend(self, context, event):
        
        if event.type == 'MOUSEMOVE':
            
            x, y = event.mouse_region_x, event.mouse_region_y
            
            #get mouse position at depth of the extrusion midpoint
            mouse_projected = view3d_utils.region_2d_to_location_3d(context.region, context.region_data, (x,y), self.new_geom_point)
            imx = self.ob.matrix_world.inverted()
            #calculate the delta vector
            local_delta = imx * mouse_projected - imx * self.new_geom_point
            
            #update bmverts position
            for v in self.extrude_verts:
                v.co = self.extrude_origins[v] + local_delta
                
            #how costly is this to do live? We will find out
            self.bme.to_mesh(self.ob.data)
            self.ob.data.update()
           
            
            
            return 'extend'
        
        elif event.type == 'LEFTMOUSE' and event.value == 'PRESS':
        
            #confirm vert positions
            self.new_geom = []
            self.new_geom_point = Vector((0,0,0))
            self.new_geom_draw_points = []
        
            self.extrude_verts = []
            self.extrude_origins = dict()
            self.extrude_geom_draw_points = []
            #clear "extrusion candidates"
            
            return 'main'
        
        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            
            #delete extruded verts
            bmesh.ops.delete(self.bme, geom = self.extrude_verts, context = 1)
            self.bme.verts.ensure_lookup_table()
            self.bme.edges.ensure_lookup_table()
            self.bme.faces.ensure_lookup_table()
            self.bme.to_mesh(self.obj.data)
            self.ob.data.update()
            
            self.new_geom = []
            self.new_geom_point = Vector((0,0,0))
            self.new_geom_draw_points = []
        
            self.extrude_verts = []
            self.extrude_origns = dict()
            self.extrude_geom_draw_points = []
            return 'main'
        
    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 == 'MOUSEMOVE':
            
            x, y = event.mouse_region_x, event.mouse_region_y
            
            
            
            #######Hover Operator Level Elements  ###
            if len(self.crv.screen_pts) == 0:
                if len(self.new_geom):
                    region = context.region
                    rv3d = context.region_data
                    extrude_screen_point = view3d_utils.location_3d_to_region_2d(region, rv3d, self.new_geom_point)
                    if extrude_screen_point != None:
                        R = Vector((x,y)) - extrude_screen_point
                        if R.length < 30:
                            
                            self.new_geom_point_color = (1,.1,.1,1)
                            self.new_geom_color = (1,.1,.1,1)
                        else:
                            self.new_geom_color = (.8, .8, .1, 1)
                            self.new_geom_point_color = (.1, .1, .8, 1)
            
            ######Hover Line Drawer Class Elments###
            self.crv.hover(context, x, y)
            
            if self.cut_method == 'SOLID':
                self.crv.calc_box()

            return 'main'
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #if len(self.crv.screen_pts) >= 2: return 'main' #can't add more
            
            if len(self.crv.screen_pts) == 0:
                
                x,y = event.mouse_region_x, event.mouse_region_y
                if len(self.new_geom):
                    region = context.region
                    rv3d = context.region_data
                    extrude_screen_point = view3d_utils.location_3d_to_region_2d(region, rv3d, self.new_geom_point)
                    if extrude_screen_point != None:
                        R = Vector((x,y)) - extrude_screen_point
                        if R.length < 30:
                            
                            eds = [ele for ele in self.new_geom if isinstance(ele, bmesh.types.BMEdge)]
                            new_geom = bmesh.ops.extrude_edge_only(self.bme, edges = eds)
                            
                            new_verts  = [v for v in new_geom['geom'] if isinstance(v, bmesh.types.BMVert)]
                            
                            self.extrude_verts = new_verts
                            for v in self.extrude_verts:
                                self.extrude_origins[v] = v.co.copy()
                                
                                
                            return 'extend'
                
                context.window.cursor_modal_set('KNIFE')
            
                help_txt = "Left Click again to place end of cut"
                self.help_box.raw_text = help_txt
                self.help_box.format_and_wrap_text()
                
            if len(self.crv.screen_pts) == 1:
                help_txt = "Left Click on side of line to delete \n click on model to limit cut to line length \n click in space to to cut infinitely."
                self.help_box.raw_text = help_txt
                self.help_box.format_and_wrap_text()
                
                
            x, y = event.mouse_region_x, event.mouse_region_y
            
            res = self.crv.click_add_point(context, x,y)
            
            if res == None and len(self.crv.screen_pts) == 2:
                print('bisecting object on 3rd click ')
                
                context.window.cursor_modal_set('WAIT')
                res2 = self.crv.ray_cast_ob(context, x, y)
                if res2:
                    filter_geom = True
                else:
                    filter_geom = False
            
                if self.cut_method == 'SOLID':
                    self.boolean_bisect_object(context, mesh_filter= True)
                    return 'finish'
                else:
                    self.bmesh_bisect_object(context, mesh_filter= filter_geom)
                    context.window.cursor_modal_set('KNIFE')
                    return 'main'

                
                #help_txt = "Left Click to start a cut, then move mouse"
                #self.help_box.raw_text = help_txt
                #self.help_box.format_and_wrap_text()
                return 'main'
            
            return 'main'
        

        
        if event.type == 'P'  and event.value == 'PRESS':
            plane = bpy.data.objects.get('Plane')
            
            if plane == None: return 'main'
            mx = self.crv.calc_matrix(context)
            plane.matrix_world = mx
            return 'main'

        #if event.type == 'K' and event.value == 'PRESS':
        #    self.bmesh_bisect_object(context)
        #    return 'main'
            
               
        if event.type == 'RET' and event.value == 'PRESS':
            self.finish(context)
            return 'finish'
            
        elif event.type == 'ESC' and event.value == 'PRESS':
            #need to return 'finish' to get the undo, since our 'cancel' function does not "reset" the bmesh data
            return 'finish' 

        return 'main'
    
        
    def modal(self, context, event):
        context.area.tag_redraw()
        
        FSM = {}    
        FSM['main']    = self.modal_main
        FSM['nav']     = self.modal_nav
        FSM['extend'] = self.modal_extend
        
        nmode = FSM[self.mode](context, event)
        
        if nmode == 'nav': 
            return {'PASS_THROUGH'}
        
        if nmode in {'finish','cancel'}:
            #clean up callbacks
            self.bme.free()
            context.window.cursor_modal_restore()
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'} if nmode == 'finish' else {'CANCELLED'}
        
        if nmode: self.mode = nmode
        
        return {'RUNNING_MODAL'}

    def invoke(self,context, event):
         
        
        Model = context.object
        
               
        for ob in bpy.data.objects:
            if ob.name == 'Plane': continue
            ob.select = False
            ob.hide = True
        Model.select = True
        Model.hide = False
        
        #bpy.ops.view3d.view_selected()
        self.crv = LineDrawer(context,snap_type ='OBJECT', snap_object = Model)
        context.space_data.show_manipulator = False
        context.space_data.transform_manipulators = {'TRANSLATE'}
        v3d = bpy.context.space_data
        v3d.pivot_point = 'MEDIAN_POINT'
        
        
        #TODO, tweak the modifier as needed
        #help_txt = "Interactive Plane Cutting\n\nLeft click, move the mouse, and left click again.\nThen place your mouse on the side of the line to remove. \n Click on the model to isolate the cut to the line limits.\nClick off the model to cut beyond the end-points of the line."
        help_txt_open = "INTERACTIVE PLANE CUTTING OPEN\n\n-  LeftClick and move mouse to define a line across your model \n-  The line will stick to your mouse until you Left Click again\n-  LeftClick a 3rd time on the side of the line to be cut\n-  If you click on the model, the cut will be limited to the edges of the line \n-  If you click off of the model, the cut will extend into space\n\n\nEXTRUDING\n\n-  A dot will appear next to the most recent cut, you can click and move your mouse to extrude the new cut edge.\n-  LeftClick to confirm the position of the extrusion"
        help_txt_solid = "INTERACTIVE PLANE CUTTING SOLID\n\n-  LeftClick and move mouse to define a line across your model \n-  The line will stick to your mouse until you Left Click again\n-  A blue preview of the region to be deleted will present itself, everything behind the blue preview will be removed from the model\n-  LeftClick a 3rd time to confirm and the operator will end"
        
        if self.cut_method == 'SOLID':
            help_txt = help_txt_solid
        else:
            help_txt = help_txt_open
        self.help_box = TextBox(context,500,500,300,200,10,20,help_txt)
        self.help_box.snap_to_corner(context, corner = [1,1])
        self.mode = 'main'
        self._handle = bpy.types.SpaceView3D.draw_handler_add(plane_cut_draw_callback, (self, context), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self) 
        
        self.bme= bmesh.new()
        self.bme.from_mesh(Model.data)
        self.ob = Model
        self.cursor_updated = True
        
        
        self.new_geom = []
        self.new_geom_point = Vector((0,0,0))
        self.new_geom_draw_points = []
        
        self.extrude_verts = []
        self.extrude_origins = dict()
        self.extrude_geom_draw_points = []
        
        
        
        self.new_geom_color = (.8, .8, .1, 1)
        self.new_geom_point_color = (.1, .1, .8, 1)
        
        return {'RUNNING_MODAL'}

    def bmesh_bisect_object(self, context, mesh_filter = True):
        
        if len(self.crv.screen_pts) != 2: return
        
        mx_ob = self.ob.matrix_world
        imx_ob = mx_ob.inverted()
        imx_ob_no = imx_ob.to_3x3()  #assumes no  scaling
        
        mx_cut = self.crv.calc_matrix(context)
        imx_cut = mx_cut.inverted()

        #cut location in oject local coords        
        l_cut = imx_ob * mx_cut.to_translation()
        
        
        #need the local direction of the normal
        #first, get the world direction
        mx_no_cut = imx_cut.transposed().to_3x3()
        #then transform it to the local space
        no_cut = imx_ob_no * mx_no_cut.to_3x3() * Vector((0,0,1))
        
        
        if mesh_filter:
            
            print('filtering verts')
            
            local_x = imx_ob_no * mx_no_cut.to_3x3() * Vector((1,0,0))
            local_x.normalize()
            
            w0, w1, world_y = self.crv.calc_line_limits(context)
            
            L = w1 - w0
            print(L.length)
            
            
            filter_verts = []
            for v in self.bme.verts:
                
                v_prime = v.co - l_cut
                vx = v_prime.dot(local_x)
                
                if abs(vx) < .5 * L.length:
                    filter_verts += [v]
            
            filter_edges = set()
            filter_faces = set()
            
            for v in filter_verts:
                filter_edges.update(v.link_edges[:])
                filter_faces.update(v.link_faces[:])    
            
            
            cut_geom = filter_verts + list(filter_edges) + list(filter_faces)
        else:
            cut_geom = self.bme.verts[:] + self.bme.edges[:] +self.bme.faces[:]
            
        gdict = bmesh.ops.bisect_plane(self.bme, geom=cut_geom,
                                     dist=0.000001, 
                                     plane_co = l_cut,
                                     plane_no=no_cut, 
                                     use_snap_center=False, 
                                     clear_outer=True, 
                                     clear_inner=False)
        
        
        new_stuff = gdict['geom_cut']
        new_vs = [ele for ele in new_stuff if isinstance(ele, bmesh.types.BMVert)]
        new_edges = [ele for ele in new_stuff if isinstance(ele, bmesh.types.BMEdge)]
        
        self.bme.verts.ensure_lookup_table()
        self.bme.edges.ensure_lookup_table()
        self.bme.faces.ensure_lookup_table()
        
        self.new_geom = new_vs + new_edges
        self.new_geom_point = get_com_bme(self.bme, [v.index for v in new_vs], self.ob.matrix_world) + 5 * mx_no_cut.to_3x3()*Vector((0,0,1))
        self.new_geom_draw_points = [self.ob.matrix_world * v.co for v in new_vs]
        
        self.bme.to_mesh(self.ob.data)
        self.ob.data.update()
        self.crv.screen_pts = [] #reset
        self.crv.selected = -1
        return True
    
    def boolean_bisect_object(self, context, mesh_filter = True):
        
        mx_ob = self.ob.matrix_world
        imx_ob = mx_ob.inverted()
        imx_ob_no = imx_ob.to_3x3()  #assumes no  scaling
        
        mx_cut = self.crv.calc_matrix(context, depth = 'BOUNDS')
        imx_cut = mx_cut.inverted()
        
        #need the local direction of the normal
        #first, get the world direction
        mx_no_cut = imx_cut.transposed().to_3x3()
        #then transform it to the local space
        no_cut = imx_ob_no * mx_no_cut.to_3x3() * Vector((0,0,1))
        
        
        bbox = self.ob.bound_box[:]
        bbox_vs = []
        for v in bbox:
            a = Vector(v)
            bbox_vs += [self.ob.matrix_world * a]
        
        v_max_x= max(bbox_vs, key = lambda x: x[0])
        v_min_x = min(bbox_vs, key = lambda x: x[0])
        v_max_y= max(bbox_vs, key = lambda x: x[1])
        v_min_y = min(bbox_vs, key = lambda x: x[1])
        v_max_z= max(bbox_vs, key = lambda x: x[2])
        v_min_z = min(bbox_vs, key = lambda x: x[2])
        
        diag_xyz = (((v_max_x - v_min_x)[0])**2 + ((v_max_y - v_min_y)[1])**2+((v_max_z - v_min_z)[1])**2)**.5
        
        cut_plane = bpy.data.meshes.new('Plane Cut')
        cut_ob = bpy.data.objects.new('Plane Cut', cut_plane)
        context.scene.objects.link(cut_ob)
        cut_ob.draw_type = 'WIRE'
        
        if mesh_filter:
            
            print('filtering verts')
            
            local_x = imx_ob_no * mx_no_cut.to_3x3() * Vector((1,0,0))
            local_x.normalize()
            
            w0, w1, world_y = self.crv.calc_line_limits(context)
            
            L = w1 - w0
            print(L.length)
            
            Lz = world_y.length
            
            cube_bme = bmesh.new()
            bmesh.ops.create_cube(cube_bme, size = 1, matrix = Matrix.Identity(4))
            bmesh.ops.subdivide_edges(cube_bme, edges = cube_bme.edges[:], cuts = 50, use_grid_fill = True)
            cube_bme.to_mesh(cut_plane)
            
            T = Matrix.Translation(.5 * world_y)
            cut_ob.matrix_world = T * mx_cut
            cut_ob.scale[1] = diag_xyz
            cut_ob.scale[0] = L.length
            cut_ob.scale[2] = Lz
            cut_ob.hide = True
            
        else:    
            
            
            grid_bme = bmesh.new()
            bmesh.ops.create_grid(grid_bme, x_segments = 150, y_segments = 150, size = diag_xyz)
            for f in grid_bme.faces:
                f.normal_flip()
            
            grid_bme.to_mesh(cut_plane)
            
            mx_cut = self.crv.calc_matrix(context)
            cut_ob.matrix_world = mx_cut
            
            cut_ob.hide = True
        
        mod = self.ob.modifiers.new('Plane Cut', type = 'BOOLEAN')
        mod.operation = 'DIFFERENCE'
        mod.object = cut_ob
        
        self.crv.screen_pts = [] #reset
        self.crv.selected = -1
        return None
    
        
    def finish(self, context):
        #settings = get_settings()
        #apply all modifiers
        context.window.cursor_modal_restore()
        tracking.trackUsage("D3Splint:PlaneCutRaw",None)
class D3SPLINT_OT_splint_manual_auto_surface(bpy.types.Operator):
    """Help make a nice flat plane"""
    bl_idname = "d3splint.splint_manual_flat_plane"
    bl_label = "Define Occlusal Contacts"
    bl_options = {'REGISTER', 'UNDO'}
    
    @classmethod
    def poll(cls,context):
        return True
    
    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_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 == 'LEFTMOUSE' and event.value == 'PRESS':
            
            x, y = event.mouse_region_x, event.mouse_region_y
            self.crv.click_add_point(context, x,y, label = None)
            
            return 'main'
        
        #TODO, right click to delete misfires
        if event.type == 'DEL' and event.value == 'PRESS':
            self.crv.click_delete_point()
            return 'main'
            
        if event.type == 'RET' and event.value == 'PRESS':
            self.finish(context)
            return 'finish'
            
        elif event.type == 'ESC' and event.value == 'PRESS':
            return 'cancel' 

        return 'main'
    
        
    def modal(self, context, event):
        context.area.tag_redraw()
        
        FSM = {}    
        FSM['main']    = 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'}:
            #context.space_data.show_manipulator = True
            
            if nmode == 'finish':
                context.space_data.transform_manipulators = {'TRANSLATE', 'ROTATE'}
            else:
                context.space_data.transform_manipulators = {'TRANSLATE'}
            #clean up callbacks
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'} if nmode == 'finish' else {'CANCELLED'}
        
        if nmode: self.mode = nmode
        
        return {'RUNNING_MODAL'}

    def invoke(self,context, event):
        n = context.scene.odc_splint_index
        self.splint = context.scene.odc_splints[n]    
        
        if self.splint.jaw_type == 'MANDIBLE':
            model = self.splint.get_maxilla()
        else:
            model = self.splint.get_mandible()
        
        
        if model == '' or model not in bpy.data.objects:
            self.report({'ERROR'}, "Need to mark the Upper and Lower model first!")
            return {'CANCELLED'}
            
        
        Model = bpy.data.objects[model]
            
        for ob in bpy.data.objects:
            ob.select = False
            
            if ob != Model:
                ob.hide = True
        Model.select = True
        Model.hide = False
        context.scene.objects.active = Model
        
        if self.splint.jaw_type == 'MAXILLA':
            bpy.ops.view3d.viewnumpad(type = 'TOP')
        
        else:
            bpy.ops.view3d.viewnumpad(type = 'BOTTOM')
        
        bpy.ops.view3d.view_selected()
        self.crv = PointPicker(context,snap_type ='OBJECT', snap_object = Model)
        context.space_data.show_manipulator = False
        context.space_data.transform_manipulators = {'TRANSLATE'}
        v3d = bpy.context.space_data
        v3d.pivot_point = 'MEDIAN_POINT'
        
        
        #TODO, tweak the modifier as needed
        help_txt = "Designate Posterior Contacts\n LeftClick on posterior cusp tips that will have light contact in CR/MIP bite \n A smooth surface will be generated between all ponts \n This surface will then be used to slice off the posterior rim"
        self.help_box = TextBox(context,500,500,300,200,10,20,help_txt)
        self.help_box.snap_to_corner(context, corner = [1,1])
        self.mode = 'main'
        self._handle = bpy.types.SpaceView3D.draw_handler_add(landmarks_draw_callback, (self, context), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self) 
        return {'RUNNING_MODAL'}

    def finish(self, context):
        #ray cast the entire grid into
        
        if 'Posterior Plane' in bpy.data.objects:
            Plane = bpy.data.objects['Posterior Plane']
            Plane.hide = False
                
        else:
            me = bpy.data.meshes.new('Posterior Plane')
            Plane = bpy.data.objects.new('Posterior Plane', me)
            context.scene.objects.link(Plane)
        
        
        pbme = bmesh.new()
        pbme.verts.ensure_lookup_table()
        pbme.edges.ensure_lookup_table()
        pbme.faces.ensure_lookup_table()
        bmesh.ops.create_grid(pbme, x_segments = 200, y_segments = 200, size = 39.9)
        pbme.to_mesh(Plane.data)
        
        pt, pno = calculate_plane(self.crv.b_pts)
        
        if self.splint.jaw_type == 'MANDIBLE':
            Zw = Vector((0,0,-1))
            Xw = Vector((1,0,0))
            Yw = Vector((0,-1,1))
            
        else:
            Zw = Vector((0,0,1))
            Xw = Vector((1,0,0))
            Yw = Vector((0,1,0))
            
        Z = pno
        Z.normalize()
        
        if Zw.dot(Z) < 0:
            Z *= -1
            
        Y = Z.cross(Xw)
        X = Y.cross(Z)
            
        
        R = Matrix.Identity(3)  #make the columns of matrix U, V, W
        R[0][0], R[0][1], R[0][2]  = X[0] ,Y[0],  Z[0]
        R[1][0], R[1][1], R[1][2]  = X[1], Y[1],  Z[1]
        R[2][0] ,R[2][1], R[2][2]  = X[2], Y[2],  Z[2]
        
        R = R.to_4x4()
        T = Matrix.Translation(pt - 5 * Z)
        
        Plane.matrix_world = T * R
    
        pmx = Plane.matrix_world
        ipmx = pmx.inverted()
        
        bme_pln = bmesh.new()
        bme_pln.from_mesh(Plane.data)
        bme_pln.verts.ensure_lookup_table()
        bme_pln.edges.ensure_lookup_table()
        bme_pln.faces.ensure_lookup_table()
        bvh = BVHTree.FromBMesh(bme_pln)
        
        
        #we are going to raycast the user world coordinate points
        #into a grid, and identify all points in the grid from the local Z direction
        #Then we will store the local location of the user picked coordinate in a dictionary
        key_verts = {}
        
        for loc in self.crv.b_pts:

            res = bvh.ray_cast(ipmx * loc, -Z, 30)
            if res[0] != None:
                
                f = bme_pln.faces[res[2]]
                for v in f.verts:
                    key_verts[v] = ipmx * loc
                    v.select_set(True)
                
                continue
            
            res = bvh.ray_cast(ipmx * loc, Z, 30)
            if res[0] != None:
                
                f = bme_pln.faces[res[2]]
                for v in f.verts:
                    key_verts[v] = ipmx * loc
                    v.select_set(True)
                
                continue
        
        #bme_pln.to_mesh(Plane.data)
        #bme_pln.free()
        #return
        kdtree = KDTree(len(key_verts))
        for v in key_verts.keys():
            kdtree.insert(v.co, v.index)
        
        kdtree.balance()
        
        #raycast  the shell if we can
        raycast_shell = False
        if 'Splint Shell' in bpy.data.objects:
            shell = bpy.data.objects.get('Splint Shell')
            bvh_shell = BVHTree.FromObject(shell, context.scene)
            mx_shell = shell.matrix_world
            imx_shell = mx_shell.inverted()
            Z_shell = imx_shell.to_3x3()*Z
            raycast_shell = True
            
        
        right_side = set()
        left_side = set()
        ray_casted = set()
        
        to_delete = set()
        
        for v in bme_pln.verts:
            if v in key_verts:
                v.co[2] = key_verts[v][2]
               
                if v.co[1] > 0:
                    left_side.add(v)
                else:
                    right_side.add(v)
                continue
                
            results = kdtree.find_range(v.co, .5)
            if len(results):
                N = len(results)
                r_total = 0
                v_new = Vector((0,0,0))
                for res in results:
                    r_total += 1/res[2]
                    v_new += (1/res[2]) * key_verts[bme_pln.verts[res[1]]]
                        
                v_new *= 1/r_total
                v.co[2] = v_new[2]
                if v.co[1] > 0:
                    left_side.add(v)
                else:
                    right_side.add(v)
                continue
                        
            results = kdtree.find_range(v.co, 6)
            if len(results):
                N = len(results)
                r_total = 0
                v_new = Vector((0,0,0))
                for res in results:
                    r_total += (1/res[2])**2
                    v_new += ((1/res[2])**2) * key_verts[bme_pln.verts[res[1]]]
                        
                v_new *= 1/r_total
                v.co[2] = v_new[2]
                if v.co[1] > 0:
                    left_side.add(v)
                else:
                    right_side.add(v)
                continue
            
            loc, no, index, d = bvh_shell.ray_cast(imx_shell * pmx * v.co, Z_shell)
            if loc:
                
                ray_casted.add(v)
                results = kdtree.find_n(v.co, 4)
                N = len(results)
                r_total = 0
                v_new = Vector((0,0,0))
                for res in results:
                    r_total += (1/res[2])**2
                    v_new += ((1/res[2])**2) * key_verts[bme_pln.verts[res[1]]]
                        
                v_new *= 1/r_total
                v.co[2] = v_new[2]
                continue

        total_verts = ray_casted | left_side | right_side
        
        ant_left = max(left_side, key = lambda x: x.co[0])
        ant_right = max(right_side, key = lambda x: x.co[0])
        
        new_verts = set()
        dilation_verts = set()  
        for v in total_verts:
            for ed in v.link_edges:
                v_new = ed.other_vert(v)
                if v_new in total_verts or v_new in new_verts: 
                    continue
                else:
                    new_verts.add(v_new)
                    
        print('adding %i new verts' % len(new_verts))
        
        
        total_verts.update(new_verts)
        dilation_verts.update(new_verts)
        
        #for v in ray_casted:
        #    if v.co[1] > 0:
        #        if v.co[0] > ant_left.co[0]:
        #            to_delete.add(v)
        #    else:
        #        if v.co[0] > ant_right.co[0]:
        #            to_delete.add(v)
        
        #print('added %i ray_casted' % len(ray_casted))
        #total_verts = ray_casted | left_side | right_side
        #total_verts.difference_update(to_delete)       
        
        #new_verts = set()   
        #for v in total_verts:
        #    for ed in v.link_edges:
        #        v_new = ed.other_vert(v)
        #        if v_new in total_verts: continue
                
        #        if v_new.co[1] > 0 and v_new.co[0] < ant_left.co[0]:
        #            if v in to_delete:
        #                new_verts.add(v)
        #        if v_new.co[1] <= 0 and v_new.co[0] < ant_right.co[0]:
        #            if v in to_delete:
        #                new_verts.add(v)   
        
        #to_delete.difference_update(new_verts)
        
        #print('adding %i new verts' % len(new_verts))   
        for j in range(0,3):
            newer_verts = set()  
            for v in new_verts:
                for ed in v.link_edges:
                    v_new = ed.other_vert(v)
                    if v_new in total_verts or v_new in newer_verts:
                        continue
                     
                    newer_verts.add(v_new)
                    
            
                       
            total_verts.update(newer_verts)
            dilation_verts.update(newer_verts)
            new_verts = newer_verts
        
        to_delete = set(bme_pln.verts[:]) - total_verts
        
        #filter out anteior dilation
        for v in dilation_verts:
            
            if v.co[1] > 0 and v.co[0] > ant_left.co[0]:
                to_delete.add(v)
                continue
            if v.co[1] <= 0 and v.co[0] > ant_right.co[0]:
                to_delete.add(v)
                continue
                
             
            results = kdtree.find_n(v.co, 4)
            N = len(results)
            r_total = 0
            v_new = Vector((0,0,0))
            for res in results:
                r_total += (1/res[2])**2
                v_new += ((1/res[2])**2) * key_verts[bme_pln.verts[res[1]]]
                        
            v_new *= 1/r_total
            v.co[2] = v_new[2]
            
        #filter out anteior dilation
        for v in ray_casted:
            if v.co[1] > 0 and v.co[0] > ant_left.co[0]:
                to_delete.add(v)
                continue
            if v.co[1] <= 0 and v.co[0] > ant_right.co[0]:
                to_delete.add(v)
                continue
                            
        bmesh.ops.delete(bme_pln, geom = list(to_delete), context = 1)
        bme_pln.to_mesh(Plane.data)
        Plane.data.update()
        
        smod = Plane.modifiers.new('Smooth', type = 'SMOOTH')
        smod.iterations = 5
        smod.factor = 1
        
        self.splint.ops_string += 'Mark Posterior Cusps:'
Esempio n. 6
0
class OPENDENTAL_OT_mark_crown_margin(bpy.types.Operator):
    """Mark Margin.  Draw a line with the mouse to extrude bezier curves"""
    bl_idname = "opendental.mark_crown_margin"
    bl_label = "Mark Crown Margin"
    bl_options = {'REGISTER', 'UNDO'}
    
    @classmethod
    def poll(cls, context):
        #restoration exists and is in scene
        teeth = odcutils.tooth_selection(context)
        
        if teeth != []:#This can only happen one tooth at a time
            #tooth = teeth[0]
            #return tooth.prep_model in bpy.data.objects
            return True
        else:
            return False   
    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_main(self,context,event):
        # general navigation
        nmode = self.modal_nav(event)
        if nmode != '':
            return nmode  #stop here and tell parent modal to 'PASS_THROUGH'

        #after navigation filter, these are relevant events in this state
        if event.type == 'G' and event.value == 'PRESS':
            if self.crv.grab_initiate():
                return 'grab'
            else:
                #error, need to select a point
                return 'main'
        
        if event.type == 'MOUSEMOVE':
            self.crv.hover(context, event.mouse_region_x, event.mouse_region_y)    
            return 'main'
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            x, y = event.mouse_region_x, event.mouse_region_y
            self.crv.click_add_point(context, x,y)
            return 'main'
        
        if event.type == 'RIGHTMOUSE' and event.value == 'PRESS':
            self.crv.click_delete_point(mode = 'mouse')
            return 'main'
        
        if event.type == 'X' and event.value == 'PRESS':
            self.crv.delete_selected(mode = 'selected')
            return 'main'
        
        if event.type == 'S' and event.value == 'PRESS' and self.margin_manager:
            self.margin_manager.prepare_slice()
            return 'slice'
            
        if event.type == 'RET' and event.value == 'PRESS':
            return 'finish'
            
        elif event.type == 'ESC' and event.value == 'PRESS':
            del_obj = self.crv.crv_obj
            context.scene.objects.unlink(del_obj)
            bpy.data.objects.remove(del_obj)
            self.tooth.margin = ''
            return 'cancel' 

        return 'main'
    
    def modal_grab(self,context,event):
        # no navigation in grab mode
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.crv.grab_confirm()
            return 'main'
        
        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.crv.grab_cancel()
            return 'main'
        
        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.crv.grab_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
            return 'grab'
    
    def modal_slice(self,context,event):
        # no navigation in grab mode
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.margin_manager.slice_confirm()
            return 'main'
        
        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.margin_manager.slice_cancel()
            return 'main'
        
        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.margin_manager.slice_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
            return 'slice'
         
    def modal(self, context, event):
        context.area.tag_redraw()
        
        FSM = {}    
        FSM['main']    = self.modal_main
        FSM['grab']    = self.modal_grab
        FSM['slice']   = self.modal_slice
        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: self.mode = nmode
        
        return {'RUNNING_MODAL'}
    
    def invoke(self, context, event):
        layers_copy = [layer for layer in context.scene.layers]
        context.scene.layers[0] = True
        
        tooth = odcutils.tooth_selection(context)[0]
        self.tooth = tooth
        sce=bpy.context.scene
        a = tooth.name
        prep = tooth.prep_model
        margin = str(a + "_Margin")
        self.crv = None
        self.margin_manager = None
        if margin in bpy.data.objects:
            self.report({'WARNING'}, "you have already made a margin for this tooth, hit esc and then undo if you didn't want to replace it")
        
        if prep and prep in bpy.data.objects:
            Prep = bpy.data.objects[prep]
            Prep.hide = False
            L = Prep.location
            ###Keep a list of unhidden objects
            for o in sce.objects:
                if o.name != prep and not o.hide:
                    o.hide = True
                    
            self.crv = CurveDataManager(context,snap_type ='OBJECT', snap_object = Prep, shrink_mod = True, name = margin)
            
            self.margin_manager = MarginSlicer(tooth, context, self.crv)
        else:
            self.report({'WARNING'}, "There is no prep for this tooth, your margin will snap to the master model or all objects in scene")
        
        master=sce.odc_props.master
        if master and master in bpy.data.objects:
            Master = bpy.data.objects[master]
            if prep not in bpy.data.objects:
                self.crv = CurveDataManager(context,snap_type ='OBJECT', snap_object = Master, shrink_mod = True, name = margin)
                self.margin_manager = MarginSlicer(tooth, context, self.crv)
        else:
            self.report({'WARNING'}, "No master model...there are risks!")
        
        if not self.crv:
            self.crv = CurveDataManager(context,snap_type ='SCENE', snap_object = None, shrink_mod = False, name = margin)
        
        tooth.margin = self.crv.crv_obj.name
        
        help_txt = "DRAW MARGIN OUTLINE\n\nLeft Click on model to draw outline \nRight click to delete a point \nLeft Click last point to make loop \n G to grab  \n S to show slice \n ENTER to confirm \n ESC to cancel"
        self.help_box = TextBox(context,500,500,300,200,10,20,help_txt)
        self.help_box.snap_to_corner(context, corner = [1,1])
        self.mode = 'main'
        self._handle = bpy.types.SpaceView3D.draw_handler_add(icrnmgn_draw_callback, (self, context), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}
Esempio n. 7
0
class D3SPLINT_OT_stencil_text(bpy.types.Operator):
    """Click and draw a line to place text on the model"""
    bl_idname = "d3splint.stencil_text"
    bl_label = "Stencil Text"
    bl_options = {'REGISTER', 'UNDO'}
    
    
    @classmethod
    def poll(cls,context):
        if context.object == None: return False
        if context.object.type != 'MESH': return False
        
        return True
    
    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_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 == 'MOUSEMOVE':
            
            x, y = event.mouse_region_x, event.mouse_region_y
            self.crv.hover(context, x, y)
            if len(self.crv.screen_pts) != 2:
                self.crv.calc_text_values()
            return 'main'
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #if len(self.crv.screen_pts) >= 2: return 'main' #can't add more
            
            if len(self.crv.screen_pts) == 0:
                context.window.cursor_modal_set('CROSSHAIR')
            
                #help_txt = "Left Click again to place end of line"
                #self.help_box.raw_text = help_txt
                #self.help_box.format_and_wrap_text()
                
            #if len(self.crv.screen_pts) == 1:
                #help_txt = "Left Click to end the text"
                #self.help_box.raw_text = help_txt
                #self.help_box.format_and_wrap_text()
                
                
            x, y = event.mouse_region_x, event.mouse_region_y
            
            res = self.crv.click_add_point(context, x,y)
            
            
            return 'main'
        
        if event.type == 'DEL' and event.value == 'PRESS':
            self.crv.click_delete_point()
            return 'main'
        
        if event.type == 'P'  and event.value == 'PRESS':
            self.create_and_project_text(context)
            return 'main'            
        
        if event.type == 'RIGHTMOUSE'  and event.value == 'PRESS':
            
            x, y = event.mouse_region_x, event.mouse_region_y
            
            v3d = context.space_data
            rv3d = v3d.region_3d
            rot = rv3d.view_rotation
            
            X = rot * Vector((1,0,0))
            Y = rot * Vector((0,1,0))
            Z = rot * Vector((0,0,1))
            
            loc, no = self.crv.ray_cast_pt(context, (x,y))
            no_mx = self.crv.snap_ob.matrix_world.inverted().transposed().to_3x3()
            world_no = no_mx * no
            
            
            
            world_no_aligned = world_no - world_no.dot(X) * X
            world_no_aligned.normalize()
            
            angle = world_no_aligned.angle(Z)
            
            if world_no.dot(Y) > 0:
                angle = -1 * angle
            R_mx = Matrix.Rotation(angle, 3, X)
            R_quat = R_mx.to_quaternion()
            rv3d.view_rotation = R_quat * rot
            
            return 'main'
               
        if event.type == 'LEFT_ARROW' and event.value == 'PRESS':
            print('reset old matrix')
            v3d = context.space_data
            rv3d = v3d.region_3d
            rv3d.view_rotation = self.last_view_rot
            rv3d.view_location = self.last_view_loc
            rv3d.view_matrix = self.last_view_matrix
            rv3d.view_distance = self.last_view_distance
            
            rv3d.update()
            return 'main'
        
        if event.type == 'RET' and event.value == 'PRESS':
            if len(self.crv.screen_pts) != 2:
                return 'main'
            
            if not len(self.crv.projected_points):
                self.create_and_project_text(context)
            
            self.finalize_text(context)    
            self.finish(context)
            return 'finish'
            
        elif event.type == 'ESC' and event.value == 'PRESS':
            return 'cancel' 

        return 'main'
    
        
    def modal(self, context, event):
        context.area.tag_redraw()
        
        FSM = {}    
        FSM['main']    = 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
            self.bme.free()
            context.window.cursor_modal_restore()
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'} if nmode == 'finish' else {'CANCELLED'}
        
        if nmode: self.mode = nmode
        
        return {'RUNNING_MODAL'}

    def invoke(self,context, event):
         
        
        label_message = get_settings().d3_model_label
        Model = context.object
        
               
        for ob in bpy.data.objects:
            if "D3T Label" in ob.name: continue
            ob.select = False
            ob.hide = True
        Model.select = True
        Model.hide = False
        
        #bpy.ops.view3d.view_selected()
        self.crv = TextLineDrawer(context,snap_type ='OBJECT', snap_object = Model, msg = label_message)
        
        context.space_data.fx_settings.use_ssao = True
        
        
        #TODO, tweak the modifier as needed
        help_txt = "INTERACTIVE LABEL STENCIL\n\n-  LeftClick and move mouse to define a line across your model \n-  The line will stick to your mouse until you Left Click again\n-  A preview of the text label will follow your line\n  -press 'ENTER' to project the text onto your model and finish the operator.\n\nADVANCED USAGE\n\n-RightMouse in the middle of the label to snap your view perpendicular to the model surface, you may need to adjust the position slightly\n-  You can press 'P' to project the  text onto the object without leaving the operator.  You can then alter your view to inspect the text projection.\n-  LEFT_ARROW key to snap back to the original view, you can then modify your viewing angle and press 'P' again.  When satisfied, press 'ENTER' to finish."
        
        self.help_box = TextBox(context,500,500,300,200,10,20,help_txt)
        self.help_box.snap_to_corner(context, corner = [1,1])
        
        
        
        self.bme= bmesh.new()
        self.bme.from_mesh(Model.data)
        self.ob = Model
        self.cursor_updated = True
        
        #get new text data and object in the scene
        self.txt_crv = bpy.data.curves.new("D3T Label", type = 'FONT')
        self.txt_crv_ob = bpy.data.objects.new("D3T Label", self.txt_crv)
        context.scene.objects.link(self.txt_crv_ob)
        context.scene.update()
        
        self.txt_crv_ob.hide = True
            
        self.txt_me_data = self.txt_crv_ob.to_mesh(context.scene, apply_modifiers = True, settings = 'PREVIEW')    
        self.txt_me_ob = bpy.data.objects.new("D3T Label Mesh", self.txt_me_data)
        context.scene.objects.link(self.txt_me_ob)
          
        self.txt_crv.align_x = 'LEFT'
        self.txt_crv.align_y = 'BOTTOM'    
        self.txt_crv.body = label_message  #TODO hook up to property
        
        
        context.space_data.show_manipulator = False
        
        self.mode = 'main'
        self._handle = bpy.types.SpaceView3D.draw_handler_add(stencil_text_callback, (self, context), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self) 
        
        v3d = context.space_data
        rv3d = v3d.region_3d
        
        self.last_view_rot = rv3d.view_rotation
        self.last_view_loc = rv3d.view_location
        self.last_view_matrix = rv3d.view_matrix.copy()
        self.last_view_distance = rv3d.view_distance
        
        return {'RUNNING_MODAL'}

    def create_and_project_text(self, context):
        
        self.crv.project_line(context, res = 20)
        
        if len(self.crv.projected_points) == 0:
            return
        
        v3d = context.space_data
        rv3d = v3d.region_3d
        self.last_view_rot = rv3d.view_rotation
        self.last_view_loc = rv3d.view_location
        self.last_view_matrix = rv3d.view_matrix.copy()
        self.last_view_distance = rv3d.view_distance
            
        txt_ob = self.txt_crv_ob
        txt_ob.matrix_world = Matrix.Identity(4)
        
        bbox = txt_ob.bound_box[:]
        bbox_vs = []
        for v in bbox:
            bbox_vs += [Vector(v)]
        
        v_max_x= max(bbox_vs, key = lambda x: x[0])
        v_min_x = min(bbox_vs, key = lambda x: x[0])
        
        X_dim = v_max_x[0] - v_min_x[0]
        print("The text object has %f length" % X_dim)
        
        #really need a path and bezier class for this kind of stuff
        proj_path_len = 0
        s_v_map = {}
        s_v_map[0.0] = 0
        for i in range(0,19):
            seg = self.crv.projected_points[i + 1] - self.crv.projected_points[i]
            proj_path_len += seg.length
            s_v_map[proj_path_len] = i+1
        
        
        def find_path_len_v(s_len):
            '''
            Get the interpolated position along a polypath
            at a given length along the path.
            '''
            p_len = 0
            
            for i in range(0, 19):
                seg = self.crv.projected_points[i + 1] - self.crv.projected_points[i]
                p_len += seg.length
                
                if p_len > s_len:
                    delta = p_len - s_len
                    vec = seg.normalized()
                    
                    v = self.crv.projected_points[i] + delta * vec
        
                    return v, vec
            return self.crv.projected_points[i+1], seg.normalized()
        
        #place the text object on the path
        s_factor = proj_path_len/X_dim
        S = Matrix.Scale(s_factor, 4)
        loc = self.crv.projected_points[0]
        T = Matrix.Translation(loc)
        R = self.crv.calc_matrix(context)
        
        
        txt_ob.matrix_world = T * R * S
            
        me = txt_ob.to_mesh(context.scene, apply_modifiers = True, settings = 'PREVIEW')
        
        bme = bmesh.new()
        bme.from_mesh(me)
        bme.verts.ensure_lookup_table()
        bme.edges.ensure_lookup_table()
        bme.faces.ensure_lookup_table()
        
        #bullshit it doesn't work
        #print('dissolving degeneration')
        #ret = bmesh.ops.dissolve_degenerate(bme, dist = .001, edges = bme.edges[:])
        #print(ret)
        
        print('beauty faces')
        bmesh.ops.beautify_fill(bme, faces = bme.faces[:], edges = bme.edges[:])
        
        
        characters = bmesh_loose_parts(bme, selected_faces = None, max_iters = 200)
        
        #parameterize each character based on it's center of mass in the x direction
        #eg, it's length down the curve path
        
        ts = []
        path_pts = []
        for fs in characters:
            vs = set()
            com = Vector((0,0,0))
            for f in fs:
                vs.update(f.verts[:])
            for v in vs:
                com += v.co
            com *= 1/len(vs)
            
            world_com = T * R * S * com
            point_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, world_com)
            loc, no = self.crv.ray_cast_pt(context, point_2d)
            
            world_projected_com = self.crv.snap_ob.matrix_world * loc
            
            #self.crv.projected_points += [world_projected_com]
            
            world_delta = world_projected_com - world_com
            
            local_delta = (R * S).inverted().to_3x3() * world_delta
            
            
            ts += [com[0]/X_dim]
            
            path_pt, path_tan = find_path_len_v(com[0]/X_dim * proj_path_len)
            
            local_tan = (R * S).inverted().to_3x3() * path_tan
            
            angle_dif = Vector((1,0,0)).angle(local_tan)
            
            if local_tan.cross(Vector((1,0,0))).dot(Vector((0,1,0))) < 0:
                angle_dif *= -1
                
                
                
            r_prime = Matrix.Rotation(-angle_dif, 4, 'Y')
            print('The angle difference is %f' % angle_dif)
            #translate to center
            for v in vs:
                v.co -= com
                
                v.co = r_prime * v.co
                
                v.co += com + local_delta    
            
            
            

        #text mesh
        
        bme.to_mesh(me)
        self.txt_me_ob.data = me
        
        if self.txt_me_data != None:
            self.txt_me_data.user_clear()
            bpy.data.meshes.remove(self.txt_me_data)
            
        self.txt_me_data = me
        
        
        self.txt_me_ob.matrix_world = T * R * S
        bme.free()
        
        if 'Solidify' not in self.txt_me_ob.modifiers:
            mod = self.txt_me_ob.modifiers.new('Solidify',type = 'SOLIDIFY')
            mod.offset = 0
        
        else:
            mod = self.txt_me_ob.modifiers.get('Solidify')
            
        mod.thickness = .5 * 1/s_factor #TODO put as setting
        
        return True
    
    
    def finalize_text(self,context):
        
        context.scene.objects.unlink(self.txt_crv_ob)
        bpy.data.objects.remove(self.txt_crv_ob)
        bpy.data.curves.remove(self.txt_crv)
        
        
        return
        
        
            
    def finish(self, context):
        #settings = get_settings()
        context.window.cursor_modal_restore()
        tracking.trackUsage("D3Splint:StencilText",None)
Esempio n. 8
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'}
Esempio n. 9
0
class OPENDENTAL_OT_mark_crown_margin(bpy.types.Operator):
    """Mark Margin.  Draw a line with the mouse to extrude bezier curves"""
    bl_idname = "opendental.mark_crown_margin"
    bl_label = "Mark Crown Margin"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        #restoration exists and is in scene
        teeth = odcutils.tooth_selection(context)

        if teeth != []:  #This can only happen one tooth at a time
            #tooth = teeth[0]
            #return tooth.prep_model in bpy.data.objects
            return True
        else:
            return False

    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_main(self, context, event):
        # general navigation
        nmode = self.modal_nav(event)
        if nmode != '':
            return nmode  #stop here and tell parent modal to 'PASS_THROUGH'

        #after navigation filter, these are relevant events in this state
        if event.type == 'G' and event.value == 'PRESS':
            if self.crv.grab_initiate():
                return 'grab'
            else:
                #error, need to select a point
                return 'main'

        if event.type == 'MOUSEMOVE':
            self.crv.hover(context, event.mouse_region_x, event.mouse_region_y)
            return 'main'

        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            x, y = event.mouse_region_x, event.mouse_region_y
            self.crv.click_add_point(context, x, y)
            return 'main'

        if event.type == 'RIGHTMOUSE' and event.value == 'PRESS':
            self.crv.click_delete_point(mode='mouse')
            return 'main'

        if event.type == 'X' and event.value == 'PRESS':
            self.crv.delete_selected(mode='selected')
            return 'main'

        if event.type == 'S' and event.value == 'PRESS' and self.margin_manager:
            self.margin_manager.prepare_slice()
            return 'slice'

        if event.type == 'RET' and event.value == 'PRESS':
            return 'finish'

        elif event.type == 'ESC' and event.value == 'PRESS':
            del_obj = self.crv.crv_obj
            context.scene.objects.unlink(del_obj)
            bpy.data.objects.remove(del_obj)
            self.tooth.margin = ''
            return 'cancel'

        return 'main'

    def modal_grab(self, context, event):
        # no navigation in grab mode

        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.crv.grab_confirm()
            return 'main'

        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.crv.grab_cancel()
            return 'main'

        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.crv.grab_mouse_move(context, event.mouse_region_x,
                                     event.mouse_region_y)
            return 'grab'

    def modal_slice(self, context, event):
        # no navigation in grab mode

        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.margin_manager.slice_confirm()
            return 'main'

        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.margin_manager.slice_cancel()
            return 'main'

        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.margin_manager.slice_mouse_move(context, event.mouse_region_x,
                                                 event.mouse_region_y)
            return 'slice'

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

        FSM = {}
        FSM['main'] = self.modal_main
        FSM['grab'] = self.modal_grab
        FSM['slice'] = self.modal_slice
        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: self.mode = nmode

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        layers_copy = [layer for layer in context.scene.layers]
        context.scene.layers[0] = True

        tooth = odcutils.tooth_selection(context)[0]
        self.tooth = tooth
        sce = bpy.context.scene
        a = tooth.name
        prep = tooth.prep_model
        margin = str(a + "_Margin")
        self.crv = None
        self.margin_manager = None
        if margin in bpy.data.objects:
            self.report({
                'WARNING'
            }, "you have already made a margin for this tooth, hit esc and then undo if you didn't want to replace it"
                        )

        if prep and prep in bpy.data.objects:
            Prep = bpy.data.objects[prep]
            Prep.hide = False
            L = Prep.location
            ###Keep a list of unhidden objects
            for o in sce.objects:
                if o.name != prep and not o.hide:
                    o.hide = True

            self.crv = CurveDataManager(context,
                                        snap_type='OBJECT',
                                        snap_object=Prep,
                                        shrink_mod=True,
                                        name=margin)

            self.margin_manager = MarginSlicer(tooth, context, self.crv)
        else:
            self.report({
                'WARNING'
            }, "There is no prep for this tooth, your margin will snap to the master model or all objects in scene"
                        )

        master = sce.odc_props.master
        if master and master in bpy.data.objects:
            Master = bpy.data.objects[master]
            if prep not in bpy.data.objects:
                self.crv = CurveDataManager(context,
                                            snap_type='OBJECT',
                                            snap_object=Master,
                                            shrink_mod=True,
                                            name=margin)
                self.margin_manager = MarginSlicer(tooth, context, self.crv)
        else:
            self.report({'WARNING'}, "No master model...there are risks!")

        if not self.crv:
            self.crv = CurveDataManager(context,
                                        snap_type='SCENE',
                                        snap_object=None,
                                        shrink_mod=False,
                                        name=margin)

        tooth.margin = self.crv.crv_obj.name

        help_txt = "DRAW MARGIN OUTLINE\n\nLeft Click on model to draw outline \nRight click to delete a point \nLeft Click last point to make loop \n G to grab  \n S to show slice \n ENTER to confirm \n ESC to cancel"
        self.help_box = TextBox(context, 500, 500, 300, 200, 10, 20, help_txt)
        self.help_box.snap_to_corner(context, corner=[1, 1])
        self.mode = 'main'
        self._handle = bpy.types.SpaceView3D.draw_handler_add(
            icrnmgn_draw_callback, (self, context), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}
Esempio n. 10
0
class OPENDENTAL_OT_mesh_trim_polyline(bpy.types.Operator):
    """Draw a line with the mouse to cut mesh into pieces"""
    bl_idname = "opendental.trim_mesh"
    bl_label = "Polyline Trim Mesh"
    bl_options = {'REGISTER', 'UNDO'}
    
    def sketch_confirm(self, context, event):
        print('sketch confirmed')
        if len(self.sketch) < 5 and self.knife.ui_type == 'DENSE_POLY':
            print('sketch too short, cant confirm')
            return
        x, y = event.mouse_region_x, event.mouse_region_y
        last_hovered = self.knife.hovered[1] #guaranteed to be a point by criteria to enter sketch mode
        self.knife.hover(context,x,y)
        print('last hovered %i' % last_hovered)
        
        sketch_3d = common_utilities.ray_cast_path(context, self.knife.cut_ob,self.sketch)
        
        if self.knife.hovered[0] == None:
            #add the points in
            if last_hovered == len(self.knife.pts) - 1:
                self.knife.pts += sketch_3d[0::5]
                print('add on to the tail')

                
            else:
                self.knife.pts = self.knife.pts[:last_hovered] + sketch_3d[0::5]
                print('snipped off and added on to the tail')
        
        else:
            print('inserted new segment')
            print('now hovered %i' % self.knife.hovered[1])
            new_pts = sketch_3d[0::5]
            if last_hovered > self.knife.hovered[1]:
                new_pts.reverse()
                self.knife.pts = self.knife.pts[:self.knife.hovered[1]] + new_pts + self.knife.pts[last_hovered:]
            else:
                self.knife.pts = self.knife.pts[:last_hovered] + new_pts + self.knife.pts[self.knife.hovered[1]:]
        self.knife.snap_poly_line()
        
    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_main(self,context,event):
        # general navigation
        nmode = self.modal_nav(event)
        if nmode != '':
            return nmode  #stop here and tell parent modal to 'PASS_THROUGH'

        #after navigation filter, these are relevant events in this state
        if event.type == 'G' and event.value == 'PRESS':
            if self.knife.grab_initiate():
                return 'grab'
            else:
                #need to select a point
                return 'main'
        
        if event.type == 'MOUSEMOVE':
            self.knife.hover(context, event.mouse_region_x, event.mouse_region_y)    
            return 'main'
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            
            x, y = event.mouse_region_x, event.mouse_region_y
            self.knife.click_add_point(context, x,y)  #takes care of selection too
            if self.knife.ui_type == 'DENSE_POLY' and self.knife.hovered[0] == 'POINT':
                self.sketch = [(x,y)]
                return 'sketch'
            return 'main'
        
        if event.type == 'RIGHTMOUSE' and event.value == 'PRESS':
            self.knife.click_delete_point(mode = 'mouse')
            return 'main'
        
        if event.type == 'X' and event.value == 'PRESS':
            self.knife.delete_selected(mode = 'selected')
            return 'main'
        
        if event.type == 'C' and event.value == 'PRESS':
            self.knife.make_cut()
            return 'main' 
        if event.type == 'D' and event.value == 'PRESS':
            print('confirm cut')
            if len(self.knife.new_cos) and len(self.knife.bad_segments) == 0 and not self.knife.split:
                print('actuall confirm cut')
                self.knife.confirm_cut_to_mesh()
                return 'main' 
            
        if event.type == 'E' and event.value == 'PRESS':     
            if self.knife.split and self.knife.face_seed and len(self.knife.ed_map):
                self.knife.split_geometry()
                return 'finish' 
            
        if event.type == 'S' and event.value == 'PRESS':
            return 'inner'
          
        if event.type == 'RET' and event.value == 'PRESS':
            self.knife.confirm_cut_to_mesh()
            return 'finish'
            
        elif event.type == 'ESC' and event.value == 'PRESS':
            return 'cancel' 

        return 'main'
    
    def modal_grab(self,context,event):
        # no navigation in grab mode
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.knife.grab_confirm()
            return 'main'
        
        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.knife.grab_cancel()
            return 'main'
        
        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.knife.grab_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
            return 'grab'
    
    def modal_sketch(self,context,event):
        if event.type == 'MOUSEMOVE':
            x, y = event.mouse_region_x, event.mouse_region_y
            if not len(self.sketch):
                return 'main'
            (lx, ly) = self.sketch[-1]
            ss0,ss1 = self.stroke_smoothing ,1-self.stroke_smoothing
            self.sketch += [(lx*ss0+x*ss1, ly*ss0+y*ss1)]
            return 'sketch'
        
        elif event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
            self.sketch_confirm(context, event)
            self.sketch = []
            return 'main'
        
    def modal_inner(self,context,event):
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            print('left click modal inner')
            x, y = event.mouse_region_x, event.mouse_region_y
            if self.knife.click_seed_select(context, x,y):
                print('seed set')
                return 'main'
            else:
                return 'inner'
        
        if event.type in {'RET', 'ESC'}:
            return 'main'
            
    def modal(self, context, event):
        context.area.tag_redraw()
        
        FSM = {}    
        FSM['main']    = self.modal_main
        FSM['sketch']  = self.modal_sketch
        FSM['grab']    = self.modal_grab
        FSM['nav']     = self.modal_nav
        FSM['inner']   = self.modal_inner
        
        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: self.mode = nmode
        
        return {'RUNNING_MODAL'}
    
    def invoke(self,context,event):
        self.mode = 'main'
        help_txt = "DRAW CUT OUTLINE\n\nLeft Click on model to draw outline outline \nRight click to delete \nLeft Click last point to close loop\n C to preview cut n\ Adjustt red segements and re-cut \n S and then click in region to split to make cut and split mesh \n ENTER to confirm"
        
        self.stroke_smoothing = .4
        self.sketch = []
        
        self.help_box = TextBox(context,500,500,300,200,10,20,help_txt)
        self.help_box.snap_to_corner(context, corner = [1,1])
        self.knife = PolyLineKnife(context,context.object)
        self._handle = bpy.types.SpaceView3D.draw_handler_add(plyknife_draw_callback, (self, context), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self)

        return {'RUNNING_MODAL'}
Esempio n. 11
0
class OPENDENTAL_OT_splint_margin(bpy.types.Operator):
    """Draw a line with the mouse to extrude bezier curves"""
    bl_idname = "opendental.initiate_splint_outline"
    bl_label = "Splint Outine"
    bl_options = {'REGISTER', 'UNDO'}
    
    @classmethod
    def poll(cls,context):
        condition_1 = context.object != None
        return condition_1
    
    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_main(self,context,event):
        # general navigation
        nmode = self.modal_nav(event)
        if nmode != '':
            return nmode  #stop here and tell parent modal to 'PASS_THROUGH'

        #after navigation filter, these are relevant events in this state
        if event.type == 'G' and event.value == 'PRESS':
            if self.crv.grab_initiate():
                return 'grab'
            else:
                #error, need to select a point
                return 'main'
        
        if event.type == 'MOUSEMOVE':
            self.crv.hover(context, event.mouse_region_x, event.mouse_region_y)    
            return 'main'
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            x, y = event.mouse_region_x, event.mouse_region_y
            self.crv.click_add_point(context, x,y)
            return 'main'
        
        if event.type == 'RIGHTMOUSE' and event.value == 'PRESS':
            self.crv.click_delete_point(mode = 'mouse')
            return 'main'
        
        if event.type == 'X' and event.value == 'PRESS':
            self.crv.delete_selected(mode = 'selected')
            return 'main'
            
        if event.type == 'RET' and event.value == 'PRESS':
            return 'finish'
            
        elif event.type == 'ESC' and event.value == 'PRESS':
            return 'cancel' 

        return 'main'
    
    def modal_grab(self,context,event):
        # no navigation in grab mode
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.crv.grab_confirm()
            return 'main'
        
        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.crv.grab_cancel()
            return 'main'
        
        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.crv.grab_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
            return 'grab'
        
    def modal(self, context, event):
        context.area.tag_redraw()
        
        FSM = {}    
        FSM['main']    = self.modal_main
        FSM['grab']    = self.modal_grab
        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: self.mode = nmode
        
        return {'RUNNING_MODAL'}


    def invoke(self,context, event):
        
        if len(context.scene.odc_splints) == 0 and context.object:
            #This is a hidden cheat, allowing quick starting of a splint
            my_item = context.scene.odc_splints.add()        
            my_item.name = context.object.name + '_Splint'
            my_item.model = context.object.name
            self.report({'WARNING'}, "Assumed you wanted to start a new splint on the active object!  If not, then UNDO")
            
            for ob in bpy.data.objects:
                ob.select = False
                
            context.object.select = True
            bpy.ops.view3d.view_selected()
            self.splint = my_item
            
        else:
            self.splint = odcutils.splint_selction(context)[0]
            
        self.crv = None
        margin = self.splint.name + '_outline'
        
        if (self.splint.model == '' or self.splint.model not in bpy.data.objects) and not context.object:
            self.report({'WARNING'}, "There is no model, the curve will snap to anything in the scene!")
            self.crv = CurveDataManager(context,snap_type ='SCENE', snap_object = None, shrink_mod = False, name = margin)
            
        elif self.splint.model != '' and self.splint.model in bpy.data.objects:
            Model = bpy.data.objects[self.splint.model]
            for ob in bpy.data.objects:
                ob.select = False
            Model.select = True
            Model.hide = False
            context.scene.objects.active = Model
            bpy.ops.view3d.view_selected()
            self.crv = CurveDataManager(context,snap_type ='OBJECT', snap_object = Model, shrink_mod = True, name = margin)
            self.crv.crv_obj.parent = Model
            
        if self.crv == None:
            self.report({'ERROR'}, "Not sure what you want, you may need to select an object or plan a splint")
            return {'CANCELLED'}
        
        self.splint.margin = self.crv.crv_obj.name
        
        if 'Wrap' in self.crv.crv_obj.modifiers:
            mod = self.crv.crv_obj.modifiers['Wrap']
            mod.offset = .75
            mod.use_keep_above_surface = True
        
            mod = self.crv.crv_obj.modifiers.new('Smooth','SMOOTH')
            mod.iterations = 10
            
        #TODO, tweak the modifier as needed
        help_txt = "DRAW MARGIN OUTLINE\n\nLeft Click on model to draw outline \nRight click to delete a point \nLeft Click last point to make loop \n G to grab  \n ENTER to confirm \n ESC to cancel"
        self.help_box = TextBox(context,500,500,300,200,10,20,help_txt)
        self.help_box.snap_to_corner(context, corner = [1,1])
        self.mode = 'main'
        self._handle = bpy.types.SpaceView3D.draw_handler_add(ispltmgn_draw_callback, (self, context), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self) 
        return {'RUNNING_MODAL'}
Esempio n. 12
0
class OPENDENTAL_OT_arch_curve(bpy.types.Operator):
    """Draw a line with the mouse to extrude bezier curves"""
    bl_idname = "opendental.draw_arch_curve"
    bl_label = "Arch Curve"
    bl_options = {'REGISTER', 'UNDO'}
    
    @classmethod
    def poll(cls,context):
        return True
    
    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_main(self,context,event):
        # general navigation
        nmode = self.modal_nav(event)
        if nmode != '':
            return nmode  #stop here and tell parent modal to 'PASS_THROUGH'

        #after navigation filter, these are relevant events in this state
        if event.type == 'G' and event.value == 'PRESS':
            if self.crv.grab_initiate():
                return 'grab'
            else:
                #error, need to select a point
                return 'main'
        
        if event.type == 'MOUSEMOVE':
            self.crv.hover(context, event.mouse_region_x, event.mouse_region_y)    
            return 'main'
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            x, y = event.mouse_region_x, event.mouse_region_y
            self.crv.click_add_point(context, x,y)
            return 'main'
        
        if event.type == 'RIGHTMOUSE' and event.value == 'PRESS':
            self.crv.click_delete_point(mode = 'mouse')
            return 'main'
        
        if event.type == 'X' and event.value == 'PRESS':
            self.crv.delete_selected(mode = 'selected')
            return 'main'
            
        if event.type == 'RET' and event.value == 'PRESS':
            return 'finish'
            
        elif event.type == 'ESC' and event.value == 'PRESS':
            return 'cancel' 

        return 'main'
    
    def modal_grab(self,context,event):
        # no navigation in grab mode
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.crv.grab_confirm()
            return 'main'
        
        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.crv.grab_cancel()
            return 'main'
        
        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.crv.grab_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
            return 'grab'
        
    def modal(self, context, event):
        context.area.tag_redraw()
        
        FSM = {}    
        FSM['main']    = self.modal_main
        FSM['grab']    = self.modal_grab
        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: self.mode = nmode
        
        return {'RUNNING_MODAL'}

    def invoke(self,context, event):
        
        if context.object:
            ob = context.object
            L = odcutils.get_bbox_center(ob, world=True)
            context.scene.cursor_location = L
        
        self.crv = CurveDataManager(context,snap_type ='SCENE', snap_object = None, shrink_mod = False, name = 'Plan Curve')
         
        #TODO, tweak the modifier as needed
        help_txt = "DRAW ARCH OUTLINE\n\nLeft Click in scene to draw a curve \nPoints will snap to objects under mouse \nNot clicking on object will make points at same depth as 3D cursor \n Right click to delete a point n\ G to grab  \n ENTER to confirm \n ESC to cancel"
        self.help_box = TextBox(context,500,500,300,200,10,20,help_txt)
        self.help_box.snap_to_corner(context, corner = [1,1])
        self.mode = 'main'
        self._handle = bpy.types.SpaceView3D.draw_handler_add(arch_crv_draw_callback, (self, context), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self) 
        return {'RUNNING_MODAL'}
Esempio n. 13
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'}
Esempio n. 14
0
class D3SPLINT_OT_live_insertion_axis(bpy.types.Operator):
    """Pick Insertin Axis by viewing model from occlusal"""
    bl_idname = "d3splint.live_insertion_axis"
    bl_label = "Pick Insertion Axis"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return True

    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_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 {'NUMPAD_1', 'NUMPAD_3', "NUMPAD_7"
                          } and event.value == 'PRESS':
            return 'nav'

        if "ARROW" in event.type and event.value == 'PRESS':
            self.rotate_arrow(context, event)
            return 'main'
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            x, y = event.mouse_region_x, event.mouse_region_y
            res = self.click_model(context, x, y)
            if res:
                self.preview_direction(context)
            return 'main'

        if event.type == 'P' and event.value == 'PRESS':
            self.preview_direction(context)
            return 'main'

        if event.type == 'RET' and event.value == 'PRESS':
            self.finish(context)
            return 'finish'

        elif event.type == 'ESC' and event.value == 'PRESS':
            return 'cancel'

        return 'main'

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

        FSM = {}
        FSM['main'] = 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'}:
            #context.space_data.show_manipulator = True

            #if nmode == 'finish':
            #    context.space_data.transform_manipulators = {'TRANSLATE', 'ROTATE'}
            #else:
            #    context.space_data.transform_manipulators = {'TRANSLATE'}
            #clean up callbacks
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'} if nmode == 'finish' else {'CANCELLED'}

        if nmode: self.mode = nmode

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):

        if len(context.scene.odc_splints) == 0:
            self.report({'ERROR'},
                        "Need to mark splint and opposing models first")
            return {'CANCELLED'}
        n = context.scene.odc_splint_index
        self.splint = context.scene.odc_splints[n]
        self.previewed = False

        max_model = self.splint.get_maxilla()
        mand_model = self.splint.get_mandible()

        if self.splint.jaw_type == 'MANDIBLE':
            Model = bpy.data.objects.get(mand_model)

        else:
            Model = bpy.data.objects.get(max_model)

        for ob in bpy.data.objects:
            ob.select = False
            ob.hide = True
        Model.select = True
        Model.hide = False
        context.scene.objects.active = Model

        add_volcolor_material_to_obj(Model, 'Undercut')

        #view a presumptive occlusal axis
        if self.splint.jaw_type == 'MAXILLA':
            bpy.ops.view3d.viewnumpad(type='BOTTOM')
        else:
            bpy.ops.view3d.viewnumpad(type='TOP')

        #add in a insertin axis direction
        loc = odcutils.get_bbox_center(Model, world=True)
        view = context.space_data.region_3d.view_rotation * Vector((0, 0, 1))
        mxT = Matrix.Translation(loc)
        mxR = context.space_data.region_3d.view_rotation.to_matrix().to_4x4()

        if "Insertion Axis" in bpy.data.objects:
            ins_ob = bpy.data.objects.get('Insertion Axis')

        else:
            ins_ob = bpy.data.objects.new('Insertion Axis', None)
            ins_ob.empty_draw_type = 'SINGLE_ARROW'
            ins_ob.empty_draw_size = 20
            context.scene.objects.link(ins_ob)

        ins_ob.hide = False
        ins_ob.parent = Model
        ins_ob.matrix_world = mxT * mxR

        self.ins_ob = ins_ob

        #get bmesh data to process
        self.bme = bmesh.new()
        self.bme.from_mesh(Model.data)
        self.bme.verts.ensure_lookup_table()
        self.bme.edges.ensure_lookup_table()
        self.bme.faces.ensure_lookup_table()

        self.model = Model

        bpy.ops.view3d.view_selected()
        context.space_data.viewport_shade = 'SOLID'
        context.space_data.show_textured_solid = True

        #TODO, tweak the modifier as needed
        help_txt = "Pick Insertion Axis\n\n-  Position your viewing direction looking onto the model\n-  LEFT CLICK on the model\n-  You can then rotate and pan your view to assess the undercuts.  This process can be repeated until the desired insertion axis is chosen.\n\nADVANCED USE\n\n-  Use LEFT_ARROW, RIGHT_ARROW, UP_ARROW and DOWN_ARROW to accurately alter the axis.  Holding SHIFT while pressing the ARROW keys will alter the axis by 0.5 degrees.\nPress ENTER when finished"
        self.help_box = TextBox(context, 500, 500, 300, 200, 10, 20, help_txt)
        self.help_box.snap_to_corner(context, corner=[1, 1])
        self.mode = 'main'
        self._handle = bpy.types.SpaceView3D.draw_handler_add(
            pick_axis_draw_callback, (self, context), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def rotate_arrow(self, context, event):
        loc = Matrix.Translation(self.ins_ob.matrix_world.to_translation())
        rot_base = self.ins_ob.matrix_world.to_3x3()

        r_model = self.model.matrix_world.to_quaternion()

        if event.type == "UP_ARROW":
            axis = r_model * Vector((0, 1, 0))
        if event.type == "DOWN_ARROW":
            axis = r_model * Vector((0, -1, 0))
        if event.type == "LEFT_ARROW":
            axis = r_model * Vector((1, 0, 0))
        if event.type == "RIGHT_ARROW":
            axis = r_model * Vector((-1, 0, 0))

        if event.shift:
            ang = .5 * math.pi / 180
        else:
            ang = 2.5 * math.pi / 180

        rot = Matrix.Rotation(ang, 3, axis)
        self.ins_ob.matrix_world = loc * (rot * rot_base).to_4x4()

        view = self.ins_ob.matrix_world.to_quaternion() * Vector((0, 0, 1))
        view_local = self.model.matrix_world.inverted().to_quaternion() * view
        fs_undercut = bme_undercut_faces(self.bme, view_local)
        vcolor_data = self.bme.loops.layers.color['Undercut']
        bmesh_color_bmfaces(self.bme.faces[:], vcolor_data, Color((1, 1, 1)))
        bmesh_color_bmfaces(fs_undercut, vcolor_data, Color((.8, .2, .5)))
        self.bme.to_mesh(self.model.data)
        return

    def click_model(self, context, x, y):
        region = context.region
        rv3d = context.region_data
        coord = x, y
        ray_max = 10000
        view_vector = region_2d_to_vector_3d(region, rv3d, coord)
        ray_origin = region_2d_to_origin_3d(region, rv3d, coord)
        ray_target = ray_origin + (view_vector * ray_max)

        imx = self.model.matrix_world.inverted()

        result, loc, normal, idx = self.model.ray_cast(
            imx * ray_origin, imx * ray_target - imx * ray_origin)

        return result

    def preview_direction(self, context):

        start = time.time()
        view = context.space_data.region_3d.view_rotation * Vector((0, 0, 1))
        mx = self.model.matrix_world
        i_mx = mx.inverted()
        view_local = i_mx.to_quaternion() * view
        fs_undercut = bme_undercut_faces(self.bme, view_local)
        print('there are %i undercts' % len(fs_undercut))
        vcolor_data = self.bme.loops.layers.color['Undercut']
        bmesh_color_bmfaces(self.bme.faces[:], vcolor_data, Color((1, 1, 1)))
        bmesh_color_bmfaces(fs_undercut, vcolor_data, Color((.8, .2, .5)))
        self.bme.to_mesh(self.model.data)
        finish = time.time()
        print('took %s to detect undercuts' % str(finish - start)[0:4])

        loc = odcutils.get_bbox_center(self.model, world=True)
        mxT = Matrix.Translation(loc)
        mxR = context.space_data.region_3d.view_rotation.to_matrix().to_4x4()
        self.ins_ob.matrix_world = mxT * mxR

        self.previewed = True
        return

    def finish(self, context):

        loc = odcutils.get_bbox_center(self.model, world=True)
        ins_ob = bpy.data.objects.get('Insertion Axis')
        view = ins_ob.matrix_world.to_quaternion() * Vector((0, 0, 1))

        #view = context.space_data.region_3d.view_rotation * Vector((0,0,1))
        odcutils.silouette_brute_force(context, self.model, view, True)

        #mxT = Matrix.Translation(loc)
        #mxR = context.space_data.region_3d.view_rotation.to_matrix().to_4x4()

        #if "Insertion Axis" in bpy.data.objects:
        #    ob = bpy.data.objects.get('Insertion Axis')
        #    ob.hide = False
        #else:
        #    ob = bpy.data.objects.new('Insertion Axis', None)
        #    ob.empty_draw_type = 'SINGLE_ARROW'
        #    ob.empty_draw_size = 20
        #    context.scene.objects.link(ob)

        bpy.ops.object.select_all(action='DESELECT')
        #ob.parent = self.model
        #ob.matrix_world = mxT * mxR
        context.scene.objects.active = self.model
        self.model.select = True

        #context.scene.cursor_location = loc
        #bpy.ops.view3d.view_center_cursor()
        #bpy.ops.view3d.viewnumpad(type = 'FRONT')
        #bpy.ops.view3d.view_selected()

        #context.space_data.transform_manipulators = {'ROTATE'}

        for i, mat in enumerate(self.model.data.materials):
            if mat.name == 'Undercut':
                break
        self.model.data.materials.pop(i, update_data=True)
        context.space_data.show_textured_solid = False
        self.splint.insertion_path = True
        self.model.lock_location[0], self.model.lock_location[
            1], self.model.lock_location[2] = True, True, True
Esempio n. 15
0
class OPENDENTAL_OT_place_bracket(bpy.types.Operator):
    """Place Bracket on surface of selected object"""
    bl_idname = "opendental.place_ortho_bracket"
    bl_label = "Ortho Bracket Place"
    bl_options = {'REGISTER', 'UNDO'}
    
    @classmethod
    def poll(cls, context):
        if context.mode == "OBJECT" and context.object != None:
            return True
        else:
            return False
        
    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_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 == 'G' and event.value == 'PRESS' and self.bracket_slicer:
            self.bracket_slicer.prepare_slice()
            return 'grab'
        
        if event.type == 'T' and event.value == 'PRESS' and self.bracket_slicer:
            self.bracket_slicer.prepare_slice()
            return 'torque'
        
        if event.type == 'R' and event.value == 'PRESS' and self.bracket_slicer:
            self.bracket_slicer.prepare_slice()
            return 'rotate'
        
        
        if event.type == 'MOUSEMOVE':  
            return 'main'
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            x, y = event.mouse_region_x, event.mouse_region_y
            self.bracket_manager.place_bracket(context, x,y)
            return 'main'
                               
        if event.type == 'RET' and event.value == 'PRESS':
            if self.bracket_slicer:
                self.bracket_slicer.cache_slice_to_grease(context)
                
            return 'finish'
            
        elif event.type == 'ESC' and event.value == 'PRESS':
            del_obj = self.bracket_manager.bracket_obj
            context.scene.objects.unlink(del_obj)
            bpy.data.objects.remove(del_obj)
            return 'cancel' 

        return 'main'
    
    def modal_torque(self,context,event):
        # no navigation in grab mode
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.bracket_slicer.slice_confirm()
            return 'main'
        
        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.bracket_slicer.slice_cancel()
            return 'main'
        
        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.bracket_slicer.slice_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
            return 'torque'
        
        elif event.type in {'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 'UP_ARROW','DOWN_ARROW'}:
            self.bracket_manager.torque_event(event.type, event.shift)
            self.bracket_slicer.slice()
            return 'torque'
    
    def modal_rotate(self,context,event):
        # no navigation in grab mode
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.bracket_slicer.slice_confirm()
            return 'main'
        
        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.bracket_slicer.slice_cancel()
            return 'main'
        
        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.bracket_slicer.slice_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
            return 'rotate'
        
        elif event.type in {'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 'UP_ARROW','DOWN_ARROW'}:
            self.bracket_manager.rotate_event(event.type, event.shift)
            self.bracket_slicer.slice()
            return 'rotate'    
    def modal_grab(self,context,event):
        # no navigation in grab mode
        #uses the slicer to manage the grab
        
        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            #confirm location
            self.bracket_slicer.slice_confirm()
            return 'main'
        
        elif event.type in {'RIGHTMOUSE', 'ESC'} and event.value == 'PRESS':
            #put it back!
            self.bracket_slicer.slice_cancel()
            return 'main'
        
        elif event.type == 'MOUSEMOVE':
            #update the b_pt location
            self.bracket_slicer.slice_mouse_move(context,event.mouse_region_x, event.mouse_region_y)
            return 'grab'
        
        elif event.type in {'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 'UP_ARROW','DOWN_ARROW'}:
            self.bracket_manager.spin_event(event.type, event.shift)
            self.bracket_slicer.slice()
            return 'grab'
      
    def modal(self, context, event):
        context.area.tag_redraw()
        
        FSM = {}    
        FSM['main']    = self.modal_main
        FSM['rotate']    = self.modal_rotate
        FSM['grab']   = self.modal_grab
        FSM['torque']  = self.modal_torque
        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: self.mode = nmode
        
        return {'RUNNING_MODAL'}
    
    def invoke(self, context, event):

        sce=bpy.context.scene

        if context.object and context.object.type == 'MESH':
            self.bracket_manager = BracketDataManager(context,snap_type ='OBJECT', snap_object = context.object, name = 'Bracket')
            self.bracket_slicer = BracektSlicer(context, self.bracket_manager)
        else:
            self.bracket_manager = BracketDataManager(context,snap_type ='SCENE', snap_object = None, name = 'Bracket')
            self.bracket_slicer = None
        
        
        help_txt = "DRAW MARGIN OUTLINE\n\nLeft Click on model to place bracket.\n G to grab  \n S to show slice \n ENTER to confirm \n ESC to cancel"
        self.help_box = TextBox(context,500,500,300,200,10,20,help_txt)
        self.help_box.snap_to_corner(context, corner = [1,1])
        self.mode = 'main'
        self._handle = bpy.types.SpaceView3D.draw_handler_add(bracket_placement_draw_callback, (self, context), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}