--- /dev/null
+// AFM operating principle graphic
+
+//size(x=9cm,keepAspect=true);
+
+import base_afm; // for Cantilever, Substrate, ...
+
+defaultpen(fontsize(size=10pt, lineskip=10pt));
+
+Cantilever c = Cantilever();
+// position base so tip touches substrate when bent
+c.set_tip_for_dx(8pt);
+c.align_tip((0,0));
+// unbend the tip and draw it in grey
+c.set_tip_for_dx(0);
+pair unbent_tip = c.tip;
+filldraw(c.tip_guide(), fillpen=grey, grey);
+draw(c.arm_guide(), grey);
+// rebend the tip and draw it in black
+c.set_tip_for_dx(8pt);
+filldraw(c.tip_guide(), fillpen=black);
+draw(c.arm_guide(), black);
+label("Cantilever", c.base-(0,14pt), SW);
+
+Substrate s = Substrate();
+s.draw();
+
+Photodiode p = Photodiode(center=(-40pt, 40pt));
+p.draw();
+label("Photodiode", p.center+(0,p.radius), N);
+
+Laser s = Laser(start=(-5pt,40pt), dir=S, cantilever=c, photodiode=p);
+s.draw();
+label("Laser", s.start, N);
// draw an AFM cantilever with the tip at TIP (x,y) position on page
// the cantilever arm is roughly parallel to DIR_TO_BASE (x,y) vector
// POINT_SCALE x gives the height of the tip.
- pair tip;
- pair tip_top;
- pair base;
- real arm_length;
- real tip_height;
- real rest_angle; // in degrees
- real dx;
- real theta; // in radians
+ pair tip; // point of the tip
+ pair tip_top; // center of the back side of the tip
+ pair tip_distal; // distal back corner of the tip
+ pair tip_proximal; // proximal back corner of the tip
+
+ pair uty; // unit vector pointing distally at the tip
+ pair utx; // unit vector pointing tip-side at the tip
+
+ pair base; // location of the cantilever base (chip attachment point)
+
+ pair uy; // unit vector pointing distally at the base
+ pair ux; // unit vector pointing tip-side at the base
+
+ real arm_length; // cantilever path length
+ real tip_height; // distance from tip_top to tip
+ real rest_angle; // in degrees. uy = dir(rest_angle)
+ real dx; // (tip_top offset from unbent) <dot> (ux)
+ real theta; // in radians
real r_curve;
+
void align_tip(pair tip) {
+ // shift the whole cantilever setup so the tip rests on TIP.
pair offset = tip - this.tip;
this.tip += offset;
this.tip_top += offset;
+ this.tip_distal += offset;
+ this.tip_proximal += offset;
this.base += offset;
}
this.r_curve = this.arm_length / this.theta;
Lp = this.r_curve * sin(this.theta);
}
- pair uy = dir(this.rest_angle);
- pair ux = dir(this.rest_angle+90);
- pair utx = dir(this.rest_angle+90+degrees(this.theta));
- this.tip = this.base + Lp*uy + (dx+this.tip_height)*utx;
- this.tip_top = this.tip - this.tip_height*utx;
+ this.uy = dir(this.rest_angle);
+ this.ux = dir(this.rest_angle+90);
+ this.uty = dir(this.rest_angle+degrees(this.theta));
+ this.utx = dir(this.rest_angle+90+degrees(this.theta));
+ this.tip = this.base + Lp*this.uy + (dx+this.tip_height)*this.utx;
+ this.tip_top = this.tip - this.tip_height*this.utx;
+ this.tip_distal = this.tip_top + this.tip_height/3.0*this.uty;
+ this.tip_proximal = this.tip_top - this.tip_height/3.0*this.uty;
}
guide tip_guide() {
path p;
- pair uty, tip_distal, tip_proximal;
- pair uty = dir(this.rest_angle+degrees(this.theta));
- tip_distal = this.tip_top + this.tip_height/3.0*uty;
- tip_proximal = this.tip_top - this.tip_height/3.0*uty;
- p = this.tip -- tip_distal -- tip_proximal -- cycle;
+ p = this.tip -- this.tip_distal -- this.tip_proximal -- cycle;
return p;
}
guide arm_guide() {
path p;
- pair uy, uty, tip_distal, tip_proximal;
- pair uy = dir(this.rest_angle);
- pair ux = dir(this.rest_angle+90);
- pair uty = dir(this.rest_angle+degrees(this.theta));
- tip_distal = this.tip_top + this.tip_height/3.0*uty;
- tip_proximal = this.tip_top - this.tip_height/3.0*uty;
if (this.dx == 0) {
- p = tip_distal -- tip_proximal -- this.base; // should be colinear
+ // all three points should be colinear
+ p = this.tip_distal -- this.tip_proximal -- this.base;
} else {
- p = tip_distal -- tip_proximal{-uty} .. tension 0.75 .. {-uy}this.base;
+ p = this.tip_distal -- this.tip_proximal{-this.uty}
+ .. tension 0.75 .. {-this.uy}this.base;
/* real angle_tip_proximal, angle_base;
pair center = this.base + this.r_curve*dx;
angle_tip_proximal = angle(tip_proximal - center);
return p;
}
- void operator init (pair base=(0,0), real arm_length=2.5cm, real tip_height=0.5cm, real rest_angle=185, real dx=0) {
+ void operator init (pair base=(0,0), real arm_length=2.5cm, real tip_height=0.5cm, real rest_angle=195, real dx=0) {
this.base = base;
this.arm_length = arm_length;
this.tip_height = tip_height;
}
}
+struct Photodiode {
+ pair center;
+ real radius = 8pt;
+ real x_scale = 0.5;
+
+ void draw(pen fillpen=grey, pen drawpen=defaultpen) {
+ pair c = this.center;
+ pair y = (0, this.radius);
+ pair x = (this.x_scale*this.radius, 0);
+ filldraw(shift(this.center)*xscale(this.x_scale)*scale(this.radius)
+ *unitcircle, fillpen, drawpen);
+ draw((c-x)--(c+x)--c--(c-y)--(c+y), drawpen);
+ }
+
+ void operator init(pair center=(0,0)) {
+ this.center = center;
+ }
+}
+
+// extension() added sometime between asy v1.40 and v1.91
+pair extension(pair P, pair Q, pair p, pair q)
+{
+ pair ac=P-Q;
+ pair bd=q-p;
+ real det=ac.x*bd.y-ac.y*bd.x;
+ if(det == 0) return (infinity,infinity);
+ return P+((p.x-P.x)*bd.y-(p.y-P.y)*bd.x)*ac/det;
+}
+
+struct Laser {
+ pair start;
+ real width = 2pt; // width at start (the laser source)
+ real spot = 1pt; // width at the cantilever reflection point (minimum)
+ pair dir;
+ Cantilever cantilever;
+ Photodiode photodiode;
+
+ void draw(pen fillpen=red) {
+ pair w = scale(this.width/2)*rotate(90)*this.dir;
+ pair rw = scale(this.spot/2)*this.cantilever.uty;
+ if (dot(w, rw) < 0) rw = -rw; // ensure similar direction
+ // find point of reflection off cantilever.
+ pair ref = extension(this.start, this.start+this.dir,
+ this.cantilever.tip_distal,
+ this.cantilever.tip_proximal);
+ // find direction of reflection post-cantilever
+ pair post_dir = reflect((0,0), this.cantilever.uty)*dir;
+ pair contact = extension(ref, ref+post_dir,
+ this.photodiode.center,
+ this.photodiode.center+N);
+ real contact_width = this.width; ///length(ref-this.start)*length(ref-contact);
+ pair cw = scale(contact_width)*rotate(90)*post_dir;
+ if (dot(cw, rw) < 0) cw = -cw; // ensure similar direction
+ // laser -> cant.
+ fill((start+w)--(start-w)--(ref-rw)--(ref+rw)--cycle, fillpen);
+ // cant. -> photo.
+ fill((contact+cw)--(contact-cw)--(ref-rw)--(ref+rw)--cycle, fillpen);
+ }
+
+ void operator init(pair start, pair dir=S, Cantilever cantilever,
+ Photodiode photodiode) {
+ this.start = start;
+ this.dir = unit(dir);
+ this.cantilever = cantilever;
+ this.photodiode = photodiode;
+ }
+}
+
void distance(Label L, pair start, pair end) {
pair center = (start+end)/2;
pair u = unit(end-start);