# How to attach a 2D rectangular image to a 3D curved surface with TikZ or PSTricks?

I want to do so called texture mapping which is to attach an image to a curved surface.

How to attach a 2D rectangular image to a 3D curved surface with TikZ or PSTricks?

For the sake of simplicity, let the curved surface be z=f(x,y)=asin(bx+cy)+d.

As Christian said, you can use an external program and include the image using pgfplots.

As a proof of concept, here's a command \drapeimageonsurface{<image>}{<equation>} that takes an image filename and an equation and uses the rgl library in R to drape the image over the surface. It removes the background from the image, and computes and sets the necessary mapping to include it in pgfplots.

The whole thing is very, very rough around the edges, there's basically no user interface, but it works.

This image (map taken from http://en.wikipedia.org/wiki/File:Equirectangular_projection_SW.jpg)

is generated by

    \begin{document}
\begin{tikzpicture}
\drapeimageonsurface{worldmap.png}{abs(x)+abs(y)}
\begin{axis}[3d box=complete, zmin=0, zmax=2]

\draw [fill=yellow] (axis cs:-0.67,0.41,1.08) circle [radius=3pt] node [font=\small, right=2pt, fill=white, fill opacity=0.5, text opacity=1] {San Francisco};
\draw [fill=yellow] (axis cs:0.78,0.39,1.17) circle [radius=3pt] node [font=\small, text depth=0pt,below left=2pt, fill=white, fill opacity=0.5, text opacity=1] {Tokyo};
\end{axis}
\end{tikzpicture}
\end{document}


And

is generated by

\begin{document}
\begin{tikzpicture}
\drapeimageonsurface{worldmap.png}{1+sin(2*x)*(-cos(y))}
\begin{axis}[3d box=complete, zmin=0, zmax=2]

\end{axis}
\end{tikzpicture}
\end{document}


Here's the complete code:

\documentclass{article}
\usepackage{pgfplots}
\makeatletter
\newcommand{\drapeimageonsurface}[2]{
% Create R script
\newwrite\tempfile
\immediate\openout\tempfile=rscript.r
\immediate\write\tempfile{library(rgl)^^J%
open3d()^^J%
par3d(windowRect=c(0,0,800,800))^^J%
x <- matrix(seq(-1,1,len=50),50,50,byrow=F)^^J%
y <- -matrix(seq(-1,1,len=50),50,50,byrow=T)^^J%
z<-#2^^J%
persp3d(x,y,z,col="white", texture="#1",specular="black",box=F,axes=F,xlab="",zlab="",ylab="")^^J%
view3d(userMatrix= rotate3d(rotationMatrix(-0.3*pi/2, 1,0,0),-pi/9,0,0,1),fov=0, scale=c(1,1,1))^^J%
rgl.snapshot('test.png')^^J%
M = par3d("modelMatrix")^^J%
viewport = par3d("viewport")^^J%
bbox=par3d("bbox")^^J%
v=rbind(matrix(bbox[c(1,3,5,2,3,5,1,4,5,2,4,6)],3),rep(1,4))^^J%
u = M \@percentchar*\@percentchar v^^J%
P = par3d("projMatrix")^^J%
A<-P \@percentchar*\@percentchar u^^J%
a=A / outer(rep(1,4),A[4,])^^J%
(a[1,]+1)/2*(viewport[3]-viewport[1])^^J%
(a[2,]+1)/2*(viewport[4]-viewport[2])^^J%
sink("3dmapping.txt")^^J%
cat("(")^^J%
cat(v[1:3,1],sep=",")^^J%
cat(") => (")^^J%
cat((a[1,1]+1)/2*(viewport[3]-viewport[1]),(a[2,1]+1)/2*(viewport[3]-viewport[1]),sep=",")^^J%
cat(")\@backslashchar n")^^J%
cat("(")^^J%
cat(v[1:3,2],sep=",")^^J%
cat(") => (")^^J%
cat((a[1,2]+1)/2*(viewport[3]-viewport[1]),(a[2,2]+1)/2*(viewport[3]-viewport[1]),sep=",")^^J%
cat(")\@backslashchar n")^^J%
cat("(")^^J%
cat(v[1:3,3],sep=",")^^J%
cat(") => (")^^J%
cat((a[1,3]+1)/2*(viewport[3]-viewport[1]),(a[2,3]+1)/2*(viewport[3]-viewport[1]),sep=",")^^J%
cat(")\@backslashchar n")^^J%
cat("(")^^J%
cat(v[1:3,4],sep=",")^^J%
cat(") => (")^^J%
cat((a[1,4]+1)/2*(viewport[3]-viewport[1]),(a[2,4]+1)/2*(viewport[3]-viewport[1]),sep=",")^^J%
cat(")")}
\immediate\write\tempfile{sink()}
\immediate\write\tempfile{viewport}
\immediate\write\tempfile{u}
\immediate\closeout\tempfile

% Run R script
\immediate\write18{R CMD BATCH rscript.r rscript.log}

% Remove image background
\immediate\write18{convert test.png -alpha set -channel alpha -fill none -floodfill +0+0 white draped.png}

% Set mapping
\everyeof{\relax}
\makeatletter
\def\auxmacro##1\relax{\pgfplotsset{plot graphics/points={##1}}}
\expandafter\auxmacro\@@input 3dmapping.txt
\makeatother
}

\begin{document}
\begin{tikzpicture}
\drapeimageonsurface{worldmap.png}{1+sin(2*x)*(-2*cos(y))}
\begin{axis}[3d box=complete, zmin=-1, zmax=4]