--- /dev/null
+// AFM drawing utilities.
+
+struct Cantilever {
+ // 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
+ real r_curve;
+
+ void align_tip(pair tip) {
+ pair offset = tip - this.tip;
+ this.tip += offset;
+ this.tip_top += offset;
+ this.base += offset;
+ }
+
+ real dx_error(real theta) {
+ /* Call the radius of curvature, r_curve=R
+ * arm_length=L lies along the perimiter, so
+ * R\theta = L
+ * The perpendicular deflection dx is then
+ * R(1-\cos\theta) = L(1-\cos\theta)/\theta = dx
+ * and the horizontal displacement L' from the base is
+ * R\sin\theta = L\sin\theta/\theta = L\sinc\theta= L'
+ */
+ return this.arm_length * (1.0 - cos(theta)) / theta - this.dx;
+ }
+
+ real dx_prime(real theta) {
+ /* d(dx)/d(\theta) = L/\theta * [(\cos\theta-1)/\theta + \sin\theta] */
+ return this.arm_length/theta * ((cos(theta)-1)/theta + sin(theta));
+ }
+
+ void set_tip_for_dx(real dx) {
+ /* invert dx(theta) using bracketed Newton-Raphson bisection.
+ * First order theta from
+ * dx = L(1-\cos\theta)/\theta
+ * = L[1-(1-\theta^2/2!+...)]/\theta
+ * ~= L\theta/2
+ * \theta != 2dx/L
+ */
+ this.dx = dx;
+ real Lp;
+ if (dx == 0) {
+ this.theta = 0;
+ this.r_curve = -1;
+ Lp = this.arm_length;
+ } else {
+ real first_order_theta = 2.0*dx/this.arm_length;
+ this.theta = newton(this.dx_error, this.dx_prime,
+ 0.1 * first_order_theta,
+ min(3.0 * first_order_theta, pi/2.0));
+ /* because we ignored tip_height when we calculated theta, cheat a
+ * bit and pretend the tip is directly under our caculated
+ * tip_top. Then adjust tip_top accordingly. This throws off the
+ * bend (and maybe the arm length) a bit, but ah well :p.
+ */
+ 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;
+ }
+
+ 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;
+ 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
+ } else {
+ p = tip_distal -- tip_proximal{-uty} .. tension 0.75 .. {-uy}this.base;
+ /* real angle_tip_proximal, angle_base;
+ pair center = this.base + this.r_curve*dx;
+ angle_tip_proximal = angle(tip_proximal - center);
+ angle_base = angle(this.base - center);
+ p = tip_distal -- arc(center, this.r_curve, angle_tip_proximal, angle_base);
+ tip_proximal{-uty} .. tension 0.75 .. {uy}this.base;
+ */
+ }
+ 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) {
+ this.base = base;
+ this.arm_length = arm_length;
+ this.tip_height = tip_height;
+ this.rest_angle = rest_angle;
+ this.set_tip_for_dx(dx);
+ }
+}
+
+struct Substrate {
+ pair prot_connect_point;
+ real substrate_width = 3cm;
+ real substrate_height = 10pt;
+ real piezo_width = 1.5cm;
+ real piezo_height = 0.75cm;
+ pen substrate_fill = grey;
+
+ path box(pair top_center, real width, real height) {
+ real w = width/2;
+ pair ul = top_center+(-w,0);
+ pair ur = top_center+( w,0);
+ pair ll = top_center+(-w,-height);
+ pair lr = top_center+( w,-height);
+ path p = ul -- ur -- lr -- ll -- cycle;
+ return p;
+ }
+
+ void draw(pair dir=(0,0)) {
+ pair piezo_top = this.prot_connect_point - (0,this.substrate_height);
+ pair piezo_bot = this.prot_connect_point - (0,this.substrate_height+this.piezo_height);
+ filldraw(this.box(this.prot_connect_point, this.substrate_width, this.substrate_height),
+ fillpen=this.substrate_fill);
+ label("Substrate", this.prot_connect_point - (0,this.substrate_height/2));
+ draw(this.box(piezo_top, this.piezo_width, this.piezo_height));
+ label("Piezo", piezo_top, S);
+ if (dir != (0,0)) {
+ real arrow_length = this.piezo_height/3;
+ pair arrow_center = piezo_bot + (0, arrow_length/2 + 2pt);
+ arrow(b=arrow_center + dir*arrow_length/2, dir=-dir,
+ length=this.piezo_height/3, Arrow(size=8pt),
+ margin=NoMargin);
+ }
+ }
+
+ void operator init(pair prot_connect_point=(0,0)) {
+ this.prot_connect_point = prot_connect_point;
+ }
+}
+
+void distance(Label L, pair start, pair end) {
+ pair center = (start+end)/2;
+ pair u = unit(end-start);
+ picture picL;
+ label(picL, L);
+ pair label_size = 1.2 * (max(picL)-min(picL));
+ real arrow_length = length(end-start)/2 - label_size.y;
+ if (arrow_length > 4pt) {
+ arrow(b=start, dir=u, length=arrow_length, Arrow(size=4pt));
+ arrow(b=end, dir=-u, length=arrow_length, Arrow(size=4pt));
+ }
+ label(L, center);
+}
//size(x=9cm,keepAspect=true);
-import stats; // for Gaussrand()
+import stats; // for unitrand
+import base_afm; // for Cantilever, Substrate, ...
struct Wiggle {
// Generate a horizontal wiggly line, centered at CENTER (x,y)
}
}
-struct Cantilever {
- // 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
- real r_curve;
-
- void align_tip(pair tip) {
- pair offset = tip - this.tip;
- this.tip += offset;
- this.tip_top += offset;
- this.base += offset;
- }
-
- real dx_error(real theta) {
- /* Call the radius of curvature, r_curve=R
- * arm_length=L lies along the perimiter, so
- * R\theta = L
- * The perpendicular deflection dx is then
- * R(1-\cos\theta) = L(1-\cos\theta)/\theta = dx
- * and the horizontal displacement L' from the base is
- * R\sin\theta = L\sin\theta/\theta = L\sinc\theta= L'
- */
- return this.arm_length * (1.0 - cos(theta)) / theta - this.dx;
- }
-
- real dx_prime(real theta) {
- /* d(dx)/d(\theta) = L/\theta * [(\cos\theta-1)/\theta + \sin\theta] */
- return this.arm_length/theta * ((cos(theta)-1)/theta + sin(theta));
- }
-
- void set_tip_for_dx(real dx) {
- /* invert dx(theta) using bracketed Newton-Raphson bisection.
- * First order theta from
- * dx = L(1-\cos\theta)/\theta
- * = L[1-(1-\theta^2/2!+...)]/\theta
- * ~= L\theta/2
- * \theta != 2dx/L
- */
- this.dx = dx;
- real Lp;
- if (dx == 0) {
- this.theta = 0;
- this.r_curve = -1;
- Lp = this.arm_length;
- } else {
- real first_order_theta = 2.0*dx/this.arm_length;
- this.theta = newton(this.dx_error, this.dx_prime,
- 0.1 * first_order_theta,
- min(3.0 * first_order_theta, pi/2.0));
- /* because we ignored tip_height when we calculated theta, cheat a
- * bit and pretend the tip is directly under our caculated
- * tip_top. Then adjust tip_top accordingly. This throws off the
- * bend (and maybe the arm length) a bit, but ah well :p.
- */
- 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;
- }
-
- 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;
- 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
- } else {
- p = tip_distal -- tip_proximal{-uty} .. tension 0.75 .. {-uy}this.base;
- /* real angle_tip_proximal, angle_base;
- pair center = this.base + this.r_curve*dx;
- angle_tip_proximal = angle(tip_proximal - center);
- angle_base = angle(this.base - center);
- p = tip_distal -- arc(center, this.r_curve, angle_tip_proximal, angle_base);
- tip_proximal{-uty} .. tension 0.75 .. {uy}this.base;
- */
- }
- 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) {
- this.base = base;
- this.arm_length = arm_length;
- this.tip_height = tip_height;
- this.rest_angle = rest_angle;
- this.set_tip_for_dx(dx);
- }
-}
-
-struct Substrate {
- pair prot_connect_point;
- real substrate_width = 3cm;
- real substrate_height = 10pt;
- real piezo_width = 1.5cm;
- real piezo_height = 0.75cm;
- pen substrate_fill = grey;
-
- path box(pair top_center, real width, real height) {
- real w = width/2;
- pair ul = top_center+(-w,0);
- pair ur = top_center+( w,0);
- pair ll = top_center+(-w,-height);
- pair lr = top_center+( w,-height);
- path p = ul -- ur -- lr -- ll -- cycle;
- return p;
- }
-
- void draw() {
- pair piezo_top = this.prot_connect_point - (0,this.substrate_height);
- pair piezo_bot = this.prot_connect_point - (0,this.substrate_height+this.piezo_height);
- filldraw(this.box(this.prot_connect_point, this.substrate_width, this.substrate_height),
- fillpen=this.substrate_fill);
- label("Substrate", this.prot_connect_point - (0,this.substrate_height/2));
- draw(this.box(piezo_top, this.piezo_width, this.piezo_height));
- label("Piezo", piezo_top, S);
- arrow(b=piezo_bot, dir=N, length=this.piezo_height/3, Arrow(size=8pt));
- }
-
- void operator init(pair prot_connect_point=(0,0)) {
- this.prot_connect_point = prot_connect_point;
- }
-}
-
-void distance(Label L, pair start, pair end) {
- pair center = (start+end)/2;
- pair u = unit(end-start);
- picture picL;
- label(picL, L);
- pair label_size = 1.2 * (max(picL)-min(picL));
- real arrow_length = length(end-start)/2 - label_size.y;
- if (arrow_length > 4pt) {
- arrow(b=start, dir=u, length=arrow_length, Arrow(size=4pt));
- arrow(b=end, dir=-u, length=arrow_length, Arrow(size=4pt));
- }
- label(L, center);
-}
-
defaultpen(fontsize(size=10pt, lineskip=10pt));
bool folded[] = {true,true,false,true};
// position base so tip touches protein when bent
c.set_tip_for_dx(12pt);
c.align_tip(p.end);
-// now unbend the tip and draw it in grey
+// 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);
-// now rebend the tip and draw it in black
+// rebend the tip and draw it in black
c.set_tip_for_dx(12pt);
filldraw(c.tip_guide(), fillpen=black);
draw(c.arm_guide(), black);
label("Cantilever", c.base-(0,10pt), SW);
Substrate s = Substrate();
-s.draw();
+s.draw(dir=S);
real xt_x = -0.45*s.substrate_width;
real xi_x = -0.27*s.substrate_width;