Beispiel #1
0
    def execute(self, context):
        settings = get_settings()
        dbg = settings.debug
        #if bpy.context.mode != 'OBJECT':
        #    bpy.ops.object.mode_set(mode = 'OBJECT')

        sce = context.scene

        world_mx = Matrix.Identity(4)

        world_mx[0][3] = sce.cursor_location[0]
        world_mx[1][3] = sce.cursor_location[1]
        world_mx[2][3] = sce.cursor_location[2]

        #is this more memory friendly than listing all objects?
        current_obs = [ob.name for ob in bpy.data.objects]

        #link the new implant from the library
        obj_from_lib(settings.ortho_lib, self.ob)

        #this is slightly more robust than trusting we don't have duplicate names.
        for ob in bpy.data.objects:
            if ob.name not in current_obs:
                Bracket = ob

        sce.objects.link(Bracket)
        rv3d = context.region_data
        view_mx = rv3d.view_rotation.to_matrix()

        Bracket.matrix_world = world_mx * view_mx.to_4x4()
        return {'FINISHED'}
 def execute(self, context):
     settings = get_settings()
     dbg = settings.debug
     #if bpy.context.mode != 'OBJECT':
     #    bpy.ops.object.mode_set(mode = 'OBJECT')
     
     sce = context.scene
       
     world_mx = Matrix.Identity(4)
         
     world_mx[0][3]=sce.cursor_location[0]
     world_mx[1][3]=sce.cursor_location[1]
     world_mx[2][3]=sce.cursor_location[2]
                                     
     #is this more memory friendly than listing all objects?
     current_obs = [ob.name for ob in bpy.data.objects]
             
     #link the new implant from the library
     obj_from_lib(settings.ortho_lib,self.ob)
             
     #this is slightly more robust than trusting we don't have duplicate names.
     for ob in bpy.data.objects:
         if ob.name not in current_obs:
             Bracket = ob
                     
     sce.objects.link(Bracket)
     rv3d = context.region_data
     view_mx = rv3d.view_rotation.to_matrix()
 
     Bracket.matrix_world = world_mx * view_mx.to_4x4()              
     return {'FINISHED'}
Beispiel #3
0
    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'}
Beispiel #4
0
    def execute(self, context):
        settings = get_settings()
        dbg = settings.debug
        #if bpy.context.mode != 'OBJECT':
        #    bpy.ops.object.mode_set(mode = 'OBJECT')

        sce = context.scene
        implants = odcutils.implant_selection(context)

        layers_copy = [layer for layer in context.scene.layers]
        context.scene.layers[0] = True

        if implants != []:

            for implant_space in implants:
                #check if space already has an implant object.
                #if so, delete, replace, print warning
                Implant = bpy.data.objects[implant_space.implant]

                if Implant.rotation_mode != 'QUATERNION':
                    Implant.rotation_mode = 'QUATERNION'
                    Implant.update_tag()
                    sce.update()

                if bpy.data.objects.get(implant_space.drill):
                    self.report(
                        {'WARNING'},
                        "replacing the existing drill with the one you chose")
                    Sleeve = bpy.data.objects[implant_space.drill]
                    #unlink it from the scene, clear it's users, remove it.
                    sce.objects.unlink(Sleeve)
                    Implant.user_clear()
                    #remove the object
                    bpy.data.objects.remove(Sleeve)

                current_obs = [ob.name for ob in bpy.data.objects]

                #link the new implant from the library
                settings = get_settings()
                odcutils.obj_from_lib(settings.drill_lib, self.drill)

                #this is slightly more robust than trusting we don't have duplicate names.
                for ob in bpy.data.objects:
                    if ob.name not in current_obs:
                        Sleeve = ob

                sce.objects.link(Sleeve)
                Sleeve.layers[19] = True
                mx_w = Implant.matrix_world.copy()
                #point the right direction
                Sleeve.rotation_mode = 'QUATERNION'
                Sleeve.rotation_quaternion = mx_w.to_quaternion()
                Sleeve.update_tag()
                context.scene.update()
                Trans = Sleeve.rotation_quaternion * Vector(
                    (0, 0, -self.depth))
                Sleeve.matrix_world[0][3] = mx_w[0][3] + Trans[0]
                Sleeve.matrix_world[1][3] = mx_w[1][3] + Trans[1]
                Sleeve.matrix_world[2][3] = mx_w[2][3] + Trans[2]

                Sleeve.name = implant_space.name + '_' + Sleeve.name
                implant_space.drill = Sleeve.name
                Sleeve.update_tag()
                context.scene.update()
                odcutils.parent_in_place(Sleeve, Implant)
                odcutils.layer_management(sce.odc_implants, debug=dbg)
        for i, layer in enumerate(layers_copy):
            context.scene.layers[i] = layer
        context.scene.layers[19] = True
        return {'FINISHED'}
