(libhooke.py) fixed stupid algorithm for finding nearest contact point
[hooke.git] / generalvclamp.py
index d3adefd9bd2d9211b70f62a0913360d178415f48..f4351d0b504b31c7ce991ca93154d2557c730a14 100644 (file)
@@ -44,6 +44,8 @@ class generalvclampCommands:
         else:
             dx,unitx,dy,unity=self._delta(set=1)
             print str(dx*(10**9))+' nm'
+            to_dump='distance '+self.current.path+' '+str(dx*(10**9))+' nm'
+            self.outlet.push(to_dump)
 
 
     def do_force(self,args):
@@ -59,6 +61,8 @@ class generalvclampCommands:
             return
         dx,unitx,dy,unity=self._delta(set=1)
         print str(dy*(10**12))+' pN'
+        to_dump='force '+self.current.path+' '+str(dy*(10**12))+' pN'
+        self.outlet.push(to_dump)
         
         
     def do_forcebase(self,args):
@@ -114,6 +118,8 @@ class generalvclampCommands:
         avg=np.mean(to_average)
         forcebase=abs(y-avg)
         print str(forcebase*(10**12))+' pN'
+        to_dump='forcebase '+self.current.path+' '+str(forcebase*(10**12))+' pN'
+        self.outlet.push(to_dump)
         
     
     def plotmanip_flatten(self, plot, current, customvalue=False):
@@ -162,8 +168,9 @@ class generalvclampCommands:
                     valn[exponent]=sp.polyfit(x_ext,y_ext,exponent)
                     yrn[exponent]=sp.polyval(valn[exponent],x_ret)
                     errn[exponent]=sp.sqrt(sum((yrn[exponent]-y_ext)**2)/float(len(y_ext)))
-                except TypeError:
+                except Exception,e:
                     print 'Cannot flatten!'
+                    print e
                     return plot
 
             best_exponent=errn.index(min(errn))
@@ -221,11 +228,17 @@ class generalvclampCommands:
         
         # Calls the function linefit_between
         parameters=[0,0,[],[]]
-        parameters=self.linefit_between(clickedpoints[0],clickedpoints[1])
+        try:
+            parameters=self.linefit_between(clickedpoints[0],clickedpoints[1])
+        except:
+            print 'Cannot fit. Did you click twice the same point?'
+            return
           
         # Outputs the relevant slope parameter
         print 'Slope:'
         print str(parameters[0])
+        to_dump='slope '+self.current.path+' '+str(parameters[0])
+        self.outlet.push(to_dump)
                 
         # Makes a vector with the fitted parameters and sends it to the GUI
         xtoplot=parameters[2]
@@ -272,60 +285,56 @@ class generalvclampCommands:
 
         return (linefit[0],linefit[1],xtofit,ytofit)
     
+    
+    
 #====================
 #AUTOMATIC ANALYSES
 #====================
+    '''
     def do_autopeak(self,args):
-        '''
-        AUTOPEAK
-        (generalvclamp.py)
-        Automatically performs a number of analyses on the peaks of the given curve.
-        Currently it automatically:
-        - fits peaks with WLC function
-        - measures peak maximum forces with a baseline
-        - measures slope in proximity of peak maximum
-        Requires flatten plotmanipulator , fit.py plugin , flatfilts.py plugin with convfilt
-        
-        Syntax:
-        autopeak [rebase] [pl=value] [t=value] [noauto] [reclick]
+        #FIXME: this function is too long. split it and make it rational.
+        #FIXME: also, *generalize fits* to allow FJC and any other model in the future!
         
