source: sans/Dev/trunk/NCNR_User_Procedures/Analysis/Alpha/Tinker/FFT_Panel.ipf @ 942

Last change on this file since 942 was 942, checked in by srkline, 9 years ago

Some cleanup of the FFT routines to be more exact in declaring mat

Rearranged the Event mode panel so that it's a little more obvious what to do, and in what order

Cleaned up the examples for the Simulation Scripting.

File size: 35.3 KB
Line 
1#pragma rtGlobals=1             // Use modern global access method.
2
3//
4// utility functions and procedures for displaying information
5// setting the matrix, and doing the calculations
6//
7
8
9//
10// TO DO:
11// x- I need to change a lot of routines (most notably Gizmo) to take "10" as the default SLD
12//               rather than "1"
13//
14//
15// -- incorporate utility function that reads the wave note from a binary file
16//                      Function/S LoadNoteFunc(PName,FName[,FileRef]) - use this as an "inspector?"
17////////////
18
19//
20
21//***********
22//For a 3D slicer view of dmat:
23//
24//Duplicate/O dmat dmatView
25//
26//Open a New 3D voxelgram. Don't set any of the levels
27//Open the 3D Slicer under the Gizmo menu
28//Make the X, Y, and Z slices
29//(Yellow Hot seems to be the best)
30//
31//// for a better view
32//dmatView = log(dmat)
33//
34//// if it all goes blank after the log transform get rid of the INF
35//dmatView = numtype(dmatView)== 0 ? dmatView : 0
36//
37//*************
38
39
40///PANEL
41///// panel procedures
42
43Proc Init_FFT()
44        DoWindow/F FFT_Panel
45        if(V_flag==0)
46                SetDataFolder root:
47                //init the globals
48                Variable/G root:FFT_N=128
49                Variable/G root:FFT_T=5
50                Variable/G root:FFT_Qmax = 0
51                Variable/G root:FFT_QmaxReal = 0
52                Variable/G root:FFT_DQ=0
53                Variable/G root:FFT_Qmin=0
54                Variable/G root:FFT_estTime = 0
55               
56                Variable/G root:FFT_SolventSLD = 0
57                Variable/G root:FFT_delRho = 1e-7                       //multiplier for SLD (other value is 1e-7)
58               
59                FFT_Qmax :=2*pi/FFT_T
60                FFT_QmaxReal := FFT_Qmax/2
61                FFT_DQ := pi/(FFT_N*FFT_T)
62                FFT_Qmin := 2*pi/(FFT_N*FFT_T)
63                //empirical fit (cubic) of time vs N=50 to N=256
64                FFT_estTime := 0.56 - 0.0156*FFT_N + 0.000116*FFT_N^2 + 8e-7*FFT_N^3
65//              FFT_estTime := FFT_N/128
66               
67                FFT_Panel()
68        endif
69End
70
71
72Proc FFT_Panel()
73        PauseUpdate; Silent 1           // building window...
74        NewPanel /W=(1452,44,1768,531)/K=1 as "FFT_Panel"
75        DoWindow/C FFT_Panel
76        SetDrawLayer UserBack
77        DrawLine 5,68,311,68
78        DrawLine 5,142,311,142
79        DrawLine 5,250,311,250
80        SetVariable FFTSetVar_0,pos={7,7},size={150,15},title="Cells per edge (N)"
81        SetVariable FFTSetVar_0,limits={50,512,2},value= FFT_N,live= 1
82        SetVariable FFTSetVar_1,pos={7,27},size={150,15},title="Length per Cell (T)"
83        SetVariable FFTSetVar_1,limits={1,5000,0.2},value= FFT_T,live= 1
84        SetVariable FFTSetVar_2,pos={183,7},size={120,15},title="Real Qmax"
85        SetVariable FFTSetVar_2,limits={0,0,0},value= FFT_QmaxReal,noedit= 1,live= 1
86        SetVariable FFTSetVar_3,pos={183,47},size={120,15},title="delta Q (A)"
87        SetVariable FFTSetVar_3,limits={0,0,0},value= FFT_DQ,noedit= 1,live= 1
88        SetVariable FFTSetVar_6,pos={183,27},size={120,15},title="Real Qmin (A)"
89        SetVariable FFTSetVar_6,limits={0,0,0},value= FFT_Qmin,noedit= 1,live= 1
90        Button FFTButton_0,pos={15,79},size={90,20},proc=FFT_MakeMatrixButtonProc,title="Make Matrix"
91        Button FFTButton_1,pos={14,157},size={90,20},proc=FFTMakeGizmoButtonProc,title="Make Gizmo"
92        Button FFTButton_2,pos={14,187},size={100,20},proc=FFTDrawSphereButtonProc,title="Draw Sphere"
93        Button FFTButton_3,pos={14,265},size={70,20},proc=DoTheFFT_ButtonProc,title="Do FFT"
94        Button FFTButton_4,pos={180,264},size={130,20},proc=FFT_PlotResultsButtonProc,title="Plot FFT Results"
95        Button FFTButton_5,pos={13,218},size={120,20},proc=FFTDrawZCylinderButtonProc,title="Draw Cylinder"
96//      Button FFTButton_6,pos={134,79},size={90,20},proc=FFTEraseMatrixButtonProc,title="Erase Matrix"
97        Button FFTButton_6a,pos={140,79},size={50,20},proc=FFTSaveMatrixButtonProc,title="Save"
98        Button FFTButton_6b,pos={200,79},size={50,20},proc=FFTLoadMatrixButtonProc,title="Load"
99        Button FFTButton_6c,pos={260,79},size={50,20},proc=FFT_AddMatrixButtonProc,title="Add"
100        Button FFTButton_7,pos={13,329},size={130,20},proc=FFT_BinnedSpheresButtonProc,title="Do Binned Debye"
101        Button FFTButton_7a,pos={180,329},size={130,20},proc=FFT_PlotResultsButtonProc,title="Plot Binned Results"
102
103        Button FFTButton_8,pos={13,297},size={130,20},proc=FFT_AltiSpheresButtonProc,title="Do Debye Spheres"
104        Button FFTButton_8a,pos={180,297},size={130,20},proc=FFT_PlotResultsButtonProc,title="Plot Debye Results"
105
106        Button FFTButton_14,pos={13,360},size={130,20},proc=FFT_BinnedSLDButtonProc,title="Do Binned SLD"
107        Button FFTButton_14a,pos={180,360},size={130,20},proc=FFT_PlotResultsButtonProc,title="Plot SLD Results"
108
109        SetVariable FFTSetVar_4,pos={7,47},size={100,15},title="FFT time(s)"
110        SetVariable FFTSetVar_4,limits={0,0,0},value= FFT_estTime,noedit= 1,live= 1,format="%d"
111        Button FFTButton_9,pos={200,400},size={100,20},proc=FFT_Get2DSlice,title="Get 2D Slice"
112
113        Button FFTButton_19,pos={168,150},size={130,20},proc=FFT_ChangeMatrixValuesButton,title="Replace Voxels"
114        Button FFTButton_12,pos={168,175},size={130,20},proc=FFT_ReplaceSolventButton,title="Replace Solvent"
115        Button FFTButton_11,pos={169,200},size={130,20},proc=FFT_RotateMat,title="Rotate Matrix"
116        Button FFTButton_10,pos={169,225},size={130,20},proc=FFT_TransposeMat,title="Transpose Matrix"
117
118        Button FFTButton_13,pos={14,109},size={120,20},proc=FFTFillSolventMatrixProc,title="Solvent Matrix"
119        SetVariable FFTSetVar_5,pos={155,111},size={150,15},title="Solvent SLD (10^-7)"
120        SetVariable FFTSetVar_5,limits={-99,99,1},value= FFT_SolventSLD,live= 1
121        Button FFTButton_15,pos={209,430},size={90,20},proc=Interp2DSliceButton,title="Interp 2D"
122        Button FFTButton_16,pos={14,460},size={70,20},proc=FFTHelpButton,title="Help"
123       
124        Button FFTButton_17,pos={13,400},size={120,20},proc=FFT_Iso2USANS,title="Iso to USANS"
125        Button FFTButton_18,pos={13,430},size={120,20},proc=FFT_Aniso2USANS,title="Aniso to USANS"
126
127EndMacro
128
129// Save a matrix wave, plus the N, T, and solvent values in the wave note for reloading
130Function FFTSaveMatrixButtonProc(ba) : ButtonControl
131        STRUCT WMButtonAction &ba
132       
133        String win = ba.win
134
135        switch (ba.eventCode)
136                case 2:
137                        // click code here
138                        String fileStr=""
139                        SaveMyMatrix(fileStr)
140                       
141                        break
142        endswitch
143
144        return 0
145End
146
147// this will wave as Igor Binary, so be sure to use the ".ibw extension.
148// - this could possibly be enforced, but that's maybe not necessary at this stage.
149//
150Function SaveMyMatrix(fileStr)
151        String fileStr
152       
153        WAVE mat=root:mat
154        NVAR FFT_T = root:FFT_T
155        NVAR FFT_N = root:FFT_N
156        NVAR FFT_SolventSLD = root:FFT_SolventSLD
157        String str=""
158        sprintf str,"FFT_T=%g;FFT_N=%d;FFT_SolventSLD=%d;",FFT_T,FFT_N,FFT_SolventSLD
159        Note mat,str
160        Save/C/P=home mat as fileStr    //will ask for a file name if fileStr="" save as Igor Binary
161        Note/K mat                      //kill wave note on exiting since I don't properly update this anywhere else
162                       
163        return(0)
164end
165
166
167// load in a previously saved matrix, and reset FFT_N, FFT_T and solvent
168// from the wave note when saved
169Function FFTLoadMatrixButtonProc(ba) : ButtonControl
170        STRUCT WMButtonAction &ba
171       
172        String win = ba.win
173
174        switch (ba.eventCode)
175                case 2:
176                        // click code here
177                        String fileStr=""
178                        ReloadMatrix(fileStr)
179                       
180                        break
181        endswitch
182
183        return 0
184End
185
186// /H flag on the LoadWave command severs the connection with the binary file
187// -- this seems to be important - otherwise I get odd results and the wave (on disk) can change!
188//
189Function ReloadMatrix(fileStr)
190        String fileStr
191       
192        LoadWave/H/M/O/W/P=home         fileStr         //will ask for a file, Igor Binary format is assumed here
193        if(V_flag == 0)
194                return(0)               //user cancel
195        endif
196       
197        String str
198        str=note(mat)
199        NVAR FFT_T = root:FFT_T
200        NVAR FFT_N = root:FFT_N
201        NVAR FFT_SolventSLD = root:FFT_SolventSLD
202       
203        FFT_T = NumberByKey("FFT_T", str, "=" ,";")
204        FFT_N = NumberByKey("FFT_N", str, "=" ,";")
205        FFT_SolventSLD = NumberByKey("FFT_SolventSLD", str, "=" ,";")
206
207// if I got bad values, put in default values                   
208        if(numtype(FFT_T) != 0 )
209                FFT_T = 5
210        endif
211        if(numtype(FFT_N) != 0 )
212                FFT_N = DimSize(mat,0)
213        endif
214        if(numtype(FFT_SolventSLD) != 0 )
215                FFT_SolventSLD = 0
216        endif                   
217
218        ColorizeGizmo()
219       
220        Print "Loaded matrix parameters = ",str
221        Execute "NumberOfPoints()"
222                       
223        return(0)
224end
225
226// load in a previously saved matrix, and reset FFT_N, FFT_T and solvent
227// from the wave note when saved
228Function FFT_AddMatrixButtonProc(ba) : ButtonControl
229        STRUCT WMButtonAction &ba
230       
231        String win = ba.win
232
233        switch (ba.eventCode)
234                case 2:
235                        // click code here
236                        String fileStr=""
237                        LoadAndAddMatrix(fileStr)
238                       
239                        break
240        endswitch
241
242        return 0
243End
244
245// This function will load and add the matrix to whatever is currently present
246// - it is checked that the N, T, and SolventSLD are the same
247//
248// - it currently SUMS the voxels - so if there is overlap, you get a new value
249//
250Function LoadAndAddMatrix(fileStr)
251        String fileStr
252
253        String toLoadStr=""
254
255// if the passed in file name is null, pick a file     
256        if(strlen(fileStr)==0)
257                toLoadStr = DoOpenFileDialog("Pick the binary file to load")
258                if(strlen(toLoadStr)==0)
259                        return(0)
260                endif
261        endif
262        fileStr=toLoadStr
263
264        // make sure that N and T are correct, just read in the note
265        String noteStr=LoadNoteFunc("home",fileStr)
266        String abortStr
267       
268        // current values
269        NVAR FFT_T = root:FFT_T
270        NVAR FFT_N = root:FFT_N
271        NVAR FFT_SolventSLD = root:FFT_SolventSLD
272
273// possible set to load
274        Variable test_T,test_N,test_SolventSLD
275        test_T = NumberByKey("FFT_T", noteStr, "=" ,";")
276        test_N = NumberByKey("FFT_N", noteStr, "=" ,";")
277        test_SolventSLD = NumberByKey("FFT_SolventSLD", noteStr, "=" ,";")
278       
279// if I got bad values, warn and abort                 
280        if(FFT_T != test_T)
281                abortStr = "Current T = "+num2str(FFT_T)+" and Selected T = "+num2str(test_T)+", aborting load"
282                Abort abortStr
283        endif
284        if(FFT_N != test_N)
285                abortStr = "Current N = "+num2str(FFT_N)+" and Selected N = "+num2str(test_N)+", aborting load"
286                Abort abortStr
287        endif
288        if(FFT_SolventSLD != test_SolventSLD)
289                abortStr = "Current SolventSLD = "+num2str(FFT_SolventSLD)+" and Selected SolventSLD = "+num2str(test_SolventSLD)+", aborting load"
290                Abort abortStr
291        endif   
292               
293        // OK, then load and add the matrix
294        // the loaded matrix is "mat" as usual, so make a copy of the current first
295        Duplicate/O mat tmpMat
296        LoadWave/H/M/O/W/P=home         fileStr         //will ask for a file, Igor Binary format is assumed here
297        if(V_flag == 0)
298                return(0)               //user cancel
299        endif
300       
301        Wave mat=root:mat
302        FastOp mat = mat + tmpMat
303       
304        KillWaves/Z tmpMat
305       
306        Print "Loaded matrix parameters = ",noteStr
307        Execute "NumberOfPoints()"
308       
309        ColorizeGizmo()
310               
311        return(0)
312end
313
314// utility function that reads the wave note from a binary file
315// where did I get this from? Igor Exchange? I didn't write this myself...
316//
317Function/S LoadNoteFunc(PName,FName[,FileRef])
318        String PName, FName
319        Variable FileRef
320       
321        Variable noteStart, noteLength, version, dependLength
322        String noteStr, typeStr = ".ibw"
323        if (ParamIsDefault(FileRef))
324                Open/R/P=$PName/T=typeStr fileRef, as FName     //open the file
325        endif
326        FSetPos fileRef, 0
327        FBinRead/F=2 fileRef, version
328       
329       
330        if (version == 5)
331       
332                FSetPos fileRef, 4
333                Make/N=(3)/I/Free SizeWave
334                FBinRead FileRef,SizeWave
335                noteStart = SizeWave[0]
336                DependLength = SizeWave[1]
337                NoteLength = SizeWave[2]
338                noteStart += dependLength+64
339               
340        elseif (version == 2)
341       
342                FBinRead/F=3 fileRef, noteStart
343//              FBinRead/F=4 fileRef, dependLength
344                FBinRead/F=3 fileRef, noteLength
345                noteStart += 16
346               
347        else
348       
349                if (ParamIsDefault(FileRef))
350                        Close(FileRef)          //close the file
351                endif
352                return ""
353       
354        endif
355        if (!NoteLength)
356                if (ParamIsDefault(FileRef))
357                        Close(FileRef)          //close the file
358                endif
359                return("")
360        endif
361        FSetPos fileRef, noteStart
362        NoteStr = PadString("",NoteLength,0)
363        FBinRead FileRef,NoteStr
364       
365        if (ParamIsDefault(FileRef))
366                Close(FileRef)          //close the file
367        endif
368        return noteStr
369       
370End //LoadNoteFunc
371
372
373Function FFTHelpButton(ba) : ButtonControl
374        STRUCT WMButtonAction &ba
375       
376        String win = ba.win
377
378        switch (ba.eventCode)
379                case 2:
380                        // click code here
381                        DisplayHelpTopic/Z/K=1 "Real-Space Modeling of SANS Data"
382                        if(V_flag !=0)
383                                DoAlert 0,"The Real-Space Modeling Help file could not be found"
384                        endif
385                        break
386        endswitch
387
388        return 0
389End
390
391
392Function Interp2DSliceButton(ba) : ButtonControl
393        STRUCT WMButtonAction &ba
394
395        switch( ba.eventCode )
396                case 2: // mouse up
397                        // click code here
398                        String folderStr=""
399                        Prompt folderStr, "Pick a 2D data folder",popup,W_DataSetPopupList()            // Set prompt for x param
400                        DoPrompt "Enter data folder", folderStr
401                        if (V_Flag || strlen(folderStr) == 0)
402                                return -1                                                               // User canceled, null string entered
403                        endif                   
404       
405                        Interpolate2DSliceToData(folderStr)
406                       
407                        //display the 2D plane
408                        DoWindow FFT_Interp2D_log
409                        if(V_flag == 0)
410                                Execute "Display2DInterpSlice_log()"
411                        endif
412                       
413                        break
414        endswitch
415
416        return 0
417End
418
419
420
421Function FFT_Get2DSlice(ctrlName) : ButtonControl
422        String ctrlName
423               
424        WAVE/Z dmat = root:dmat
425        if(WaveExists(dmat)==1)
426                get2DSlice(dmat)
427        endif
428       
429        //display the 2D plane
430        DoWindow FFT_Slice2D
431        if(V_flag == 0)
432                Execute "Display2DSlice()"
433        endif
434       
435        //display the 2D plane
436        DoWindow FFT_Slice2D_log
437        if(V_flag == 0)
438                Execute "Display2DSlice_log()"
439        endif
440       
441        //display the 1D binning of the 2D plane
442        DoWindow FFT_Slice1D
443        if(V_flag == 0)
444                Execute "Slice2_1D()"
445        endif
446       
447        return(0)       
448End
449
450Proc Display2DSlice()
451        PauseUpdate; Silent 1           // building window...
452        Display /W=(1038,44,1404,403)
453        DoWindow/C FFT_Slice2D
454        AppendImage/T detPlane
455        ModifyImage detPlane ctab= {*,*,YellowHot,0}
456        ModifyGraph margin(left)=14,margin(bottom)=14,margin(top)=14,margin(right)=14
457        ModifyGraph mirror=2
458        ModifyGraph nticks=4
459        ModifyGraph minor=1
460        ModifyGraph fSize=9
461        ModifyGraph standoff=0
462        ModifyGraph tkLblRot(left)=90
463        ModifyGraph btLen=3
464        ModifyGraph tlOffset=-2
465//      SetAxis/A/R left
466End
467
468Proc Display2DInterpSlice_log()
469        PauseUpdate; Silent 1           // building window...
470        Display /W=(1038,44,1404,403)
471        DoWindow/C FFT_Interp2D_log
472        AppendImage/T interp2DSlice_log
473        ModifyImage interp2DSlice_log ctab= {*,*,YellowHot,0}
474        ModifyGraph margin(left)=14,margin(bottom)=14,margin(top)=14,margin(right)=14
475        ModifyGraph mirror=2
476        ModifyGraph nticks=4
477        ModifyGraph minor=1
478        ModifyGraph fSize=9
479        ModifyGraph standoff=0
480        ModifyGraph tkLblRot(left)=90
481        ModifyGraph btLen=3
482        ModifyGraph tlOffset=-2
483//      SetAxis/A/R left
484End
485
486Proc Display2DSlice_log()
487        PauseUpdate; Silent 1           // building window...
488        Display /W=(1038,44,1404,403)
489        DoWindow/C FFT_Slice2D_log
490        AppendImage/T logP
491        ModifyImage logP ctab= {*,*,YellowHot,0}
492        ModifyGraph margin(left)=14,margin(bottom)=14,margin(top)=14,margin(right)=14
493        ModifyGraph mirror=2
494        ModifyGraph nticks=4
495        ModifyGraph minor=1
496        ModifyGraph fSize=9
497        ModifyGraph standoff=0
498        ModifyGraph tkLblRot(left)=90
499        ModifyGraph btLen=3
500        ModifyGraph tlOffset=-2
501//      SetAxis/A/R left
502End
503Proc Slice2_1D()
504        PauseUpdate; Silent 1           // building window...
505        Display /W=(1034,425,1406,763) iBin_2d vs qBin_2d
506        DoWindow/C FFT_Slice1D
507        ModifyGraph mode=4
508        ModifyGraph marker=19
509        ModifyGraph msize=2
510        ModifyGraph grid=1
511        ModifyGraph log=1
512        ModifyGraph mirror=2
513        Legend
514EndMacro
515
516
517Function FFT_TransposeMat(ctrlName) : ButtonControl
518        String ctrlName
519
520        Variable mode=1
521        Prompt mode,"Transform XYZ to:",popup,"XZY;ZXY;ZYX;YZX;YXZ;"
522        DoPrompt "Transform mode",mode
523        if (V_Flag)
524                return 0                                                                        // user canceled
525        endif
526       
527        fFFT_TransposeMat(mode)
528       
529        return (0)
530End
531
532// starting in XYZ
533// mode 1;2;3;4;5;
534//correspond to
535// "XZY;ZXY;ZYX;YZX;YXZ;"
536//
537Function fFFT_TransposeMat(mode)
538        Variable mode
539
540        WAVE/Z mat=mat
541        ImageTransform /G=(mode) transposeVol mat
542        WAVE M_VolumeTranspose=M_VolumeTranspose
543        Duplicate/O M_VolumeTranspose mat
544       
545        return(0)       
546End
547
548Function FFT_RotateMat(ctrlName) : ButtonControl
549        String ctrlName
550       
551        Variable angleX=45,angleY=0,angleZ=0
552        Prompt angleX, "Degrees of rotation around X-axis:"
553        Prompt angleY, "Degrees of rotation around Y-axis:"
554        Prompt angleZ, "Degrees of rotation around Z-axis:"
555        DoPrompt "Enter angles for rotation", angleX,angleY,angleZ
556       
557        if (V_Flag)
558                return 0                                                                        // user canceled
559        endif
560
561        XYZRotate(angleX,angleY,angleZ)
562
563
564//////////// old way, only one angle
565//      Variable degree=45,sense=1
566//      Prompt degree, "Degrees of rotation around Z-axis:"
567////    Prompt sense, "Direction of rotation:",popup,"CW;CCW;"
568//      DoPrompt "Enter parameters for rotation", degree
569//     
570//      if (V_Flag)
571//              return 0                                                                        // user canceled
572//      endif
573       
574
575        // old way using ImageRotate that interpolates, and is only around Z-axis
576//      fFFT_RotateMat(degree)
577        return(0)
578End
579
580// note that the rotation is not perfect. if the rotation produces an
581// odd number of pixels, then the object will "walk" one pixel. Also, some
582// small artifacts are introduced, simply due to the voxelization of the object
583// as it is rotated. rotating a cylinder 10 then 350 shows a few extra "bumps" on
584// the surface, but the calculated scattering is virtually identical.
585//
586// these issues, may be correctable, if needed
587//
588Function fFFT_RotateMat(degree)
589        Variable degree
590       
591        Variable fill=0
592        NVAR solventSLD = root:FFT_SolventSLD
593        fill = solventSLD
594       
595        WAVE mat = root:mat
596        Variable dx,dy,dz,nx,ny,nz
597        dx = DimSize(mat,0)
598        dy = DimSize(mat,1)
599        dz = DimSize(mat,2)
600               
601        //? the /W and /C flags seem to be special cases for 90 deg rotations
602        ImageRotate /A=(degree)/E=(fill) mat            //mat is not overwritten, unless /O
603       
604        nx = DimSize(M_RotatedImage,0)
605        ny = DimSize(M_RotatedImage,1)
606        nz = DimSize(M_RotatedImage,2)
607//      Print "rotated dims = ",dx,dy,dz,nx,ny,nz
608        Variable delx,dely,odd=0
609        delx = nx-dx
610        dely = ny-dy
611       
612        if(mod(delx,2) != 0)            //sometimes the new x,y dims are odd!
613                odd = 1
614        endif
615//      delx = (delx + odd)/2
616//      dely = (dely + odd)/2
617        delx = trunc(delx/2)
618        dely = trunc(dely/2)
619
620        //+odd removes an extra point if there is one
621        Duplicate/O/R=[delx+odd,nx-delx-1][dely+odd,ny-dely-1][0,nz-1] M_RotatedImage mat
622
623// - not sure why the duplicate operation changes the start and delta of mat, but I
624// need to reset the start and delta to be sure that the display is correct, and that the scaling
625// is read correctly later
626        SetScale/P x 0,1,"", mat
627        SetScale/P y 0,1,"", mat
628       
629        nx = DimSize(mat,0)
630        ny = DimSize(mat,1)
631        nz = DimSize(mat,2)
632//      Print "redim = ",dx,dy,dz,nx,ny,nz
633       
634        KillWaves/Z M_RotatedImage
635       
636End
637
638// also look for ImageTransform operations that will allow translation of the objects.
639// then simple objects could be oriented @0,0,0, and translated to the correct position (and added)
640//
641Function FFT_AddRotatedObject(ctrlName) : ButtonControl
642        String ctrlName
643
644        Print "Not yet implemented"
645End
646
647Function FFT_ChangeMatrixValuesButton(ctrlName)
648        String ctrlName
649       
650        Execute "ChangeMatrixValues()"
651
652end
653
654Function FFT_ReplaceSolventButton(ctrlname)
655        String ctrlName
656       
657        Execute "ReplaceSolvent()"
658end
659
660Function FFT_MakeMatrixButtonProc(ctrlName) : ButtonControl
661        String ctrlName
662
663        NVAR nn=root:FFT_N
664        if(mod(nn, 2 ) ==1)             //force an even number for FFT
665                nn +=1
666        endif
667        MakeMatrix("mat",nn,nn,nn)
668End
669
670Function FFTMakeGizmoButtonProc(ctrlName) : ButtonControl
671        String ctrlName
672       
673        DoWindow/F Gizmo_VoxelMat
674        if(V_flag==0)
675                Execute "Gizmo_VoxelMat()"
676        endif
677        ColorizeGizmo()
678End
679
680Function FFTDrawSphereButtonProc(ctrlName)  : ButtonControl
681        String ctrlName
682       
683        Execute "FFTDrawSphereProc()"
684End
685
686Proc FFTDrawSphereProc(matStr,rad,xc,yc,zc,fill,periodic)
687        String matStr="mat"
688        Variable rad=25,xc=50,yc=50,zc=50,fill=10,periodic=1
689        Prompt matStr,"the wave"                //,popup,WaveList("*",";","")
690        Prompt rad,"enter real radius (A)"
691        Prompt xc,"enter the X-center"
692        Prompt yc,"enter the Y-center"
693        Prompt zc,"enter the Z-center"
694        Prompt fill,"fill SLD value"
695        Prompt periodic,"enter 1 for periodic, 0 for non-periodic fill"
696       
697        Variable grid=root:FFT_T
698       
699        if(periodic)
700                FillSphereRadiusPeriodic($matStr,grid,rad,xc,yc,zc,fill)
701        else
702                FillSphereRadius($matStr,grid,rad,xc,yc,zc,fill)
703        endif
704End
705
706Function FFTDrawZCylinderButtonProc(ctrlName)  : ButtonControl
707        String ctrlName
708       
709        Execute "FFTDrawCylinder()"
710End
711
712Proc FFTDrawCylinder(direction,matStr,rad,len,xc,yc,zc,fill)
713        String direction
714        String matStr="mat"
715        Variable rad=25,len=300,xc=50,yc=50,zc=50,fill=10
716        Prompt direction, "Direction", popup "X;Y;Z;"
717        Prompt matStr,"the wave"                //,popup,WaveList("*",";","")
718        Prompt rad,"enter real radius (A)"
719        Prompt len,"enter length (A)"
720        Prompt xc,"enter the X-center"
721        Prompt yc,"enter the Y-center"
722        Prompt zc,"enter the Z-center"
723        Prompt fill,"fill SLD value"
724       
725       
726        Variable grid=root:FFT_T
727       
728        if(cmpstr(direction,"X")==0)
729                FillXCylinder($matStr,grid,rad,xc,yc,zc,len,fill)
730        endif
731        if(cmpstr(direction,"Y")==0)
732                FillYCylinder($matStr,grid,rad,xc,yc,zc,len,fill)
733        endif
734        if(cmpstr(direction,"Z")==0)
735                FillZCylinder($matStr,grid,rad,xc,yc,zc,len,fill)
736        endif
737       
738End
739
740
741Function DoTheFFT_ButtonProc(ctrlName) : ButtonControl
742        String ctrlName
743
744        Calc_IQ_FFT()
745//      Execute "DoFFT()"
746End
747
748Function FFT_PlotResultsButtonProc(ctrlName) : ButtonControl
749        String ctrlName
750
751        Variable first=0
752        DoWindow/F FFT_IQ
753        if(V_flag==0)
754                first = 1
755                Display /W=(295,44,627,302)
756                DoWindow/C FFT_IQ
757        Endif
758       
759        // append the desired data, if it's not already there
760        // FFTButton_4 = FFT            = iBin
761        // FFTButton_7a = binned = _XOP
762        // FFTButton_8a = Debye = _full
763        // FFTButton_14a = SLD = _SLD
764        // 17 = iso USANS
765        // 18 = Anisotropic USANS
766        //
767        strswitch(ctrlName)     
768                case "FFTButton_4":
769                        if(!isTraceOnGraph("iBin","FFT_IQ") && exists("iBin")==1)               //only append if it's not already there
770                                AppendToGraph /W=FFT_IQ iBin vs qBin
771                                ModifyGraph mode=4,marker=19,msize=2,rgb(iBin)=(65535,0,0)
772                        endif
773                        break
774                case "FFTButton_7a":
775                        if(!isTraceOnGraph("ival_XOP","FFT_IQ") && exists("ival_XOP")==1)               //only append if it's not already there
776                                AppendToGraph /W=FFT_IQ ival_XOP vs qval_XOP
777                                ModifyGraph mode=4,marker=19,msize=2,rgb(ival_XOP)=(1,12815,52428)
778                        endif           
779                        break
780                case "FFTButton_8a":
781                        if(!isTraceOnGraph("ival_full","FFT_IQ") && exists("ival_full")==1)             //only append if it's not already there
782                                AppendToGraph /W=FFT_IQ ival_full vs qval_full
783                                ModifyGraph mode=4,marker=19,msize=2,rgb(ival_full)=(0,0,0)
784                        endif           
785                        break
786                case "FFTButton_14a":
787                        if(!isTraceOnGraph("ival_SLD","FFT_IQ") && exists("ival_SLD")==1)               //only append if it's not already there
788                                AppendToGraph /W=FFT_IQ ival_SLD vs qval_SLD
789                                ModifyGraph mode=4,marker=19,msize=2,rgb(ival_SLD)=(2,39321,1)
790                        endif           
791                        break
792                case "FFTButton_14a":
793                        if(!isTraceOnGraph("ival_SLD","FFT_IQ") && exists("ival_SLD")==1)               //only append if it's not already there
794                                AppendToGraph /W=FFT_IQ ival_SLD vs qval_SLD
795                                ModifyGraph mode=4,marker=19,msize=2,rgb(ival_SLD)=(2,39321,1)
796                        endif           
797                        break
798                case "FFTButton_17":
799                        if(!isTraceOnGraph("FFT_iUSANS_i","FFT_IQ") && exists("FFT_iUSANS_i")==1)               //only append if it's not already there
800                                AppendToGraph /W=FFT_IQ FFT_iUSANS_i vs FFT_iUSANS_q
801                                ModifyGraph mode=4,marker=19,msize=2,rgb(FFT_iUSANS_i)=(39321,1,31457)
802                        endif           
803                        break
804                case "FFTButton_18":
805                        if(!isTraceOnGraph("FFT_aUSANS_i","FFT_IQ") && exists("FFT_aUSANS_i")==1)               //only append if it's not already there
806                                AppendToGraph /W=FFT_IQ FFT_aUSANS_i vs FFT_aUSANS_q
807                                ModifyGraph mode=4,marker=19,msize=2,rgb(FFT_aUSANS_i)=(52428,34958,1)
808                        endif           
809                        break
810                       
811                       
812        endswitch
813       
814        if(first)
815                ModifyGraph mode=4
816                ModifyGraph marker=19
817                ModifyGraph msize=2
818                ModifyGraph gaps=0
819                ModifyGraph grid=1
820                ModifyGraph log=1
821                ModifyGraph mirror=2
822                Legend
823        endif
824       
825        return(0)
826End
827
828Function isTraceOnGraph(traceStr,winStr)
829        String traceStr,winStr
830       
831        Variable isOn=0
832        String str
833        str = TraceNameList(winStr,";",1)               //only normal traces
834        isOn = strsearch(str,traceStr,0)                //is the trace there?
835        isOn = isOn == -1 ? 0 : 1                       // return 0 if not there, 1 if there
836
837        return(isOn)
838End
839
840Function FFTEraseMatrixButtonProc(ctrlName) : ButtonControl
841        String ctrlName
842       
843        Wave mat=root:mat
844        FastOp mat=0
845        return(0)
846End
847
848Function FFTFillSolventMatrixProc(ctrlName) : ButtonControl
849        String ctrlName
850       
851        Wave mat=root:mat
852        NVAR val=root:FFT_SolventSLD
853        FastOp mat=(val)
854        return(0)
855End
856
857Function FFT_AltiSpheresButtonProc(ctrlName) : ButtonControl
858        String ctrlName
859
860        Execute "DoSpheresCalcFFTPanel()"
861End
862
863Function FFT_BinnedSpheresButtonProc(ctrlName) : ButtonControl
864        String ctrlName
865
866        Execute "DoBinnedSpheresCalcFFTPanel()"
867End
868
869Function FFT_BinnedSLDButtonProc(ctrlName) : ButtonControl
870        String ctrlName
871
872        Execute "DoBinnedSLDCalcFFTPanel()"
873End
874
875Function FFT_Iso2USANS(ctrlName) : ButtonControl
876        String ctrlName
877
878        Execute "Isotropic_FFT_to_USANS()"
879        FFT_PlotResultsButtonProc(ctrlName)
880End
881
882Function FFT_Aniso2USANS(ctrlName) : ButtonControl
883        String ctrlName
884
885        Execute "Anisotropic_FFT_to_USANS()"
886        FFT_PlotResultsButtonProc(ctrlName)
887End
888
889
890
891
892/////UTILITIES
893
894// inverts the values in the matrix 0<->1
895Function InvertMatrixFill(mat)
896        Wave mat
897       
898        mat = (mat==1) ? 0 : 1
899End
900
901// replaces specified values
902Proc ChangeMatrixValues(old,new)
903        Variable old,new
904       
905        mat = (mat==old) ? new : mat
906        // sequence of steps to get the gizmo to update the display correctly
907        RemoveFromGizmo/N=Gizmo_VoxelMat object=Voxelgram0
908        RemoveFromGizmo/N=Gizmo_VoxelMat displayItem=axes0
909        AppendToGizmo/N=Gizmo_VoxelMat voxelgram=root:mat,name=voxelgram0
910        ModifyGizmo/N=Gizmo_VoxelMat  setDisplayList=-1, object=voxelgram0
911        ModifyGizmo/N=Gizmo_VoxelMat  setDisplayList=-1, object=axes0           //so that the axes are drawn last
912        ModifyGizmo ModifyObject=voxelgram0 property={ pointSize,3}
913
914       
915        ColorizeGizmo()
916End
917
918
919
920// replaces the solvent value and updates the global
921Proc ReplaceSolvent(newSolv)
922        Variable newSolv
923       
924        Variable solv = root:FFT_SolventSLD
925       
926        mat = (mat==solv) ? newSolv : mat
927       
928        root:FFT_SolventSLD = newSolv
929
930// sequence of steps to get the gizmo to update the display correctly
931        RemoveFromGizmo/N=Gizmo_VoxelMat object=Voxelgram0
932        RemoveFromGizmo/N=Gizmo_VoxelMat displayItem=axes0
933        AppendToGizmo/N=Gizmo_VoxelMat voxelgram=root:mat,name=voxelgram0
934        ModifyGizmo/N=Gizmo_VoxelMat  setDisplayList=-1, object=voxelgram0
935        ModifyGizmo/N=Gizmo_VoxelMat  setDisplayList=-1, object=axes0           //so that the axes are drawn last
936        ModifyGizmo ModifyObject=voxelgram0 property={ pointSize,3}
937
938
939        ColorizeGizmo()
940End
941
942
943//overwrites any existing matrix
944// matrix is byte, to save space
945//forces the name to be "mat"
946//
947// switched to signed byte
948//
949Function MakeMatrix(nam,xd,yd,zd)
950        String nam
951        Variable xd,yd,zd
952       
953        nam="mat"
954        Print "Matrix has been created and named \"mat\""
955//      Make/O/U/B/N=(xd,yd,zd) $nam
956        Make/O/B/N=(xd,yd,zd) $nam
957End
958
959
960// calculate the average center-to-center distance between points
961// assumes evenly spaced on a cubic grid
962//
963Proc Center_to_Center(np)
964        Variable np = 100
965       
966        Variable Nedge = root:FFT_N
967        Variable Tscale = root:FFT_T
968       
969        Variable davg
970       
971        davg = (Nedge*Tscale)^3 / np
972        davg = davg^(1/3)
973        Print "Average separation (A) = ",davg
974       
975End
976
977// calculate the number of points required on a grid
978// to yield a given average center-to-center distance between points
979// assumes evenly spaced on a cubic grid
980//
981// davg and Tscale are the same units, Angstroms
982//
983Proc Davg_to_Np(davg)
984        Variable davg = 400
985       
986        Variable Nedge = root:FFT_N
987        Variable Tscale = root:FFT_T
988       
989        Variable np
990       
991        np = (Nedge*Tscale)^3 / davg^3
992        Print "Number of points required = ",np
993       
994End
995
996
997
998// The matrix is not necessarily 0|1, this reports the number of filled voxels
999// - needed to estimate the time required for the AltiVec_Spheres calculation
1000Proc NumberOfPoints()
1001       
1002        Print "Number of points = ",NumberOfPoints_Occ(root:mat)
1003        Print "Fraction occupied = ",VolumeFraction_Occ(root:mat)
1004        Print "Overall Cube Edge [A] = ",root:FFT_T * root:FFT_N
1005        Print "Found values in matrix = ",ListOfValues(root:mat)
1006       
1007End
1008
1009Function NumberOfPoints_Occ(m)
1010        Wave m
1011       
1012        Variable num = NonZeroValues(m)
1013
1014        return(num)
1015End
1016
1017
1018Function VolumeFraction_Occ(m)
1019        Wave m
1020       
1021        Variable num = NonZeroValues(m)
1022        Variable dim = DimSize(m,0)
1023
1024        return(num/dim^3)
1025End
1026
1027//it's a byte wave, so I can't use the trick of setting the zero values to NaN
1028// since NaN can't be expressed as byte. So make it binary 0|1
1029//
1030// - the assumption here is that the defined solvent value is not part of the
1031// particle volume.
1032//
1033Function NonZeroValues(m)
1034        Wave m
1035       
1036        Variable num
1037        NVAR val = root:FFT_SolventSLD
1038       
1039//      Variable t1=ticks,dim
1040//      dim = DimSize(m, 0 )            //assume NxNxN
1041        Duplicate/O m,mz
1042       
1043        MultiThread mz = (m[p][q] != val) ? 1 : 0
1044       
1045        WaveStats/Q/M=1 mz              // NaN and Inf are not reported in V_npnts
1046       
1047        num = V_npnts*V_avg
1048       
1049        KillWaves/Z mz
1050//      Print "Number of points = ",num
1051//      Print "Fraction occupied = ",num/V_npnts
1052       
1053//      Print "time = ",(ticks-t1)/60.15
1054        return(num)
1055End
1056
1057//
1058// return a list of the different values of the voxels in the matrix
1059//
1060Function/S ListOfValues(m)
1061        Wave m
1062       
1063        String list=""
1064        Variable done
1065
1066        Duplicate/O m,mz
1067       
1068        done=0
1069        do
1070                WaveStats/Q/M=1 mz              // NaN and Inf are not reported in V_npnts
1071                if(V_max == V_min)
1072                        list += num2str(V_min) + ";"
1073                        done = 1
1074                else
1075                        list += num2str(V_max) + ";"
1076                        MultiThread mz = mz[p][q] == V_max ? V_min : mz[p][q]           // replace the max with min                     
1077                endif
1078        while(!done)   
1079       
1080//      Print "Found values in matrix = ",list
1081        KillWaves/Z mz
1082
1083        return(list)
1084End
1085
1086
1087
1088// returns estimate in seconds
1089//
1090//              updated for quad-core iMac, 2010
1091//
1092// type 3 = binned distances and SLD
1093// type 2 = binned distances
1094// type 1 is rather meaningless
1095// type 0 = is the Debye, double sum (multithreaded)
1096//
1097//
1098// - types 2 and 3, the binned methods are inherently AAO, so there is no significant
1099// dependence on the number of q-values (unless it's  >> 1000). So values reported are
1100// for 100 q-values.
1101//
1102// There is a function Timing_Method(type) in FFT_ConcentratedSpheres.ipf to automate some of this
1103//
1104Function EstimatedTime(nx,nq,type)
1105        Variable nx,nq,type
1106       
1107        Variable est=0
1108       
1109        if(type==0)             // "full" XOP
1110                est = 0.4 + 1.622e-5*nx+4.86e-7*nx^2
1111                est /= 100
1112                est *= nq
1113        endif
1114       
1115        if(type==1)             //Igor function (much slower, 9x)
1116                est = (nx^2)*0.0000517          //empirical values (seconds) for igor code, 100 q-values
1117                est /= 100                                      // per q now...
1118                est *= nq
1119        endif
1120       
1121        if(type==2)             //binned distances, slow parts XOPed
1122                est = 0.0680 + 2.94e-6*nx+4.51e-9*nx^2                  //with threading
1123               
1124//              est /= 100                                      // per q now...
1125//              est *= nq
1126        endif
1127       
1128        if(type==3)             //binned distances AND sld, slow parts XOPed
1129                est = 0.576 + 4.22e-7*nx+1.76e-8*nx^2           //with threading
1130               
1131//              est /= 100                                      // per q now...
1132//              est *= nq
1133        endif
1134       
1135        return(est)
1136End
1137
1138////////////// my functions to rotate the matrix in a XYZ coordinate system
1139//
1140//
1141// definitely not generic, is expecting NxNxN volume
1142//
1143// not very friendly in that it "clips" anything that rotates out of the volume
1144//
1145// does translate the center of the box to 000, rotates, then translates back
1146//
1147// friendly in the sense that the rotated matrix is the same size as the original.
1148//  --this is important for my final application (FFT)
1149//
1150// does no interpolation of values, so be sure to keep a copy of the original
1151// -- multiple rotation steps are going to make a mess of things.
1152//
1153// The multi axis rotation is done as one step, and probably violates every conventional
1154//  coordinate system. The rotation is applied as RxRyRz, but this could easily be changed
1155//
1156// I just want it to be correct, so speed was not an issue.
1157// -- it's nested for loops.
1158// -- it's working with the full matrix, even when 99% is empty.
1159//
1160// 20 NOV 2013 SRK
1161//
1162//
1163
1164//
1165// mat is the input volume
1166// rotVol is the output rotated volume
1167Function XYZRotate(angleX,angleY,angleZ)
1168        Variable angleX,angleY,angleZ
1169       
1170        NVAR FFT_N=root:FFT_N
1171        WAVE mat=root:mat
1172        Variable dist=FFT_N/2
1173
1174
1175// convert the NxNxN into 3xN xyz locations + wave of "w" values named "values"
1176        fVolumeToXYZTriplet(mat,"trip")
1177        Wave trip=root:trip
1178
1179// translate to get the center of the xyz values to 0,0,0       
1180        fTranslateCoordinate(trip,dist)         //subtracts dist
1181
1182// do the rotation as a matrix multiplication   
1183// putting zero is no rotation around that axis
1184// the triplet wave "trip" is overwritten with the output
1185        DoRotation(trip,angleX,angleY,angleZ)
1186//      Wave rotated=root:rotated
1187
1188// translate back to a 0->N based coordinate
1189//      fTranslateCoordinate(rotated,-dist)
1190        fTranslateCoordinate(trip,-dist)
1191        Wave values=root:values
1192
1193// convert the triplet back to a volume
1194// this CLIPS anything that has rotated out of the NxNxN volume
1195//      fXYZTripletToVolume(rotated,values,"rotVol",FFT_N)
1196        fXYZTripletToVolume(trip,values,"rotVol",FFT_N)
1197
1198// clean up by killng the extra waves that were generated
1199//
1200        KillWaves/Z trip,rotated,values
1201       
1202        Wave rotVol=root:rotVol
1203        mat=rotVol
1204       
1205        return(0)
1206End
1207
1208
1209
1210Function fVolumeToXYZTriplet(matrixWave, outputName)
1211        Wave matrixWave
1212        String outputName       
1213       
1214        Variable dimx=DimSize(matrixWave,0)
1215        Variable dimy=DimSize(matrixWave,1)
1216        Variable dimz=DimSize(matrixWave,2)
1217        Variable rows=dimx*dimy*dimz
1218        Make/O/N=(3,rows) $outputName
1219        Make/O/N=(rows) values
1220        WAVE TripletWave= $outputName
1221        Wave values=values
1222       
1223
1224        Variable ii,jj,kk,count=0
1225        Variable xVal,yVal,zval
1226        for(kk=0;kk<dimz;kk+=1)                 // kk is z (layer)
1227                zval=kk
1228                for(jj=0;jj<dimy;jj+=1)         // jj is y (column)
1229                        yVal=jj
1230                        for(ii=0;ii<dimx;ii+=1) // ii is x (row)
1231                                xVal=ii
1232                                TripletWave[0][count]=xVal
1233                                TripletWave[1][count]=yVal
1234                                TripletWave[2][count]=zval
1235                                values[count]=matrixWave[ii][jj][kk] // value at [row][col][lay]
1236                                count+=1
1237                        endfor
1238                endfor
1239        endfor
1240       
1241        return(0)
1242End
1243
1244
1245Function fXYZTripletToVolume(triplet, values, outputName, outputDim)
1246        Wave triplet,values
1247        String outputName
1248        Variable outputDim
1249       
1250        Variable numPt=DimSize(triplet,1)
1251
1252        Variable num = outputDim
1253       
1254        Make/O/B/N=(num,num,num) $outputName
1255        WAVE newVol= $outputName
1256
1257        FastOp newVol = 0
1258       
1259        Variable ii,jj,kk,count=0
1260        Variable xVal,yVal,zval
1261        Variable xOK, yOK, zOK
1262
1263       
1264        for(ii=0;ii<numPt;ii+=1)
1265                xval = round(triplet[0][ii])
1266                yval = round(triplet[1][ii])
1267                zval = round(triplet[2][ii])
1268               
1269                // round and keep in bounds (returns truth)
1270                xOK = inRange(xval,0,num-1)
1271                yOK = inRange(yval,0,num-1)
1272                zOK = inRange(zval,0,num-1)
1273               
1274                if(xOK && yOK && zOK)
1275                        newVol[xval][yval][zval] = values[ii]
1276                endif
1277                       
1278        endfor
1279
1280
1281        return(0)
1282End
1283
1284// if val < lo or > hi, bad val
1285// fi both of these pass, pt is OK
1286ThreadSafe Static Function inRange(val, lo, hi)
1287        Variable val, lo, hi
1288 
1289        if(val < lo)
1290                return (0)
1291        endif
1292        if(val > hi)
1293                return (0)
1294        endif
1295       
1296        return(1)
1297 
1298End
1299
1300// Rotation is applied in the order Rx Ry Rz
1301Function DoRotation(triplet,angleX,angleY,angleZ)
1302        Wave triplet
1303        Variable angleX,angleY,angleZ
1304       
1305        Variable thetaX,thetaY,thetaZ
1306        thetaX = angleX*pi/180          // convert degrees to radians
1307        thetaY = angleY*pi/180          // convert degrees to radians
1308        thetaZ = angleZ*pi/180          // convert degrees to radians
1309       
1310        Make/O/D/N=(3,3) Rx,Ry,Rz
1311        Rx=0
1312        Ry=0
1313        Rz=0
1314       
1315        Rx[0][0] = 1
1316        Rx[1][1] = cos(thetaX)
1317        Rx[1][2] = -sin(thetaX)
1318        Rx[2][1] = sin(thetaX)
1319        Rx[2][2] = cos(thetaX)
1320       
1321        Ry[0][0] = cos(thetaY)
1322        Ry[0][2] = sin(thetaY)
1323        Ry[1][1] = 1
1324        Ry[2][0] = -sin(thetaY)
1325        Ry[2][2] = cos(thetaY)
1326       
1327        Rz[0][0] = cos(thetaZ)
1328        Rz[0][1] = -sin(thetaZ)
1329        Rz[1][0] = sin(thetaZ)
1330        Rz[1][1] = cos(thetaZ)
1331        Rz[2][2] = 1   
1332       
1333       
1334//      MatrixOp/O rotated = Rx x Ry x Rz x triplet
1335        MatrixOp/O triplet = Rx x Ry x Rz x triplet
1336       
1337
1338        return(0)
1339end
1340
1341
1342// the rotation matrix, as I copied from wikipedia, is a rotation around (0,0,0), not
1343// the center of the gizmo plot
1344Function fTranslateCoordinate(trip,dist)
1345        Wave trip
1346        Variable dist
1347       
1348//      MatrixOp/O trip = trip - dist
1349        trip = trip - dist
1350       
1351        return(0)
1352End
1353
Note: See TracBrowser for help on using the repository browser.