# A challenging horizontal/vertical centering problem

I need a box that typesets a kind of compass in a square. It should look like this figure

 +-----------------+
|        N        |
|                 |
|                 |
| W      X      E |
|                 |
|                 |
|        S        |
+-----------------+


The requirements

1) The N and S should be horizontally centered with respect to the left/right side of the frame and at say 0.3em from the top/lower side of the frame.

2) The W and E should be vertically centered with respect to the top/lower side of the frame and at say 0.3em from the left/right side of the frame.

3) The middle of X (normally a 1 or 2 digit number) should be on the crosspoint of the (hor.) midline through W and E AND the (vert.) midline through N and S.

4) enlarging the font size should equally (well more or less) enlarge the compass size, as well as the thickness of the frame. This should work from 'tiny' upto 'HUGE' and also when 'scalefont' is used to change the font size.

5) Important is that the centering (hor. and vert.) is maintained when the size changes.

I tried several implementations, e.g. using a tabular or using raiseboxes. But every time I got stuck with the vertical alignment.

Does someone know how to do this?

BTW: Requirement 3 is not a mathematical one. It should just look like that.

Picture mode!

The side of the square is 5em, because \unitlength is set to 1em (which will take into account the current font). Therefore 0.3 represents a distance of 0.3em, which is what you seem to want. Fix the size according to your taste.

\documentclass{article}
\usepackage{pict2e}