Beispiel #5
0
    def execute(self, context):
        settings = get_settings()
        dbg = settings.debug
        #if bpy.context.mode != 'OBJECT':
        #    bpy.ops.object.mode_set(mode = 'OBJECT')

        sce = context.scene
        n = sce.odc_implant_index
        implant_space = sce.odc_implants[n]

        implants = odcutils.implant_selection(context)
        layers_copy = [layer for layer in context.scene.layers]
        context.scene.layers[0]

        if implants != []:

            for implant_space in implants:
                #check if space already has an implant object.
                #if so, delete, replace, print warning
                if implant_space.implant and implant_space.implant in bpy.data.objects:
                    self.report({
                        'WARNING'
                    }, "replacing the existing implant with the one you chose")
                    Implant = bpy.data.objects[implant_space.implant]

                    #the origin/location of the implant is it's apex
                    L = Implant.location.copy()

                    world_mx = Implant.matrix_world.copy()

                    #the platorm is the length of the implant above the apex, in the local Z direction
                    #local Z positive is out the apex, soit's negative.
                    #Put the cursor there
                    sce.cursor_location = L - Implant.dimensions[
                        2] * world_mx.to_3x3() * Vector((0, 0, 1))

                    #first get rid of children...so we can use the
                    #parent to find out who the children are
                    if Implant.children:
                        for child in Implant.children:
                            sce.objects.unlink(child)
                            child.user_clear()
                            bpy.data.objects.remove(child)

                    #unlink it from the scene, clear it's users, remove it.
                    sce.objects.unlink(Implant)
                    Implant.user_clear()
                    #remove the object
                    bpy.data.objects.remove(Implant)

                #TDOD what about the children/hardwares?
                else:
                    world_mx = Matrix.Identity(4)

                world_mx[0][3] = sce.cursor_location[0]
                world_mx[1][3] = sce.cursor_location[1]
                world_mx[2][3] = sce.cursor_location[2]

                #is this more memory friendly than listing all objects?
                current_obs = [ob.name for ob in bpy.data.objects]

                #link the new implant from the library
                odcutils.obj_from_lib(settings.imp_lib, self.imp)

                #this is slightly more robust than trusting we don't have duplicate names.
                for ob in bpy.data.objects:
                    if ob.name not in current_obs:
                        Implant = ob

                sce.objects.link(Implant)

                #this relies on the associated hardware objects having the parent implant
                #name inside them
                if self.hardware:
                    current_obs = [ob.name for ob in bpy.data.objects]

                    inc = self.imp + '_'
                    hardware_list = odcutils.obj_list_from_lib(
                        settings.imp_lib, include=inc)
                    print(hardware_list)
                    for ob in hardware_list:
                        odcutils.obj_from_lib(settings.imp_lib, ob)

                    for ob in bpy.data.objects:
                        if ob.name not in current_obs:
                            sce.objects.link(ob)
                            ob.parent = Implant
                            ob.layers[11] = True

                delta = Implant.dimensions[2] * world_mx.to_3x3() * Vector(
                    (0, 0, 1))
                print(delta.length)
                world_mx[0][3] += delta[0]
                world_mx[1][3] += delta[1]
                world_mx[2][3] += delta[2]

                Implant.matrix_world = world_mx

                if sce.odc_props.master:
                    Master = bpy.data.objects[sce.odc_props.master]
                    odcutils.parent_in_place(Implant, Master)
                else:
                    self.report({
                        'WARNING'
                    }, 'No Master Model, placing implant anyway, moving objects may not preserve spatial relationships'
                                )

                #looks a little redundant, but it ensure if any
                #duplicates exist our referencing stays accurate
                Implant.name = implant_space.name + '_' + Implant.name
                implant_space.implant = Implant.name

                odcutils.layer_management(sce.odc_implants, debug=dbg)

        for i, layer in enumerate(layers_copy):
            context.scene.layers[i] = layer
        context.scene.layers[11] = True
        return {'FINISHED'}
