Try this:
\documentclass[margin=10pt]{standalone}
\usepackage{asymptote}
\begin{asydef}
struct planeTransformation {
int nInterpolate = 4;
pair apply(real, real);
pair apply(pair uv) { return apply(uv.x, uv.y); }
transform derivative(real, real);
transform derivative(pair uv) { return derivative(uv.x, uv.y); }
transform linearization(real u, real v) {
return shift(apply(u,v)) * derivative(u,v) * shift(-(u,v));
}
transform linearization(pair uv) {
return linearization(uv.x, uv.y);
}
/* Apply to a single Bezier spline. */
guide _apply(pair p1, pair c1, pair c2, pair p2) {
return apply(p1) .. controls linearization(p1)*c1 and linearization(p2)*c2 .. apply(p2);
}
guide _apply(path g) {
assert((length(g)) == 1);
return _apply(point(g,0), postcontrol(g,0), precontrol(g,1), point(g,1));
}
path apply(path g, int nInterpolate = nInterpolate) {
guide toreturn;
for (int i = 0; i < nInterpolate*length(g); ++i) {
real currentpos = i / nInterpolate;
real nextpos = (i+1) / nInterpolate;
toreturn = toreturn & _apply(subpath(g, currentpos, nextpos));
}
if (cyclic(g)) toreturn = toreturn & cycle;
return toreturn;
}
}
planeTransformation polar;
polar.apply = new pair(real r, real theta) {
return r * expi(theta);
};
polar.derivative = new transform(real r, real theta) {
transform t = (0, 0, cos(theta), -r*sin(theta), sin(theta), r*cos(theta));
return t;
};
\end{asydef}
\begin{document}
\begin{asy}
size(5cm);
path curvedbox = polar.apply(box((1, -pi), (2, pi)));
draw(curvedbox);
\end{asy}
\end{document}
The result:

Note that this is only an approximate transformation. To make it more precise, call
the function e.g. as polar.apply(g, nInterpolate=16);
where g
is the path to which the transformation should be applied. The default value is nInterpolate = 4
; different paths will require different levels of interpolation. The more intricate the path already is, the fewer interpolation points are likely to be required.
Here's the result with
path curvedbox = polar.apply(box((1, -pi), (2, pi)), nInterpolate=16);

Additional note: The code above maps the y
-value to the angle, whereas the OP originally requested mapping the y
-value to the radius. Here's a function that should switch to the order originally requested:
path switchedPolar(path g) {
return polar.apply(reflect((0,0),(1,1)) * g);
}
Alternatively, in the definitions of polar.apply
and polar.derivative
, the order of r
and theta
can be switched:
polar.apply = new pair(real theta, real r) {
etc.