feat: support \angl and \angln (#2334)

* Support \angl and \angln

* Update screenshots

* Update documentation
This commit is contained in:
Ron Kok
2020-07-25 06:53:11 -07:00
committed by GitHub
parent d1ad2e0b8b
commit e76857ec03
10 changed files with 88 additions and 11 deletions

View File

@@ -84,7 +84,8 @@ table td {
|\And|$\And$|| |\And|$\And$||
|\and|<span style="color:firebrick;">Not supported</span>|[Deprecated](https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax)| |\and|<span style="color:firebrick;">Not supported</span>|[Deprecated](https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax)|
|\ang|<span style="color:firebrick;">Not supported</span>|[Deprecated](https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax)| |\ang|<span style="color:firebrick;">Not supported</span>|[Deprecated](https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax)|
|\angl|<span style="color:firebrick;">Not supported</span>|| |\angl|$a_{\angl n}$||
|\angln|$a_\angln$||
|\angle|$\angle$|| |\angle|$\angle$||
|\approx|$\approx$|| |\approx|$\approx$||
|\approxeq|$\approxeq$|| |\approxeq|$\approxeq$||

View File

@@ -207,6 +207,7 @@ For Persian composite characters, a user-supplied [plug-in](https://github.com/
|$\bcancel{5}$ `\bcancel{5}` |$\underbrace{a+b+c}_{\text{note}}$ `\underbrace{a+b+c}_{\text{note}}` |$\bcancel{5}$ `\bcancel{5}` |$\underbrace{a+b+c}_{\text{note}}$ `\underbrace{a+b+c}_{\text{note}}`
|$\xcancel{ABC}$ `\xcancel{ABC}`|$\not =$ `\not =` |$\xcancel{ABC}$ `\xcancel{ABC}`|$\not =$ `\not =`
|$\sout{abc}$ `\sout{abc}`|$\boxed{\pi=\frac c d}$ `\boxed{\pi=\frac c d}` |$\sout{abc}$ `\sout{abc}`|$\boxed{\pi=\frac c d}$ `\boxed{\pi=\frac c d}`
|$a_{\angl n}$ `$a_{\angl n}`|$a_\angln$ `a_\angln`
`\tag{hi} x+y^{2x}` `\tag{hi} x+y^{2x}`
$$\tag{hi} x+y^{2x}$$ $$\tag{hi} x+y^{2x}$$

View File

@@ -40,12 +40,15 @@ const htmlBuilder = (group, options) => {
if (!isSingleChar) { if (!isSingleChar) {
inner.classes.push("cancel-pad"); inner.classes.push("cancel-pad");
} }
} else if (label === "angl") {
inner.classes.push("anglpad");
} else { } else {
inner.classes.push("boxpad"); inner.classes.push("boxpad");
} }
// Add vertical padding // Add vertical padding
let vertPad = 0; let topPad = 0;
let bottomPad = 0;
let ruleThickness = 0; let ruleThickness = 0;
// ref: cancel package: \advance\totalheight2\p@ % "+2" // ref: cancel package: \advance\totalheight2\p@ % "+2"
if (/box/.test(label)) { if (/box/.test(label)) {
@@ -53,18 +56,30 @@ const htmlBuilder = (group, options) => {
options.fontMetrics().fboxrule, // default options.fontMetrics().fboxrule, // default
options.minRuleThickness, // User override. options.minRuleThickness, // User override.
); );
vertPad = options.fontMetrics().fboxsep + topPad = options.fontMetrics().fboxsep +
(label === "colorbox" ? 0 : ruleThickness); (label === "colorbox" ? 0 : ruleThickness);
bottomPad = topPad;
} else if (label === "angl") {
ruleThickness = Math.max(
options.fontMetrics().defaultRuleThickness,
options.minRuleThickness
);
topPad = 4 * ruleThickness; // gap = 3 × line, plus the line itself.
bottomPad = Math.max(0, 0.25 - inner.depth);
} else { } else {
vertPad = isSingleChar ? 0.2 : 0; topPad = isSingleChar ? 0.2 : 0;
bottomPad = topPad;
} }
img = stretchy.encloseSpan(inner, label, vertPad, options); img = stretchy.encloseSpan(inner, label, topPad, bottomPad, options);
if (/fbox|boxed|fcolorbox/.test(label)) { if (/fbox|boxed|fcolorbox/.test(label)) {
img.style.borderStyle = "solid"; img.style.borderStyle = "solid";
img.style.borderWidth = `${ruleThickness}em`; img.style.borderWidth = `${ruleThickness}em`;
} else if (label === "angl" && ruleThickness !== 0.049) {
img.style.borderTopWidth = `${ruleThickness}em`;
img.style.borderRightWidth = `${ruleThickness}em`;
} }
imgShift = inner.depth + vertPad; imgShift = inner.depth + bottomPad;
if (group.backgroundColor) { if (group.backgroundColor) {
img.style.backgroundColor = group.backgroundColor; img.style.backgroundColor = group.backgroundColor;
@@ -138,6 +153,9 @@ const mathmlBuilder = (group, options) => {
case "\\fbox": case "\\fbox":
node.setAttribute("notation", "box"); node.setAttribute("notation", "box");
break; break;
case "\\angl":
node.setAttribute("notation", "actuarial");
break;
case "\\fcolorbox": case "\\fcolorbox":
case "\\colorbox": case "\\colorbox":
// <menclose> doesn't have a good notation option. So use <mpadded> // <menclose> doesn't have a good notation option. So use <mpadded>
@@ -253,3 +271,21 @@ defineFunction({
htmlBuilder, htmlBuilder,
mathmlBuilder, mathmlBuilder,
}); });
defineFunction({
type: "enclose",
names: ["\\angl"],
props: {
numArgs: 1,
argTypes: ["hbox"],
allowedInText: false,
},
handler({parser}, args) {
return {
type: "enclose",
mode: parser.mode,
label: "\\angl",
body: args[0],
};
},
});

View File

@@ -588,6 +588,18 @@
border-bottom-style: solid; border-bottom-style: solid;
border-bottom-width: 0.08em; border-bottom-width: 0.08em;
} }
.angl {
// from package actuarialangle, which is always used in a subscript.
box-sizing: border-content;
border-top: 0.049em solid; // defaultRuleThickness in scriptstyle
border-right: 0.049em solid; // ditto
margin-right: 0.03889em; // 1 mu
}
.anglpad {
padding: 0 0.03889em 0 0.03889em; // pad 1mu left and right (in scriptstyle)
}
} }
.katex-display { .katex-display {

View File

@@ -987,6 +987,10 @@ defineMacro("\\braket", "\\mathinner{\\langle{#1}\\rangle}");
defineMacro("\\Bra", "\\left\\langle#1\\right|"); defineMacro("\\Bra", "\\left\\langle#1\\right|");
defineMacro("\\Ket", "\\left|#1\\right\\rangle"); defineMacro("\\Ket", "\\left|#1\\right\\rangle");
//////////////////////////////////////////////////////////////////////
// actuarialangle.dtx
defineMacro("\\angln", "{\\angl n}");
// Custom Khan Academy colors, should be moved to an optional package // Custom Khan Academy colors, should be moved to an optional package
defineMacro("\\blue", "\\textcolor{##6495ed}{#1}"); defineMacro("\\blue", "\\textcolor{##6495ed}{#1}");
defineMacro("\\orange", "\\textcolor{##ffa500}{#1}"); defineMacro("\\orange", "\\textcolor{##ffa500}{#1}");

View File

@@ -304,14 +304,15 @@ const svgSpan = function(
const encloseSpan = function( const encloseSpan = function(
inner: HtmlDomNode, inner: HtmlDomNode,
label: string, label: string,
pad: number, topPad: number,
bottomPad: number,
options: Options, options: Options,
): DomSpan | SvgSpan { ): DomSpan | SvgSpan {
// Return an image span for \cancel, \bcancel, \xcancel, or \fbox // Return an image span for \cancel, \bcancel, \xcancel, \fbox, or \angl
let img; let img;
const totalHeight = inner.height + inner.depth + 2 * pad; const totalHeight = inner.height + inner.depth + topPad + bottomPad;
if (/fbox|color/.test(label)) { if (/fbox|color|angl/.test(label)) {
img = buildCommon.makeSpan(["stretchy", label], [], options); img = buildCommon.makeSpan(["stretchy", label], [], options);
if (label === "fbox") { if (label === "fbox") {

View File

@@ -2465,6 +2465,24 @@ describe("A strike-through builder", function() {
}); });
}); });
describe("A actuarial angle parser", function() {
it("should not fail in math mode", function() {
expect`a_{\angl{n}}`.toParse();
});
it("should fail in text mode", function() {
expect`\text{a_{\angl{n}}}`.not.toParse();
});
});
describe("A actuarial angle builder", function() {
it("should not fail", function() {
expect`a_{\angl{n}}`.toBuild();
expect`a_{\angl{n}i}`.toBuild();
expect`a_{\angl n}`.toBuild();
expect`a_\angln`.toBuild();
});
});
describe("A phantom parser", function() { describe("A phantom parser", function() {
it("should not fail", function() { it("should not fail", function() {
expect`\phantom{x}`.toParse(); expect`\phantom{x}`.toParse();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -68,7 +68,11 @@ BoldSymbol: |
\sum_{\boldsymbol{\alpha}}^{\boldsymbol{\beta}} \boldsymbol{\omega}+ \boldsymbol{\int_\alpha^\beta} \boldsymbol{\Omega + {}} \\ \sum_{\boldsymbol{\alpha}}^{\boldsymbol{\beta}} \boldsymbol{\omega}+ \boldsymbol{\int_\alpha^\beta} \boldsymbol{\Omega + {}} \\
\boldsymbol{\lim_{x \to \infty} \log Ax2k\omega\Omega\imath+} \\ \boldsymbol{\lim_{x \to \infty} \log Ax2k\omega\Omega\imath+} \\
x \boldsymbol{+} y \boldsymbol{=} z x \boldsymbol{+} y \boldsymbol{=} z
Boxed: \boxed{F=ma} \quad \boxed{ac}\color{magenta}{\boxed{F}}\boxed{F=mg} Boxed: |
\begin{array}{l}
\boxed{F=ma} \quad \boxed{ac}\color{magenta}{\boxed{F}}\boxed{F=mg} \\[2em]
A_{\angl n} + B_{\angl g} + C_\angln
\end{array}
Cases: | Cases: |
f(a,b)=\begin{cases} f(a,b)=\begin{cases}
a+1&\text{if }b\text{ is odd} \\ a+1&\text{if }b\text{ is odd} \\