3 ;; it turns out that using a few points on the border of the circles
4 ;; is too sparse. the following scheme should sample the space a bit
5 ;; better and therefore give a better approximation of the exposure
9 ;; ---/ \--- xy-cross section through a
10 ;; / \ nucleus in the sample plane
12 ;; / \ choose s points along a line
21 ;; ---\ /-- -----------
22 ;; --------- ---/ \---
29 ;; b points .... a b c d e f
32 ;; inclination theta \ /
39 ;; now shoot rays from each o the points in the bfp to each of the
40 ;; points in the sample plane. we have to make sure that a nucleus
41 ;; inside the illumination cone convolved with the cross section of
42 ;; the illuminated nucleus is hit by at least one ray. the necessary
43 ;; number of points s and b depends on the size of the nuclei as well
44 ;; as the furthest distance to the border of the stack. for now i
45 ;; don't want to think too hard about that. i guess it will be
46 ;; sufficient to sample with .01 in the back focal plane and 3 points
48 ;; two dimensional coordinates will be represented as complex numbers.
50 (defun sample-line (nr-ffp nr-bfp
)
51 "Create two lists like -1 0 1 and -1 -.5 0 .5 1. Here nr-ffp would
52 be 3 and nr-bfp would be 5. The result is the outer product -1 -1, -1
53 -.5, -1, 0, ..., a list with all interconnections. Note that the pair
54 0,0 isn't emitted as it would result in duplications when turned by
56 (declare ((integer 2) nr-ffp nr-bfp
))
57 (let ((ffps (loop for i below nr-ffp collect
58 (complex (- (/ (* 2d0 i
) (1- nr-ffp
)) 1))))
59 (bfps (loop for i below nr-bfp collect
60 (complex (- (/ (* 2d0 i
) (1- nr-bfp
)) 1))))
62 (loop for f in ffps do
63 (loop for b in bfps do
64 ;; prevent duplication of central ray
65 (unless (= (complex 0d0
) f b
)
66 (push (list f b
) result
))))
72 (defun sample-circles (nr-ffp nr-bfp nr-theta
)
73 "Create coordinates in front and backfocal plane that sample circles
74 in a regular pattern."
75 (declare ((integer 2) nr-ffp nr-bfp
)
76 ((integer 1) nr-theta
)
77 (values cons
&optional
))
78 (let ((line (sample-line nr-ffp nr-bfp
))
80 (loop for theta below nr-theta do
81 (let ((rotator (exp (complex 0d0
(/ (* pi theta
) nr-theta
)))))
82 (loop for
(f b
) in line do
83 (push (list (* rotator f
)
86 (when (and (oddp nr-ffp
) (oddp nr-bfp
)) ;; central ray was omitted above
87 (push (list (complex 0d0
) (complex 0d0
)) result
))
91 (sample-circles 2 2 1)
99 ;; / |y +---+------+ \
104 ;; -+--------------------+---+----------------+--
108 (defun move-complex-circle (z rr x
/rr y
/rr r
/rr
)
109 "Given a complex number Z inside the unit circle, move the unit
110 circle to position X,Y inside the BFP with radius RR. Scale the unit
111 circle to the window-radius R."
112 (declare ((complex double-float
) z
)
113 ((double-float -
1d0
1d0
) x
/rr y
/rr
)
114 ((double-float 0d0
1d0
) r
/rr
)
116 (values (complex double-float
) &optional
))
117 (+ (complex (* x
/rr rr
) (* y
/rr rr
))
121 (move-complex-circle (complex 1d0
0d0
) 2d0
.9d0
0d0
.1d0
)
124 (move-complex-circle (complex 1d0
0d0
) 1d0
.9d0
0d0
.1d0
)
127 (move-complex-circle (complex 1d0
) 3.601d0
0d0
0d0
.1d0
)
129 (defmethod make-rays ((objective lens
::objective
) (model sphere-model
)
130 nucleus positions win-x
/r win-y
/r win-r
/r
)
131 "Given an objective and a nucleus in a model generate rays from a
132 circle on the back focal plane into the front focal plane. The pattern
133 of the rays is given as a list of 2-lists of complex numbers. The
134 first complex number gives the relative position inside the central
135 cross section of the nucleus and the second number gives the relative
136 position in the bfp. The coordinates and size of the window in the
137 back focal plane are given relative to the radius of the bfp. The
138 return value is a list of 2-lists of rays, where the first ray starts
139 from the principal sphere and the second ray from the bfp."
140 (declare (fixnum nucleus
)
142 (double-float win-x
/r win-y
/r win-r
/r
)
143 (values (or null cons
) &optional
))
144 (assert (subtypep (type-of (first (first positions
)))
145 '(complex double-float
)))
146 (assert (subtypep (type-of (second (first positions
)))
147 '(complex double-float
)))
148 (with-slots (centers-mm radii-mm
) model
149 (let ((center (elt centers-mm nucleus
))
150 (radius (elt radii-mm nucleus
))
152 (with-slots ((bfp-radius lens
::bfp-radius
)
153 (ri lens
::immersion-index
)
154 (f lens
::focal-length
)) objective
155 (loop for
(f b
) in positions do
156 (let ((br (move-complex-circle b
1d0
157 win-x
/r win-y
/r win-r
/r
))
158 (fr (move-complex-circle f
1d0
(vec-x center
) (vec-y center
)
161 (multiple-value-bind (exit enter
)
162 (lens:get-ray-behind-objective
164 (realpart fr
) (imagpart fr
)
165 (realpart br
) (imagpart br
))
166 (push (list exit enter
) result
))
168 (nreverse result
)))))
172 (loop for
(exit enter
) in
(make-rays (lens:make-objective
:center
(v 0 0 1))
174 (sample-circles 2 2 1)
177 (vector::start enter
)))
179 (defun merit-function (vec2 params
&key
(border-value 100d0
))
180 "Vec2 contains the center of the window in th bfp. Params must be a
181 list containing objective model nucleus-index window-radius
182 positions (positions is a list of 2-lists of complex
183 numbers). BORDER-VALUE has to be bigger than then the maximum of
184 integrals in the back focal plane. It will be returned when the beam
185 wanders outside of the bfp."
186 (declare ((simple-array double-float
(2)) vec2
)
188 (values double-float
&optional
))
189 (destructuring-bind (objective model nucleus-index
190 window-radius positions
)
192 (let* ((border-width window-radius
) ;; in this range to the
197 (radius (norm2 vec2
)))
198 (if (< radius
(- .99d0 border-width
))
200 (let* ((rays (make-rays objective model nucleus-index
201 positions
(vec2-x vec2
)
202 (vec2-y vec2
) window-radius
)))
204 (return-from merit-function border-value
))
205 (let ((s (/ 1d0
(length rays
))))
206 (loop for
(exit enter
) in rays do
208 (* s
(raytrace:ray-spheres-intersection
209 exit model nucleus-index
))))))
210 ;; in the border-width or outside of bfp
211 (incf sum border-value
))
214 #+nil
;; call merit function for one window center position
215 (let* ((obj (lens:make-objective
:center
(v) :normal
(v 0 0 1)))
217 (positions (sample-circles 3 10 12))
218 (z-plane-mm (vec-z (elt (raytrace::centers-mm
*model
*) 0))))
219 (with-slots ((c lens
::center
)
220 (ri lens
::immersion-index
)
221 (f lens
::focal-length
)) obj
222 (setf c
(make-vec 0d0
0d0
(+ (- (* ri f
)) z-plane-mm
)))
223 (let* ((params (list obj
*model
* 0 window-radius positions
)))
224 (merit-function (make-vec2 :x
.4d0
:y
.4d0
)
227 #+nil
;; store the scan for different bfp window sizes
230 (nn 6 #+nil
(length (centers *model
*)))
231 (mosaicx (ceiling (sqrt nn
)))
232 (mosaic (make-array (list (* n mosaicx
) (* n mosaicx
))
233 :element-type
'double-float
))
234 (obj (lens:make-objective
:center
(v) :normal
(v 0 0 1)))
236 (positions (sample-circles 3 7 5)))
238 (with-slots ((c lens
::center
)
239 (ri lens
::immersion-index
)
240 (f lens
::focal-length
)) obj
241 (let* ((window-radius (* nuc
(/ .30d0 nn
)))
242 (z-plane-mm (vec-z (elt (raytrace::centers-mm
*model
*) nucleus
)))
244 (setf c
(make-vec 0d0
0d0
(+ (- (* ri f
)) z-plane-mm
)))
245 (let* ((params (list obj
*model
* nucleus window-radius positions
))
246 (px (* n
(mod nuc mosaicx
)))
247 (py (* n
(floor nuc mosaicx
))))
248 (do-region ((j i
) (n n
))
249 (let* ((x (- (* 2d0
(/ i n
)) 1d0
))
250 (y (- (* 2d0
(/ j n
)) 1d0
))
251 (v (merit-function (make-vec2 :x x
:y y
)
254 (setf (aref mosaic
(+ py j
) (+ px i
)) v
)
255 (unless (= v
0d0
) (push v vals
)))))
256 (format t
"min ~2,6f max ~2,6f win-r ~2,3f~%"
260 (write-pgm "/home/martin/tmp/scan-mosaic.pgm" (normalize-2-df/ub8 mosaic
))))
262 (defun find-optimal-bfp-window-center (nucleus params
)
263 (declare (fixnum nucleus
)
265 (values vec2
&optional
))
266 (setf (elt params
2) nucleus
)
268 (multiple-value-bind (min point
)
269 (simplex-anneal:anneal
(simplex-anneal:make-simplex
270 (make-vec2 :x -
.4d0
:y -
.4d0
) .3d0
)
272 ;; set temperature bigger than the
273 ;; maxima in the bfp but smaller
275 :start-temperature
.04d0
277 :eps
/m
.001d0
;; high eps/m cools faster
278 :itmax
100 ;; steps per temperature
282 (return-from find-optimal-bfp-window-center point
)))))
287 (nn 5 #+nil
(length (centers *model
*)))
288 (mosaicx (ceiling (sqrt nn
)))
289 (mosaic (make-array (list (* n mosaicx
) (* n mosaicx
))
290 :element-type
'double-float
))
291 (obj (lens:make-objective
:center
(v) :normal
(v 0 0 1)))
292 (positions (sample-circles 3 7 5))
295 (with-open-file (*standard-output
* "/home/martin/tmp/scan-min.dat"
297 :if-exists
:supersede
)
298 (with-slots ((c lens
::center
)
299 (ri lens
::immersion-index
)
300 (f lens
::focal-length
)) obj
301 (let* ((window-radius .08d0
#+nil
(* nuc
(/ .20d0 nn
)))
302 (z-plane-mm (vec-z (elt (raytrace::centers-mm
*model
*) nucleus
))))
303 (setf c
(make-vec 0d0
0d0
(+ (- (* ri f
)) z-plane-mm
)))
304 (let* ((params (list obj
*model
* nucleus window-radius positions
))
305 (px (* n
(mod scan mosaicx
)))
306 (py (* n
(floor scan mosaicx
))))
307 (find-optimal-bfp-window-center nucleus params
)
308 #+nil
(format t
"~a~%"
310 #+nil
(write-pgm "/home/martin/tmp/scan-mosaic.pgm"
311 (normalize-2-df/ub8 mosaic
))))