*! version 1.0.0 03apr2022 Ben Jann program colorcheck version 14.2 syntax [, NOGRaph /// TItle(passthru) GRopts(passthru) HORizontal VERTical /// span LABels(passthru) LColor(passthru) LWidth(passthru) /// BARWidth(passthru) NONUMbers sort SORT2(passthru) * ] if "`horizontal'"!="" & "`vertical'"!="" { di as err "may not combine options horizontal and vertical" exit 198 } local noinfo 0 if `"`r(ptype)'"'!="color" local noinfo 1 else if `"`r(p)'"'=="" local noinfo 1 if `noinfo' { di as err "no color info found; need to run {bf:colorpalette} " /* */ "before applying {bf:colorcheck}" exit 498 } tempname rhold _return hold `rhold' _return restore `rhold', hold capt n _colorcheck, `options' if _rc { _return restor `rhold' exit _rc } else { _return drop `rhold' } if "`change'`nograph'"!="" { _colorcheck_display } if "`nograph'"!="" exit _return hold `rhold' _return restore `rhold', hold capt n _colorcheck_graph, `title' `gropts' `horizontal' /* */ `vertical' `labels' `lcolor' `lwidth' `barwidth' /* */ `nonumbers' `sort' `sort2' _return restor `rhold' if _rc exit _rc end program _colorcheck_display di di _col(20) as txt "Number of colors =" as res %8.0g r(n) di _col(20) as txt "N. of comparisons =" as res %8.0g comb(r(n),2) di _col(20) as txt "CVD severity =" as res %8.0g r(cvd) di _col(20) as txt "Proportion of gray =" as res %8.0g r(mono) di _col(20) as txt "Grayscale method =" as res %8s r(mono_method) di _col(20) as txt "Difference metric =" as res %8s r(metric) matlist r(delta), rowtitle("Delta E") border(rows) twidth(14) end program _colorcheck, rclass // defaults local d_metric "Jab" local d_mono 1 local d_mono_method "LCh" local d_cvd 1 // syntax syntax [, Metric(str) mono(str) cvd(numlist max=1 >=0 <=1) ] _parse_mono `mono' if `"`metric'"'=="" local metric "`d_metric'" if `"`mono'"'=="" local mono `d_mono' if `"`mono_method'"'=="" local mono_method "`d_mono_method'" if `"`cvd'"'=="" local cvd `d_cvd' // check whether results are already computed and settings did not change capt confirm matrix r(delta) if _rc==1 exit _rc if _rc==0 { // r(delta) exists local change 0 if `"`metric'"'!=`"`r(metric)'"' local change 1 else if `"`mono'"'!=`"`r(mono)'"' local change 1 else if `"`mono_method'"'!=`"`r(mono_method)'"' local change 1 else if `"`cvd'"'!=`"`r(cvd)'"' local change 1 if `change'==0 { return add exit // nothing to do } } c_local change "change" // obtain color info local pclass `"`r(pclass)'"' local colors `"`r(p)'"' return add // compute results tempname delta mata: colorcheck() mat coln `delta' = minimum maximum mean mat rown `delta' = "normal sight" /* */ monochromacy deuteranomaly protanomaly tritanomaly // return results return local metric `"`metric'"' return scalar mono = `mono' return local mono_method `"`mono_method'"' return scalar cvd = `cvd' return matrix delta = `delta' return local p_mono `"`p_mono'"' return local p_deut `"`p_deut'"' return local p_prot `"`p_prot'"' return local p_trit `"`p_trit'"' end program _parse_mono _parse comma p opts : 0 gettoken comma opts : opts, parse(",") local 0 `", mono(`p') `opts'"' syntax [, mono(numlist >=0 <=1) * ] if "`mono'"=="" local mono 1 c_local mono `mono' c_local mono_method `"`options'"' end program _colorcheck_graph syntax [, TItle(passthru) GRopts(str asis) HORizontal VERTical /// LABels(passthru) LColor(passthru) LWidth(passthru) /// BARWidth(passthru) NONUMbers sort SORT2(str) ] if `"`sort2'"'!="" local sort sort if "`sort'"!="" _parse_sort, `sort2' if `"`title'"'=="" local title title(`r(pname)') local pct `:di % 7.0g r(mono)*100' local cvd `:di % 7.0g r(cvd)*100' if "`vertical'"!="" { if `"`labels'"'=="" { local haspct = !(`pct'==100 & `cvd'==100) local lbl "Normal sight" if `haspct' local lbl `""`lbl'" " ""' local labels `"`"`lbl'"'"' local lbl "Monochromacy" if `pct'!=100 local lbl `""`lbl'" "(`pct' percent)""' else if `haspct' local lbl `""`lbl'" " ""' local labels `"`labels' `"`lbl'"'"' local i 2 foreach nm in Deuter Prot Trit { local ++i if `cvd'!=100 local lbl `""`nm'anomaly" "(`cvd' percent)""' else if `haspct' local lbl `""`nm'anopia" " ""' else local lbl "`nm'anopia" local labels `"`labels' `"`lbl'"'"' } local labels labels(`labels') } local mlab forv i = 1/5 { local di `: di %9.1f el(r(delta),`i',1)' local mlab `"`mlab'`i' "{&Delta}E {&ge} `di'" "' } local gopts xlabel(,labgap(4)) xmlabel(`mlab', labsize(medsmall) notick labgap(-1)) } else { local txt .5 .5 "Normal sight" if `pct'==100 local txt `txt' 1.5 .5 "Monochromacy" else local txt `txt' 1.5 .5 "Monochromacy (`pct' percent)" if `cvd'==100 local txt `txt' /* */ 2.5 .5 "Deuteranopia" 3.5 .5 "Protanopia" 4.5 .5 "Tritanopia" else local txt `txt' 2.5 .5 "Deuteranomaly (`cvd' percent)" /* */ 3.5 .5 "Protanomaly (`cvd' percent)" /* */ 4.5 .5 "Tritanomaly (`cvd' percent)" local gopts text(`txt', place(e)) if `"`labels'"'=="" { forv i = 1/5 { local di `: di %9.1f el(r(delta),`i',1)' local labels `"`labels'"{&Delta}E {&ge} `di'" "' } local labels labels(`labels') local gopts `gopts' yscale(alt) } if `"`barwidth'"'=="" local barwidth barwidth(0.6) } mata: colorcheck_sort(`"`sort2'"') colorpalette, `title' `horizontal' `vertical' `lcolor' `lwidth' /* */ `barwidth' `nonumbers' `labels' gropts(`gopts' `gropts'): /* */ `p_normal' / `p_mono' / `p_deut' / `p_prot' / `p_trit' end program _parse_sort syntax [, Normal Mono Deuter Prot Trit ] local sort `normal' `mono' `deuter' `prot' `trit' if `:list sizeof sort'>1 { di as err "sort(): too many keyword specified" exit 198 } if "`sort'"=="" local sort normal c_local sort2 `sort' end version 14 mata: mata set matastrict on void colorcheck() { real scalar i, gs_p, cvd_p string scalar metric, gs_space string rowvector cvd real matrix delta, RGB, P real colvector E class ColrSpace scalar S // settings gs_p = strtoreal(st_local("mono")) cvd_p = strtoreal(st_local("cvd")) gs_space = st_local("mono_method") metric = st_local("metric") // import colors and prepare analysis S.colors(st_local("colors")) if (S.N()<=1) { stata(`"di as err "need at least two colors""') exit(error(498)) } RGB = S.get("RGB1") P = colorcheck_subsets(S.N()) delta = J(5,3,.) // original E = S.delta(P, metric) delta[1,] = minmax(E), mean(E) // grayscale S.gray(gs_p, gs_space) E = S.delta(P, metric) delta[2,] = minmax(E), mean(E) st_local("p_mono", S.colors()) // CVD cvd = ("deut","prot","trit") for (i=length(cvd);i;i--) { S.set(RGB, "RGB1") S.cvd(cvd_p, cvd[i]) E = S.delta(P, metric) delta[2+i,] = minmax(E), mean(E) st_local("p_"+cvd[i], S.colors()) } // return results st_matrix(st_local("delta"), delta) } real matrix colorcheck_subsets(real scalar n) { real scalar i, j, k real matrix P P = J(comb(n, 2),2,.) k = 0 for (i=1; i<=n; i++) { for (j=i+1;j<=n;j++) P[++k,] = (i,j) } return(P) } void colorcheck_sort(string scalar sort) { real colvector p string scalar xlab class ColrSpace scalar S if (sort=="") { st_local("p_normal", st_global("r(p)")) st_local("p_mono", st_global("r(p_mono)")) st_local("p_deut", st_global("r(p_deut)")) st_local("p_prot", st_global("r(p_prot)")) st_local("p_trit", st_global("r(p_trit)")) return } if (sort=="mono") { S.colors(st_global("r(p_mono)")) p = order(S.get("RGB1")[,1],1) } else if (sort=="deuter") { S.colors(st_global("r(p_deut)")) p = order(S.get("HSL")[,1],1) } else if (sort=="prot") { S.colors(st_global("r(p_prot)")) p = order(S.get("HSL")[,1],1) } else if (sort=="trit") { S.colors(st_global("r(p_trit)")) p = order(S.get("HSL")[,1],1) } else if (sort=="normal") { S.colors(st_global("r(p)")) p = order(S.get("HSL")[,1],1) } st_local("p_normal", invtokens((`"""':+tokens(st_global("r(p)")):+`"""')[p])) st_local("p_mono", invtokens((`"""':+tokens(st_global("r(p_mono)")):+`"""')[p])) st_local("p_deut", invtokens((`"""':+tokens(st_global("r(p_deut)")):+`"""')[p])) st_local("p_prot", invtokens((`"""':+tokens(st_global("r(p_prot)")):+`"""')[p])) st_local("p_trit", invtokens((`"""':+tokens(st_global("r(p_trit)")):+`"""')[p])) if (st_local("nonumbers")=="") { xlab = invtokens((strofreal(1::S.N()):+" ":+`"""':+strofreal(p):+`"""')') if (st_local("vertical")!="") st_local("gopts", st_local("gopts") + " ylabel(" + xlab+", nogrid notick angle(hor))") else st_local("gopts", st_local("gopts") + " xlabel(" + xlab+", notick)") st_local("nonumbers", "nonumbers") } } end exit