How can I make an enclosure similar to \sqrt?

55 views
Skip to first unread message

Dave Keenan

unread,
Dec 7, 2023, 2:05:12 AM12/7/23
to MathJax Users
I would like to define a macro that works like \sqrt in the sense of automatically resizing both vertically and horizontally to enclose any expression, but with a different shape on the left. Specifically, I want a simple slash in place of the radical's hook. I don't need the optional degree argument. I'm familiar with \enclose, but it doesn't offer this option.

I can mock it up for an enclosed expression of a specific height, as follows. But I'd really like it to work for any enclosed expression. Is that possible in MathJax?

\require{enclose}
\def \recip #1{{\small/}\hspace{-1mu}\enclose{top}{#1} \, }
\recip{x}

recip.png

Davide Cervone

unread,
Dec 8, 2023, 9:34:18 AM12/8/23
to mathja...@googlegroups.com
The various notations are implemented in the output jax's menclose wrapper objects, using their notations map.  If you add new entries to that map, you can create new non-standard enclosure types.  Here is a configuration that adds a notation "angletop" that does what I hope you are looking for:

MathJax = {
  startup: {
    ready() {
      const {CHTMLmenclose} = MathJax._.output.chtml.Wrappers.menclose;
      const a = .3;   // skew angle in radians
      CHTMLmenclose.notations.set('angletop', {
        renderer: (node, child) => {
          const {h, d, w} = node.getBBox();
          const t = node.thickness;
          const W = (h + d) * Math.tan(a);
          const angle = node.adjustBorder(node.html('mjx-angle', {style: {
            width: 0, height: node.em(h + d - t),
            transform: 'skewX(' + node.fixed(-a) + 'rad) translateX(' + node.em(W) + ')',
            'transform-origin': 'top right',
            'border-right': node.em(t) + ' solid',
            position: 'absolute',
            left: 0, bottom: 0
          }}));
          const top = node.adjustBorder(node.html('mjx-topline', {style: {
            width: node.em(w - W),
            'border-top': node.em(node.thickness) + ' solid',
            top: 0, right: 0,
            position: 'absolute'
          }}));
          node.adaptor.append(node.chtml, angle);
          node.adaptor.append(node.chtml, top);
        },
        bbox: (node) => {
          const {h, d} = node.childNodes[0].getBBox();
          const p = node.padding / 2;
          const t = node.thickness;
          const w = (h + d + 3 * p) * Math.tan(a);
          return [2 * p + t, p, p, w];
        },
        border: (node) => [node.thickness, 0, 0, 0]
      });
      MathJax.startup.defaultReady();
    }
  }
}

I wasn't sure how you wanted the angled portion to change depending on the height of the content, so I made is be at a fixed angle, as that seemed to look best over a variety of sizes.  The angle is given by the const a = .3 near the top, and you can change that to suit your taste.  It works by creating two extra elements for the angles and top lines, and skewing the angled line, then placing them in the correct locations.  The bbox method gives the changes to the bounding box of the contents due to the enclosure (as an array of top, right, bottom, left deltas), and the border method tells how the border size is affected.

This is for the CHTML output, so if you are using SVG, this would need to be adjusted.  And if you wanted it work work for both if the user switches the renderer via the contextual menu, then that would take a bit more work.   But perhaps this gets you what you need.

Davide


<recip.png>

--
You received this message because you are subscribed to the Google Groups "MathJax Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mathjax-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/mathjax-users/3b7ca8bd-f243-41c8-994a-c6fa093c3212n%40googlegroups.com.
<recip.png>

Dave Keenan

unread,
Dec 9, 2023, 4:11:24 AM12/9/23
to MathJax Users
Thank you so much Davide! That must have been quite an effort. Much appreciated. You chose a nice angle.

I got my web-developer friend Douglas Blumeyer to figure out how to install it into our phpBB-based forum. He only had to change
const {CHTMLmenclose} = MathJax._.output.chtml.Wrappers.menclose;
to
const CHTMLmenclose = MathJax._.output.chtml.Wrappers.menclose.CHTMLmenclose;
and I changed the angle to