\newcommand{\compass}[1]{%
\begingroup
\setlength\unitlength{1em}%
\linethickness{0.2ex}%
\begin{picture}(5,5)
% the frame
\moveto(0,0)\lineto(0,5)\lineto(5,5)\lineto(5,0)\closepath\strokepath
% the cardinal points
\put(2.5,4.7){\makebox[0pt]{\raisebox{-\height}{N}}}
\put(2.5,0.3){\makebox[0pt]{S}}
\put(0.3,2.5){\makebox[0pt][l]{\raisebox{-0.5\height}{W}}}
\put(4.7,2.5){\makebox[0pt][r]{\raisebox{-0.5\height}{E}}}
% the center
\put(2.5,2.5){\makebox(0,0){#1}}
\end{picture}%
\endgroup
}

\begin{document}

\bigskip

\end{document}


• Was thinking whether I should use picture but decided against it. Btw. you're missing the thickening of the surrounding line it seems. – Skillmon Apr 1 '18 at 19:57
• @Skillmon Done! – egreg Apr 1 '18 at 20:01
• Then it's a +1. – Skillmon Apr 1 '18 at 20:02
• @ShreevatsaR 2.5 is half of 5 -> the middle. 4.7 is 0.3 less than 5 -> 0.3 from border. And the unit of measurement is set to 1em with \setlength\unitlength{1em}. 5 is the width and height because of the \polyline command's arguments. – Skillmon Apr 1 '18 at 20:33
• @Jack There's no “closepath” for \polyline. I modified the code so an additional segment is not needed. – egreg Apr 3 '18 at 9:57

The following is a wild mix between LaTeX stuff and stuff you'd do using plain TeX. Change the default value of the optional argument of \compass to change the overall size. The overall size is not equal to the optional argument, but to #1+.6em+.4ex in width, #1+.3em+.2ex in height and .3em+.2ex in depth.

\documentclass[border=2mm]{standalone}

% this might already be defined depending on included packages
\providecommand\clap[1]{\hbox to 0pt{\hss #1\hss}}

\newcommand\compass[2][10ex]
{{%
\fboxrule=.2ex
\fboxsep=.3em
\fbox
{%
\vbox to #1{\hsize=#1
\hbox to #1{\hss N\hss}%
\vss
\hbox to #1{\rlap{W}\hss \clap{#2}\hss \llap{E}}%
\vss
\hbox to #1{\hss S\hss}%
}%
}%
}}

\begin{document}
\compass{1}
\end{document}


Vertically it only aligns the baselines of W x E so if x is recognizable smaller than W and E it would not look vertically centred.

Version that also vertically centres the middle line using $\vcenter{...}$ (and utilizing that the \xlap macros use a \hbox internally so the text is not typeset in math mode):

\documentclass[border=2mm]{standalone}

% this might already be defined depending on included packages
\providecommand\clap[1]{\hbox to 0pt{\hss #1\hss}}

\newcommand\compass[2][10ex]
{{%
\fboxrule=.2ex
\fboxsep=.3em
\fbox
{%
\vbox to #1{\hsize=#1
\hbox to #1{\hss N\hss}%
\vss
\hbox to #1{%
$\vcenter{\rlap{W}}$\hss
$\vcenter{\clap{#2}}$\hss
$\vcenter{\llap{E}}$%
}%
\vss
\hbox to #1{\hss S\hss}%
}%
}%
}}

\begin{document}
\compass{\tiny1}
\end{document}


Version that gives the same distance from the centre of the W and E to the border. Unfortunately it doesn't look as good as I thought.

\documentclass[border=2mm]{standalone}

% this might already be defined depending on included packages
\providecommand\clap[1]{\hbox to 0pt{\hss #1\hss}}

\newbox\myTestbox
\newdimen\myhoffset
\newcommand\compass[2][10ex]
{{%
\setbox\myTestbox\hbox{E}%
\myhoffset=.5\wd\myTestbox
\setbox\myTestbox\hbox{W}%
\ifdim\myhoffset<.5\wd\myTestbox
\myhoffset=.5\wd\myTestbox
\fi
\fboxrule=.2ex
\fboxsep=.3em
\fbox
{%
\vbox to #1{\hsize=#1
\hbox to #1{\hss N\hss}%
\vss
\hbox to #1{%
\hskip\myhoffset
$\vcenter{\clap{W}}$\hss
$\vcenter{\clap{#2}}$\hss
$\vcenter{\clap{E}}$%
\hskip\myhoffset
}%
\vss
\hbox to #1{\hss S\hss}%
}%
}%
}}

\begin{document}
\compass[8ex]{\tiny1}
\end{document}


As OP noted, the \vcenter code has a small vertical offset from the middle. Therefore here another version, this time using some \smashs and a calculated \raise amount for #2:

\documentclass[border=2mm]{standalone}

% this might already be defined depending on included packages
\providecommand\clap[1]{\hbox to 0pt{\hss #1\hss}}

\newdimen\myvoffset
\newbox\mytestboxA
\newbox\mytestboxB
\newcommand\compass[2][10ex]
{{%
\setbox\mytestboxA\hbox{WE}%
\setbox\mytestboxB\hbox{#2}%
\myvoffset=\dimexpr
\ht\mytestboxA-\ht\mytestboxB+\dp\mytestboxB-\dp\mytestboxA\relax
\myvoffset=.5\myvoffset
\fboxrule=.2ex
\fboxsep=.3em
\fbox
{%
\vbox to #1{\hsize=#1
\hbox to #1{\hss N\hss}%
\vss
\hbox to #1{%
\smash{\rlap{W}}\hss
\smash{\clap{\raise\myvoffset\hbox{#2}}}\hss
\smash{\llap{E}}%
}%
\vss
\hbox to #1{\hss S\hss}%
}%
}%
}}

\begin{document}
\compass[2.5em]{\tiny1}
\end{document}


Just for completeness (as this is overkill for the problem) a TikZ based answer (you'll need \usepackage{tikz} in your preamble). The optional argument matches the size

\documentclass[tikz,border=2mm]{standalone}

\newcommand\compass[2][15ex]
{%
\begin{tikzpicture}[inner sep=0pt]
\draw[line width=.2ex,use as bounding box] (0,0) rectangle (#1,#1);
\node at (0.5*#1,0.5*#1) {#2};
\node[anchor=north] at (0.5*#1,#1-0.3em) {N};
\node[anchor=south] at (0.5*#1,0.3em) {S};
\node[anchor=west] at (0.3em,0.5*#1) {W};
\node[anchor=east] at (#1-0.3em,0.5*#1) {E};
\end{tikzpicture}%
}

\begin{document}
\compass{\tiny1}
\end{document}


Tikz version

\documentclass[border=5mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcommand\compass[1]{%
\begin{tikzpicture}[x=1em,y=1em]
\coordinate (SW);
\draw[line width=0.1em] (SW) rectangle +(6,6)coordinate(NE);
\coordinate (Mid) at ($(SW)!0.5!(NE)$);
\node[inner sep=0.3em,anchor=west] at (Mid -| SW) {W};
\node[inner sep=0.3em,anchor=east] at (Mid -| NE) {E};
\node[inner sep=0.3em,anchor=south] at (Mid |- SW) {S};
\node[inner sep=0.3em,anchor=north] at (Mid |- NE) {N};
\node[inner sep=0.3em] at (Mid) {#1};
\end{tikzpicture}}
\begin{document}
A\compass{12}A%
\Huge A\compass{12}A%
\footnotesize A\compass{12}A
\end{document}


• Why do you use \MyAht instead of a unit like 1ex or 1em? – Skillmon Apr 1 '18 at 20:38
• @Skillmon, Yes, that is better. I have updated. Quite close to your solution. Didn't see that until now. – StefanH Apr 1 '18 at 23:00
• I honestly think yours is better. Instead of using the inner sep to get the distances I calculate the reference point and set inner sep to 0pt. Yours is cleverer. – Skillmon Apr 2 '18 at 6:58

This is a nice usecase for the xcoffins package:

\documentclass{standalone}
\usepackage{xcoffins}
\NewCoffin\CompassCoffin
\NewCoffin\NorthCoffin
\NewCoffin\SouthCoffin
\NewCoffin\EastCoffin
\NewCoffin\WestCoffin
\NewCoffin\CenterCoffin
\newcommand\compass[1][10ex]{%
\begingroup
\SetHorizontalCoffin\NorthCoffin{N}%
\SetHorizontalCoffin\SouthCoffin{S}%
\SetHorizontalCoffin\EastCoffin{E}%
\SetHorizontalCoffin\WestCoffin{W}%
\SetHorizontalCoffin\CenterCoffin{$+$}%
\SetVerticalCoffin\CompassCoffin{#1}{%
\vbox to #1{%
\hfil\vfil
}%
}%
% Add the north at the *t*op, *h*orizontal *c*enter
\JoinCoffins
\CompassCoffin[t,hc]
\NorthCoffin[t,hc]%
% Add the east at the *v*ertical *c*enter, *r*ight
\JoinCoffins
\CompassCoffin[vc,r]
\EastCoffin[vc,r]%
% Add the south at the *b*ottom, *h*orizontal *c*enter
\JoinCoffins
\CompassCoffin[b,hc]
\SouthCoffin[b,hc]%
% Add the west at the *v*ertical *c*enter, *l*eft
\JoinCoffins
\CompassCoffin[vc,l]
\WestCoffin[vc,l]%
% Add the center at the *v*ertical *c*enter, *h*orizontal *c*enter
\JoinCoffins
\CompassCoffin[vc,hc]
\CenterCoffin[vc,hc]%
\setlength\fboxrule{.2ex}%
\setlength\fboxsep{.3em}%
\fbox{\TypesetCoffin\CompassCoffin}%
\endgroup
}
\begin{document}
\noindent\compass\\
\large\compass\\
\footnotesize\compass
\end{document}