Remove silly tex/ directory.
[thesis.git] / src / figures / schematic / base_afm.asy
1 // AFM drawing utilities.
2
3 struct Cantilever {
4   // draw an AFM cantilever with the tip at TIP (x,y) position on page
5   // the cantilever arm is roughly parallel to DIR_TO_BASE (x,y) vector
6   // POINT_SCALE x gives the height of the tip.
7   pair tip;          // point of the tip
8   pair tip_top;      // center of the back side of the tip
9   pair tip_distal;   // distal back corner of the tip
10   pair tip_proximal; // proximal back corner of the tip
11
12   pair uty;          // unit vector pointing distally at the tip
13   pair utx;          // unit vector pointing tip-side at the tip
14   
15   pair base;         // location of the cantilever base (chip attachment point)
16
17   pair uy;           // unit vector pointing distally at the base
18   pair ux;           // unit vector pointing tip-side at the base
19
20   real arm_length;   // cantilever path length
21   real tip_height;   // distance from tip_top to tip
22   real rest_angle;   // in degrees.  uy = dir(rest_angle)
23   real dx;           // (tip_top offset from unbent) <dot> (ux)
24   real theta;        // in radians
25   real r_curve;
26
27   
28   void align_tip(pair tip) {
29     // shift the whole cantilever setup so the tip rests on TIP.
30     pair offset = tip - this.tip;
31     this.tip += offset;
32     this.tip_top += offset;
33     this.tip_distal += offset;
34     this.tip_proximal += offset;
35     this.base += offset;
36   }
37
38   real dx_error(real theta) {
39     /* Call the radius of curvature, r_curve=R
40      * arm_length=L lies along the perimiter, so
41      *   R\theta = L
42      * The perpendicular deflection dx is then
43      *   R(1-\cos\theta) = L(1-\cos\theta)/\theta = dx
44      * and the horizontal displacement L' from the base is
45      *   R\sin\theta = L\sin\theta/\theta = L\sinc\theta= L'
46      */
47      return this.arm_length * (1.0 - cos(theta)) / theta  -  this.dx;
48   }
49
50   real dx_prime(real theta) {
51     /* d(dx)/d(\theta) = L/\theta * [(\cos\theta-1)/\theta + \sin\theta]  */
52     return this.arm_length/theta * ((cos(theta)-1)/theta + sin(theta));
53   }
54
55   void set_tip_for_dx(real dx) {
56     /* invert dx(theta) using bracketed Newton-Raphson bisection.
57      * First order theta from
58      *   dx = L(1-\cos\theta)/\theta
59      *      = L[1-(1-\theta^2/2!+...)]/\theta
60      *      ~= L\theta/2
61      *   \theta != 2dx/L
62      */
63     this.dx = dx;
64     real Lp;
65     if (dx == 0) {
66       this.theta = 0;
67       this.r_curve = -1;
68       Lp = this.arm_length;
69     } else {
70       real first_order_theta = 2.0*dx/this.arm_length;
71       this.theta = newton(this.dx_error, this.dx_prime,
72                           0.1 * first_order_theta,
73                           min(3.0 * first_order_theta, pi/2.0));
74       /* because we ignored tip_height when we calculated theta, cheat a
75        * bit and pretend the tip is directly under our caculated
76        * tip_top.  Then adjust tip_top accordingly.  This throws off the
77        * bend (and maybe the arm length) a bit, but ah well :p.
78        */
79       this.r_curve = this.arm_length / this.theta;
80       Lp = this.r_curve * sin(this.theta);
81     }
82     this.uy = dir(this.rest_angle);
83     this.ux = dir(this.rest_angle+90);
84     this.uty = dir(this.rest_angle+degrees(this.theta));
85     this.utx = dir(this.rest_angle+90+degrees(this.theta));
86     this.tip = this.base + Lp*this.uy + (dx+this.tip_height)*this.utx;
87     this.tip_top = this.tip - this.tip_height*this.utx;
88     this.tip_distal = this.tip_top + this.tip_height/3.0*this.uty;
89     this.tip_proximal = this.tip_top - this.tip_height/3.0*this.uty;
90   }
91
92   guide tip_guide() {
93     path p;
94     p = this.tip -- this.tip_distal -- this.tip_proximal -- cycle;
95     return p;
96   }
97
98   guide arm_guide() {
99     path p;
100     if (this.dx == 0) {
101       // all three points should be colinear
102       p = this.tip_distal -- this.tip_proximal -- this.base;
103     } else {
104       p = this.tip_distal -- this.tip_proximal{-this.uty}
105           .. tension 0.75 .. {-this.uy}this.base;
106       /*      real angle_tip_proximal, angle_base;
107       pair center = this.base + this.r_curve*dx;
108       angle_tip_proximal = angle(tip_proximal - center);
109       angle_base = angle(this.base - center);
110       p = tip_distal -- arc(center, this.r_curve, angle_tip_proximal, angle_base);
111       tip_proximal{-uty} .. tension 0.75 .. {uy}this.base;
112       */
113     }
114     return p;
115   }
116
117   void operator init (pair base=(0,0), real arm_length=2.5cm, real tip_height=0.5cm, real rest_angle=195, real dx=0) {
118     this.base = base;
119     this.arm_length = arm_length;
120     this.tip_height = tip_height;
121     this.rest_angle = rest_angle;
122     this.set_tip_for_dx(dx);
123   }
124 }
125
126 struct Substrate {
127   pair prot_connect_point;
128   real substrate_width = 3cm;
129   real substrate_height = 10pt;
130   real piezo_width = 1.5cm;
131   real piezo_height = 0.75cm;
132   pen substrate_fill = grey;
133
134   path box(pair top_center, real width, real height) {
135     real w = width/2;
136     pair ul = top_center+(-w,0);
137     pair ur = top_center+( w,0);
138     pair ll = top_center+(-w,-height);
139     pair lr = top_center+( w,-height);
140     path p = ul -- ur -- lr -- ll -- cycle;
141     return p;
142   }
143
144   void draw(pair dir=(0,0)) {
145     pair piezo_top = this.prot_connect_point - (0,this.substrate_height);
146     pair piezo_bot = this.prot_connect_point - (0,this.substrate_height+this.piezo_height);
147     filldraw(this.box(this.prot_connect_point, this.substrate_width, this.substrate_height),
148              fillpen=this.substrate_fill);
149     label("Substrate", this.prot_connect_point - (0,this.substrate_height/2));
150     draw(this.box(piezo_top, this.piezo_width, this.piezo_height));
151     label("Piezo", piezo_top, S);
152     if (dir != (0,0)) {
153       real arrow_length = this.piezo_height/3;
154       pair arrow_center = piezo_bot + (0, arrow_length/2 + 2pt);
155       arrow(b=arrow_center + dir*arrow_length/2, dir=-dir,
156             length=this.piezo_height/3, Arrow(size=8pt),
157             margin=NoMargin, L="");
158     }
159   }
160
161   void operator init(pair prot_connect_point=(0,0)) {
162     this.prot_connect_point = prot_connect_point;
163   }
164 }
165
166 struct Photodiode {
167   pair center;
168   real radius = 8pt;
169   real x_scale = 0.5;
170   
171   void draw(pen fillpen=grey, pen drawpen=defaultpen) {
172     pair c = this.center;
173     pair y = (0, this.radius);
174     pair x = (this.x_scale*this.radius, 0);
175     filldraw(shift(this.center)*xscale(this.x_scale)*scale(this.radius)
176              *unitcircle, fillpen, drawpen);
177     draw((c-x)--(c+x)--c--(c-y)--(c+y), drawpen);
178   }
179
180   void operator init(pair center=(0,0)) {
181     this.center = center;
182   }
183 }
184
185 // extension() added sometime between asy v1.40 and v1.91
186 pair extension(pair P, pair Q, pair p, pair q)
187 {
188   pair ac=P-Q;
189   pair bd=q-p;
190   real det=ac.x*bd.y-ac.y*bd.x;
191   if(det == 0) return (infinity,infinity);
192   return P+((p.x-P.x)*bd.y-(p.y-P.y)*bd.x)*ac/det;
193 }
194
195 struct Laser {
196   pair start;
197   real width = 2pt; // width at start (the laser source)
198   real spot = 1pt;  // width at the cantilever reflection point (minimum)
199   pair dir;
200   Cantilever cantilever;
201   Photodiode photodiode;
202
203   void draw(pen fillpen=red) {
204     pair w = scale(this.width/2)*rotate(90)*this.dir;
205     pair rw = scale(this.spot/2)*this.cantilever.uty;
206     if (dot(w, rw) < 0) rw = -rw; // ensure similar direction
207     // find point of reflection off cantilever.
208     pair ref = extension(this.start, this.start+this.dir,
209                          this.cantilever.tip_distal,
210                          this.cantilever.tip_proximal);
211     // find direction of reflection post-cantilever
212     pair post_dir = reflect((0,0), this.cantilever.uty)*dir;
213     pair contact = extension(ref, ref+post_dir,
214                              this.photodiode.center,
215                              this.photodiode.center+N);
216     real contact_width = this.width; ///length(ref-this.start)*length(ref-contact);
217     pair cw = scale(contact_width)*rotate(90)*post_dir;
218     if (dot(cw, rw) < 0) cw = -cw; // ensure similar direction
219     // laser -> cant.
220     fill((start+w)--(start-w)--(ref-rw)--(ref+rw)--cycle, fillpen);
221     // cant. -> photo.
222     fill((contact+cw)--(contact-cw)--(ref-rw)--(ref+rw)--cycle, fillpen);
223   }
224
225   void operator init(pair start, pair dir=S, Cantilever cantilever,
226                      Photodiode photodiode) {
227     this.start = start;
228     this.dir = unit(dir);
229     this.cantilever = cantilever;
230     this.photodiode = photodiode;
231   }
232 }
233
234 void distance(Label L, pair start, pair end) {
235   pair center = (start+end)/2;
236   pair u = unit(end-start);
237   picture picL;
238   label(picL, L);
239   pair label_size = 1.2 * (max(picL)-min(picL));
240   real arrow_length = length(end-start)/2 - label_size.y;
241   if (arrow_length > 4pt) {
242     arrow(b=start, dir=u, length=arrow_length, Arrow(size=4pt), L="");
243     arrow(b=end, dir=-u, length=arrow_length, Arrow(size=4pt), L="");
244   }
245   label(L, center);
246 }