#rewritten Sept 18,2013 to allow much more controll over the points in a two dimensional biplot
#the three or more dimensional case remains the same as before
#The code for the two dimensional case is adapted (heavily) from the stats:::biplot function
#corrected April 9, 2015 to allow multiple biplots in the same window 
#Seriously modified June 25, 2016 to allow better control for labels, as well as just generally cleaner code
#Switched name to biPlot to be a cleaner call  2/10/26

"biplot.psych" <-
function(x, labels=NULL,cex=c(.75,1),main="Biplot from fa",hist.col="cyan",xlim.s=c(-3,3),ylim.s=c(-3,3),xlim.f=c(-1,1),ylim.f=c(-1,1),
   maxpoints=100,adjust=1.2,col,pos, arrow.len = 0.1,pch=16,choose=NULL,cuts=1,cutl=.0,group=NULL,smoother = FALSE,vars=TRUE,...) {
#biplot.psych has been deprecated as of 2/10/26
warning("biplot.psych has been deprecated,  please use biPlot.")
biPlot(x, labels=NULL,cex=c(.75,1),main=main,hist.col="cyan",xlim.s=c(-3,3),ylim.s=c(-3,3),xlim.f=c(-1,1),ylim.f=c(-1,1),maxpoints=100,adjust=1.2,col,pos, arrow.len = 0.1,pch=16,choose=NULL,cuts=1,cutl=.0,group=NULL,smoother = FALSE,vars=TRUE,...)}


