How to find the width of a symbol?

I’m writing this (long!) answer because I kind of promised CarLaTeX that I would do. I apologize for the long delay, but I simply couldn’t find the time to complete it until now.

There are several methods that permit to find out the width, height, and depth of a given character, the simplest of which is to use the commands \settowidth, \settoheight, and \settodepth, respectively, which are defined by the LaTeX2e kernel and are described in a variety of sources, ranging from Lamport’s LaTeX – A Document Preparation System (see p. 216) to the answers to the question Retrieve length of a character, already cited in a comment to the present question (but see also Get width of a given text as length; other duplicates exist). Another viable approach, provided that you know the font from which the character in question is drawn, is to look up the required information in the TFM (TeX Font Metric) file of that font, after converting it to its textual representation, a PL (Property List) file.

This question, however, is not an exact duplicate of the abovementioned ones, in that it involves math and asks to express the required length (the width, in this case) in math units. I think that a brief digression is needed, here, in order to give some background information. After that, we’ll be able not only to get a better understanding of the methods hinted at above, but also to ask ourselves the fundamental question: “Is this ability of measuring character widths in math units of any practical utility?”—and to tell, alas, that in all probability the answer is “No”.

Background

Most of TeX’s units of measure are absolute, in the sense that they correspond to a fixed physical dimension (e.g., 1 point = 1/72.27 inches = roughly 0.351460 millimeters); but some of them, on the other hand, are relative, in that they are specified in terms of another dimension linked to some font. Perhaps better-known examples of units of the latter type are em and ex, but math units (mu) are of this kind too. More precisely (cf. The TeXbook, p. 168), 1mu = 1/18 of 1em, where the measure of 1em is taken from the font used for the symbols math group (in NFSS parlance; it is equivalent to TeX’s “family 2”), which, in the configuration set up by the LaTeX2e kernel, corresponds to cmsy10 in \displaystyle and \textstyle, to cmsy7 in \scriptstyle, and to cmsy5 in \scriptscriptstyle; note that the value assigned to the base measure of 1em varies with the current math style, and this is precisely why, and how, math units scale with the current style as well.

The following points, though, should be kept in mind:

  1. Since the effective width of a math unit depends on a parameter linked to the symbols font, and since the actual external font that is used for this purpose can (and usually does) vary if you choose different math fonts, it could well happen that the width of a symbol, when expressed in math units, changes even if the symbols in itself stays the same also in the new math setup for fonts. For instance, we’ll present below a situation in which, after loading the newtxmath package, we ensure, with a stratagem, that the symbol used for \vartriangleright doesn’t change at all (not only it’s always taken from the same slot in the msam font family, as it would ordinarily be the case even without any special expedient, it also retains exactly the same size as when the newtxmath package is not loaded), but its width measured in math units varies notwithstanding, in the script and scriptscript styles, exactly because of the different length of the math unit.

  2. Even if you don’t change the kernel’s setup for the math fonts, and although, as we have just said, math units do scale according to the current math style, there is no guarantee that the width of a symbol remains the same, when measured in math units, in passing, say, from \textstyle to \scripstyle; indeed, we’ll see below that it typically changes, even for symbols taken from the same font family as the one from which the math unit itself is derived—the cmsy family, for instance.

These two remarks entail that, given a problem as the one this question arose from, every solution that relies on kerning amounts that are specified in the code as literal, precomputed values is virtually doomed to break down sooner or later, although the problem described in 2 could be sidestepped with the help of \mathchoice.

Use of \settowidth (and of \widthof) with math

We won’t dwell on the general usage of the \settowidth, \settoheight, and \settodepth commands, nor on that of the companion commands \widthof, \heightof, \depthof, \totalheightof, and \settototalheight, that are made available by the calc package: as already said, there are plenty of sources of information about their usage. It is perhaps worth remarking, though, that when you want to measure the width of a math symbol, you shouldn’t forget to reset, locally, the \mathsurround parameter. Recall that TeX can be instructed to insert a certain amount of kerning before and after each math formula: this happens precisely when the value of \mathsurround is not zero at the end of a formula, in which case it gives the amount of kerning that should be applied to that particular formula. In LaTeX, \mathsurround is typically equal to zero, but this could not always be the case (indeed, this parameter exists precisely because the house styles of certain publishers require that math formulas be typeset with this extra kerning). So, instead of writing just