def teeth_to_curve(context, arch, sextant, tooth_library, teeth = [], shift = 'BUCCAL', limit = False, link = False, reverse = False, mirror = False, debug = False, reorient = True):
    '''
    puts teeth along a curve for full arch planning
    args:
       curve - blender Curve object
       sextant - the quadrant or sextant that the curve corresponds to. enum in 'MAX', 'MAND', 'UR' 'LR' 'LR' 'LL' 'UA' 'LA' '
       teeth - list of odc_teeth, to link to or from.  eg, if tooth already
         a restoration it will use that object, if not, it will link a new
         blender object to that tooth as the restoration or contour.
       shift = whether to use buccal cusps, center of mass or, center of fossa to align onto cirve.  enum in 'BUCCAL', 'COM', 'FOSSA'
       limit - only link teeth for each tooth in teeth
       link - Bool, whether or not to link to/from the teeth list
    '''
    if debug:
        start = time.time()
        
    orig_arch_name = arch.name
    
    bpy.ops.object.select_all(action='DESELECT')
    context.scene.objects.active = arch
    arch.hide = False
    arch.select = True
    
    if mirror:
        #This should help with the mirroring?
        arch.data.resolution_u = 5 
        
        #if it doesn't have a mirror, we need to mirror it
        if "Mirror" not in arch.modifiers:
            bpy.ops.object.modifier_add(type='MIRROR')
        #non mirrored curve needed for appropriate constraining..
        #convert to mesh applies mirror, reconvert to curve gives us a full length curve
        arch.modifiers["Mirror"].merge_threshold = 5
        bpy.ops.object.convert(target='MESH',keep_original = True)
        bpy.ops.object.convert(target='CURVE', keep_original = False) #this will be the new full arch        
        arch = context.object
        arch.name = orig_arch_name + "_Mirrored"
    

    
    #we may want to switch the direction of the curve :-)
    #we may also want to handle this outside of this function
    if reverse:
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.curve.switch_direction()
        bpy.ops.object.mode_set(mode='OBJECT')
        
    bpy.ops.object.convert(target='MESH', keep_original = True)
    arch_mesh = context.object #now the mesh conversion
    arch_len = 0
    mx = arch_mesh.matrix_world
    
    #do some calcs to the curve
    #TODO:  split this method off.  It may already
    #be in odcutils.
    occ_dir = Vector((0,0,0))  #this will end  be a normalized, global direction
    for i in range(0,len(arch_mesh.data.vertices)-1):
        v0 = arch_mesh.data.vertices[i]
        v1 = arch_mesh.data.vertices[i+1]
        V0 = mx*v1.co - mx*v0.co
        arch_len += V0.length
    
        if i < len(arch_mesh.data.vertices)-2:
            v2 = arch_mesh.data.vertices[i+2]
            V1 = mx*v2.co - mx*v1.co
            
            occ_dir += V0.cross(V1)
    
    if debug:
        print("arch is %f mm long" % arch_len)

    #pull values from the tooth size/data
        #if we are mirroring, we need to do some logic
    if mirror:
        if sextant not in ["UR","UL","LR","LL","MAX","MAND"]:
            print('Incorrect sextant for mirroring')
            return {'CANCELLED'}
        else:
            if sextant.startswith("U"):
                sextant = "MAX"
            elif sextant.startswith("L"):
                sextant = "MAND"
            #else..leave quadrant alone
            
    curve_teeth = quadrant_dict[sextant]
    occ_dir *= occ_direct_dict[sextant] * 1/(len(arch_mesh.data.vertices)-2)
    occ_dir.normalize()
    
    #this deletes the arch mesh...not the arch curve
    bpy.ops.object.delete()
    
    if reorient:
        arch_z = mx.to_quaternion() * Vector((0,0,1))
        arch_z.normalize()
        if math.pow(arch_z.dot(occ_dir),2) < .9:
            orient = odcutils.rot_between_vecs(Vector((0,0,1)), occ_dir) #align the local Z of bezier with occlusal direction (which is global).
            odcutils.reorient_object(arch, orient)
    if debug:
        print("working on these teeth %s" % ":".join(curve_teeth))
    
    #import/link teeth from the library
    restorations = []
    if link and len(context.scene.odc_teeth): 
        for tooth in context.scene.odc_teeth:
            if tooth.name[0:2] in curve_teeth:
                #TODO: restoration etc?
                #we will have to check later if we need to use the restoration
                #from this tooth
                restorations.append(tooth.name)
        if debug:               
            print("These restorations are already in the proposed quadrant %s" % ", ".join(restorations))
            
    
    #figure out which objects we are going to distribute.
    lib_teeth_names = odcutils.obj_list_from_lib(tooth_library) #TODO: check if tooth_library is valid?
    tooth_objects=[[None]]*len(curve_teeth) #we want this list to be mapped to curve_teeth with it's index...dictionary if we have to
    delete_later = []
    for i, planned_tooth in enumerate(curve_teeth):
        #this will be a one item list
        tooth_in_scene = [tooth for tooth in context.scene.odc_teeth if tooth.name.startswith(planned_tooth)]
        if link and len(tooth_in_scene):
            #check if the restoration is already there...if so, use it
            if tooth_in_scene[0].contour:
                tooth_objects[i] = bpy.data.objects[tooth_in_scene[0].contour]
        
            #if it's not there, add it in, and associate it with ODCTooth Object
            else:
                for tooth in lib_teeth_names:
                    if tooth.startswith(planned_tooth):  #necessary that the planned teeth have logical names
                        
                        new_name = tooth + "_ArchPlanned"
                        if new_name in bpy.data.objects:   
                            ob = bpy.data.objects[new_name]
                            me = ob.data
                            ob.user_clear()
                            bpy.data.objects.remove(ob)
                            bpy.data.meshes.remove(me)
                            context.scene.update()
                           
                        odcutils.obj_from_lib(tooth_library, tooth)
                        ob = bpy.data.objects[tooth]
                        context.scene.objects.link(ob)
                        ob.name = new_name
                        tooth_objects[i] = ob
                        tooth_in_scene[0].contour = ob.name
                        break #in case there are multiple copies?
        else: #the tooth is not existing restoration, and we want to put it in anyway
            for tooth in lib_teeth_names:
                if tooth.startswith(planned_tooth):
                    new_name = tooth + "_ArchPlanned"
                    if new_name in bpy.data.objects:   
                        ob = bpy.data.objects[new_name]
                        me = ob.data
                        ob.user_clear()
                        bpy.data.objects.remove(ob)
                        bpy.data.meshes.remove(me)
                        context.scene.update()
                        
                    odcutils.obj_from_lib(tooth_library, tooth)
                    ob = bpy.data.objects[tooth]
                    ob.name += "_ArchPlanned"
                    if limit:
                        context.scene.objects.link(ob)
                        delete_later.append(ob)    
                    else:
                        context.scene.objects.link(ob)
                        
                    tooth_objects[i]= ob
                    break  
    if debug:
        print(tooth_objects)
    
    #secretly, we imported the whole quadrant..we will delete them later
    teeth_len = 0
    lengths = [[0]] * len(curve_teeth) #list of tooth mesial/distal lengths
    locs = [[0]] * len(curve_teeth) #normalized list of locations
    for i, ob in enumerate(tooth_objects):
        lengths[i] = ob.dimensions[0]
        
        teeth_len += ob.dimensions[0]
        locs[i] = teeth_len - ob.dimensions[0]/2
    scale = arch_len/teeth_len
    crowding = teeth_len - arch_len
    
    if debug > 1:
        print(lengths)
        print(locs)
        print(scale)
        print("there is %d mm of crowding" % round(crowding,2))
        print("there is a %d pct archlength discrepancy" % round(100-scale*100, 2))
       
    #scale them to the right size
    for i, ob in enumerate(tooth_objects):
        
        if shift == 'FOSSA':
            delta = .05
        else:
            delta = 0
        #resize it
        ob.scale[0] *= scale + delta
        ob.scale[1] *= scale + delta
        ob.scale[2] *= scale + delta
        
    
        #find the location of interest we want?
        # bbox center, cusp tip? fossa/grove, incisal edge?
        #TODO:  odcutils.tooth_features(tooth,feature)  (world coords or local?)
        ob.location = Vector((0,0,0))
        if ob.rotation_mode != 'QUATERNION':
            ob.rotation_mode = 'QUATERNION'
        
        ob.rotation_quaternion = Quaternion((1,0,0,0))
            
        #center line...we want palatinal face median point z,y with midpointx and center line min local z
        #buccal line...we want incisal edge median local y, maxlocal z, midpoing bbox x and buccal cusp max z?

        context.scene.objects.active = ob
        ob.select = True
        ob.hide = False

        ob.constraints.new('FOLLOW_PATH')
        path_constraint = ob.constraints["Follow Path"]
        path_constraint.target = arch
        path_constraint.use_curve_follow = True
        #find out if we cross the midline
        if sextant in ['MAX','MAND','UA','LA']:
            path_constraint.forward_axis = 'FORWARD_X'
            if int(curve_teeth[i]) > 20 and int(curve_teeth[i]) < 40:
                path_constraint.forward_axis = 'TRACK_NEGATIVE_X'
        else:
            path_constraint.forward_axis = 'FORWARD_X'

        path_constraint.offset = 100*(-1 + locs[i]/teeth_len)


    #after arranging them on the curve, make a 2nd pass to spin them or not
    
    #decrease in number means mesial.  Except at midline.this will happen
    #we have constructed curve_teeth such that there will never be a non
    #integer change in adjacent list members. #eg, 
    #quaternion rotation rules
    # Qtotal = Qa * Qb represtnts rotation b followed by rotation a
    #what we are doing is testing the occlusal direction of one tooth vs the arch occlusal direction
    context.scene.update()
    ob_dist = tooth_objects[1]
    ob_mes = tooth_objects[0]
    mesial = int(curve_teeth[1]) - int(curve_teeth[0]) == 1 #if true....distal numbers > mesial numbers
    vect = ob_mes.matrix_world * ob_mes.location - ob_dist.matrix_world * ob_dist.location
    spin = (vect.dot(ob_dist.matrix_world.to_quaternion() * Vector((1,0,0))) < 0) == mesial

    tooth_occ = ob_mes.matrix_world.to_quaternion() * Vector((0,0,1))
    flip = tooth_occ.dot(occ_dir) > 0
        
    if debug:
        print('We will flip the teeth: %s. We will spin the teeth: %s.' % (str(flip), str(spin)))
    
    for ob in tooth_objects:
        if flip:
            ob.rotation_quaternion = Quaternion((0,1,0,0))
        if spin:
            ob.rotation_quaternion = Quaternion((0,0,0,1)) * ob.rotation_quaternion 
 
    for i, ob in enumerate(tooth_objects):
                    
            if shift == 'BUCCAL':
                groups = ["Incisal Edge", "Distobuccal Cusp","Mesiobuccal Cusp", "Buccal Cusp"]
                inds = []
                for vgroup in groups:
                    if vgroup in ob.vertex_groups:
                        inds += odcutils.vert_group_inds_get(context, ob, vgroup)
                max_z = 0
                max_ind = 0       
                for j in inds:
                    z = ob.data.vertices[j].co[2]
                    if z > max_z:
                        max_ind = j
                        max_z = z
                tip = ob.data.vertices[max_ind].co
                tooth_shift = Vector((0,tip[1]*ob.scale[1],tip[2]*ob.scale[2]))
                
                if sextant in ['MAX','MAND','UA','LA']: #no freakin idea why this is happening, but empirically, it's working
                    tooth_shift[1]*= -1
                    
                ob.location += (-1 + 2*flip) * tooth_shift
    
    
            if shift == 'FOSSA':
                groups = ["Middle Fissure", "Palatinal Face"]
                inds = []
                for vgroup in groups:
                    if vgroup in ob.vertex_groups and vgroup == "Middle Fissure":
                        inds += odcutils.vert_group_inds_get(context, ob, vgroup)
                
                        min_z = ob.dimensions[2]
                        min_ind = 0       
                        for j in inds:
                            z = ob.data.vertices[j].co[2]
                            if z < min_z:
                                min_ind = j
                                min_z = z
                                depth = ob.data.vertices[min_ind].co
                                tooth_shift = Vector((0,depth[1]*ob.scale[1],depth[2]*ob.scale[2]))
                                
                    elif vgroup in ob.vertex_groups and vgroup  == "Palatinal Face":
                        inds += odcutils.vert_group_inds_get(context, ob, vgroup)
                        mx =  Matrix.Identity(4)
                        com = odcutils.get_com(ob.data, inds, mx)
                        tooth_shift = odcutils.scale_vec_mult(com, ob.matrix_world.to_scale())
                
                if sextant in ['MAX','MAND','UA','LA']: #no freakin idea why this is happening, but empirically, it's working
                    tooth_shift[1]*= -1
                    
                ob.location += (-1 + 2*flip) * tooth_shift
    
    if limit:
        bpy.ops.object.select_all(action='DESELECT')        
        for ob in delete_later:
            ob.select = True
            
        context.scene.objects.active = ob
        bpy.ops.object.delete()        
