1 // AFM drawing utilities.
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
12 pair uty; // unit vector pointing distally at the tip
13 pair utx; // unit vector pointing tip-side at the tip
15 pair base; // location of the cantilever base (chip attachment point)
17 pair uy; // unit vector pointing distally at the base
18 pair ux; // unit vector pointing tip-side at the base
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
28 void align_tip(pair tip) {
29 // shift the whole cantilever setup so the tip rests on TIP.
30 pair offset = tip - this.tip;
32 this.tip_top += offset;
33 this.tip_distal += offset;
34 this.tip_proximal += offset;
38 real dx_error(real theta) {
39 /* Call the radius of curvature, r_curve=R
40 * arm_length=L lies along the perimiter, so
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'
47 return this.arm_length * (1.0 - cos(theta)) / theta - this.dx;
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));
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
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.
79 this.r_curve = this.arm_length / this.theta;
80 Lp = this.r_curve * sin(this.theta);
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;
94 p = this.tip -- this.tip_distal -- this.tip_proximal -- cycle;
101 // all three points should be colinear
102 p = this.tip_distal -- this.tip_proximal -- this.base;
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;
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) {
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);
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;
134 path box(pair top_center, real width, real height) {
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;
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);
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="");
161 void operator init(pair prot_connect_point=(0,0)) {
162 this.prot_connect_point = prot_connect_point;
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);
180 void operator init(pair center=(0,0)) {
181 this.center = center;
185 // extension() added sometime between asy v1.40 and v1.91
186 pair extension(pair P, pair Q, pair p, pair q)
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;
197 real width = 2pt; // width at start (the laser source)
198 real spot = 1pt; // width at the cantilever reflection point (minimum)
200 Cantilever cantilever;
201 Photodiode photodiode;
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
220 fill((start+w)--(start-w)--(ref-rw)--(ref+rw)--cycle, fillpen);
222 fill((contact+cw)--(contact-cw)--(ref-rw)--(ref+rw)--cycle, fillpen);
225 void operator init(pair start, pair dir=S, Cantilever cantilever,
226 Photodiode photodiode) {
228 this.dir = unit(dir);
229 this.cantilever = cantilever;
230 this.photodiode = photodiode;
234 void distance(Label L, pair start, pair end) {
235 pair center = (start+end)/2;
236 pair u = unit(end-start);
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="");