\settowidth{\mylength}{$\vartriangleright$}

it’s much better, and safer, to write

\settowidth{\mylength}{$\mathsurround=0pt \vartriangleright$}

instead, lest you include the extra kerning in your measurement. The change to \mathsurround is local to the group implied by $...$ (and \settowidth actually supplies another group around it), so it won’t spoil the value that has been set for this style parameter in the rest of the document.

Both plain TeX and LaTeX define the abbreviation \m@th for \mathsurround=0pt , so you can actually write

\settowidth{\mylength}{$\m@th\vartriangleright$}

provided, of course, that you execute the statement in a context where the @ character is interpreted as a letter (e.g., between \makeatletter and \makeatother).

As the answers to the abovementioned question Get width of a given text as length explain, the trick of typesetting something into a temporary box, and then asking for the width of that box by means of the \wd primitive, as in @CarLaTeX’s answer, is precisely what the \settowidth command does internally; so, the above remark about \mathsurround applies to this technique as well: you should say

\sbox0{$\mathsurround=0pt \vartriangleright$}\showthe\wd0

(or

\sbox0{$\m@th\vartriangleright$}\showthe\wd0

if applicable) to guard against an unusual (i.e., non-null) setting of this parameter.

Asking TeX to measure your symbol

With the precautions just outlined about \mathsurround, it is possible to write a small program that measures, and then reports, the width of a given symbol in math units; we’ll use a program of this kind to prove the claims we’ve set forth above. The following points should be noted about the code we are going to present:

  1. The command named \WidthInMathUnitsOf can be used only in contexts in which TeX is typesetting text, but not in the so-called “purely expandable contexts”; this means, among other things, that it cannot be used inside the argument of certain commands, and in particular inside the argument of \typeout (not even if you \protect it).

  2. Therefore, you can use a command like \WidthInMathUnitsOf to have the measured width reported directly in print amid the typeset text, but not on the terminal.

  3. If, on the other hand, your intent is not just to have the width reported, but you want to use the measured value directly in your code for some purpose (for example, you want to pass it as an argument to the \mspace command), probably the best strategy is to use a declaration like our \SetToWidthInMathUnits, that stores the result in a control sequence, in the form of a string of decimal digits, from where it can subsequently be retrieved by means of pure expansion (you must add the unit of measure, that is, mu).

  4. Both \WidthInMathUnitsOf and \SetToWidthInMathUnits compute their result using e-TeX’s \dimexpr extension: this is not the same as using the original TeX’s primitives \multiply and \divide, because e-TeX stores the intermediate result as a 64-bit value, whereas, with the original TeX, you’d almost surely get an “arithmetic overflow” error (unless you play some trick that, however, would severely affect the precision of the result).

Here’s the code (it must be compiled with pdflatex):

% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly 
                                 % declare the paper format.

\usepackage[T1]{fontenc}         % Not always necessary, but recommended.
% End of standard header.  What follows pertains to the problem at hand.

% \usepackage{newtxtext,newtxmath}
\usepackage{amsmath}
\usepackage{amssymb}

% % Uncomment the following line of code to undo (but only for "\normalsize", 
% % which in this document means 10 points) the change made to the math sizes by 
% % the "newtxmath" package:
% \DeclareMathSizes{10}{10}{7}{5} 

\makeatletter

