Changeset 32


Ignore:
Timestamp:
Feb 25, 2010 3:29:55 PM (12 years ago)
Author:
vondreel
Message:

finished 1st round of ellipse fitting
need to do LS fitting

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/GSASII.py

    r31 r32  
    415415                        Data['tilt'] = 0.0
    416416                        Data['rotation'] = 0.0
    417                         Data['refine'] = [True,False,True,True,True]
    418417                        Data['showLines'] = False
    419418                        Data['ring'] = []
    420419                        Data['rings'] = []
     420                        Data['cutoff'] = 10
     421                        Data['pixLimit'] = 20
    421422                        Data['ellipses'] = []
    422423                        Data['masks'] = []
     
    426427                        Data['outChannels'] = 2500
    427428                        Data['fullIntegrate'] = False
     429                        Data['setRings'] = False
    428430                    Data['setDefault'] = False
    429431                    Data['range'] = [(Imin,Imax),[Imin,Imax]]
     
    11551157                    return
    11561158                Ypos = int(event.ydata)*self.imScale
    1157                 if event.key == 'c' and Data['refine'][0]:
     1159                if event.key == 'c':
    11581160                    cent = Data['center'] = [Xpos*pixelSize[0]/1000.,Ypos*pixelSize[1]/1000.] #convert to mm
    1159                     self.centText.SetValue(("%8.3f,%8.3f" % (cent[0],cent[1])))
    11601161                elif event.key == 'm':
    11611162                    xpos = Xpos*pixelSize[0]/1000.
     
    13141315            yring *= scaley
    13151316            ax.text(xring,yring,'+',color='r',ha='center',va='center',picker=3)
    1316 #        for ring in Data['rings']:
    1317 #            for xring,yring in ring:
    1318 #                xring *= scalex
    1319 #                yring *= scaley
    1320 #                ax.text(xring,yring,'+',ha='center',va='center')           
     1317        if Data['setRings']:
     1318            for ring in Data['rings']:
     1319                for xring,yring in ring:
     1320                    xring *= scalex
     1321                    yring *= scaley
     1322                    ax.text(xring,yring,'+',ha='center',va='center')           
    13211323        for ellipse in Data['ellipses']:
    13221324            cent,phi,[width,height] = ellipse
    1323             ax.add_artist(Ellipse([cent[0]*scalex,cent[1]*scaley],2*width*scalex,2*height*scalex,phi,fc=None))
     1325            ax.add_artist(Ellipse([cent[0]*scalex,cent[1]*scaley],2*width*scalex,2*height*scalex,phi,ec='r',fc=None))
    13241326        self.Img.axes.set_xlim(xlim)
    13251327        self.Img.axes.set_ylim(ylim)
  • trunk/GSASIIcomp.py

    r31 r32  
    12081208    cent = [(ell[1]*ell[4]-2.0*ell[2]*ell[3])/det, \
    12091209        (ell[1]*ell[3]-2.0*ell[0]*ell[4])/det]
    1210     phi = 0.5*atand(ell[1]/(ell[0]-ell[2]))
     1210    phi = 0.5*atand(ell[1]/(ell[0]-ell[2]+1e-32))
    12111211   
    12121212    a3 = 0.5*(ell[0]+ell[2]+(ell[0]-ell[2])/cosd(2.0*phi))
     
    12151215    if f3/a3 < 0 or f3/b3 < 0:
    12161216        return 0
    1217     if phi > 0.:       
    1218         sr1 = math.sqrt(f3/a3)
    1219         sr2 = math.sqrt(f3/b3)
    1220     else:
    1221         sr1 = math.sqrt(f3/a3)
    1222         sr2 = math.sqrt(f3/b3)
    1223         phi *= -1.       
     1217    sr1 = math.sqrt(f3/a3)
     1218    sr2 = math.sqrt(f3/b3)
     1219    if sr1 > sr2:
     1220        sr1,sr2 = SwapXY(sr1,sr2)
     1221        phi -= 90.
     1222        if phi < -90.:
     1223            phi += 180.
    12241224    return cent,phi,[sr1,sr2]
    12251225       
     
    12451245    w2 = w*2
    12461246    size = len(image)
    1247     if (w < Xpos < size-w) and (w < Ypos < size-w):
     1247    if (w < Xpos < size-w) and (w < Ypos < size-w) and image[Ypos,Xpos]:
    12481248        Z = image[Ypos-w:Ypos+w,Xpos-w:Xpos+w]
    12491249        Zmax = np.argmax(Z)
     
    12551255        return 0,0,0,0
    12561256   
    1257 def makeRing(ellipse,scalex,scaley,imScale,image):
    1258     cent,phi,semiradii = ellipse
     1257def makeRing(ellipse,pix,reject,scalex,scaley,imScale,image):
     1258    cent,phi,radii = ellipse
    12591259    cphi = cosd(phi)
    12601260    sphi = sind(phi)
    12611261    ring = []
    1262     for a in range(0,360):
    1263         x = semiradii[0]*cosd(a)
    1264         y = semiradii[1]*sind(a)
     1262    for a in range(0,360,2):
     1263        x = radii[0]*cosd(a)
     1264        y = radii[1]*sind(a)
    12651265        X = (cphi*x-sphi*y+cent[0])*scalex*imScale
    12661266        Y = (sphi*x+cphi*y+cent[1])*scaley*imScale
    1267         X,Y,I,J = ImageLocalMax(image,20,X,Y)     
    1268         if I and J:
     1267        X,Y,I,J = ImageLocalMax(image,pix,X,Y)     
     1268        if I and J and I/J > reject:
    12691269            X /= scalex*imScale
    12701270            Y /= scaley*imScale
    12711271            ring.append([X,Y])
    1272     if len(ring) < 75:             #want more than 1/3 of a circle
     1272    if len(ring) < 45:             #want more than 1/4 of a circle
    12731273        return []
    12741274    return ring
     1275   
     1276def calcDist(radii,tth):
     1277    stth = sind(tth)
     1278    ctth = cosd(tth)
     1279    ttth = tand(tth)
     1280    return math.sqrt(radii[0]**4/(ttth**2*((radii[0]*ctth)**2+(radii[1]*stth)**2)))
     1281   
     1282def calcZdisCosB(radius,tth,radii):
     1283    sinb = cosB = min(1.0,radii[0]**2/(radius*radii[1]))
     1284    cosb = math.sqrt(1.-sinb**2)
     1285    ttth = tand(tth)
     1286    zdis = radii[1]*ttth*cosb/sinb
     1287    return zdis,cosB
    12751288       
    12761289def ImageCalibrate(self,data):
     
    12811294    scalex = data['scalex']             # = 1000./(pixelSize[0]*self.imScale)
    12821295    scaley = data['scaley']
     1296    cutoff = data['cutoff']
    12831297    if len(ring) < 5:
    12841298        print 'not enough inner ring points for ellipse'
     
    12941308        return False
    12951309       
    1296     #setup 360 points on that ring for "good" fit
    1297     Ring = makeRing(ellipse,scalex,scaley,self.imScale,self.ImageZ)
     1310    #setup 180 points on that ring for "good" fit
     1311    Ring = makeRing(ellipse,20,cutoff,scalex,scaley,self.imScale,self.ImageZ)
    12981312    if Ring:
    12991313        ellipse = FitEllipse(Ring)
     
    13021316        return False
    13031317    print 'inner ring:',ellipse
    1304     data['center'] = ellipse[0]           #not right!! (but useful for now)
     1318    data['center'] = copy.copy(ellipse[0])           #not right!! (but useful for now)
    13051319    data['ellipses'].append(ellipse[:])
    13061320    self.PlotImage()
     
    13151329    A = cell2A(cell)
    13161330    wave = data['wavelength']
    1317     dist = data['distance']
    1318     refine = data['refine']             #flags for center, wavelength,distance, tilt angle, tilt rotation
     1331    cent = data['center']
     1332    pixLimit = data['pixLimit']
     1333    elcent,phi,radii = ellipse
    13191334    HKL = GenHBravais(0.5,Bravais,A)
    1320     dList = []
     1335    dsp = HKL[0][3]
     1336    tth = 2.0*asind(wave/(2.*dsp))
     1337    ttth = tand(tth)
     1338    data['distance'] = dist = calcDist(radii,tth)
     1339    radius = dist*tand(tth)
     1340    zdis,cosB = calcZdisCosB(radius,tth,radii)
     1341    sinp = sind(ellipse[1])
     1342    cosp = cosd(ellipse[1])
     1343    cent1 = []
     1344    cent2 = []
     1345    Zsign = 1.
     1346    xSum = 0
     1347    ySum = 0
     1348    phiSum = 0
     1349    tiltSum = 0
     1350    distSum = 0
     1351    Zsum = 0
    13211352    for i,H in enumerate(HKL):
    1322         cent,phi,semiradii = ellipse
    1323         meanRadius = semiradii[0]**2/semiradii[1]
    1324         dsp = 1.0/math.sqrt(calc_rDsq(H,A))
    1325         tth = 2.0*asind(wave/(2.*dsp))
    1326         radius = dist*tand(tth)
    1327         dList.append([dsp,radius])
    1328         semiradii[0] *= radius/meanRadius
    1329         semiradii[1] *= radius/meanRadius       
    1330         Ring = makeRing(ellipse,scalex,scaley,self.imScale,self.ImageZ)
     1353        dsp = H[3]
     1354        tth = 2.0*asind(0.5*wave/dsp)
     1355        stth = sind(tth)
     1356        ctth = cosd(tth)
     1357        ttth = tand(tth)
     1358        radius = dist*ttth
     1359        elcent,phi,radii = ellipse
     1360        radii[1] = dist*stth*ctth*cosB/(cosB**2-stth**2)
     1361        radii[0] = math.sqrt(radii[1]*radius*cosB)
     1362        zdis,cosB = calcZdisCosB(radius,tth,radii)
     1363        zdis *= Zsign
     1364        sinp = sind(phi)
     1365        cosp = cosd(phi)
     1366        cent = data['center']
     1367        elcent = [cent[0]+zdis*sinp,cent[1]-zdis*cosp]
     1368        ratio = radii[1]/radii[0]
     1369        Ring = makeRing(ellipse,pixLimit,cutoff,scalex,scaley,self.imScale,self.ImageZ)
    13311370        if Ring:
     1371            numZ = len(ring)
    13321372            data['rings'].append(Ring)
    1333             ellipse = FitEllipse(Ring)
    1334             cosb = (1.-ellipse[2][0]**2/ellipse[2][1]**2)*cosd(tth)**2
    1335             print 'for ring #',i,ellipse[1],cosb,(ellipse[2][0]**2/ellipse[2][1])/tand(tth)
     1373            elcent,phi,radii = ellipse = FitEllipse(Ring)
     1374            if abs(phi) > 45. and phi < 0.:
     1375                phi += 180.
     1376            dist = calcDist(radii,tth)
     1377            distR = 1.-dist/data['distance']
     1378            if distR > 0.001:
     1379                print 'Wavelength too large?'
     1380            elif distR < -0.001:
     1381                print 'Wavelength too small?'
     1382            else:
     1383                if abs((radii[1]/radii[0]-ratio)/ratio) > 0.01:
     1384                    print 'Bad fit for ring # %i. Try reducing Pixel search range'%(i)
     1385                    return False
     1386            zdis,cosB = calcZdisCosB(radius,tth,radii)
     1387            Tilt = acosd(cosB)
     1388            sinp = sind(ellipse[1])
     1389            cosp = cosd(ellipse[1])
     1390            cent1.append(np.array([elcent[0]+zdis*sinp,elcent[1]-zdis*cosp]))
     1391            cent2.append(np.array([elcent[0]-zdis*sinp,elcent[1]+zdis*cosp]))
     1392            dist1 = dist2 = 0
     1393            if i:
     1394                d1 = cent1[-1]-cent1[-2]
     1395                dist1 = np.dot(d1,d1)
     1396                d2 = cent2[-1]-cent2[-2]
     1397                dist2 = np.dot(d2,d2)
     1398                if dist2 > dist1:
     1399                    data['center'] = cent1[-1]
     1400                    Zsign *= -1.               
     1401                else:
     1402                    data['center'] = cent2[-1]
     1403                    Zsign = 1.
     1404                Zsum += numZ
     1405                phiSum += numZ*phi
     1406                distSum += numZ*dist
     1407                xSum += numZ*data['center'][0]
     1408                ySum += numZ*data['center'][1]
     1409                tiltSum += numZ*Tilt
     1410            cent = data['center']
     1411            print 'for ring # %i dist %.3f rotate %.2f tilt %.2f Xcent %.3f Ycent %.3f' \
     1412                %(i,dist,phi,Tilt,cent[0],cent[1])
    13361413            data['ellipses'].append(copy.deepcopy(ellipse))
    13371414            self.PlotImage()
    13381415        else:
    13391416            break
     1417    fullSize = len(self.ImageZ)/(self.imScale*scalex)
     1418    if 2*radii[1] < .9*fullSize:
     1419        print 'Are all usable rings (>25% visible) used? Try reducing Min ring I/Ib'
     1420    if Zsum:
     1421        data['center'] = [xSum/Zsum,ySum/Zsum]
     1422        data['tilt'] = tiltSum/Zsum
     1423        data['distance'] = distSum/Zsum
     1424        data['rotation'] = phiSum/Zsum
     1425        print data['center'],data['tilt'],data['distance'],data['rotation']
     1426    else:
     1427        print 'Only one ring fitted. Check your wavelength.'
     1428        return False
    13401429    self.PlotImage()
    1341    
    1342            
    1343    
    1344        
    1345    
    1346    
    1347        
    13481430       
    13491431    return True
  • trunk/GSASIIgrid.py

    r31 r32  
    11091109        data['calibrant'] = calSel.GetValue()
    11101110       
     1111    def OnPixLimit(event):
     1112        data['pixLimit'] = int(pixLimit.GetValue())
     1113       
    11111114    def OnMaxSlider(event):
    11121115        imax = max(data['range'][1][0],int(maxSel.GetValue()))
     
    11451148        distSel.SetValue("%8.3f"%(data['distance']))          #reset in case of error 
    11461149       
    1147     def OnImRefine(event):
    1148         ImageCalibRef[0] = centRef.GetValue()
    1149         ImageCalibRef[1] = waveRef.GetValue()
    1150         ImageCalibRef[2] = distRef.GetValue()
    1151         ImageCalibRef[3] = tiltRef.GetValue()
    1152         ImageCalibRef[4] = rotRef.GetValue()
    1153         SetStatusLine()
    1154                
     1150    def OnCutOff(event):
     1151        try:
     1152            cutoff = float(cutOff.GetValue())
     1153            data['cutoff'] = cutoff
     1154        except ValueError:
     1155            pass
     1156        cutOff.SetValue("%.1f"%(data['cutoff']))          #reset in case of error 
     1157       
    11551158    def OnShowLines(event):
    11561159        if data['showLines']:
     
    11801183            data['setDefault'] = True
    11811184           
     1185    def OnSetRings(event):
     1186        if data['setRings']:
     1187            data['setRings'] = False
     1188        else:
     1189            data['setRings'] = True
     1190        setRings.SetValue(data['setRings'])
     1191        self.PlotImage()
     1192           
    11821193    def OnClearCalib(event):
    11831194        data['ring'] = []
     
    11871198           
    11881199    def OnCalibrate(event):       
     1200        data['setRings'] = False
     1201        setRings.SetValue(data['setRings'])
    11891202        msg = \
    1190         '''Select > 5 points on inner ring of image pattern.
     1203        '''Select > 4 points on inner ring of image pattern.
    11911204        Click right mouse button to select point.
    11921205          Use left mouse button to delete point.
     
    12001213            Status.SetStatusText('Calibration successful')
    12011214            cent = data['center']
    1202             self.centText.SetValue(("%8.3f,%8.3f" % (cent[0],cent[1])))
     1215            centText.SetValue(("%8.3f,%8.3f" % (cent[0],cent[1])))
     1216            distSel.SetValue("%8.3f"%(data['distance']))
     1217            tiltSel.SetValue("%9.3f"%(data['tilt']))           
     1218            rotSel.SetValue("%9.3f"%(data['rotation']))
    12031219        else:
    12041220            Status.SetStatusText('Calibration failed')
     
    12081224       
    12091225    def SetStatusLine():
    1210         if data['refine'][0]:
    1211             Status.SetStatusText("On Image: key 'c' to mark center")
    1212         else:
    1213             Status.SetStatusText("")
     1226        Status.SetStatusText("On Image: key 'c' to mark center")
    12141227                             
    12151228    colorList = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
     
    12431256    mainSizer.Add(maxSizer,1,wx.EXPAND|wx.RIGHT)
    12441257   
    1245     comboSizer = wx.FlexGridSizer(2,4,5,5)
     1258    comboSizer = wx.BoxSizer(wx.HORIZONTAL)
    12461259    comboSizer.Add(wx.StaticText(parent=self.dataDisplay,label=' Color bar '),0,
    12471260        wx.ALIGN_CENTER_VERTICAL)
     
    12571270    calSel.Bind(wx.EVT_COMBOBOX, OnNewCalibrant)
    12581271    comboSizer.Add(calSel,0,wx.ALIGN_CENTER_VERTICAL)
     1272    comboSizer.Add(wx.StaticText(parent=self.dataDisplay,label=' Pixel search range '),0,
     1273        wx.ALIGN_CENTER_VERTICAL)
     1274    pixLimit = wx.ComboBox(parent=self.dataDisplay,value=str(data['pixLimit']),choices=['5','10','15','20'],
     1275        style=wx.CB_READONLY|wx.CB_DROPDOWN)
     1276    pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
     1277    comboSizer.Add(pixLimit,0,wx.ALIGN_CENTER_VERTICAL)
     1278    comboSizer.Add(wx.StaticText(parent=self.dataDisplay,label=' Min ring I/Ib '),0,
     1279        wx.ALIGN_CENTER_VERTICAL)
     1280    cutOff = wx.TextCtrl(parent=self.dataDisplay,value=("%.1f" % (data['cutoff'])),
     1281        style=wx.TE_PROCESS_ENTER)
     1282    cutOff.Bind(wx.EVT_TEXT_ENTER,OnCutOff)
     1283    comboSizer.Add(cutOff,0,wx.ALIGN_CENTER_VERTICAL)
     1284
    12591285    mainSizer.Add(comboSizer,0,wx.ALIGN_CENTER_HORIZONTAL)
    12601286    mainSizer.Add((5,5),0)
    12611287         
    1262     dataSizer = wx.FlexGridSizer(6,5,5,5)
    1263     dataSizer.Add((5,0),0)
     1288    dataSizer = wx.FlexGridSizer(6,4,5,5)
    12641289    dataSizer.Add(wx.StaticText(parent=self.dataDisplay,label=' Calibration coefficients'),0,
    12651290        wx.ALIGN_CENTER_VERTICAL)   
     
    12691294    dataSizer.Add((5,0),0)
    12701295   
    1271     ImageCalibRef = data['refine']
    12721296    dataSizer.Add(wx.StaticText(parent=self.dataDisplay,label=' Beam center X,Y'),0,
    12731297        wx.ALIGN_CENTER_VERTICAL)
    12741298    cent = data['center']
    1275     self.centText = wx.TextCtrl(parent=self.dataDisplay,value=("%8.3f,%8.3f" % (cent[0],cent[1])),style=wx.TE_READONLY)
    1276     dataSizer.Add(self.centText,0,wx.ALIGN_CENTER_VERTICAL)
    1277     centRef = wx.CheckBox(parent=self.dataDisplay,label='refine?')
    1278     centRef.Bind(wx.EVT_CHECKBOX, OnImRefine)
    1279     centRef.SetValue(ImageCalibRef[0])
    1280     dataSizer.Add(centRef,0,wx.ALIGN_CENTER_VERTICAL)
     1299    centText = wx.TextCtrl(parent=self.dataDisplay,value=("%8.3f,%8.3f" % (cent[0],cent[1])),style=wx.TE_READONLY)
     1300    dataSizer.Add(centText,0,wx.ALIGN_CENTER_VERTICAL)
    12811301   
    12821302    dataSizer.Add(wx.StaticText(parent=self.dataDisplay,label=' Inner/Outer radii'),0,
     
    12931313    waveSel.Bind(wx.EVT_TEXT_ENTER,OnWavelength)
    12941314    dataSizer.Add(waveSel,0,wx.ALIGN_CENTER_VERTICAL)
    1295     waveRef = wx.CheckBox(parent=self.dataDisplay,label='refine?')
    1296     waveRef.Bind(wx.EVT_CHECKBOX, OnImRefine)
    1297     waveRef.SetValue(ImageCalibRef[1])
    1298     dataSizer.Add(waveRef,0,wx.ALIGN_CENTER_VERTICAL)
    12991315         
    13001316    dataSizer.Add(wx.StaticText(parent=self.dataDisplay,label=' Start/End azimuth'),0,
     
    13101326    distSel.Bind(wx.EVT_TEXT_ENTER,OnDistance)
    13111327    dataSizer.Add(distSel,0,wx.ALIGN_CENTER_VERTICAL)
    1312     distRef = wx.CheckBox(parent=self.dataDisplay,label='refine?')
    1313     distRef.Bind(wx.EVT_CHECKBOX, OnImRefine)
    1314     distRef.SetValue(ImageCalibRef[2])
    1315     dataSizer.Add(distRef,0,wx.ALIGN_CENTER_VERTICAL)
    13161328
    13171329    dataSizer.Add(wx.StaticText(parent=self.dataDisplay,label=' No. bins'),0,
     
    13231335    dataSizer.Add(wx.StaticText(parent=self.dataDisplay,label=' Tilt angle'),0,
    13241336        wx.ALIGN_CENTER_VERTICAL)
    1325     self.tiltSel = wx.TextCtrl(parent=self.dataDisplay,value=("%9.3f"%(data['tilt'])),style=wx.TE_READONLY)
    1326     dataSizer.Add(self.tiltSel,0,wx.ALIGN_CENTER_VERTICAL)
    1327     tiltRef = wx.CheckBox(parent=self.dataDisplay,label='refine?')
    1328     tiltRef.Bind(wx.EVT_CHECKBOX, OnImRefine)
    1329     tiltRef.SetValue(ImageCalibRef[3])
    1330     dataSizer.Add(tiltRef,0,wx.ALIGN_CENTER_VERTICAL)
     1337    tiltSel = wx.TextCtrl(parent=self.dataDisplay,value=("%9.3f"%(data['tilt'])),style=wx.TE_READONLY)
     1338    dataSizer.Add(tiltSel,0,wx.ALIGN_CENTER_VERTICAL)
    13311339    showLines = wx.CheckBox(parent=self.dataDisplay,label='Show integration limits?')
    13321340    dataSizer.Add(showLines,0)
     
    13401348    dataSizer.Add(wx.StaticText(parent=self.dataDisplay,label=' Tilt rotation'),0,
    13411349        wx.ALIGN_CENTER_VERTICAL)
    1342     self.rotSel = wx.TextCtrl(parent=self.dataDisplay,value=("%9.3f"%(data['rotation'])),style=wx.TE_READONLY)
    1343     dataSizer.Add(self.rotSel,0,wx.ALIGN_CENTER_VERTICAL)
    1344     rotRef = wx.CheckBox(parent=self.dataDisplay,label='refine?')
    1345     rotRef.Bind(wx.EVT_CHECKBOX, OnImRefine)
    1346     rotRef.SetValue(ImageCalibRef[4])
    1347     dataSizer.Add(rotRef,0,)
     1350    rotSel = wx.TextCtrl(parent=self.dataDisplay,value=("%9.3f"%(data['rotation'])),style=wx.TE_READONLY)
     1351    dataSizer.Add(rotSel,0,wx.ALIGN_CENTER_VERTICAL)
    13481352    setDefault = wx.CheckBox(parent=self.dataDisplay,label='Use as default for all images?')
    13491353    dataSizer.Add(setDefault,0)
    13501354    setDefault.Bind(wx.EVT_CHECKBOX, OnSetDefault)
    13511355    setDefault.SetValue(data['setDefault'])
    1352     dataSizer.Add((10,5),0)
     1356    setRings = wx.CheckBox(parent=self.dataDisplay,label='Show ring picks?')
     1357    dataSizer.Add(setRings,0)
     1358    setRings.Bind(wx.EVT_CHECKBOX, OnSetRings)
     1359    setRings.SetValue(data['setRings'])
    13531360       
    13541361    mainSizer.Add(dataSizer,0)
     
    17411748    else:
    17421749       self.dataFrame = DataFrame(parent=self.mainPanel)
    1743            
     1750
     1751    self.dataFrame.Raise()           
    17441752    self.PickId = 0
    17451753    self.PatternId = 0
Note: See TracChangeset for help on using the changeset viewer.