# 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]
\end{axis}
\end{tikzpicture}
\end{document}

-
Please wait for a couple of days or weeks or months or maybe years to accept this answer as I still let others think of other solutions (if any) gradually. – kiss my armpit Jun 8 '12 at 14:43
@Forgiver: Sure! I'd be interested to see other (maybe more native) solutions – Jake Jun 8 '12 at 14:49

To my knowledge, there are no texture mapping libraries for TikZ.

What you can do is: generate your texture mapping image using an external tool (perhaps matlab) and import the graphics (and only the graphics) into pgfplots. This would result in axis descriptions generated by TikZ. Pgfplots can overlay a correct 3d axis below/on top a 2d projection of some 3d scene (orthogonal projection).

See 3-dimensional histogram in pgfplots for a related example (i.e. something which has been produced by means of 3rd party tools and imported into pgfplots).

-