Is there, in Asymptote an equivalent of the fill command that works for planar 3D paths?

I thought surface did this: I expected the commands




to always produce identical results, for any planar path3 p. However, this does not work for the path I am working on (called tile(identity4) in the code below): basically it is some nonconvex shape, and the 3D surface contains parts of its convex hull not enclosed by the path.

I did not manage to produce a simple MWE. For what it's worth, here is my code; I'm afraid it is a bit complicated to understand in detail! I removed every definition irrelevant to the problem; but when I tried to simplify it further, it suddently stopped compiling in a reasonable time, so I dropped the matter.

You might ask why I do not simply use the former command. The problem is that I have other data in my picture, and it seems that when I draw a 2D picture, all previously drawn 3D objects get erased.

import three;
import graph3;
import solids;

real tmax = 1.15; //contraction strength
real translation_speed = 0.2; //ratio of translation part over contraction part of an infinitesimal generator
real height = 1; //height of the cutting plane
real radius = 5; //radius of the "projective sphere
real width = 2; //how far do the "infinite" surfaces actually go
int subdiv = 1; //how much we subdivide curves when projecting to the sphere

transform3 squish(real t) {
  return scale(exp(-t), 1, exp(t)) * shift(t*translation_speed*Y);
transform3 halfgplus = squish(tmax);
transform3 halfgminus = squish(-tmax);
transform3 gplus = squish(2*tmax);
transform3 gminus = squish(-2*tmax);
transform3 conj = rotate(90,X+Z);
transform3 halfhplus = conj * halfgplus * inverse(conj);
transform3 halfhminus = conj * halfgminus * inverse(conj);
transform3 hplus = conj * gplus * inverse(conj);
transform3 hminus = conj * gminus * inverse(conj);

triple c1 = (1,0,1);
triple c3 = (1,0,-1);
triple c5 = (-1,0,-1);
triple c7 = (-1,0,1);

path3 tennisball = scale3(width) *
(arc((1,0,0),c1,c3,-X) &
arc((0,0,-1),c3,c5,-Z) &
arc((-1,0,0),c5,c7,X) &

path3[] cut(path3 p, triple center) {
//Cuts the path p by the plane x+z = height, then projects the upper part onto the plane.
//Returns {flattened upper part, lower part}.
//Assumes exactly 2 intersections.
  path p_projected = project(p, orthographic(1,0,-1));
  real [] cuttimes = times(p_projected, (0, height));
  real t1 = cuttimes[0];
  real t2 = cuttimes[1];
  pair test_point = point(p_projected, (t1+t2)/2);
  path3 upper_part = (test_point.y >= height) ? subpath(p, t1, t2) :
                                                subpath(p, t2, length(p)) & subpath(p, 0, t1);
  path3 lower_part = (test_point.y >= height) ? subpath(p, t2, length(p)) & subpath(p, 0, t1) :
                                                subpath(p, t1, t2);
  triple projection_target = planeproject(X+Z, sqrt(2)*height*Z) * center;
  path plane_upper_part = project(upper_part, perspective(center, target=projection_target));
  path3 flat_upper_part = invert(plane_upper_part, X+Z, sqrt(2)*height*Z, perspective(center, target=projection_target));
  return new path3[] {flat_upper_part, lower_part};

triple sph_proj(triple x, triple proj_cen) {
//Projects a point from some center (assumed to be inside the sphere) onto the sphere of center O and given radius.
  triple v = unit(x - proj_cen); //Unit vector generating the half-line.
  triple q = proj_cen - dot(v, proj_cen) * v; //Point of the line closest to O.
  return q + (radius +-+ abs(q)) * v;

path3 sph_proj_path(path3 p, triple proj_cen) {
//Projects a smooth, cyclic, non null path from some center (assumed to be inside the sphere) onto the sphere of center O and given radius.
  int n=length(p);
  guide3 g=sph_proj(point(p,0), proj_cen);
  for(int i=1; i < n*subdiv; ++i)
    g=g..sph_proj(point(p,i/subdiv), proj_cen);
  return g..cycle;

path3 side(transform3 gamma) {
  triple cen = gamma * O;
  return cut(sph_proj_path((gamma * tennisball), cen), cen)[0];

path3 tile(transform3 gamma) {
  path3 side_gplus = side(gamma * halfgplus);
  path3 side_gminus = side(gamma * halfgminus);
  path3 side_hplus = side(gamma * halfhplus * conj);
  path3 side_hminus = side(gamma * halfhminus * conj);
  path3 tile_contour = reverse(side_gplus) -- reverse(side_hplus) -- side_gminus -- side_hminus -- cycle;
  return planeproject(X+Z, sqrt(2)*height*Z) * tile_contour;

draw(surface(tile(identity4)), gray+opacity(0.8));

Update: I also tried

draw(surface(project(p, orthographic(n)), plane=myplane))

where myplane is the plane where I would like to draw the surface and n is its normal. This produces yet a different result, but still not the one I expect.


With Asymptore 3D surfaces are Bézier patches. A planar path3 does not define such a surface. The only solution I know is to use bezulate routines which transform a 2D path (cyclic or union of cyclic path) into a Bézier triangulation (2D). Then you can draw it with surface(bezulate(your 2D path))). It was developed to have 3D label.

I had some problems with your code (projections choice) and in particular for halfgminus and halfhminus the bezulate routine returns an error message. Here a part of your picture

path[]  aa=  project(side(identity4*halfgplus)--cycle ^^reverse(side(identity4*halfhplus*conj)--cycle),
path[] gg=bezulate(aa);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.