From: W. Trevor King Date: Mon, 20 May 2013 13:40:02 +0000 (-0400) Subject: media/src/asy: Add Asympote graphics from my thesis X-Git-Tag: thesis-v1.0~32 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=fe19669418d4b134bcb2bd1164fd14d39a60b158;p=reveal.js.git media/src/asy: Add Asympote graphics from my thesis --- diff --git a/media/src/asy/afm.asy b/media/src/asy/afm.asy new file mode 100644 index 0000000..432bdbb --- /dev/null +++ b/media/src/asy/afm.asy @@ -0,0 +1,33 @@ +// 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); diff --git a/media/src/asy/base_afm.asy b/media/src/asy/base_afm.asy new file mode 100644 index 0000000..2a58f37 --- /dev/null +++ b/media/src/asy/base_afm.asy @@ -0,0 +1,246 @@ +// 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; // 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) (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; + } + + 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); + } + 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; + p = this.tip -- this.tip_distal -- this.tip_proximal -- cycle; + return p; + } + + guide arm_guide() { + path p; + if (this.dx == 0) { + // all three points should be colinear + p = this.tip_distal -- this.tip_proximal -- this.base; + } else { + 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); + 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=195, 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, L=""); + } + } + + void operator init(pair prot_connect_point=(0,0)) { + this.prot_connect_point = prot_connect_point; + } +} + +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); + 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), L=""); + arrow(b=end, dir=-u, length=arrow_length, Arrow(size=4pt), L=""); + } + label(L, center); +} diff --git a/media/src/asy/expt-sawtooth-Ls.dat b/media/src/asy/expt-sawtooth-Ls.dat new file mode 100644 index 0000000..17b1f7c --- /dev/null +++ b/media/src/asy/expt-sawtooth-Ls.dat @@ -0,0 +1,9 @@ +4.19963e-08 +6.52187e-08 +8.84682e-08 +1.10442e-07 +1.33067e-07 +1.55498e-07 +1.77802e-07 +1.99098e-07 +2.2189e-07 diff --git a/media/src/asy/expt-sawtooth.asy b/media/src/asy/expt-sawtooth.asy new file mode 100644 index 0000000..f80d5ca --- /dev/null +++ b/media/src/asy/expt-sawtooth.asy @@ -0,0 +1,71 @@ +import wtk_graph; + +size(6cm,4cm,IgnoreAspect); + +scale(Linear, Linear); +real k = infinity; /* spring constant in N/m, already accounted for (0.05) */ +real xscale=1e9; +real fscale=1e12; + +real xmin = -5e-9; +real xmax = 300e-9; +real fmin = -100e-12; +real fmax = 1000e-12; +real fnmax = 300e-12; + +real kB = 1.3806504e-23; +real T = 300; +real p = 3.7e-10; +real WLC(real x, real L, real p=p, real T=T, real kB=kB, + real xLmax=0.99, real fmax=fnmax) { + real f = infinity; + if (x < 0) + return infinity; + if (x/L <= xLmax) + f = kB*T/p*(0.25*(1/(1-x/L)^2 - 1) + x/L); + if (f > fnmax) + return infinity; + return f; +} + +void graphSawtooth(string file="datafile", real k, + int xcol=0, int fcol=1, + real xscale=1, real fscale=1, real dx=0, real df=0, + pen p=red) { + file fin=input(file).line(); + real[][] a=fin.dimension(0,0); + a=transpose(a); + real[] x=a[xcol]; + real[] f=a[fcol]; + x = x - f/k; /* Remove cantilever extension */ + graphData(x=x, y=f, xscale=xscale, yscale=fscale, + dx=dx, dy=df, p=p, dots=false); +} + +/* expt-sawtooth.dat already scaled to nm and pN */ +graphSawtooth("expt-sawtooth.dat", k=k, p=psoft); + +file fin = input('expt-sawtooth-Ls.dat').line(); +real[][] a = fin.dimension(0,0); +a = transpose(a); +real[] Ls = a[0]; +int i; +for (i=0; i 0 + clip(p, g); + fill(shift(this.center)*g); // fill the hole + add(p, this.center); // add the door panel + draw(shift(this.center)*g); // outline the hole again + } + + void operator init (pair center=(0,0), Label L="", real angle=0) { + this.center = center; + this.L = L; + this.angle = angle; + } +} + +struct Die { + pair center; + int top; + int left; + bool righthanded = true; + real side = 1cm; + real dot_ratio = 0.2; + triple up = (0, 1, 0.4); // up, but tilted out of the page + real theta = -20; + pair[][] pips; + + int right() { + /* It is traditional to combine pairs of numbers that total seven + * to opposite faces; this implies that at one vertex the faces 1, + * 2 and 3 intersect. It leaves one other abstract design choice: + * the faces representing 1, 2 and 3 respectively can be placed in + * either clockwise or counterclockwise order about this + * vertex. If the 1, 2 and 3 faces run counterclockwise around + * their common vertex, the die is called "right handed"; if they + * run clockwise it is called "left handed". Standard modern + * Western dice are right-handed, whereas Chinese dice are often + * left-handed. + * - http://en.wikipedia.org/wiki/Dice + */ + int little_top = min(this.top, 7-this.top); + int little_left = min(this.left, 7-this.left); + int little_right = 6 - little_top - little_left; + bool righthanded; + int little_ccw[] = {little_top, little_left, little_right}; + int i = 0; + little_ccw.cyclic = true; + while (little_ccw[i] != 1 && i < 3) i += 1; + if (i == 3) return -1; + if (little_ccw[i+1] == 2) righthanded = true; + else righthanded = false; + if (this.top != little_top) righthanded = !righthanded; + if (this.left != little_left) righthanded = !righthanded; + if (righthanded == this.righthanded) return little_right; + return 7-little_right; + } + + void setup_pips() { + real d = 1.0/3.5; + this.pips = new pair[7][]; + this.pips[0] = null; + this.pips[1].push((0,0)); + this.pips[2].push((-d,d)); + this.pips[2].push((d,-d)); + this.pips[3] = copy(this.pips[2]); + this.pips[3].push((0,0)); + this.pips[4] = copy(this.pips[2]); + this.pips[4].push((d,d)); + this.pips[4].push((-d,-d)); + this.pips[5] = copy(this.pips[4]); + this.pips[5].push((0,0)); + this.pips[6] = copy(this.pips[4]); + this.pips[6].push((-d,0)); + this.pips[6].push((d,0)); + } + + // z coordinate in face unitvectors will end up facing this.up + pair transform(pair face_coords, triple face_x, triple face_y) { + triple face_z = cross(unit(face_x), unit(face_y)); + triple f_c = face_coords.x*face_x + face_coords.y*face_y +0.5*face_z; + triple u = cross(unit(this.up), (0,0,1)); + if (length(u) > sqrtEpsilon) { + // rotate z coordinate to point along this.up + f_c = rotate(-aSin(length(u)), u) * f_c; + } + triple c = rotate(this.theta, this.up) * f_c; + return shift(this.center)*scale(this.side)*(c.x, c.y); + } + + guide transform(guide face_coords, triple face_x, triple face_y) { + guide g; + pair p; + pair[] c; // for holding Bezier control points + for (int i=0; i < length(face_coords); ++i) { + p = this.transform(point(face_coords, i), face_x, face_y); + if (i == 0) { + g = g--p; + } else { + c = controlSpecifier(face_coords, i-1); // {outgoing, incoming) + c[0] = this.transform(c[0], face_x, face_y); + c[1] = this.transform(c[1], face_x, face_y); + g = g..controls c[0] and c[1]..p; + } + } + if (cyclic(face_coords)) { + c = controlSpecifier(face_coords, -1); + c[0] = this.transform(c[0], face_x, face_y); + c[1] = this.transform(c[1], face_x, face_y); + g = g..controls c[0] and c[1]..cycle; + } + return g; + } + + void draw_pip(pair z, triple face_x, triple face_y) { + fill(this.transform(shift(z)*scale(this.dot_ratio/2.0)*unitcircle, + face_x, face_y)); + } + + /* azimuth = 0, elevation=90 -> facing up + * azimuth = 0, elevation=0 -> facing out of the page + * azimuth = 90, elevation=0 -> facing right + */ + void draw_face(int number, triple face_x, triple face_y) { + filldraw(this.transform(shift((-0.5,-0.5))*unitsquare, face_x, face_y), + white); + for (pair z : this.pips[number]) + this.draw_pip(z, face_x, face_y); + } + + void draw() { + this.draw_face(this.top, face_x=(1,0,0), face_y=(0,1,0)); + this.draw_face(this.left, face_x=(1,0,0), face_y=(0,0,1)); + this.draw_face(this.right(), face_x=(0,0,-1), face_y=(0,1,0)); + } + + void operator init (pair center=(0,0), int top=1, int left=2) { + this.center = center; + this.top = top; + this.left = left; + this.setup_pips(); + } +} + +Door L = Door((-2cm,0), + L=minipage("\centering{\Large Unfold}\\$P=1/3$\\(\epsdice{1}\epsdice{2})")); +Door R = Door((2cm,0), + L=minipage("\centering{\Large Stay folded}\\$Q=1-P$\\(\epsdice{3}\epsdice{4}\epsdice{5}\epsdice{6})"), + angle=30); +Die d = Die((-0.3cm, -2.5cm), top=3, left=5); + +L.draw(); +R.draw(); +d.draw(); diff --git a/media/src/asy/pbs-hist.asy b/media/src/asy/pbs-hist.asy new file mode 100644 index 0000000..8603a09 --- /dev/null +++ b/media/src/asy/pbs-hist.asy @@ -0,0 +1,19 @@ +import wtk_graph; + + +size(6cm, 4cm, IgnoreAspect); + +scale(Linear, Log); +real fscale=1e12; + +pen p = psoft; + +histFile("pbs-hist.hist", bin_scale=fscale, fillpen=(p+opacity(0.3)), + drawpen=p); + +//set xrange [100:400] +//#set yrange [0.0001:1] +//xlimits(pic, xmin, xmax); +//ylimits(pic, ymin, ymax); +xaxis(sLabel("Force (pN)"), BottomTop, LeftTicks, above=true); +yaxis(sLabel("Frequency"), LeftRight, RightTicks, above=true); diff --git a/media/src/asy/pbs-hist.dat b/media/src/asy/pbs-hist.dat new file mode 100644 index 0000000..e9c177d --- /dev/null +++ b/media/src/asy/pbs-hist.dat @@ -0,0 +1,131 @@ +1.84458445371e-10 +2.36485186373e-10 +1.91553000963e-10 +1.08783185732e-10 +5.49828058318e-11 +1.12921676493e-10 +1.01097417175e-10 +1.24154722846e-10 +2.2820820485e-10 +2.51856723488e-10 +2.35302760442e-10 +1.92735426894e-10 +2.05150899179e-10 +1.97465130622e-10 +2.39441251203e-10 +2.22296075191e-10 +2.32346695612e-10 +1.94509065792e-10 +2.11063028838e-10 +2.41214890101e-10 +1.22381083948e-10 +1.63765991564e-10 +2.06333325111e-10 +2.42988528999e-10 +2.41806103067e-10 +2.52447936454e-10 +2.67819473568e-10 +2.37667612305e-10 +1.5844507487e-10 +1.76772676814e-10 +2.21704862225e-10 +9.63677134472e-11 +1.90370575031e-10 +2.25843352987e-10 +2.02194834349e-10 +1.81502380542e-10 +2.27025778919e-10 +1.82093593508e-10 +1.89188149099e-10 +1.93917852826e-10 +1.56671435972e-10 +1.76181463848e-10 +2.13427880702e-10 +2.73140390261e-10 +2.19931223327e-10 +2.11063028838e-10 +1.37752621063e-10 +2.33529121544e-10 +2.30573056714e-10 +1.72042973087e-10 +1.52532945211e-10 +2.03377260281e-10 +1.65539630461e-10 +2.10471815872e-10 +2.00421195452e-10 +2.34711547476e-10 +2.25252140021e-10 +2.38850038237e-10 +1.51350519279e-10 +2.27025778919e-10 +1.83276019439e-10 +2.00421195452e-10 +2.47718232726e-10 +2.83782223648e-10 +9.51852875153e-11 +7.50840466736e-11 +8.74995189582e-11 +8.27698152307e-11 +1.96873917656e-10 +1.63174778598e-10 +2.01012408417e-10 +2.50674297556e-10 +1.64948417495e-10 +2.06924538077e-10 +2.00421195452e-10 +2.19340010361e-10 +1.86823297235e-10 +1.72634186053e-10 +2.1224545477e-10 +2.60133705011e-10 +2.71366751364e-10 +2.5303914942e-10 +1.57853861904e-10 +1.30066852505e-10 +2.21704862225e-10 +2.24069714089e-10 +2.75505242125e-10 +2.90285566273e-10 +3.42312307276e-10 +3.99068752005e-10 +1.18833806153e-10 +1.9923876952e-10 +2.10471815872e-10 +2.05150899179e-10 +1.86232084269e-10 +2.32937908578e-10 +2.59542492045e-10 +1.79728741644e-10 +2.06924538077e-10 +1.67313269359e-10 +1.70269334189e-10 +1.9628270469e-10 +1.60218713768e-10 +2.19340010361e-10 +1.90370575031e-10 +2.64863408738e-10 +2.42397316033e-10 +2.08106964009e-10 +1.83276019439e-10 +2.20522436293e-10 +2.3116426968e-10 +3.4172109431e-10 +2.90876779239e-10 +2.81417371784e-10 +2.21113649259e-10 +2.08106964009e-10 +2.1224545477e-10 +2.22296075191e-10 +2.30573056714e-10 +2.61316130943e-10 +1.57262648938e-10 +2.14610306634e-10 +2.23478501123e-10 +1.51350519279e-10 +1.6435720453e-10 +1.56671435972e-10 +2.00421195452e-10 +2.28799417816e-10 +1.29475639539e-10 +1.06418333868e-10 +1.24745935812e-10 diff --git a/media/src/asy/pbs-hist.hist b/media/src/asy/pbs-hist.hist new file mode 100644 index 0000000..a037bbc --- /dev/null +++ b/media/src/asy/pbs-hist.hist @@ -0,0 +1,18 @@ +4e-11 1 +6e-11 1 +8e-11 4 +1e-10 5 +1.2e-10 6 +1.4e-10 8 +1.6e-10 13 +1.8e-10 18 +2e-10 25 +2.2e-10 26 +2.4e-10 10 +2.6e-10 7 +2.8e-10 4 +3e-10 0 +3.2e-10 0 +3.4e-10 2 +3.6e-10 0 +3.8e-10 1 diff --git a/media/src/asy/piezo.asy b/media/src/asy/piezo.asy new file mode 100644 index 0000000..fa61a6f --- /dev/null +++ b/media/src/asy/piezo.asy @@ -0,0 +1,232 @@ +// Cylindrical piezo operating principle graphic + +//size(x=9cm,keepAspect=true); + +import graph3; +import three; + +pen piezo_pen = opacity(0.1) + grey; +pen x_electrode_pen = opacity(0.4) + red; +pen y_electrode_pen = opacity(0.4) + blue; +pen z_electrode_pen = opacity(0.4) + green; +pen outline_pen = linewidth(1pt) + black; + +real R = 1.1cm; +real r = 0.9cm; +real h = 5cm; +real ethick = 1pt; +real espace = 1cm; +real theta1 = 0.4*pi; +real dtheta = 2*asin(espace/(R+r)/4); +real theta2 = theta1 + pi/2 - dtheta; +real tavg = (theta1 + theta2)/2; +real dr = 4pt; +real dR = 6pt; + +currentprojection = FrontView; + +triple cylinder(real r, real theta, real z) { + return (r*cos(theta), r*sin(theta), z); +} + +struct Cylinder { + real r; + real R; + real z1; + real z2; + real theta1; + real theta2; + + void operator init(real r=0, real R=1, real z1=0, real z2=1, + real theta1=0, real theta2=2pi) { + this.r = r; + this.R = R; + this.z1 = z1; + this.z2 = z2; + this.theta1 = theta1; + this.theta2 = theta2; + } + + triple _cap(pair t) { + return cylinder(t.x, t.y, 0); + } + + triple _inner_wall(pair t) { + return cylinder(this.r, t.x, t.y); + } + + triple _outer_wall(pair t) { + return cylinder(this.R, t.x, t.y); + } + + triple _box1(pair t) { + return cylinder(t.x, this.theta1, t.y); + } + + triple _box2(pair t) { + return cylinder(t.x, this.theta2, t.y); + } + + surface cap() { + // surface() described in graph3 documentation. + return surface( + this._cap, (this.r, this.theta1), (this.R, this.theta2), 8, 8, Spline); + } + + surface[] surfaces() { + surface ret[]; + surface cap = this.cap(); + ret.push(shift(0, 0, this.z1)*cap); + ret.push(shift(0, 0, this.z2)*cap); + ret.push(surface( + this._outer_wall, (this.theta1, this.z1), (this.theta2, this.z2), + 8, 8, Spline)); + ret.push(surface( + this._inner_wall, (this.theta1, this.z1), (this.theta2, this.z2), + 8, 8, Spline)); + if (this.theta1 % 2pi != this.theta2 % 2pi) { + ret.push(surface(this._box1, (this.r, this.z1), (this.R, this.z2))); + ret.push(surface(this._box2, (this.r, this.z1), (this.R, this.z2))); + } + return ret; + } + + path3[] cap_paths() { + triple c = (0, 0, 0); + real t1 = degrees(this.theta1); + real t2 = degrees(this.theta2); + return new path3[] { + arc(c, this.r, 90, t1, 90, t2, (0, 0, 1)), + arc(c, this.R, 90, t1, 90, t2, (0, 0, 1)), + }; + } + + path3[] outline() { + path3 ret[]; + path3 caps[] = this.cap_paths(); + ret.push(shift(0, 0, this.z1)*caps[0]); + ret.push(shift(0, 0, this.z1)*caps[1]); + ret.push(shift(0, 0, this.z2)*caps[0]); + ret.push(shift(0, 0, this.z2)*caps[1]); + if (this.theta1 % 2pi != this.theta2 % 2pi) { + ret.push( + cylinder(this.r, this.theta1, this.z1) -- + cylinder(this.r, this.theta1, this.z2) -- + cylinder(this.R, this.theta1, this.z2) -- + cylinder(this.R, this.theta1, this.z1) -- cycle); + ret.push( + cylinder(this.r, this.theta2, this.z1) -- + cylinder(this.r, this.theta2, this.z2) -- + cylinder(this.R, this.theta2, this.z2) -- + cylinder(this.R, this.theta2, this.z1) -- cycle); + } + return ret; + } + + void draw(pen surface_pen, pen outline_pen, transform3 t=identity4) { + surface surfaces[] = this.surfaces(); + path3 paths[] = this.outline(); + for (int i=0; i < surfaces.length; ++i) { + draw(t*surfaces[i], surface_pen, nolight); + } + for (int i=0; i < paths.length; ++i) { + draw(t*paths[i], outline_pen, nolight); + } + } + + void cap(pen surface_pen, pen outline_pen, transform3 t=identity4) { + surface cap = this.cap(); + path3 paths[] = this.cap_paths(); + draw(t*cap, surface_pen, nolight); + for (int i=0; i < paths.length; ++i) { + draw(t*paths[i], outline_pen, nolight); + } + } +} + +void wire(Label label="", triple a, triple b, transform3 t=identity4, + align align=NoAlign) { + draw(t*(a--b)); + dot(t*a); + dot(label, t*b, align=align); +} + +Cylinder piezo = Cylinder(r, R, -h/2, h/2); +Cylinder z_in = Cylinder(r-ethick, r, espace/2, h/2-espace/2, 0, 2pi); +Cylinder z_out = Cylinder(R, R+ethick, espace/2, h/2-espace/2, 0, 2pi); +Cylinder xa_in = Cylinder( + r-ethick, r, -h/2+espace/2, -espace/2, theta1, theta2); +Cylinder xa_out = Cylinder( + R, R+ethick, -h/2+espace/2, -espace/2, theta1, theta2); +Cylinder ya_in = Cylinder( + r-ethick, r, -h/2+espace/2, -espace/2, theta1+pi/2, theta2+pi/2); +Cylinder ya_out = Cylinder( + R, R+ethick, -h/2+espace/2, -espace/2, theta1+pi/2, theta2+pi/2); +Cylinder xb_in = Cylinder( + r-ethick, r, -h/2+espace/2, -espace/2, theta1+pi, theta2+pi); +Cylinder xb_out = Cylinder( + R, R+ethick, -h/2+espace/2, -espace/2, theta1+pi, theta2+pi); +Cylinder yb_in = Cylinder( + r-ethick, r, -h/2+espace/2, -espace/2, theta1-pi/2, theta2-pi/2); +Cylinder yb_out = Cylinder( + R, R+ethick, -h/2+espace/2, -espace/2, theta1-pi/2, theta2-pi/2); + +transform3 piezo_transform = shift(-1.5*R, 0, 0)*rotate(12, (1, 0, 0)); + +piezo.draw(piezo_pen, outline_pen, piezo_transform); +z_in.draw(z_electrode_pen, outline_pen, piezo_transform); +z_out.draw(z_electrode_pen, outline_pen, piezo_transform); +xa_in.draw(x_electrode_pen, outline_pen, piezo_transform); +xa_out.draw(x_electrode_pen, outline_pen, piezo_transform); +ya_in.draw(y_electrode_pen, outline_pen, piezo_transform); +ya_out.draw(y_electrode_pen, outline_pen, piezo_transform); +xb_in.draw(x_electrode_pen, outline_pen, piezo_transform); +xb_out.draw(x_electrode_pen, outline_pen, piezo_transform); +yb_in.draw(y_electrode_pen, outline_pen, piezo_transform); +yb_out.draw(y_electrode_pen, outline_pen, piezo_transform); + + +transform3 z_transform = shift((1.5*R, 0, 1.3*R))*rotate(90, (1, 0, 0)); +transform3 label_transform = shift((0, 1cm, 0)); + +piezo.draw(piezo_pen, invisible, z_transform); +z_in.draw(z_electrode_pen, invisible, z_transform); +z_out.draw(z_electrode_pen, invisible, z_transform); +wire("$z_-$", (-R-ethick, 0, 0), (-R-ethick, 3R/4, 0), + label_transform*z_transform, align=W); +wire("$z_+$", (-r+ethick, 0, 0), (-r/3, 0, 0), + label_transform*z_transform, align=E); + +transform3 xy_transform = shift((1.5*R, 0, -1.3*R))*rotate(90, (1, 0, 0)); + +piezo.draw(piezo_pen, invisible, xy_transform); +xa_in.draw(x_electrode_pen, invisible, xy_transform); +xa_out.draw(x_electrode_pen, invisible, xy_transform); +ya_in.draw(y_electrode_pen, invisible, xy_transform); +ya_out.draw(y_electrode_pen, invisible, xy_transform); +xb_in.draw(x_electrode_pen, invisible, xy_transform); +xb_out.draw(x_electrode_pen, invisible, xy_transform); +yb_in.draw(y_electrode_pen, invisible, xy_transform); +yb_out.draw(y_electrode_pen, invisible, xy_transform); +wire("$x_-$", cylinder(r-ethick, tavg, 0), cylinder(r-ethick-dr, tavg, 0), + label_transform*xy_transform, align=-expi(tavg)); +wire("$x_+$", cylinder(R+ethick, tavg, 0), cylinder(R+ethick+dR, tavg, 0), + label_transform*xy_transform, align=expi(tavg)); +wire("$y_-$", cylinder(r-ethick, tavg + pi/2, 0), + cylinder(r-ethick-dr, tavg + pi/2, 0), + label_transform*xy_transform, align=-expi(tavg + pi/2)); +wire("$y_+$", cylinder(R+ethick, tavg + pi/2, 0), + cylinder(R+ethick+dR, tavg + pi/2, 0), + label_transform*xy_transform, align=expi(tavg + pi/2)); +wire("$x_+$", cylinder(r-ethick, tavg + pi, 0), + cylinder(r-ethick-dr, tavg + pi, 0), + label_transform*xy_transform, align=-expi(tavg + pi)); +wire("$x_-$", cylinder(R+ethick, tavg + pi, 0), + cylinder(R+ethick+dR, tavg + pi, 0), + label_transform*xy_transform, align=expi(tavg + pi)); +wire("$y_+$", cylinder(r-ethick, tavg - pi/2, 0), + cylinder(r-ethick-dr, tavg - pi/2, 0), + label_transform*xy_transform, align=-expi(tavg - pi/2)); +wire("$y_-$", cylinder(R+ethick, tavg - pi/2, 0), + cylinder(R+ethick+dR, tavg - pi/2, 0), + label_transform*xy_transform, align=expi(tavg - pi/2)); diff --git a/media/src/asy/unfolding.asy b/media/src/asy/unfolding.asy new file mode 100644 index 0000000..00e6623 --- /dev/null +++ b/media/src/asy/unfolding.asy @@ -0,0 +1,111 @@ +// Protein unfolding graphic + +//size(x=9cm,keepAspect=true); + +import base_afm; // for Cantilever, Substrate, ... +import wiggle; // for Wiggle + +struct ProteinChain { + // Generate and draw a mixed protein (2nd protein unfolded) + // starting at START (x,y) + // of folded length LEN, with NUM segments + // with the unfolded protein's dimensions given by UNFOLD_LEN and UNFOLD_WID + // (so the final length with be LEN - LEN/NUM + UNFOLD_LEN + pair start; + pair end; + real angle; + real len_u = 2cm; + real len_f = 0.5cm; + real rough_u = 0.4cm; + real rough_f = 0.6cm; + Wiggle prot_u; + Wiggle prot_f; + bool folded[]; + + void gen_prots() { + this.prot_u = Wiggle(end=(0,this.len_u), roughness=this.rough_u); + this.prot_f = Wiggle(end=(0,this.len_f), roughness=this.rough_f); + } + + pair prot_start(int n) { + assert(n < this.folded.length); + pair start = this.start; + pair u = dir(this.angle); + for (int i; i < n; i+=1) { + if (this.folded[i] == true) + start += u*this.len_f; + else + start += u*this.len_u; + } + return start; + } + + Wiggle prot(int n) { + assert(n < this.folded.length); + if (this.folded[n] == true) + return this.prot_f; + else + return this.prot_u; + } + + guide guide() { + guide g; + guide p; + for (int i=0; i < this.folded.length; i+=1) { + p = shift(this.prot_start(i))*this.prot(i).guide(); + g = g .. p; + } + return g; + } + + void operator init(pair start=(0,0), real angle=90, + bool folded[]={true,true,false,true}) { + this.start = start; + this.angle = angle; + this.folded = folded; + this.gen_prots(); + guide g = this.guide(); + this.end = point(g, length(g)); + } +} + +defaultpen(fontsize(size=10pt, lineskip=10pt)); + +bool folded[] = {true,true,false,true}; +ProteinChain p = ProteinChain(folded=folded); +draw(p.guide(), red); +label("\vbox{\hbox{Folded}\hbox{protein}}", p.prot_start(1)+(1.4*p.rough_f, 0)/2, E); +label("\vbox{\hbox{Unfolded}\hbox{protein}}", p.prot_start(2)+(1.3*p.rough_u, p.len_u)/2, E); + + +Cantilever c = Cantilever(); +// position base so tip touches protein when bent +c.set_tip_for_dx(12pt); +c.align_tip(p.end); +// 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(12pt); +filldraw(c.tip_guide(), fillpen=black); +draw(c.arm_guide(), black); +label("Cantilever", c.base-(0,16pt), SW); + +Substrate s = Substrate(); +s.draw(dir=S); + +real xt_x = -0.45*s.substrate_width; +real xi_x = -0.27*s.substrate_width; +real bar_extra = 6pt; +distance("$x_t$", (xt_x, 0), (xt_x, unbent_tip.y)); +distance("$x_c$", (xi_x, c.tip.y), (xi_x, unbent_tip.y)); +distance("$x_{f1}$", (xi_x, p.prot_start(3).y), (xi_x, c.tip.y)); +distance("$x_u$", (xi_x, p.prot_start(2).y), (xi_x, p.prot_start(3).y)); +distance("$x_{f2}$", (xi_x, 0), (xi_x, p.prot_start(2).y)); +real y; +y = unbent_tip.y; draw((xt_x-bar_extra,y)--(xi_x+bar_extra,y)); +y = p.prot_start(2).y; draw((xi_x-bar_extra,y)--(xi_x+bar_extra,y)); +y = p.prot_start(3).y; draw((xi_x-bar_extra,y)--(xi_x+bar_extra,y)); +y = c.tip.y; draw((xi_x-bar_extra,y)--(xi_x+bar_extra,y)); diff --git a/media/src/asy/wiggle.asy b/media/src/asy/wiggle.asy new file mode 100644 index 0000000..8ea6546 --- /dev/null +++ b/media/src/asy/wiggle.asy @@ -0,0 +1,122 @@ +// Define Wiggle structure for drawing random wiggles. + +struct Wiggle { + // Generate a horizontal wiggly line, centered at CENTER (x,y) + // with a width WIDTH x + // and randomly distributed heights. + // The random points are unformly distributed within an envelope + // with the maximum possible deviation given by ROUGHNESS x + + pair start; + pair end; + real roughness; + real tense; + int num; + int random_seed; + pair points[]; + + void generate(bool labels=false) { + int n = this.num; + pair step = (this.end-this.start) / (n-1); + pair perp = scale(this.roughness)*rotate(90)*unit(step); + real y, envelope; + srand(this.random_seed); + for (int i=0; i < n; i+=1) { + if (i == (n-1)/2 || i == 0 || i == n-1) { + y = 0.0; + } else { + real frac = i/n; + envelope = (frac*(1.0-frac)*4)**3; + y = (2*unitrand()-1.0) * envelope; + } + this.points.push(this.start + i*step + perp*y); + if (labels==true) + dot(format("%d", i), this.points[-1], S); + } + } + + guide guide() { + guide g; + for (int i=0; i < this.points.length; i+=1) { + g = g .. tension this.tense .. this.points[i]; + } + this.end = point(g, length(g)); + return g; + } + + void operator init(pair start=(0,0), pair end=(1cm,0), real roughness=1cm, + real tense=1, int num=15, int random_seed=5) { + this.start = start; + this.end = end; + this.roughness = roughness; + this.tense = tense; + this.num = num; + this.random_seed = random_seed; + this.generate(); + } +} + +struct RandomWalk { + // Generate a random walk starting from START (x,y) + // with NUM steps, each of step-size DX, + // and randomly distributed step directions. + + pair start; + pair end; + real dx; + int num; + real tense; + int random_seed; + pair points[]; + + void generate(bool labels=false) { + int n = this.num; + srand(this.random_seed); + pair p = this.start; + this.points.push(p); + if (labels==true) + dot("0", this.points[0], S); + for (int i=1; i <= this.num; i+=1) { + real theta = 360*unitrand(); + p += rotate(theta)*(this.dx, 0); + this.points.push(p); + if (labels==true) + dot(format("%d", i), this.points[i], S); + } + this.end = p; + } + + void align(real dir) { + real dtheta = dir - degrees(this.end - this.start); + for (int i=1; i < this.points.length; i+=1) { + this.points[i] = rotate(dtheta, this.start)*this.points[i]; + } + this.end = this.points[this.num]; + } + + guide curved_guide() { + guide g; + for (int i=0; i < this.points.length; i+=1) { + g = g .. tension this.tense .. this.points[i]; + } + return g; + } + + guide straight_guide(interpolate join=operator ..) { + guide g; + for (int i=0; i < this.points.length; i+=1) { + g = g -- this.points[i]; + } + return g; + } + + void operator init(pair start=(0,0), real dx=3mm, + real tense=1, int num=15, int random_seed=25) { + this.start = start; + this.dx = dx; + this.tense = tense; + this.num = num; + this.random_seed = random_seed; + this.generate(); + } +} diff --git a/media/src/asy/wlc-model.asy b/media/src/asy/wlc-model.asy new file mode 100644 index 0000000..e8e6034 --- /dev/null +++ b/media/src/asy/wlc-model.asy @@ -0,0 +1,7 @@ +import wiggle; // for RandomWalk + +RandomWalk w = RandomWalk(dx=1cm); +w.align(90); +draw(w.curved_guide(), red); +dot(w.start); +dot(w.end); diff --git a/media/src/asy/wtk_graph.asy b/media/src/asy/wtk_graph.asy new file mode 100644 index 0000000..77e1395 --- /dev/null +++ b/media/src/asy/wtk_graph.asy @@ -0,0 +1,228 @@ +import graph; +import palette; +import stats; + + +Label sLabel(string s, align align=NoAlign, int extra_spaces=0) { + string spaces = ""; + int i; + for (i=0; i= 0) + x = a[xcol]; + else + x = sequence(y.length); + graphData(pic=pic, x=x, y=y, xscale=xscale, yscale=yscale, dx=dx, dy=dy, p=p, + mpath=mpath, markroutine=markroutine, t=t, dots=dots); +} + +void fitFile(picture pic=currentpicture, string file="fitparamfile", + int pcol=0, real f(real x, real[] params), + real xmin=realMin, real xmax=realMax, + real xscale=1, real yscale=1, pen p=red, + string t="Title") { + file fin=input(file).line(); + real[][] a=fin.dimension(0,0); + a=transpose(a); + real[] params=a[pcol]; + real fn(real x) { + return f(x / xscale, params) * yscale; + } + /* should be able to extract xmin and xmax from the figure... + if (xmin == realMin) + xmin = point(pic, W).x / xscale; + if (xmax == realMax) + xmax = point(pic, E).x / xscale; + */ + if (t != "Title") { + draw(pic=pic, graph(pic, fn, a=xmin*xscale, b=xmax*xscale), p=p, + legend=sLabel(t,extra_spaces=1)); + } else { + draw(pic=pic, graph(pic, fn, a=xmin*xscale, b=xmax*xscale), p=p); + } +} + +void histFile(picture pic=currentpicture, string file="datafile", + int bin_col=0, int count_col=1, + real bin_scale=1, real count_scale=1, real low=-infinity, + pen fillpen=red, pen drawpen=nullpen, bool bars=false, + string t="Title") { + file fin = input(file).line(); + real[][] a = fin.dimension(0,0); + a = transpose(a); + real[] bins = a[bin_col] * bin_scale; + real[] count = a[count_col] * count_scale; + /* Convert bin-center marks to bin-edge marks */ + real bin_width = bins[1]-bins[0]; + bins = bins - bin_width / 2; /* shift centers to the left */ + bins.push(bins[bins.length-1]+bin_width); /* add terminal edge on right */ + /* Normalize counts -> frequency */ + real total = sum(count); + count = count / total; + if (t != "Title") { + histogram(pic=pic, bins=bins, count=count, low=low, + fillpen=fillpen, drawpen=drawpen, bars=bars, + legend=sLabel(t,extra_spaces=1)); + } else { + histogram(pic=pic, bins=bins, count=count, low=low, + fillpen=fillpen, drawpen=drawpen, bars=bars); + } +} + +real[] identity_zfn(real[] z) { + return z; +} + +/* Return a sorted list of unique entries in an array x */ +real[] set(real[] x) { + int i=1; + x = sort(x); + while (i < x.length){ + if (x[i] == x[i-1]) + x.delete(i); + else + i += 1; + } + return x; +} + +/* Convert x,y,z arrays to a z matrix */ +real[][] extract_matrix(real[] xs, real[] ys, real[] zs) { + int i, j, k; + real[] x_set = set(xs); + real[] y_set = set(ys); + real[] z_col = array(y_set.length, 0); + real[][] z_matrix = array(n=x_set.length, z_col); + //assert zs.length == x_set.length*y_set.length; + for (i=0; i= 0; --i) + if (a[i].length < 3) + a.delete(i); + a = transpose(a); + real[] xs = a[xcol]; + real[] ys = a[ycol]; + real[] zs = zfn(a[zcol]); + real[][] z = extract_matrix(xs, ys, zs); + pair initial = (xscale*min(xs), yscale*min(ys)); + pair final = (xscale*max(xs), yscale*max(ys)); + //bounds range = image(pic=pic, x=xscale*x, y=yscale*y, f=z, palette=p); + bounds range = image(pic=pic, z, initial, final, range=Automatic, palette=p); + // Add axes before palette or palette will end up in same box as the image + if (x_label != null) + xaxis(pic, x_label, BottomTop,LeftTicks,above=true); + if (y_label != null) + yaxis(pic, y_label, LeftRight,RightTicks,above=true); + transform graph_to_pic = pic.calculateTransform(); + transform pic_to_graph = inverse(graph_to_pic); + pair p_SE_pic = graph_to_pic * (xscale*max(xs), yscale*min(ys)); + pair p_initial_graph = pic_to_graph * (p_SE_pic + palette_offset*E); + pair p_NE_pic = graph_to_pic * (xscale*max(xs), yscale*max(ys)); + pair p_final_graph = pic_to_graph * (p_NE_pic + + (palette_offset+palette_width)*E); + if (palette_label == null) + palette(pic, bounds=range, initial=p_initial_graph, final=p_final_graph, + axis=Right, palette=p); + else + palette(pic, L=palette_label, bounds=range, initial=p_initial_graph, + final=p_final_graph, axis=Right, palette=p); +} + +string math(string contents) { + return "$" + contents + "$"; +} + +string Exp(string value) { + return "\cdot 10^{" + value + "}"; +} + +string units(string value, string units) { + return value+"\mbox{ "+units+"}"; +} + +pen phard=blue; +pen pmed=green; +pen psoft=red; + +path m1=(0,0)--(1,0)--(.5,1)--cycle; +m1 = scale(3mm)*shift(-.5,-.33)*m1; +path m8=scale(2mm)*shift(-.5,-.5)*unitsquare; +path m30=scale(1.5mm)*unitcircle; +path mdot=scale(0.2pt)*unitcircle; + +markroutine marksize(string file="datafile", pen p=red, path mpath=scale(0.8mm)*unitcircle, real size=1) { + return new void(picture pic=currentpicture, frame f, path g) { + /* frame f is the marker we setup in graphFile(). We can't scale + that directly though, since the drawn line thickness would change. + Instead, we give marksize() the same p and mpath that graphFile() + got, so it can scale the path and stroke it with the unscaled pen. + */ + file fin=input(file).line(); + real[][] a=fin.dimension(0,0); + a=transpose(a); + real[] s=a[3]; + for (int i=0; i <= length(g); ++i) { + pair z = point(g, i); + frame f; + draw(f, scale(sqrt(s[i]/size))*mpath, p); + add(pic, f, z); + } + }; +}