*! version 1.0.1 29apr2023 Ben Jann program grinset version 14.2 // syntax capt _on_colon_parse `0' if _rc==1 exit _rc if _rc==0 { local 0 `"`s(before)'"' local gr `"`s(after)'"' if `"`gr'"'=="" { di as err "must specify plot command or is inset ID after colon" exit 198 } capt numlist `"`gr'"' if _rc==0 { numlist `"`gr'"', max(1) int local ID = abs(`r(numlist)') local gr } } syntax [anything(equalok)] [, /* */ Size(numlist max=2 >0) /* */ SCale(numlist max=1 >0) /* */ name(str) nodraw ] // graph to modify gettoken NAME : anything, parse(" =") if `"`NAME'"'!="" { capt confirm name `NAME' if _rc local NAME else { gettoken NAME next : anything, parse(" =") gettoken next : next, parse(" =") if `"`next'"'=="=" local NAME else gettoken NAME anything : anything } } if `"`NAME'"'!="" { capt classutil d `NAME' if _rc { di as err `"graph `NAME' not found"' exit 111 } } else { capt gr_current NAME :, query if _rc { di as err "no graph found" exit 111 } } // name of output graph _parse_name `name' // returns name replace // positioning mata: _parse_positions(st_local("anything")) // ypos, yref, xpos, xref if "`ypos'"!="" confirm number `ypos' if "`xpos'"!="" confirm number `xpos' // no insert to add; modify existing inset if `"`gr'"'=="" { capt n _modify_inset "`NAME'" "`name'" "`replace'" "`draw'" "`ID'" /* */ "`size'" "`scale'" "`yref'" "`ypos'" "`xref'" "`xpos'" if _rc exit _rc exit } // size and scale options if "`size'"=="" local size 25 local size `size' `size' gettoken fysize size : size gettoken fxsize size : size if "`scale'"=="" local scale .5 // properties of main (current/topmost) graph; determine fysize/fxsize local scheme scheme(`.`NAME'._scheme.scheme_name') local ysize `.`NAME'.style.declared_ysize.val' local xsize `.`NAME'.style.declared_xsize.val' if `ysize'>`xsize' local fysize = `fysize' * (`ysize'/`xsize') else local fxsize = `fxsize' * (`xsize'/`ysize') // create inset graph _parse comma lhs gropts: gr _parse_gropts `gropts' // returns ischeme, updates gropts if `"`ischeme'"'=="" local ischeme `scheme' tempname INSET `lhs', `gropts' `ischeme' /* */ graphregion(style(none) istyle(none) margin(zero)) nodraw /* */ name(`INSET') fysize(`fysize') fxsize(`fxsize') scale(`scale') // add inset to main graph if "`name'"=="" { local name `NAME' if "`name'"!="Graph" { local nameopt name(`name', replace) } } else if "`name'"!="Graph" { if "`replace'"!="" local nameopt(`name', replace) else local nameopt(`name') } graph combine `NAME' `INSET', `nameopt' `scheme' nodraw /* */ graphregion(margin(zero)) altshrink /* */ ysize(`ysize') xsize(`xsize') _gm_edit .`name'.plotregion1.Expand graph2 left 1 // reposition insert if "`ypos'`xpos'"!="" { _position_inset "`name'" "`yref'" "`ypos'" "`xref'" "`xpos'" /* */ `ysize' `xsize' } // done if "`draw'"=="" { graph display `name', ysize(`ysize') xsize(`xsize') } end program _parse_name syntax [name] [, Replace ] c_local name `namelist' c_local replace `replace' end program _parse_gropts syntax [, SCHeme(passthru) /* remove: */ GRAPHRegion(str) nodraw name(str) /* */ fysize(str) fxsize(str) scale(str) /* */ * ] c_local ischeme `scheme' c_local gropts `macval(options)' end program _modify_inset args NAME name replace draw ID size scale yref ypos xref xpos if "`name'"=="`NAME'" local name if "`name'"!="" { if "`name'"=="Graph" local replace replace graph copy `NAME' `name', `replace' local NAME `name' } else local name `NAME' local id `name' if "`ID'"=="" local ID 1 forv i=2/`ID' { local id `id'.plotregion1.graph1 } capture { if "`scale'"!="" { _gm_edit .`id'.plotregion1.graph2.gmetric_mult = `scale' } local ysize `.`name'.style.declared_ysize.val' local xsize `.`name'.style.declared_xsize.val' if "`size'"!="" { local size `size' `size' gettoken fysize size : size gettoken fxsize size : size if `ysize'>`xsize' local fysize = `fysize' * (`ysize'/`xsize') else local fxsize = `fxsize' * (`xsize'/`ysize') _gm_edit .`id'.plotregion1.graph2.fixed_ysize = `fysize' _gm_edit .`id'.plotregion1.graph2.fixed_xsize = `fxsize' } if "`ypos'`xpos'"!="" { _position_inset "`id'" "`yref'" "`ypos'" "`xref'" "`xpos'" /* */ `ysize' `xsize' } } if _rc==1 exit _rc if _rc { di as err "could not modify inset; maybe inset does not exist" exit 498 } if "`draw'"=="" { graph display `name' } end program _position_inset args id yref ypos xref xpos ysize xsize local fy 1 local fx 1 if `ysize'>`xsize' local fy = `ysize'/`xsize' else local fx = `xsize'/`ysize' if "`yref'"!="" { local ypos = 50*`fy' - `ypos' - /* */ `.`id'.plotregion1.graph2.fixed_ysize'/2 if "`yref'"=="b" local ypos = -`ypos' } if "`xref'"!="" { local xpos = 50*`fx' - `xpos' - /* */`.`id'.plotregion1.graph2.fixed_xsize'/2 if "`xref'"=="l" local xpos = -`xpos' } _gm_edit .`id'.plotregion1.graph2.yoffset = `ypos' _gm_edit .`id'.plotregion1.graph2.xoffset = `xpos' end version 14.2 mata: mata set matastrict on void _parse_positions(string scalar s) { real scalar l string scalar ypos, xpos, yref, xref string rowvector t, xrefs, yrefs yref = xref = "" yrefs = ("t", "b") xrefs = ("l", "r") t = tokens(s, " =") l = length(t) if (l<=0) { // nothing specified ypos = xpos = "" } else if (l<=2) { // ypos [xpos] ypos = t[1] xpos = (l<2 ? "0" : t[2]) } else { if (t[2]!="=") { if (l!=4) _parse_positions_err(s) if (t[3]!="=") _parse_positions_err(s) if (anyof(xrefs, t[2])) { // ypos {l|r}=xpos ypos = t[1]; xref = t[2]; xpos = t[4] } else if (anyof(yrefs, t[2])) { // xpos {b|t}=ypos xpos = t[1]; yref = t[2]; ypos = t[4] } else _parse_positions_err(s) } else { if (anyof(yrefs, t[1])) { // {t|b}=ypos ... yref = t[1]; ypos = t[3] } else if (anyof(xrefs, t[1])) { // {l|r}=xpos ... xref = t[1]; xpos = t[3] } else _parse_positions_err(s) if (l==3) { if (yref!="") xpos = "0" // {t|b}=ypos else ypos = "0" // {l|r}=xpos } else if (l==4) { if (yref!="") xpos = t[4] // {t|b}=ypos xpos else ypos = t[4] // {l|r}=xpos ypos } else if (l==6) { if (t[5]!="=") _parse_positions_err(s) if (anyof(xrefs, t[4])) { // {t|b}=ypos {l|r}=xpos if (xref!="") _parse_positions_err(s) xref = t[4]; xpos = t[6] } else if (anyof(yrefs, t[4])) { // {l|r}=xpos {t|b}=ypos if (yref!="") _parse_positions_err(s) yref = t[4]; ypos = t[6] } else _parse_positions_err(s) } else _parse_positions_err(s) } } st_local("ypos", ypos) st_local("yref", yref) st_local("xpos", xpos) st_local("xref", xref) } void _parse_positions_err(string scalar s) { errprintf("invalid specification: %s\n", s) exit(198) } end