*! program to draw chernoff faces *! version 1.0.0 20jul2009 *author: Rafal Raciborski *contact: rraciborski@gmail.com /* * Credits * * Translated from the program FACES.SAS written by Michael Friendly * http://www.math.yorku.ca/SCS/sasmac/faces.html * Original source: M. Schupbach (1987) ASYMFACE Asymmetrical Faces in TURBO PASCAL, Bern University * */ *! sortpreserve does not work when nocombine is specified, why? program define chernoff, sortpreserve version 9.0 syntax [ , /* */ ISize(passthru) /* 1. eye size */ PSize(passthru) /* 2. pupil size */ PPos(passthru) /* 3. pupil position */ IAngle(passthru) /* 4. eye slant */ IHor(passthru) /* 5. eye horizontal position */ IVert(passthru) /* 6. eye vertical position */ BCurv(passthru) /* 7. brow curvature */ BDens(passthru) /* 8. brow density */ BHor(passthru) /* 9. brow horizontal position */ BVert(passthru) /* 10. brow vertical position */ HUpper(passthru) /* 11. upper hair line */ HLower(passthru) /* 12. lower hair line */ FLine(passthru) /* 13. face line */ HDark(passthru) /* 14. hair darkness */ HSlant(passthru) /* 15. hair shading slant */ NOse(passthru) /* 16. nose */ MSize(passthru) /* 17. mouth size */ MCurv(passthru) /* 18. mouth curvature ********** INDIVIDUAL FACE GRAPH OPTIONS ********** */ ORder(varlist) /* order faces by varlist */ SHow /* draw individual face graphs */ LHalf /* draw only left half of face ++++++++++++++++++++++++++++++++++++ NEW */ RHalf /* draw only right half of face ++++++++++++++++++++++++++++++++++++ NEW ASpectratio(passthru) aspect ratio ++++++++++++++++++++++++++++++++++++++++++++++++++++ NEW */ HSPace(real .75) /* space between half face graphs, .5 will make them really close ++ NEW ********** FACE TITLES, LABELS, ETC ********** */ ITitle(varname) /* title for individual face graph +++++++++++++++++++++++++++++++++ NEW */ INote(varname) /* note for individual face graph ++++++++++++++++++++++++++++++++++ NEW */ ILabel(varname) /* label faces with varname */ XLabel(real 64) /* position of face label x */ YLabel(real -10) /* position of face label y */ LSize(passthru) /* size of label text - default is large */ PLacement(integer 64) /* where to place the label relative to (y, x) position */ JUstification(string) /* justification of face label (left, center, right) */ ISCale(varname) /* scale for individual graphs +++++++++++++++++++++++++++++++++++++ NEW */ IMargin(passthru) /* same as graphregion(margin()) in twoway graph */ IRegion(passthru) /* same as plotregion(margin()) in twoway graph ++++++++++++++++++++ NEW */ XFace(real 5.00) /* X size of FACE graph ---------------------------------------- DELETE? */ YFace(real 6.00) /* Y size of FACE graph ---------------------------------------- DELETE? */ saveall /* save individual face graphs */ REscale(real 1) /* restrict the range of transformed variables to less than [0,1] */ gmin(numlist max=1) /* global theoretical minimum */ gmax(numlist max=1) /* global theoretical maximum ********* COMBINED GRAPH OPTIONS ********* */ ROws(passthru) /* number of rows in combined graph */ COls(passthru) /* number of columns in combined graph */ XCombined(passthru) /* X size of COMBINED graph */ YCombined(passthru) /* Y size of COMBINED graph FYsize(real 100) forced size option ++++++++++++++++++++++++++++++++++++++++++++++ NEW FXSIZE(real 100) forced size option ++++++++++++++++++++++++++++++++++++++++++++++ NEW */ LEgend (string) /* adds legend - can be legend(2|3 [nolabel]) ++++++++++++++++++++++ NEW */ NOCombine /* do not draw a combined graph */ COLFirst /* display down columns */ TItle(passthru) /* title of combined graph */ SUbtitle(passthru) /* subtitile of combined graph */ note(passthru) /* note for combined graph */ nodraw /* do not draw final combined graph */ SAving(string) /* save the combined graph */ timer ] /* shows time to completion */ //isize psize ppos iangle ihor ivert bcurv bdens bhor bvert hupper hlower fline hdark hslant nose msize mcurv tempvar z1 z2 z3 z4 z5 z6 z7 z8 z9 z10 z11 z12 z13 z14 z15 z16 z17 z18 nfaces tempvar X1 Y1 X1L X2 Y2 X2L X3 Y3 X3L X4 Y4 X4L X7 Y7 X7L X16 Y16 X16L X17 Y17 X17L X18 Y18 X18L tempvar Xface Yface XfaceL Xlow Ylow XlowL Xup Yup XupL XXU YYU XXL YYL XT1 YT1 XT2 YT2 capture sort `order' qui gen `nfaces' = _n qui summ `nfaces' local faces = r(max) // use it later as a counter in loops if ("`lhalf'" != "" & "`rhalf'" != "") { di in yellow "lhalf " as err "and " in yellow "rhalf " as err "cannot be specified together." exit } if (`hspace' < .50 | `hspace' > 1) { di in yellow "hspace " as error "must be within [.50, 1.00]." exit } if (`rescale' <= .50 | `rescale' > 1) { di as error "The value of " in yellow "rescale " as error "must be within (.50, 1.00]." exit } // do everything in terms of a single face, even if it involves recalculating some statistics, // as processing all faces at once generates too many vars and you run into memory issues di in yellow _n "Processing " in green `faces' in yellow " observations...." _n tempvar one gen byte `one' = 1 // to mark original observations capture set obs 121 // need that many obs for hair coordinates qui replace `one' = 0 if `one' == . local shift = 1 - `rescale' // rescale all variables to [0,1] range, the default being .50 local facef "isize psize ppos iangle ihor ivert bcurv bdens bhor bvert hupper hlower fline hdark hslant nose msize mcurv" local c = 1 foreach f of local facef { // each face feature is specified as f(varname|.|_null_ [, #|. #|.]) tokenize `"``f''"', parse("(), ") macro shift macro shift local v1 `1' macro shift macro shift local lmin `1' local lmax `2' /* di as err "v1 is `v1'" di as err "lmin is `lmin'" di as err "lmax is `lmax'" di as err "gmin is `gmin'" di as err "gmax is `gmax'" */ if ("`v1'" != "" & "`v1'" != "." & "`v1'" != "_null_") { qui count if `v1' == . & `one' == 1 if `r(N)' > 0 { di as err "Warning: variable " in green "`v1' " as err "contains missing values." } } if ("`v1'" == "" | "`v1'" == "." | "`v1'" == "_null_") { qui gen double `z`c'' = .5 } else { qui su `v1' local varmin = `r(min)' local varmax = `r(max)' if ("`lmin'"==")") local lmin = `varmin' if ("`lmax'"==")") local lmax = `varmax' // check theoretical global and local min and max if ("`gmin'" != "" ) { if (`gmin' > `varmin') { di as err "Warning `f': gmin(" in green "`gmin'" as err ") > min_`v1'(" in green "`varmin'" _c di as err ") - min_`v1' will be used." } else { local varmin = `gmin' } } else { if ("`lmin'" != "" & "`lmin'" != ".") { if (`lmin' > `varmin') { di as err "Warning `f': " in green "`lmin'" as err " > min_`v1'(" in green "`varmin'" _c di as err ") -- min_`v1' will be used." } else { local varmin = `lmin' } } } if ("`gmax'" != "" ) { if (`gmax' < `varmax') { di as err "Warning `f': gmax(" in green "`gmax'" as err ") < max_`v1'(" in green "`varmax'" _c di as err ") - max_`v1' will be used." } else { local varmax = `gmax' } } else { if ("`lmax'" != "" & "`lmax'" != ".") { if (`lmax' < `varmax') { di as err "Warning `f': " in green "`lmax'" as err " < max_`v1'(" in green "`varmax'" _c di as err ") -- max_`v1' will be used." } else { local varmax = `lmax' } } } chscale `v1' `z`c'' `varmin' `varmax' // reverse the values of hslant to match Flury & Riedwyl (1981) // if hslant, hdark, and hlower are all 1, a hair is drawn over the face - force all three down to .99 if ("`f'"=="hslant") qui replace `z`c'' = abs(`z`c''-1)*.99 if ("`f'"=="hdark" | "`f'"=="hlower") qui replace `z`c'' = `z`c''*.99 qui replace `z`c'' = (`rescale' - `shift')*`z`c'' + `shift' qui replace `z`c'' = .5 if `z`c'' == . } local c = `c'+1 } if "`timer'" == "timer" { capture timer clear 1 timer on 1 } serset clear local nsersets = 0 forval f = 1/`faces' { //di as err "processing face `f'" // begin construction of face +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //list in 1 local z1F = `z1'[`f'] local z2F = `z2'[`f'] local z3F = `z3'[`f'] local z4F = `z4'[`f'] local z5F = `z5'[`f'] local z6F = `z6'[`f'] local z7F = `z7'[`f'] local z8F = `z8'[`f'] local z9F = `z9'[`f'] local z10F = `z10'[`f'] local z11F = `z11'[`f'] local z12F = `z12'[`f'] local z13F = `z13'[`f'] local z14F = `z14'[`f'] local z15F = `z15'[`f'] local z16F = `z16'[`f'] local z17F = `z17'[`f'] local z18F = `z18'[`f'] ********************************************* EYES Z1-Z6 ++++++++++++++++++++++++++++++++++ local h = 1.5 local dmin = .2 local dmax = 1.2 local rpmin = .2 local rpmax = 1.2 local pshift = 1 local xm = 2 + 2*`z5F' local ym = 2*`z6F' - 1 local xl = `xm' - `h' local xr = `xm' + `h' local psi = (`z4F' - .5)*(_pi/2) local cospsi = cos(`psi') local sinpsi = sin(`psi') local d = `dmin' + `z1F' if `d' <= .05 { rotate `xl' `xm' `cospsi' `sinpsi' `xm' `ym' local xl = `r(outx)' local ym = `r(outy)' rotate `xr' `xm' `cospsi' `sinpsi' `xm' `ym' local xr = `r(outx)' local ym = `r(outy)' // same as ym above } else { if `d' > `h' local d = `h' qui gen `X1' = . // upper eye line xxx qui gen `Y1' = . qui gen `X2' = . // lower eye line qui gen `Y2' = . local y0 = `ym' - (`h'+`d')*(`h'-`d') / (2*`d') local rad2 = (`ym' - `y0' + `d')^2 // upper eye line local xx = `xl' local yy = `y0' + sqrt(max(0, `rad2' - (`xx'-`xm')^2)) rotate `xx' `yy' `cospsi' `sinpsi' `xm' `ym' local xx = `r(outx)' local yy = `r(outy)' qui replace `X1' = `xx' in 1 qui replace `Y1' = `yy' in 1 forval i = 2/26 { local xx = `xl' + (`i'-1)*(`h'/25) local yy = `y0' + sqrt(max(0, `rad2' - (`xx'-`xm')^2)) rotate `xx' `yy' `cospsi' `sinpsi' `xm' `ym' local xx = `r(outx)' local yy = `r(outy)' qui replace `X1' = `xx' in `i' qui replace `Y1' = `yy' in `i' } forval i = 27/51 { local ii = 52 - `i' local x1 = `xl' + (`ii'-1)*(`h'/25) local xx = 2*`xm' - `x1' local yy = `y0' + sqrt(max(0, `rad2' - (`x1'-`xm')^2)) rotate `xx' `yy' `cospsi' `sinpsi' `xm' `ym' local xx = `r(outx)' local yy = `r(outy)' qui replace `X1' = `xx' in `i' qui replace `Y1' = `yy' in `i' } qui gen `X1L' = -`X1' // lower eye line local xx = 2*`xm' - `xl' local yy = 2*`ym' - `y0' - sqrt(max(0, `rad2' - (`xl'-`xm')^2)) rotate `xx' `yy' `cospsi' `sinpsi' `xm' `ym' local xx = `r(outx)' local yy = `r(outy)' qui replace `X2' = `xx' in 1 qui replace `Y2' = `yy' in 1 forval i = 2/26 { local x1 = `xl' + (`i'-1)*(`h'/25) local xx = 2*`xm' - `x1' local y1 = `y0' + sqrt(max(0, `rad2' - (`x1'-`xm')^2)) local yy = 2*`ym' - `y1' rotate `xx' `yy' `cospsi' `sinpsi' `xm' `ym' local xx = `r(outx)' local yy = `r(outy)' qui replace `X2' = `xx' in `i' qui replace `Y2' = `yy' in `i' } forval i = 27/51 { local ii = 52 - `i' local xx = `xl' + (`ii'-1)*(`h'/25) local y1 = `y0' + sqrt(max(0, `rad2' - (`xx'-`xm')^2)) local yy = 2*`ym' - `y1' rotate `xx' `yy' `cospsi' `sinpsi' `xm' `ym' local xx = `r(outx)' local yy = `r(outy)' qui replace `X2' = `xx' in `i' qui replace `Y2' = `yy' in `i' } qui gen `X2L' = -`X2' qui gen `X3' = . // upper pupil line qui gen `Y3' = . qui gen `X4' = . // lower pupil line qui gen `Y4' = . local rpup = `rpmin' + `z2F' // pupil size if `rpup' < 0 local rpup = 0 local xpup = `xm' + (`z3F' - .5)*`pshift' // pupil position // upper pupil line local xx = `xpup' - `rpup' if `xx' < `xl' local xx = `xl' if `xx' > `xr' local xx = `xr' local yy = `ym' local hp = `y0' + sqrt(max(0, `rad2' - (`xx'-`xm')^2)) local yy = min(`yy',`hp') rotate `xx' `yy' `cospsi' `sinpsi' `xm' `ym' local xx = `r(outx)' local yy = `r(outy)' forval i = 2/51 { local fi = (_pi/2)*(`i'-1)/25 local xx = `xpup' - `rpup'*cos(`fi') if `xx' < `xl' local xx = `xl' if `xx' > `xr' local xx = `xr' local yy = `ym' + `rpup'*sin(`fi') local hp = `y0' + sqrt(max(0, `rad2' - (`xx'-`xm')^2)) local yy = min(`yy',`hp') rotate `xx' `yy' `cospsi' `sinpsi' `xm' `ym' local xx = `r(outx)' local yy = `r(outy)' qui replace `X3' = `xx' in `i' qui replace `Y3' = `yy' in `i' } qui gen `X3L' = -`X3' // lower pupil line local fi = (_pi/2) local xx = `xpup' - `rpup'*cos(`fi') if `xx' < `xl' local xx = `xl' if `xx' > `xr' local xx = `xr' local yy = `ym' + `rpup'*sin(`fi') local hp = `y0' + sqrt(max(0, `rad2' - (`xx'-`xm')^2)) local yy = min(`yy',`hp') local yy = 2*`ym' - `yy' rotate `xx' `yy' `cospsi' `sinpsi' `xm' `ym' local xx = `r(outx)' local yy = `r(outy)' forval i = 2/51 { local ii = 52-`i' local fi = (_pi/2)*(`ii'-1)/25 local xx = `xpup' - `rpup'*cos(`fi') if `xx' < `xl' local xx = `xl' if `xx' > `xr' local xx = `xr' local yy = `ym' + `rpup'*sin(`fi') local hp = `y0' + sqrt(max(0, `rad2' - (`xx'-`xm')^2)) local yy = min(`yy',`hp') local yy = 2*`ym' - `yy' rotate `xx' `yy' `cospsi' `sinpsi' `xm' `ym' local xx = `r(outx)' local yy = `r(outy)' qui replace `X4' = `xx' in `i' qui replace `Y4' = `yy' in `i' } qui gen `X4L' = -`X4' } ***************************************** EYEBROWS Z7-Z10 ++++++++++++++++++++++++++++++++ qui gen `X7' = . qui gen `Y7' = . local xb = 2 + 2*`z9F' local yb = 1 + 2*`z10F' local dens = `z8F'/2 local xl = `xb'-2 local dmin = -1 local dmax = .5 local d = `dmin' + 1.5*`z7F' local c = `d' / (`h')^2 qui replace `X7' = `xl' in 1 qui replace `Y7' = `yb' + `c'*(`xl' - `xb')^2 - `dens' in 1 forval i = 2/25 { local xx = `xl' + (`i'-1)*(2/12) qui replace `X7' = `xx' in `i' local yy = `yb' + `c'*(`xx' - `xb')^2 + ((-1)^`i')*`dens' qui replace `Y7' = `yy' in `i' } qui gen `X7L' = -`X7' ********************************************* FACE & HAIRLINE ++++++++++++++++++++++++++++ ************************************************ FACE LINE Z13 qui gen `Xface' = . qui gen `Yface' = . qui replace `Xface' = 7 in 1 qui replace `Yface' = 0 in 1 forval i = 2/96 { local r = -1 + (`i'-1)/48 poly5 `r' 4.6951 -2.6606 .1939 -1.3368 -1.4519 .5025 local min = `r(out)' poly5 `r' 6.3767 -2.1462 -4.1037 -2.9179 1.2404 1.5972 local max = `r(out)' qui replace `Xface' = `min' + `z13F'*(`max' - `min') in `i' poly5 `r' -4.7097 -5.4093 -2.2439 .2125 1.9345 .2350 local min = `r(out)' poly5 `r' -6.5371 -8.7286 1.2045 7.5676 .3321 -3.8549 local max = `r(out)' qui replace `Yface' = `min' + `z13F'*(`max' - `min') in `i' } qui replace `Xface' = 0 in 97 qui replace `Yface' = -10 in 97 qui gen `XfaceL' = -`Xface' ********************************************** LOWER HAIR LINE Z12 qui gen `Xlow' = . qui gen `Ylow' = . qui replace `Xlow' = 0 in 1 qui replace `Ylow' = 6.5 in 1 forval i = 2/120 { local k = -1 + (`i'-1)/22 local h = -1 + (`i'-1)/60 if `i' < 45 { poly5 `k' 2.3096 2.7696 -.2053 -.2040 .3026 -.1693 local min = `r(out)' poly5 `h' 3.5608 4.0885 .2812 -.5919 -.3595 .0412 local max = `r(out)' qui replace `Xlow' = `min' + `z12F'*(`max' - `min') in `i' poly5 `k' 8.1185 .3246 -1.5201 .3933 .1948 -.4255 local min = `r(out)' poly5 `h' 3.9792 -1.9186 -.8270 -.9849 .1044 -.3504 local max = `r(out)' qui replace `Ylow' = `min' + `z12F'*(`max' - `min') in `i' } else if `i' > 44 & `i' < 102 { poly5 `h' 5.5221 3.7880 -3.0211 -.2974 .9965 .0660 local min = `r(out)' poly5 `h' 3.5608 4.0885 .2812 -.5919 -.3595 .0412 local max = `r(out)' qui replace `Xlow' = `min' + `z12F'*(`max' - `min') in `i' poly5 `h' 6.1704 -5.6920 -.5460 .9206 -.6389 -.2504 local min = `r(out)' poly5 `h' 3.9792 -1.9186 -.8270 -.9849 .1044 -.3504 local max = `r(out)' qui replace `Ylow' = `min' + `z12F'*(`max' - `min') in `i' } else { local obs = `i'-101 local sub = `Xface'[`obs'] qui replace `Xlow' = `sub' in `i' local sub = `Yface'[`obs'] qui replace `Ylow' = `sub' in `i' } } local sub = `Xface' in 20 qui replace `Xlow' = `sub' in 121 local sub = `Yface' in 20 qui replace `Ylow' = `sub' in 121 qui gen `XlowL' = -`Xlow' ************************************************ UPPER HAIR LINE Z11 qui gen `Xup' = . qui gen `Yup' = . qui replace `Xup' = 0 in 1 qui replace `Yup' = 9.9 in 1 forval i = 2/120 { local h = -1 + (`i'-1)/51 local t = -1 + (`i'-1)/60 if `i' < 102 { poly5 `h' 5.5221 3.7880 -3.0211 -.2974 .9965 .0660 local min = `r(out)' poly5 `t' 8.1147 2.7487 -7.3495 4.2360 2.8299 -3.5240 local max = `r(out)' qui replace `Xup' = `min' + `z11F'*(`max' - `min') in `i' poly5 `h' 6.1704 -5.6920 -.5460 .9206 -.6389 -.2504 local min = `r(out)' poly5 `t' 6.7029 -10.3740 -3.6243 5.8058 .5964 -1.5585 local max = `r(out)' qui replace `Yup' = `min' + `z11F'*(`max' - `min') in `i' } else { local obs = `i'-101 local sub = `Xface'[`obs'] poly5 `t' 8.1147 2.7487 -7.3495 4.2360 2.8299 -3.5240 local max = `r(out)' qui replace `Xup' = `sub' + `z11F'*(`max' - `sub') in `i' local sub = `Yface'[`obs'] poly5 `t' 6.7029 -10.3740 -3.6243 5.8058 .5964 -1.5585 local max = `r(out)' qui replace `Yup' = `sub' + `z11F'*(`max' - `sub') in `i' } } local sub = `Xface' in 20 qui replace `Xup' = `sub' in 121 local sub = `Yface' in 20 qui replace `Yup' = `sub' in 121 qui gen `XupL' = -`Xup' ****************************************** HAIR DENSITY Z14 & SHADING SLANT Z15 local dd = 3*(1 - `z14F'*.9) if `dd' < .1 local dd = .1 local angle = 45 - `z15F'*90 local t1 = `angle' * (_pi/180) local co = cos(`t1') local si = sin(`t1') local xmin = 10000 local xmax = -`xmin' qui gen `XXU' = . qui gen `YYU' = . qui gen `XXL' = . qui gen `YYL' = . qui gen `XT1' = . qui gen `YT1' = . qui gen `XT2' = . qui gen `YT2' = . forval i = 2/121 { local j = `i'-1 local xupj = `Xup'[`j'] local yupj = `Yup'[`j'] local xlowj = `Xlow'[`j'] local ylowj = `Ylow'[`j'] rot `xupj' `yupj' `co' `si' local xxui = `r(out)' qui replace `XXU' = `xxui' in `i' if `xxui' > `xmax' local xmax = `xxui' if `xxui' < `xmin' local xmin = `xxui' rot `xupj' `yupj' -`si' `co' local yyui = `r(out)' qui replace `YYU' = `yyui' in `i' rot `xlowj' `ylowj' `co' `si' local xxli = `r(out)' qui replace `XXL' = `xxli' in `i' if `xxli' > `xmax' local xmax = `xxli' if `xxli' < `xmin' local xmin = `xxli' rot `xlowj' `ylowj' -`si' `co' local yyli = `r(out)' qui replace `YYL' = `yyli' in `i' } qui replace `XXL' = `XXL'[2] in 1 qui replace `YYL' = `YYL'[2] in 1 qui replace `XXU' = `XXL'[2] in 1 qui replace `YYU' = `YYL'[2] in 1 local xxu2 = `XXU'[2] local yyu2 = `YYU'[2] local xxl2 = `XXL'[2] local xup2 = `Xup'[2] if (`xxu2' < `xxl2' & `xup2' > 0 ) | (`xxu2' > `xxl2' & `xup2' < 0) { qui replace `XXL' = `XXU'[2] in 1 qui replace `YYL' = `YYU'[2] in 1 qui replace `XXU' = `XXU'[2] in 1 qui replace `YYU' = `YYU'[2] in 1 } if `xup2' > 0 { local x0 = `dd' + `xmin' } else { local x0 = `xmax' - `dd' } local hair = " || pci 0 0 0 0 " local DONE = 0 while `DONE' == 0 { local nl = 0 local nu = 0 forval i = 1/120 { local j = `i'+1 local xxui = `XXU'[`i'] local xxuj = `XXU'[`j'] local yyui = `YYU'[`i'] local yyuj = `YYU'[`j'] if (`xxui' < `x0' & `xxuj' > `x0') | (`xxui' > `x0' & `xxuj' < `x0') { local nu = `nu'+1 qui replace `XT1' = `x0' in `nu' qui replace `YT1' = ((`yyui'-`yyuj')*`x0' -(`yyui'*`xxuj' - `yyuj'*`xxui')) / (`xxui'-`xxuj') in `nu' } local xxli = `XXL'[`i'] local xxlj = `XXL'[`j'] local yyli = `YYL'[`i'] local yylj = `YYL'[`j'] if (`xxli' < `x0' & `xxlj' > `x0') | (`xxli' > `x0' & `xxlj' < `x0') { local nl = `nl'+1 qui replace `XT2' = `x0' in `nl' qui replace `YT2' = ((`yyli'-`yylj')*`x0' -(`yyli'*`xxlj' - `yylj'*`xxli')) / (`xxli'-`xxlj') in `nl' } } local xt11 = `XT1'[1] local xt12 = `XT1'[2] local yt11 = `YT1'[1] local yt12 = `YT1'[2] local xt21 = `XT2'[1] local xt22 = `XT2'[2] local yt21 = `YT2'[1] local yt22 = `YT2'[2] //di in red "nl = `nl', nu = `nu'" if (`nl'==0 & `nu'==2) { rot `xt11' `yt11' `co' -`si' local x1 = `r(out)' rot `xt11' `yt11' `si' `co' local y1 = `r(out)' rot `xt12' `yt12' `co' -`si' local x2 = `r(out)' rot `xt12' `yt12' `si' `co' local y2 = `r(out)' if ("`lhalf'" == "") { //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } if ("`rhalf'" == "") { local x1 = -`x1' // left-hand side local x2 = -`x2' //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } } else if (`nl'==2 & `nu'==0) { rot `xt21' `yt21' `co' -`si' local x1 = `r(out)' rot `xt21' `yt21' `si' `co' local y1 = `r(out)' rot `xt22' `yt22' `co' -`si' local x2 = `r(out)' rot `xt22' `yt22' `si' `co' local y2 = `r(out)' if ("`lhalf'" == "") { //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } if ("`rhalf'" == "") { local x1 = -`x1' // left-hand side local x2 = -`x2' //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } } else if (`nl' >= 1 & `nu'==1) | (`nl'==1 & `nu' >= 1) { rot `xt11' `yt11' `co' -`si' local x1 = `r(out)' rot `xt11' `yt11' `si' `co' local y1 = `r(out)' rot `xt21' `yt21' `co' -`si' local x2 = `r(out)' rot `xt21' `yt21' `si' `co' local y2 = `r(out)' if ("`lhalf'" == "") { //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } if ("`rhalf'" == "") { local x1 = -`x1' // left-hand side local x2 = -`x2' //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } } else if (`nl'==2 & `nu'==2) { if (`yt11' < `yt12' & `yt21' < `yt22') | (`yt11' > `yt12' & `yt21' > `yt22') { rot `xt11' `yt11' `co' -`si' local x1 = `r(out)' rot `xt11' `yt11' `si' `co' local y1 = `r(out)' rot `xt21' `yt21' `co' -`si' local x2 = `r(out)' rot `xt21' `yt21' `si' `co' local y2 = `r(out)' if ("`lhalf'" == "") { //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } if ("`rhalf'" == "") { local x1 = -`x1' // left-hand side local x2 = -`x2' //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } rot `xt12' `yt12' `co' -`si' local x1 = `r(out)' rot `xt12' `yt12' `si' `co' local y1 = `r(out)' rot `xt22' `yt22' `co' -`si' local x2 = `r(out)' rot `xt22' `yt22' `si' `co' local y2 = `r(out)' if ("`lhalf'" == "") { //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } if ("`rhalf'" == "") { local x1 = -`x1' // left-hand side local x2 = -`x2' //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } } else { rot `xt11' `yt11' `co' -`si' local x1 = `r(out)' rot `xt11' `yt11' `si' `co' local y1 = `r(out)' rot `xt22' `yt22' `co' -`si' local x2 = `r(out)' rot `xt22' `yt22' `si' `co' local y2 = `r(out)' if ("`lhalf'" == "") { //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } if ("`rhalf'" == "") { local x1 = -`x1' // left-hand side local x2 = -`x2' //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } rot `xt12' `yt12' `co' -`si' local x1 = `r(out)' rot `xt12' `yt12' `si' `co' local y1 = `r(out)' rot `xt21' `yt21' `co' -`si' local x2 = `r(out)' rot `xt21' `yt21' `si' `co' local y2 = `r(out)' if ("`lhalf'" == "") { //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } if ("`rhalf'" == "") { local x1 = -`x1' // left-hand side local x2 = -`x2' //local hair "`hair' || pci `y1' `x1' `y2' `x2', lcolor(black)" local hair "`hair' `y1' `x1' `y2' `x2' " } } } local xup = `Xup'[2] if `xup' > 0 { local x0 = `x0' + `dd' if `x0' >= `xmax' local DONE = 1 } else { local x0 = `x0' - `dd' if `x0' <= `xmin' local DONE = 1 } } if (`z15F' < .51 & `z15F' > .49) { // xxx local xx2 = `XXL'[1] local yy2 = `YYL'[1] //local hair "`hair' || pci 9.9 0 `yy2' `xx2', lcolor(black)" local hair "`hair' 9.9 0 `yy2' `xx2' " } local hair "`hair', lcolor(black)" //di as err "hair is" _n as err "`hair'" ************************************ NOSE LINE Z16 local ti = 2.282 poly5 `ti' 1.2245 -.4339 .1431 -.0135 -.1396 .0537 local x1t = `r(out)' local xx = .3 + `z16F'*(`x1t' - .3) qui gen `X16' = . qui gen `Y16' = . qui replace `X16' = `xx' in 1 qui replace `Y16' = 0 in 1 forval i = 2/81 { local yy = -((`i'-1)/20) qui replace `Y16' = `yy' in `i' local ti = `yy' + 2.282 poly5 `ti' 1.2245 -.4339 .1431 -.0135 -.1396 .0537 local x1t = `r(out)' qui replace `X16' = .3 + (`z16F'*(`x1t'-.3)) in `i' } qui gen `X16L' = -`X16' ************************************ MOUTH SIZE Z17 & MOUTH CURVATURE Z18 local d = (`z18F' - .5)/3 qui gen `X17' = . qui gen `Y17' = . qui replace `X17' = 0 in 1 qui replace `Y17' = -6 in 1 forval i = 2/30 { qui replace `X17' = (`i'-1)/10 in `i' local xx = `X17'[`i'] local ti = `xx' - 1.5 poly5 `ti' -6.1531 -.1583 -.0347 -.0418 -.0038 .0174 local x0t = `r(out)' poly5 `ti' -5.7326 -.3889 -.1487 .0233 -.0366 .0347 local x1t = `r(out)' local yy1 = `x0t' + `z17F'*(`x1t' - `x0t') local a = `d'*(`xx')^2 qui replace `Y17' = `yy1' + `a' in `i' } qui replace `X17' = 3 in 31 qui replace `Y17' = -6.5 + 9*`d' in 31 qui gen `X17L' = -`X17' **************************************** Z18 qui gen `X18' = . qui gen `Y18' = . qui replace `X18' = 0 in 1 qui replace `Y18' = -7 in 1 forval i = 2/30 { qui replace `X18' = (`i'-1)/10 in `i' local xx = `X18'[`i'] local ti = `xx' - 1.5 poly5 `ti' -6.6522 .1503 -.0513 .0402 .0040 -.0148 local x0t = `r(out)' poly5 `ti' -6.9965 .0482 .0609 .0191 .0201 .0144 local x1t = `r(out)' local yy1 = `x0t' + `z17F'*(`x1t' - `x0t') local a = `d'*(`xx')^2 qui replace `Y18' = `yy1' + `a' in `i' } qui replace `X18' = 3 in 31 qui replace `Y18' = -6.5 + 9*`d' in 31 qui gen `X18L' = -`X18' // check for _null_ ++++++++++++++++++++++++++++++++++++++++++++++++++ $$$ local c = 1 foreach null of local facef { tokenize `"``null''"', parse("(), ") local head `1' //di in yellow "head is `head'" macro shift macro shift local v1 `1' //di in yellow "v1 is `v1'" if ("`v1'" == "_null_") { if ("`head'" == "nose") { qui replace `X16' = . qui replace `X16L' = . } if ("`head'" == "fline") { qui replace `Xface' = . qui replace `XfaceL' = . } if ("`head'" == "bcurv" | "`head'" == "bdens" | "`head'" == "bhor" | "`head'" == "bvert") { qui replace `X7' = . qui replace `X7L' = . } if ("`head'" == "isize" | "`head'" == "ihor" | "`head'" == "ivert" | "`head'" == "iangle") { qui replace `X1' = . qui replace `X1L' = . qui replace `X2' = . qui replace `X2L' = . } if ("`head'" == "hdark" | "`head'" == "hslant") { local hair = "" } if ("`head'" == "hupper") { qui replace `Xup' = . qui replace `XupL' = . } if ("`head'" == "hlower") { qui replace `Xlow' = . qui replace `XlowL' = . } if ("`head'" == "msize" | "`head'" == "mcurv" ) { qui replace `X17' = . qui replace `X17L' = . qui replace `X18' = . qui replace `X18L' = . } if ("`head'" == "psize" | "`head'" == "ppos" ) { qui replace `X3' = . qui replace `X3L' = . qui replace `X4' = . qui replace `X4L' = . } } local c = `c'+1 } // construction of face finished ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ local FACE`f' "FACE`f'" // do not use tempfile as they are written to c(tmpdir) // and network users may not have access to it local FACES "`FACES' `FACE`f''.gph" if "`show'" != "show" { local noshow "nodraw" // do not draw individual face graphs } if "`ititle'" == "" { local facetitle = "" } else { local facetitle = `ititle'[`f'] } if "`inote'" == "" { local facenote = "" } else { local facenote = `inote'[`f'] } if "`iscale'" == "" { local facescale = 1 } else { local facescale = `iscale'[`f'] } if "`ilabel'" == "" { local facelbl = "" } else { local flbl = `ilabel'[`f'] if ("`lhalf'" != "") { // defaults for left face if `xlabel' == 64 local xlabel = -8 if `placement' == 64 local placement = 3 if "`justification'" == "" local justification = "right" } else if ("`rhalf'" != "") { // defaults for right face if `xlabel' == 64 local xlabel = 8 if `placement' == 64 local placement = 9 if "`justification'" == "" local justification = "left" } else { // full face if `xlabel' == 64 local xlabel = 10 if `placement' == 64 local placement = 9 if "`justification'" == "" local justification = "left" } tokenize `lsize', parse("()") macro shift macro shift local lblsize `1' if ("`lblsize'" == "") local lblsize = "large" local facelbl "text(`ylabel' `xlabel' `"`flbl'"', place(`placement') just(`justification') size(`lblsize'))" } tokenize `imargin', parse("()") macro shift macro shift local mgnsize `1' if (`"`imargin'"' == "") { local imargin = "margin(zero)" // default margin for faces is zero } else { local imargin = `"margin(`mgnsize')"' } tokenize `iregion', parse("()") macro shift macro shift local rgnsize `1' if (`"`iregion'"' == "") { local iregion = "margin(medsmall)" // default margin fof faces is medsmall } else { local iregion = `"margin(`rgnsize')"' } if ("`rhalf'" != "") { // draw only the right side of the face qui replace `XfaceL' = . qui replace `XlowL' = . qui replace `XupL' = . qui replace `X16L' = . qui replace `X17L' = . qui replace `X18L' = . qui replace `X7L' = . qui replace `X1L' = . qui replace `X2L' = . qui replace `X3L' = . qui replace `X4L' = . } if ("`lhalf'" != "") { // draw only the left side of the face qui replace `Xface' = . qui replace `Xlow' = . qui replace `Xup' = . qui replace `X16' = . qui replace `X17' = . qui replace `X18' = . qui replace `X7' = . qui replace `X1' = . qui replace `X2' = . qui replace `X3' = . qui replace `X4' = . } local aspectratio = "aspectratio(1.2, placement(center))" if (`"`lhalf'"' != "" | `"`rhalf'"' != "") { local aspectratio = "aspectratio(2.4, placement(center))" } if ("`rhalf'" != "" | "`lhalf'" != "") { local forcedX = 50 } else { local forcedX = 100 } qui twoway line `Yface' `Xface', lcolor(black) || line `Yface' `XfaceL', lcolor(black) /// || line `Ylow' `Xlow', lcolor(black) || line `Ylow' `XlowL', lcolor(black) /// || line `Yup' `Xup', lcolor(black) || line `Yup' `XupL', lcolor(black) /// || line `Y16' `X16', lcolor(black) || line `Y16' `X16L', lcolor(black) /// || line `Y17' `X17', lcolor(black) || line `Y18' `X18', lcolor(black) /// || line `Y17' `X17L', lcolor(black) || line `Y18' `X18L', lcolor(black) /// || line `Y7' `X7', lcolor(black) lwidth(medthick) || line `Y7' `X7L', lcolor(black) lwidth(medthick) /// || line `Y1' `X1', lcolor(black) || line `Y2' `X2', lcolor(black) /// || line `Y1' `X1L', lcolor(black) || line `Y2' `X2L', lcolor(black) /// || line `Y3' `X3', lcolor(black) || line `Y4' `X4', lcolor(black) /// || line `Y3' `X3L', lcolor(black) || line `Y4' `X4L', lcolor(black) /// `hair' `facelbl' /// , legend(off) xscale(off) yscale(off) xlabel(,nogrid) ylabel(,nogrid) xsize(`xface') ysize(`yface') /// fxsize(`forcedX') fysize(100) saving("`FACE`f''",replace) `noshow' graphregion(color(white) `imargin') /// note(`"`facenote'"', color(black)) title(`"`facetitle'"', color(black)) scale(`facescale') `aspectratio' /// plotregion(`iregion') //serset dir qui serset local rid = `r(id)' if `r(id)' == 0 local rid = 1 local nsersets = `nsersets' + `rid' capture confirm file FACE0.gph if _rc { // draw blank graph - but only once quietly twoway line `Yface' `Xface', lcolor(white) legend(off) xscale(off) yscale(off) /// || line `Yup' `Xup', lcolor(white) legend(off) xscale(off) yscale(off) xsize(`xface') ysize(`yface') /// fxsize(`forcedX') fysize(100) xlabel(,nogrid) ylabel(,nogrid) saving(FACE0, replace) nodraw /// graphregion(color(white) `imargin') `aspectratio' } //drop `z1' `z2' `z3' `z4' `z5' `z6' `z7' `z8' `z9' `z10' `z11' `z12' `z13' `z14' `z15' `z16' `z17' `z18' drop `X1' `Y1' `X1L' `X2' `Y2' `X2L' `X3' `Y3' `X3L' `X4' `Y4' `X4L' `X7' `Y7' `X7L' `X16' `Y16' `X16L' `X17' `Y17' `X17L' `X18' `Y18' `X18L' drop `Xface' `Yface' `XfaceL' `Xlow' `Ylow' `XlowL' `Xup' `Yup' `XupL' `XXU' `YYU' `XXL' `YYL' `XT1' `YT1' `XT2' `YT2' } // end of individual faces construction //di as err "number of sersets is `nsersets'" if "`timer'" == "timer" { timer off 1 timer list 1 capture timer clear 2 timer on 2 } if "`nocombine'" == "" { // combine all individual graphs into one graph //di as err "number of sersets to combine is `nsersets'" if `nsersets' > 1999 { di as err "Number of sersets exceeds 1,999, cannot draw a combined graph." di as err "Individual face graphs are saved as FACE1.gph, ..., FACE`faces'.gph." } else { tokenize `rows', parse("()") macro shift macro shift local ROW `1' tokenize `cols', parse("()") macro shift macro shift local COL `1' tokenize `xcombined', parse("()") macro shift macro shift local XS `1' //di "XS is `XS'" tokenize `ycombined', parse("()") macro shift macro shift local YS `1' //di "YS is `YS'" // calculate the size of the combined graph if the user did not specify it if ("`XS'" == "" & "`YS'" == "") { if ("`ROW'" == "" & "`COL'" == "") { local COL = ceil(sqrt(`faces')) local ROW = ceil(`faces' / `COL') } else if ("`COL'" == "") { // only rows() was specified local COL = ceil(`faces' / `ROW') } else if ("`ROW'" == "") { // only cols() was specified local ROW = ceil(`faces' / `COL') } else if ("`ROW'" != "" & "`COL'" != "") { // if both specified, cols() takes precedence local ROW = ceil(`faces' / `COL') } local YS = `ROW' * `yface' local XS = `COL' * `xface' if ("`rhalf'" != "" | "`lhalf'" != "") local XS = `XS'*`hspace' //.50 will keep half graphs close to each other // looks like graph dimensions cannot exceed 20 so rescale local RCmax = max(`XS',`YS') if (`RCmax' > 20) { local shrink = 20 / `RCmax' local XS = `XS' * `shrink' local YS = `YS' * `shrink' } } gettoken SAVE RPL : saving , parse(",") quotes //di as res "saveas is " as res `"`SAVE'"' //di as res "replace is " as res "`RPL'" di di in yellow "Finished creating individual faces, now processing " in green "graph combine" in yellow "." di in yellow "Please be patient, it may take a while...." di // +++++++++++++++++++++++++++++++++++++++++ legend - displays legend in two or three rows $$$ tokenize `legend' local legend1 `1' local legend2 `2' if ("`legend1'" == "2" | "`legend1'" == "3") { local note = "" // legend overrides graph note local features "isize iangle ihor ivert psize ppos bcurv bdens bhor bvert hupper hlower hdark hslant fline nose msize mcurv" local b2t = "" local cap = "" local r2t = "" local c = 1 foreach f of local features { tokenize `"``f''"', parse("(), ") //di as err "f is `f' and ``f''" macro shift macro shift local v1 `1' //di as err "v1 is `v1'" if ("`v1'" != "" & "`v1'" != "." & "`v1'" != "_null_") { local feature = "`f'" if ("`legend2'" == "nolab" | "`legend2'" == "nolabe" | "`legend2'" == "nolabel") { local vlegend = "`v1'" } else { capture local vlegend : variable label `v1' if ("`vlegend'" == "") local vlegend = "`v1'" } if ("`legend1'"=="2") { if (mod(`c',2) != 0) { local b2t `"`b2t'"`feature' - `vlegend'" "' } else { local cap `"`cap'"`feature' - `vlegend'" "' } local c = `c'+1 } else if ("`legend1'"=="3") { if (mod(`c',3)/3 == 1/3) { local b2t `"`b2t'"`feature' - `vlegend'" "' } else if (mod(`c',3)/3 == 2/3) { local cap `"`cap'"`feature' - `vlegend'" "' } else { local r2t `"`r2t'"`feature' - `vlegend'" "' } local c = `c'+1 } } } } // if local SAVE is empty, specifying saving() creates Graph.gph - use IF .... ELSE to solve this problem if "`saving'" == "" { qui graph combine `FACES' , `title' `subtitle' `note' rows(`ROW') cols(`COL') `colfirst' /// scheme(s1mono) /*`imargin'*/ xsize(`XS') ysize(`YS') `draw' plotregion(margin(zero)) graphregion(margin(zero)) /// b2title(" " `b2t',size(vsmall) ring(1) justification(left) placement(west) pos(7)) /// caption(" " `cap',size(vsmall) ring(1) justification(left) placement(center) pos(5)) /// note(" " `r2t',size(vsmall) ring(1) justification(left) placement(east) pos(6)) } else { qui graph combine `FACES' , `title' `subtitle' `note' rows(`ROW') cols(`COL') `colfirst' /// scheme(s1mono) /*`imargin'*/ xsize(`XS') ysize(`YS') `draw' plotregion(margin(zero)) graphregion(margin(zero)) /// b2title(" " `b2t',size(vsmall) ring(1) justification(left) placement(west) pos(7)) /// caption(" " `cap',size(vsmall) ring(1) justification(left) placement(center) pos(5)) /// note(" " `r2t',size(vsmall) ring(1) justification(left) placement(east) pos(6)) } } // end of else } if "`timer'" == "timer" { timer off 2 timer list 2 } //save or erase individual face graphs if ("`saveall'" == "" & `nsersets' < 2000) { forval i = 1/`faces' { capture erase "`FACE`i''.gph" } erase FACE0.gph } qui drop if `one' == 0 // restore the original number of obs if it is less than 121 di di in yellow "The command has finished." end ***************************************************************** SUROUTINES +++++++++++++++++++++++++++++++++++++++ capture program drop poly5 program define poly5, rclass args x c0 c1 c2 c3 c4 c5 return local out = ((((`c5'*(`x') + `c4')*(`x') + `c3')*(`x') + `c2')*(`x') + `c1')*(`x') + `c0' end capture program drop rotate program define rotate, rclass args rdx rdy rc rs rzx rzy local rdx1 = `rdx' - `rzx' local rdy1 = `rdy' - `rzy' return local outx = `rc'*`rdx1' - `rs'*`rdy1' + `rzx' return local outy = `rs'*`rdx1' + `rc'*`rdy1' + `rzy' end capture program drop rot program define rot, rclass args x y al be return local out = `x'*`al' + `y'*`be' end //standardize x to [0,1] range capture program drop chscale program define chscale args var1 var2 min max qui gen `var2' = (`var1' - `min') / (`max' - `min') end exit