Beispiel #7
0
def place_implant(context, implant_space, location,orientation,imp, hardware = True):
    '''
    
    args:
        context
        implant_space - ODC Implant Space type
        location - Vector
        orientation - Matrix or Quaternion
        lib_implants - 
        imp - string representing implant object name in link library
    '''
    #check if space already has an implant object.
    #if so, delete, replace, print warning
    sce = context.scene
    if implant_space.implant and implant_space.implant in bpy.data.objects:
        print("replacing the existing implant with the one you chose")
        Implant = bpy.data.objects[implant_space.implant]
        #unlink it from the scene, clear it's useres, remove it.
        
        
        if Implant.children:
            for child in Implant.children:
                sce.objects.unlink(child)
                child.user_clear
                bpy.data.objects.remove(child)
                            
        sce.objects.unlink(Implant)
        implant_mesh = Implant.data
        
        Implant.user_clear()
        #remove the object
        bpy.data.objects.remove(Implant)
        implant_mesh.user_clear()
        bpy.data.meshes.remove(implant_mesh)
        
        sce.update()
        #TDOD what about the children/hardwares?
   
    world_mx = Matrix.Identity(4)
    world_mx[0][3]=location[0]
    world_mx[1][3]=location[1]
    world_mx[2][3]=location[2]
        
        #mx_b = Matrix.Identity(4)
        #mx_l = Matrix.Identity(4)
    #is this more memory friendly than listing all objects?
    current_obs = [ob.name for ob in bpy.data.objects]
    
    #link the new implant from the library
    settings = get_settings()
    odcutils.obj_from_lib(settings.imp_lib, imp)
    
    #this is slightly more robust than trusting we don't have duplicate names.
    for ob in bpy.data.objects:
        if ob.name not in current_obs:
            Implant = ob
    
    
    sce.objects.link(Implant)
    #Implant.matrix_basis = mx_b
    Implant.matrix_world = world_mx
    Implant.update_tag()
    sce.update()
    Implant.rotation_mode = 'QUATERNION'
    Implant.rotation_quaternion = orientation
    sce.update()
    #Implant.matrix_local = mx_l
    #Implant.location = L
    
    if sce.odc_props.master:
        Master = bpy.data.objects[sce.odc_props.master]
        odcutils.parent_in_place(Implant, Master)
    else:
        print('No Master Model, placing implant anyway, moving objects may not preserve spatial relationships')
    
    #looks a little redundant, but it ensure if any
    #duplicates exist our referencing stays accurate
    Implant.name = implant_space.name + "_" + Implant.name
    implant_space.implant = Implant.name
    
    if hardware:
        current_obs = [ob.name for ob in bpy.data.objects]
                    
        inc = imp + '_'
        
        settings = get_settings()
        hardware_list = odcutils.obj_list_from_lib(settings.imp_lib, include = inc)
        print(hardware_list)
        for ob in hardware_list:
            odcutils.obj_from_lib(settings.imp_lib,ob)
                
        for ob in bpy.data.objects:
            if ob.name not in current_obs:
                sce.objects.link(ob)
                ob.parent = Implant
                ob.layers[11] = True  #TODO: put this in layer management.
                                           
    return Implant
