5th Approach: Wrapping a world map sphere
with surfaces
A combination of the 2nd and 4th approaches.
Output:
Code:
\documentclass{standalone}
\usepackage[inline]{asymptote}
\begin{document}
\begin{asy}
import math;
import three;
import graph3;
size(500);
//file fin=input("world.dat"); //1317 limit for the for loop
file fin=input("world_110m.dat"); //5254 limit for the for loop
real zenith = pi/12.0;
real azimuth = pi/12.0;
currentprojection = perspective(cos(azimuth)*cos(zenith),
sin(azimuth)*cos(zenith),
sin(zenith));
//------------------------------------------------- TUBE
defaultpen(0.5mm);
pen darkgreen=rgb(0,138/255,122/255);
real R=3;
real a=0.75;
triple f(pair t) {
return ((R+a*cos(t.y))*cos(t.x),(R+a*cos(t.y))*sin(t.x),a*sin(t.y));
}
surface s=surface(f,(radians(90),0),(radians(345),2pi),8,8,Spline);
draw(s,surfacepen=material(gray+opacity(0.9), emissivepen=0.2*white),render(compression=Low,merge=true));
//-------------------------------------------------- RING
triple f(real t) {
return (3*cos(0.125*2pi*t)+0.08*cos(2pi*t), 3*sin(0.125*2pi*t),0+ 0.08*sin(2pi*t));
}
path3 helix = graph(f, 0, 8, n=500, operator..);
surface helixtube = tube(helix, width=0.5).s;
draw(helixtube, red);
//--------------------------------------------------- PLANET EARTH
real r = 1.25;
real ar = 1.2;
path3 myarc = Arc(c=O,normal=X, v1=-Z*r, v2=Z*r, n=24);
surface sphere = surface(myarc, angle1=0, angle2=360, c=O, axis=Z, n=24);
draw(sphere, surfacepen=material(royalblue+opacity(0.9),ambientpen=white));
real[][] a=fin.dimension(0,2);
a=transpose(a);
for(int i=0; i <=5254; ++i){
real u=a[0][i];
real v=a[1][i];
real U=a[0][i+1];
real V=a[1][i+1];
if (u==360 & v==360 | U==360 & V==360){}
else{
real x1=r*Cos(v)*Cos(u);
real y1=r*Cos(v)*Sin(u);
real z1=r*Sin(v);
real x2=r*Cos(V)*Cos(U);
real y2=r*Cos(V)*Sin(U);
real z2=r*Sin(V);
draw((x1,y1,z1)--(x2,y2,z2), darkolive+linewidth(0.2mm));
}
}
\end{asy}
\end{document}
4th Approach: A world map sphere
Answer to:
Of course, in the best of all worlds, somebody might figure out how to draw a 3D earth with e.g. Mathematicas
GeoData in asymptote
. Here I just want to put the 3D stuff in front of the background picture. It would be great if opacity
would also work.
The package pst-map3d
(pst-geo
) offers the possibility to plot a 3D world map. The data set is based on the CIA World DataBank II. Gnuplot
contains also a world.dat
file with a low resolution world map data. Natural Earth is a public domain map dataset available at 1:10m, 1:50m, and 1:110 million scales. Based on the approaches Plotting the world and Plotting the world revisited given by Hagen Wierstorf on his website Gnuplotting, its implementation in asymptote
or tikz
is possible.
Joining pair of points with draw
: draw((x1,y1,z1)--(x2,y2,z2), darkolive);
The coastlines are drawn joining a pair of points.
guide3
and path3
in combination with draw
and surface
is used to fill the surface with a color.
guide3 g;
g=g--(x1,y1,z1);
path3 s=g;
draw(surface(s--cycle),heavygreen);
Unfortunately, it does not work as expected. Any other ideas how to fill with a color the right surfaces?
Output for modified ´world_110m.dat´ with fill color (small scale data):
Output for modified ´world.dat´ with fill color (low resolution data):
Output for modified ´world_110m.dat´ without fill color (small scale data):
Output for modified ´world.dat´ without fill color (low resolution data):
Code:
\documentclass{standalone}
\usepackage[inline]{asymptote}
\begin{document}
\begin{asy}
import solids;
import three;
import graph3;
size(6cm);
//file fin=input("world.dat"); //1317 limit for the for loop
file fin=input("world_110m.dat"); //5254 limit for the for loop
real zenith = pi/12.0;
real azimuth = pi/12.0;
currentprojection = perspective(cos(azimuth)*cos(zenith),
sin(azimuth)*cos(zenith),
sin(zenith));
real r = 1;
real ar = 1.2;
path3 myarc = Arc(c=O,normal=X, v1=-Z*r, v2=Z*r, n=24);
surface sphere = surface(myarc, angle1=0, angle2=360, c=O, axis=Z, n=24);
draw(sphere, surfacepen=material(royalblue+opacity(0.7),ambientpen=white));
real[][] a=fin.dimension(0,2);
a=transpose(a);
guide3 g;
for(int i=0; i <=5254; ++i){
real u=a[0][i];
real v=a[1][i];
real U=a[0][i+1];
real V=a[1][i+1];
if (u==360 & v==360 | U==360 & V==360){}
else{
real x1=r*Cos(v)*Cos(u);
real y1=r*Cos(v)*Sin(u);
real z1=r*Sin(v);
real x2=r*Cos(V)*Cos(U);
real y2=r*Cos(V)*Sin(U);
real z2=r*Sin(V);
draw((x1,y1,z1)--(x2,y2,z2), darkolive);
g=g--(x1,y1,z1);
}
}
path3 s=g;
//draw(surface(s--cycle),heavygreen);
\end{asy}
\end{document}
Building a guide3
/ path3
with single points: draw(p3,black)
The coastlines as well as the colored filling are drawn with guide3
, paht3
an subpath
in combination with draw
and surface
.
Unfortunately, it compiles only for the first 202 pair of points (world_110m.dat
). For a higher number of points brings an error (the next pair of points (203) is a new segment (360 360
)). I have not found the error yet. Any ideas?
Output for modified world_110m.dat
with fill color (small scale data):
Code:
\documentclass{standalone}
\usepackage[inline]{asymptote}
\begin{document}
\begin{asy}
import solids;
import three;
import graph3;
size(6cm);
//file fin=input("world.dat"); //1317 limit for the for loop
file fin=input("world_110m.dat"); //5254 limit for the for loop
real zenith = pi/12.0;
real azimuth = pi/12.0;
currentprojection = perspective(cos(azimuth)*cos(zenith),
sin(azimuth)*cos(zenith),
sin(zenith));
real r = 1;
real ar = 1.2;
path3 myarc = Arc(c=O,normal=X, v1=-Z*r, v2=Z*r, n=24);
surface sphere = surface(myarc, angle1=0, angle2=360, c=O, axis=Z, n=24);
draw(sphere, surfacepen=material(royalblue+opacity(0.5),ambientpen=white));
real[][] a=fin.dimension(0,2);
a=transpose(a);
int j=0;
guide3 g;
for(int i=0; i<=202; ++i){
real u=a[0][i];
real v=a[1][i];
if (u!=360){
real x=r*Cos(v)*Cos(u);
real y=r*Cos(v)*Sin(u);
real z=r*Sin(v);
g=g--(x,y,z);
++j;
} else{
int n0=i-1-j;
int nn=i-1;
path3 s=g;
path3 sp3=subpath(s,n0,nn);
draw(sp3--cycle,darkolive);
draw(surface(sp3--cycle),heavygreen);
j=0;
}
}
\end{asy}
\end{document}
Joining pair of points with Arc
: Arc(O,r,one.y,one.x,two.y,two.x);
An other solution could be to implement the approach from Fill enclosed region on sphere by Frenzy Li.
Output for modified world_110m.dat
without fill color (small scale data):
(only for a few points (70) since Overleaf
(free version) returns the error: Timed out. Sorry, your compile took too long to run and timed out. This may be due to a large number of high-res images, or complicated diagrams).
Code:
\documentclass{standalone}
\usepackage[inline]{asymptote}
\begin{document}
\begin{asy}
import solids;
import three;
import graph3;
size(6cm);
//file fin=input("world.dat"); //1317 limit for the for loop
//file fin=input("world_110m.dat"); //5254 limit for the for loop
real zenith = pi/12.0;
real azimuth = pi/12.0;
currentprojection = perspective(cos(azimuth)*cos(zenith),
sin(azimuth)*cos(zenith),
sin(zenith));
real r = 1;
real ar = 1.2;
path3 myarc = Arc(c=O,normal=X, v1=-Z*r, v2=Z*r, n=24);
surface sphere = surface(myarc, angle1=0, angle2=360, c=O, axis=Z, n=24);
draw(sphere, surfacepen=material(royalblue+opacity(0.7),ambientpen=white));
real[][] a=fin.dimension(0,2);
a=transpose(a);
pair[] region = new pair[];
for(int i=0; i <=60; ++i){
region[i]=(a[0][i],a[1][i]);}
for(int i=1; i<region.length; ++i){
pair one, two;
if (region[i].x==360 & region[i].y==360 | region[i-1].x==360 & region[i-1].y==360){} else{
if(region[i-1].y < region[i].y){
one = (region[i] .x, 90 - region[i] .y);
two = (region[i-1].x, 90 - region[i-1].y);
}else if(region[i-1].y > region[i].y){
one = (region[i-1].x, 90 - region[i-1].y);
two = (region[i] .x, 90 - region[i] .y);
}else if(region[i-1].x > region[i].x){
one = (region[i] .x, 90 - region[i] .y);
two = (region[i-1].x, 90 - region[i-1].y);
}else{
one = (region[i-1].x, 90 - region[i-1].y);
two = (region[i] .x, 90 - region[i] .y);
}
path3 temp = Arc(O,r,one.y,one.x,two.y,two.x);
draw(temp,darkolive+linewidth(.3pt));}
}
\end{asy}
\end{document}
The original data contains blank lines to define the segment limits. These lines have been replaced with the pair 360 360
. For instance, the first lines of world_110m.dat
:
-163.71289567772871 -78.595667413241543
-163.105800951163786 -78.223338718578589
-161.245113491846439 -78.380176690584435
-160.24620805564453 -78.693645928866943
-159.482404548154477 -79.046337579258974
-159.208183560197654 -79.497007745276406
-161.127601284814716 -79.634208673011329
-162.439846768218416 -79.281465346186991
-163.027407803377002 -78.928773695794959
-163.06660437727038 -78.869965915846805
-163.71289567772871 -78.595667413241543
-6.197884894220991 53.867565009163364
-6.032985398777611 53.153190009160497
-6.788856573910849 52.260117906292336
Modified world_110m.dat
:
-163.71289567772871 -78.595667413241543
-163.105800951163786 -78.223338718578589
-161.245113491846439 -78.380176690584435
-160.24620805564453 -78.693645928866943
-159.482404548154477 -79.046337579258974
-159.208183560197654 -79.497007745276406
-161.127601284814716 -79.634208673011329
-162.439846768218416 -79.281465346186991
-163.027407803377002 -78.928773695794959
-163.06660437727038 -78.869965915846805
-163.71289567772871 -78.595667413241543
360 360
-6.197884894220991 53.867565009163364
-6.032985398777611 53.153190009160497
-6.788856573910849 52.260117906292336
In this way, with a conditional expression it is possible to know when a new segment starts.
3rd Attempt: Wrapping a label
with surfaces
I have tried a combination of the 1st and 2nd approaches. That is to say, trying to replace the planet by the imported picture.
There is a possibility to draw
a label
in 3D by using the base modules three
and labelpath3
. For example:
path3 g=(0,-5,0)..(0,5,0);
string world="earth";
draw(labelpath(world,subpath(g,0,reltime(g,0.95)),angle=-90),orange);
gives the following output:
Since (p. 9 of the documentation):
The function string graphic(string name, string options="")
returns a string that can be used to include an encapsulated PostScript (EPS) file.
I thought that defining
real sc=2;
unitsize(sc*1bp);
real wd=100*sc;
real ht=80*sc;
string world2=graphic("earth.pdf","width="+string(wd)+"bp"+",height="+string(ht)+"bp"+",scale="+string(sc));
draw(labelpath(world2,subpath(g,0,reltime(g,0.95)),angle=-90));
could work, but unfortunately it did not.
Code:
\documentclass{standalone}
\usepackage[inline]{asymptote}
\begin{document}
\begin{asy}
import graph3;
import math;
import three;
import labelpath3;
size(500);
currentprojection=perspective(
camera=(25,5,5),
up=Z,
target=(-0.6,0.7,-0.6),
zoom=1,
autoadjust=true);
//------------------------------------------------- TUBE
defaultpen(0.5mm);
pen darkgreen=rgb(0,138/255,122/255);
real R=3;
real a=0.75;
triple f(pair t) {
return ((R+a*cos(t.y))*cos(t.x),(R+a*cos(t.y))*sin(t.x),a*sin(t.y));
}
surface s=surface(f,(radians(90),0),(radians(345),2pi),8,8,Spline);
draw(s,surfacepen=material(gray+opacity(0.9), emissivepen=0.2*white),render(compression=Low,merge=true));
//-------------------------------------------------- RING
triple f(real t) {
return (3*cos(0.125*2pi*t)+0.08*cos(2pi*t), 3*sin(0.125*2pi*t),0+ 0.08*sin(2pi*t));
}
path3 helix = graph(f, 0, 8, n=500, operator..);
surface helixtube = tube(helix, width=0.5).s;
draw(helixtube, red);
//--------------------------------------------------- PLANET
material m=
// diffusepen, ambientpen, emissivepen, specularpen
material( green, yellow, blue, black);
//draw(surface(sphere(1.2)), m);
//--------------------------------------------------- LABEL TEXT
path3 g=(0,-5,0)..(0,5,0);
string world="earth";
draw(labelpath(world,subpath(g,0,reltime(g,0.95)),angle=-90),orange);
//--------------------------------------------------- LABEL PICTURE
real sc=2;
unitsize(sc*1bp);
real wd=100*sc;
real ht=80*sc;
string world2=graphic("earth.pdf","width="+string(wd)+"bp"+",height="+string(ht)+"bp"+",scale="+string(sc));
draw(labelpath(world2,subpath(g,0,reltime(g,0.95)),angle=-90));
\end{asy}
\end{document}
2nd Approach: Wrapping a sphere
with surfaces
Answer to:
However, I want to create an output i which the tube wraps around the planet.
Output:
Code:
\documentclass{standalone}
\usepackage[inline]{asymptote}
\begin{document}
\begin{asy}
import graph3;
import solids;
import interpolate;
import math;
import three;
import labelpath3;
size(500);
currentprojection=perspective(
camera=(25,5,5),
up=Z,
target=(-0.6,0.7,-0.6),
zoom=1,
autoadjust=true);
//------------------------------------------------- TUBE
defaultpen(0.5mm);
pen darkgreen=rgb(0,138/255,122/255);
real R=3;
real a=0.75;
triple f(pair t) {
return ((R+a*cos(t.y))*cos(t.x),(R+a*cos(t.y))*sin(t.x),a*sin(t.y));
}
surface s=surface(f,(radians(90),0),(radians(345),2pi),8,8,Spline);
draw(s,gray,render(compression=Low,merge=true));
//-------------------------------------------------- RING
triple f(real t) {
return (3*cos(0.125*2pi*t)+0.08*cos(2pi*t), 3*sin(0.125*2pi*t),0+ 0.08*sin(2pi*t));
}
path3 helix = graph(f, 0, 8, n=500, operator..);
surface helixtube = tube(helix, width=0.5).s;
draw(helixtube, surfacepen=material(red+opacity(0.9), emissivepen=0.2*white));
//--------------------------------------------------- PLANET
material m=
// diffusepen, ambientpen, emissivepen, specularpen
material( green, yellow, blue, black);
draw(surface(sphere(1.2)), m);
\end{asy}
\end{document}
However, the background is not transparent ...
1st Approach: Overlay of saved pictures in Asymptote
Answer to:
I'd like to see the 3D stuff also in front of the imported picture. Is that possible?
Output:
Firstly compile the code pdflatex asy pdflatex
to save the pictures and then compile it again by commenting the shipout("");
´s:
//shipout("D2FIG");
//shipout("D3FIG1");
//shipout("D3FIG2");
Code:
\documentclass[12pt]{article}
\usepackage[inline]{asymptote}
\begin{document}
\begin{asy}
size(10cm);
import graph3;
import three;
import labelpath3;
import graph;
import math;
// ----------------------------------------------------2D
// it is not important which precise picture gets imported
defaultpen(fontsize(10pt));
real sc=2;
unitsize(sc*1bp);
real wd=100*sc;
real ht=80*sc;
label(
shift(wd/2,ht/2)*
graphic("earth.pdf"
,"width="+string(wd)+"bp"
+",height="+string(ht)+"bp"
+",scale="+string(sc)
),(0,0)
);
layer();
int ngrid=10;
int n=(int)(wd/ngrid/sc);
int m=(int)(ht/ngrid/sc);
add(scale(ngrid)*grid(n,m,green));
xaxis(0,wd/sc,RightTicks(Step=ngrid));
yaxis(0,ht/sc,LeftTicks(Step=ngrid));
draw(((0,0)--(wd,ht)/sc),blue+2pt);
shipout("D2FIG");
picture D2FIG = currentpicture;
// ----------------------------------------------------3D1
currentpicture = new picture;
size(10cm);
import graph3;
import three;
import labelpath3;
import graph;
import math;
triple f(real t) {
return (3*cos(0.125*2pi*t)+0.08*cos(2pi*t), 3*sin(0.125*2pi*t),0+ 0.08*sin(2pi*t));
}
path3 helix = graph(f, 0, 8, n=500, operator..);
surface helixtube = tube(helix, width=0.4).s;
draw(helixtube, surfacepen=material(blue+opacity(0.3), emissivepen=0.2*white));
shipout("D3FIG1");
picture D3FIG1 = currentpicture;
// ----------------------------------------------------3D2
currentpicture = new picture;
size(10cm);
import graph3;
import three;
import labelpath3;
import graph;
import math;
real R=300;
real a=100;
triple f(pair t) {
return ((R+a*cos(t.y))*cos(t.x),(R+a*cos(t.y))*sin(t.x),a*sin(t.y));
}
surface s=surface(f,(radians(90),0),(radians(345),2pi),8,8,Spline);
draw(s,gray,render(compression=Low,merge=true));
shipout("D3FIG2");
picture D3FIG2 = currentpicture;
// ----------------------------------------------------Merge/Overlay
currentpicture = new picture;
size(10cm);
add(D2FIG);
label(graphic("D3FIG1+0_0.pdf"),(0,0));
label(graphic("D3FIG2+0_0.pdf"),(25,-25));
\end{asy}
\end{document}
I have taken the earth picture from MATLAB script for 3D visualizing geodata on a rotating globe
asy -k
(keep intermediate files) you can observe the construction of the picture. You can modify it so that the torus is "at the end". But the white color of the 3D is not transparent. You have to modify the "white" color of the 3D png output withconvert
=> "white" becomes transparent... In my opinion you have to process the final picture with two intermediate pictures (the 2D and the 3D)...asymptote
features such as opacity.