Not a timely reply, but in case it's helpful to posterity, the code below uses fewer ePiX internals:
The strategy is to plot the surface first, then to overlay the vectors manually; vec_field()
draws individual arrows, draw_surface()
does the overlaying. The scene is manually chopped into three blocks to fine-tune the hidden object removal (see comments).
The vector field is specified by my_F()
, and may be changed as needed. The return value should be a periodic, pair-valued function of u and v.
Andy
/* -*-ePiX-*- */
#include "epix.h"
using namespace ePiX;
const double MAX(2); // outside radius of torus
const int N1(40); // longitudes
const int N2(20); // latitudes
const double r0(0.95); // minor radius
const double R0(MAX - r0); // major radius
domain dom(P(0, 0), P(360, 360), mesh(N1, N2), mesh(3*N1, 3*N2));
// torus parametrization
P Phi(double u, double v)
{
double rad(R0 + r0*Cos(v));
return P(rad*Cos(u), rad*Sin(u), r0*Sin(v));
}
// vector field to plot (in plane coordinates)
P my_F(double u, double v)
{
P loc(Phi(u, v));
double x(loc.x1()), y(loc.x2()), z(loc.x3());
return 0.05*P((x - y)*(x + y), 2*x*y); // adjust as desired
}
// auxiliary mappings
// partial derivatives (manually computed)
P Phi_u(double u, double v)
{
double rad(R0 + r0*Cos(v));
return P(-rad*Sin(u), rad*Cos(u), 0);
}
P Phi_v(double u, double v)
{
return P(-r0*Cos(u)*Sin(v), -r0*Sin(u)*Sin(v), r0*Cos(v));
}
// draw vector if surface element faces camera
const double du(360/N1);
const double dv(360/N2);
void vec_field(double u, double v)
{
double u1(u + 0.5*du), v1(v + 0.5*dv); // middle of mesh element
P loc(Phi(u1, v1)),
Xu(Phi_u(u1, v1)), Xv(Phi_v(u1, v1)), dir(Xu*Xv); // cross product
if (0 < ((camera.viewpt() - loc)|dir))
{
P tmp(my_F(u1, v1)), // vector field in coordinates
vec(tmp.x1()*Xu + tmp.x2()*Xv); // field in R^3
dart(loc, loc + vec);
}
}
// will chop scene manually; group repeated drawing commands
void draw_surface()
{
pen(White(), 0);
surface(Phi, dom, -1); // cull back-facing elements
plain(Black());
for (int i = 0; i < 2*N1; ++i)
for (int j = 0; j < 2*N2; ++j)
vec_field(0.5*i*du, 0.5*j*dv);
}
int main()
{
picture(P(-3, -2.5),P(3, 2.5), "12x10cm");
begin();
degrees();
camera.at(sph(10, 80, 30));
P O(0, 0, 0), // origin
back(-MAX, -MAX, -MAX); // back corner
// axes and labels
arrow(back, back + (2*MAX + 1)*E_1);
label(back + (2*MAX + 1)*E_1, P(-4, 0), "$x$", l);
arrow(back, back + (2*MAX + 1)*E_2);
label(back + (2*MAX + 1)*E_2, P(0, -4), "$y$", b);
arrow(back, back + (2*MAX + 1)*E_3);
label(back + (2*MAX + 1)*E_3, P(0, 4), "$z$", t);
label_mask(White());
axis aX(P(-MAX, MAX, -MAX), P( MAX, MAX, -MAX), 2*MAX, P(0, -4), b);
axis aY(P( MAX, -MAX, -MAX), P( MAX, MAX - 1, -MAX), 2*MAX - 1, P(-2, 0), l);
axis aZ(P(-MAX, MAX, -MAX + 1), P(-MAX, MAX, MAX), 2*MAX - 1, P(0, -2), br);
aX.draw_labels();
aY.draw_labels();
aZ.draw_labels();
// background grids
black(0.5);
grid(P(-MAX, -MAX, -MAX), P(MAX, MAX, -MAX), 2*MAX, 2*MAX);
grid(P(-MAX, -MAX, -MAX), P(MAX, -MAX, MAX), 2*MAX, 2*MAX);
grid(P(-MAX, -MAX, -MAX), P(-MAX, MAX, MAX), 2*MAX, 2*MAX);
black();
fill(White());
// manually chop the scene
clip_face(O, -E_3); // bottom half
draw_surface();
clip_restore();
clip_face(O, E_3); // top back quarter
clip_face(O, -E_2);
draw_surface();
clip_restore();
clip_face(O, E_3); // top front quarter
clip_face(O, E_2);
draw_surface();
clip_restore();
tikz_format();
end();
}
Best Answer
This is too complicated for a comment. It is based on Gregor Perčič's solution.
First, you can simplify drawing rectangles. Second, using a scope you can move a group of objects around using the shift key, and create a node (more or less) using the local bounding box key.