*! dashgph - program to add dashed lines to scatterplots
*! NJGW 1.0.1 12-2-2000
*
*   1.0.1 fixes casewise deletion
*

program define dashgph
	version 6.0

****************
* Alter not to drop casewise - this messes up multiple-line graphs!
****************


	qui syntax varlist(min=2 max=21) [if] [in] [aw iw fw], [ Dash(numlist) SPACe(numlist) SAving(string) /*
			*/ Connect(string) PEn(string) /*
			*/ HIstogram Matrix Oneway Box STar BAr Pie /*
			*/ RScale(string) Jitter(integer 0) Rescale XReverse * ]

	if "`histogr'"!="" | "`matrix'"!="" | "`oneway'"!="" | "`box'"!="" | "`star'"!="" /*
		*/ | "`bar'"!="" | "`pie'"!="" {
		di in red "dashgph only works for scatterplots"
		error 198
	}

	if "`jitter'"!="0" {
		di in red "jitter() option invalid with dashgph"
		error 198
	}
	if "`xreverse'"!="" {
		di in red "xreverse option invalid with dashgph"
		error 198
	}
	if "`rscale'"!="" {
		di in blue "Warning: dashed line(s) will only be correct if it applies to the"
		di in blue "         variable on the right-hand scale"
		local rscale "rscale(`rscale')"
	}
	if "`rescale'"!="" {
		di in blue "Warning: dashed line(s) will only be correct if it applies to the"
		di in blue "         variable on the right-hand scale"
	}

	if "`if'"!="" {
		preserve
		qui keep `if'
	}
	if "`in'"!="" {
		preserve
		qui keep `in'
	}

	if "`weight'"!="" {
		local weight "[`weight'`exp']"
	}

	local ndash : word count `dash'
	local nspace : word count `space'
	if `ndash'>1 | `nspace'>1 {
		local i 1
		local n 0
		while `i'<=length("`connect'") {
			local n=`n'+ (substr("`connect'",`i',1)=="D")
			local i=`i'+1
		}
		if `n'!=`ndash' & `ndash'!=0 & `ndash'!=1 {
			di in red "Must specify a single dash(), or one per variable"
			error 198
		}
		if `n'!=`nspace' & `nspace'!=0 & `nspace'!=1 {
			di in red "Must specify a single space(), or one per variable"
			error 198
		}
	}

	if "`dash'"!="" {
		local dlist "dlist(`dash')"
	}
	if "`space'"!="" {
		local slist "slist(`space')"
	}

	if `"`saving'"'!="" {
		if index(`"`saving'"',"replace")==0 {
			if index(`"`saving'"',".gph") {
				confirm new file `"`saving'"'
			}
			else {
				confirm new file `"`saving'.gph"'
			}
		}
		local saving `"saving(`saving')"'
	}

	local nvars : word count `varlist'
	local xvar : word `nvars' of `varlist'

	if length("`pen'")<`nvars'-1 {
		local pen="`pen'"+substr("2345678923456789234",length("`pen'")+1,`nvars'-1-length("`pen'"))
	}

	local Dpos=index("`connect'","D")
	while `Dpos'!=0  {
		if `Dpos'<`nvars' {								/* don't deal with extras */
			local Dv : word `Dpos' of `varlist'
			local Dvars "`Dvars' `Dv'"
			local Dpen="`Dpen'"+substr("`pen'",`Dpos',1)
		}
		local connect=substr("`connect'",1,`Dpos'-1)+"."+substr("`connect'",`Dpos'+1,.)
		local Dpos=index("`connect'","D")
	}
	if "`connect'"!="" {
		local connect "connect(`connect')"
	}

	graph `varlist' `weight', `connect' pen(`pen') `options' `rscale' `rescale'

	if "`Dvars'"!="" {
		DoDash `Dvars' `xvar' , `dlist' `slist' pen(`Dpen') `saving'
	}

end



program define DoDash
	version 6.0

	syntax varlist , pen(string) [ dlist(numlist) slist(numlist) SAving(string) ]

	if `"`saving'"'!="" {
		local saving `"saving(`saving')"'
	}

	local nvars : word count `varlist'
	local xvar : word `nvars' of `varlist'
	sort `xvar'

	local ndash : word count `dlist'
	if `ndash'==0 {
		local dsize 200
		local i 1
		while `i' < `nvars' {				/* skip xvar... */
			local dlist "`dlist' `dsize'"
			local dsize=`dsize'+200
			local i=`i'+1
		}
	}
	else if `ndash'==1 {
		local dsize "`dlist'"
		local dlist
		local i 1
		while `i' < `nvars' {
			local dlist "`dlist' `dsize'"
			local i=`i'+1
		}
	}

	if "`slist'"=="" {
		local slist "100"
	}
	local nspace : word count `slist'
	if `nspace'==1 {
		local ssize "`slist'"
		local slist
		local i 1
		while `i' < `nvars' {				/* skip xvar... */
			local slist "`slist' `ssize'"
			local i=`i'+1
		}
	}

	local ay=`r(ay)'
	local by=`r(by)'
	local ax=`r(ax)'
	local bx=`r(bx)'
	local obs=_N

	gph open, `saving'
	graph

	local j 1
	while `j' < `nvars' {
		local yvar : word `j' of `varlist'
		local curpen=substr("`pen'",`j',1)
		gph pen `curpen'
		local dash : word `j' of `dlist'
		local space : word `j' of `slist'

		local i 1
		while `i' < `obs' {

			local next 1
			while (`xvar'[`i'+`next']==. | `yvar'[`i'+`next']==.) & (`i'+`next'<=`obs') {
				local next=`next'+1
			}

			if `xvar'[`i'+`next']!=. {					/* deal with missing at end */

				local x0=`xvar'[`i']
				local x1=`xvar'[`i'+`next']
				local y0=`yvar'[`i']
				local y1=`yvar'[`i'+`next']
				local r0 = `ay'*`y0' + `by'
				local c0 = `ax'*`x0' + `bx'
				local r1 = `ay'*`y1' + `by'
				local c1 = `ax'*`x1' + `bx'

				local slope = (`r1'-`r0')/(`c1'-`c0')
				if `slope'<. {
					local xstep=sqrt(`dash'^2/((`slope')^2+1))
					local ystep=`xstep'*`slope'
				}
				else {
					local xstep  0
					if `r1'<`r0' {
						local ystep=(-1)*(`dash')
					}
					else {
						local ystep=`dash'
					}
				}

				local curr `r0'
				local curc `c0'

				local signr=sign(`r1'-`r0')
				local signc=sign(`c1'-`c0')

				while  (`curr'<=max(`r0',`r1')) & (`curr'>=min(`r0',`r1')) &     /*
					*/ (`curc'<=max(`c0',`c1')) & (`curc'>=min(`c0',`c1')) {

					local nextc = min(`curc' + `xstep',`c1')
					if `slope'< 0 | (`slope'==. & `r1'<`r0') {
						local nextr = max(`curr' + `ystep',`r1')
					}
					else {
						local nextr = min(`curr' + `ystep',`r1')
					}

					if min(`curr',`nextr',`curc',`nextc')>=0 & max(`curr',`nextr')<=23063 & /*
					*/ max(`curc',`nextc')<=32000 {
						gph line `curr' `curc' `nextr' `nextc'
					}

					local curr=`curr' + (1+(`space')/100)*(`ystep')
					local curc=`curc' + (1+(`space')/100)*(`xstep')

				}
			}
			local i=`i'+`next'
		}
		local j=`j'+1
	}

	gph close

end