biPlot <- function(x, labels=NULL,cex=c(.75,1),main="BiPlot from fa",
     hist.col="cyan",xlim.s=c(-3,3),ylim.s=c(-3,3),xlim.f=c(-1,1),ylim.f=c(-1,1),
    maxpoints=100,adjust=1.2,col,pos, arrow.len = 0.1,pch=16,choose=NULL,
   cuts=1,cutl=.0,group=NULL,smoother = FALSE,vars=TRUE,...) {

if(is.null(x$scores)) stop("I am sorry, but Biplot requires factor/component scores. \nYou need to  run fa/pca from the raw data")
op <- par()
old.par <- par(no.readonly = TRUE)
on.exit(par(old.par)) 
MAR <- par("mar")
MFROW <- par("mfrow")
title <- main
main <- NULL
 
#fa.poly nests the fa and scores within a list
if(is.list(x$scores)) x$scores <- x$scores$scores  #the case of fa.poly output
if(is.list(x$fa)) x$loadings <- x$fa$loadings  #once again, the case of fa.poly
if(!is.null(choose)) {  #plot just a pair 
     x$scores <- x$scores[,choose,drop=FALSE]
     x$loadings <- x$loadings[,choose,drop=FALSE]
     }
colnames(x$scores) <- colnames(x$loadings)
if((missing(group)) || (is.null(group))) group <- rep(1,nrow(x$scores))

#if(missing(pos)) pos <- NULL
#if(is.null(labels)) {if(nrow(x$scores) > maxpoints ) {labels = rep(".",dim(x$scores)[1] )} else {labels = rep("o",dim(x$scores)[1] )}}

n.dims <- dim(x$loadings)[2]
#this is taken directly from biplot
# if (missing(col)) {
#         col <- par("col")
#         if (!is.numeric(col)) 
#             col <- match(col, palette(), nomatch = 1L)
#         col <- c(col, col + 1L)
#     }
#     else if (length(col) == 1L) 
#         col <- c(col, col)
if(missing(col)) {col <-  c("black","red","blue","#FF0000FF", "#00FF00FF", "#00FFFFFF", "#0000FFFF" ,"#FF00FFFF")}  #rainbow + black, red
 ch.col <- col
 
#here is where we add some plotting controls that are missing from stats:::biplot  
     
if (n.dims == 2) { #just do a one panel graph

  op <- par(pty = "s")
  
  #we no longer resize the margins, but rather adjust where the title goes
  #if (!is.null(main)) op1 <- c(op, par("mar" = MAR + c(0, 0, 1, 0))) #give room for the title  -- 
 # if (!is.null(main)) par("mar" = MAR + c(0, 0, 1, 0))  #give room for the title  -
  #plotone does the work

plotone(x$scores,x$loading,labels=labels,main=main,xlim.s=xlim.s,ylim.s=ylim.s,xlim.f=xlim.f,ylim.f=ylim.f,maxpoints=maxpoints,adjust=adjust,col=col,pos=pos, arrow.len = arrow.len,pch=pch,choose=choose,cuts=cuts,cutl=cutl,group=group,ch.col=ch.col,smoother=smoother,vars=vars,... )
  par(new = TRUE)  #this sets it up so that we can plot on top of a plot 
    dev.hold()
   
    on.exit(dev.flush(), add = FALSE)
  } else {  #the case of 3 or more factors -- we do the equivalent of a pairs plot
	op1 <- par(mfrow=c(n.dims,n.dims), mar=c(2,3,3,2))
	if(nrow(x$scores) > maxpoints) {labels <- rep(".",nrow(x$scores))} else {labels <- rep("o",nrow(x$scores))}
	for (i in 1:n.dims) {
   		for (j in 1:n.dims){ 
  		   if(i==j) {h <- hist(x$scores[,i],freq=FALSE, main=colnames(x$loadings)[i],xlab="",ylab="",col=hist.col)
  		        breaks <- h$breaks; nB <- length(breaks)
   		 tryd <- try( d <- density(x$scores[,i],na.rm=TRUE,bw="nrd",adjust=adjust),silent=TRUE)
     	if(!inherits(tryd,"try-error")) {
    	 lines(d)}
  } else {
   
     #  biplot(x$scores[,c(j,i)],x$loadings[,c(j,i)],xlabs=labels,xlab="",ylab="",cex=cex,xlim=xlim.s,ylim=ylim.s,pch=pch,...)}

      plotone(x$scores[,c(j,i)],x$loadings[,c(j,i)],main=NULL,xlim.s=xlim.s,ylim.s=ylim.s,xlim.f=xlim.f,ylim.f=ylim.f,maxpoints=maxpoints,adjust=adjust,col=col,pos=pos, arrow.len = arrow.len,pch=pch,choose=choose,cuts=cuts,cutl=cutl,group=group,ch.col=ch.col,smoother=smoother,vars=vars,... )}  #work on this way of doing it
           }  }
   }
   #We do not want to reset the margins back to their prior values, because then we lose the ability to add lines to the figure
  # par(old.par)
  # par("mar"=MAR) #this puts the margins back to what they were when we started. Important for multiple biplots
 #  par("mfrow"=MFROW) 
title(title,line=2)
}   #End of biplot.psych

 plotone <- function( scores,loadings,labels=NULL,cex=c(.75,1),main=main,hist.col="cyan",xlim.s=c(-3,3),ylim.s=c(-3,3),xlim.f=c(-1,1),ylim.f=c(-1,1),maxpoints=100,adjust=1.2,col,pos, arrow.len = 0.1,pch=16,choose=NULL,cuts=1,cutl=.0,group=NULL,ch.col=c("black","blue"),smoother=FALSE, vars=TRUE,...)  { 
#There are three different conditions
#1 just show the (unlabeled) points
#2 don't show the points, but put in labels at their locations
#3 show the points with labels near them according to pos

 choice <- "one"
 if(!is.null(labels)) {
     if(missing(pos))  {choice <- "two" } else {choice <- "three"}
     }
if(smoother) choice="smoother"

switch(choice,   #we plot the scores here for scores > abs(cut) on x and y
 "one" = {plot(scores, xlim = xlim.s, ylim = ylim.s, cex=cex[1],main=main,pch=pch[group],bg=ch.col[group],col=col[group],...) } ,
 "two" = {plot(scores,typ='n',  xlim = xlim.s, ylim = ylim.s,cex=cex[1],main=main,pch=pch[group],bg=ch.col[group],col=col[group],...) 
       labels[sqrt((abs(scores[,1])^2  +   abs(scores[,2])^2 ) ) <  cuts] <-   NA
       text(scores,labels=labels,col=ch.col[group],pos=NULL,cex=cex[1])
       },
 "three" = {plot(scores,  xlim = xlim.s, ylim = ylim.s,cex=cex[1],main=main,pch=pch[group],bg=ch.col[group],col=col[group],...) 
        labels[sqrt((abs(scores[,1])^2 + abs(scores[,2])^2)) <  cuts] <-   NA  
       text(scores,labels=labels,pos=pos,cex=cex[1],col=ch.col[group])},
  "smoother" = {smoothScatter(scores, nrpoints=0)
    }
  )
       

     par(new = TRUE)  #this sets it up so that we can plot on top of a plot 
    dev.hold()
   
    on.exit(dev.flush(), add = FALSE)
    plot(loadings, axes = FALSE, type = "n", xlim = xlim.f, ylim = ylim.f,
      xlab = "", ylab = "", col = col[1L], ...)
    labels <- rownames(loadings)
    labels[sqrt(loadings[,1]^2  + loadings[,2]^2) <  cutl] <-   NA
    text(loadings, labels = labels, cex = cex[2L], col = col[2L], ...)
    if(vars) {arrows(0, 0, loadings[, 1L] * 0.8, loadings[, 2L] * 0.8, col = col[2L], 
            length = arrow.len)} else {
            arrows(0, 0, scores[, 1L] * xlim.f/xlim.s, scores[, 2L] * ylim.f/ylim.s, col = col[2L], 
            length = arrow.len)}
    axis(3, col = col[2L], ...)
    axis(4, col = col[2L], ...)
    box(col = col[1L])
  
}    
  

