Okay, I think I've more or less got a solution.
The silhouette function is defined by the following code:
import graph3;
import contour;
// A bunch of auxiliary functions.
real fuzz = .001;
real umin(surface s) { return 0; }
real vmin(surface s) { return 0; }
pair uvmin(surface s) { return (umin(s), vmin(s)); }
real umax(surface s, real fuzz=fuzz) {
if (s.ucyclic()) return s.index.length;
else return s.index.length - fuzz;
}
real vmax(surface s, real fuzz=fuzz) {
if (s.vcyclic()) return s.index[0].length;
return s.index[0].length - fuzz;
}
pair uvmax(surface s, real fuzz=fuzz) { return (umax(s,fuzz), vmax(s,fuzz)); }
typedef real function(real, real);
function normalDot(surface s, triple eyedir) {
real toreturn(real u, real v) {
return dot(s.normal(u, v), eyedir);
}
return toreturn;
}
guide[] normalpathuv(surface s, triple eyedir, int n = ngraph) {
return contour(normalDot(s, eyedir), uvmin(s), uvmax(s), new real[] {0}, nx=n)[0];
}
path3 onSurface(surface s, path p) {
triple f(real t) {
pair point = point(p,t);
return s.point(point.x, point.y);
}
if (cyclic(p)) {
guide3 toreturn = f(0);
for (int i = 1; i < size(p); ++i)
toreturn = toreturn -- f(i);
toreturn = toreturn -- cycle;
return toreturn;
}
return graph(f, 0, length(p));
}
/*
* This method returns an array of paths that trace out all the
* points on s at which s is parallel to eyedir.
*/
path3[] silhouetteNoEdges(surface s, triple eyedir, int n = ngraph) {
guide[] uvpaths = normalpathuv(s, eyedir, n);
path3[] toreturn = new path3[uvpaths.length];
for (int i = 0; i < uvpaths.length; ++i) {
toreturn[i] = onSurface(s, uvpaths[i]);
}
return toreturn;
}
/*
* Now, add in the edges (if there are any).
*/
path3[] silhouette(surface s, triple eyedir, int n = ngraph) {
path3[] toreturn = silhouetteNoEdges(s, eyedir, n);
if (!s.ucyclic()) {
toreturn.push(s.uequals(umin(s)));
toreturn.push(s.uequals(umax(s)));
}
if (!s.vcyclic()) {
toreturn.push(s.vequals(vmin(s)));
toreturn.push(s.vequals(vmax(s)));
}
return toreturn;
}
After saving the code above in a file called silhouette.asy
, here's how you draw a silhouette of your surface:
settings.outformat="pdf";
int resolutionfactor = 4;
settings.render=2.resolutionfactor;
settings.prc=false;
import silhouette;
size(200);
triple eye = (6,8,2);
currentprojection=orthographic(eye);
viewportmargin=(1cm,0);
real c0=0.1;
real f(real r) {return r*(1-r/6)*exp(-r/3);}
/* Note that the function below has been modified so as not to throw a divide by zero error
* when f(r) == 0.
*/
triple f(pair t) {
real r=t.x;
real phi=t.y;
real f=f(r);
real s;
//This assumes c0 > 0
if (0 > f && f > -c0) s = -1;
else if (0 <= f && f < c0) s = 1;
else s = c0/f;
//real s=max(min(c0/f,1),-1);
real R=r*sqrt(1-s^2);
return (R*cos(phi),R*sin(phi),r*s);
}
bool cond(pair t) {return f(t.x) != 0;}
real R=abs((20,20,20));
surface s=surface(f,(0,0),(R,2pi),nu=100,nv=8,Spline);
draw(silhouette(s,eye,n=200));
draw(s,surfacepen=emissive(white));
surface s2 = zscale3(-1)*s;
draw(silhouette(s2, eye, n=200));
draw(s2, surfacepen=emissive(white));
shipout(scale(resolutionfactor)*currentpicture.fit());
Here's the result:
Best Answer
From what I understand, arcs are defined mostly in two different ways:
In the first case, the starting and end points are defined through Cartesian coordinates, while in the second case, they are defined through spherical coordinates. I think there are other ways when you know the normal of the plane in which the arc is drawn. Have a look at the code below and the corresponding figure.