Beispiel #8
0
 def execute(self, context):
     settings = get_settings()
     dbg = settings.debug
     #if bpy.context.mode != 'OBJECT':
     #    bpy.ops.object.mode_set(mode = 'OBJECT')
     
     sce = context.scene
     implants = odcutils.implant_selection(context)
     
     layers_copy = [layer for layer in context.scene.layers]
     context.scene.layers[0] = True
     
     if implants != []:
     
         for implant_space in implants:
             #check if space already has an implant object.
             #if so, delete, replace, print warning
             Implant = bpy.data.objects[implant_space.implant]
             
             if Implant.rotation_mode != 'QUATERNION':
                 Implant.rotation_mode = 'QUATERNION'
                 Implant.update_tag()
                 sce.update()
                 
             if bpy.data.objects.get(implant_space.drill):
                 self.report({'WARNING'}, "replacing the existing drill with the one you chose")
                 Sleeve = bpy.data.objects[implant_space.drill]
                 #unlink it from the scene, clear it's users, remove it.
                 sce.objects.unlink(Sleeve)
                 Implant.user_clear()
                 #remove the object
                 bpy.data.objects.remove(Sleeve)
                 
             current_obs = [ob.name for ob in bpy.data.objects]
             
             #link the new implant from the library
             settings = get_settings()
             odcutils.obj_from_lib(settings.drill_lib,self.drill)
             
             #this is slightly more robust than trusting we don't have duplicate names.
             for ob in bpy.data.objects:
                 if ob.name not in current_obs:
                     Sleeve = ob
                     
             sce.objects.link(Sleeve)
             Sleeve.layers[19] = True
             mx_w = Implant.matrix_world.copy()
             #point the right direction
             Sleeve.rotation_mode = 'QUATERNION'
             Sleeve.rotation_quaternion = mx_w.to_quaternion()          
             Sleeve.update_tag()
             context.scene.update()   
             Trans = Sleeve.rotation_quaternion * Vector((0,0,-self.depth))
             Sleeve.matrix_world[0][3] = mx_w[0][3] + Trans[0]
             Sleeve.matrix_world[1][3] = mx_w[1][3] + Trans[1]
             Sleeve.matrix_world[2][3] = mx_w[2][3] + Trans[2]
             
             Sleeve.name = implant_space.name + '_' + Sleeve.name
             implant_space.drill = Sleeve.name
             Sleeve.update_tag()
             context.scene.update()    
             odcutils.parent_in_place(Sleeve, Implant)
             odcutils.layer_management(sce.odc_implants, debug = dbg)
     for i, layer in enumerate(layers_copy):
         context.scene.layers[i] = layer
     context.scene.layers[19] = True                
     return {'FINISHED'}