It works very well. But there is a small problem. The angle part creeps upward with repeated nesting. I'm hoping this will be easy for you to fix, because I can't figure it out. Sorry to ask you for more help when you have already been so generous with your time. The attached image shows the problem. It would also be nice if an empty angletop enclosure had a little more top showing. I figure it should be as wide overall as an empty top-left enclosure.

AngletopTest.png

Davide Cervone

unread,
Dec 9, 2023, 7:11:19 AM12/9/23
to mathja...@googlegroups.com
Turns out to be an easy fix:  remove the 

border: (node) => [node.thickness, 0, 0, 0]

line and the comma at the end of the previous line.  That should do it.

For a somewhat more pleasant nesting layout, you can add

      const b = .75;  // how much of the angle is inside the bounding box

right after the const a = .3 line near the top of the file, and then add *  b just before the semicolon in the const W line in the renderer, and similarly for the the const w line in the bbox function.

The updated code is

MathJax = {
  startup: {
    ready() {
      const {CHTMLmenclose} = MathJax._.output.chtml.Wrappers.menclose;
      const a = .3;   // skew angle in radians
      const b = .75;  // how much of the angle is inside the bounding box
      CHTMLmenclose.notations.set('angletop', {
        renderer: (node, child) => {
          const {h, d, w} = node.getBBox();
          const t = node.thickness;
          const W = (h + d) * Math.tan(a) * b;
          const strike = node.adjustBorder(node.html('mjx-angle', {style: {
            width: 0, height: node.em(h + d - t),
            transform: 'skewX(' + node.fixed(-a) + 'rad) translateX(' + node.em(W) + ')',
            'transform-origin': 'top right',
            'border-right': node.em(t) + ' solid',
            position: 'absolute',
            left: 0, bottom: 0
          }}));
          const top = node.adjustBorder(node.html('mjx-topline', {style: {
            width: node.em(w - W),
            'border-top': node.em(node.thickness) + ' solid',
            top: 0, right: 0,
            position: 'absolute'
          }}));
          node.adaptor.append(node.chtml, strike);
          node.adaptor.append(node.chtml, top);
        },
        bbox: (node) => {
          const {h, d} = node.childNodes[0].getBBox();
          const p = node.padding / 2;
          const t = node.thickness;
          const w = (h + d + 3 * p) * Math.tan(a) * b;
          return [2 * p + t, p, p, w];
        }
      });
      MathJax.startup.defaultReady();
    }
  }
}

With this, the results look like


As for the empty enclosures, the "top left" enclose adds padding on the left and top of the contents, so you are seeing the effect of that by having top line extend over the left padding, and the left line extend over the top adding.  But for "angletop", the angle takes up all the left padding, and the top line is only over the original content, which is empty in this case.  That means you get no horizontal line in that case (actually, you get a little bit of a one, because there is a small amount of right padding being added, and the line extends over that).  You can use \enclose{angletop}{\ } to get some width to the content, thus getting a longer top line.

Better yet might be \enclose{angletop}{\phantom{i}} and \enclose{left top}{\vphantom{i}\,} to give more natural heights to the empty enclosures.


See if those changes make it better for you.

Davide


<AngletopTest.png>

--
You received this message because you are subscribed to the Google Groups "MathJax Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mathjax-user...@googlegroups.com.

Dave Keenan

unread,
Dec 10, 2023, 12:14:00 AM12/10/23
to MathJax Users
Wonderful! Thanks again, Davide.

You can see how I'm using it, and how I have credited you, here.

I increased the angle very slightly, so it has a tan of 1/3, thinking that might render as slightly smoother, and be easier to reason about when tweaking the algorithm. I found I have to set b = 1, otherwise an outer angletop enclosure may be cut off at the left margin.

There are sometimes still tiny imperfections, at least in my browser (Firefox for Windows) such as tiny gaps or tiny cusps, but I can probably figure out how to fix that. You have got me 99.9% of the way there. Thank you.


Davide Cervone

unread,
Dec 10, 2023, 7:24:04 AM12/10/23
to mathja...@googlegroups.com
Thanks for sharing your site and your usage of this enclosure.  I see now why you needed to be concerned about the nesting!  I had not heard of this algebra before, and so I learned something, too.  Looks really interesting.  Thanks for crediting me, and best of luck with the site.

Davide


--
You received this message because you are subscribed to the Google Groups "MathJax Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mathjax-user...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages