Greetings, I would like to add 3D arrows (i.e. arrow-headed vectors linking X1Y1Z1 to X2,Y2,Z2) to a 3D plot; ideally the sort of plot that can be rotated interactively. Is this possible using plot3d, or another 3d plotter in R? While it is easy to draw segments in plot3d (e.g. below), I haven't figured out how to add arrow heads, or to create 3d arrows from scratch. ##two headless segments: library(rgl) matrix(1:2,2,3)->segment1 matrix(1:3,2,3)->segment2 plot3d(segment2,type="l",col="red",xlim=c(0,3),ylim=c(0,3),zlim=c(0,3)) plot3d(segment1,type="l",add=TRUE,col="blue") thanks for any assistance, -Eben J. Gering Graduate Student, Section of Integrative Biology The University of Texas at Austin
adding 3D arrows to 3D plots
4 messages · Eben J. Gering, Duncan Murdoch, Barry Rowlingson +1 more
Eben J. Gering wrote:
Greetings, I would like to add 3D arrows (i.e. arrow-headed vectors linking X1Y1Z1 to X2,Y2,Z2) to a 3D plot; ideally the sort of plot that can be rotated interactively. Is this possible using plot3d, or another 3d plotter in R? While it is easy to draw segments in plot3d (e.g. below), I haven't figured out how to add arrow heads, or to create 3d arrows from scratch. ##two headless segments: library(rgl) matrix(1:2,2,3)->segment1 matrix(1:3,2,3)->segment2 plot3d(segment2,type="l",col="red",xlim=c(0,3),ylim=c(0,3),zlim=c(0,3)) plot3d(segment1,type="l",add=TRUE,col="blue")
There isn't any current code to do that in rgl, but if you have a particular shape of arrow in mind, you could probably draw it with some combination of functions. The main problem is that I've never seen any very appealing rotatable 3d arrows. There's a Matlab function to draw some here: http://www.mathworks.com/matlabcentral/fileexchange/8396-draw-3d-arrows; it could probably be translated into rgl if you wanted that. Duncan Murdoch
3 days later
Have a go with this:
arrow3d <- function(p0=c(0,1,0),p1=c(1,1,1),s=0.1,theta=pi/4,n=3,...){
## p0: start point
## p1: end point
## s: length of barb as fraction of line length
## theta: opening angle of barbs
## n: number of barbs
## ...: args passed to lines3d for line styling
require(geometry)
require(rgl)
## rotational angles of barbs
phi=seq(0,2*pi,len=n+1)[-1]
## length of line
lp = sqrt(sum((p1-p0)^2))
## point down the line where the barb ends line up
cpt=(1-(s*lp*cos(theta)))*(p1-p0)
## draw the main line
line = lines3d(c(p0[1],p1[1]),c(p0[2],p1[2]),c(p0[3],p1[3]),...)
## need to find a right-angle to the line. So create a random point:
rpt = jitter(c(
runif(1,min(p0[1],p1[1]),max(p0[1],p1[1])),
runif(1,min(p0[2],p1[2]),max(p0[2],p1[2])),
runif(1,min(p0[3],p1[3]),max(p0[3],p1[3]))
))
## and if it's NOT on the line the cross-product gives us a vector
at right angles:
r = extprod3d(p1-p0,rpt)
## normalise it:
r = r / sqrt(sum(r^2))
## now compute the barb end points and draw:
pts = list()
for(i in 1:length(phi)){
ptb=rotate3d(r,phi[i],(p1-p0)[1],(p1-p0)[2],(p1-p0)[3])
lines3d(
c(p1[1],cpt[1]+p0[1]+lp*s*sin(theta)*ptb[1]),
c(p1[2],cpt[2]+p0[2]+lp*s*sin(theta)*ptb[2]),
c(p1[3],cpt[3]+p0[3]+lp*s*sin(theta)*ptb[3]),
...
)
}
return(line)
}
This creates a line with 'n' arrow barbs at one end, equally spaced
when you look at the line end-on. The barb length is 's' times the
length of the line, and the opening angle is theta. Just do arrow3d()
to get something.
Might be useful, plus I wanted to brush up on my vector geometry anyway...
Barry
Cool, Barry. I set n=30, col="red" and stuck it into my daiquiri! -Peter Ehlers
Barry Rowlingson wrote:
Have a go with this:
arrow3d <- function(p0=c(0,1,0),p1=c(1,1,1),s=0.1,theta=pi/4,n=3,...){
## p0: start point
## p1: end point
## s: length of barb as fraction of line length
## theta: opening angle of barbs
## n: number of barbs
## ...: args passed to lines3d for line styling
require(geometry)
require(rgl)
## rotational angles of barbs
phi=seq(0,2*pi,len=n+1)[-1]
## length of line
lp = sqrt(sum((p1-p0)^2))
## point down the line where the barb ends line up
cpt=(1-(s*lp*cos(theta)))*(p1-p0)
## draw the main line
line = lines3d(c(p0[1],p1[1]),c(p0[2],p1[2]),c(p0[3],p1[3]),...)
## need to find a right-angle to the line. So create a random point:
rpt = jitter(c(
runif(1,min(p0[1],p1[1]),max(p0[1],p1[1])),
runif(1,min(p0[2],p1[2]),max(p0[2],p1[2])),
runif(1,min(p0[3],p1[3]),max(p0[3],p1[3]))
))
## and if it's NOT on the line the cross-product gives us a vector
at right angles:
r = extprod3d(p1-p0,rpt)
## normalise it:
r = r / sqrt(sum(r^2))
## now compute the barb end points and draw:
pts = list()
for(i in 1:length(phi)){
ptb=rotate3d(r,phi[i],(p1-p0)[1],(p1-p0)[2],(p1-p0)[3])
lines3d(
c(p1[1],cpt[1]+p0[1]+lp*s*sin(theta)*ptb[1]),
c(p1[2],cpt[2]+p0[2]+lp*s*sin(theta)*ptb[2]),
c(p1[3],cpt[3]+p0[3]+lp*s*sin(theta)*ptb[3]),
...
)
}
return(line)
}
This creates a line with 'n' arrow barbs at one end, equally spaced
when you look at the line end-on. The barb length is 's' times the
length of the line, and the opening angle is theta. Just do arrow3d()
to get something.
Might be useful, plus I wanted to brush up on my vector geometry anyway...
Barry
______________________________________________ R-help at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-help PLEASE do read the posting guide http://www.R-project.org/posting-guide.html and provide commented, minimal, self-contained, reproducible code.
Peter Ehlers University of Calgary 403.202.3921