\def\do#1{\@ifdefinable#1{\newdimen#1}}
\do\@TempWidthOne
\do\@TempWidthTwo
\newcommand*\WidthInMathUnitsOf[2]{%
    \settowidth\@TempWidthOne{$\m@th #1#2$}%
    \settowidth\@TempWidthTwo{$\m@th #1\mspace{1mu}$}%
    \strip@pt \dimexpr \@TempWidthOne*\p@/\@TempWidthTwo \relax
}
\newcommand*\SetToWidthInMathUnits[3]{%
    \settowidth\@TempWidthOne{$\m@th #2#3$}%
    \settowidth\@TempWidthTwo{$\m@th #2\mspace{1mu}$}%
    \edef #1{%
        \strip@pt \dimexpr \@TempWidthOne*\p@/\@TempWidthTwo \relax
    }%
}
\newcommand*\ExtractHexDigitOfFamilyOfSymbol[1]{%
    \expandafter\@ExtractHexDigitOfFamily \meaning #1% must be a cs token
}
\@ifdefinable\@ExtractHexDigitOfFamily{
    \def\@ExtractHexDigitOfFamily#1"#2#3#4#5{#3}
}

\makeatother

\newcommand*{\ReportWidthsOf}[1]{%
    \par
    \begingroup
        \setlength{\abovedisplayskip}{\smallskipamount}%
        \indent
        Width of $#1$
        (\texttt{\string #1}, \emph{i.e.}, \texttt{\meaning #1})
        in:%
        \begin{align*}%
            % \ReportWidthFor\displaystyle     {#1}\\%
            \ReportWidthFor\textstyle        {#1}\\%
            \ReportWidthFor\scriptstyle      {#1}\\%
            \ReportWidthFor\scriptscriptstyle{#1}%
        \end{align*}%
    \endgroup
}
\newcommand*{\ReportWidthFor}[2]{%
    \texttt{\string #1}&=
        \texttt{\WidthInMathUnitsOf #1{#2}mu}%
}
\newcommand*{\ReportValueOfOneEm}[1]{%
    &\text{in #1 size,}%
        &\quad\texttt{1em}%
            &=\texttt{\the \fontdimen 6 \csname #1font\endcsname 2}%
        &&\qquad\text{(from \texttt{\fontname \csname #1font\endcsname 2})}%
}
\newcommand*{\ReportSizeOfFontOfSymbol}[2]{%
    &\texttt{\pdffontsize\csname #2font\endcsname
                "\ExtractHexDigitOfFamilyOfSymbol{#1}}%
        &&\quad\text{in #2 size}%
        &\qquad(\texttt{\string\fontname}%
            &=\texttt{\fontname\csname #2font\endcsname
                "\ExtractHexDigitOfFamilyOfSymbol{#1}})%
}
\newcommand*{\ReportSizeOfMSAMFont}{%
    \ReportSizeOfFontOfSymbol{\vartriangleright}%
}

\newcommand*{\mytemp}{} % just declare the name



\begin{document}

% \show\defaultscriptratio

\ReportWidthsOf{\oplus}
\ReportWidthsOf{\wedge}
\ReportWidthsOf{\vartriangleright}

Note that \verb|\vartriangleright| now means \texttt{\meaning\vartriangleright},
but that the external font which corresponds to
family~\texttt{"\ExtractHexDigitOfFamilyOfSymbol{\vartriangleright}} remains
\texttt{\fontname\textfont"\ExtractHexDigitOfFamilyOfSymbol{\vartriangleright}}
(in text size).  On the other hand, the external font linked to the
\texttt{symbols} font family is currently \texttt{\fontname\textfont2} (again in
text size), in which \( \texttt{1em} = \texttt{\the \fontdimen6 \textfont2} \).
More generally, for this \texttt{symbols} font family it turns out that:
%
\begin{alignat*}{3}
    \ReportValueOfOneEm{text};\\
    \ReportValueOfOneEm{script};\\
    \ReportValueOfOneEm{scriptscript}.
\end{alignat*}
%
But---surprise!\@---the packages that configure the math fonts sometimes
redeclare the math sizes for the script and scriptscript styles (for example,
the \texttt{newtxmath} package does so), and this affects also the sizes at
which the fonts of the \texttt{msam} family are loaded.  Indeed, in the present
run those fonts are loaded at the following sizes:
%
\begin{alignat*}{3}
    \ReportSizeOfMSAMFont{text};\\
    \ReportSizeOfMSAMFont{script};\\
    \ReportSizeOfMSAMFont{scriptscript}.
\end{alignat*}
%

% Use the computed width in the argument of another command:

\SetToWidthInMathUnits{\mytemp}{\textstyle}{\vartriangleright}

% \show\mytemp

Now \verb|\mytemp| is \texttt{\mytemp}, and can be used in the argument of
another command:\\
X[$\vartriangleright$]X\\
X[$\mspace{\mytemp mu}$]X

\end{document}

If you compile this code as it stands, Computer Modern fonts will be used, and you’ll get the following output:

Output with CM fonts

On the other hand, if you uncomment the line that says

% \usepackage{newtxtext,newtxmath}

the “newtx” fonts will be used instead, and the output will be

Output with newtx fonts

We see that, although the symbol for \vartriangleright is always taken from the same source (namely, slot ”42 of the msam font family), in the script sizes its width, expressed in math units, is significantly different than it was before. This, however, is in part a false clue, because the newtxmath package slightly alters the ratios between the math size used for the main symbols and those used for scripts and double scripts, and this affects also the sizes at which the msam7 and msam5 fonts are loaded, as the output shows. For this reason, you have a third option: if you—after enabling the “newtx” fonts as we just did—uncomment the following line of code

% \DeclareMathSizes{10}{10}{7}{5} 

the changes that the newtxmath package applied to the math sizes are undone (but only for formulas that get typeset in \normalsize). In this case, the output looks like this:

Output with newtx fonts at 7pt and 5pt

Note that, even in this case, the ntxsy7 font is loaded at 7.0pt, that is, not at its nominal design size (which, as we’ll see below, is 10pt), and a similar remark applies to ntxsy5 as well. On the other hand, the msam7 and msam5 fonts are loaded at their design size (7pt and 5pt, respectively), exactly as they were in our first experiment; this proves beyond any doubt that the difference in the widths of \vartriangleright (Computer Modern experiment: 13.72888mu in \scriptstyle, 13.24544mu in \scriptscriptstyle; this experiment: 16.07161mu in \scriptstyle, 19.50072mu in \scriptscriptstyle) is purely due, at least in this case, to the shrinkage of the math unit.

Looking up the measure in TFM files

Instead of asking TeX to find it out, you could also look up the required measure directly in the TFM (TeX Font Metric) file of the font that contains the character in question; of course, we are assuming here that this font is a “classic” TeX font, as it will be always the case if you are using a “classic” engine (TeX or pdfTeX). Keep in mind, however, that in TFM files all character-related dimensions are expressed in relative units, with one relative unit equaling the size at which the font is loaded by TeX; and that, by default, TeX loads each font at its design size, which is also specified in the TFM file for the font. In order to show how to handle the conversion from these relative units to absolute ones both in the case of a font loaded at its design size, and in the case of a font loaded at a custom size (specified with an at clause), we choose to exemplify the process of measuring the width, in math units, of the \vartriangleright character when used in \scriptstyle with the “newtx” fonts, but with our “math-size-reverting” stratagem in force (cf. the third of our experiments, above): this will involve a font loaded at its design size (msam7) and a font, namely ntxsy7, loaded at 7.0pt, the latter being the font that supplies the width of one math unit.

The first thing you need to do is to convert the relevant TFM files to their human-readable counterpart, a PL (Property List) file; this is much like disassembling an object code file into a human-readable text file. So, move to a clean directory of your choice and type the following at the shell prompt:

tftopl msam7.tfm msam7.pl
tftopl ntxsy7.tfm ntxsy7.pl

The tftopl utility should automatically pick up the .tfm files from your TEXMF tree, and save the corresponding .pl files in the local directory.

Next, open the msam7.pl file in any text editor; its first lines should read

(FAMILY MSAM V2.2)
(FACE O 360)
(CODINGSCHEME TEX MATH SYMBOLS)
(DESIGNSIZE R 7.0)
(COMMENT DESIGNSIZE IS IN POINTS)
(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)
(CHECKSUM O 27035367067)

The lines that says

(DESIGNSIZE R 7.0)

tells (as you can expect!) that the design size of this font is 7.0pt; in the case of the code we posted above, this is also the size at which this font is loaded. Therefore, all subsequent sizes shown in the msam7.pl file must be multiplied by 7.0pt in order to find their actual, absolute value. Now, scroll down in the msam7.pl file until you find the lines

(CHARACTER O 102
   (CHARWD R 0.892861)
   (CHARHT R 0.565615)
   (CHARDP R 0.075675)
   )

This is the description of the character whose number is 102 when expressed in octal (O) notation, which is the same as 42 in hexadecimal; so, it is precisely the character that corresponds to \vartriangleright, as we saw above. Its width is given by the real (R) number that follows the CHARWD keyword; multiplying this number (0.892861) by 7.0pt we obtain the absolute width, 6.250027pt.

Now we have to find out the width of one math unit in \scriptstyle: this is 1/18 of the width of 1em in the ntxsy7 font. So, we open ntxsy7.pl, and find that it begins like this:

(FAMILY UNSPECIFIED)
(FACE F MRR)
(CODINGSCHEME TEX MATH SYMBOLS)
(DESIGNSIZE R 10.0)
(COMMENT DESIGNSIZE IS IN POINTS)
(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)
(CHECKSUM O 31601054554)
(SEVENBITSAFEFLAG TRUE)
(FONTDIMEN
   (SLANT R 0.0)
   (SPACE R 0.0)
   (STRETCH R 0.0)
   (SHRINK R 0.0)
   (XHEIGHT R 0.441)
   (QUAD R 1.0)
   (EXTRASPACE R 0.0)
   (NUM1 R 0.677)
   (NUM2 R 0.394)
   (NUM3 R 0.444)
   (DENOM1 R 0.686)
   (DENOM2 R 0.345)
   (SUP1 R 0.413)
   (SUP2 R 0.363)
   (SUP3 R 0.289)
   (SUB1 R 0.15)
   (SUB2 R 0.247)
   (SUPDROP R 0.386)
   (SUBDROP R 0.05)
   (DELIM1 R 2.39)
   (DELIM2 R 1.01)
   (AXISHEIGHT R 0.257)
   )

We learned from our experiment, above, that this font is loaded at 7.0pt, not at its design size (which, according to the DESIGNSIZE line, is 10.0pt even if this is meant to be—or, better, to be used as—a “7-point” font). So, it turns out that the amount by which the relative dimensions given in the rest of the file should be multiplied is again 7.0pt, albeit for a different reason than before. To get the measure of 1em we have to look at the QUAD item inside the FONTDIMEN list: we read

   (QUAD R 1.0)

so we obtain that 1em = 1.0 × 7.0pt = 7.0pt (as the output of our experiment said); then, 1mu = 7.0pt/18 = 0.388889pt.

Finally, dividing 6.250027pt by 0.388889pt we find that the width of \vartriangleright in math units is 16.07149. In our experiment, a width of 16.07161mu was reported: the difference, 0.00012mu, is equivalent to roughly 0.00005pt, or about 3sp, and is therefore fully accounted for by rounding errors.

Furthermore, it should be remarked that in a case like this, in which both fonts are loaded at the same size, the step of converting the sizes to absolute units (in our example, multiplying them by 7.0pt) is obviously superfluous.

If you are interested in learning the whole story on how to read, and write, PL files, type

texdoc pltotf

at a command-line prompt and read sections 7–14 of the document that is put up (5 pages).

Conclusions

I hope that the foregoing discussion has exhaustively shown that the method of overprinting two symbols, in order to build a composite one, by means of a suitable backspace specified in the code as a literal, precomputed amount given in terms of math units is far from being robust enough to be deployed in any general-purpose application, and can, at most, be considered adequate for an ad-hoc solution. Probably, the idea underlying the use of math units was the belief that an amount specified in terms of these units would automatically scale down in the script and scriptscript styles, but we’ve seen that this is not at all the case. In principle, it would be possible to replace the literal precomputed amount with an amount calculated at run-time, also taking the current math font configuration into account; but this solution couldn’t withstand comparison with the \ooalign method, in that:

  • it would entail replicating at the level of the TeX interpreter more or less the same computations that the \halign primitive, which \ooalign invokes, would perform at the machine-code level of the host computer;

  • it would not avoid the recourse to \mathchoice (that the \ooalign approach requires in order to behave properly).

In conclusion, \ooalign is the way to go in solving this kind of problem.


Since egreg doesn't want to get 15 points for free (@yo'), I'll try to answer by myself.

The width of the symbol, of course, depends on the font you're using.

To find the actual width (in points) you can use \sbox0{$<your_symbol>$} (adding \showthe\wd0 to print it in the log file), and divide it by the width (in points) of 1 math unit, \sbox0{$\mspace{1mu}$}.

In this way you'll find the width of your symbol in math units and you can easily calculate the correct kerning to apply.

\sbox is like \savebox with no optional arguments, see, for example, here for its use with \wd.

In the example, with \sbox0{$\vartriangleright$}\showthe\wd0 in the log file you'll read 7.7778pt.

With \sbox0{$\mspace{1mu}$}\showthe\wd0 in the log you'll read 0.55554pt.

With \sbox0{$\circ$}\showthe\wd0 in the log you'll read 5.00002pt.

Hence the width of $\vartriangleright$ in math units is 7.7778/0.55554=14, and, since you have a -18.5 math units to compensate (and $\circ$ is shorter, 9 math units), the correct kerning is 18.5-14=4.5.

In this way it's possible to find a solution equivalent to the ones with \stackengine or \ooalign.

For more info about \stackengine macro see the documentation of the gorgeous Steven's package, subsection 2.3.1.

Some detailed explanation about \ooalign can be found in this - as always great - egreg's answer.

In the following code, I've added some \vlines to show to alignment more clearly.

Edit:

As Gustavo pointed out, the solution with \mspace does not remain the same in passing from \textstyle to \scriptstyle or to \scriptscriptstyle. First of all, {..} is needed around \vartriangleright, otherwise, the new symbol is messed up in that passage. However, the \circ becomes misaligned:

\documentclass{article}
\usepackage{amsmath, amssymb, stackengine}
\newcommand{\stsym}{\mathbin{\ensurestackMath{%
            \stackengine{0pt}{\vartriangleright}{\circ\mkern3.5mu}{O}{c}{F}{T}{L}}}}
\newcommand{\oosym}{\mathbin{\ooalign{$\vartriangleright$\cr$\mkern.5mu\circ$\cr}}}
\newcommand{\mssym}{\mathbin{{\vartriangleright}\mspace{-13.5mu}\circ\mspace{4.5mu}}}

\begin{document}
\begin{tabular}{ll}
Only \textbackslash\texttt{vartriangleright} for bechmark: 
&
$a\vline\mathbin{\vartriangleright}\vline b$ 
\\
Steven's solution (with \textbackslash\texttt{stackengine}): 
&
$a\vline\stsym\vline b$ 
\\
Solution with \textbackslash\texttt{ooalign}: 
&
$a\vline\oosym\vline b$ 
\\
Solution with \textbackslash\texttt{mspace} and the correct kerning: 
&
$a\vline\mssym\vline b$ 
\\
Original example of the question: 
&
$a\vline\mathbin{\vartriangleright\mspace{-18.5mu}\circ\mspace{3.5mu}}\vline b$ 
\\
\end{tabular}
\vspace{10pt}

\begin{tabular}{ll}
Example in \textbackslash\texttt{scriptstyle}:& \\  
Only \textbackslash\texttt{vartriangleright} for bechmark: 
&
$x_{a\vline\mathbin{\vartriangleright}\vline b}$ 
\\
Steven's solution (with \textbackslash\texttt{stackengine}): 
&
$x_{a\vline\stsym\vline b}$ 
\\
Solution with \textbackslash\texttt{ooalign}: 
&
$x_{a\vline\oosym\vline b}$ 
\\
Solution with \textbackslash\texttt{mspace} and the correct kerning: 
&
$x_{a\vline\mssym\vline b}$ 
\\
Original example of the question: 
&
$x_{a\vline\mathbin{\vartriangleright\mspace{-18.5mu}\circ\mspace{3.5mu}}\vline b}$ 
\\
\end{tabular}

\sbox0{$\vartriangleright$}\showthe\wd0
\sbox0{$\mspace{1mu}$}\showthe\wd0
\sbox0{$\circ$}\showthe\wd0
\end{document}

Output:

enter image description here

Log messages:

> 7.7778pt.
l.30 \sbox0{$\vartriangleright$}\showthe\wd0


> 0.55554pt.
l.31 \sbox0{$\mspace{1mu}$}\showthe\wd0


> 5.00002pt.
l.32 \sbox0{$\circ$}\showthe\wd0

This stackengine approach does not remove the need to manually calculate a shift (since a glyph's sidebearings are unknowable without examination).

However, this approach does simplify things in that the stacked item (in this case the shifted \circ) will not affect the underlying math spacing of the \mathbin{\vartriangleleft}. This effect is brought about because the \useanchorwidth parameter 7 of the \stackengine call is set True.

\documentclass{article}
\usepackage{amsmath, amssymb,stackengine}
\newcommand\mysym{\mathbin{\ensurestackMath{%
  \stackengine{0pt}{\vartriangleright}{\circ\mkern3.5mu}{O}{c}{F}{T}{L}}}}
\begin{document}
\[
a \mysym b
\]
\end{document}

enter image description here