I was playing around with a Bifactor Model and found no elegant way to do it in 2D. So here is my attempt to do it in 3D:
My code in R:
library(rgl) library(heplots) vNorm<-function(x){sqrt(t(x)%*%x)} vUnit<-function(a,b){(b-a)/vNorm(b-a)} r3dDefaults$windowRect <- c(10, 40, 700, 700) open3d() nBarbs<-20 s1<-c(3,0,6) s2<-c(12,0,6) s3<-c(21,0,6) g<-c(12,0,-6) o<-c(12,-12, 6) iDist<- c(0,0,-6) iSpace<- c(3,0,0) i1<-cbind(s1-iSpace+iDist,s1+iDist,s1+iSpace+iDist) for (i in 1:3){shade3d( translate3d( cube3d(col="gray80"), i1[1,i],i1[2,i],i1[3,i]))} i2<-cbind(s2-iSpace+iDist,s2+iDist,s2+iSpace+iDist) for (i in 1:3){shade3d( translate3d( cube3d(col="gray60"), i2[1,i],i2[2,i],i2[3,i]))} i3<-cbind(s3-iSpace+iDist,s3+iDist,s3+iSpace+iDist) for (i in 1:3){shade3d( translate3d( cube3d(col="gray40"), i3[1,i],i3[2,i],i3[3,i]))} spheres3d(s1,col="gray80",point_antialias=TRUE,smooth=TRUE) spheres3d(s2,col="gray60",point_antialias=TRUE,smooth=TRUE) spheres3d(s3,col="gray40",point_antialias=TRUE,smooth=TRUE) for (i in 1:3){ arrow3d(s1,i1[,i]+c(0,0,1),color='gray80',n=nBarbs,barblen=0.2,lwd=2) arrow3d(s2,i2[,i]+c(0,0,1),color='gray60',n=nBarbs,barblen=0.2,lwd=2) arrow3d(s3,i3[,i]+c(0,0,1),color='gray40',n=nBarbs,barblen=0.2,lwd=2) arrow3d(o,io[,i]+c(0,0,1),color='gray90',n=nBarbs,barblen=0.2,lwd=2) } spheres3d(g,col="black",point_antialias=T,smooth=T) for (i in 0:8){ arrow3d(g,c(i*3,0,-1),color="black",barlen=0.05,n=nBarbs,barblen=0.15,lwd=2) # text3d(x=i*3,y=-1.3,0,paste0("T",i)) } spheres3d(o,col="gray90",point_antialias=TRUE,smooth=TRUE) io<-cbind(o-iSpace+iDist,o+iDist,o+iSpace+iDist) for (i in 1:3){shade3d( translate3d( cube3d(col="gray80"), io[1,i],io[2,i],io[3,i]))} arrow3d(s1,o-vUnit(s1,o),color='gray80',n=nBarbs,barblen=0.1,lwd=2) arrow3d(s2,o-vUnit(s2,o),color='gray60',n=nBarbs,barblen=0.1,lwd=2) arrow3d(s3,o-vUnit(s3,o),color='gray40',n=nBarbs,barblen=0.1,lwd=2) arrow3d(g,o-vUnit(g,o),color='black',n=nBarbs,barblen=0.1,lwd=2) text3d(s1+c(0,0,2),texts="S1",font=1,family="serif") text3d(s2+c(0,0,2),texts="S2",font=1,family="serif") text3d(s3+c(0,0,2),texts="S3",font=1,family="serif") text3d(o+c(0,0,2),texts="Outcome",font=1,family="serif") text3d(g+c(0,0,-2),texts="g",font=3,family="serif") if (!rgl.useNULL()) play3d(spin3d(axis=c(0,0,1), rpm=10), duration=6)