Beispiel #9
0
    def execute(self, context):
        settings = get_settings()
        dbg = settings.debug
        #if bpy.context.mode != 'OBJECT':
        #    bpy.ops.object.mode_set(mode = 'OBJECT')
        
        sce = context.scene
        n = sce.odc_implant_index
        implant_space = sce.odc_implants[n]
        
        implants = odcutils.implant_selection(context)
        layers_copy = [layer for layer in context.scene.layers]
        context.scene.layers[0] 
        
        if implants != []:
        
            for implant_space in implants:
                #check if space already has an implant object.
                #if so, delete, replace, print warning
                if implant_space.implant and implant_space.implant in bpy.data.objects:
                    self.report({'WARNING'}, "replacing the existing implant with the one you chose")
                    Implant = bpy.data.objects[implant_space.implant]
                    
                    
                    #the origin/location of the implant is it's apex
                    L = Implant.location.copy()  

                    world_mx = Implant.matrix_world.copy()
                    
                    #the platorm is the length of the implant above the apex, in the local Z direction
                    #local Z positive is out the apex, soit's negative.
                    #Put the cursor there
                    sce.cursor_location = L - Implant.dimensions[2] * world_mx.to_3x3() *  Vector((0,0,1))
                                        
                    #first get rid of children...so we can use the
                    #parent to find out who the children are
                    if Implant.children:
                        for child in Implant.children:
                            sce.objects.unlink(child)
                            child.user_clear()
                            bpy.data.objects.remove(child)
                            
                    #unlink it from the scene, clear it's users, remove it.
                    sce.objects.unlink(Implant)
                    Implant.user_clear()
                    #remove the object
                    bpy.data.objects.remove(Implant)
                    
                    
                    
                #TDOD what about the children/hardwares?
                else:
                    world_mx = Matrix.Identity(4)
                    
                world_mx[0][3]=sce.cursor_location[0]
                world_mx[1][3]=sce.cursor_location[1]
                world_mx[2][3]=sce.cursor_location[2]
                                        
                #is this more memory friendly than listing all objects?
                current_obs = [ob.name for ob in bpy.data.objects]
                
                #link the new implant from the library
                odcutils.obj_from_lib(settings.imp_lib,self.imp)
                
                #this is slightly more robust than trusting we don't have duplicate names.
                for ob in bpy.data.objects:
                    if ob.name not in current_obs:
                        Implant = ob
                        
                sce.objects.link(Implant)
                
                #this relies on the associated hardware objects having the parent implant
                #name inside them
                if self.hardware:
                    current_obs = [ob.name for ob in bpy.data.objects]
                    
                    inc = self.imp + '_'
                    hardware_list = odcutils.obj_list_from_lib(settings.imp_lib, include = inc)
                    print(hardware_list)
                    for ob in hardware_list:
                        odcutils.obj_from_lib(settings.imp_lib,ob)
                
                    for ob in bpy.data.objects:
                        if ob.name not in current_obs:
                            sce.objects.link(ob)
                            ob.parent = Implant
                            ob.layers[11] = True
                

                delta =  Implant.dimensions[2] * world_mx.to_3x3() * Vector((0,0,1))
                print(delta.length)
                world_mx[0][3] += delta[0]
                world_mx[1][3] += delta[1]
                world_mx[2][3] += delta[2]
                    

                Implant.matrix_world = world_mx

                
                if sce.odc_props.master:
                    Master = bpy.data.objects[sce.odc_props.master]
                    odcutils.parent_in_place(Implant, Master)
                else:
                    self.report({'WARNING'}, 'No Master Model, placing implant anyway, moving objects may not preserve spatial relationships')
                
                #looks a little redundant, but it ensure if any
                #duplicates exist our referencing stays accurate
                Implant.name = implant_space.name + '_' + Implant.name
                implant_space.implant = Implant.name
                    
                odcutils.layer_management(sce.odc_implants, debug = dbg)
        
        for i, layer in enumerate(layers_copy):
            context.scene.layers[i] = layer
        context.scene.layers[11] = True           
        return {'FINISHED'}