-        rebase : Re-asks baseline interval
-        
-        pl=[value] : Use a fixed persistent length for the fit. If pl is not given, 
-                     the fit will be a 2-variable  
-                     fit. DO NOT put spaces between 'pl', '=' and the value.
-                     The value must be in meters. 
-                     Scientific notation like 0.35e-9 is fine.
-        
-        t=[value] : Use a user-defined temperature. The value must be in
-                    kelvins; by default it is 293 K.
-                    DO NOT put spaces between 't', '=' and the value.
-        
-        noauto : allows for clicking the contact point by 
-                 hand (otherwise it is automatically estimated) the first time.
-                 If subsequent measurements are made, the same contact point
-                 clicked the first time is used
-        
-        reclick : redefines by hand the contact point, if noauto has been used before
-                  but the user is unsatisfied of the previously choosen contact point.
-        
-        When you first issue the command, it will ask for the filename. If you are giving the filename
-        of an existing file, autopeak will resume it and append measurements to it. If you are giving
-        a new filename, it will create the file and append to it until you close Hooke.
-        
-        Useful variables (to set with SET command):
-        
-        temperature= temperature of the system for wlc fit (in K)
-        auto_fit_points = number of points to fit before the peak maximum, for wlc
-        auto_slope_span = number of points on which measure the slope, for slope
-        '''
+        def fit_interval_nm(start_index,plot,nm,backwards):
+    '''
+            
+    '''
+            Calculates the number of points to fit, given a fit interval in nm
+            start_index: index of point
+            plot: plot to use
+            backwards: if true, finds a point backwards.
+    '''
+            
+    '''
+            x_vect=plot.vectors[1][0]
+            
+            c=0
+            i=start_index
+            start=x_vect[start_index]
+            maxlen=len(x_vect)
+            while abs(x_vect[i]-x_vect[start_index])*(10**9) < nm:
+                if i==0 or i==maxlen-1: #we reached boundaries of vector!
+                    return c
+                
+                if backwards:
+                    i-=1
+                else:
+                    i+=1
+                c+=1
+            return c
+            
         
         pl_value=None
         T=self.config['temperature']
         
-        fit_points=int(self.config['auto_fit_points'])
+        if 'usepoints' in args.split():
+            fit_points=int(self.config['auto_fit_points'])
+            usepoints=True
+        else:
+            fit_points=None
+            usepoints=False
+            
         slope_span=int(self.config['auto_slope_span'])
-        
         delta_force=10
         rebase=False #if true=we select rebase
         
@@ -346,24 +355,24 @@ class generalvclampCommands:
             if ('t=' in arg[0:2]) or ('T=' in arg[0:2]):
                 t_expression=arg.split('=')
                 T=float(t_expression[1])
-               
+                              
         #Handle contact point arguments
-        #FIXME: code duplication
-        if 'reclick' in args.split():
-            print 'Click contact point'
+        def pickup_contact_point():
+            #macro to pick up the contact point by clicking
             contact_point=self._measure_N_points(N=1, whatset=1)[0]
             contact_point_index=contact_point.index
             self.wlccontact_point=contact_point
             self.wlccontact_index=contact_point.index
             self.wlccurrent=self.current.path
+            return contact_point, contact_point_index
+                
+        if 'reclick' in args.split():
+            print 'Click contact point'
+            contact_point, contact_point_index = pickup_contact_point()
         elif 'noauto' in args.split():
             if self.wlccontact_index==None or self.wlccurrent != self.current.path:
                 print 'Click contact point'
-                contact_point=self._measure_N_points(N=1, whatset=1)[0]
-                contact_point_index=contact_point.index
-                self.wlccontact_point=contact_point
-                self.wlccontact_index=contact_point.index
-                self.wlccurrent=self.current.path
+                contact_point , contact_point_index = pickup_contact_point()
             else:
                 contact_point=self.wlccontact_point
                 contact_point_index=self.wlccontact_index
@@ -376,27 +385,50 @@ class generalvclampCommands:
             contact_point.is_marker=True
         
         
+        #Find peaks.
+        defplot=self.current.curve.default_plots()[0]
+        flatten=self._find_plotmanip('flatten') #Extract flatten plotmanip
+        defplot=flatten(defplot, self.current, customvalue=1) #Flatten curve before feeding it to has_peaks
+        peak_location,peak_size=self.has_peaks(defplot, self.convfilt_config['mindeviation'])
+        
+        #Create a new plot to send
+        fitplot=copy.deepcopy(displayed_plot)
+        
         #Pick up force baseline
         whatset=1 #fixme: for all sets
         if 'rebase' in args or (self.basecurrent != self.current.path):
             rebase=True               
         if rebase:
-            print 'Select baseline'
-            self.basepoints=self._measure_N_points(N=2, whatset=whatset)
+            clicks=self.config['baseline_clicks']
+            if clicks==0:
+                self.basepoints=[]
+                self.basepoints.append(ClickedPoint())
+                self.basepoints.append(ClickedPoint())
+                self.basepoints[0].index=peak_location[-1]+fit_interval_nm(peak_location[-1], displayed_plot, self.config['auto_right_baseline'],False)
+                self.basepoints[1].index=self.basepoints[0].index+fit_interval_nm(self.basepoints[0].index, displayed_plot, self.config['auto_left_baseline'],False)
+                for point in self.basepoints:
+                    #for graphing etc. purposes, fill-in with coordinates
+                    point.absolute_coords=displayed_plot.vectors[1][0][point.index], displayed_plot.vectors[1][1][point.index]
+                    point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])
+            elif clicks>0:
+                print 'Select baseline'
+                if clicks==1:
+                    self.basepoints=self._measure_N_points(N=1, whatset=whatset)
+                    self.basepoints.append(ClickedPoint())
+                    self.basepoints[1].index=self.basepoints[0].index+fit_interval_nm(self.basepoints[0].index, displayed_plot, self.config['auto_left_baseline'], False)
+                    #for graphing etc. purposes, fill-in with coordinates
+                    self.basepoints[1].absolute_coords=displayed_plot.vectors[1][0][self.basepoints[1].index], displayed_plot.vectors[1][1][self.basepoints[1].index]
+                    self.basepoints[1].find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])
+                else:
+                    self.basepoints=self._measure_N_points(N=2, whatset=whatset)
+            
             self.basecurrent=self.current.path
+        
         boundaries=[self.basepoints[0].index, self.basepoints[1].index]
         boundaries.sort()
         to_average=displayed_plot.vectors[1][1][boundaries[0]:boundaries[1]] #y points to average
         avg=np.mean(to_average)
         
-        #Find peaks.
-        defplot=self.current.curve.default_plots()[0]
-        flatten=self._find_plotmanip('flatten') #Extract flatten plotmanip
-        defplot=flatten(defplot, self.current, customvalue=1) #Flatten curve before feeding it to has_peaks
-        peak_location,peak_size=self.has_peaks(defplot, self.convfilt_config['mindeviation'])
-        
-        #Create a new plot to send
-        fitplot=copy.deepcopy(displayed_plot)
         
         #Initialize data vectors
         c_lengths=[]
@@ -404,6 +436,8 @@ class generalvclampCommands:
         forces=[]
         slopes=[]
         
+        
+        
         #Cycle between peaks and do analysis.
         for peak in peak_location:
             #Do WLC fits.
@@ -412,12 +446,21 @@ class generalvclampCommands:
             peak_point=ClickedPoint()
             peak_point.absolute_coords=displayed_plot.vectors[1][0][peak], displayed_plot.vectors[1][1][peak]
             peak_point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])    
+            
+            if not usepoints:
+                fit_points=fit_interval_nm(peak, displayed_plot, self.config['auto_fit_nm'], True)
+            
             #-create a clicked point for the other fit point
             other_fit_point=ClickedPoint()
             other_fit_point.absolute_coords=displayed_plot.vectors[1][0][peak-fit_points], displayed_plot.vectors[1][1][peak-fit_points]
             other_fit_point.find_graph_coords(displayed_plot.vectors[1][0], displayed_plot.vectors[1][1])    
             #do the fit
             points=[contact_point, peak_point, other_fit_point]
+            
+            #Check if we have enough points for a fit. If not, wlc_fit could crash
+            if abs(peak_point.index-other_fit_point.index) < 2:
+                continue
+            
             params, yfit, xfit = self.wlc_fit(points, displayed_plot.vectors[1][0], displayed_plot.vectors[1][1],pl_value,T)
             #save wlc values (nm)
             c_lengths.append(params[0]*(1.0e+9))
@@ -427,11 +470,12 @@ class generalvclampCommands:
                 p_lengths.append(pl_value)
             #Add WLC fit lines to plot
             fitplot.add_set(xfit,yfit)
+            
             if len(fitplot.styles)==0:
                 fitplot.styles=[]
             else:
                 fitplot.styles.append(None)
-
             #Measure forces
             delta_to_measure=displayed_plot.vectors[1][1][peak-delta_force:peak+delta_force]
             y=min(delta_to_measure)
@@ -441,7 +485,11 @@ class generalvclampCommands:
             #Measure slopes
             slope=self.linefit_between(peak-slope_span,peak)[0]
             slopes.append(slope)
-                            
+        
+        #--DEBUG STUFF--
+        fitplot.add_set([self.basepoints[0].graph_coords[0],self.basepoints[1].graph_coords[0]],[self.basepoints[0].graph_coords[1],self.basepoints[1].graph_coords[1]]) 
+        fitplot.styles.append('scatter')
+        
         #Show wlc fits and peak locations
         self._send_plot([fitplot])
         self.do_peaks('')
@@ -476,7 +524,7 @@ class generalvclampCommands:
         
         #Save file info
         if self.autofile=='':
-            self.autofile=raw_input('Filename? (return to ignore) ')
+            self.autofile=raw_input('Autopeak filename? (return to ignore) ')
             if self.autofile=='':
                 print 'Not saved.'
                 return
@@ -497,4 +545,4 @@ class generalvclampCommands:
             
         f.close()
         self.do_note('autopeak')
-        
\ No newline at end of file
+        '''
\ No newline at end of file