def teeth_to_curve(context,
                   arch,
                   sextant,
                   tooth_library,
                   teeth=[],
                   shift='BUCCAL',
                   limit=False,
                   link=False,
                   reverse=False,
                   mirror=False,
                   debug=False,
                   reorient=True):
    '''
    puts teeth along a curve for full arch planning
    args:
       curve - blender Curve object
       sextant - the quadrant or sextant that the curve corresponds to. enum in 'MAX', 'MAND', 'UR' 'LR' 'LR' 'LL' 'UA' 'LA' '
       teeth - list of odc_teeth, to link to or from.  eg, if tooth already
         a restoration it will use that object, if not, it will link a new
         blender object to that tooth as the restoration or contour.
       shift = whether to use buccal cusps, center of mass or, center of fossa to align onto cirve.  enum in 'BUCCAL', 'COM', 'FOSSA'
       limit - only link teeth for each tooth in teeth
       link - Bool, whether or not to link to/from the teeth list
    '''
    if debug:
        start = time.time()

    orig_arch_name = arch.name

    bpy.ops.object.select_all(action='DESELECT')
    context.scene.objects.active = arch
    arch.hide = False
    arch.select = True

    if mirror:
        #This should help with the mirroring?
        arch.data.resolution_u = 5

        #if it doesn't have a mirror, we need to mirror it
        if "Mirror" not in arch.modifiers:
            bpy.ops.object.modifier_add(type='MIRROR')
        #non mirrored curve needed for appropriate constraining..
        #convert to mesh applies mirror, reconvert to curve gives us a full length curve
        arch.modifiers["Mirror"].merge_threshold = 5
        bpy.ops.object.convert(target='MESH', keep_original=True)
        bpy.ops.object.convert(
            target='CURVE',
            keep_original=False)  #this will be the new full arch
        arch = context.object
        arch.name = orig_arch_name + "_Mirrored"

    #we may want to switch the direction of the curve :-)
    #we may also want to handle this outside of this function
    if reverse:
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.curve.switch_direction()
        bpy.ops.object.mode_set(mode='OBJECT')

    bpy.ops.object.convert(target='MESH', keep_original=True)
    arch_mesh = context.object  #now the mesh conversion
    arch_len = 0
    mx = arch_mesh.matrix_world

    #do some calcs to the curve
    #TODO:  split this method off.  It may already
    #be in odcutils.
    occ_dir = Vector(
        (0, 0, 0))  #this will end  be a normalized, global direction
    for i in range(0, len(arch_mesh.data.vertices) - 1):
        v0 = arch_mesh.data.vertices[i]
        v1 = arch_mesh.data.vertices[i + 1]
        V0 = mx * v1.co - mx * v0.co
        arch_len += V0.length

        if i < len(arch_mesh.data.vertices) - 2:
            v2 = arch_mesh.data.vertices[i + 2]
            V1 = mx * v2.co - mx * v1.co

            occ_dir += V0.cross(V1)

    if debug:
        print("arch is %f mm long" % arch_len)

    #pull values from the tooth size/data
    #if we are mirroring, we need to do some logic
    if mirror:
        if sextant not in ["UR", "UL", "LR", "LL", "MAX", "MAND"]:
            print('Incorrect sextant for mirroring')
            return {'CANCELLED'}
        else:
            if sextant.startswith("U"):
                sextant = "MAX"
            elif sextant.startswith("L"):
                sextant = "MAND"
            #else..leave quadrant alone

    curve_teeth = quadrant_dict[sextant]
    occ_dir *= occ_direct_dict[sextant] * 1 / (len(arch_mesh.data.vertices) -
                                               2)
    occ_dir.normalize()

    #this deletes the arch mesh...not the arch curve
    bpy.ops.object.delete()

    if reorient:
        arch_z = mx.to_quaternion() * Vector((0, 0, 1))
        arch_z.normalize()
        if math.pow(arch_z.dot(occ_dir), 2) < .9:
            orient = odcutils.rot_between_vecs(
                Vector((0, 0, 1)), occ_dir
            )  #align the local Z of bezier with occlusal direction (which is global).
            odcutils.reorient_object(arch, orient)
    if debug:
        print("working on these teeth %s" % ":".join(curve_teeth))

    #import/link teeth from the library
    restorations = []
    if link and len(context.scene.odc_teeth):
        for tooth in context.scene.odc_teeth:
            if tooth.name[0:2] in curve_teeth:
                #TODO: restoration etc?
                #we will have to check later if we need to use the restoration
                #from this tooth
                restorations.append(tooth.name)
        if debug:
            print(
                "These restorations are already in the proposed quadrant %s" %
                ", ".join(restorations))

    #figure out which objects we are going to distribute.
    lib_teeth_names = odcutils.obj_list_from_lib(
        tooth_library)  #TODO: check if tooth_library is valid?
    tooth_objects = [[None]] * len(
        curve_teeth
    )  #we want this list to be mapped to curve_teeth with it's index...dictionary if we have to
    delete_later = []
    for i, planned_tooth in enumerate(curve_teeth):
        #this will be a one item list
        tooth_in_scene = [
            tooth for tooth in context.scene.odc_teeth
            if tooth.name.startswith(planned_tooth)
        ]
        if link and len(tooth_in_scene):
            #check if the restoration is already there...if so, use it
            if tooth_in_scene[0].contour:
                tooth_objects[i] = bpy.data.objects[tooth_in_scene[0].contour]

            #if it's not there, add it in, and associate it with ODCTooth Object
            else:
                for tooth in lib_teeth_names:
                    if tooth.startswith(
                            planned_tooth
                    ):  #necessary that the planned teeth have logical names

                        new_name = tooth + "_ArchPlanned"
                        if new_name in bpy.data.objects:
                            ob = bpy.data.objects[new_name]
                            me = ob.data
                            ob.user_clear()
                            bpy.data.objects.remove(ob)
                            bpy.data.meshes.remove(me)
                            context.scene.update()

                        odcutils.obj_from_lib(tooth_library, tooth)
                        ob = bpy.data.objects[tooth]
                        context.scene.objects.link(ob)
                        ob.name = new_name
                        tooth_objects[i] = ob
                        tooth_in_scene[0].contour = ob.name
                        break  #in case there are multiple copies?
        else:  #the tooth is not existing restoration, and we want to put it in anyway
            for tooth in lib_teeth_names:
                if tooth.startswith(planned_tooth):
                    new_name = tooth + "_ArchPlanned"
                    if new_name in bpy.data.objects:
                        ob = bpy.data.objects[new_name]
                        me = ob.data
                        ob.user_clear()
                        bpy.data.objects.remove(ob)
                        bpy.data.meshes.remove(me)
                        context.scene.update()

                    odcutils.obj_from_lib(tooth_library, tooth)
                    ob = bpy.data.objects[tooth]
                    ob.name += "_ArchPlanned"
                    if limit:
                        context.scene.objects.link(ob)
                        delete_later.append(ob)
                    else:
                        context.scene.objects.link(ob)

                    tooth_objects[i] = ob
                    break
    if debug:
        print(tooth_objects)

    #secretly, we imported the whole quadrant..we will delete them later
    teeth_len = 0
    lengths = [[0]] * len(curve_teeth)  #list of tooth mesial/distal lengths
    locs = [[0]] * len(curve_teeth)  #normalized list of locations
    for i, ob in enumerate(tooth_objects):
        lengths[i] = ob.dimensions[0]

        teeth_len += ob.dimensions[0]
        locs[i] = teeth_len - ob.dimensions[0] / 2
    scale = arch_len / teeth_len
    crowding = teeth_len - arch_len

    if debug > 1:
        print(lengths)
        print(locs)
        print(scale)
        print("there is %d mm of crowding" % round(crowding, 2))
        print("there is a %d pct archlength discrepancy" %
              round(100 - scale * 100, 2))

    #scale them to the right size
    for i, ob in enumerate(tooth_objects):

        if shift == 'FOSSA':
            delta = .05
        else:
            delta = 0
        #resize it
        ob.scale[0] *= scale + delta
        ob.scale[1] *= scale + delta
        ob.scale[2] *= scale + delta

        #find the location of interest we want?
        # bbox center, cusp tip? fossa/grove, incisal edge?
        #TODO:  odcutils.tooth_features(tooth,feature)  (world coords or local?)
        ob.location = Vector((0, 0, 0))
        if ob.rotation_mode != 'QUATERNION':
            ob.rotation_mode = 'QUATERNION'

        ob.rotation_quaternion = Quaternion((1, 0, 0, 0))

        #center line...we want palatinal face median point z,y with midpointx and center line min local z
        #buccal line...we want incisal edge median local y, maxlocal z, midpoing bbox x and buccal cusp max z?

        context.scene.objects.active = ob
        ob.select = True
        ob.hide = False

        ob.constraints.new('FOLLOW_PATH')
        path_constraint = ob.constraints["Follow Path"]
        path_constraint.target = arch
        path_constraint.use_curve_follow = True
        #find out if we cross the midline
        if sextant in ['MAX', 'MAND', 'UA', 'LA']:
            path_constraint.forward_axis = 'FORWARD_X'
            if int(curve_teeth[i]) > 20 and int(curve_teeth[i]) < 40:
                path_constraint.forward_axis = 'TRACK_NEGATIVE_X'
        else:
            path_constraint.forward_axis = 'FORWARD_X'

        path_constraint.offset = 100 * (-1 + locs[i] / teeth_len)

    #after arranging them on the curve, make a 2nd pass to spin them or not

    #decrease in number means mesial.  Except at midline.this will happen
    #we have constructed curve_teeth such that there will never be a non
    #integer change in adjacent list members. #eg,
    #quaternion rotation rules
    # Qtotal = Qa * Qb represtnts rotation b followed by rotation a
    #what we are doing is testing the occlusal direction of one tooth vs the arch occlusal direction
    context.scene.update()
    ob_dist = tooth_objects[1]
    ob_mes = tooth_objects[0]
    mesial = int(curve_teeth[1]) - int(
        curve_teeth[0]) == 1  #if true....distal numbers > mesial numbers
    vect = ob_mes.matrix_world * ob_mes.location - ob_dist.matrix_world * ob_dist.location
    spin = (vect.dot(ob_dist.matrix_world.to_quaternion() * Vector(
        (1, 0, 0))) < 0) == mesial

    tooth_occ = ob_mes.matrix_world.to_quaternion() * Vector((0, 0, 1))
    flip = tooth_occ.dot(occ_dir) > 0

    if debug:
        print('We will flip the teeth: %s. We will spin the teeth: %s.' %
              (str(flip), str(spin)))

    for ob in tooth_objects:
        if flip:
            ob.rotation_quaternion = Quaternion((0, 1, 0, 0))
        if spin:
            ob.rotation_quaternion = Quaternion(
                (0, 0, 0, 1)) * ob.rotation_quaternion

    for i, ob in enumerate(tooth_objects):

        if shift == 'BUCCAL':
            groups = [
                "Incisal Edge", "Distobuccal Cusp", "Mesiobuccal Cusp",
                "Buccal Cusp"
            ]
            inds = []
            for vgroup in groups:
                if vgroup in ob.vertex_groups:
                    inds += odcutils.vert_group_inds_get(context, ob, vgroup)
            max_z = 0
            max_ind = 0
            for j in inds:
                z = ob.data.vertices[j].co[2]
                if z > max_z:
                    max_ind = j
                    max_z = z
            tip = ob.data.vertices[max_ind].co
            tooth_shift = Vector(
                (0, tip[1] * ob.scale[1], tip[2] * ob.scale[2]))

            if sextant in [
                    'MAX', 'MAND', 'UA', 'LA'
            ]:  #no freakin idea why this is happening, but empirically, it's working
                tooth_shift[1] *= -1

            ob.location += (-1 + 2 * flip) * tooth_shift

        if shift == 'FOSSA':
            groups = ["Middle Fissure", "Palatinal Face"]
            inds = []
            for vgroup in groups:
                if vgroup in ob.vertex_groups and vgroup == "Middle Fissure":
                    inds += odcutils.vert_group_inds_get(context, ob, vgroup)

                    min_z = ob.dimensions[2]
                    min_ind = 0
                    for j in inds:
                        z = ob.data.vertices[j].co[2]
                        if z < min_z:
                            min_ind = j
                            min_z = z
                            depth = ob.data.vertices[min_ind].co
                            tooth_shift = Vector((0, depth[1] * ob.scale[1],
                                                  depth[2] * ob.scale[2]))

                elif vgroup in ob.vertex_groups and vgroup == "Palatinal Face":
                    inds += odcutils.vert_group_inds_get(context, ob, vgroup)
                    mx = Matrix.Identity(4)
                    com = odcutils.get_com(ob.data, inds, mx)
                    tooth_shift = odcutils.scale_vec_mult(
                        com, ob.matrix_world.to_scale())

            if sextant in [
                    'MAX', 'MAND', 'UA', 'LA'
            ]:  #no freakin idea why this is happening, but empirically, it's working
                tooth_shift[1] *= -1

            ob.location += (-1 + 2 * flip) * tooth_shift

    if limit:
        bpy.ops.object.select_all(action='DESELECT')
        for ob in delete_later:
            ob.select = True

        context.scene.objects.active = ob
        bpy.ops.object.delete()